NFT Dealers

First Flight #58
Beginner FriendlyFoundry
100 EXP
Submission Details
Impact: low
Likelihood: medium

With `cancelListing()` collateral return, it allows minters to mint NFT without collateral

Author Revealed upon completion

Root + Impact

Description

  • The protocol expect minters to put up lockAmount USDC as collateral for each NFT minting.

  • Now when a minter go through the following flow:
    1. mintNft() <- this call requires the minter to put up a collateral.
    2. list()
    3. cancelListing() <- this call returns the collateral to the minter

    The minter can basically mint NFT for free using this flow.

// Root cause in the codebase with @> marks to highlight the relevant section
function cancelListing(uint256 _listingId) external {
Listing memory listing = s_listings[_listingId];
if (!listing.isActive) revert ListingNotActive(_listingId);
require(listing.seller == msg.sender, "Only seller can cancel listing");
s_listings[_listingId].isActive = false;
activeListingsCounter--;
@> usdc.safeTransfer(listing.seller, collateralForMinting[listing.tokenId]);
@> collateralForMinting[listing.tokenId] = 0;
emit NFT_Dealers_ListingCanceled(_listingId);
}

Risk

Likelihood:

  • When a minter go through the 1) mintNFT, 2) list, 3) cancelListing flow, it can mint NFT for free.

Impact:

  • Going through the 1) mintNFT, 2) list, 3) cancelListing flow multiple times, an attacker can use just lockAmount USDC to mint up to MAX_SUPPLY of NFTs.

Proof of Concept

// A minter go through the mintNft(), list(), and cancelListing() flow will get their collateral back.

Recommended Mitigation

Remove the collateral return logic on cancelList().

The collateral can be returned to the minter when the NFT is sold, logics added inside the buy()function.

function cancelListing(uint256 _listingId) external {
Listing memory listing = s_listings[_listingId];
if (!listing.isActive) revert ListingNotActive(_listingId);
require(listing.seller == msg.sender, "Only seller can cancel listing");
s_listings[_listingId].isActive = false;
activeListingsCounter--;
- usdc.safeTransfer(listing.seller, collateralForMinting[listing.tokenId]);
- collateralForMinting[listing.tokenId] = 0;
emit NFT_Dealers_ListingCanceled(_listingId);
}

Support

FAQs

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

Give us feedback!