DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Lack of Slippage Protection in _doDexSwap

Summary

The _doDexSwap function currently lacks slippage protection, which can lead to potential issues where the output
amount from the swap is significantly lower than expected. This can result in substantial losses for users.

Vulnerability Details

Current implementation:

function _doDexSwap(bytes memory data, bool isCollateralToIndex) internal returns (uint256 outputAmount) {
(address to, uint256 amount, bytes memory callData) = abi.decode(data, (address, uint256, bytes));
IERC20 inputToken;
IERC20 outputToken;
if (isCollateralToIndex) {
inputToken = collateralToken;
outputToken = IERC20(indexToken);
} else {
inputToken = IERC20(indexToken);
outputToken = collateralToken;
}
uint256 balBefore = outputToken.balanceOf(address(this));
ParaSwapUtils.swap(to, callData);
outputAmount = IERC20(outputToken).balanceOf(address(this)) - balBefore;
// no slippage protection or minimum output amount check
emit DexSwap(address(inputToken), amount, address(outputToken), outputAmount, isCollateralToIndex);
}

Issues:

  1. No check to ensure output amount meets a minimum threshold

  2. No slippage protection to prevent significant losses during swaps

  3. Potential for significant value loss if the swap rate changes drastically

Impact

MEDIUM severity because:

  1. Users can suffer significant losses due to unfavorable swap rates

  2. Lack of slippage protection can lead to unexpected and undesirable outcomes

  3. Compromises the integrity and trustworthiness of the protocol

Recommendations

  1. Add slippage protection to _doDexSwap:

function _doDexSwap(bytes memory data, bool isCollateralToIndex, uint256 minOutputAmount) internal returns (uint256 outputAmount) {
(address to, uint256 amount, bytes memory callData) = abi.decode(data, (address, uint256, bytes));
IERC20 inputToken;
IERC20 outputToken;
if (isCollateralToIndex) {
inputToken = collateralToken;
outputToken = IERC20(indexToken);
} else {
inputToken = IERC20(indexToken);
outputToken = collateralToken;
}
uint256 balBefore = outputToken.balanceOf(address(this));
ParaSwapUtils.swap(to, callData);
outputAmount = IERC20(outputToken).balanceOf(address(this)) - balBefore;
// Ensure output amount is greater than zero and meets the minimum output amount for slippage protection
require(outputAmount > 0, "Output amount must be greater than zero");
require(outputAmount >= minOutputAmount, "Slippage protection: output amount is less than minimum required");
emit DexSwap(address(inputToken), amount, address(outputToken), outputAmount, isCollateralToIndex);
}
  1. Update _runSwap to include minOutputAmount parameter:

function _runSwap(bytes[] memory metadata, bool isCollateralToIndex, MarketPrices memory prices, uint256 minOutputAmount)
internal
returns (bool completed)
{
if (metadata.length == 0) {
revert Error.InvalidData();
}
if (metadata.length == 2) {
(PROTOCOL _protocol, bytes memory data) = abi.decode(metadata[0], (PROTOCOL, bytes));
if (_protocol != PROTOCOL.DEX) {
revert Error.InvalidData();
}
swapProgressData.swapped = swapProgressData.swapped + _doDexSwap(data, isCollateralToIndex, minOutputAmount);
(_protocol, data) = abi.decode(metadata[1], (PROTOCOL, bytes));
if (_protocol != PROTOCOL.GMX) {
revert Error.InvalidData();
}
_doGmxSwap(data, isCollateralToIndex);
return false;
} else {
if (metadata.length != 1) {
revert Error.InvalidData();
}
(PROTOCOL _protocol, bytes memory data) = abi.decode(metadata[0], (PROTOCOL, bytes));
if (_protocol == PROTOCOL.DEX) {
uint256 outputAmount = _doDexSwap(data, isCollateralToIndex, minOutputAmount);
// update global state
if (flow == FLOW.DEPOSIT) {
_mint(counter, outputAmount + swapProgressData.swapped, true, prices);
} else if (flow == FLOW.WITHDRAW) {
_handleReturn(outputAmount + swapProgressData.swapped, false, true);
} else {
_updateState(!isCollateralToIndex, isCollateralToIndex);
}
return true;
} else {
_doGmxSwap(data, isCollateralToIndex);
return false;
}
}
}

This ensures that the minOutputAmount parameter is passed along and used in _doDexSwap to provide slippage
protection.

Updates

Lead Judging Commences

n0kto Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

invalid_swap_slippage_and_deadline

Slippage and deadline are handled externally. Paraswap implementation used by the current code (behind the proxy): https://etherscan.io/address/0xdffd706ee98953d3d25a3b8440e34e3a2c9beb2c GMX code: https://github.com/gmx-io/gmx-synthetics/blob/caf3dd8b51ad9ad27b0a399f668e3016fd2c14df/contracts/order/OrderUtils.sol#L150C15-L150C33

Support

FAQs

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