Summary
The protocol uses the swap process to convert the one asset into another asset. There can be single path or multiple path involved swap process.
In all of the swap process, it does not have check for expected amount of output from the swap.
This is well known issue where someone can front run before swap to buy asset and sell after the swap and get profit. This is not desirable to the protocol as it impacted with loss of asset than the actual value during the swap.
Vulnerability Details
There are in few places the swap operation is done.
lets see in one of the places start from FeeConversionKeeper
The function converts the accumulated fee into the WETH by calling the function performUpkeep
function performUpkeep(bytes calldata performData) external override onlyForwarder {
FeeConversionKeeperStorage memory self = _getFeeConversionKeeperStorage();
IMarketMakingEngine marketMakingEngine = self.marketMakingEngine;
(uint128[] memory marketIds, address[] memory assets) = abi.decode(performData, (uint128[], address[]));
for (uint256 i; i < marketIds.length; i++) {
marketMakingEngine.convertAccumulatedFeesToWeth(marketIds[i], assets[i], self.dexSwapStrategyId, ""); -->> lets follow here
}
}
function convertAccumulatedFeesToWeth(
uint128 marketId,
address asset,
uint128 dexSwapStrategyId,
bytes calldata path
)
external
onlyRegisteredSystemKeepers
{
...........................
ctx.weth = MarketMakingEngineConfiguration.load().weth;
if (asset == ctx.weth) {
ctx.receivedWethX18 = ctx.assetAmountX18;
} else {
Collateral.Data storage wethCollateral = Collateral.load(ctx.weth);
AssetSwapPath.Data storage swapPath = AssetSwapPath.load(asset);
if (swapPath.enabled) {
ctx.tokensSwapped = _performMultiDexSwap(swapPath, ctx.assetAmount);
} else if (path.length == 0) {
DexSwapStrategy.Data storage dexSwapStrategy = DexSwapStrategy.loadExisting(dexSwapStrategyId);
IERC20(asset).approve(dexSwapStrategy.dexAdapter, ctx.assetAmount);
SwapExactInputSinglePayload memory swapCallData = SwapExactInputSinglePayload({
tokenIn: asset,
tokenOut: ctx.weth,
amountIn: ctx.assetAmount,
recipient: address(this)
});
ctx.tokensSwapped = dexSwapStrategy.executeSwapExactInputSingle(swapCallData);
} else {
DexSwapStrategy.Data storage dexSwapStrategy = DexSwapStrategy.loadExisting(dexSwapStrategyId);
IERC20(asset).approve(dexSwapStrategy.dexAdapter, ctx.assetAmount);
SwapExactInputPayload memory swapCallData = SwapExactInputPayload({
path: path,
tokenIn: asset,
tokenOut: ctx.weth,
amountIn: ctx.assetAmount,
recipient: address(this)
});
ctx.tokensSwapped = dexSwapStrategy.executeSwapExactInput(swapCallData);
}
ctx.receivedWethX18 = wethCollateral.convertTokenAmountToUd60x18(ctx.tokensSwapped);
}
.........................................
}
as shown above, in the swap process, no check is done to make sure the swap operation fetched expected amount of WETH.
Impact
Loss of asset
Tools Used
Manual review
Recommendations
Please add slippage check as done in other applications.