Snowman Merkle Airdrop

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

Incorrect ABI Encoding Offset Assumption

Root + Impact

Description

  • Merkle leaf hashes should be calculated consistently using proper ABI encoding to ensure proof verification works correctly.

    The script assumes abi.encode(bytes32[]) always produces exactly 64 bytes of prefix (offset + length), but this can vary based on Solidity version and array contents.

// Root cause in the codebase with @> marks to highlight the relevant section
// SnowMerkle.sol
leafs[i] = keccak256(bytes.concat(keccak256(ltrim64(abi.encode(data)))));
// @> ltrim64 removes exactly 64 bytes, but encoding might not always have 64-byte prefix
// @> This creates incorrect leaf hashes

Risk

Likelihood:

  • Occurs when ABI encoding format doesn't match the 64-byte assumption

  • Solidity compiler optimizations can change encoding structure

Impact:

  • Invalid Merkle leaf hashes lead to proof verification failures

  • All users unable to claim despite having correct data

Proof of Concept

Encode Alice's data [bytes32(uint160(alice)), bytes32(1)] using abi.encode(data) then apply ltrim64 - if the compiler generates different offset lengths (not exactly 64 bytes), the resulting leaf hash won't match 0x51c4b9a3cc313d7d7325f2d5d9e782a5a484e56a38947ab7eea7297ec86ff138 from output.json, breaking all proof verifications.

function testIncorrectABIEncoding() public {
// Replicate SnowMerkle leaf generation logic
bytes32[] memory data = new bytes32[](2);
data[0] = bytes32(uint256(uint160(alice))); // Address as bytes32
data[1] = bytes32(uint256(1)); // Amount as bytes32
bytes memory encoded = abi.encode(data);
// Expected leaf from output.json for Alice:
// 0x51c4b9a3cc313d7d7325f2d5d9e782a5a484e56a38947ab7eea7297ec86ff138
// ltrim64 assumes first 64 bytes are offset+length
bytes memory trimmed = ltrim64(encoded);
bytes32 leaf = keccak256(bytes.concat(keccak256(trimmed)));
// If encoding format changes, leaf hash will be wrong
// This could happen with different Solidity versions or compiler settings
// Making all proofs in output.json invalid
}

Recommended Mitigation

Replace dynamic encoding with direct packed encoding: leafs[i] = keccak256(abi.encodePacked(keccak256(abi.encodePacked(data[0], data[1])))) to eliminate offset assumptions and ensure consistent hashing across compiler versions.

- remove this code
+ add this code
- leafs[i] = keccak256(bytes.concat(keccak256(ltrim64(abi.encode(data)))));
+ leafs[i] = keccak256(abi.encodePacked(keccak256(abi.encodePacked(data[0], data[1])))); // Direct packed encoding
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!