makeMerkle.js builds the Merkle tree with amount = (25 * 1e18).toString() (18 decimals), but the airdrop token is USDC on zkSync Era, which has 6 decimals and only 4 * (25 * 1e6) = 100e6 is ever funded (Deploy.s.sol:11). The deployed root 0xf69aaa25... therefore commits each winner to 25e18 = 25 trillion USDC units, while the contract holds at most 100e6. Every claim requires proof for amount = 25e18; a winner passing the real 25 USDC (25e6) fails MerkleProof.verify, and passing 25e18 requires a balance the contract can never have. The airdrop is entirely unclaimable and the funded USDC is stuck.
makeMerkle.js:7 — const amount = (25 * 1e18).toString() (out of scope, but the docs state it generates the root used by Deploy.s.sol).
Deploy.s.sol:9 — s_merkleRoot = 0xf69aaa25... reproduces the 25e18 tree root exactly.
Deploy.s.sol:11 — s_amountToAirdrop = 4 * (25 * 1e6) = 100e6 (6-decimal USDC).
MerkleAirdrop.sol:30-40 — claim(account, amount, proof) verifies amount against i_merkleRoot. With the deployed root, valid amount must be 25e18.
Magnitude: a single claim at 25e18 requires 2.5e11× the entire funded balance (100e6); all four claims need 1e12×. The contract can never satisfy a valid claim.
Root confirmed reproducible: node makeMerkle.js → 0xf69aaa25bd4dd10deb2ccd8235266f7cc815f6e9d539e9f4d47cae16e0c36a05 (matches Deploy.s.sol:9).
The airdrop is 100% non-functional: no winner can ever claim their 25 USDC.
The entire 100e6 USDC is permanently locked in the contract. The only ETH-withdrawal path is claimFees (owner-only, ETH only); there is no function to recover airdrop tokens. Funds are lost.
Passing Forge tests:
Manual review; node makeMerkle.js (root/leaf reproduction); Foundry PoCs.
Fix the off-chain amount to 6 decimals and rebuild the tree + root:
Update Deploy.s.sol:9 to the regenerated root and fund the matching balance. As hardening, cross-check root ↔ token decimals at deploy time.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.