Open c4-bot-10 opened 9 months ago
raymondfam marked the issue as insufficient quality report
raymondfam marked the issue as duplicate of #28
alcueca changed the severity to 2 (Med Risk)
alcueca changed the severity to QA (Quality Assurance)
alcueca marked the issue as grade-b
Lines of code
https://github.com/code-423n4/2024-01-curves/blob/main/contracts/Curves.sol#L166-L178 https://github.com/code-423n4/2024-01-curves/blob/main/contracts/Curves.sol#L218-L261
Vulnerability details
Fees in ETH will necessarily get stuck inside the
Curves
contract, whenever their percentage is not null.This is always true for the
protocolFee
, when selling tokens; it can also happen for thereferralFee
and theholderFee
, depending on the configuration, to be stuck inside the contract, or sent to the zero address.Description
With various degrees of exposure to the configuration, there are multiple ways for fees to be stuck inside the
Curves
contract, or sent to the zero address. Namely, the following:feesEconomics.protocolFeePercent
is set:feesEconomics.referralFeePercent
is set, but there is no referral destination for the subject:referralFee
will stay inside the contract anytime a token is sold.feesEconomics.protocolFeeDestination
has not been set, thereferralFee
will be sent to the zero address anytime a token is bought.feesEconomics.holderFeePercent
is set, but thefeeRedistributor
is the zero address, or any address that is incompatible with theFeeSplitter
contract (less likely because it's set in the constructor, but it can still be incorrectly updated):holderFee
will stay inside the contract anytime a token is sold.This raises two concerns:
This whole mechanism is not explicit at all, as the protocol fees would be transferred when buying a token, but would be kept inside the contract when selling it. Which is at best misleading, and at worst a shady method of collecting concealed fees.
Even worse, some fees (the
protocolFee
,referralFee
and even though less likely, theholderFee
), depending on the conditions, will get either stuck inside the contract, or sent to the zero address. Which means that ultimately, and with an increasing gravity as the prices for subjects increase, these funds will be lost forever.Impact
Whenever a
feesEconomics.protocolFeePercent
is set, theprotocolFee
will stay inside the contract anytime a token is sold, thus being lost forever as there is no way to withdraw ETH from the contract.Not only this first issue cannot be avoided at all, but it is also easy to replicate a similar issue with the other fees, by missing any of the multiple required steps to set the configuration correctly.
As a result, there will necessarily be some fees getting lost, and even more as the prices increase; this can lead to a significant loss of funds, especially considering how easy it is to replicate this issue with other fees, and will reflect extremely poorly on the protocol.
Proof of Concept (short)
Take a look at the
_transferFees
function:Proof of Concept (code)
To run the PoC with Foundry, follow the steps below:
Exploit.t.sol
insidetest/foundry
, and paste the code belowforge test --mc Curves_exploit
Mitigation
There are multiple options to mitigate this issue, depending on the degree of control/simplicity.
A very basic first step, might be to add a function allowing the owner to withdraw additional funds from the
Curves
contract; meaning, funds that are not allocated to current holders. This would allow recovering any funds mistakenly sent to the contract, e.g. due to a wrong configuration in a UI, or miscalculations in the fees distribution.Basically, using a variable to keep track of expected funds (increase on buy, decrease on sell), and a privileged function to withdraw any surplus (the difference between the expected and actual funds).
However, this should not be the only step in solving this issue, as it would practically mean charging hidden fees (e.g.
protocolFee
) from the users; a very shady practice, which would not be in the best interest of the protocol.Something that could help, would be to add some explicit checks in the
_transferFees
function, for destination addresses. On how to handle different fees, based on each other (e.g. thereferralFee
when there is no destination), this is purely a design choice, on which I cannot comment. Basically, you need to handle the following cases:protocolFee
(currently not handled at all).firstDestination
whenfeesEconomics.protocolFeeDestination
is not set (==address(0)
);referralFee
will be sent along theprotocolFee
.referralFee
when there is noreferralFeeDestination
for the subject;holderFee
when there is nofeeRedistributor
set, or it is incompatible with theFeeSplitter
contract.Most of this can be done by performing checks on the fees configuration before calculating the values of various fees. A proposition to mitigate these could be to update the functions as follows:
Assessed type
ETH-Transfer