PoC
Add your zksync rpc url ZKSYNC_MAINNET_RPC_URL
to .env
file.
Add this helper code to Deploy.s.sol
so our test can capture the address of MerkleAirdrop
and IERC20
contract:
- function run() public {
+ function run() public returns (MerkleAirdrop, IERC20) {
vm.startBroadcast();
MerkleAirdrop airdrop = deployMerkleDropper(s_merkleRoot, IERC20(s_zkSyncUSDC));
// Send USDC -> Merkle Air Dropper
IERC20(0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4).transfer(address(airdrop), s_amountToAirdrop);
vm.stopBroadcast();
+ // add helper
+ return (airdrop, IERC20(s_zkSyncUSDC));
}
Add MerkleAirdropDeployScriptTest.t.sol
to test
folder.
MerkleAirdropDeployScriptTest.t.sol
:
pragma solidity 0.8.24;
import { MerkleAirdrop, IERC20 } from "../src/MerkleAirdrop.sol";
import { AirdropToken } from "./mocks/AirdropToken.sol";
import { Deploy } from "script/Deploy.s.sol";
import { Test } from "forge-std/Test.sol";
import { console } from "forge-std/Test.sol";
contract MerkleAirdropTest is Test, Deploy {
MerkleAirdrop public airdrop;
IERC20 public tokenIERC20;
AirdropToken public token;
uint256 amountToCollect = (25 * 1e6);
uint256 amountToSend = amountToCollect * 4;
address collectorOne = 0x20F41376c713072937eb02Be70ee1eD0D639966C;
bytes32 proofOne = 0x4fd31fee0e75780cd67704fbc43caee70fddcaa43631e2e1bc9fb233fada2394;
bytes32 proofTwo = 0xc88d18957ad6849229355580c1bde5de3ae3b78024db2e6c2a9ad674f7b59f84;
bytes32[] public proof = [proofOne, proofTwo];
function setUp() public {
address correctUSDCAddress = 0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4;
deal(address(IERC20(correctUSDCAddress)), address(msg.sender), amountToSend);
console.log("USDC before:", IERC20(correctUSDCAddress).balanceOf(address(msg.sender)));
(airdrop, tokenIERC20) = Deploy.run();
console.log("USDC after:", IERC20(correctUSDCAddress).balanceOf(address(msg.sender)));
}
function testPOCUsingDeployScriptContractBrokenBecauseWrongMerkleRoot() public {
uint256 startingBalance = tokenIERC20.balanceOf(collectorOne);
vm.deal(collectorOne, airdrop.getFee());
vm.startPrank(collectorOne);
airdrop.claim{ value: airdrop.getFee() }(collectorOne, amountToCollect, proof);
vm.stopPrank();
uint256 endingBalance = tokenIERC20.balanceOf(collectorOne);
assertEq(endingBalance - startingBalance, amountToCollect);
}
after that run the following forge test --zksync --fork-url $ZKSYNC_MAINNET_RPC_URL --mt testPOCUsingDeployScriptContractBrokenBecauseWrongMerkleRoot
the test will FAIL:
Failing tests:
Encountered 1 failing test in test/MerkleAirdropDeployScriptTest.t.sol:MerkleAirdropTest
[FAIL. Reason: MerkleAirdrop__InvalidProof()] testPOCUsingDeployScriptContractBrokenBecauseWrongMerkleRoot() (gas: 32340)
Changing the correct Merkle Root can solve this problem.
Code
Using modified makeMerkle.js
we can generate the correct value:
.
.
.
/*//////////////////////////////////////////////////////////////
INPUTS
//////////////////////////////////////////////////////////////*/
- const amount = (25 * 1e18).toString();
+ const amount = (25 * 1e6).toString();
const userToGetProofOf = "0x20F41376c713072937eb02Be70ee1eD0D639966C";
// (1)
const values = [
[userToGetProofOf, amount],
["0x277D26a45Add5775F21256159F089769892CEa5B", amount],
["0x0c8Ca207e27a1a8224D1b602bf856479b03319e7", amount],
["0xf6dBa02C01AF48Cf926579F77C9f874Ca640D91D", amount],
];
/*//////////////////////////////////////////////////////////////
PROCESS
//////////////////////////////////////////////////////////////*/
// (2)
const tree = StandardMerkleTree.of(values, ["address", "uint256"]);
// (3)
console.log("Merkle Root:", tree.root);
// (4)
for (const [i, v] of tree.entries()) {
// if (v[0]
// (3)
const proof = tree.getProof(i);
+ console.log(`Proof for address: ${v} with amount: ${amount}:\n`, proof);
- console.log(`Proof for address: ${userToGetProofOf} with amount: ${amount}:\n`, proof);
- }
}
.
.
.
after that run yarn run makeMerkle
and the following should returned:
Merkle Root: 0x3b2e22da63ae414086bec9c9da6b685f790c6fab200c7918f2879f08793d77bd
Proof for address: 0x20F41376c713072937eb02Be70ee1eD0D639966C,25000000 with amount: 25000000:
[
'0x32cee63464b09930b5c3f59f955c86694a4c640a03aa57e6f743d8a3ca5c8838',
'0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c'
]
Proof for address: 0x277D26a45Add5775F21256159F089769892CEa5B,25000000 with amount: 25000000:
[
'0x2683f462a4457349d6d7ef62d4208ef42c89c2cff9543cd8292d9269d832c3e8',
'0xdcad361f30c4a5b102a90b4ec310ffd75c577ccdff1c678adb20a6f02e923366'
]
Proof for address: 0x0c8Ca207e27a1a8224D1b602bf856479b03319e7,25000000 with amount: 25000000:
[
'0xee1cda884ead2c9f34338f48263e7edd6e5f35bf4f09c9c0930d995911004eed',
'0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c'
]
Proof for address: 0xf6dBa02C01AF48Cf926579F77C9f874Ca640D91D,25000000 with amount: 25000000:
[
'0x1e6784ff835523401f4db6e3ab48fa5bdf523a46a5bc0410a5639d837352b194',
'0xdcad361f30c4a5b102a90b4ec310ffd75c577ccdff1c678adb20a6f02e923366'
]
using the correct value above, we should change the Deploy.s.sol
value of s_merkleRoot
:
Deploy.s.sol
:
- bytes32 public s_merkleRoot = 0xf69aaa25bd4dd10deb2ccd8235266f7cc815f6e9d539e9f4d47cae16e0c36a05;
+ bytes32 public s_merkleRoot = 0x3b2e22da63ae414086bec9c9da6b685f790c6fab200c7918f2879f08793d77bd;
// 4 users, 25 USDC each
uint256 public s_amountToAirdrop = 4 * (25 * 1e6);
after that add the following code to MerkleAirdropDeployScriptTest.t.sol
that we already created beforehand.
MerkleAirdropDeployScriptTest.t.sol
:
function testUsingDeployScriptContractCorrectMerkleRoot() public {
bytes32[] memory correctProofs = new bytes32[](8);
correctProofs[0] = 0x32cee63464b09930b5c3f59f955c86694a4c640a03aa57e6f743d8a3ca5c8838;
correctProofs[1] = 0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c;
correctProofs[2] = 0x2683f462a4457349d6d7ef62d4208ef42c89c2cff9543cd8292d9269d832c3e8;
correctProofs[3] = 0xdcad361f30c4a5b102a90b4ec310ffd75c577ccdff1c678adb20a6f02e923366;
correctProofs[4] = 0xee1cda884ead2c9f34338f48263e7edd6e5f35bf4f09c9c0930d995911004eed;
correctProofs[5] = 0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c;
correctProofs[6] = 0x1e6784ff835523401f4db6e3ab48fa5bdf523a46a5bc0410a5639d837352b194;
correctProofs[7] = 0xdcad361f30c4a5b102a90b4ec310ffd75c577ccdff1c678adb20a6f02e923366;
address[4] memory eligibleAddress = [
0x20F41376c713072937eb02Be70ee1eD0D639966C,
0x277D26a45Add5775F21256159F089769892CEa5B,
0x0c8Ca207e27a1a8224D1b602bf856479b03319e7,
0xf6dBa02C01AF48Cf926579F77C9f874Ca640D91D
];
for (uint256 i = 0; i < eligibleAddress.length; ++i) {
uint256 startingBalance = tokenIERC20.balanceOf(eligibleAddress[i]);
vm.deal(eligibleAddress[i], airdrop.getFee());
bytes32[] memory proofs = new bytes32[](2);
proofs[0] = correctProofs[i * 2];
proofs[1] = correctProofs[i * 2 + 1];
vm.startPrank(eligibleAddress[i]);
airdrop.claim{ value: airdrop.getFee() }(eligibleAddress[i], amountToCollect, proofs);
vm.stopPrank();
uint256 endingBalance = tokenIERC20.balanceOf(eligibleAddress[i]);
assertEq(endingBalance - startingBalance, amountToCollect);
}
}
then run the following command forge test --zksync --fork-url $ZKSYNC_MAINNET_RPC_URL --mt testUsingDeployScriptContractCorrectMerkleRoot
the test result PASS:
Ran 1 test for test/MerkleAirdropDeployScriptTest.t.sol:MerkleAirdropTest
[PASS] testUsingDeployScriptContractCorrectMerkleRoot() (gas: 63544)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 76.19s (23.89s CPU time)