The expected behavior of the progressive fee system is that NFT sales above 10,000 USDC are charged a 5% fee, sales between 1,000–10,000 USDC are charged 3%, and sales below 1,000 USDC are charged 1%. This is documented by the three fee constants and two thresholds in the contract.
What actually happens is that the price field in the Listing struct and the _price parameter in list() are both typed as uint32. The maximum value of uint32 is 4,294,967,295 — approximately 4,294 USDC with 6 decimal places. The HIGH_FEE_THRESHOLD is 10_000e6 = 10,000,000,000, which is more than twice uint32.max. It is therefore structurally impossible for any listing to ever reach the HIGH fee tier, regardless of what price a seller wants to set.
The same truncation applies when fees are calculated in collectUsdcFromSelling(), which reads listing.price as uint32 when passing it to _calculateFees().
Likelihood:
This affects every single sale in the protocol — there is no configuration or user action that can reach the HIGH tier
The issue is present from deployment and requires a contract upgrade to fix
Impact:
Every sale that a seller intends to list above ~4,294 USDC is either impossible to list (the caller's value is silently truncated to a different price) or capped at the MID (3%) fee tier instead of HIGH (5%)
The protocol permanently loses the difference between the 3% and 5% fee on all high-value sales — for a 10,000 USDC sale this is a 2% shortfall (200 USDC per sale)
Sellers who want to list above ~4,294 USDC cannot do so at their intended price
Run with:
Expected console output:
Change Listing.price and all related function parameters from uint32 to uint256. The struct packs poorly with a uint32 price anyway since seller and nft are address (160-bit) and tokenId is uint256, so there is no meaningful gas benefit from the smaller type.
This allows the _calculateFees() thresholds to operate as designed and enables sellers to list at any price without silent truncation.
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.