Audit of Snowman Merkle Airdrop contracts revealing 1 High, 3 Medium, and 2 Low severity vulnerabilities.
SnowmanAirdrop.sol, Snow.sol, Snowman.sol
High Risk
SnowmanAirdrop.claimSnowmanFunction sends tokens before updating the s_hasClaimedSnowman mapping.
This violates the CEI pattern, permitting reentrancy via token hooks.
Likelihood: Malicious token contract or hook triggers during transferFrom.
Impact: Attacker recursively claims Snowman NFTs, draining the total supply.
Attacker deploys contract with a tokensReceived hook (ERC777-style) or similar malicious logic.
Attacker calls claimSnowman.
Inside safeTransferFrom, the attacker's hook re-calls claimSnowman.
Since s_hasClaimedSnowman is still false, the second call succeeds.
Repeat until the NFT supply is exhausted or gas limits are met.
Update state before external interactions.
SnowmanAirdrop_isValidSignature lacks "low-s" enforcement, allowing valid non-canonical signatures.
Likelihood: Attacker modifies s and v to create a valid alternative signature.
Impact: Potential for replay attacks if specific signatures aren't tracked and invalidated.
An attacker can calculate a second valid signature (r, -s mod n) for any valid user signature. If s is greater than n/2, it can be flipped to n - s, and v toggled between 27 and 28. Both will recover to the same address but have different bytes.
Check that s is in the lower half of the curve order.
Snow.earnSnowearnSnow uses a global s_earnTimer, restricting minting to one user/address per week project-wide.
Likelihood: Multiple addresses controlled by one user or competitive users.
Impact: First user blocks all others for a week; Sybil attacks bypass individual intent.
User A calls earnSnow() with Address 1. s_earnTimer is set to now.
User B (or User A with Address 2) tries to call earnSnow().
Transaction reverts because block.timestamp is less than s_earnTimer + 1 week.
Result: Only 1 token total can be earned per week across all users.
Use a per-user mapping(address => uint256) for timers.
buySnowMultiplication of s_buyFee * amount can overflow.
Likelihood: High s_buyFee or extreme amount values.
Impact: Unexpected transaction reverts or logic failure in contract accounting.
If s_buyFee = 1e18 and amount = 2**256 / 1e18 + 1, the multiplication overflows. In Solidity 0.8+, this reverts automatically, but the lack of explicit handling can lead to poor UX or opaque integration bugs.
Assign to variable and check or rely on Solidity 0.8+ reverts explicitly.
MESSAGE_TYPEHASHDescription: Typo "addres" in EIP-712 string.
Mitigation: Correct "addres" to "address".
SnowDescription: Hardcoded 1 in _mint.
Mitigation: Define uint256 private constant EARN_AMOUNT = 1;.
The protocol requires immediate fixes for reentrancy and distribution logic to ensure security.
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.