NFT空投预售的几种方法
预售是运营NFT的常见手段。
一、链上预售白名单
方法是在合约的存储中简单地包含一个地址映射变量,将每个地址映射到一个布尔值,或者每个地址映射到该地址允许的铸币数量。
1. mapping(address => uint8) _allowList;
2.
3.functionsetAllowList(
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. bytes32publicgenesisMerkleRoot;
5. bytes32publicauthorsMerkleRoot;
6. bytes32publicpresaleMerkleRoot;
7. ...
8. // separate functions to set the roots of each individual Merkle Tree
9. functionsetGenesisMerkleRoot(bytes32 _root) external onlyOwner {
10. genesisMerkleRoot = _root;
11. }
12.functionsetAuthorsMerkleRoot(bytes32 _root) external onlyOwner {
13. authorsMerkleRoot = _root;
14.}
15.functionsetPresaleMerkleRoot(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.returnkeccak256(abi.encodePacked(_tokenId, _account));
25. }
26.function_generateAuthorsMerkleLeaf(
27. address _account,
28. uint256 _tokenCount
29. ) internal pure returns (bytes32) {
30.returnkeccak256(abi.encodePacked(_account, _tokenCount));
31. }
32.function_generatePresaleMerkleLeaf(
33. address _account,
34. uint256 _max
35. ) internal pure returns (bytes32) {
36.returnkeccak256(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.returnMerkleProof.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");