Open c4-bot-7 opened 10 months ago
raymondfam marked the issue as sufficient quality report
raymondfam marked the issue as duplicate of #39
raymondfam marked the issue as not a duplicate
raymondfam marked the issue as duplicate of #74
raymondfam marked the issue as not a duplicate
raymondfam marked the issue as primary issue
raymondfam marked the issue as high quality report
not high, because a DAO is responsible of that changes but will fix it
andresaiello (sponsor) disputed
andresaiello marked the issue as disagree with severity
alcueca changed the severity to 2 (Med Risk)
alcueca marked the issue as selected for report
alcueca marked the issue as satisfactory
@alcueca Actually, fees should never have to be transferred when holderFeePercent == 0
. The fundamental issue here is rather that the userFeeOffset
is only set in _transferFees()
, and not explicitly when a user receives tokens (through buying or transferring), which is where this should happen instead. The issue described here is merely a particular manifestation of this issue, and it is more generally dealt with in #1491 (or rather its duplicates which do not obfuscate by referring to it as duration fairness).
Since the root cause both here and in #1491 is a failure to set/update userFeeOffset
for the recipient, I do not think the surface detail regarding holderFeePercent
warrants keeping this issue distinct from #1491, but should be duplicated to it.
Hi @d3e4, I don't agree that the root cause of issue #1491 is the same as this one.
The root cause of #1491 is that fees are not updated when doing normal transfers, and this happens on _transfer()
function. On the other hand, the root cause of this issue is that fees are not updated when buying or selling tokens while holdersFeePercent
is zero, and this happens on the _transferFees()
function.
The _transfer()
function is never called when buying or selling tokens and therefore this issue won't be fixed by fixing issue #1491.
Event after merging #1491 with #247 which is more general, it still doesn't cover the issue here that unclaimed fees need to be considered if changing the holders fee to zero.
Lines of code
https://github.com/code-423n4/2024-01-curves/blob/main/contracts/Curves.sol#L246-L249
Vulnerability details
Description
In the Curves protocol, a holder fee is imposed on all buy and sell transactions. This fee is distributed among share holders via the
FeeSplitter
contract, incentivizing long-term holding over frequent trading. The_transferFees()
function plays a crucial role in this process by transferring the holder fee toFeeSplitter
.This code ensures that
onBalanceChange()
andaddFees()
are called only whenholdersFeePercent
is non-zero, deemingFeeSplitter
unnecessary otherwise.A critical vulnerability arises when the
holderFeePercent
, initially set to a positive value, is later changed to zero. In this case, previously collected holder fees remain in theFeeSplitter
, awaiting distribution. The absence ofonBalanceChange()
calls in such scenarios allows new shareholders to unjustly claim these fees, leading to a potential theft of funds.Impact
When the holder fee is reduced from a positive value to zero, new shareholders can exploit this to illicitly claim holder fees from
FeeSplitter
. This theft is not only limited to these new shareholders but can also be perpetuated across multiple addresses, progressively draining theFeeSplitter
of its ETH reserves.Proof of Concept
The PoC can be executed in a standard Foundry environment for the Curves project. The test is conducted using the command
forge test --match-test testSetHolderFeeZero
.The test mimics an attack where a new shareholder exploits the system to claim holder fees that rightfully belong to previous shareholders.
Tools Used
Manual Review.
Recommended Mitigation Steps
To prevent this exploitation, it is advised to always call
onBalanceChange()
in_transferFees()
, regardless of theholdersFeePercent
status. This ensures continuous and accurate tracking of shareholder balances and rightful fee distribution, even when the holder fee is set to zero.Assessed type
Timing