/// @title Sando /// @notice SPDX-License-Identifier: MIT /// @author 0xmouseless /// @notice gas optimized sandwich contract. // +-------------------------------------------+ // | Constants | // +-------------------------------------------+ #define constant MAX_SQRT_PRICE_LIMIT_X96 = 0xFFFD8963EFD1FC6A506488495D951D5263988D25 #define constant MIN_SQRT_PRICE_LIMIT_X96 = 0x1000276AD // `MAX_SQRT_PRICE_LIMIT_X96` is the gloabl upper bound for v3 swaps // `MIN_SQRT_PRICE_LIMIT_X96` is the global lower bound for v3 swaps #define constant FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984000000000000000000000000 #define constant PAIR_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54 #define constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 #define constant SEARCHER = 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf // +-------------------------------------------+ // | Functin Sigs | // +-------------------------------------------+ // Transfer(address to, uint value) #define constant TRANSFER_SIG = 0xa9059cbb00000000000000000000000000000000000000000000000000000000 // TransferFrom(address from, address to, uint value) #define constant TRANSFER_FROM_SIG = 0x23b872dd00000000000000000000000000000000000000000000000000000000 // Swap(uint amount0Out, uint amount1Out, address to, bytes data) #define constant V2_Swap_Sig = 0x022c0d9f00000000000000000000000000000000000000000000000000000000 // Swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes data) #define constant V3_SWAP_SIG = 0x128acb0800000000000000000000000000000000000000000000000000000000 // -- SAFETY IMPROVEMENTS -- // // * Make a base fee check for all frontrun methods (check tx.baseFee = block.baseFee) // this ensures that tx does not get mined in the wrong block (very rare edge case) // // * Store gas on stack at start of backrun methods and before returning, check if gas_used is // equal to 70% of start (gasUsed is set to 70% of gasLimit on botside). // This prevents our bundle from being frontrun by another tx targeting the same pool as us, // which could cause execution at an unexpected price (could lead to a net negative). #define macro MAIN() = takes (0) returns (0) { // extract function selector (JUMPDEST encoding) push0 // [0x00] calldataload // [calldata] push0 // [0x00, calldata] byte // [jumplabel] jump // [] // (UniswapV2) Weth is token0 && output v2_backrun0_astral_gate: v2_backrun0 // [v2_backrun0_label] jump // [] // (UniswapV2) Weth is token0 && input v2_frontrun0_astral_gate: v2_frontrun0 // [v2_frontrun0_label] jump // [] // (UniswapV2) Weth is token1 && output v2_backrun1_astral_gate: v2_backrun1 // [v2_backrun1_label] jump // [] // (UniswapV2) Weth is token1 && input v2_frontrun1_astral_gate: v2_frontrun1 // [v2_frontrun1_label] jump // [] // (UniswapV3) Weth is token0 && output v3_backrun0_astral_gate: v3_backrun0 // [v3_backrun0_label] jump // [] // (UniswapV3) Weth is token0 && input v3_frontrun0_astral_gate: v3_frontrun0 // [v3_frontrun0_label] jump // [] // (UniswapV3) Weth is token1 && output v3_backrun1_astral_gate: v3_backrun1 // [v3_backrun1_label] jump // [] // (UniswapV3) Weth is token1 && input v3_frontrun1_astral_gate: v3_frontrun1 // [v3_frontrun1_label] jump // [] seppuku_astral_gate: seppuku // [seppuku_label] jump // [] recover_eth_astral_gate: recover_eth // [recover_eth_label] jump // [] recover_weth_astral_gate: recover_weth // [recover_weth_label] jump // [] // wall of stops used so that the jumpdest of callback JUMPDEST equals 0xfa // 0xfa = first byte of function sig 0xfa461e33 stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop stop // calldata: int256 amount0Delta, int256 amount1Delta, bytes data uniswap_v3_callback: // Extract pool_key_hash for msg.sender validation 0x99 calldataload // setup variables for keccak256("FF", factory_addr, pool_key_hash, pair_init_code_hash) // ref : https://docs.uniswap.org/contracts/v3/reference/periphery/libraries/CallbackValidation // ref : https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/CallbackValidation.sol#L15 // ref : https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/PoolAddress.sol#L33 __RIGHTPAD(0xFF) 0x00 mstore // [pool_key_hash] *mstore(ff, 0) [FACTORY] chainid mstore // [pool_key_hash] *mstore(FACTORY, 1) 0x15 mstore // [pool_key_hash] *mstore(POOLKEYHASH, 21) [PAIR_INIT_CODE_HASH] 0x35 mstore // [] *mstore(PAIR_HASH, 51) // call keccak256 and transform result to address 0x55 0x00 sha3 // [keccak256_result] 0x000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF and // [pool_addr] // check if caller is equal to calculated pool addr caller eq iszero gtfo jumpi // [] * Check if msg.sender is pool // setup stack for when we call inputToken.transfer push0 // [retsize] push0 // [retoffset, retsize] 0x44 // 100 (4+32+32) // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] push0 // [value, argoffset, argsize, retoffset, retsize] // *** extract inputToken address // inputToken address bit mask 0x00ffffffffffffffffffffffffffffffffffffffff0000000000000000000000 0x84 calldataload and 0x58 shr // [token_in_addr, value, argoffset, argsize, retoffset, retsize] // extract isZeroForOne 0x84 calldataload 0xf8 shr // [isZeroForOne, token_in_addr, value, argoffset, argsize, retoffset, retsize] isZeroForOne jumpi // [token_in_addr, value, argoffset, argsize, retoffset, retsize] // // if execution continues here then isZeroForOne = false // // setup calldata (in memory) for transfer(to,value) [TRANSFER_SIG] push0 mstore // [token_in_addr, value, argoffset, argsize, retoffset, retsize] - mstore(0, 0x23b872dd) caller 0x04 mstore // [token_in_addr, value, argoffset, argsize, retoffset, retsize] - mstore(36, msg.sender) 0x24 calldataload 0x24 mstore // [token_in_addr, value, argoffset, argsize, retoffset, retsize] - mstore(56, amount1Delta) gas // [gas, token_in_addr, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [*pair_swap_args] stop // ****KEEP THIS LABEL UNJUMPABLE TO IF YOU EVER INCREASE FUNCTION DISPATCH FROM 1BYTES to 2BYTES**** // ****KEEP THIS LABEL UNJUMPABLE TO IF YOU EVER INCREASE FUNCTION DISPATCH FROM 1BYTES to 2BYTES**** // ****KEEP THIS LABEL UNJUMPABLE TO IF YOU EVER INCREASE FUNCTION DISPATCH FROM 1BYTES to 2BYTES**** // ****KEEP THIS LABEL UNJUMPABLE TO IF YOU EVER INCREASE FUNCTION DISPATCH FROM 1BYTES to 2BYTES**** isZeroForOne: // // if execution continues here then isZeroForOne = true // // setup calldata (in memory) for transfer(to,value) [TRANSFER_SIG] push0 mstore // [token_in_addr, value, argoffset, argsize, retoffset, retsize] - mstore(0, 0x23b872dd) caller 0x04 mstore // [token_in_addr, value, argoffset, argsize, retoffset, retsize] - mstore(36, msg.sender) 0x04 calldataload 0x24 mstore // [token_in_addr, value, argoffset, argsize, retoffset, retsize] - mstore(56, amount0Delta) gas // [gas, token_in_addr, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [*pair_swap_args] stop v3_frontrun1: // validate msg.sender [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] // ***Preset the stack for when we call pool.swap(recipient, zeroForOne, amountSpecified, sqrtPriceLimitX96, data) push0 // [retsize] push0 // [retoffset, retsize] 0xf9 // 249 (4+32+32+32+32+{32+32+53}) // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] push0 // [value, argoffset, argsize, retoffset, retsize] chainid calldataload 0x60 shr // [pair_address, value, argoffset, argsize, retoffset, retsize] = pair_swap_args // setup calldata for pool.swap(address(this), false, amountSpecified, minSqrtPriceLimitX96, data) [V3_SWAP_SIG] 0x00 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(0, 0x022c0d9f) address 0x04 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(4, address(this)) //0x00 0x24 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(36, false) 0x0186a0 callvalue mul 0x44 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(68, callvalue * 100000) [MAX_SQRT_PRICE_LIMIT_X96] 0x64 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(100, address(this)) // data part used for callback 0xa0 0x84 mstore // 100 // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(132, 0x80) *dynamic byte offset 0x35 0xa4 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(164, 0x02) *dynamic byte length // ** abi.encodePacked(isZeroForOne, input_token, pool_key_hash) [WETH] 0x58 shl 0xc4 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(196, isZeroForOne_&_InputTokenAddr) 0x15 calldataload 0xd9 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(217, pool_key_hash) // call pool.swap gas // [gas, pair_address, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [] *did swap succeed without failure? stop // (UniswapV3) Weth is token0 && input && has an offset of 0 v3_frontrun0: // validate msg.sender [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] // ***Preset the stack for when we call pool.swap(recipient, zeroForOne, amountSpecified, sqrtPriceLimitX96, data) push0 // [retsize] push0 // [retoffset, retsize] 0xf9 // 249 (4+32+32+32+32+{32+32+53}) // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] push0 // [value, argoffset, argsize, retoffset, retsize] chainid calldataload 0x60 shr // [pair_address, value, argoffset, argsize, retoffset, retsize] = pair_swap_args // setup calldata for pool.swap(address(this), true, amountSpecified, minSqrtPriceLimitX96, data) [V3_SWAP_SIG] 0x00 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(0, 0x022c0d9f) address 0x04 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(4, address(this)) 0x01 0x24 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(36, true) 0x0186a0 callvalue mul 0x44 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(68, callvalue * 100000) [MIN_SQRT_PRICE_LIMIT_X96] 0x64 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(100, address(this)) // data part used for callback 0xa0 0x84 mstore // 100 // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(132, 0x80) *dynamic byte offset 0x35 0xa4 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(164, 0x02) *dynamic byte length // ** abi.encodePacked(isZeroForOne, input_token, pool_key_hash) 0x0100000000000000000000000000000000000000000000000000000000000000 [WETH] 0x58 shl add 0xc4 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(196, isZeroForOne_&_InputTokenAddr) 0x15 calldataload 0xd9 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(217, pool_key_hash) // call pool.swap gas // [gas, pair_address, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [] *did swap succeed without failure? stop v3_backrun1: // validate msg.sender [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] // ***Preset the stack for when we call pool.swap(recipient, zeroForOne, amountSpecified, sqrtPriceLimitX96, data) push0 // [retsize] push0 // [retoffset, retsize] 0xf9 // 249 (4+32+32+32+32+{32+32+53}) // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] push0 // [value, argoffset, argsize, retoffset, retsize] chainid calldataload 0x60 shr // [pair_address, value, argoffset, argsize, retoffset, retsize] = pair_swap_args // setup calldata for pool.swap(address(this), true, amountSpecified, minSqrtPriceLimitX96, data) [V3_SWAP_SIG] 0x00 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(0, 0x022c0d9f) address 0x04 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(4, address(this)) 0x01 0x24 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(36, true) // get amountIn and store at memOffset (5 byte encoding) 0x49 calldataload dup1 0x08 shl swap1 // extract encodedValue by removing memOffset 0x00 byte mstore // extract memOffset and perform mstore [MIN_SQRT_PRICE_LIMIT_X96] 0x64 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(100, address(this)) // data part used for callback 0xa0 0x84 mstore // 100 // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(132, 0x80) *dynamic byte offset 0x35 0xa4 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(164, 0x02) *dynamic byte length // ** abi.encodePacked(isZeroForOne, input_token, pool_key_hash) 0x0100000000000000000000000000000000000000000000000000000000000000 0x15 calldataload 0x60 shr 0x58 shl // [input_token, pair_address, value, argoffset, argsize, retoffset, retsize] add 0xc4 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(196, isZeroForOne_&_InputTokenAddr) 0x29 calldataload 0xd9 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(217, pool_key_hash) // call pool.swap gas // [gas, pair_address, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [] *did swap succeed without failure? stop v3_backrun0: // validate msg.sender [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] // ***Preset the stack for when we call pool.swap(recipient, zeroForOne, amountSpecified, sqrtPriceLimitX96, data) push0 // [retsize] push0 // [retoffset, retsize] 0xf9 // 249 (4+32+32+32+32+{32+32+53}) // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] push0 // [value, argoffset, argsize, retoffset, retsize] chainid calldataload 0x60 shr // [pair_address, value, argoffset, argsize, retoffset, retsize] = pair_swap_args // setup calldata for pool.swap(address(this), false, amountSpecified, minSqrtPriceLimitX96, data) [V3_SWAP_SIG] 0x00 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(0, 0x022c0d9f) address 0x04 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(4, address(this)) // get amountIn and store at memOffset (5 byte encoding) 0x49 calldataload dup1 0x08 shl swap1 // extract encodedValue by removing memOffset 0x00 byte mstore // extract memOffset and perform mstore [MAX_SQRT_PRICE_LIMIT_X96] 0x64 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(100, address(this)) // data part used for callback 0xa0 0x84 mstore // 100 // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(132, 0x80) *dynamic byte offset 0x35 0xa4 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(164, 0x02) *dynamic byte length // ** abi.encodePacked(isZeroForOne, input_token, pool_key_hash) 0x15 calldataload 0x60 shr 0x58 shl // [input_token, pair_address, value, argoffset, argsize, retoffset, retsize] 0xc4 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(196, isZeroForOne_&_InputTokenAddr) 0x29 calldataload 0xd9 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(217, pool_key_hash) // call pool.swap gas // [gas, pair_address, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [] *did swap succeed without failure? stop // Make swap when Weth is token0 && output v2_backrun0: // validate msg.sender [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] // ***Preset the stack for when we call pair.swap(token0Out, token1Out, to, data)*** push0 // [retsize] push0 // [retoffset, retsize] 0xA4 // 164 (4 + 32 + 32 + 32 + {32 + 32}) // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] push0 // [value, argoffset, argsize, retoffset, retsize] chainid calldataload 0x60 shr // [pair_address, value, argoffset, argsize, retoffset, retsize] = pair_swap_args // ***Preset the stack for when we call token.transfer*** push0 // [retsize, *pair_swap_args] push0 // [retoffset, retsize, *pair_swap_args] = token_transfer_args // setup calldata (in memory) for transfer(to,value) [TRANSFER_SIG] push0 mstore // [*token_transfer_args, *pair_swap_args] - mstore(0, 0x23b872dd) dup3 0x04 mstore // [*token_transfer_args, *pair_swap_args] - mstore(36, pair_address) // get amountIn and store at memOffset (5 byte encoding) 0x29 calldataload dup1 0x08 shl swap1 // extract encodedValue by removing memOffset 0x00 byte mstore // extract memOffset and perform mstore // call tokenIn transfer(to,value) 0x44 // 68 (4+32+32) // [argsize, retoffset, retsize, *pair_swap_args] !expanded token_transfer_args push0 // [argoffset, argsize, retoffset, retsize, *pair_swap_args] push0 // [value, argoffset, argsize, retoffset, retsize, *pair_swap_args] = token_transfer_args 0x15 calldataload 0x60 shr // [token_in_addr, value, argoffset, argsize, retoffset, retsize, *pair_swap_args] gas // [gas, token_in_addr, value, argoffset, argsize, retoffset, retsize, *pair_swap_args] call pop // [*pair_swap_args] // [pair_address, value, argoffset, argsize, retoffset, retsize] !expanded pair_swap_args // setup calldata for swap(wethOut, 0, address(this), "") [V2_Swap_Sig] 0x00 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(0, 0x022c0d9f) 0x0186a0 callvalue mul 0x04 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(4, callvalue * 100000) 0x00 0x24 mstore // in adr // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(32, 0) address 0x44 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(68, address(this)) 0x80 0x64 mstore // 100 // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(100, 0x80) 4 + 32 + 32 + 32, 0x80 because bytes is dynamic // call pair.swap gas // [gas, pair_address, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [] *did swap succeed without failure? stop // Make swap when Weth is token1 && output v2_backrun1: // validate msg.sender [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] // ***Preset the stack for when we call pair.swap(token0Out, token1Out, to, data)*** push0 // [retsize] push0 // [retoffset, retsize] 0xA4 // 164 (4 + 32 + 32 + 32 + {32 + 32}) // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] push0 // [value, argoffset, argsize, retoffset, retsize] chainid calldataload 0x60 shr // [pair_address, value, argoffset, argsize, retoffset, retsize] = pair_swap_args // ***Preset the stack for when we call token.transfer*** push0 // [retsize, *pair_swap_args] push0 // [retoffset, retsize, *pair_swap_args] = token_transfer_args // setup calldata (in memory) for transfer(to,value) [TRANSFER_SIG] push0 mstore // [*token_transfer_args, *pair_swap_args] - mstore(0, 0x23b872dd) dup3 0x04 mstore // [*token_transfer_args, *pair_swap_args] - mstore(36, pair_address) // get amountIn and store at memOffset (5 byte encoding) 0x29 calldataload dup1 0x08 shl swap1 // extract encodedValue by removing memOffset 0x00 byte mstore // extract memOffset and perform mstore // call tokenIn transfer(to,value) 0x44 // 100 (4+32+32) // [argsize, retoffset, retsize, *pair_swap_args] !expanded token_transfer_args push0 // [argoffset, argsize, retoffset, retsize, *pair_swap_args] push0 // [value, argoffset, argsize, retoffset, retsize, *pair_swap_args] = token_transfer_args 0x15 calldataload 0x60 shr // [token_in_addr, value, argoffset, argsize, retoffset, retsize, *pair_swap_args] gas // [gas, token_in_addr, value, argoffset, argsize, retoffset, retsize, *pair_swap_args] call pop // [*pair_swap_args] // [pair_address, value, argoffset, argsize, retoffset, retsize] !expanded pair_swap_args // setup calldata for swap(0, wethOut, address(this), "") [V2_Swap_Sig] 0x00 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(0, 0x022c0d9f) 0x00 0x04 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(4, amount1Out) 0x0186a0 callvalue mul 0x24 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(36, callvalue * 100000) address 0x44 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(68, address(this)) 0x80 0x64 mstore // 100 // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(100, 0x80) 4 + 32 + 32 + 32, 0x80 because bytes is dynamic // call pair.swap gas // [gas, pair_address, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [] *did swap succeed without failure? stop // Make swap when Weth is token0 && input v2_frontrun0: // validate msg.sender [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] // ***Preset the stack for when we call pair.swap(token0Out, token1Out, to, data)*** push0 // [retsize] push0 // [retoffset, retsize] 0xA4 // 164 (4 + 32 + 32 + 32 + {32 + 32}) // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] push0 // [value, argoffset, argsize, retoffset, retsize] chainid calldataload 0x60 shr // [pair_address, value, argoffset, argsize, retoffset, retsize] = pair_swap_args // ***Preset the stack for when we call token.transfer*** push0 // [retsize, *pair_swap_args] push0 // [retoffset, retsize, *pair_swap_args] = token_transfer_args // setup calldata for transferFrom(from,to,value) [TRANSFER_FROM_SIG] push0 mstore // [*token_transfer_args, *pair_swap_args] - mstore(0, 0x23b872dd) address 0x04 mstore // [*token_transfer_args, *pair_swap_args] - mstore(4, address(this)) dup3 0x24 mstore // [*token_transfer_args, *pair_swap_args] - mstore(36, pair_address) 0x0186a0 callvalue mul 0x44 mstore // [*token_transfer_args, *pair_swap_args] - mstore(68, callvalue * 100000) // call weth transferFrom(from,to,value) 0x64 // 100 (4+32+32+32) // [argsize, retoffset, retsize, *pair_swap_args] !expanded token_transfer_args push0 // [argoffset, argsize, retoffset, retsize, *pair_swap_args] push0 // [value, argoffset, argsize, retoffset, retsize, *pair_swap_args] = token_transfer_args [WETH] // [weth_addr, value, argoffset, argsize, retoffset, retsize, *pair_swap_args] gas // [gas, weth_addr, value, argoffset, argsize, retoffset, retsize, *pair_swap_args] call pop // [*pair_swap_args] // [pair_address, value, argoffset, argsize, retoffset, retsize] !expanded pair_swap_args // setup calldata for swap(0, otherTokenOut, address(this), "") [V2_Swap_Sig] 0x00 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(0, 0x022c0d9f) 0x00 0x04 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(4, 0) 0x00 0x24 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(36, 0) **Clear Memory** // get amountOut and store at memOffset (5 byte encoding) 0x15 calldataload dup1 0x08 shl swap1 // extract encodedValue by removing memOffset 0x00 byte mstore // extract memOffset and perform mstore address 0x44 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(68, address(this)) 0x80 0x64 mstore // 100 // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(100, 0x80) 4 + 32 + 32 + 32, 0x80 because bytes is dynamic // call pair.swap gas // [gas, pair_address, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [] *did swap succeed without failure? stop // Make swap when Weth is token1 && input v2_frontrun1: // validate msg.sender [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] // ***Preset the stack for when we call pair.swap(token0Out, token1Out, to, data)*** push0 // [retsize] push0 // [retoffset, retsize] 0xA4 // 164 (4 + 32 + 32 + 32 + {32 + 32}) // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] push0 // [value, argoffset, argsize, retoffset, retsize] chainid calldataload 0x60 shr // [pair_address, value, argoffset, argsize, retoffset, retsize] = pair_swap_args // ***Preset the stack for when we call token.transfer*** push0 // [retsize, *pair_swap_args] push0 // [retoffset, retsize, *pair_swap_args] = token_transfer_args // setup calldata for transferFrom(from,to,value) [TRANSFER_FROM_SIG] push0 mstore // [*token_transfer_args, *pair_swap_args] - mstore(0, 0x23b872dd) address 0x04 mstore // [*token_transfer_args, *pair_swap_args] - mstore(4, address(this)) dup3 0x24 mstore // [*token_transfer_args, *pair_swap_args] - mstore(36, pair_address) 0x0186a0 callvalue mul 0x44 mstore // [*token_transfer_args, *pair_swap_args] - mstore(68, callvalue * 100000) // call weth transferFrom(from,to,value) 0x64 // 100 (4+32+32+32) // [argsize, retoffset, retsize, *pair_swap_args] !expanded token_transfer_args push0 // [argoffset, argsize, retoffset, retsize, *pair_swap_args] push0 // [value, argoffset, argsize, retoffset, retsize, *pair_swap_args] = token_transfer_args [WETH] // [weth_addr, value, argoffset, argsize, retoffset, retsize, *pair_swap_args] gas // [gas, weth_addr, value, argoffset, argsize, retoffset, retsize, *pair_swap_args] call pop // [*pair_swap_args] // setup calldata for swap(otherTokenOut, 0, address(this), "") [V2_Swap_Sig] 0x00 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(0, 0x022c0d9f) 0x00 0x04 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(4, 0) **Clear Memory** // get amountOut and store at memOffset (5 byte encoding) 0x15 calldataload dup1 0x08 shl swap1 // extract encodedValue by removing memOffset 0x00 byte mstore // extract memOffset and perform mstore 0x00 0x24 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(36, 0) address 0x44 mstore // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(68, address(this)) 0x80 0x64 mstore // 100 // [pair_address, value, argoffset, argsize, retoffset, retsize] - mstore(100, 0x80) 4 + 32 + 32 + 32, 0x80 because bytes is dynamic // call pair.swap gas // [gas, pair_address, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [] *did swap succeed without failure? stop // incase you deploy with metamorphic factory seppuku: // validate caller [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] caller // [caller] selfdestruct // [] stop recover_eth: // validate caller [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] push0 // [retsize] push0 // [retoffset, retsize] push0 // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] selfbalance // [contract_value, argoffset, argsize, retoffset, retsize] caller // [caller, contract_value, argoffset, argsize, retoffset, retsize] gas // [gas, caller, contract_value, argoffset, argsize, retoffset, retsize] call // [] stop // +-----------+---------------------------+ // | Bytes | Value | // +-----------+---------------------------+ // | 32 | AmountOut | // +-----------+---------------------------+ recover_weth: // validate caller [SEARCHER] caller eq // [is_caller_searcher] iszero // [!is_caller_searcher] gtfo // [gtfo_label, is_caller_searcher] jumpi // [] // prepare variables for transfer call [TRANSFER_SIG] msize mstore // store 0xa9059cbb as mem(0x00) caller 0x04 mstore // store caller at mem(0x04) chainid calldataload 0x24 mstore // store amountOut at mem(0x24) // make transfer call push0 // [retsize] push0 // [retoffset, retsize] 0x44 // 68 (4 + 32 + 32) // [argsize, retoffset, retsize] push0 // [argoffset, argsize, retoffset, retsize] push0 // [value, argoffset, argsize, retoffset, retsize] [WETH] // [weth_addr, value, argoffset, argsize, retoffset, retsize] gas // [gas, weth_addr, value, argoffset, argsize, retoffset, retsize] call iszero gtfo jumpi // [] * did call succeed? stop gtfo: 0x03 // [3] dup1 // [3, 3] revert // revert (3,3) for wagmi }