-
Notifications
You must be signed in to change notification settings - Fork 522
/
Sandwich.yulp
154 lines (130 loc) · 3.91 KB
/
Sandwich.yulp
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/// @title Sandwich contract
object "Sandwich" {
// Constructor
code {
// constructor(address owner)
// saves owner into bytecode (IMMUTABLE VARIABLE)
// no SLOAD
codecopy(datasize("Runtime"), sub(codesize(), 32), 32)
// copy runtime code
datacopy(0, dataoffset("Runtime"), datasize("Runtime"))
// return runtime code and shoehorned immutable variable
return(0, add(datasize("Runtime"), 32))
}
// Runtime code
object "Runtime" {
code {
const _calldata := 128 // free memory pointer
// Function signatures
const _erc20_xfer_sig := 0xa9059cbb
const _pair_swap_sig := 0x022c0d9f
// Extract out IMMUTABLE VARIABLE owner
// Avoids SLOAD
// Read more - https://github.com/libevm/cpo
datacopy(0, datasize("Runtime"), 32)
let owner := mload(0)
// Not owner? Revert, but still (3, 3) wgmi I guess
if iszero(eq(caller(), owner)) {
revert (3, 3)
}
// Check function sigs
calldatacopy(_calldata, 0, calldatasize())
switch mslice(_calldata, 4)
// RecoverERC20 token
case sig"recoverERC20(address token, uint256 balance)" {
mstruct recoverERC20Calldata(sig: 4, token: 32, balance: 32)
// mstore left pads zeros
// so we need to start from 252 when doing call
// 224 + 32 - 4
mstore(224, _erc20_xfer_sig)
// transfer(address recipient, uint256 amount)
mstore(256, owner)
mstore(288, recoverERC20Calldata.balance(_calldata))
// Call transfer
if iszero(
call(
gas(),
recoverERC20Calldata.token(_calldata), // Target
0, // Value
252, // Memory offset 224 + 28
68, // Length 4 + 32 + 32
224, // Return offset
32 // Return length
)
) {
revert (3, 3)
}
return (0, 0)
}
// Sandwich
default {
// No function sig, boom
mstruct sandwichCalldata(
token: 20,
pair: 20,
amountIn: 16,
amountOut: 16,
tokenOut: 1
)
// *** Calls token.transfer(pair, amountIn) ****
mstore(224, _erc20_xfer_sig)
// transfer(address recipient, uint256 amount)
mstore(256, sandwichCalldata.pair(_calldata))
mstore(288, sandwichCalldata.amountIn(_calldata))
// Call transfer
if iszero(
call(
gas(),
sandwichCalldata.token(_calldata), // Target
0, // Value
252, // Memory offset 224 + 28
68, // Length 4 + 32 + 32
0, // Return offset
0 // Return length
)
) {
revert (3, 3)
}
// *********
/*
calls pair.swap(
tokenOutNo == 0 ? amountOut : 0,
tokenOutNo == 1 ? amountOut : 0,
address(this),
new bytes(0)
)
*/
mstore(224, _pair_swap_sig)
// token0 or token1
switch sandwichCalldata.tokenOut(_calldata)
case 0 {
mstore(256, sandwichCalldata.amountOut(_calldata))
mstore(288, sandwichCalldata.tokenOut(_calldata))
}
case 1 {
mstore(256, sandwichCalldata.tokenOut(_calldata))
mstore(288, sandwichCalldata.amountOut(_calldata))
}
// address(this)
mstore(320, address())
// new bytes(0) - empty bytes
mstore(352, 0x80)
if iszero(
call(
gas(),
sandwichCalldata.pair(_calldata), // Target
0, // Value
252, // Memory offset 224 + 28
164, // Length 4 + 32 + 32
0, // Return offset
0 // Return length
)
) {
revert (3, 3)
}
}
}
// Stop execution here
stop()
}
}