Part 2

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

Missing slippage protection when swap usdc

Summary

Vulnerability Details

The _convertAssetsToUsdc function is responsible for swapping an input asset into USDC using a decentralized exchange (DEX) adapter. While the function ensures asset approval, swap execution, and protocol fee deductions, it does not implement any form of slippage protection, which could result in suboptimal execution prices or losses due to market volatility.

The function calls dexSwapStrategy.executeSwapExactInputSingle and dexSwapStrategy.executeSwapExactInput without specifying a minimum expected output amount. Without a slippage limit, the swap could execute at an unexpectedly low rate, leading to significant losses if the market moves unfavorably between transaction submission and execution.

SwapExactInputSinglePayload memory swapCallData = SwapExactInputSinglePayload({
tokenIn: asset,
tokenOut: usdc,
amountIn: assetAmount,
recipient: recipient
});
function _convertAssetsToUsdc(
uint128 dexSwapStrategyId,
address asset,
uint256 assetAmount,
bytes memory path,
address recipient,
address usdc
)
internal
returns (uint256 usdcOut)
{
// revert if the amount is zero
if (assetAmount == 0) revert Errors.AssetAmountIsZero(asset);
// if the asset being handled is usdc, simply output it to `usdcOut`
if (asset == usdc) {
usdcOut = assetAmount;
} else {
// approve the asset to be spent by the dex adapter contract
DexSwapStrategy.Data storage dexSwapStrategy = DexSwapStrategy.loadExisting(dexSwapStrategyId);
IERC20(asset).approve(dexSwapStrategy.dexAdapter, assetAmount);
// verify if the swap should be input single or multihop
if (path.length == 0) {
// prepare the data for executing the swap
SwapExactInputSinglePayload memory swapCallData = SwapExactInputSinglePayload({
tokenIn: asset,
tokenOut: usdc,
amountIn: assetAmount,
recipient: recipient
});
// swap the credit deposit assets for USDC and store the output amount
usdcOut = dexSwapStrategy.executeSwapExactInputSingle(swapCallData);
} else {
// prepare the data for executing the swap
SwapExactInputPayload memory swapCallData = SwapExactInputPayload({
path: path,
tokenIn: asset,
tokenOut: usdc,
amountIn: assetAmount,
recipient: recipient
});
// swap the credit deposit assets for USDC and store the output amount
usdcOut = dexSwapStrategy.executeSwapExactInput(swapCallData);
}
// load market making config
MarketMakingEngineConfiguration.Data storage marketMakingEngineConfiguration =
MarketMakingEngineConfiguration.load();
// cache the settlement base fee value using usdc's native decimals
uint256 settlementBaseFeeUsd = Collateral.load(usdc).convertUd60x18ToTokenAmount(
ud60x18(marketMakingEngineConfiguration.settlementBaseFeeUsdX18)
);
if (settlementBaseFeeUsd > 0) {
// revert if there isn't enough usdc to cover the base fee
// NOTE: keepers must be configured to buy good chunks of usdc at minimum (e.g $500)
// as the settlement base fee shouldn't be much greater than $1.
if (usdcOut < settlementBaseFeeUsd) {
revert Errors.FailedToPaySettlementBaseFee();
}
usdcOut -= settlementBaseFeeUsd;
// distribute the base fee to protocol fee recipients
marketMakingEngineConfiguration.distributeProtocolAssetReward(usdc, settlementBaseFeeUsd);
}
}
}

Impact

loss of fund

Tools Used

Manual review

Recommendations

check the amount out returned after swap

Updates

Lead Judging Commences

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

Support

FAQs

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