Santa's List

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

buyPresent() Allows Unlimited NFT Purchases, Bypassing "1 NFT Per User" Rule

Root + Impact

The buyPresent() function has no check to prevent minting multiple NFTs to the same address, breaking the protocol's documented rule that each user should receive only one NFT. This allows users to bypass the duplicate prevention that exists in collectPresent().

Description

  • Protocol implements "1 NFT per user" rule in collectPresent() function

buyPresent() has no such check, allowing unlimited NFTs

  • Inconsistency where legitimate users can only get 1 NFT, but anyone with tokens can buy unlimited

// src/SantasList.sol - Lines 107-109
function collectPresent() external {
// ...
if (balanceOf(msg.sender) > 0) {
revert SantasList__AlreadyCollected(); // ✅ Prevents duplicates
}
// ...
}
// src/SantasList.sol - Lines 128-131
function buyPresent(address presentReceiver) external {
i_santaToken.burn(presentReceiver); // ❌ No balance check!
_mintAndIncrement(); // ❌ No duplicate prevention
}

Risk

Likelihood:

  • Anyone with tokens can exploit this

No conditions needed beyond having tokens

  • Can be repeated unlimited times

Impact:

  • Protocol rule "1 NFT per user" is bypassable

Unlimited NFTs can be minted

  • Unfair advantage for users with tokens

  • Combined with Finding #2 - attacker can force unlimited NFTs onto victims while draining their tokens

Proof of Concept

This test demonstrates that a user can receive multiple NFTs through buyPresent(), breaking the "1 NFT per user" rule.

function test_buyMultipleNFTs() public {
// Setup: User gets tokens and collects first NFT
vm.startPrank(santa);
santasList.checkList(user, SantasList.Status.EXTRA_NICE);
santasList.checkTwice(user, SantasList.Status.EXTRA_NICE);
vm.stopPrank();
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME());
vm.prank(user);
santasList.collectPresent();
// User now has 1 NFT
assertEq(santasList.balanceOf(user), 1);
// User tries to collect again - should fail
vm.expectRevert(SantasList.SantasList__AlreadyCollected.selector);
vm.prank(user);
santasList.collectPresent();
// But buyPresent() has no such check!
vm.prank(attacker);
santasList.buyPresent(user); // User gets 2nd NFT
vm.prank(attacker);
santasList.buyPresent(user); // User gets 3rd NFT
// Verify: User now has multiple NFTs
assertEq(santasList.balanceOf(user), 3);
}

Recommended Mitigation

Add duplicate check to buyPresent() to enforce the "1 NFT per user" rule consistently.

function buyPresent(address presentReceiver) external {
+ // Prevent minting duplicate NFTs
+ if (balanceOf(presentReceiver) > 0) {
+ revert SantasList__AlreadyCollected();
+ }
i_santaToken.burn(msg.sender);
_safeMint(presentReceiver, s_tokenCounter++);
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 18 hours 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!