Part 2

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

Overcommitment in Multi-Swap Initiation

Summary

The initiateSwap function allows users to initiate multiple swap requests without accounting for cumulative expectedAssetOut, risking overcommitment of vault assets.

Vulnerability Details

The initiateSwap function processes multiple swap requests by checking each request's expectedAssetOut against the vault's current balance but it fails to account for the cumulative expectedAssetOut across all requests within a single transaction. This oversight allows users to initiate multiple swap requests that individually pass balance checks but collectively exceed the vault's available balance.

Impact

I am rating this as a medium because it can lead to overcommitment of vault assets and potential asset draining.

Tools Used

Manual Review

Recommendations

I reccomend a cumulative tracking of expectedAssetOut for all swap requests within a single transaction to inforce the total does not exceed the vault's balance.

function initiateSwap(
uint128[] calldata vaultIds,
uint128[] calldata amountsIn,
uint128[] calldata minAmountsOut
)
external
{
// Perform length checks
if (vaultIds.length != amountsIn.length) {
revert Errors.ArrayLengthMismatch(vaultIds.length, amountsIn.length);
}
if (amountsIn.length != minAmountsOut.length) {
revert Errors.ArrayLengthMismatch(amountsIn.length, minAmountsOut.length);
}
// working data
InitiateSwapContext memory ctx;
// cache the vault's index token and asset addresses
Vault.Data storage currentVault = Vault.load(vaultIds[0]);
ctx.initialVaultIndexToken = currentVault.indexToken;
ctx.initialVaultCollateralAsset = currentVault.collateral.asset;
// load collateral data; must be enabled
Collateral.Data storage collateral = Collateral.load(ctx.initialVaultCollateralAsset);
collateral.verifyIsEnabled();
// load market making engine config
MarketMakingEngineConfiguration.Data storage configuration = MarketMakingEngineConfiguration.load();
// load usd token swap data
UsdTokenSwapConfig.Data storage tokenSwapData = UsdTokenSwapConfig.load();
// cache additional common fields
ctx.collateralPriceX18 = currentVault.collateral.getPrice();
ctx.maxExecTime = uint120(tokenSwapData.maxExecutionTime);
ctx.vaultAssetBalance = IERC20(ctx.initialVaultCollateralAsset).balanceOf(ctx.initialVaultIndexToken);
uint256 totalExpectedAssetOut = 0; // NEW: Initialize cumulative counter for expected asset out
for (uint256 i; i < amountsIn.length; i++) {
// for all but first iteration, refresh the vault and enforce same collateral asset
if (i != 0) {
currentVault = Vault.load(vaultIds[i]);
if (currentVault.collateral.asset != ctx.initialVaultCollateralAsset) {
revert Errors.VaultsCollateralAssetsMismatch();
}
ctx.vaultAssetBalance = IERC20(ctx.initialVaultCollateralAsset).balanceOf(currentVault.indexToken);
}
ctx.expectedAssetOut =
getAmountOfAssetOut(vaultIds[i], ud60x18(amountsIn[i]), ctx.collateralPriceX18).intoUint256();
if (ctx.expectedAssetOut == 0) revert Errors.ZeroOutputTokens();
if (ctx.expectedAssetOut < minAmountsOut[i]) {
revert Errors.SlippageCheckFailed(minAmountsOut[i], ctx.expectedAssetOut);
}
totalExpectedAssetOut += ctx.expectedAssetOut; // NEW: Accumulate expected asset out for cumulative balance check
if (ctx.vaultAssetBalance < totalExpectedAssetOut) { // NEW: Verify that cumulative expected asset out does not exceed vault balance
revert Errors.InsufficientVaultBalance(vaultIds[i], ctx.vaultAssetBalance, totalExpectedAssetOut);
}
ctx.usdTokenOfEngine = IERC20(configuration.usdTokenOfEngine[currentVault.engine]);
ctx.usdTokenOfEngine.safeTransferFrom(msg.sender, address(this), amountsIn[i]);
ctx.requestId = tokenSwapData.nextId(msg.sender);
UsdTokenSwapConfig.SwapRequest storage swapRequest = tokenSwapData.swapRequests[msg.sender][ctx.requestId];
swapRequest.minAmountOut = minAmountsOut[i];
swapRequest.vaultId = vaultIds[i];
swapRequest.assetOut = ctx.initialVaultCollateralAsset;
ctx.deadlineCache = uint120(block.timestamp) + ctx.maxExecTime;
swapRequest.deadline = ctx.deadlineCache;
swapRequest.amountIn = amountsIn[i];
emit LogInitiateSwap(
msg.sender,
ctx.requestId,
vaultIds[i],
amountsIn[i],
minAmountsOut[i],
ctx.initialVaultCollateralAsset,
ctx.deadlineCache
);
}
}
Updates

Lead Judging Commences

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

Support

FAQs

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

Give us feedback!