pragma solidity ^0.8.24;
interface IUniswapV2Callee {
function uniswapV2Call(
address sender,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external;
}
contract UniswapV2FlashSwap is IUniswapV2Callee {
address private constant UNISWAP_V2_FACTORY =
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address private constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
IUniswapV2Factory private constant factory =
IUniswapV2Factory(UNISWAP_V2_FACTORY);
IERC20 private constant weth = IERC20(WETH);
IUniswapV2Pair private immutable pair;
uint256 public amountToRepay;
constructor() {
pair = IUniswapV2Pair(factory.getPair(DAI, WETH));
}
function flashSwap(uint256 wethAmount) external {
bytes memory data = abi.encode(WETH, msg.sender);
pair.swap(0, wethAmount, address(this), data);
}
function uniswapV2Call(
address sender,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external {
require(msg.sender == address(pair), "not pair");
require(sender == address(this), "not sender");
(address tokenBorrow, address caller) =
abi.decode(data, (address, address));
require(tokenBorrow == WETH, "token borrow != WETH");
uint256 fee = (amount1 * 3) / 997 + 1;
amountToRepay = amount1 + fee;
weth.transferFrom(caller, address(this), fee);
weth.transfer(address(pair), amountToRepay);
}
}
interface IUniswapV2Pair {
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external;
}
interface IUniswapV2Factory {
function getPair(address tokenA, address tokenB)
external
view
returns (address pair);
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount)
external
returns (bool);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount)
external
returns (bool);
}
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint256 amount) external;
}
pragma solidity ^0.8.24;
import {Test} from "forge-std/Test.sol";
import "../../../src/defi/uniswap-v2-flash-swap/UniswapV2FlashSwap.sol";
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
contract UniswapV2FlashSwapTest is Test {
IWETH private weth = IWETH(WETH);
UniswapV2FlashSwap private uni = new UniswapV2FlashSwap();
function setUp() public {}
function testFlashSwap() public {
weth.deposit{value: 1e18}();
weth.approve(address(uni), 1e18);
uint256 amountToBorrow = 10 * 1e18;
uni.flashSwap(amountToBorrow);
assertGt(uni.amountToRepay(), amountToBorrow);
}
}