uint256 totalSupply_ = totalSupply(token);
if (totalSupply_ == 0) revert NoTokenHolders();
, i.e. reverts in this case.
Thus the seller is prevented from selling his token. This of course generalizes to not being able to sell several tokens at once if all other tokens are withdrawn.
Recommended Mitigation Steps
Handle the holder fee correctly by excluding it from the total fee, or sending it to the protocol, in the case there are no holders left.
Lines of code
https://github.com/code-423n4/2024-01-curves/blob/516aedb7b9a8d341d0d2666c23780d2bd8a9a600/contracts/FeeSplitter.sol#L91
Vulnerability details
Impact
A token cannot be sold, even when it is not the last token, if all other tokens are withdrawn.
Proof of Concept
If all but one token are withdrawn then
FeeSplitter.totalSupply(token) == 1 * PRECISION
. This is calculated as(curves.curvesTokenSupply(token) - curves.curvesTokenBalance(token, address(curves))) * PRECISION
. When a token is sold (sellCurvesToken()
)curvesTokenSupply[token]
is reduced, which in this case results inFeeSplitter.totalSupply(token) == 0
, before transferring fees. Fees are transferred byFeeSplitter.addFees()
but this, i.e. reverts in this case.
Thus the seller is prevented from selling his token. This of course generalizes to not being able to sell several tokens at once if all other tokens are withdrawn.
Recommended Mitigation Steps
Handle the holder fee correctly by excluding it from the total fee, or sending it to the protocol, in the case there are no holders left.
Assessed type
Other