Closed c4-bot-2 closed 9 months ago
raymondfam marked the issue as sufficient quality report
raymondfam marked the issue as duplicate of #41
alcueca changed the severity to 2 (Med Risk)
alcueca changed the severity to 3 (High Risk)
alcueca marked the issue as satisfactory
Lines of code
https://github.com/code-423n4/2024-01-curves/blob/main/contracts/Curves.sol#L296-L325 https://github.com/code-423n4/2024-01-curves/blob/main/contracts/FeeSplitter.sol#L75 https://github.com/code-423n4/2024-01-curves/blob/main/contracts/FeeSplitter.sol#L40
Vulnerability details
Impact
In the protocol, when a user purchases a curvesTokenSubject, they can claim allocated fees via the FeeSplitter contract's claimFees function. The claimable fees are calculated as:
(data.cumulativeFeePerToken - data.userFeeOffset[account]) * balance
Where
balance
is the user's curvesTokenSubject balance,cumulativeFeePerToken
is the protocol's current accumulated fees, anduserFeeOffset[account]
is the accumulated fees the user is entitled to.In theory,
userFeeOffset[account]
should be updated when the user's curvesTokenSubject balance changes, and settle the fees the user is entitled to for holding the curvesTokenSubject.Unfortunately, when users transfer curvesTokenBalance via transferCurvesToken or transferAllCurvesTokens, the current fees for the
from
address are not settled. This results in reduced claimable fees for thefrom
address. More severely, theto
address can claim much more fees than expected. An attacker can exploit this to drain all fees in the FeeSplitter contract.Proof of Concept
In the FeeSplitter contract, users can claim fees via the claimFees function. https://github.com/code-423n4/2024-01-curves/blob/main/contracts/FeeSplitter.sol#L80-L87
It will first update the user's unclaimedFees and userFeeOffset via the updateFeeCredit function, where unclaimedFees is the amount of fees the user can claim. https://github.com/code-423n4/2024-01-curves/blob/main/contracts/FeeSplitter.sol#L67-L68
unclaimedFees is calculated as:
(data.cumulativeFeePerToken - data.userFeeOffset[account]) * balance
This calculation relies on the user's curvesTokenSubject balance,
userFeeOffset[account]
, andcumulativeFeePerToken
.cumulativeFeePerToken
anduserFeeOffset[account]
are only updated when the user buys/sells curvesTokenSubjects. After update,userFeeOffset[account]
will equalcumulativeFeePerToken
. This avoids the user claiming fees immediately after buying, and claiming fees after selling. https://github.com/code-423n4/2024-01-curves/blob/main/contracts/Curves.sol#L247-L248 https://github.com/code-423n4/2024-01-curves/blob/main/contracts/Curves.sol#L274 https://github.com/code-423n4/2024-01-curves/blob/main/contracts/Curves.sol#L292 https://github.com/code-423n4/2024-01-curves/blob/main/contracts/FeeSplitter.sol#L89-L100However, a user's balance changes not only on buying/selling of curvesTokenSubjects, but also when transferring via transferCurvesToken/transferAllCurvesTokens. https://github.com/code-423n4/2024-01-curves/blob/main/contracts/Curves.sol#L296-L325
Unfortunately, Curves does not call FeeSplitter's updateFeeCredit to settle the user's fees before curvesTokenSubject transfers. This allows malicious users to continually transfer then claim fees to profit more. Here's a simple example:
from
address has 1 curvesTokenSubject,to
has 0, FeeSplitter's cumulativeFeePerToken is 100.from
transfers 1 curvesTokenSubject toto
, nowto
's balance is 1.to
claims fees, since itsuserFeeOffset
is unupdated, it's 0.cumulativeFeePerToken
is current accumulated fees, sodata.cumulativeFeePerToken - data.userFeeOffset[account]
is 100.to
's balance is 1. Therefore(data.cumulativeFeePerToken - data.userFeeOffset[account]) * balance
is 100.to
can claim 100 fees without paying anything.In actual exploits, if the FeeSplitter contract has insufficient balance to pay the fees, the attacker can use a flashloan to cover the difference and drain all fees from the FeeSplitter contract in one transaction.
Tools Used
N/A
Recommended Mitigation Steps
It is recommended for the FeeSplitter contract to provide an updateFeeCredit function that can be called by the Curves contract. Before a user's curvesTokenSubject is transferred, their fees should be settled via updateFeeCredit. Consider the following fixes:
https://github.com/code-423n4/2024-01-curves/blob/main/contracts/Curves.sol
https://github.com/code-423n4/2024-01-curves/blob/main/contracts/FeeSplitter.sol
Assessed type
Error