开篇
元宇宙大牌的Azuki 项目在2022年 1 月 13 日举行的 Phase 1 销售中瞬间售罄了 8700 个 NFT(每个价格高达1个以太坊,当前市价$3000)。 这款NFT 是使用了Azuki自己开发的 ERC721A标准,能够显著降低销售时的交易费用。
ERC721 是以太坊等 EVM 生态系统中 NFT 的代表标准,与加密资产等 同质化代币相比,会产生大量的交易费用(所谓的 Gas 费)。
通过为项目优化 ERC721 的内部结构,Azuki 实现了低发行费用的兼容 ERC721 的 NFT。 在本文中,我将解释这个 ERC721A。 源代码基于发布在Etherscan 上的代码和发布在 GitHub 上的代码。
ERC721A的三大特点
- 保留 ERC721 可枚举接口
- 铸造多个 NFT 时降低费用
- tokenOwnerByIndex Gas减量及注意事项
这里将分三个部分解释ERC721A的特点。
保留 ERC721 可枚举接口
ERC721 是一个标准,它拥有某个代币拥有的账户的信息,但它没有将某个账户拥有的代币列表数据存储起来。换句话说,你无法从包括你自己在内的智能合约中看到一个账户的代币列表。
因此,一个名为 ERC721Enumerable 的 ERC721 扩展标准被提出并广泛用于 OpenZeppelin 等等。 ERC721Enumerable 将用于令牌列表的数据保存起来,以方便引用。因此,与简单的 ERC721 相比,会使用额外的存储空间,因此在实现 ERC721Enumerable 时,传输和铸币所需的Gas会增加。
关于Enumerable,如果是不需要一览表的只有一个的艺术NFT,或者是构建一个离链游戏系统,请不要选择 Enumerable。这样可以降低铸币成本和未来转账成本。
鉴于未来的链上生态系统,Azuki 没有选择移除 ERC721 Enumerable。 如后所述,即使是简单的所有权信息也在参照发行时的存储信息,因此 ERC721Enumerable 是通过从所有令牌中扫描所拥有的令牌的方式来实现的。
铸造多个 NFT 时降低费用
作为ERC721A的一个亮点,它声称在新发行(mint)的同时铸造多个时可以显着降低gas。
首先,向智能合约的存储区域保存数据时会消费gas。 将此gas乘以创建交易时指定的gas价格就是交易费用。(译者注:交易费=gas量*gas价格,gas量对于某个具体操作是固定的,gas价格会根据行情实时变化)
在 ERC721 的情况下,Token ID 的所有者信息记录在智能合约的存储区中,每个 Token ID都会存储其所有人信息,每个ID的铸币和转移都会需要Gas费用。
ERC721的数数使用了链上存储 |
---|
与此不同,ERC721A的前提是发行多个(BatchSize个)带有连续序列号的NFT,只有最小的token ID使用存储,BatchSize-1个Token不写入存储。
参考上图,所有者0xA铸造了5个NFT,只有1号存储所有者信息,2-5号不做记录。确定所有者信息时候,通过ID升序扫描,可以看出2到5号token的所有者与1号token的所有者相同,即0xA。
你可以看到,此实现只是推迟了写入所有权信息。 如果发生转移,所有者信息还是需要写入存储。 正如 Azuki 所提到的,您还可以看到它仅在同时铸造多个代币时才有效。
上图中,3号NFT转移给0xC,这个时候4号NFT的所有人需要写入0xA,以保持数据正确性。
通过将4号所有人写入0xA,5号所有权信息与以前保持一致。 当所有代币通过重复此操作多次转移时,存储状态将与标准 ERC721 相同。
这样,通过将每个token单独写入存储,与对mint有负担的传统ERC721相比,可以尽可能减少mint时的负担,可以说是ERC721A 的最大的特点。
tokenOfOwnerByIndex 气体减量及注意事项
如上所述,ERC721A 实现了 ERC721Enumerable,因此可以从智能合约中获取拥有的Token列表。通常每个账户的token所有权信息会保存在链上,通过执行tokenOfOwnerByIndex函数获取列表。
ERC721A 是通过在执行 tokenOfOwnerByIndex 时扫描所有已发行的Token来实现的,而不保存 ERC721Enumerable生成的附加信息。这样在实施 Enemerable 的同时会显著减少铸币和传输过程中Gas费。
另一方面,tokenOfOwnerByIndex 会扫描所有已发行的Token,因此如果将此函数作为交易的一部分执行的话,它被处理的次数就会与已铸造的Token数一样多。这个过程由于不涉及更新存储,所以每个都不需要很多gas,但如果你有大量的Token,就需要当心。
因此,Azuki 的在合约中明确写到,如果有超过 10,000 个Token,需要小心使用。
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
* This read function is O(collectionSize). If calling from a separate contract, be sure to test gas first.
* It may also degrade with extremely large collection sizes (e.g >> 10000), test for your use case.
*/
function tokenOfOwnerByIndex(address owner, uint256 index)
public
view
override
returns (uint256)
{
require(index < balanceOf(owner), "ERC721A: owner index out of bounds");
uint256 numMintedSoFar = totalSupply();
uint256 tokenIdsIdx = 0;
address currOwnershipAddr = address(0);
for (uint256 i = 0; i < numMintedSoFar; i++) {
TokenOwnership memory ownership = _ownerships[i];
if (ownership.addr != address(0)) {
currOwnershipAddr = ownership.addr;
}
if (currOwnershipAddr == owner) {
if (tokenIdsIdx == index) {
return i;
}
tokenIdsIdx++;
}
}
revert("ERC721A: unable to get token of owner by index");
}
什么情况下可以使用ERC721A?
前面解释了基于 Azuki NFT 的 ERC721A。那么,在什么情况下可以使用这个标准?
如果查看 Azuki 的 Twitter 帖子和网站,您就会发现这个项目的动机之一是打开 NBA Top Shot 和 Pokemon Card Packs 的乐趣。
购买Token,过两天打开盲盒,与每个都独一无二的NFT相遇,假设有这样的经历,购买多个Token是一个很自然的想法。
如本文所述,ERC721A 不是降低交易费用的灵丹妙药,而是适应NFT项目的特点,针对 ERC721 标准的优化 gas 费的使用时机。
自以太坊费用飙升以来已经过去一年多,但以太坊上的包括NFT的各种项目,依然势头不减。随着更多符合 ERC 标准的特殊实现,各种创新将持续不断推出!