Smart Contract Factories

Factories are a wonderful design pattern, which are not only used with smart contracts, but generally in any other coding language to help facilitate better automation.

Factories are one of my favorite design patterns when dealing with generic smart contracts, because they eliminate the need to open up my IDE and do another command line deployment, when I could be doing a deployment with a simple transaction through MetaMask. Along with the benefits of a factory, they are also simple to create.

TLDR; Show Me the Goods!

If you are someone that likes to jump right into the code here it is:
ApetasticERC20Factory GitHub Repo

The factory is deployed below and you can start making your very own ERC-20 tokens through the Write Contract tab.
ApetasticERC20Factory Deployed

Full Details: How it Works

Solidity language has a very useful feature for creating new contracts using the new keyword.

The new keyword works similarly to javascript in that it creates a new instance. In this case, a new deployed contract instance.

In the example below, pulled from the ApetasticERC20Factory, you can see that we first need to:

  1. Import the contract that we want to deploy. In this case ApetasticERC20.sol.
  2. Use the new keyword in a function using the name of the contract to deploy a new instance.
  3. Pass in arguments which the constructor of the contract needs.
  4. The return value represents the address of the deployed contract. In the factory it is a good idea to record it so that new contracts can be found later if needed. In this case we are pushing the address to the allTokens array.

ApetasticERC20 token is also more than just an address. It also represents an ApetasticERC20 interface which means uint256 tokenTotalSupply = token.totalSupply() would also be possible. This allows your factory contract to call methods on the newly deployed contract for configuration or other purposes.

/* from ApetasticERC20Factory contract */

import "./ApetasticERC20.sol";

/// @notice Create a new ERC-20 contract
/// @dev Tokens created have 18 decimals which means add 18 zeros the integer supply: 000000000000000000 
/// @param name The name of the token
/// @param symbol The symbol of the token
/// @param supply The totalSupply of the token
function createToken(string memory name, string memory symbol, uint256 supply) external returns (address) {
    ApetasticERC20 token = new ApetasticERC20(name, symbol, msg.sender, supply);
    allTokens.push(address(token));
    emit TokenCreated(address(token), supply);
    return address(token);
}

The ApetasticERC20.sol Contract

The ApetasticERC20 contract is an extension of OpenZeppelin's ERC20 implementation. This was done so the proper parameters could be set in the constructor of the token. You could take a similar approach and add a bit more functionality if needed.

As seen in the contract code below, the constructor is used to mint the initial supply by calling the internal _mint function and sending it to the mintTo address.

Because the factory contract is deploying the contract, msg.sender would actually send the tokens to the factory which could lock them. To avoid this, the factory passes its msg.sender (the address that initiated the creation in this case) into ApetasticERC20 so that the tokens are sent to the correct address.

/* from ApetasticERC20 contract */

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/// @notice Create an ERC20 token with adjustable initial parameters
contract ApetasticERC20 is ERC20 {

    /// @param name The name of the token
    /// @param symbol The symbol of the token
    /// @param mintTo The address that the initial supply should be sent to
    /// @param supply The totalSupply of the token
    constructor(
        string memory name,
        string memory symbol,
        address mintTo,
        uint256 supply
    ) ERC20(name, symbol) {
        _mint(mintTo, supply);
    }
}

Time to Get Dirty!

Sweet, now you can apply this fairly straightforward concept to ever more and more complex smart contracts. Let's build!

Challenge: Factory Improvements

This is just an MVP for factories. From here a good improvement is to make the factory contract upgradeable which allows us to add features without changing the contract address.

Contracts which don't hold value are good candidates for upgradeability.

Give it a shot & let me know if there are other features you desire!

19