// Since this is an arbitrage contract for a MEV bot, including the source wouldn't make sense, but it's super simple. BYTECODE IS LAW ;p bytes memory bytecode = hex"608060405260015f5f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055506111f0806100655f395ff3fe608060405260043610610058575f3560e01c806309c5eabe1461006357806351cff8d91461007f57806353d6fd59146100a757806362308e85146100cf57806391dd7346146100f95780639b19251a146101355761005f565b3661005f57005b5f5ffd5b61007d600480360381019061007891906107b1565b610171565b005b34801561008a575f5ffd5b506100a560048036038101906100a09190610856565b610334565b005b3480156100b2575f5ffd5b506100cd60048036038101906100c891906108b6565b610468565b005b3480156100da575f5ffd5b506100e3610547565b6040516100f0919061094f565b60405180910390f35b348015610104575f5ffd5b5061011f600480360381019061011a91906107b1565b61055a565b60405161012c91906109d8565b60405180910390f35b348015610140575f5ffd5b5061015b60048036038101906101569190610856565b610723565b6040516101689190610a07565b60405180910390f35b60015f5f6101000a815c8160ff021916908315150217905d505f4790506e04444c5dc75cb358380d2e3de08a9073ffffffffffffffffffffffffffffffffffffffff166348c8949184846040518363ffffffff1660e01b81526004016101d8929190610a5a565b5f604051808303815f875af11580156101f3573d5f5f3e3d5ffd5b505050506040513d5f823e3d601f19601f8201168201806040525081019061021b9190610b96565b505f81476102299190610c13565b90505f811161026d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161026490610ca0565b60405180910390fd5b5f3273ffffffffffffffffffffffffffffffffffffffff168260405161029290610ceb565b5f6040518083038185875af1925050503d805f81146102cc576040519150601f19603f3d011682016040523d82523d5f602084013e6102d1565b606091505b5050905080610315576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161030c90610d49565b60405180910390fd5b5f5f5f6101000a815c8160ff021916908315150217905d505050505050565b5f5f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff166103bc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103b390610db1565b60405180910390fd5b5f8173ffffffffffffffffffffffffffffffffffffffff16476040516103e190610ceb565b5f6040518083038185875af1925050503d805f811461041b576040519150601f19603f3d011682016040523d82523d5f602084013e610420565b606091505b5050905080610464576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045b90610d49565b60405180910390fd5b5050565b5f5f3373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f9054906101000a900460ff166104f0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104e790610db1565b60405180910390fd5b805f5f8473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f6101000a81548160ff0219169083151502179055505050565b6e04444c5dc75cb358380d2e3de08a9081565b60605f5f905c906101000a900460ff166105a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105a090610e19565b60405180910390fd5b5f83838101906105b99190611087565b90505f5f90505b815181101561070a575f6e04444c5dc75cb358380d2e3de08a9073ffffffffffffffffffffffffffffffffffffffff16838381518110610603576106026110ce565b5b602002602001015160200151848481518110610622576106216110ce565b5b60200260200101515f01518585815181106106405761063f6110ce565b5b60200260200101516040015160405160200161065d92919061114b565b6040516020818303038152906040526040516106799190611172565b5f6040518083038185875af1925050503d805f81146106b3576040519150601f19603f3d011682016040523d82523d5f602084013e6106b8565b606091505b50509050806106fc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106f3906111d2565b60405180910390fd5b5080806001019150506105c0565b5060405180602001604052805f81525091505092915050565b5f602052805f5260405f205f915054906101000a900460ff1681565b5f604051905090565b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5f83601f84011261077157610770610750565b5b8235905067ffffffffffffffff81111561078e5761078d610754565b5b6020830191508360018202830111156107aa576107a9610758565b5b9250929050565b5f5f602083850312156107c7576107c6610748565b5b5f83013567ffffffffffffffff8111156107e4576107e361074c565b5b6107f08582860161075c565b92509250509250929050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610825826107fc565b9050919050565b6108358161081b565b811461083f575f5ffd5b50565b5f813590506108508161082c565b92915050565b5f6020828403121561086b5761086a610748565b5b5f61087884828501610842565b91505092915050565b5f8115159050919050565b61089581610881565b811461089f575f5ffd5b50565b5f813590506108b08161088c565b92915050565b5f5f604083850312156108cc576108cb610748565b5b5f6108d985828601610842565b92505060206108ea858286016108a2565b9150509250929050565b5f819050919050565b5f61091761091261090d846107fc565b6108f4565b6107fc565b9050919050565b5f610928826108fd565b9050919050565b5f6109398261091e565b9050919050565b6109498161092f565b82525050565b5f6020820190506109625f830184610940565b92915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6109aa82610968565b6109b48185610972565b93506109c4818560208601610982565b6109cd81610990565b840191505092915050565b5f6020820190508181035f8301526109f081846109a0565b905092915050565b610a0181610881565b82525050565b5f602082019050610a1a5f8301846109f8565b92915050565b828183375f83830152505050565b5f610a398385610972565b9350610a46838584610a20565b610a4f83610990565b840190509392505050565b5f6020820190508181035f830152610a73818486610a2e565b90509392505050565b5f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610ab682610990565b810181811067ffffffffffffffff82111715610ad557610ad4610a80565b5b80604052505050565b5f610ae761073f565b9050610af38282610aad565b919050565b5f67ffffffffffffffff821115610b1257610b11610a80565b5b610b1b82610990565b9050602081019050919050565b5f610b3a610b3584610af8565b610ade565b905082815260208101848484011115610b5657610b55610a7c565b5b610b61848285610982565b509392505050565b5f82601f830112610b7d57610b7c610750565b5b8151610b8d848260208601610b28565b91505092915050565b5f60208284031215610bab57610baa610748565b5b5f82015167ffffffffffffffff811115610bc857610bc761074c565b5b610bd484828501610b69565b91505092915050565b5f819050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610c1d82610bdd565b9150610c2883610bdd565b9250828203905081811115610c4057610c3f610be6565b5b92915050565b5f82825260208201905092915050565b7f4e6f2070726f666974206d6164650000000000000000000000000000000000005f82015250565b5f610c8a600e83610c46565b9150610c9582610c56565b602082019050919050565b5f6020820190508181035f830152610cb781610c7e565b9050919050565b5f81905092915050565b50565b5f610cd65f83610cbe565b9150610ce182610cc8565b5f82019050919050565b5f610cf582610ccb565b9150819050919050565b7f5472616e73666572206661696c656400000000000000000000000000000000005f82015250565b5f610d33600f83610c46565b9150610d3e82610cff565b602082019050919050565b5f6020820190508181035f830152610d6081610d27565b9050919050565b7f4e6f742077686974656c697374656400000000000000000000000000000000005f82015250565b5f610d9b600f83610c46565b9150610da682610d67565b602082019050919050565b5f6020820190508181035f830152610dc881610d8f565b9050919050565b7f4e6f7420657865637574696e67000000000000000000000000000000000000005f82015250565b5f610e03600d83610c46565b9150610e0e82610dcf565b602082019050919050565b5f6020820190508181035f830152610e3081610df7565b9050919050565b5f67ffffffffffffffff821115610e5157610e50610a80565b5b602082029050602081019050919050565b5f5ffd5b5f5ffd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b610e9e81610e6a565b8114610ea8575f5ffd5b50565b5f81359050610eb981610e95565b92915050565b610ec881610bdd565b8114610ed2575f5ffd5b50565b5f81359050610ee381610ebf565b92915050565b5f610efb610ef684610af8565b610ade565b905082815260208101848484011115610f1757610f16610a7c565b5b610f22848285610a20565b509392505050565b5f82601f830112610f3e57610f3d610750565b5b8135610f4e848260208601610ee9565b91505092915050565b5f60608284031215610f6c57610f6b610e62565b5b610f766060610ade565b90505f610f8584828501610eab565b5f830152506020610f9884828501610ed5565b602083015250604082013567ffffffffffffffff811115610fbc57610fbb610e66565b5b610fc884828501610f2a565b60408301525092915050565b5f610fe6610fe184610e37565b610ade565b9050808382526020820190506020840283018581111561100957611008610758565b5b835b8181101561105057803567ffffffffffffffff81111561102e5761102d610750565b5b80860161103b8982610f57565b8552602085019450505060208101905061100b565b5050509392505050565b5f82601f83011261106e5761106d610750565b5b813561107e848260208601610fd4565b91505092915050565b5f6020828403121561109c5761109b610748565b5b5f82013567ffffffffffffffff8111156110b9576110b861074c565b5b6110c58482850161105a565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f819050919050565b61111561111082610e6a565b6110fb565b82525050565b5f61112582610968565b61112f8185610cbe565b935061113f818560208601610982565b80840191505092915050565b5f6111568285611104565b600482019150611166828461111b565b91508190509392505050565b5f61117d828461111b565b915081905092915050565b7f43616c6c6261636b20657865637574696f6e206661696c6564000000000000005f82015250565b5f6111bc601983610c46565b91506111c782611188565b602082019050919050565b5f6020820190508181035f8301526111e9816111b0565b905091905056"; address addr; assembly { addr := create(DEPOSIT, add(bytecode, 0x20), mload(bytecode)) } BOT_ADDR = addr; }
To run the function, it requires uint8(STORAGE[0]) to be true (transient storage). It does not have any other access control. The Uniswap docs says “Always implement proper access control in your unlock callback. Only the PoolManager should be able to call it.”. Therefore, it’s very suspicious here. Let’s investigate how to make uint8(STORAGE[0]) true.
1 2 3 4 5 6
function unlockCallback(bytes rawData) public nonPayable { // [...] require(uint8(STORAGE[0]), Error('Not executing')); // [Call PoolManager with (selector, value, args) from rawData] // [...] }
The only place that make STORAGE[0] == 1 (transient storage) is at the beginning of the execute function. But STORAGE[0] be reset to 0 after the execute function ends. Therefore, we can only call the unlockCallback when the execute function is executing.
Note that tx.origin will be called if the BOT contract earn some ether. Therefore, we have a re-entrancy vulnerability here.
V4 decides whether to invoke specific hooks by inspecting the least significant bits of the address that the hooks contract is deployed to. For example, a hooks contract deployed to address: 0x0000000000000000000000000000000000002400 has the lowest bits ‘10 0100 0000 0000’ which would cause the ‘before initialize’ and ‘after add liquidity’ hooks to be used. See the Hooks library for the full spec.
After deploying the hook, we deploy our own pool to the PoolManager.
receive() external payable { // unlock the pool // if value is 1000 ether if (msg.value != 1000 ether) IPoolManager(POOL_MANAGER_ADDRESS).unlock(""); }
In our unlockCallback, we call BOT.unlockCallback to drain all the ether from the BOT. We use BOT.unlockCallback to invoke PoolManager.settleFor(PLAYER_ADDR). Then, we use PoolManager.take to withdraw the ethers.