1
+ # Merkle Distributor Airdrop
2
+
1
3
## 介绍
2
4
3
5
### NFT merkel airdrop
13
15
### ERC20 merkel airdrop
14
16
15
17
参考 1inch,dydx,uniswap 都实现 merkle 空投。 具体原理请参考:
16
- https://itzone.com.vn/en/article/merkle-airdrop-the-airdrop-solution-for-token-issues/
17
18
18
- ### 抢红包
19
+ - < https://itzone.com.vn/en/article/merkle-airdrop-the-airdrop-solution-for-token-issues/ >
20
+
21
+ ### 抢红包
22
+
19
23
本样例演示了抢红包合约的功能, 在节假日的时候可以部署相应的合约进行红包发放.
20
24
对应合约路径为 contracts/redpacket
21
25
@@ -24,159 +28,164 @@ https://itzone.com.vn/en/article/merkle-airdrop-the-airdrop-solution-for-token-i
24
28
- ERC721Basic
25
29
最简单的 NFT 空投合约, 项目方直接调用 mint 接口, 对指定账户投放 NFT
26
30
27
- ``` js
28
- //
29
- await expect (this .registry .connect (this .accounts [1 ]).mint (account, tokenId))
30
- .to .emit (this .registry , ' Transfer' )
31
- .withArgs (ethers .constants .AddressZero , account, tokenId);
32
- ```
31
+ ``` js
32
+ //
33
+ await expect (this .registry .connect (this .accounts [1 ]).mint (account, tokenId))
34
+ .to .emit (this .registry , ' Transfer' )
35
+ .withArgs (ethers .constants .AddressZero , account, tokenId);
36
+ ```
33
37
34
38
- ERC721LazyMint
35
39
经过验证的空投方式. 可能在下面这种场景中出现, 比如项目方准备空投 NFT 给一些用户, 那么项目方先发送一个邮件给该用户, 邮件中包含此 NFT 的 tokenID. 用户收到邮件后, 在线下根据 toukenID 和 账户地址进行签名, 然后发送给项目方. 项目方拿到用户签名后, 调用空投合约的 redeem 接口, 传入 account, tokenId, signature. 其中 account, tokenId 需要从预先保留的项目方数据库中获取, 以验证该用户确实对应该 NFT. 如果验证通过, 则生成一个 NFT 给该用户.
36
40
37
- ``` js
38
- // 签名
39
- this .token .signature = await this .accounts [1 ].signMessage (hashToken (this .token .tokenId , this .token .account ));
41
+ ``` js
42
+ // 签名
43
+ this .token .signature = await this .accounts [1 ].signMessage (hashToken (this .token .tokenId , this .token .account ));
40
44
41
- // 签名上链验证
42
- await expect (this .registry .redeem (this .token .account , this .token .tokenId , this .token .signature ))
43
- .to .emit (this .registry , ' Transfer' )
44
- .withArgs (ethers .constants .AddressZero , this .token .account , this .token .tokenId );
45
- ```
45
+ // 签名上链验证
46
+ await expect (this .registry .redeem (this .token .account , this .token .tokenId , this .token .signature ))
47
+ .to .emit (this .registry , ' Transfer' )
48
+ .withArgs (ethers .constants .AddressZero , this .token .account , this .token .tokenId );
49
+ ```
46
50
47
51
- ERC721LazyMintWith712
48
52
在传统的签名方式中, 我们直接调用签名接口, 传入需要的签名参数, 这个过程中我们无法直观的感知需要签名具体参数意义, 特别是当 MetaMask 弹出提示, 需要你对一笔数据进行签名时, 如果不能结构化的看到需要签名的具体数据, 我们可能会拒绝签名这笔交易.
49
53
EIP-712 就是在用户签名时把结构化数据展示给他们确认的场景. 之后链上确认签名是否正确, 过程和 ERC721LazyMint 类似
50
54
链下签名样例如下:
51
55
52
- ``` js
53
- // Domain
54
- {
55
- name: ' Name' ,
56
- version: ' 1.0.0' ,
57
- chainId: this .chainId ,
58
- verifyingContract: this .registry .address ,
59
- },
60
- // Types
61
- {
62
- NFT : [
63
- { name: ' tokenId' , type: ' uint256' },
64
- { name: ' account' , type: ' address' },
65
- ],
66
- },
67
- // Value
68
- this .token ,
69
- );
70
- ```
56
+ ``` js
57
+ // Domain
58
+ {
59
+ name: ' Name' ,
60
+ version: ' 1.0.0' ,
61
+ chainId: this .chainId ,
62
+ verifyingContract: this .registry .address ,
63
+ },
64
+ // Types
65
+ {
66
+ NFT : [
67
+ { name: ' tokenId' , type: ' uint256' },
68
+ { name: ' account' , type: ' address' },
69
+ ],
70
+ },
71
+ // Value
72
+ this .token ,
73
+ );
74
+ ```
71
75
72
76
- ERC721LazyMintWith712SignatureChecker
73
77
和 ERC721LazyMintWith712 类似, 唯一的区别就是在链上进行验证时, 增加了 SignatureChecker
74
78
75
- ``` js
76
- function _verify (address signer , bytes32 digest , bytes memory signature )
77
- internal view returns (bool)
78
- {
79
- return hasRole (MINTER_ROLE , signer) && SignatureChecker .isValidSignatureNow (signer, digest, signature);
80
- }
81
- ```
79
+ ``` js
80
+ function _verify (address signer , bytes32 digest , bytes memory signature )
81
+ internal view returns (bool)
82
+ {
83
+ return hasRole (MINTER_ROLE , signer) && SignatureChecker .isValidSignatureNow (signer, digest, signature);
84
+ }
85
+ ```
82
86
83
87
- ERC721MerkleDrop
84
88
链下生成 Merkle 证明, 之后把 Merkle 证明发送到链上进行验证, 验证通过后, 就会给用户生成相应的 NFT token
85
89
86
- ``` js
87
- // 链下生成 Merkle 证明
88
- this .token .proof = this .merkleTree .getHexProof (hashToken (this .token .tokenId , this .token .account ));
90
+ ``` js
91
+ // 链下生成 Merkle 证明
92
+ this .token .proof = this .merkleTree .getHexProof (hashToken (this .token .tokenId , this .token .account ));
89
93
90
- // 调用链上接口进行验证, 同时生成 NFT token
91
- await expect (this .registry .redeem (this .token .account , this .token .tokenId , this .token .signature ))
92
- .to .emit (this .registry , ' Transfer' )
93
- .withArgs (ethers .constants .AddressZero , this .token .account , this .token .tokenId );
94
- ```
94
+ // 调用链上接口进行验证, 同时生成 NFT token
95
+ await expect (this .registry .redeem (this .token .account , this .token .tokenId , this .token .signature ))
96
+ .to .emit (this .registry , ' Transfer' )
97
+ .withArgs (ethers .constants .AddressZero , this .token .account , this .token .tokenId );
98
+ ```
95
99
96
100
## 测试流程
97
- ### Merkle airdrop
101
+
102
+ ### Merkle airdrop
103
+
98
104
- 安装依赖
99
105
100
- ``` bash
101
- yarn
102
- ```
106
+ ``` bash
107
+ yarn
108
+ ```
103
109
104
110
- 执行测试程序
105
111
106
- ``` bash
107
- npx hardhat test
108
- ```
112
+ ``` bash
113
+ npx hardhat test
114
+ ```
109
115
110
- ### 抢红包
111
- - 配置环境环境变量
112
- ``` shell
113
- cp .env.exmpale .env
116
+ ### HappyRedPacket
114
117
115
- # # 在 .env 文件中配置 PRIVATE_KEY, INFURA_ID, PROJECT_ID, TARGET_ACCOUNT
116
- # # 比如有一个账户 A , 那么 PRIVATE_KEY 为账户 A 对应的 PRIVATE_KEY, TARGET_ACCOUNT 为账户 A 对应的账户地址
117
- ```
118
+ - 配置环境环境变量
118
119
119
- - 配置 redpacketAddressList
120
- 因为红包领取时会进行 Merkle 校验, 所以需要在 Merkle List 中配置对应的账户地址.
121
- 修改 scripts/redpacket/redpacketAddressList.json 文件,在其中加入上一步操作中 "TARGET_ACCOUNT" 的值.
120
+ ``` shell
121
+ cp .env.exmpale .env
122
122
123
- - 安装依赖
124
- ``` shell
125
- yarn
126
- ```
123
+ # # 在 .env 文件中配置 PRIVATE_KEY, INFURA_ID, PROJECT_ID, TARGET_ACCOUNT
124
+ # # 比如有一个账户 A , 那么 PRIVATE_KEY 为账户 A 对应的 PRIVATE_KEY, TARGET_ACCOUNT 为账户 A 对应的账户地址
125
+ # # 模拟多人抢红包,需要配置三个私钥
126
+ ```
127
+
128
+ - 安装依赖
129
+
130
+ ``` shell
131
+ yarn
132
+ ```
127
133
128
134
- 部署 ERC20 合约
129
- 执行如下命令,然后获取输出的 "Token address" 值
130
- ``` shell
131
- npx hardhat run scripts/redpacket/1-deploySimpleToken.js --network kovan
135
+ 执行如下命令,然后获取输出的 "Token address" 值
136
+
137
+ ``` shell
138
+ npx hardhat run scripts/redpacket/1-deploySimpleToken.js --network kovan
132
139
133
- # # 输入信息如下:
134
- Deploying contracts with the account: 0x3238f24e7C752398872B768Ace7dd63c54CfEFEc
135
- Account balance: 796474026501725149
136
- Token address: 0xdc6999dC3f818B4f74550569CCC7C82091cA419F
137
- 1000000000
138
- ```
140
+ # # 输入信息如下:
141
+ Deploying contracts with the account: 0x3238f24e7C752398872B768Ace7dd63c54CfEFEc
142
+ Account balance: 796474026501725149
143
+ Token address: 0xdc6999dC3f818B4f74550569CCC7C82091cA419F
144
+ 1000000000
145
+ ```
139
146
140
147
- 部署 RedPacket 合约
141
- 执行如下命令,然后获取输出的 "RedPacket address" 值
142
- ``` shell
143
- npx hardhat run scripts/redpacket/2-deployHappyRedPacket.js --network kovan
148
+ 执行如下命令,然后获取输出的 "RedPacket address" 值
144
149
145
- # # 输出信息如下:
146
- Deploying contracts with the account: 0x3238f24e7C752398872B768Ace7dd63c54CfEFEc
147
- Account balance: 783625061469463255
148
- RedPacket address: 0x6F35e57a7421F5b04DDb47b67453A5a5Be32e58B
149
- ```
150
+ ``` shell
151
+ npx hardhat run scripts/redpacket/2-deployHappyRedPacket.js --network kovan
152
+
153
+ # # 输出信息如下:
154
+ Deploying contracts with the account: 0x3238f24e7C752398872B768Ace7dd63c54CfEFEc
155
+ Account balance: 783625061469463255
156
+ RedPacket address: 0x6F35e57a7421F5b04DDb47b67453A5a5Be32e58B
157
+ ```
150
158
151
159
- 创建红包
152
- 修改 scripts/redpacket/3-createRedPacket.js 文件中的 "HappyRedPacketAddress" 和 "SimpleTokenAddress" 地址为上面输出的地址, 然后执行下面的命令, 获取输出的 "RedpacketId"
153
- ``` shell
154
- npx hardhat run scripts/redpacket/3-createRedPacket.js --network kovan
160
+ 修改 scripts/redpacket/3-createRedPacket.js 文件中的 "HappyRedPacketAddress" 和 "SimpleTokenAddress" 地址为上面输出的地址, 然后执行下面的命令, 获取输出的 "RedpacketId"
155
161
156
- # # 输出值
157
- Approve Successfully
158
- merkleTree Root: 0x5cc6f1ff34a2c6f871d40cdc4559468f96a7ec06d7bf6ab0f9b5aeccc9b33154
159
- CreationSuccess Event, total: 10000 RedpacketId: 0x45eb11e56a1b699f5e99bd16785c84b73a8257c712e0d1f31306ab1e3423b2e0
160
- Create Red Packet successfully
161
- ```
162
+ ``` shell
163
+ npx hardhat run scripts/redpacket/3-createRedPacket.js --network kovan
162
164
163
- - 领取红包
164
- 修改 "4-claimRedpacket.js" 文件中的 "HappyRedPacketAddress" 和 "redpacketID" 值为上面的输出值, 然后执行下面的命令进行红包的 claim
165
- ``` shell
166
- npx hardhat run scripts/redpacket/4-claimRedpacket.js --network kovan
165
+ # # 输出值
166
+ Approve Successfully
167
+ merkleTree Root: 0x5cc6f1ff34a2c6f871d40cdc4559468f96a7ec06d7bf6ab0f9b5aeccc9b33154
168
+ CreationSuccess Event, total: 10000 RedpacketId: 0x45eb11e56a1b699f5e99bd16785c84b73a8257c712e0d1f31306ab1e3423b2e0
169
+ Create Red Packet successfully
170
+ ```
167
171
168
- # # 得到的输出 "Sign Message:" 即为领取红包时需要输入的签名信息,防止恶意领取
169
- ```
172
+ - 领取红包
173
+ 修改 "4-claimRedpacket.js" 文件中的 "HappyRedPacketAddress" 和 "redpacketID" 值为上面的输出值, 然后执行下面的命令进行红包的 claim
170
174
171
- ## 参考链接
175
+ ``` shell
176
+ npx hardhat run scripts/redpacket/4-claimRedpacket.js --network kovan
172
177
173
- - https://github.com/Anish-Agnihotri/merkle-airdrop-starter
174
- - https://github.com/OpenZeppelin/workshops/tree/master/06-nft-merkle-drop/contracts
175
- - https://github.com/miguelmota/merkletreejs
176
- - erc20 merkel drop: https://github.com/trustlines-protocol/merkle-drop/blob/master/contracts/contracts/MerkleDrop.sol
177
- - merkel drop discussion: https://forum.openzeppelin.com/t/creating-a-claimable-air-drop-too-many-addresses/6806
178
- - Evolution of Airdrop: https://medium.com/hackernoon/evolution-of-airdrop-from-common-spam-to-the-merkle-tree-30caa2344170
179
- - github demo: https://github.com/smartzplatform/constructor-eth-merkle-airdrop
180
- - uni airdrop :https://github.com/Uniswap/merkle-distributor
181
- - uni airdrop :https://steveng.medium.com/performing-merkle-airdrop-like-uniswap-85e43543a592
178
+ # # 得到的输出 "Sign Message:" 即为领取红包时需要输入的签名信息,防止恶意领取
179
+ ```
180
+
181
+ ## 参考链接
182
182
183
+ - < https://github.com/Anish-Agnihotri/merkle-airdrop-starter >
184
+ - < https://github.com/OpenZeppelin/workshops/tree/master/06-nft-merkle-drop/contracts >
185
+ - < https://github.com/miguelmota/merkletreejs >
186
+ - erc20 merkel drop: < https://github.com/trustlines-protocol/merkle-drop/blob/master/contracts/contracts/MerkleDrop.sol >
187
+ - merkel drop discussion: < https://forum.openzeppelin.com/t/creating-a-claimable-air-drop-too-many-addresses/6806 >
188
+ - Evolution of Airdrop: < https://medium.com/hackernoon/evolution-of-airdrop-from-common-spam-to-the-merkle-tree-30caa2344170 >
189
+ - github demo: < https://github.com/smartzplatform/constructor-eth-merkle-airdrop >
190
+ - uni airdrop: < https://github.com/Uniswap/merkle-distributor >
191
+ - uni airdrop: < https://steveng.medium.com/performing-merkle-airdrop-like-uniswap-85e43543a592 >
0 commit comments