After a successful sale, the seller is expected to call collectUsdcFromSelling once to receive the sale proceeds plus their minting collateral, while the protocol retains its fee.
collectUsdcFromSelling does not clear collateralForMinting[listing.tokenId] after paying the seller, and does not mark the listing as "collected". A seller can call the function repeatedly after their NFT is sold, draining the contract of all USDC held on behalf of other users.
Additionally, usdc.safeTransfer(address(this), fees) is a no-op (the contract transferring to itself), meaning fees are never separated from spendable funds, yet totalFeesCollected increments each call, eventually causing withdrawFees() to revert due to insufficient balance.
Likelihood:
A sold listing is permanently inactive the guard require(!listing.isActive) is satisfied on every repeated call with no additional state check.
collateralForMinting has no "collected" flag, so nothing prevents re-entry across separate transactions.
Impact:
A malicious or opportunistic seller drains USDC belonging to other sellers and buyers who deposited collateral.
totalFeesCollected becomes inflated beyond the actual contract balance, permanently breaking withdrawFees().
The following test demonstrates a seller calling collectUsdcFromSelling three times on the same listing after the sale, draining the contract balance below the expected fees-only level.
Add a collected flag to the Listing struct and clear collateralForMinting after the first collection. Remove the self-transfer fees are already held in the contract and do not need to be moved.
collateral is not reset to zero after collecting USDC from sold NFT. No accounting for collected USDC
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.