File: src/MerkleAirdrop.sol constructor (line 25–28), script/Deploy.s.sol
The constructor accepts an arbitrary IERC20 airdropToken without verifying that the address contains deployed bytecode. On zkSync, if the USDC contract is not deployed at the expected address, or if Deploy.s.sol is accidentally run on a different chain (e.g., mainnet Ethereum where that address holds a different contract or EOA), the MerkleAirdrop contract will be silently initialized with a non-contract token address. All subsequent safeTransfer calls will either revert with obscure errors or silently succeed (depending on OpenZeppelin's SafeERC20 behavior with zero-code addresses).
In OZ v5 SafeERC20, calls to addresses with no code will revert due to the Address.functionCall check — however, this failure happens at runtime (during claim()) rather than at deployment. The contract is deployed "successfully" but is entirely non-functional, with no warning to the deployer.
Additionally, the Deploy.s.sol script has mismatched USDC addresses (MEDIUM-1 from first audit), meaning the constructor could receive one USDC address while transfer() is called on a different one — the contract is funded with the wrong token or not funded at all.
Contract deployed in broken state with no immediate indication.
Users call claim(), pay the fee, and always revert.
ETH fees accumulate but tokens are never distributed.
Silent misconfiguration is difficult to diagnose post-deployment on zkSync.
Manual analysis
OZ SafeERC20 v5 code review
Add a deployment-time sanity check:
And in Deploy.s.sol, add post-deployment assertions:
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.