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: 86/120
Findings: 1
Award: $23.08
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: sashik_eth
Also found by: 0x4non, 0x6980, 0xAgro, Cryptor, Kaysoft, Kenshin, Madalad, SaeedAlipoor01988, Sathish9098, W0RR1O, adriro, ayden, btk, catellatech, codeslide, devscrooge, georgits, giovannidisiena, lukris02, matrix_0wl, sayan, tnevler, tsvetanovv
23.0813 USDC - $23.08
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L230-L231 https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L323-L324
In PrivatePool.sol
we have functions buy()
and sell()
.
The buy()
function is responsible for allowing users to buy a basket of NFTs represented by a list of token IDs with different weights.
The sell()
function sells NFTs into the pool and transfers base tokens to the caller.
In these functions, we have unsafe cast from uint128
to uint256
.
In buy()
we have:
231: virtualNftReserves -= uint128(weightSum);
weightSum
is calculated as:
219: uint256 weightSum = sumWeightsAndValidateProof(       tokenIds,       tokenWeights,       proof     );
Which returns uint256 weightSum
. We have the potential risk of overflow if the sum of the NFT weights is larger than what can be represented by a uint128
integer.
If the value of weightSum
is greater than 2^128-1, the subtraction operation could result in an integer underflow.
We have a similar situation in sell()
function.
323: virtualBaseTokenReserves -= uint128(       netOutputAmount + protocolFeeAmount + feeAmount     ); 324:    virtualNftReserves += uint128(weightSum);
virtualNftReserves
is a uint128
variable that tracks the virtual reserves of the NFTs being sold:
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L112
112: uint128 public virtualNftReserves;
weightSum
is a uint256
variable that represents the total weight of the NFTs being sold.
To update the virtualNftReserves
variable, the weightSum
variable is cast to uint128
using the unsafe cast operator. This may result in data loss if the weightSum
variable is larger than the maximum value that can be represented by uint128
.
The same applies to virtualBaseTokenReserves
.
Manual Review
Consider using OpenZeppelin's SafeCast library to prevent unexpected overflow/underflow when casting or change uint128
to uint256
where necessary.
#0 - c4-pre-sort
2023-04-20T18:00:19Z
0xSorryNotSorry marked the issue as primary issue
#1 - c4-sponsor
2023-04-22T13:43:45Z
outdoteth marked the issue as sponsor confirmed
#2 - outdoteth
2023-04-24T12:05:39Z
Fixed in: https://github.com/outdoteth/caviar-private-pools/pull/10
Proposed fix is to use OpenZeppelin's SafeCast library when casting from uint256 down to uint128 in the reserve updates.
// update the virtual reserves virtualBaseTokenReserves += SafeCast.toUint128(netInputAmount - feeAmount - protocolFeeAmount); virtualNftReserves -= SafeCast.toUint128(weightSum); // -- snip --// // update the virtual reserves virtualBaseTokenReserves -= SafeCast.toUint128(netOutputAmount + protocolFeeAmount + feeAmount); virtualNftReserves += SafeCast.toUint128(weightSum);
#3 - c4-judge
2023-04-27T08:54:23Z
GalloDaSballo marked the issue as duplicate of #167
#4 - c4-judge
2023-05-02T07:55:16Z
GalloDaSballo changed the severity to 3 (High Risk)
#5 - c4-judge
2023-05-02T07:56:05Z
GalloDaSballo marked the issue as satisfactory