totalSupply() 与 tokenID 计数器的气体效率 | ERC-721

Gas efficiency of totalSupply() vs. a tokenID counter | ERC-721

我正在为 NFT 创建一个 solidity 合约,在我的 mint 函数中,我不确定调用 totalSupply() 与使用令牌计数器并递增它是否更好。任何一种变化都会花费更多的汽油吗?是更标准的做法吗?我见过两者都被使用的例子。

变体 1:

contract MyNFT is ERC721Enumerable, PaymentSplitter, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private currentTokenId;
...

function mint(uint256 _count)
        public payable
{
    uint256 tokenId = currentTokenId.current();
    require(tokenId < MAX_SUPPLY, "Max supply reached");
    for(uint i = 0; i < _count; ++i){
        currentTokenId.increment();
        uint256 newItemId = currentTokenId.current();
        _safeMint(msg.sender, newItemId);
    }
}
}

变体 2:

function mint(uint256 _count)
        public payable
{
    uint supply = totalSupply();
    require( supply + _count <= MAX_SUPPLY, "Exceeds max supply." );
    for(uint i = 0; i < _count; ++i){
        _safeMint(msg.sender, supply + i);
    }
}

两个版本似乎都有效。我只是想确定我使用的是最有效/最安全的。感谢您的任何建议!

首先,您需要向我们展示底层实现。但是,我可以推测这些是 ERC721Enumerable and Counters.

的未修改的 openzeppelin 实现

仅针对您的情况,使用 Counter 对我来说似乎有点毫无意义。

  1. 它会增加您的部署成本(只是一点点),因为来自 Counter 库的冗余代码
  2. 你已经知道你的tokens数组的长度了,为什么要保留两次呢?计数器是为您不知道元素数量的情况创建的,例如映射。

本人不保证以下分析的正确性

调用totalSupply(从操作码的角度来看)将:

  • 跳转到总供应量(8 气)
  • sload tokens.slot (200) gas

但是,在使用 Counter 时,您每次减量 sstore(>= 5000 gas),每次阅读 sload(200 gas)。

只要我没有误会 Counter 使用存储,因此 sstoresload 操作码,第二个变体将使用更少的 gas。