Slippage Validation Remediation
How to add slippage protection to DEX swap operations by exposing minimum output parameters and bounded deadlines to callers.
Slippage Validation Remediation
Overview
Missing slippage protection allows MEV bots to execute sandwich attacks against any DEX swap. The remediation requires exposing a minAmountOut parameter to callers and validating a bounded deadline. Never use 0 as the minimum output or block.timestamp as the deadline.
Related Detector: Slippage Validation
Recommended Fix
Before (Vulnerable)
function swap(address tokenIn, address tokenOut, uint256 amount) external {
address[] memory path = new address[](2);
path[0] = tokenIn;
path[1] = tokenOut;
// amountOutMin = 0, deadline = now — trivially sandwich-able
router.swapExactTokensForTokens(amount, 0, path, msg.sender, block.timestamp);
}
After (Fixed)
function swap(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOutMin, // Caller specifies minimum acceptable output
uint256 deadline // Caller specifies transaction window
) external {
require(deadline > block.timestamp, "Deadline in past");
require(amountOutMin > 0, "No slippage protection");
address[] memory path = new address[](2);
path[0] = tokenIn;
path[1] = tokenOut;
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
IERC20(tokenIn).approve(address(router), amountIn);
router.swapExactTokensForTokens(amountIn, amountOutMin, path, msg.sender, deadline);
}
Alternative Mitigations
Protocol-enforced maximum slippage — compute minimum output from the current on-chain price and cap maximum slippage:
uint256 constant MAX_SLIPPAGE_BPS = 100; // 1%
function swap(address tokenIn, address tokenOut, uint256 amountIn, uint256 deadline) external {
address[] memory path = new address[](2);
path[0] = tokenIn;
path[1] = tokenOut;
uint256[] memory amounts = router.getAmountsOut(amountIn, path);
uint256 expectedOut = amounts[amounts.length - 1];
uint256 minOut = expectedOut * (10000 - MAX_SLIPPAGE_BPS) / 10000;
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
IERC20(tokenIn).approve(address(router), amountIn);
router.swapExactTokensForTokens(amountIn, minOut, path, msg.sender, deadline);
}
Private mempool for vault compounds — for automated vault strategies, submit compound transactions through MEV-protected RPC endpoints (Flashbots, MEV Blocker) in addition to protocol-level slippage.
Common Mistakes
Deriving minOut from the same pool’s spot price — if getAmountsOut() is called in the same transaction before the swap, an attacker can manipulate the pool to inflate expectedOut, then let the swap execute at the inflated price anyway (because minOut was derived from the manipulated price).
deadline = block.timestamp + 0 — equivalent to no deadline. Use at least block.timestamp + 15 minutes for user-initiated swaps.
amountOutMin = 1 — functionally equivalent to 0 for large swaps. Minimum must be a meaningful fraction of expected output.