Skip to main content
Sigvex

Sandwich Attack Remediation

Enforce minimum-output and deadline checks on swap functions so MEV searchers cannot extract value by bracketing trades.

Sandwich Attack Remediation

Overview

A sandwich attack frontruns a victim’s swap to move the price, lets the victim trade at the worse rate, then backruns to reverse the move and capture the difference. Any swap function that fills at whatever price the pool offers — without a caller-supplied minimum output and a deadline — is exposed. The fix is to make the caller specify both bounds and to enforce them on chain.

Related Detector: Sandwich Attack

Before (Vulnerable)

function swap(address tokenIn, uint256 amountIn) external returns (uint256) {
    uint256 amountOut = pool.swap(tokenIn, amountIn);
    IERC20(tokenOut).transfer(msg.sender, amountOut);
    return amountOut;
}

After (Fixed)

function swap(
    address tokenIn,
    uint256 amountIn,
    uint256 minAmountOut,
    uint256 deadline
) external returns (uint256) {
    require(block.timestamp <= deadline, "Transaction expired");
    uint256 amountOut = pool.swap(tokenIn, amountIn);
    require(amountOut >= minAmountOut, "Insufficient output");
    IERC20(tokenOut).transfer(msg.sender, amountOut);
    return amountOut;
}

minAmountOut caps how much price slippage the trade tolerates, so a frontrun that pushes the price past that bound causes the transaction to revert instead of filling at a loss. deadline prevents a transaction from sitting in the mempool and executing later under conditions the caller never approved.

Alternative Mitigations

  • Submit trades through a private transaction relay so they do not appear in the public mempool before inclusion.
  • Compute minAmountOut from an independent oracle price at submission time rather than from the pool’s own quote, which an attacker can manipulate in the same block.
  • Use commit-reveal or batch-auction settlement designs that remove the ordering advantage MEV searchers rely on.

Common Mistakes

  • Defaulting minAmountOut to zero, which disables the protection entirely while appearing to support it.
  • Deriving the minimum from getAmountsOut on the same pool in the same transaction — an attacker moves that quote before the swap executes.
  • Setting deadline to type(uint256).max, which neutralizes the expiry check.
  • Validating slippage in an outer function but exposing an inner swap helper without the same checks.

References