Closed c4-bot-2 closed 9 months ago
Picodes marked the issue as unsatisfactory: Invalid
It seems to work as intended as you want to do the arbitrage after the swap has been made?
Yeah, the arbitrage is executed after the swap, however, it is needed to compute the swapped value in terms of ETH to bound the maximum and minimum amount of WETH to execute this arbitrage. To compute this value it is used the reserves of the pool involving the swapped token and WETH:
function _attemptArbitrage( IERC20 swapTokenIn, IERC20 swapTokenOut, uint256 swapAmountIn ) internal returns (uint256 arbitrageProfit )
{
// Determine the value of swapTokenIn (in WETH) that the user has specified as it will impact the arbitrage trade size
uint256 swapAmountInValueInETH = _determineSwapAmountInValueInETH(swapTokenIn, swapAmountIn);
if ( swapAmountInValueInETH == 0 )
return 0;
...
}
// Determine the WETH equivalent of swapAmountIn of swapTokenIn
function _determineSwapAmountInValueInETH(IERC20 swapTokenIn, uint256 swapAmountIn) internal view returns (uint256 swapAmountInValueInETH)
{
if ( address(swapTokenIn) == address(weth) )
return swapAmountIn;
// All tokens within the DEX are paired with WETH (and WBTC) - so this pool will exist.
(uint256 reservesWETH, uint256 reservesTokenIn) = getPoolReserves(weth, swapTokenIn);
if ( (reservesWETH < PoolUtils.DUST) || (reservesTokenIn < PoolUtils.DUST) )
return 0; // can't arbitrage as there are not enough reserves to determine swapAmountInValueInETH
@> swapAmountInValueInETH = ( swapAmountIn * reservesWETH ) / reservesTokenIn;
if ( swapAmountInValueInETH <= PoolUtils.DUST )
return 0;
}
Imagine that someone swaps USDS for DAI, this swap will only change the reserves for USDS and DAI pool, as a result, when it computes the value of the swapped amount in ETH it will look at the USDS-WETH reserves. Since these reserves have not been changed, the computation of the value will be correct.
However, when someone swaps any arbitrary token for WETH, it will always increase the reserve of this arbitrary token and will decrease the reserve of WETH. After changing these reserve amounts due to the swap, it will compute the value of the amount swapped in terms of ETH by calculating this:
swapAmountIn * reservesWETH
swapAmountInValueInETH =----------------------------------------
reservesTokenIn
Since the reservesWETH will have decreased and reservesTokenIn will have increased, the computed value will be much lower, and as a result, the arbitrage oportunity will not be fully leveraged by the protocol.
Example: The pool for WETH-BTC is balanced with the open market Initial reserves: WETH = 20.000 ETH BTC = 1.000 BTC
Now imagine that a user swaps 500 BTC for ETH, when a swap would be executed, the value of btc swapped in terms of ETH would need to be 500 BTC * 20 ETH = 10000 ETH according to the open market. However, since to calculate the value of the swapped amount in terms of ETH is based on the reserves of the token-WETH pool AFTER THE SWAP it will be wrongly calculated because the reserves will have changed. For this specific example:
After swapping 500 BTC the reserves remain as follows: WETH = 6666.7 ETH BTC = 1500 BTC
The value of the swapped amount would be computed as:
500 * 6666.7
swapAmountInValueInETH =----------------------------- = 2222.2
1500
As a result, the computed value to execute the arbitrage is 2222.2 WETH when in fact, the value swapped by the user was 10.000 WETH. Since the arbitrage is the main differentiator from other AMMs it really affects the correct functionality.
@Draiakoo thanks.
swapAmountInValueInETH
is useful only to fix the bounds of the bisectionSearch
so the impact here at most is that the bounds aren't correct, which isn't a critical issue at all.Overall this report doesn't show precisely how this could be exploited and why it could be an issue. The most extreme case I see would be during a very large swap that the bisection search could be a bit misset but it seems very far fetched considering the user would lose a lot and the search should still find an arbitrage
Hey @Picodes I wanted to say that it can not be exploited by a user, I was just refering that when a large swap is executed, the arbitrage will be done with less funds than it should. As a result, the protocol will get less arbitrage value (which is the main feature of this protocol) and it is also the way that liquidity providers get rewards. It is not critical but maybe a medium could be considered. Thanks for you patience!
@Draiakoo I don't see how this leads to "the arbitrage will be done with less funds than it should".
What's happening is that the bisection search range can be lower than it should but in the extreme majority of the cases, the search range is large enough so a correct result will still be found.
Lines of code
https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L347-L359 https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L235-L276
Vulnerability details
Impact
When a swap is executed, in order to set boundries for the arbitrage binary search, the value of the
amountIn
is calculated in terms of ETH by using pool reserves. This binary search will look for the most efficient amount to swap in order to get more profit. This amount will be bounded betweenamountIn
// 128 andamountIn
* 1,25.The value calculation of the input amount is done using the pool reserves
However, this calculation is made right after the swap is accounted in the reserves:
As a result, when any token is swapped for ETH, the value of the input token will be computed with the already changed reserves of the pool which has suffered from the impact of the swap and the possible arbitrage profits for that pool will drop.
Note that the greater the swapped amount is, the less arbitrage profits will be obtained by the pool because the swap will impact significantly in its reserves. And the bigger amounts in the pool reserves, the less impact these kind of swaps will have.
Proof of Concept
Check this foundry test
Setup:
Proof of Concept:
Output traces:
Notice that this value lost due to the calculation will leave open arbitrage oportunities to anyone and basically the protocol will not benefit properly from the main feature of this codebase.
Tools Used
Manual review
Recommended Mitigation Steps
Cache the reserves before the swap is executed and calculate the input value with these reserves previous to the swap
Assessed type
Error