Steadefi

Steadefi
DeFiHardhatFoundryOracle
35,000 USDC
View results
Submission Details
Severity: high
Invalid

Incorrect _amountOutMinimum calculation cause vault loss fund

Summary

Incorrect _amountOutMinimum calculation cause vault loss fund when swap tokens.

Vulnerability Details

UniswapSwap#swapExactTokensForTokens and TraderJoeSwap#swapExactTokensForTokens is used as swap tokens by GMXCompound#compound, vi call: GMXCompound#compound -> GMXManager.swapExactTokensForTokens -> GMXWorker.swapTokensForExactTokens -> swapRouter.swapTokensForExactTokens, before actually swap, swapRouter calculate _amountOutMinimum as below:

uint256 _valueIn = amountIn * oracle.consultIn18Decimals(sp.tokenIn) / SAFE_MULTIPLIER;
uint256 _amountOutMinimum = _valueIn
* SAFE_MULTIPLIER
/ oracle.consultIn18Decimals(sp.tokenOut)
/ (10 ** (18 - IERC20Metadata(sp.tokenOut).decimals()))
* (10000 - sp.slippage) / 10000;
ISwapRouter.ExactInputSingleParams memory _eisp =
ISwapRouter.ExactInputSingleParams({
tokenIn: sp.tokenIn,
tokenOut: sp.tokenOut,
fee: fees[sp.tokenIn][sp.tokenOut],
recipient: address(this),
deadline: sp.deadline,
amountIn: sp.amountIn,
amountOutMinimum: _amountOutMinimum,
sqrtPriceLimitX96: 0
});
router.exactInputSingle(_eisp);

However, the _amountOutMinimum is not correct because different token has different decimal.

Image such condition: keeper want to compound with 1800 usdc, sp.tokenA = USDC, sp.tokenB = WETH, slippage = 1%, so the expected swap result is 0.99 WETH.

Following poc show the calculation result:

contract DemotTest is Test {
constructor() {}
uint256 public constant SAFE_MULTIPLIER = 1e18;
function testSwap() public {
// 1800 usdc
uint amountIn = 1800 * 1e6;
// usdc in 18 decimal
uint usdcPriceIn18Dec = 1 * 1e18;
uint usdcDecimal = 6;
uint wethPriceIn18Dec = 1800 * 1e18;
// weth in 18 decimal
uint wethDecimal = 18;
uint _valueIn = amountIn * usdcPriceIn18Dec / SAFE_MULTIPLIER;
uint slippage = 100;
uint256 _amountOutMinimum = (((_valueIn * SAFE_MULTIPLIER) /
wethPriceIn18Dec /
(10 ** (18 - wethDecimal))) * (10000 - slippage)) / 10000;
console.log("_amountOutMinimum: ", _amountOutMinimum);
}
}

Results:

[PASS] testSwap() (gas: 4147)
Logs:
_amountOutMinimum: 990000

Obviously, It's much smaller than expected result, so those swap txs can be sandwiched by MEV.

Impact

Protocol may loss fund when swap tokens.

Tools Used

vscode, Manual Review

Recommendations

Change uint256 _valueIn = sp.amountIn * oracle.consultIn18Decimals(sp.tokenIn) / SAFE_MULTIPLIER; inside swapRouter#swapExactTokensForTokens to uint256 _valueIn = sp.amountIn * oracle.consultIn18Decimals(sp.tokenIn) / 10 ** IERC20Metadata(sp.tokenIn).decimals().

Updates

Lead Judging Commences

hans Auditor
almost 2 years ago
hans Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.