-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rounding on division in changeFeeQuote()
may cause Pool owners to mistakenly or by purpose avoid paying the protocol fee
#217
Comments
changeFeeQuote()
may cause Pool owners to maliciously or mistakenly avoid paying the protocol feechangeFeeQuote()
may cause Pool owners to mistakenly or by purpose avoid paying the protocol fee
0xSorryNotSorry marked the issue as high quality report |
0xSorryNotSorry marked the issue as primary issue |
outdoteth marked the issue as disagree with severity |
not sure if this should be a low or not |
outdoteth requested judge review |
GalloDaSballo changed the severity to QA (Quality Assurance) |
Fees will round down to zero due to integer math, I believe this is Low Severity |
Leaving a fact based comment as I believe the impact and likelihood was missed or rather not properly presented in the issue.
|
GalloDaSballo marked the issue as grade-c |
Thank you for the feedback @indijanc I still believe that a rounding error is Low Severity as the loss is marginal, I think quality was good and believe if you sent more this would have been awarded as QA |
Also note that inputAmount is normalized in 1e18s meaning that this should not happen unless it's misconfigured |
Lines of code
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L737
Vulnerability details
Impact
Depending on how the
PrivatePool
is set-up (initialized) the owner of the pool may mistakenly or by purpose avoid paying protocol fees on the change NFT functionality -change()
function. The same issue applies to the pool fee. Please note that this is easier or rather will be more common if abaseToken
with fewer decimals is used. We'll pick USDC in this case which is currently the second most popular stablecoin. USDC is currently configured with 6 decimals.Proof of Concept
Let's expand how the
protocolFeeAmount
is calculated inchangeFeeQuote()
. This issue is more apparent when usingbaseToken
with fewer decimals, so we will use USDC which has 6 decimals. In this case the calculatedexponent
is 2.The following legend will be used for brevity:
$= 2$
$= feePN$
$= chFee$
$= fee$
$= inpt$
$= protoFeeAmnt$
$= protoFeeRate$
exponent
feePerNft
changeFee
feeAmount
inputAmount
protocolFeeAmount
protocolFeeRate
When the numerator is less than the denominator the result will be rounded down to 0, which means the
protocolFeeAmount
will not be paid.So this happens whenever this equation is satisfied:
$inpt \times chFee \times protoFeeRate < 10^{20}$
changeFee
is an absolute value specified with 4 decimals precision and heavily depends on the selectedbaseToken
, let's assume in case of USDC thatchangeFee
is somewhere in the range of 10000 to 1000000 (1 to 100 USDC).protocolFeeRate
is in basis points, for brevity let's assume it is 100 (1.0%). The overwhelmingly important factor here is theinputAmount
which is dependant on the tokenWeights that is arbitrarily set by the pool owner. Let's put in the numbers (using 100 USDC for change fee):So in this scenario if the NFT token weight is less than$10^{12}$ the protocol fee on
change()
is 0. The similar issue exists forfeeAmount
that goes to the pool owner, but in that case the owner of the pool could correct the contract settings and provide new NFT weights to rectify the scenario, while theprotocolFeeRate
is global across all created private pools.Note that this is easier achieved with
baseToken
with fewer decimals because of the smallerexponent
in the equation, hence using USDC as an example. Also note we're using a single NTF change scenario for simplifying the showcase.Let's verify this with a simple test (using the project test suite):
Play around with the input of
privatePoolUSDC.changeFeeQuote()
and notice the test will succeed on input 1e12, but fail with anything less.Tools Used
Manual review.
Recommended Mitigation Steps
Some mitigation ideas:
protocolFeeAmount
ends up 0. This would force the pool owner to update the weights according to the documentation (see Agreements & Disclosures #1)inputAmount
inchangeFeeQuote()
is equal or greater than 1e18.The text was updated successfully, but these errors were encountered: