Normal behavior: _isValidSignature() should validate that a signature was produced by the expected receiver address.
The issue: ECDSA.tryRecover() returns three values: (address signer, RecoverError error, bytes32 errorArg). The function only captures the first value and ignores the error code:
(address actualSigner,,) = ECDSA.tryRecover(digest, v, r, s);
If tryRecover() fails due to an invalid signature (malformed, wrong length, etc.), it returns address(0) as the signer and a non-zero error code. The function then checks actualSigner == receiver. If receiver is somehow address(0) (which is guarded against), this would pass. More critically, the error state is completely invisible and the caller has no way to distinguish a valid recovery failure from a genuine signer mismatch.
Likelihood:
Any caller can submit a malformed signature and tryRecover returns address(0) with no error propagation.
The signature verification path is the primary security gate for airdrop claims.
Impact:
Signature validation errors are silently swallowed and no distinction between wrong signer and invalid signature.
Combined with the MESSAGE_TYPEHASH typo, signature verification is doubly broken.
The following demonstrates that _isValidSignature() silently ignores
ECDSA error codes. When tryRecover() fails due to a malformed signature,
it returns address(0) with a non-zero error code. The function only checks
the returned address, making it impossible to distinguish between a wrong
signer and a completely invalid signature. Combined with the MESSAGE_TYPEHASH
typo, this means signature verification is broken at two independent levels.
The fix has two options depending on the desired behavior. Option A uses
ECDSA.recover() which reverts on any invalid signature — this is the
simplest and most secure approach, as invalid signatures immediately halt
execution rather than returning a potentially misleading false. Option B
preserves tryRecover() but explicitly checks the error code before trusting
the recovered address and this is appropriate if the caller needs to handle
invalid signatures gracefully without reverting. Either option eliminates
the silent failure. Option A is recommended for this use case since
claimSnowman() already reverts on invalid signatures via SA__InvalidSignature.
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.