Deploying factory contract which deploys ERC721 & ERC20 exceeds block gas limit

I have a main contract to help me deploy ERC721 && ERC20 token

but it seems to have some problems when i tried deploy both ERC20 and ERC721 in a same factory contract.

i use @openzeppelin/contracts": "^2.5.1" in package.json

My ERC721 contract

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC721/ERC721Full.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721Mintable.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721MetadataMintable.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721Holder.sol";
import "@openzeppelin/contracts/drafts/Counters.sol";

contract myERC721 is
    ERC721Full,
    ERC721Mintable,
    ERC721MetadataMintable,
    ERC721Holder
{
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    event awardNewItem(uint256 indexed newID);

    constructor(
        string memory name,
        string memory symbol,
        string memory baseURI 
    ) public ERC721Full(name, symbol) {
        _setBaseURI(baseURI);
    }

    function awardItem(
        address player,
        string memory _tokenURI
    ) public returns (uint256) {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        mint(player, newItemId);
        _setTokenURI(newItemId, _tokenURI);

        return newItemId;
    }
}

My ERC20 contract

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Mintable.sol";

contract myERC20 is ERC20, ERC20Detailed, ERC20Burnable, ERC20Mintable {
    constructor(
        string memory name, 
        string memory symbol,
        uint8 decimals, 
        uint256 initialSupply
    ) public ERC20Detailed(name, symbol, decimals) {
        _mint(msg.sender, initialSupply * (10**uint256(decimals)));
    }
}


My main factory contract

pragma solidity ^0.5.0;

import "./myERC721.sol";
import "./myERC20.sol";

contract tokenFactory {

    address[] tokenAddress;
 
    function deploy721Contract(
        string calldata name,
        string calldata symbol,
        string calldata baseUrl
    ) external returns (myERC721 cardAddress) {

        myERC721 newCards = new myERC721(name, symbol, baseUrl);

        tokenAddress.push(address(newCards));
        return newCards;
    }

    function deploy20Contract(
        string calldata name,
        string calldata symbol,
        uint8 decimals,
        uint256 initialSupply
    ) external returns (myERC20 creditsAddress) {

        myERC20 newCredits = new myERC20(
            name,
            symbol,
            decimals,
            initialSupply
        );

        tokenAddress.push(address(newCredits));
        return newCredits;
    }

}


Problem

  1. when i comment out the deploy20Contract function code. the factory contract can deploy and use 3598155 gas
    image image

  2. when i comment out deploy721Contract function code. the factory contract can deploy and use 2208084 gas
    image image

BOTH 1 and 2 do not have cost much gas.

  1. when i don’t comment out any code, both have the deploy20Contract and deploy721Contract in the factory contract

it will get out of gas. and i increase the gasLimit. it still out of gas and finally tell me Exceeds block gas limit

image

image

image

It seems that when i depoy both ERC20 && ERC721 contract in the same contrat. it will stuck in an endless loop and use unlimited gas.

how should i do? Is there any conflict about importing the 721 & 20 contract at the same contract?

1 Like

Hi @Serenity,

Welcome to the community forum :wave:

The tokenFactory contract is failing to deploy as it is over the bytecode size limit of 24577 or greater (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-170.md)

With optimization disabled the bytecode size is as follows:

$ jq '.deployedBytecode | length / 2 - 1' build/contracts/tokenFactory.json
26394

With optimization enabled, 200 runs the bytecode size is reduced and can be deployed:

$ jq '.deployedBytecode | length / 2 - 1' build/contracts/tokenFactory.json
17403

When you change the optimization settings, we need to rebuild (either remove the build directory or use npx truffle compile --all)


You are currently using OpenZeppelin Contracts 2.x. You may want to look at using OpenZeppelin Contracts 3.x.


Assuming that you are deploying multiple ERC20 tokens and ERC721 tokens, I would suggest looking at deploying an implementation contract for each and then deploy minimal proxies: https://blog.openzeppelin.com/deep-dive-into-the-minimal-proxy-contract/

This would reduce the amount of gas required to create new tokens.

but why still i can’t deploy it after i enabled optimization in remix?
image

1 Like

Hi @Serenity,

I deployed in Remix to Ropsten with optimization enabled and was then able to use the factory to create an ERC20 and an ERC721

I converted your contracts to use GitHub imports so I could deploy using Remix.

myERC721.sol

pragma solidity ^0.5.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC721/ERC721Full.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC721/ERC721Mintable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC721/ERC721MetadataMintable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC721/ERC721Holder.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/drafts/Counters.sol";

contract myERC721 is
    ERC721Full,
    ERC721Mintable,
    ERC721MetadataMintable,
    ERC721Holder
{
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    event awardNewItem(uint256 indexed newID);

    constructor(
        string memory name,
        string memory symbol,
        string memory baseURI 
    ) public ERC721Full(name, symbol) {
        _setBaseURI(baseURI);
    }

    function awardItem(
        address player,
        string memory _tokenURI
    ) public returns (uint256) {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        mint(player, newItemId);
        _setTokenURI(newItemId, _tokenURI);

        return newItemId;
    }
}

myERC20.sol

pragma solidity ^0.5.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC20/ERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC20/ERC20Detailed.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC20/ERC20Burnable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.5.1/contracts/token/ERC20/ERC20Mintable.sol";

contract myERC20 is ERC20, ERC20Detailed, ERC20Burnable, ERC20Mintable {
    constructor(
        string memory name, 
        string memory symbol,
        uint8 decimals, 
        uint256 initialSupply
    ) public ERC20Detailed(name, symbol, decimals) {
        _mint(msg.sender, initialSupply * (10**uint256(decimals)));
    }
}

tokenFactory.sol

pragma solidity ^0.5.0;

import "./myERC721.sol";
import "./myERC20.sol";

contract tokenFactory {

    address[] tokenAddress;
 
    function deploy721Contract(
        string calldata name,
        string calldata symbol,
        string calldata baseUrl
    ) external returns (myERC721 cardAddress) {

        myERC721 newCards = new myERC721(name, symbol, baseUrl);

        tokenAddress.push(address(newCards));
        return newCards;
    }

    function deploy20Contract(
        string calldata name,
        string calldata symbol,
        uint8 decimals,
        uint256 initialSupply
    ) external returns (myERC20 creditsAddress) {

        myERC20 newCredits = new myERC20(
            name,
            symbol,
            decimals,
            initialSupply
        );

        tokenAddress.push(address(newCredits));
        return newCredits;
    }

}

Hi @Serenity,

I wanted to check that you were able to deploy with optimization?

Hi @abcoathup , I have a question regarding the use of the minimal proxies for the ERC20 tokens. You suggested deploying an implementation contract for the ERC20 token then deploying minimal proxies. Looking at the the "Clones" contract the clone() method only takes an implementation address. I do not see how we can pass arguments to clone. The reason I am asking is that if we are using the minimal proxy as a factory for the ERC20 tokens, how can we specify a different token id, name? or are stuck with the same token id and name for all?

Thanks.