The deploy script defines s_zkSyncUSDC as a configurable state variable on line 8. The deployMerkleDropper() call on line 16 correctly uses this variable. But the token transfer on line 18 hardcodes a different representation of the same address instead of referencing s_zkSyncUSDC:
The two addresses resolve to the same underlying value today (0x1d17...38d4), so the current deployment works. But the inconsistency means:
The MerkleAirdrop constructor receives s_zkSyncUSDC as its i_airdropToken
The funding transfer uses a hardcoded copy of that address
If s_zkSyncUSDC is updated for a different chain or token (e.g., USDC on a new L2), the constructor gets the new address but the transfer still sends from the old hardcoded one. The airdrop contract deploys pointing to the new token but receives zero funding.
Likelihood:
Any redeployment where s_zkSyncUSDC is changed to a different address triggers this inconsistency. The developer naturally updates the state variable and assumes all references follow, but the hardcoded address on line 18 silently stays stale.
The different EIP-55 checksums between line 8 and line 18 make this harder to catch visually during code review.
Impact:
The airdrop contract deploys with the correct i_airdropToken but receives zero tokens because the transfer targets the old hardcoded address. All claim() calls revert with insufficient balance, and the 100 USDC sits in the deploy script's address with no recovery path in the airdrop contract.
If the hardcoded address happens to be a different valid token on the new chain, the transfer silently succeeds but funds the contract with the wrong token entirely.
Visual confirmation of the inconsistency:
Replace the hardcoded address with the state variable so both the constructor and the transfer use the same single source of truth:
This ensures any future change to s_zkSyncUSDC propagates to both the MerkleAirdrop constructor and the token funding transfer.
## Description The `s_zkSyncUSDC` address in `Deploy.s.sol` is incorrectly set, leading to a failure in the claiming process. This error results in funds being stuck in the `MerkleAirdrop` contract due to the immutability of the token address. ## Impact All funds become permanently trapped in the `MerkleAirdrop` contract, rendering them inaccessible for claiming or transfer. **Proof of Concept:** To demonstrate the issue, a test contract can be added and executed using the following command: `forge test --zksync --rpc-url $RPC_ZKSYNC --mt testDeployOnZkSync` Use the RPC URL `https://mainnet.era.zksync.io` for testing. <details> <summary>Proof Of Code</summary> ```javascript // SPDX-License-Identifier: MIT pragma solidity 0.8.24; import { MerkleAirdrop, IERC20 } from "../src/MerkleAirdrop.sol"; import { Test, console2 } from "forge-std/Test.sol"; contract MerkleAirdropTest is Test { MerkleAirdrop public s_airdrop; uint256 s_amountToCollect = (25 * 1e6); // 25.000000 address s_collectorOne = 0x20F41376c713072937eb02Be70ee1eD0D639966C; bytes32 s_proofOne = 0x32cee63464b09930b5c3f59f955c86694a4c640a03aa57e6f743d8a3ca5c8838; bytes32 s_proofTwo = 0x8ff683185668cbe035a18fccec4080d7a0331bb1bbc532324f40501de5e8ea5c; bytes32[] s_proof = [s_proofOne, s_proofTwo]; address public deployer; // From Deploy.t.sol bytes32 public s_merkleRoot = 0x3b2e22da63ae414086bec9c9da6b685f790c6fab200c7918f2879f08793d77bd; address public s_zkSyncUSDC = 0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4; uint256 public s_amountToAirdrop = 4 * (25 * 1e6); function setUp() public { deployer = makeAddr("deployer"); deal(0x1D17CbCf0D6d143135be902365d2e5E2a16538d4, deployer, 100 * 1e6); vm.deal(s_collectorOne, 100 ether); } function testDeployOnZkSync() public { if (block.chainid != 324) { return; } vm.startPrank(deployer); // From here there is the code from run() s_airdrop = deployMerkleDropper(s_merkleRoot, IERC20(s_zkSyncUSDC)); // Send USDC -> Merkle Air Dropper IERC20(0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4).transfer(address(s_airdrop), s_amountToAirdrop); // end code from run vm.stopPrank(); vm.startPrank(s_collectorOne); s_airdrop.claim{ value: s_airdrop.getFee() }(s_collectorOne, s_amountToCollect, s_proof); vm.stopPrank(); } function deployMerkleDropper(bytes32 merkleRoot, IERC20 zkSyncUSDC) public returns (MerkleAirdrop) { return (new MerkleAirdrop(merkleRoot, zkSyncUSDC)); } } ``` </details> ## Recommendations To resolve the issue, update the s_zkSyncUSDC address in Deploy.s.sol to the correct value: ```diff - address public s_zkSyncUSDC = 0x1D17CbCf0D6d143135be902365d2e5E2a16538d4; + address public s_zkSyncUSDC = 0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4; ```
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.