-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathUniswapV2Arb2.sol
129 lines (111 loc) · 4.11 KB
/
UniswapV2Arb2.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {IUniswapV2Pair} from
"../../../src/interfaces/uniswap-v2/IUniswapV2Pair.sol";
import {IERC20} from "../../../src/interfaces/IERC20.sol";
error InsufficientProfit();
contract UniswapV2Arb2 {
struct FlashSwapData {
// Caller of flashSwap (msg.sender inside flashSwap)
address caller;
// Pair to flash swap from
address pair0;
// Pair to swap from
address pair1;
// True if flash swap is token0 in and token1 out
bool isZeroForOne;
// Amount in to repay flash swap
uint256 amountIn;
// Amount to borrow from flash swap
uint256 amountOut;
// Revert if profit is less than this minimum
uint256 minProfit;
}
// Exercise 1
// - Flash swap to borrow tokenOut
/**
* @param pair0 Pair contract to flash swap
* @param pair1 Pair contract to swap
* @param isZeroForOne True if flash swap is token0 in and token1 out
* @param amountIn Amount in to repay flash swap
* @param minProfit Minimum profit that this arbitrage must make
*/
function flashSwap(
address pair0,
address pair1,
bool isZeroForOne,
uint256 amountIn,
uint256 minProfit
) external {
// Write your code here
// Don’t change any other code
(uint112 reserve0, uint112 reserve1,) =
IUniswapV2Pair(pair0).getReserves();
// Hint - use getAmountOut to calculate amountOut to borrow
uint256 amountOut = isZeroForOne
? getAmountOut(amountIn, reserve0, reserve1)
: getAmountOut(amountIn, reserve1, reserve0);
bytes memory data = abi.encode(
FlashSwapData({
caller: msg.sender,
pair0: pair0,
pair1: pair1,
isZeroForOne: isZeroForOne,
amountIn: amountIn,
amountOut: amountOut,
minProfit: minProfit
})
);
IUniswapV2Pair(pair0).swap({
amount0Out: isZeroForOne ? 0 : amountOut,
amount1Out: isZeroForOne ? amountOut : 0,
to: address(this),
data: data
});
}
function uniswapV2Call(
address sender,
uint256 amount0Out,
uint256 amount1Out,
bytes calldata data
) external {
// Write your code here
// Don’t change any other code
// NOTE - anyone can call
FlashSwapData memory params = abi.decode(data, (FlashSwapData));
// Token in and token out from flash swap
address token0 = IUniswapV2Pair(params.pair0).token0();
address token1 = IUniswapV2Pair(params.pair0).token1();
(address tokenIn, address tokenOut) =
params.isZeroForOne ? (token0, token1) : (token1, token0);
(uint112 reserve0, uint112 reserve1,) =
IUniswapV2Pair(params.pair1).getReserves();
uint256 amountOut = params.isZeroForOne
? getAmountOut(params.amountOut, reserve1, reserve0)
: getAmountOut(params.amountOut, reserve0, reserve1);
IERC20(tokenOut).transfer(params.pair1, params.amountOut);
IUniswapV2Pair(params.pair1).swap({
amount0Out: params.isZeroForOne ? amountOut : 0,
amount1Out: params.isZeroForOne ? 0 : amountOut,
to: address(this),
data: ""
});
// NOTE - no need to calculate flash swap fee
IERC20(tokenIn).transfer(params.pair0, params.amountIn);
uint256 profit = amountOut - params.amountIn;
if (profit < params.minProfit) {
revert InsufficientProfit();
}
IERC20(tokenIn).transfer(params.caller, profit);
}
function getAmountOut(
uint256 amountIn,
uint256 reserveIn,
uint256 reserveOut
) internal pure returns (uint256 amountOut) {
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = reserveIn * 1000 + amountInWithFee;
amountOut = numerator / denominator;
}
}