NFT空投预售的几种方法

374 阅读2分钟

预售是运营NFT的常见手段。

一、 链上预售白名单

方法是在合约的存储中简单地包含一个地址映射变量,将每个地址映射到一个布尔值,或者每个地址映射到该地址允许的铸币数量。

1. mapping(address => uint8) _allowList;  

2.   

3.     function setAllowList(  

4.         address[] calldata addresses,  

5.         uint8 numAllowedToMint  

6.     ) external onlyOwner {  

7.         for (uint256 i = 0; i < addresses.length; i++) {  

8.             _allowList[addresses[i]] = numAllowedToMint;  

9.         }  

10.     } 

这种方法使用起来简单,但在配置白名单时,将会耗费大量的gas费用。

二、 默克尔树

使用openzeppelin的MerkleProof.verify进行验证。

在合约中只需要存储roothash,验证时用户需传入默克尔树证明,新增叶子节点时,也只需要更新roothash。

验证通过后可领取空投,并将状态改为已领取,避免重复领取。

1. import '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol';  

2. ...  

3. // declare bytes32 variables to store each root (a hash)  

4. bytes32 public genesisMerkleRoot;  

5. bytes32 public authorsMerkleRoot;  

6. bytes32 public presaleMerkleRoot;  

7. ...  

8. // separate functions to set the roots of each individual Merkle Tree  

9. function setGenesisMerkleRoot(bytes32 _root) external onlyOwner {  

10.  genesisMerkleRoot = _root;  

11. }  

12. function setAuthorsMerkleRoot(bytes32 _root) external onlyOwner {  

13.   authorsMerkleRoot = _root;  

14. }  

15. function setPresaleMerkleRoot(bytes32 _root) external onlyOwner {  

16.   presaleMerkleRoot = _root;  

17. }  

18. ...  

19. // create merkle leaves from supplied data  

20. function _generateGenesisMerkleLeaf(  

21.   address _account,  

22.   uint256 _tokenId  

23. )  internal  pure  returns (bytes32) {  

24.  return keccak256(abi.encodePacked(_tokenId, _account));  

25. }  

26. function _generateAuthorsMerkleLeaf(  

27.   address _account,  

28.   uint256 _tokenCount  

29. )  internal  pure  returns (bytes32) {  

30.   return keccak256(abi.encodePacked(_account, _tokenCount));  

31. }  

32. function _generatePresaleMerkleLeaf(  

33.   address _account,  

34.   uint256 _max  

35. )  internal  pure  returns (bytes32) {  

36.   return keccak256(abi.encodePacked(_max, _account));  

37. }  

38. ...  

39. // function to verify that the given leaf belongs to a given tree using its root for comparison  

40. function _verifyMerkleLeaf(  

41.   bytes32 _leafNode,  

42.   bytes32 _merkleRoot,  

43.   bytes32[] memory _proof ) internal view returns (bool) {  

44.   return MerkleProof.verify(_proof, _merkleRoot, _leafNode);  

45. }  

然后,调用每个 mint/claim 函数都需要使用发送者的地址来生成和验证叶子节点。 例如,当使用 for loop 铸造多个代币时

1. require(  

2.   _verifyMerkleLeaf(  

3.      _generateGenesisMerkleLeaf(  

4.         msg.sender,  

5.         _tokenIds[i]),  

6.      genesisMerkleRoot,  

7.      _proofs[i]  

8. ), "Invalid proof, you don't own that Token ID");