When a whitelisted user mints an NFT, the protocol locks a USDC collateral amount recorded in collateralForMinting[tokenId]. After the NFT is sold, the original seller calls collectUsdcFromSelling() to receive their sale proceeds plus the returned collateral.
The function reads collateralForMinting[listing.tokenId] and adds it to amountToSeller, but never sets it to zero. Any seller who has completed a sale can call the function repeatedly on the same listingId, receiving the collateral amount on every call until the contract is empty. Additionally, usdc.safeTransfer(address(this), fees) performs a self-transfer — the contract sends USDC to itself — which is a no-op, while totalFeesCollected += fees is still incremented on every repeated call, inflating the accounting beyond the actual contract balance.
Likelihood:
A seller calls collectUsdcFromSelling() a second time immediately after the first successful collection — there is no state change, flag, or guard preventing repeated execution since the only check (!listing.isActive) is already satisfied after the first call.
Any seller who has completed at least one sale has permanent, unrestricted access to call this function in a loop for as long as the contract holds a USDC balance.
Impact:
A single malicious seller drains the entire USDC balance of the contract, stealing collateral and proceeds belonging to every other minter and seller.
totalFeesCollected becomes inflated beyond the actual contract balance, causing withdrawFees() to revert or attempt to transfer more than available once a drain has occurred.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.