我们将结合代码进行分析。
最先运行的 _beforeTokenTransfers 和最后运行的
_afterTokenTransfers 都是由用户自定义的函数,用于实现白名单等功能。函数具体定义如下:
function _beforeTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
function _afterTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
的核心。值得注意的是,这些函数都定义在 unchecked 代码块中,因为 NFT 的各个参数设置不会产生溢出情况,通过 unchecked 可以避免编译过程中插入溢出检查代码以减少 gas 消耗。
简而言之,在某些已经确定不会出现数据溢出的场景中使用 unchecked 包裹代码可以减少 gas 消耗
最开始,我们设置表示 NFT 所有者的 _packOwnershipData 数据结构,具体设置方法如下:
_packedOwnerships[startTokenId] = _packOwnershipData(
to,
_nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
);
为方便读者理解代码,在此处,我们给出 _packedOwnerships 的定义:
// Bits Layout:
// - [0..159] addr
// - [160..223] startTimestamp
// - [224] burned
// - [225] nextInitialized
// - [232..255] extraData
mapping(uint256 => uint256) private _packedOwnerships;
1
2
3
4
5
6
7
我们先对 _packOwnershipData 函数的输入参数进行分析,需要解决 _nextInitializedFlag 和 _nextExtraData 的定义问题,
前者定义如下:
function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
// For branchless setting of the nextInitialized
flag.
assembly {
// (quantity == 1) << _BITPOS_NEXT_INITIALIZED
.
result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
}
}
显然,此函数用于设置 nextInitialized 标识,如果铸造的数量为 1 ,我们将此标识置为 1 (即 True )。当然,我们也使用了位移操作使其处于合适的位置。