Skip to content

Commit 2118635

Browse files
authored
Merge pull request #587 from maroonstar/main
update redpacket scripts file
2 parents 188bf26 + be5a595 commit 2118635

File tree

9 files changed

+262
-181
lines changed

9 files changed

+262
-181
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
PRIVATE_KEY=yyyy
2+
PRIVATE_KEY1=yyyy
3+
PRIVATE_KEY2=yyyy
24
INFURA_ID=zzzz
35
PROJECT_ID=xxxxx
46
TARGET_ACCOUNT=mmm
Lines changed: 122 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Merkle Distributor Airdrop
2+
13
## 介绍
24

35
### NFT merkel airdrop
@@ -13,9 +15,11 @@
1315
### ERC20 merkel airdrop
1416

1517
参考 1inch,dydx,uniswap 都实现 merkle 空投。 具体原理请参考:
16-
https://itzone.com.vn/en/article/merkle-airdrop-the-airdrop-solution-for-token-issues/
1718

18-
### 抢红包
19+
- <https://itzone.com.vn/en/article/merkle-airdrop-the-airdrop-solution-for-token-issues/>
20+
21+
### 抢红包
22+
1923
本样例演示了抢红包合约的功能, 在节假日的时候可以部署相应的合约进行红包发放.
2024
对应合约路径为 contracts/redpacket
2125

@@ -24,159 +28,164 @@ https://itzone.com.vn/en/article/merkle-airdrop-the-airdrop-solution-for-token-i
2428
- ERC721Basic
2529
最简单的 NFT 空投合约, 项目方直接调用 mint 接口, 对指定账户投放 NFT
2630

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+
```
3337

3438
- ERC721LazyMint
3539
经过验证的空投方式. 可能在下面这种场景中出现, 比如项目方准备空投 NFT 给一些用户, 那么项目方先发送一个邮件给该用户, 邮件中包含此 NFT 的 tokenID. 用户收到邮件后, 在线下根据 toukenID 和 账户地址进行签名, 然后发送给项目方. 项目方拿到用户签名后, 调用空投合约的 redeem 接口, 传入 account, tokenId, signature. 其中 account, tokenId 需要从预先保留的项目方数据库中获取, 以验证该用户确实对应该 NFT. 如果验证通过, 则生成一个 NFT 给该用户.
3640

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));
4044

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+
```
4650

4751
- ERC721LazyMintWith712
4852
在传统的签名方式中, 我们直接调用签名接口, 传入需要的签名参数, 这个过程中我们无法直观的感知需要签名具体参数意义, 特别是当 MetaMask 弹出提示, 需要你对一笔数据进行签名时, 如果不能结构化的看到需要签名的具体数据, 我们可能会拒绝签名这笔交易.
4953
EIP-712 就是在用户签名时把结构化数据展示给他们确认的场景. 之后链上确认签名是否正确, 过程和 ERC721LazyMint 类似
5054
链下签名样例如下:
5155

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+
```
7175

7276
- ERC721LazyMintWith712SignatureChecker
7377
和 ERC721LazyMintWith712 类似, 唯一的区别就是在链上进行验证时, 增加了 SignatureChecker
7478

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+
```
8286

8387
- ERC721MerkleDrop
8488
链下生成 Merkle 证明, 之后把 Merkle 证明发送到链上进行验证, 验证通过后, 就会给用户生成相应的 NFT token
8589

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));
8993

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+
```
9599

96100
## 测试流程
97-
### Merkle airdrop
101+
102+
### Merkle airdrop
103+
98104
- 安装依赖
99105

100-
```bash
101-
yarn
102-
```
106+
```bash
107+
yarn
108+
```
103109

104110
- 执行测试程序
105111

106-
```bash
107-
npx hardhat test
108-
```
112+
```bash
113+
npx hardhat test
114+
```
109115

110-
### 抢红包
111-
- 配置环境环境变量
112-
```shell
113-
cp .env.exmpale .env
116+
### HappyRedPacket
114117

115-
## 在 .env 文件中配置 PRIVATE_KEY, INFURA_ID, PROJECT_ID, TARGET_ACCOUNT
116-
## 比如有一个账户 A , 那么 PRIVATE_KEY 为账户 A 对应的 PRIVATE_KEY, TARGET_ACCOUNT 为账户 A 对应的账户地址
117-
```
118+
- 配置环境环境变量
118119

119-
- 配置 redpacketAddressList
120-
因为红包领取时会进行 Merkle 校验, 所以需要在 Merkle List 中配置对应的账户地址.
121-
修改 scripts/redpacket/redpacketAddressList.json 文件,在其中加入上一步操作中 "TARGET_ACCOUNT" 的值.
120+
```shell
121+
cp .env.exmpale .env
122122

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+
```
127133

128134
- 部署 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
132139

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+
```
139146

140147
- 部署 RedPacket 合约
141-
执行如下命令,然后获取输出的 "RedPacket address" 值
142-
```shell
143-
npx hardhat run scripts/redpacket/2-deployHappyRedPacket.js --network kovan
148+
执行如下命令,然后获取输出的 "RedPacket address" 值
144149

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+
```
150158

151159
- 创建红包
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"
155161

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
162164

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+
```
167171

168-
## 得到的输出 "Sign Message:" 即为领取红包时需要输入的签名信息,防止恶意领取
169-
```
172+
- 领取红包
173+
修改 "4-claimRedpacket.js" 文件中的 "HappyRedPacketAddress" 和 "redpacketID" 值为上面的输出值, 然后执行下面的命令进行红包的 claim
170174

171-
## 参考链接
175+
```shell
176+
npx hardhat run scripts/redpacket/4-claimRedpacket.js --network kovan
172177

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+
## 参考链接
182182

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>

basic/42-merkle-distributor-airdrop/hardhat.config.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const settings = {
1111
};
1212

1313
function mnemonic() {
14-
return process.env.PRIVATE_KEY;
14+
return [process.env.PRIVATE_KEY, process.env.PRIVATE_KEY1, process.env.PRIVATE_KEY2];
1515
}
1616

1717
/**
@@ -37,23 +37,23 @@ module.exports = {
3737
},
3838
rinkeby: {
3939
url: 'https://rinkeby.infura.io/v3/' + process.env.INFURA_ID, //<---- YOUR INFURA ID! (or it won't work)
40-
accounts: [mnemonic()],
40+
accounts: mnemonic(),
4141
},
4242
kovan: {
4343
url: 'https://kovan.infura.io/v3/' + process.env.INFURA_ID, //<---- YOUR INFURA ID! (or it won't work)
44-
accounts: [mnemonic()],
44+
accounts: mnemonic(),
4545
},
4646
mainnet: {
4747
url: 'https://mainnet.infura.io/v3/' + process.env.INFURA_ID, //<---- YOUR INFURA ID! (or it won't work)
48-
accounts: [mnemonic()],
48+
accounts: mnemonic(),
4949
},
5050
ropsten: {
5151
url: 'https://ropsten.infura.io/v3/' + process.env.INFURA_ID, //<---- YOUR INFURA ID! (or it won't work)
52-
accounts: [mnemonic()],
52+
accounts: mnemonic(),
5353
},
5454
matic: {
5555
url: 'https://polygon-mainnet.infura.io/v3/' + process.env.PROJECT_ID,
56-
accounts: [mnemonic()]
56+
accounts: mnemonic()
5757
},
5858
},
5959
gasReporter: {

basic/42-merkle-distributor-airdrop/scripts/redpacket/1-deploySimpleToken.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,27 @@
44
// When running the script with `hardhat run <script>` you'll find the Hardhat
55
// Runtime Environment's members available in the global scope.
66
const { ethers } = require('hardhat');
7+
const { saveRedpacketDeployment } = require('../../utils');
78

89
async function main() {
910
const [deployer] = await ethers.getSigners();
1011

1112
console.log('Deploying contracts with the account:', deployer.address);
1213

13-
console.log('Account balance:', (await deployer.getBalance()).toString());
14+
// console.log('Account balance:', (await deployer.getBalance()).toString());
1415

1516
const Token = await ethers.getContractFactory('SimpleToken');
16-
const token = await Token.deploy('DappLearning', 'DL', 1, 100000000);
17+
const token = await Token.deploy('DappLearning', 'DL', 18, 1000000);
1718
await token.deployed();
1819

1920
console.log('Token address:', token.address);
2021

2122
let balance = await token.balanceOf(deployer.address);
22-
console.log(balance.toString());
23+
console.log(`balance of deployer ${balance.toString()}`);
24+
25+
saveRedpacketDeployment({
26+
simpleTokenAddress: token.address,
27+
});
2328
}
2429

2530
// We recommend this pattern to be able to use async/await everywhere

basic/42-merkle-distributor-airdrop/scripts/redpacket/2-deployHappyRedPacket.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// When running the script with `hardhat run <script>` you'll find the Hardhat
55
// Runtime Environment's members available in the global scope.
66
const { ethers } = require('hardhat');
7+
const { saveRedpacketDeployment } = require('../../utils');
78

89
async function main() {
910
const [deployer] = await ethers.getSigners();
@@ -22,7 +23,13 @@ async function main() {
2223
let initRecipt = await redPacket.initialize();
2324
await initRecipt.wait();
2425

25-
console.log("Init HappyRedPacket successfully");
26+
// save contract address to file
27+
saveRedpacketDeployment({
28+
redPacketAddress: redPacket.address,
29+
redPacketOwner: deployer.address,
30+
})
31+
32+
console.log('Init HappyRedPacket successfully');
2633
}
2734

2835
// We recommend this pattern to be able to use async/await everywhere

0 commit comments

Comments
 (0)