AirDropper

AI First Flight #5
Beginner FriendlyDeFiFoundry
EXP
View results
Submission Details
Severity: high
Valid

Incorrect Hardcoded Token Address in Deployment Script Can Break Airdrop Funding

Incorrect Hardcoded Token Address in Deployment Script Can Break Airdrop Funding

Description

The deployment script responsible for initializing and funding the MerkleAirdrop contract contains an inconsistency in the token address used for transferring funds.

contract Deploy is Script {
address public s_zkSyncUSDC = 0x1D17CbCf0D6d143135be902365d2e5E2a16538d4;
bytes32 public s_merkleRoot = 0xf69aaa25bd4dd10deb2ccd8235266f7cc815f6e9d539e9f4d47cae16e0c36a05;
uint256 public s_amountToAirdrop = 4 * (25 * 1e6);
function run() public {
vm.startBroadcast();
MerkleAirdrop airdrop = deployMerkleDropper(s_merkleRoot, IERC20(s_zkSyncUSDC));
// Hardcoded address does not match s_zkSyncUSDC
IERC20(0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4)
.transfer(address(airdrop), s_amountToAirdrop);
vm.stopBroadcast();
}
}

Although the script correctly deploys the MerkleAirdrop contract using s_zkSyncUSDC, it transfers tokens from a separately hardcoded address that differs from the declared variable.

As a result, the airdrop contract may end up receiving tokens from an unintended contract while the intended token (s_zkSyncUSDC) is never transferred. This can leave the airdrop deployed in an unfunded or unusable state. In the worst case, tokens may be mistakenly transferred from or to an unintended contract, creating operational confusion and requiring redeployment.


Risk

Likelihood: Medium

Deployment scripts are executed manually or during CI/CD processes, and such inconsistencies are easy to overlook, especially when addresses visually resemble each other. Since both addresses appear similar, the mistake may not be immediately detected before broadcasting the transaction.

Impact: Medium

If the incorrect token address is used during funding, the deployed airdrop contract may remain unfunded or funded with the wrong asset, breaking the distribution process. While this does not directly compromise contract security, it can lead to failed launches, incorrect token transfers, or operational disruption requiring redeployment.


Proof of Concept

The issue is directly observable in the deployment script:

  • s_zkSyncUSDC is defined as:

0x1D17CbCf0D6d143135be902365d2e5E2a16538d4
  • The actual transfer uses:

0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4

Since these addresses are not identical, the funding step does not reference the same token contract that was used during deployment.

This confirms that the script does not consistently use the intended token address.


Recommended Mitigation

Avoid hardcoded duplicate addresses and consistently reference the declared variable throughout the deployment script.

Replace:

- IERC20(0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4)
.transfer(address(airdrop), s_amountToAirdrop);

With:

+ IERC20(s_zkSyncUSDC)
.transfer(address(airdrop), s_amountToAirdrop);

Or, even better, reuse the already typed variable:

IERC20 token = IERC20(s_zkSyncUSDC);
token.transfer(address(airdrop), s_amountToAirdrop);

This approach establishes a single source of truth for the token address and eliminates the risk of silent copy-paste inconsistencies. It also improves the overall safety and maintainability of the deployment logic by ensuring that all references to the token are derived from the same variable.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 7 hours ago
Submission Judgement Published
Validated
Assigned finding tags:

[H-01] Address of USDC token in `Deploy.s.sol` is wrong causing the claiming process to fail

## 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; ```

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!