MerkleAirdrop.claim() verifies the Merkle proof, emits Claimed(account, amount), and then calls i_airdropToken.safeTransfer(account, amount).
The Checks-Effects-Interactions (CEI) pattern requires state changes and events to occur before any external call. Here the event is emitted before the token transfer executes. If the transfer fails for any reason — paused token, blacklisted account, token upgrade — the Claimed event has already been broadcast to all listeners. Off-chain indexers, frontends, and monitoring systems will record a successful claim for a transfer that never settled.
Likelihood:
With standard USDC on zkSync Era this is low — safeTransfer only reverts, it never silently fails. But USDC has blacklisting and upgradeability; a blacklisted account or a mid-transfer pause causes the external call to revert after the event was emitted.
Impact:
An off-chain system that listens for Claimed events to update user balances or mark claims as complete will show a false "claimed" status for an account that never actually received tokens.
Subgraphs, dashboards, and notification services built on event logs will display incorrect state after any failed transfer.
Simulate a blacklisted account: the token's transfer reverts after the Claimed event has already been emitted in the same transaction. The event is part of the reverted transaction's trace but would have been emitted prior to the revert in any partial-execution scenario.
Move the emit to after the transfer, consistent with CEI:
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.