1

I'm unable to compile my proxy contract due to Warning: Contract code size exceeds 24576 bytes

If the implementation logic is written inside the main MyNFT.sol proxy contract, won't the Proxy contract eventually get a "size exceeds" warning when updating the contract with functions in the future?

I'm able to deploy my proxy without any implementation logic but when I tried to create MyNFTV2.sol, but I can't access _tokenIdCounter.current(); from the proxy contract. It gives the error Member "current" not found or not visible after argument-dependent lookup in struct CountersUpgradeable.Counter storage ref.

Proxy Contract w/o my logic compiles fine:

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

import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";

contract MyNFT is Initializable, ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable, OwnableUpgradeable, UUPSUpgradeable {
    using StringsUpgradeable for uint256;
    using CountersUpgradeable for CountersUpgradeable.Counter;
    CountersUpgradeable.Counter private _tokenIdCounter;
    uint256 public maxSupply;
    uint256 public maxMintAmount;
    uint256 public cost;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() initializer {}

    function initialize() initializer public {
        __ERC721_init("MyNFT", "MN");
        __ERC721Enumerable_init();
        __ERC721URIStorage_init();
        __Ownable_init();
        __UUPSUpgradeable_init();
        _tokenIdCounter.increment();
        maxSupply = 10000;
        maxMintAmount = 5;
        cost = 0.01 ether;
    }

    function safeMint(address to, string memory uri) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    function _authorizeUpgrade(address newImplementation)
        internal
        onlyOwner
        override
    {}

    // The following functions are overrides required by Solidity.

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721Upgradeable, ERC721EnumerableUpgradeable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId)
        internal
        override(ERC721Upgradeable, ERC721URIStorageUpgradeable)
    {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721Upgradeable, ERC721URIStorageUpgradeable)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721Upgradeable, ERC721EnumerableUpgradeable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }   
}

It won't compile when I try to add the following import and functions to the contract:

import "base64-sol/base64.sol";

function create(uint256 mintAmount) external payable {
    require(msg.value >= cost, "Not enough ether sent");
    require(totalSupply() + 1 <= maxSupply, "There are not enough NFTs in the supply");

    for (uint256 i=1; i<=mintAmount; i++) {
        uint newTokenId = _tokenIdCounter.current();
        _safeMint(msg.sender, newTokenId);
        _setTokenURI(newTokenId, generateImage(['white','white','white','white','white','white','white','white']));
        _tokenIdCounter.increment();
    }
}

function generateImage(string[8] memory colors) internal pure returns (string memory) {
     return string(
        abi.encodePacked(
            [mySVGimg]
        )
    );
}

function buildPolygons() internal pure returns (string memory) {
     string memory polygons;
     for (uint256 i=1; i<=20; i++) {
         polygons = string(abi.encodePacked(polygons,'<polygon class="triangle triangle-',i,'" points="500,200 759,650 241,650"/>'));
     }
     return polygons;
}

function buildTriangles() internal pure returns (string memory) {
    string memory triangles;
    uint delay = 0;
    uint incr = 1;
     for (uint256 i=0; i<20; i++) {
         triangles = string(abi.encodePacked(triangles,'.triangle-',i,'{animation-delay:',delay,'s}'));
         delay = delay - (incr/2);
     }
     return triangles;
}

function svgToImageURI(string memory svg) internal pure returns (string memory) {
    return string(abi.encodePacked("data:image/svg+xml;base64,",Base64.encode(bytes(string(abi.encodePacked(svg))))));
}

function formatTokenURI(string memory imageURI, uint newTokenId, string[8] memory colors) internal pure returns(string memory) {
    return string(
        abi.encodePacked(
            "data:application/json;base64,",
            Base64.encode(
                bytes(
                    abi.encodePacked(
                        '{"tokenId":"',newTokenId.toString(),'", "name":"Test#',newTokenId.toString(),'", "description" : "A Test", "attributes":[{"trait_type":"Background #1","value":"',colors[0],'"},{"trait_type":"Background #2","value":"',colors[1],'"},{"trait_type":"Background #3","value":"',colors[2],'"},{"trait_type":"Color #1","value":"',colors[3],'"},{"trait_type":"Color #2","value":"',colors[4],'"},{"trait_type":"Color #3","value":"',colors[5],'"},{"trait_type":"Color #4","value":"',colors[6],'"},{"trait_type":"Color #5","value":"',colors[7],'"}], "image":"',imageURI,'"}'
                    )
                )
            )
        )
    );
}

1 Answer 1

2

Move all your image processing functions to a library and deploy separately.

They do not require access to storage and it is very clean to keep that code separate.

There's also other fancy ways to get that much text data on-chain, but the above will work fine.

Thank you for putting all image stuff on-chain!

2
  • You're welcome. I'm new to solidity and loved the concept of having everthing 100% on-chain. Do you have any recommendations for the other ways to get that much text on-chain? I'm still working on variable packing and refactoring my code. Also, any recommendations on testing gas fees with my contract without having to deploy and mint everytime I make a logic change?
    – 626
    Dec 12, 2021 at 9:08
  • Hardhat is a great way to benchmark. I worked on this, and you can use it to study how to set up a project with gas benchmarking locally github.com/AreaWorld/ethereum-contracts. As for other approaches, this is very implementation specific. Dec 13, 2021 at 17:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.