Snowman Merkle Airdrop

AI First Flight #10
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

State Isolation Between Script Executions

Root + Impact

Description

Scripts should generate a Merkle tree based on actual token balances that will exist when the airdrop contract is deployed. Users should be able to claim tokens using valid Merkle proofs that match their real balances.

// Root cause in the codebase with @> marks to highlight the relevant section
// GenerateInput.sol
function _createJSON() internal returns (string memory) {
helper = new Helper();
helper.run(); // @> Creates NEW temporary contract instances
snowAmountAlice = helper.aliceSB(); // @> Gets balances from temporary contracts
snowAmountBob = helper.bobSB();
// These balances are from contracts that won't exist in production
}

Risk

Likelihood:

  • Every script execution creates isolated blockchain state in Foundry

  • The deployment will always create different contract instances than the test instances

Impact:

  • Complete airdrop system failure - no user can claim tokens

  • Merkle proofs are valid for non-existent contract state

  • Production contract has zero connection to the generated Merkle tree

Proof of Concept

Run GenerateInput to create balances in temporary contracts, then deploy actual airdrop which creates new Snow contract instances - the Merkle tree references addresses and balances that don't exist in the deployed contract state

function testStateIsolation() public {
// Run GenerateInput - creates Snow contract at address X
GenerateInput gen = new GenerateInput();
gen.run(); // Alice gets 100 tokens in contract X
// Run SnowMerkle - reads Alice=100, generates Merkle root
SnowMerkle merkle = new SnowMerkle();
merkle.run();
// Deploy actual airdrop - creates NEW Snow contract at address Y
DeploySnowmanAirdrop deployer = new DeploySnowmanAirdrop();
(SnowmanAirdrop airdrop, Snow snow,) = deployer.deploySnowmanAirdrop();
// Alice's actual balance is 0 in the new contract, but Merkle expects 100
assertEq(snow.balanceOf(alice), 0); // Passes - wrong state!
}

Recommended Mitigation

Pass the deployed Snow contract instance to _createJSON() and read actual on-chain balances instead of creating temporary Helper instances.

- remove this code
+ add this code
- function _createJSON() internal returns (string memory) {
- helper = new Helper();
- helper.run();
+ function _createJSON(Snow snow) internal view returns (string memory) {
+ // Read actual balances from deployed contract
+ snowAmountAlice = snow.balanceOf(alice);
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 13 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!