Platform: Code4rena
Start Date: 07/04/2023
Pot Size: $47,000 USDC
Total HM: 20
Participants: 120
Period: 6 days
Judge: GalloDaSballo
Total Solo HM: 4
Id: 230
League: ETH
Rank: 50/120
Findings: 2
Award: $81.97
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xLanterns
Also found by: AkshaySrivastav, Bason, CodingNameKiki, DadeKuma, DishWasher, Dug, ElKu, J4de, MiloTruck, Nyx, RaymondFam, Ruhum, T1MOH, Voyvoda, abiih, adriro, aviggiano, bshramin, sashik_eth, savi0ur, yixxas
9.3258 USDC - $9.33
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L333-L351
Take the PrivatePool
contract as an example.
File: PrivatePool.sol 333 if (payRoyalties) { 334 // calculate the sale price (assume it's the same for each NFT even if weights differ) 335 uint256 salePrice = (netOutputAmount + feeAmount + protocolFeeAmount) / tokenIds.length; 336 337 // get the royalty fee for the NFT 338 (uint256 royaltyFee, address recipient) = _getRoyalty(tokenIds[i], salePrice); 339 340 // tally the royalty fee amount 341 royaltyFeeAmount += royaltyFee; 342 343 // transfer the royalty fee to the recipient if it's greater than 0 344 if (royaltyFee > 0 && recipient != address(0)) { 345 if (baseToken != address(0)) { 346 ERC20(baseToken).safeTransfer(recipient, royaltyFee); 347 } else { 348 recipient.safeTransferETH(royaltyFee); 349 } 350 } 351 }
When calculating royalties, the PrivatePool
contract calculates the royalties of all NFTs based on the average price of NFTs. This is wrong, and the royalties should be calculated at the price of each NFT.
For example, there are 2 NFTs, one worth 1000 and one worth 200, their royalty rates are 2% and 1% respectively.
600 * 2% + 600 * 1% = 18
1000 * 2% + 200 * 1% = 22
In actual situations, the error may be greater, and users can avoid tax by purchasing some NFTs that are very cheap and have a low royalty rate.
This problem also exists in the EthRouter
contract.
Manual review
It is recommended to calculate royalties according to the price of each NFT.
#0 - c4-pre-sort
2023-04-20T17:43:24Z
0xSorryNotSorry marked the issue as duplicate of #580
#1 - c4-judge
2023-04-30T16:30:25Z
GalloDaSballo marked the issue as duplicate of #669
#2 - c4-judge
2023-05-01T07:26:31Z
GalloDaSballo changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-05-01T07:27:07Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: Voyvoda
Also found by: CodingNameKiki, DishWasher, GT_Blockchain, J4de, JGcarv, Josiah, RaymondFam, neumo, saian
72.6437 USDC - $72.64
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L731-L738
File: PrivatePool.sol 731 function changeFeeQuote(uint256 inputAmount) public view returns (uint256 feeAmount, uint256 protocolFeeAmount) { 732 // multiply the changeFee to get the fee per NFT (4 decimals of accuracy) 733 uint256 exponent = baseToken == address(0) ? 18 - 4 : ERC20(baseToken).decimals() - 4; 734 uint256 feePerNft = changeFee * 10 ** exponent; 735 736 feeAmount = inputAmount * feePerNft / 1e18; 737 [1]-> protocolFeeAmount = feeAmount * Factory(factory).protocolFeeRate() / 10_000; 738 }
In code [1], protocolFeeAmount
should be calculated based on inputAmount
instead of feeAmount
, which will result in much less protocolFeeAmount
. Please refer to buyQuote
and sellQuote
for correct usage.
Manual review
function changeFeeQuote(uint256 inputAmount) public view returns (uint256 feeAmount, uint256 protocolFeeAmount) { // multiply the changeFee to get the fee per NFT (4 decimals of accuracy) uint256 exponent = baseToken == address(0) ? 18 - 4 : ERC20(baseToken).decimals() - 4; uint256 feePerNft = changeFee * 10 ** exponent; feeAmount = inputAmount * feePerNft / 1e18; - protocolFeeAmount = feeAmount * Factory(factory).protocolFeeRate() / 10_000; + protocolFeeAmount = inputAmount * Factory(factory).protocolFeeRate() / 10_000; }
#0 - c4-pre-sort
2023-04-20T16:36:31Z
0xSorryNotSorry marked the issue as duplicate of #463
#1 - c4-judge
2023-05-01T07:21:41Z
GalloDaSballo marked the issue as satisfactory