定义如下:function _nextExtraData( address from, address to, uint256 prevOwnershipPacked) private view returns (uint256) { uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA); return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;此函数用于写入额外的信息,开发者需要自行定义 _extraData 函数以实现相关数据的写入。此过程的核心函数为 _packOwnershipData ,其定义如下:function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) { assembly { // Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean. owner := and(owner, _BITMASK_ADDRESS) // `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`. result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags)) }}有了上述 _nextInitializedFlag 和 _nextExtraData 的补充和注释,相信读者可以理解 _packOwnershipData 的实现原理,简单来说,该函数使用 or 操作符拼接 owner 、 timestamp 和 flags 以实现最终的数据结构。显然,我们只需要构造以下部分作为flags输入,即可完成 _packOwnershipData 的构造:// - [224] `burned`// - [225] `nextInitialized`// - [232..255] `extraData`读者可以注意到 owner 、 timestamp 和 flags 均为 uint256 数据类型,所以直接使用 or 进行拼接是合适的接下来设置 _packedAddressData 数据结构。此数据结构定义如下:// Bits Layout:// - [0..63] `balance`// - [64..127] `numberMinted`// - [128..191] `numberBurned`// - [192..255] `aux`mapping(address => uint256) private _packedAddressData;mint 过程仅涉及 balance 和 numberMinted 两部分数据。所以设置较为简单,代码如下:_packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);1我们使用 ((1 << _BITPOS_NUMBER_MINTED) | 1) 构造(此处 _BITPOS_NUMBER_MINTED = 64 )出如下二进制数字 (以 16 进制表示):0b100000011使用 Python 运行 bin((64 << 1) | 1) 可以获得此结果所以我们可以直接将数字与 balance 和 numberMinted 对齐相加。在释放 Transfer 事件前,我们需要对 NFT 接受方的地址进行简单校验,即保证 NFT 接受方的地址不为 0 地址,校验代码如下:uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;if (toMasked == 0) _revert(MintToZeroAddress.selector);此处进行了一个有趣的操作,将地址转化为 uint256 后与 0 进行比较。此处涉及 address 与 uint256 类型的转化。众所周知, address 类型事实上就是 uint160 ,两者可以直接转化。如果读者对 address 类型不熟悉,可参考 文档在直接转化后,为了避免直接转化导致的高位不为 0 的特殊情况出现,我们使用 _BITMASK_ADDRESS 进行清理。此常量定义如下:uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;1通过使用此常量进行 & ,我们可以保证 address 与 uint256 的安全转换。此处我们没有深入讨论为什么 uint160 到 uint256 的直接转化可能导致高位不为 0 的情况发生,读者可编写一简单合约编译后使用字节码研究此问题释放 Transfer 事件,此处我们可以一窥 emit 背后的原理:uint256 end = startTokenId + quantity;uint256 tokenId = startTokenId;do { assembly { // Emit the `Transfer` event. log4( 0, // Start of data (0, since no data). 0, // End of data (0, since no data). _TRANSFER_EVENT_SIGNATURE, // Signature. 0, // `address(0)`. toMasked, // `to`. tokenId // `tokenId`. ) } // The `!=` check ensures that large values of `quantity` // that overflows uint256 will make the loop run out of gas.} while (++tokenId != end);
标签:uint256,代币,分红,BITPOS,lt,flags,address,owner,BNB From: https://blog.51cto.com/u_16489205/9102057