Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: high
Invalid

Absense of slippage protection could lead to loss of funds when swap

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

// call FeeDistributionBranch::convertAccumulatedFeesToWeth
function performUpkeep(bytes calldata performData) external override onlyForwarder {
FeeConversionKeeperStorage memory self = _getFeeConversionKeeperStorage();
IMarketMakingEngine marketMakingEngine = self.marketMakingEngine;
// decode performData
(uint128[] memory marketIds, address[] memory assets) = abi.decode(performData, (uint128[], address[]));
// convert accumulated fees to weth for decoded markets and assets
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
{
...........................
// load weth address
ctx.weth = MarketMakingEngineConfiguration.load().weth;
// if asset is weth directly add to accumulated weth, else swap token for weth
if (asset == ctx.weth) {
// store the amount of weth
ctx.receivedWethX18 = ctx.assetAmountX18;
} else {
// load the weth collateral data storage pointer
Collateral.Data storage wethCollateral = Collateral.load(ctx.weth);
// load custom swap path for asset if enabled
AssetSwapPath.Data storage swapPath = AssetSwapPath.load(asset);
// verify if the swap should be input multi-dex/custom swap path, single or multihop
if (swapPath.enabled) {
ctx.tokensSwapped = _performMultiDexSwap(swapPath, ctx.assetAmount);
} else if (path.length == 0) {
// loads the dex swap strategy data storage pointer
DexSwapStrategy.Data storage dexSwapStrategy = DexSwapStrategy.loadExisting(dexSwapStrategyId);
// approve the collateral token to the dex adapter
IERC20(asset).approve(dexSwapStrategy.dexAdapter, ctx.assetAmount);
// prepare the data for executing the swap
SwapExactInputSinglePayload memory swapCallData = SwapExactInputSinglePayload({
tokenIn: asset,
tokenOut: ctx.weth,
amountIn: ctx.assetAmount,
recipient: address(this)
});
// Swap collected collateral fee amount for WETH and store the obtained amount
ctx.tokensSwapped = dexSwapStrategy.executeSwapExactInputSingle(swapCallData);
} else {
// loads the dex swap strategy data storage pointer
DexSwapStrategy.Data storage dexSwapStrategy = DexSwapStrategy.loadExisting(dexSwapStrategyId);
// approve the collateral token to the dex adapter
IERC20(asset).approve(dexSwapStrategy.dexAdapter, ctx.assetAmount);
// prepare the data for executing the swap
SwapExactInputPayload memory swapCallData = SwapExactInputPayload({
path: path,
tokenIn: asset,
tokenOut: ctx.weth,
amountIn: ctx.assetAmount,
recipient: address(this)
});
// Swap collected collateral fee amount for WETH and store the obtained amount
ctx.tokensSwapped = dexSwapStrategy.executeSwapExactInput(swapCallData);
}
// uint256 -> ud60x18
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.

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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