Snowman Merkle Airdrop

First Flight #42
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Incorrect typehash in MESSAGE_TYPEHASH constant

Description: There's a critical typo in the MESSAGE_TYPEHASH constant definition that will cause all signature verifications to fail:

bytes32 private constant MESSAGE_TYPEHASH = keccak256("SnowmanClaim(addres receiver, uint256 amount)");

The type string contains "addres" instead of "address" for the receiver parameter. This mismatch means that the hash used for EIP-712 signing in the contract won't match what users are actually signing in their wallets. As a result, the signature verification in _isValidSignature will fail for all users attempting to claim their Snowman NFTs, making the entire airdrop system unusable.

Impact: EIP-712 signatures are extremely sensitive to the exact string used in the type hash. Even a single character difference will produce a completely different hash, causing signature verification to fail.

Proof of Concept: Include the following test in the TestSnowmanAirdrop.t.sol file:

import {Offline} from "../script/Offline.s.sol";
contract TestSnowmanAirdrop is Test {
// ...
Offline offline;
// ...
function setUp() public {
// ...
offline = new Offline();
// ...
}
function testSignatureFails() public {
vm.prank(alice);
snow.approve(address(airdrop), 1);
bytes32 alDigest = offline.getMessageHash(alice);
(uint8 alV, bytes32 alR, bytes32 alS) = vm.sign(alKey, alDigest);
vm.expectRevert(SnowmanAirdrop.SA__InvalidSignature.selector);
airdrop.claimSnowman(alice, AL_PROOF, alV, alR, alS);
}

Create a script 'Offline.s.sol' file in the 'script' folder with the following code:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Script} from "forge-std/Script.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
contract Offline is Script, EIP712("Snowman Airdrop", "1") {
struct SnowmanClaim {
address receiver;
uint256 amount;
}
bytes32 private constant MESSAGE_TYPEHASH = keccak256("SnowmanClaim(address receiver, uint256 amount)");
function getMessageHash(address receiver) public view returns (bytes32) {
uint256 amount = 1; // Assuming a fixed amount of 1 for the claim
return _hashTypedDataV4(
keccak256(abi.encode(MESSAGE_TYPEHASH, SnowmanClaim({receiver: receiver, amount: amount})))
);
}
}

Recommended Mitigation: Fix the typo in the MESSAGE_TYPEHASH:

bytes32 private constant MESSAGE_TYPEHASH = keccak256("SnowmanClaim(address receiver, uint256 amount)");
Updates

Lead Judging Commences

yeahchibyke Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Inconsistent MESSAGE_TYPEHASH with standard EIP-712 declaration

A typo in the `MESSAGE_TYPEHASH` variable of the `SnowmanAirdrop` contract will prevent signature verification claims. Used `addres` instead of `address`

Support

FAQs

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