Summary
In the chop
and convert
functions lacks slippage protection, which can result in users receiving less value than anticipated during conversions.
Vulnerability Details
In the chop
and convert
functions there are no checks to ensure that the conversion results in a minimum expected amount of toAmount
. This means users might receive less than expected due to slippage. Without slippage protection, the contract is vulnerable to front-running and other issues that can lead to significant losses for users.
function chop(
address unripeToken,
uint256 amount,
LibTransfer.From fromMode,
LibTransfer.To toMode
) external payable nonReentrant returns (uint256) {
uint256 supply = IBean(unripeToken).totalSupply();
amount = LibTransfer.burnToken(IBean(unripeToken), amount, msg.sender, fromMode);
(address underlyingToken, uint256 underlyingAmount) = LibChop.chop(
unripeToken,
amount,
supply
);
require(underlyingAmount > 0, "Chop: no underlying");
IERC20(underlyingToken).sendToken(underlyingAmount, msg.sender, toMode);
emit Chop(msg.sender, unripeToken, amount, underlyingAmount);
return underlyingAmount;
}
function convert(
bytes calldata convertData,
int96[] memory stems,
uint256[] memory amounts
)
external
payable
nonReentrant
returns (int96 toStem, uint256 fromAmount, uint256 toAmount, uint256 fromBdv, uint256 toBdv)
{
uint256 grownStalk;
LibConvert.convertParams memory cp = LibConvert.convert(convertData);
if (cp.decreaseBDV) {require(stems.length == 1 && amounts.length == 1, "Convert: DecreaseBDV only supports updating one deposit.");}
require(cp.fromAmount > 0, "Convert: From amount is 0.");
if(cp.account == address(0)) cp.account = msg.sender;
LibSilo._mow(cp.account, cp.fromToken);
if (cp.fromToken != cp.toToken) LibSilo._mow(cp.account, cp.toToken);
(grownStalk, fromBdv) = _withdrawTokens(
cp.fromToken,
stems,
amounts,
cp.fromAmount,
cp.account
);
uint256 newBdv = LibTokenSilo.beanDenominatedValue(cp.toToken, cp.toAmount);
toBdv = (newBdv > fromBdv || cp.decreaseBDV) ? newBdv : fromBdv;
toStem = _depositTokensForConvert(cp.toToken, cp.toAmount, toBdv, grownStalk, cp.account);
toAmount = cp.toAmount;
fromAmount = cp.fromAmount;
emit Convert(cp.account, cp.fromToken, cp.toToken, cp.fromAmount, cp.toAmount);
}
The functions does not include a parameter to define the minimum acceptable toAmount
for each conversion.
Impact
Tools Used
Manual Review
Recommendations