Closed sherlock-admin2 closed 1 year ago
2 comment(s) were left on this issue during the judging contest.
shogoki commented:
This was a fix from the last audit. Should not be a problem
0xyPhilic commented:
invalid because the isKilled switch is used whenever a Curve pool is stopped so there is no way to expect a minOut tokens
nobody2018
high
In CurveSpell.closePositionFarm, _removeLiquidity lacks slippage protection if isKilled is True
Summary
When [closePositionFarm](https://github.com/sherlock-audit/2023-07-blueberry/blob/main/blueberry-core/contracts/spell/CurveSpell.sol#L168-L174) is called,
isKilled
will affect the execution logic of [_removeLiquidity](https://github.com/sherlock-audit/2023-07-blueberry/blob/main/blueberry-core/contracts/spell/CurveSpell.sol#L257-L264). IfisKilled
is set to false, liquidity will be removed via [ICurvePool(pool).remove_liquidity_one_coin](https://github.com/sherlock-audit/2023-07-blueberry/blob/main/blueberry-core/contracts/spell/CurveSpell.sol#L291-L294), whose second parameter isparam.amountOutMin
[passed from the caller](https://github.com/sherlock-audit/2023-07-blueberry/blob/main/blueberry-core/contracts/spell/CurveSpell.sol#L169), so this is no problem. IfisKilled
is set to true, the liquidity will be removed via [ICurvePool(pool).remove_liquidity](https://github.com/sherlock-audit/2023-07-blueberry/blob/main/blueberry-core/contracts/spell/CurveSpell.sol#L280), whose second parameter is [an empty array](https://github.com/sherlock-audit/2023-07-blueberry/blob/main/blueberry-core/contracts/spell/CurveSpell.sol#L279) representing the minOut of each token received. In Solidity, uninitialized values are all 0, so this way is vulnerable to sandwich attacks due to the lack of slippage protection.Vulnerability Detail
closePositionFarm
internally calls_removeLiquidity
to remove liquidity. Only cases whereisKilled
is set to true are discussed here.We can see that the
minOuts
array of L280/L283/L286 are all empty arrays, where each item is 0.minOuts
represents the minimum amounts of underlying coins to receive, which is specified by the caller. [This is slippage protection](https://github.com/curvefi/curve-contract/blob/master/contracts/pools/aeth/StableSwapAETH.vy#L498). IfminOuts
are all 0, then the underlying coins allowed to be received are arbitrary values. If this is noticed by a malicious user, the caller will suffer from a sandwich attack.Impact
When calling
closePositionFarm
, ifisKilled
is set to true, the caller is vulnerable to a sandwich attack and loses funds.Code Snippet
https://github.com/sherlock-audit/2023-07-blueberry/blob/main/blueberry-core/contracts/spell/CurveSpell.sol#L277-L287
Tool used
Manual Review
Recommendation
If
minOuts
cannot be calculated off-chain, it is recommended to delete the relevant code whenisKilled
is true and only useremove_liquidity_one_coin
.Here is an unproved idea for
minOuts
:Suppose pool has two tokens, the liquidity to be removed is 10e18 which will be divided into two parts, and
maxPer
is the slippage protection 95%. We can calculate theminOuts
array off-chain:minOuts
can be passed as an parameter toclosePositionFarm
. This is better than an empty array.