Repository: solidquant/whack-a-mole Branch: main Commit: 290840b7de88 Files: 51 Total size: 630.1 KB Directory structure: gitextract_xq82mp3m/ ├── .gitignore ├── .gitmodules ├── README.md ├── abi/ │ ├── ERC20.json │ ├── UniswapV2Pool.json │ ├── UniswapV2Router2.json │ ├── UniswapV3Pool.json │ ├── UniswapV3Quoter2.json │ ├── UniswapV3SwapRouter2.json │ └── WETH.json ├── addresses/ │ ├── __init__.py │ ├── arbitrum.py │ ├── ethereum.py │ └── polygon.py ├── configs.py ├── contracts/ │ ├── foundry.toml │ ├── src/ │ │ ├── SimulatorV1.sol │ │ ├── WhackAMoleBotV1.sol │ │ ├── lib/ │ │ │ └── SafeTransfer.sol │ │ └── protocols/ │ │ ├── IERC20.sol │ │ ├── IWETH.sol │ │ ├── curve/ │ │ │ └── ICurvePool.sol │ │ └── uniswap/ │ │ ├── IQuoterV2.sol │ │ ├── IUniswapV2Pair.sol │ │ ├── IUniswapV2Router.sol │ │ ├── IUniswapV3SwapRouter.sol │ │ └── UniswapV2Library.sol │ └── test/ │ └── WhackAMoleBotV1.t.sol ├── data/ │ ├── __init__.py │ ├── cex.py │ ├── cex_streams.py │ ├── dex.py │ ├── dex_streams.py │ └── utils.py ├── examples/ │ └── dex.py ├── execution/ │ ├── WhackAMoleBotV1.json │ ├── __init__.py │ └── dex_order.py ├── external/ │ ├── __init__.py │ ├── influxdb.py │ └── telegram_bot.py ├── main.py ├── requirements.txt ├── simulation/ │ ├── SimulatorV1.json │ ├── __init__.py │ ├── online_simulator.py │ ├── uniswap_v2.py │ └── uniswap_v3.py ├── strategies/ │ └── dex_arb_base.py └── tests/ ├── test_WhackAMoleBotV1.py └── test_simulation.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Dotenv file .env # Mac .DS_Store # IDE .idea/ # Cache / Env venv/ __pycache__/ # Large files *.pyc *.zip *.csv # Compiler files contracts/cache/ contracts/out/ # Ignores development broadcast logs !/broadcast /broadcast/*/31337/ /broadcast/**/dry-run/ # Docs docs/ # Test scripts scripts/ ================================================ FILE: .gitmodules ================================================ [submodule "contracts/lib/forge-std"] path = contracts/lib/forge-std url = https://github.com/foundry-rs/forge-std [submodule "contracts/lib/openzeppelin-contracts"] path = contracts/lib/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts ================================================ FILE: README.md ================================================ # Whack-A-Mole

The image is of Dugtrio from Pokemon. *And the banner is... Pokemon. I'm a Pokemon fan, what more can I say?* --- ### What the heck? Whack-A-Mole is a CEX-DEX arbitrage bot written in Python. Arbitrage strategies are like the global Whack-A-Mole game played in parallel. Multiple players participate to find the mole that pops up, and jump to capture that opportunity. Who knows who'll win... What we know for certain is that you'll need a fast pair of eyes on the market at all times, and an extra fast execution engine to capture the moment without latency. Will our beloved Python be able to accomplish this? We'll see 😎 ### Example Strategy #1: DEX arbitrage The **main** branch is likely to go through a lot of changes, so to run an example that runs without breaking, you should switch to the **examples/strategy/dex_arb_base** branch before running **main.py**. Run: ``` git checkout examples/strategy/dex_arb_base ``` ### Example Strategy #2: CEX-DEX arbitrage #### You said this is a CEX-DEX arbitrage bot, where the f is it? I know...🥲 I'm still actively researching the CEX-DEX arbitrage space. Everyone interested in the process can go visit my other repository which is actually a derivative of Whack-A-Mole: https://github.com/solidquant/cex-dex-arb-research This research template is an attempt to find alphas within the crypto space. You can focus on DEX only arbs, CEX only arbs, and also CEX-DEX arbs using this template. ### 🛠 Recent Updates (2023.08.08): 1. **asyncio error**: added nest_asyncio 2. **.env error**: added some randomly generated sample private keys, addresses for people that want a complete testing environment 3. **requirements.txt**: web3, flashbots, websockets often times have conflicting versions. You sometimes need to delete their fields in the requirements.txt file and install them manually 4. **Telegram bot**: updated the code so that the bot runs without having to set the Telegram token in .env Now with these issues resolved, you can easily test this bot out. It is set to debug, and the private keys are set at random, so you don't need to worry! Just run: ```python import asyncio import nest_asyncio from strategies.dex_arb_base import main if __name__ == '__main__': nest_asyncio.apply() asyncio.run(main()) ``` This code is in **main.py**. ```bash python main.py ``` --- Check out my blog post describing in detail what this project attempts to do, and how you can use it. [Go to blog 👉](https://medium.com/@solidquant/how-i-built-my-first-mev-arbitrage-bot-introducing-whack-a-mole-66d91657152e) --- ⚡️ For readers that want to talk about MEV and any other quant related stuff with people, please join my Discord! There’s currently no one on the Discord server, so it’s not too active yet, but I hope to meet new people there! 🌎🪐 https://discord.com/invite/e6KpjTQP98 ================================================ FILE: abi/ERC20.json ================================================ [ { "constant": true, "inputs": [], "name": "name", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_spender", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "approve", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "totalSupply", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_from", "type": "address" }, { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "transferFrom", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "decimals", "outputs": [ { "name": "", "type": "uint8" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "balance", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [], "name": "symbol", "outputs": [ { "name": "", "type": "string" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "transfer", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [ { "name": "_owner", "type": "address" }, { "name": "_spender", "type": "address" } ], "name": "allowance", "outputs": [ { "name": "", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "payable": true, "stateMutability": "payable", "type": "fallback" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "owner", "type": "address" }, { "indexed": true, "name": "spender", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "from", "type": "address" }, { "indexed": true, "name": "to", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "Transfer", "type": "event" } ] ================================================ FILE: abi/UniswapV2Pool.json ================================================ [{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0Out","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1Out","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint112","name":"reserve0","type":"uint112"},{"indexed":false,"internalType":"uint112","name":"reserve1","type":"uint112"}],"name":"Sync","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":true,"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MINIMUM_LIQUIDITY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint112","name":"_reserve0","type":"uint112"},{"internalType":"uint112","name":"_reserve1","type":"uint112"},{"internalType":"uint32","name":"_blockTimestampLast","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_token0","type":"address"},{"internalType":"address","name":"_token1","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"kLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"price0CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"price1CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"skim","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount0Out","type":"uint256"},{"internalType":"uint256","name":"amount1Out","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"sync","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] ================================================ FILE: abi/UniswapV2Router2.json ================================================ [{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_WETH","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"reserveIn","type":"uint256"},{"internalType":"uint256","name":"reserveOut","type":"uint256"}],"name":"getAmountIn","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"reserveIn","type":"uint256"},{"internalType":"uint256","name":"reserveOut","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsIn","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsOut","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"reserveA","type":"uint256"},{"internalType":"uint256","name":"reserveB","type":"uint256"}],"name":"quote","outputs":[{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETHSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermit","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermitSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityWithPermit","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapETHForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETHSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] ================================================ FILE: abi/UniswapV3Pool.json ================================================ [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"int24","name":"tickLower","type":"int24"},{"indexed":true,"internalType":"int24","name":"tickUpper","type":"int24"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"int24","name":"tickLower","type":"int24"},{"indexed":true,"internalType":"int24","name":"tickUpper","type":"int24"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"}],"name":"Collect","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"}],"name":"CollectProtocol","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paid0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"paid1","type":"uint256"}],"name":"Flash","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"observationCardinalityNextOld","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"observationCardinalityNextNew","type":"uint16"}],"name":"IncreaseObservationCardinalityNext","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"indexed":false,"internalType":"int24","name":"tick","type":"int24"}],"name":"Initialize","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"int24","name":"tickLower","type":"int24"},{"indexed":true,"internalType":"int24","name":"tickUpper","type":"int24"},{"indexed":false,"internalType":"uint128","name":"amount","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"feeProtocol0Old","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"feeProtocol1Old","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"feeProtocol0New","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"feeProtocol1New","type":"uint8"}],"name":"SetFeeProtocol","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"int256","name":"amount0","type":"int256"},{"indexed":false,"internalType":"int256","name":"amount1","type":"int256"},{"indexed":false,"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"indexed":false,"internalType":"uint128","name":"liquidity","type":"uint128"},{"indexed":false,"internalType":"int24","name":"tick","type":"int24"}],"name":"Swap","type":"event"},{"inputs":[{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint128","name":"amount","type":"uint128"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint128","name":"amount0Requested","type":"uint128"},{"internalType":"uint128","name":"amount1Requested","type":"uint128"}],"name":"collect","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint128","name":"amount0Requested","type":"uint128"},{"internalType":"uint128","name":"amount1Requested","type":"uint128"}],"name":"collectProtocol","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeGrowthGlobal0X128","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeGrowthGlobal1X128","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"observationCardinalityNext","type":"uint16"}],"name":"increaseObservationCardinalityNext","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidity","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLiquidityPerTick","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mint","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"observations","outputs":[{"internalType":"uint32","name":"blockTimestamp","type":"uint32"},{"internalType":"int56","name":"tickCumulative","type":"int56"},{"internalType":"uint160","name":"secondsPerLiquidityCumulativeX128","type":"uint160"},{"internalType":"bool","name":"initialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32[]","name":"secondsAgos","type":"uint32[]"}],"name":"observe","outputs":[{"internalType":"int56[]","name":"tickCumulatives","type":"int56[]"},{"internalType":"uint160[]","name":"secondsPerLiquidityCumulativeX128s","type":"uint160[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"positions","outputs":[{"internalType":"uint128","name":"liquidity","type":"uint128"},{"internalType":"uint256","name":"feeGrowthInside0LastX128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthInside1LastX128","type":"uint256"},{"internalType":"uint128","name":"tokensOwed0","type":"uint128"},{"internalType":"uint128","name":"tokensOwed1","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFees","outputs":[{"internalType":"uint128","name":"token0","type":"uint128"},{"internalType":"uint128","name":"token1","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"feeProtocol0","type":"uint8"},{"internalType":"uint8","name":"feeProtocol1","type":"uint8"}],"name":"setFeeProtocol","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slot0","outputs":[{"internalType":"uint160","name":"sqrtPriceX96","type":"uint160"},{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"uint16","name":"observationIndex","type":"uint16"},{"internalType":"uint16","name":"observationCardinality","type":"uint16"},{"internalType":"uint16","name":"observationCardinalityNext","type":"uint16"},{"internalType":"uint8","name":"feeProtocol","type":"uint8"},{"internalType":"bool","name":"unlocked","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"}],"name":"snapshotCumulativesInside","outputs":[{"internalType":"int56","name":"tickCumulativeInside","type":"int56"},{"internalType":"uint160","name":"secondsPerLiquidityInsideX128","type":"uint160"},{"internalType":"uint32","name":"secondsInside","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bool","name":"zeroForOne","type":"bool"},{"internalType":"int256","name":"amountSpecified","type":"int256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[{"internalType":"int256","name":"amount0","type":"int256"},{"internalType":"int256","name":"amount1","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int16","name":"","type":"int16"}],"name":"tickBitmap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tickSpacing","outputs":[{"internalType":"int24","name":"","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"","type":"int24"}],"name":"ticks","outputs":[{"internalType":"uint128","name":"liquidityGross","type":"uint128"},{"internalType":"int128","name":"liquidityNet","type":"int128"},{"internalType":"uint256","name":"feeGrowthOutside0X128","type":"uint256"},{"internalType":"uint256","name":"feeGrowthOutside1X128","type":"uint256"},{"internalType":"int56","name":"tickCumulativeOutside","type":"int56"},{"internalType":"uint160","name":"secondsPerLiquidityOutsideX128","type":"uint160"},{"internalType":"uint32","name":"secondsOutside","type":"uint32"},{"internalType":"bool","name":"initialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}] ================================================ FILE: abi/UniswapV3Quoter2.json ================================================ [{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_WETH9","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"quoteExactInput","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint160[]","name":"sqrtPriceX96AfterList","type":"uint160[]"},{"internalType":"uint32[]","name":"initializedTicksCrossedList","type":"uint32[]"},{"internalType":"uint256","name":"gasEstimate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"internalType":"struct IQuoterV2.QuoteExactInputSingleParams","name":"params","type":"tuple"}],"name":"quoteExactInputSingle","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceX96After","type":"uint160"},{"internalType":"uint32","name":"initializedTicksCrossed","type":"uint32"},{"internalType":"uint256","name":"gasEstimate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"quoteExactOutput","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint160[]","name":"sqrtPriceX96AfterList","type":"uint160[]"},{"internalType":"uint32[]","name":"initializedTicksCrossedList","type":"uint32[]"},{"internalType":"uint256","name":"gasEstimate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"internalType":"struct IQuoterV2.QuoteExactOutputSingleParams","name":"params","type":"tuple"}],"name":"quoteExactOutputSingle","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceX96After","type":"uint160"},{"internalType":"uint32","name":"initializedTicksCrossed","type":"uint32"},{"internalType":"uint256","name":"gasEstimate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"path","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"view","type":"function"}] ================================================ FILE: abi/UniswapV3SwapRouter2.json ================================================ [{"inputs":[{"internalType":"address","name":"_factoryV2","type":"address"},{"internalType":"address","name":"factoryV3","type":"address"},{"internalType":"address","name":"_positionManager","type":"address"},{"internalType":"address","name":"_WETH9","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveMax","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveMaxMinusOne","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveZeroThenMax","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"approveZeroThenMaxMinusOne","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"callPositionManager","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"paths","type":"bytes[]"},{"internalType":"uint128[]","name":"amounts","type":"uint128[]"},{"internalType":"uint24","name":"maximumTickDivergence","type":"uint24"},{"internalType":"uint32","name":"secondsAgo","type":"uint32"}],"name":"checkOracleSlippage","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint24","name":"maximumTickDivergence","type":"uint24"},{"internalType":"uint32","name":"secondsAgo","type":"uint32"}],"name":"checkOracleSlippage","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"}],"internalType":"struct IV3SwapRouter.ExactInputParams","name":"params","type":"tuple"}],"name":"exactInput","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMinimum","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"internalType":"struct IV3SwapRouter.ExactInputSingleParams","name":"params","type":"tuple"}],"name":"exactInputSingle","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"}],"internalType":"struct IV3SwapRouter.ExactOutputParams","name":"params","type":"tuple"}],"name":"exactOutput","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMaximum","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"internalType":"struct IV3SwapRouter.ExactOutputSingleParams","name":"params","type":"tuple"}],"name":"exactOutputSingle","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factoryV2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getApprovalType","outputs":[{"internalType":"enum IApproveAndCall.ApprovalType","name":"","type":"uint8"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"}],"internalType":"struct IApproveAndCall.IncreaseLiquidityParams","name":"params","type":"tuple"}],"name":"increaseLiquidity","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint256","name":"amount0Min","type":"uint256"},{"internalType":"uint256","name":"amount1Min","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct IApproveAndCall.MintParams","name":"params","type":"tuple"}],"name":"mint","outputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"previousBlockhash","type":"bytes32"},{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"positionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"pull","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"refundETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitAllowed","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitAllowedIfNecessary","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"selfPermitIfNecessary","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"}],"name":"sweepToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"sweepTokenWithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"sweepTokenWithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"unwrapWETH9","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"}],"name":"unwrapWETH9","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"unwrapWETH9WithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountMinimum","type":"uint256"},{"internalType":"uint256","name":"feeBips","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"name":"unwrapWETH9WithFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"wrapETH","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}] ================================================ FILE: abi/WETH.json ================================================ [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}] ================================================ FILE: addresses/__init__.py ================================================ from addresses.arbitrum import TOKENS as ARBITRUM_TOKENS from addresses.arbitrum import POOLS as ARBITRUM_POOLS from addresses.ethereum import TOKENS as ETHEREUM_TOKENS from addresses.ethereum import POOLS as ETHEREUM_POOLS from addresses.ethereum import SIMULATION_HANDLERS as ETHEREUM_SIMULATION_HANDLERS from addresses.ethereum import EXECUTION_HANDLERS as ETHEREUM_EXECUTION_HANDLERS from addresses.polygon import TOKENS as POLYGON_TOKENS from addresses.polygon import POOLS as POLYGON_POOLS ================================================ FILE: addresses/arbitrum.py ================================================ EXCHANGE = 'arbitrum' TOKENS = { 'ETH': ['0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', 18], 'USDT': ['0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', 6], 'USDC': ['0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', 6], 'BTC': ['0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f', 8], 'ARB': ['0x912CE59144191C1204E64559FE8253a0e49E6548', 18], } columns = ['chain', 'exchange', 'version', 'name', 'address', 'fee', 'token0', 'token1'] POOLS = [ ['uniswap', 3, 'ETH/USDC', '0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443', 500, 'ETH', 'USDC'], ['uniswap', 3, 'BTC/ETH', '0x2f5e87C9312fa29aed5c179E456625D79015299c', 500, 'BTC', 'ETH'], ['uniswap', 3, 'ETH/USDT', '0x641C00A822e8b671738d32a431a4Fb6074E5c79d', 500, 'ETH', 'USDT'], ['uniswap', 3, 'USDT/USDC', '0x8c9D230D45d6CfeE39a6680Fb7CB7E8DE7Ea8E71', 100, 'USDT', 'USDC'], ] POOLS = [dict(zip(columns, [EXCHANGE] + pool)) for pool in POOLS] ================================================ FILE: addresses/ethereum.py ================================================ EXCHANGE = 'ethereum' TOKENS = { 'ETH': ['0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 18], 'USDT': ['0xdAC17F958D2ee523a2206206994597C13D831ec7', 6], 'USDC': ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6], 'BTC': ['0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 8], 'DAI': ['0x6B175474E89094C44Da98b954EedeAC495271d0F', 18], 'PEPE': ['0x6982508145454Ce325dDbE47a25d4ec3d2311933', 18], } columns = ['chain', 'exchange', 'version', 'name', 'address', 'fee', 'token0', 'token1'] POOLS = [ ['uniswap', 3, 'ETH/USDT', '0x11b815efB8f581194ae79006d24E0d814B7697F6', 500, 'ETH', 'USDT'], ['uniswap', 3, 'USDC/USDT', '0x3416cF6C708Da44DB2624D63ea0AAef7113527C6', 100, 'USDC', 'USDT'], ['uniswap', 3, 'BTC/ETH', '0x4585FE77225b41b697C938B018E2Ac67Ac5a20c0', 500, 'BTC', 'ETH'], ['uniswap', 3, 'DAI/USDC', '0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168', 100, 'DAI', 'USDC'], ['uniswap', 3, 'PEPE/ETH', '0x11950d141EcB863F01007AdD7D1A342041227b58', 3000, 'PEPE', 'ETH'], ['uniswap', 2, 'ETH/USDT', '0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852', 3000, 'ETH', 'USDT'], ['uniswap', 2, 'USDC/ETH', '0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc', 3000, 'USDC', 'ETH'], ['uniswap', 2, 'DAI/USDC', '0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5', 3000, 'DAI', 'USDC'], ['uniswap', 2, 'PEPE/ETH', '0xA43fe16908251ee70EF74718545e4FE6C5cCEc9f', 3000, 'PEPE', 'ETH'], ['sushiswap', 3, 'ETH/USDT', '0x72c2178E082feDB13246877B5aA42ebcE1b72218', 500, 'ETH', 'USDT'], ['sushiswap', 3, 'USDC/ETH', '0x35644Fb61aFBc458bf92B15AdD6ABc1996Be5014', 500, 'USDC', 'ETH'], ['sushiswap', 3, 'BTC/ETH', '0x801CCFae9d2C77893B545E8D0E4637C055CD26cB', 500, 'BTC', 'ETH'], ['sushiswap', 3, 'USDC/USDT', '0xfA6e8E97ecECDC36302eCA534f63439b1E79487B', 100, 'USDC', 'USDT'], ['sushiswap', 3, 'DAI/USDC', '0x31ac258B911Af9a0d2669ebDFC4e39D92e96b772', 100, 'DAI', 'USDC'], ['sushiswap', 2, 'BTC/ETH', '0xCEfF51756c56CeFFCA006cD410B03FFC46dd3a58', 3000, 'BTC', 'ETH'], ['sushiswap', 2, 'ETH/USDT', '0x06da0fd433C1A5d7a4faa01111c044910A184553', 3000, 'ETH', 'USDT'], ['sushiswap', 2, 'USDC/ETH', '0x397FF1542f962076d0BFE58eA045FfA2d347ACa0', 3000, 'USDC', 'ETH'], ['sushiswap', 2, 'DAI/ETH', '0xC3D03e4F041Fd4cD388c549Ee2A29a9E5075882f', 3000, 'DAI', 'ETH'], ] POOLS = [dict(zip(columns, [EXCHANGE] + pool)) for pool in POOLS] SIMULATION_HANDLERS = { 'uniswap_v2': '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f', # factory 'sushiswap_v2': '0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac', # factory 'uniswap_v3': '0x61fFE014bA17989E743c5F6cB21bF9697530B21e', # quoterv2 'sushiswap_v3': '0x64e8802FE490fa7cc61d3463958199161Bb608A7', # quoterv2 } EXECUTION_HANDLERS = { 'uniswap_v2': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', # router2 'sushiswap_v2': '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F', # router2 'uniswap_v3': '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45', # swap router2 'sushiswap_v3': '', # TODO: Sushiswap V3 doesn't have SwapRouter2, needs to use pool contract } ================================================ FILE: addresses/polygon.py ================================================ EXCHANGE = 'polygon' TOKENS = { 'ETH': ['0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619', 18], 'USDT': ['0xc2132D05D31c914a87C6611C10748AEb04B58e8F', 6], 'USDC': ['0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', 6], 'BTC': ['0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6', 8], 'MATIC': ['0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', 18], } columns = ['chain', 'exchange', 'version', 'name', 'address', 'fee', 'token0', 'token1'] POOLS = [ ['uniswap', 3, 'USDC/ETH', '0x45dDa9cb7c25131DF268515131f647d726f50608', 500, 'USDC', 'ETH'], ['uniswap', 3, 'BTC/ETH', '0x50eaEDB835021E4A108B7290636d62E9765cc6d7', 500, 'BTC', 'ETH'], ['uniswap', 3, 'USDC/USDT', '0xDaC8A8E6DBf8c690ec6815e0fF03491B2770255D', 100, 'USDC', 'USDT'], ['uniswap', 3, 'MATIC/USDC', '0xA374094527e1673A86dE625aa59517c5dE346d32', 500, 'MATIC', 'USDC'], ] POOLS = [dict(zip(columns, [EXCHANGE] + pool)) for pool in POOLS] ================================================ FILE: configs.py ================================================ import os from dotenv import load_dotenv from addresses import ( ETHEREUM_TOKENS, POLYGON_TOKENS, ARBITRUM_TOKENS, ETHEREUM_POOLS, POLYGON_POOLS, ARBITRUM_POOLS, ETHEREUM_SIMULATION_HANDLERS, ETHEREUM_EXECUTION_HANDLERS, ) load_dotenv(override=True) RPC_ENDPOINTS = { 'ethereum': os.getenv('ETHEREUM_HTTP_RPC_URL'), 'polygon': os.getenv('POLYGON_HTTP_RPC_URL'), 'arbitrum': os.getenv('ARBITRUM_HTTP_RPC_URL'), } WS_ENDPOINTS = { 'ethereum': os.getenv('ETHEREUM_WS_RPC_URL'), 'polygon': os.getenv('POLYGON_WS_RPC_URL'), 'arbitrum': os.getenv('ARBITRUM_WS_RPC_URL'), } TOKENS = { 'ethereum': ETHEREUM_TOKENS, 'polygon': POLYGON_TOKENS, 'arbitrum': ARBITRUM_TOKENS, } POOLS = ETHEREUM_POOLS + POLYGON_POOLS + ARBITRUM_POOLS SIMULATION_HANDLERS = { 'ethereum': ETHEREUM_SIMULATION_HANDLERS, } EXECUTION_HANDLERS = { 'ethereum': ETHEREUM_EXECUTION_HANDLERS, } ================================================ FILE: contracts/foundry.toml ================================================ [profile.default] src = "src" out = "out" libs = ["lib"] # See more config options https://github.com/foundry-rs/foundry/tree/master/config ================================================ FILE: contracts/src/SimulatorV1.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "openzeppelin-contracts/contracts/utils/math/SafeMath.sol"; import "./protocols/uniswap/UniswapV2Library.sol"; import "./protocols/uniswap/IQuoterV2.sol"; import "./protocols/curve/ICurvePool.sol"; // Deployment gas: 610,194 // With optimizations: 773,502 contract SimulatorV1 { using SafeMath for uint256; struct SwapParams { uint8 protocol; // 0 (UniswapV2), 1 (UniswapV3), 2 (Curve Finance) address handler; // UniswapV2: Factory, UniswapV3: Quoter, Curve: Pool address tokenIn; address tokenOut; uint24 fee; // only used in Uniswap V3 uint256 amount; // amount in (1 USDC = 1,000,000 / 1 MATIC = 1 * 10 ** 18) } constructor() {} function simulateSwapIn( SwapParams[] calldata paramsArray ) public returns (uint256) { uint256 amountOut; uint256 paramsArrayLength = paramsArray.length; for (uint256 i; i < paramsArrayLength; ) { SwapParams memory params = paramsArray[i]; if (amountOut == 0) { amountOut = params.amount; } else { params.amount = amountOut; } if (params.protocol == 0) { amountOut = simulateUniswapV2SwapIn(params); } else if (params.protocol == 1) { amountOut = simulateUniswapV3SwapIn(params); } unchecked { ++i; } } return amountOut; } function simulateUniswapV2SwapIn( SwapParams memory params ) public view returns (uint256 amountOut) { (uint reserveIn, uint reserveOut) = UniswapV2Library.getReserves( params.handler, params.tokenIn, params.tokenOut ); amountOut = UniswapV2Library.getAmountOut( params.amount, reserveIn, reserveOut ); } function simulateUniswapV3SwapIn( SwapParams memory params ) public returns (uint256 amountOut) { IQuoterV2 quoter = IQuoterV2(params.handler); IQuoterV2.QuoteExactInputSingleParams memory quoterParams; quoterParams.tokenIn = params.tokenIn; quoterParams.tokenOut = params.tokenOut; quoterParams.amountIn = params.amount; quoterParams.fee = params.fee; quoterParams.sqrtPriceLimitX96 = 0; (amountOut, , , ) = quoter.quoteExactInputSingle(quoterParams); } function simulateCurveSwapIn( SwapParams memory params ) public returns (uint256 amountOut) { ICurvePool pool = ICurvePool(params.handler); int128 i; int128 j; int128 coinIdx; while (i == j) { address coin = pool.coins(coinIdx); if (coin == params.tokenIn) { i = coinIdx; } else if (coin == params.tokenOut) { j = coinIdx; } if (i != j) { break; } unchecked { ++coinIdx; } } amountOut = pool.get_dy(i, j, params.amount); } } ================================================ FILE: contracts/src/WhackAMoleBotV1.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "openzeppelin-contracts/contracts/utils/math/SafeMath.sol"; import "openzeppelin-contracts/contracts/token/erc20/IERC20.sol"; import "openzeppelin-contracts/contracts/token/erc20/utils/SafeERC20.sol"; import "./protocols/uniswap/IUniswapV2Router.sol"; import "./protocols/uniswap/IUniswapV3SwapRouter.sol"; // Deployment gas: 1,004,388 contract WhackAMoleBotV1 { using SafeERC20 for IERC20; address internal immutable owner; struct SwapParams { uint8 protocol; address handler; address tokenIn; address tokenOut; uint24 fee; uint256 amount; } error Unauthorized(); error TradeFailed(); constructor() { owner = msg.sender; } modifier onlyOwner() { _checkOwner(); _; } function _checkOwner() internal view virtual { if (msg.sender != owner) { revert Unauthorized(); } } function recoverTokens(address[] calldata tokens) public payable onlyOwner { uint length = tokens.length; for (uint i; i < length; ) { address token = tokens[i]; IERC20(token).safeTransfer( msg.sender, IERC20(token).balanceOf(address(this)) ); unchecked { ++i; } } } // 2 tokens, 2 protocols: 142,582 gas function approveHandlers( address[] calldata tokens, address[] calldata protocols ) public payable { // Used to allow Routers from Uniswap V2, Uniswap V3, etc. // the access to tokens held by this contract uint maxInt = type(uint256).max; uint tokensLength = tokens.length; uint protocolsLength = protocols.length; for (uint i; i < tokensLength; ) { IERC20 token = IERC20(tokens[i]); for (uint j; j < protocolsLength; ) { address protocol = protocols[j]; token.safeApprove(protocol, maxInt); unchecked { ++j; } } unchecked { ++i; } } } function whack( SwapParams[] calldata paramsArray, uint256 minAmountOut ) public payable onlyOwner returns (uint256) { uint256 amountOut; uint256 paramsArrayLength = paramsArray.length; for (uint256 i; i < paramsArrayLength; ) { SwapParams memory params = paramsArray[i]; if (amountOut == 0) { amountOut = params.amount; } else { params.amount = amountOut; } if (params.protocol == 0) { amountOut = uniswapV2Swap(params); } else if (params.protocol == 1) { amountOut = uniswapV3Swap(params); } unchecked { ++i; } } if (amountOut < minAmountOut) { revert TradeFailed(); } return amountOut; } function uniswapV2Swap( SwapParams memory params ) internal returns (uint256 amountOut) { address[] memory path; path = new address[](2); path[0] = params.tokenIn; path[1] = params.tokenOut; uint[] memory amounts = IUniswapV2Router(params.handler) .swapExactTokensForTokens( params.amount, 0, path, address(this), block.timestamp ); return amounts[1]; } function uniswapV3Swap( SwapParams memory params ) internal returns (uint256 amountOut) { // Sushiswap V3 doesn't have SwapRouter, needs to reimplement this to use Pools ISwapRouter.ExactInputSingleParams memory singleParams = ISwapRouter .ExactInputSingleParams({ tokenIn: params.tokenIn, tokenOut: params.tokenOut, fee: params.fee, recipient: address(this), deadline: block.timestamp, amountIn: params.amount, amountOutMinimum: 0, sqrtPriceLimitX96: 0 }); amountOut = ISwapRouter(params.handler).exactInputSingle(singleParams); } } ================================================ FILE: contracts/src/lib/SafeTransfer.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "../protocols/IERC20.sol"; library SafeTransfer { function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { (bool s, ) = address(token).call( abi.encodeWithSelector( IERC20.transferFrom.selector, from, to, value ) ); require(s, "safeTransferFrom failed"); } function safeTransfer(IERC20 token, address to, uint256 value) internal { (bool s, ) = address(token).call( abi.encodeWithSelector(IERC20.transfer.selector, to, value) ); require(s, "safeTransfer failed"); } function safeApprove(IERC20 token, address to, uint256 value) internal { (bool s, ) = address(token).call( abi.encodeWithSelector(IERC20.approve.selector, to, value) ); require(s, "safeApprove failed"); } function safeTransferETH(address to, uint256 value) internal { (bool s, ) = to.call{value: value}(new bytes(0)); require(s, "safeTransferETH failed"); } } ================================================ FILE: contracts/src/protocols/IERC20.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; interface IERC20 { function totalSupply() external view returns (uint); function balanceOf(address account) external view returns (uint); function transfer(address recipient, uint amount) external returns (bool); function allowance( address owner, address spender ) external view returns (uint); function approve(address spender, uint amount) external returns (bool); function transferFrom( address sender, address recipient, uint amount ) external returns (bool); event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); } ================================================ FILE: contracts/src/protocols/IWETH.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "./IERC20.sol"; interface IWETH is IERC20 { function deposit() external payable; function withdraw(uint amount) external; } ================================================ FILE: contracts/src/protocols/curve/ICurvePool.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; interface ICurvePool { function get_virtual_price() external returns (uint256 out); function add_liquidity( uint256[2] calldata amounts, uint256 deadline ) external; function get_dy( int128 i, int128 j, uint256 dx ) external returns (uint256 out); function get_dy_underlying( int128 i, int128 j, uint256 dx ) external returns (uint256 out); function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external; function exchange( int128 i, int128 j, uint256 dx, uint256 min_dy, uint256 deadline ) external; function exchange_underlying( int128 i, int128 j, uint256 dx, uint256 min_dy ) external; function exchange_underlying( int128 i, int128 j, uint256 dx, uint256 min_dy, uint256 deadline ) external; function remove_liquidity( uint256 _amount, uint256 deadline, uint256[2] calldata min_amounts ) external; function remove_liquidity_imbalance( uint256[2] calldata amounts, uint256 deadline ) external; function commit_new_parameters( int128 amplification, int128 new_fee, int128 new_admin_fee ) external; function apply_new_parameters() external; function revert_new_parameters() external; function commit_transfer_ownership(address _owner) external; function apply_transfer_ownership() external; function revert_transfer_ownership() external; function withdraw_admin_fees() external; function coins(int128 arg0) external returns (address out); function underlying_coins(int128 arg0) external returns (address out); function balances(int128 arg0) external returns (uint256 out); function A() external returns (int128 out); function fee() external returns (int128 out); function admin_fee() external returns (int128 out); function owner() external returns (address out); function admin_actions_deadline() external returns (uint256 out); function transfer_ownership_deadline() external returns (uint256 out); function future_A() external returns (int128 out); function future_fee() external returns (int128 out); function future_admin_fee() external returns (int128 out); function future_owner() external returns (address out); } ================================================ FILE: contracts/src/protocols/uniswap/IQuoterV2.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; pragma abicoder v2; /// @title QuoterV2 Interface /// @notice Supports quoting the calculated amounts from exact input or exact output swaps. /// @notice For each pool also tells you the number of initialized ticks crossed and the sqrt price of the pool after the swap. /// @dev These functions are not marked view because they rely on calling non-view functions and reverting /// to compute the result. They are also not gas efficient and should not be called on-chain. interface IQuoterV2 { /// @notice Returns the amount out received for a given exact input swap without executing the swap /// @param path The path of the swap, i.e. each token pair and the pool fee /// @param amountIn The amount of the first token to swap /// @return amountOut The amount of the last token that would be received /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path /// @return gasEstimate The estimate of the gas that the swap consumes function quoteExactInput(bytes memory path, uint256 amountIn) external returns ( uint256 amountOut, uint160[] memory sqrtPriceX96AfterList, uint32[] memory initializedTicksCrossedList, uint256 gasEstimate ); struct QuoteExactInputSingleParams { address tokenIn; address tokenOut; uint256 amountIn; uint24 fee; uint160 sqrtPriceLimitX96; } /// @notice Returns the amount out received for a given exact input but for a swap of a single pool /// @param params The params for the quote, encoded as `QuoteExactInputSingleParams` /// tokenIn The token being swapped in /// tokenOut The token being swapped out /// fee The fee of the token pool to consider for the pair /// amountIn The desired input amount /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap /// @return amountOut The amount of `tokenOut` that would be received /// @return sqrtPriceX96After The sqrt price of the pool after the swap /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed /// @return gasEstimate The estimate of the gas that the swap consumes function quoteExactInputSingle(QuoteExactInputSingleParams memory params) external returns ( uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate ); /// @notice Returns the amount in required for a given exact output swap without executing the swap /// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order /// @param amountOut The amount of the last token to receive /// @return amountIn The amount of first token required to be paid /// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path /// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path /// @return gasEstimate The estimate of the gas that the swap consumes function quoteExactOutput(bytes memory path, uint256 amountOut) external returns ( uint256 amountIn, uint160[] memory sqrtPriceX96AfterList, uint32[] memory initializedTicksCrossedList, uint256 gasEstimate ); struct QuoteExactOutputSingleParams { address tokenIn; address tokenOut; uint256 amount; uint24 fee; uint160 sqrtPriceLimitX96; } /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool /// @param params The params for the quote, encoded as `QuoteExactOutputSingleParams` /// tokenIn The token being swapped in /// tokenOut The token being swapped out /// fee The fee of the token pool to consider for the pair /// amountOut The desired output amount /// sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap /// @return amountIn The amount required as the input for the swap in order to receive `amountOut` /// @return sqrtPriceX96After The sqrt price of the pool after the swap /// @return initializedTicksCrossed The number of initialized ticks that the swap crossed /// @return gasEstimate The estimate of the gas that the swap consumes function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params) external returns ( uint256 amountIn, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate ); } ================================================ FILE: contracts/src/protocols/uniswap/IUniswapV2Pair.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; interface IUniswapV2Pair { event Approval( address indexed owner, address indexed spender, uint256 value ); event Transfer(address indexed from, address indexed to, uint256 value); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 value) external returns (bool); function transfer(address to, uint256 value) external returns (bool); function transferFrom( address from, address to, uint256 value ) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); function nonces(address owner) external view returns (uint256); function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; event Mint(address indexed sender, uint256 amount0, uint256 amount1); event Burn( address indexed sender, uint256 amount0, uint256 amount1, address indexed to ); event Swap( address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); function MINIMUM_LIQUIDITY() external pure returns (uint256); function factory() external view returns (address); function token0() external view returns (address); function token1() external view returns (address); function getReserves() external view returns ( uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast ); function price0CumulativeLast() external view returns (uint256); function price1CumulativeLast() external view returns (uint256); function kLast() external view returns (uint256); function mint(address to) external returns (uint256 liquidity); function burn(address to) external returns (uint256 amount0, uint256 amount1); function swap( uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data ) external; function skim(address to) external; function sync() external; function initialize(address, address) external; } ================================================ FILE: contracts/src/protocols/uniswap/IUniswapV2Router.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; interface IUniswapV2Router { function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); } ================================================ FILE: contracts/src/protocols/uniswap/IUniswapV3SwapRouter.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; interface ISwapRouter { struct ExactInputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint deadline; uint amountIn; uint amountOutMinimum; uint160 sqrtPriceLimitX96; } /// @notice Swaps amountIn of one token for as much as possible of another token /// @param params The parameters necessary for the swap, encoded as ExactInputSingleParams in calldata /// @return amountOut The amount of the received token function exactInputSingle( ExactInputSingleParams calldata params ) external payable returns (uint amountOut); struct ExactInputParams { bytes path; address recipient; uint deadline; uint amountIn; uint amountOutMinimum; } /// @notice Swaps amountIn of one token for as much as possible of another along the specified path /// @param params The parameters necessary for the multi-hop swap, encoded as ExactInputParams in calldata /// @return amountOut The amount of the received token function exactInput( ExactInputParams calldata params ) external payable returns (uint amountOut); } ================================================ FILE: contracts/src/protocols/uniswap/UniswapV2Library.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "./IUniswapV2Pair.sol"; import "openzeppelin-contracts/contracts/utils/math/SafeMath.sol"; library UniswapV2Library { using SafeMath for uint256; // returns sorted token addresses, used to handle return values from pairs sorted in this order function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES"); (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS"); } // calculates the CREATE2 address for a pair without making any external calls function pairFor( address factory, address tokenA, address tokenB ) internal pure returns (address pair) { (address token0, address token1) = sortTokens(tokenA, tokenB); bytes32 initCodeHash = 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f; bytes32 salt = keccak256(abi.encodePacked(token0, token1)); pair = address( uint160( uint256( keccak256( abi.encodePacked( bytes1(0xff), factory, salt, initCodeHash ) ) ) ) ); return pair; } // fetches and sorts the reserves for a pair function getReserves( address factory, address tokenA, address tokenB ) internal view returns (uint256 reserveA, uint256 reserveB) { (address token0, ) = sortTokens(tokenA, tokenB); (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair( pairFor(factory, tokenA, tokenB) ) .getReserves(); (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); } // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut ) internal pure returns (uint256 amountOut) { require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT"); require( reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY" ); uint256 amountInWithFee = amountIn.mul(997); uint256 numerator = amountInWithFee.mul(reserveOut); uint256 denominator = reserveIn.mul(1000).add(amountInWithFee); amountOut = numerator / denominator; } // given an output amount of an asset and pair reserves, returns a required input amount of the other asset function getAmountIn( uint256 amountOut, uint256 reserveIn, uint256 reserveOut ) internal pure returns (uint256 amountIn) { require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT"); require( reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY" ); uint256 numerator = reserveIn.mul(amountOut).mul(1000); uint256 denominator = reserveOut.sub(amountOut).mul(997); amountIn = (numerator / denominator).add(1); } // performs chained getAmountOut calculations on any number of pairs function getAmountsOut( address factory, uint256 amountIn, address[] memory path ) internal view returns (uint256[] memory amounts) { require(path.length >= 2, "UniswapV2Library: INVALID_PATH"); amounts = new uint256[](path.length); amounts[0] = amountIn; for (uint256 i; i < path.length - 1; i++) { (uint256 reserveIn, uint256 reserveOut) = getReserves( factory, path[i], path[i + 1] ); amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut); } } // performs chained getAmountIn calculations on any number of pairs function getAmountsIn( address factory, uint256 amountOut, address[] memory path ) internal view returns (uint256[] memory amounts) { require(path.length >= 2, "UniswapV2Library: INVALID_PATH"); amounts = new uint256[](path.length); amounts[amounts.length - 1] = amountOut; for (uint256 i = path.length - 1; i > 0; i--) { (uint256 reserveIn, uint256 reserveOut) = getReserves( factory, path[i - 1], path[i] ); amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut); } } } ================================================ FILE: contracts/test/WhackAMoleBotV1.t.sol ================================================ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.9; import "forge-std/Test.sol"; import "../src/WhackAMoleBotV1.sol"; import "../src/SimulatorV1.sol"; contract FoundryDemoTest is Test { WhackAMoleBotV1 bot; SimulatorV1 simulator; uint256 simulatedAmountOut1; uint256 simulatedAmountOut2; uint256 simulatedAmountOut3; function setUp() public {} function testBotDeploy() public { bot = new WhackAMoleBotV1(); } function testSimulatorDeploy() public { simulator = new SimulatorV1(); } } ================================================ FILE: data/__init__.py ================================================ from data.dex import * from data.dex_streams import * ================================================ FILE: data/cex.py ================================================ import ccxt from typing import List class CEX: """ Use CCXT to build CEX execution The Python project is a prototype CEX-DEX arb. bot, use as much of what others have built already """ def __init__(self, trading_symbols: List[str]): self.trading_symbols = trading_symbols if __name__ == '__main__': """ Test with Binance, OKX, Bybit These are the top 3 derivatives exchanges """ from configs import TRADING_SYMBOLS cex = CEX(TRADING_SYMBOLS) # print(ccxt.exchanges) # # binance = ccxt.binance({'options': {'defaultType': 'future'}}) # markets = binance.load_markets() # print(markets) ================================================ FILE: data/cex_streams.py ================================================ import json import time import eth_abi import asyncio import datetime import eth_utils import websockets import numpy as np import aioprocessing from functools import partial from typing import Any, Dict, Optional from data.cex import CEX from data.utils import reconnecting_websocket_loop class CexStream: """ WIP The first step to building CEX-DEX arbitrage bot is making sure DEX execution works well Data from CEX and DEX are updated at different time frames, as CEX data is real-time, whereas DEX data is updated every new block. Analyze how this will affect execution of CEX-DEX arbitrage """ def __init__(self, cex: CEX, publisher: Optional[aioprocessing.AioQueue] = None, debug: bool = False): self.cex = cex self.publisher = publisher self.debug = debug def publish(self, data: Any): if self.publisher: self.publisher.put(data) def start_stream(self): pass async def stream_binance_usdm_orderbook(self): async with websockets.connect('wss://fstream.binance.com/ws/') as ws: params = [f'{s.replace("/", "").lower()}@depth5@100ms' for s in self.cex.trading_symbols] subscription = { 'method': 'SUBSCRIBE', 'params': params, 'id': 1, } await ws.send(json.dumps(subscription)) _ = await ws.recv() while True: msg = await asyncio.wait_for(ws.recv(), timeout=15) data = json.loads(msg) print(data) async def stream_okx_usdm_orderbook(self): async with websockets.connect('wss://ws.okx.com:8443/ws/v5/public') as ws: args = [{'channel': 'books5', 'instId': f'{s.replace("/", "-")}-SWAP'} for s in self.cex.trading_symbols] subscription = { 'op': 'subscribe', 'args': args, } await ws.send(json.dumps(subscription)) _ = await ws.recv() while True: msg = await asyncio.wait_for(ws.recv(), timeout=15) data = json.loads(msg) print(data) async def stream_bybit_usdm_orderbook(self): """ Bybit doesn't provide you with the option to stream orderbook snapshot data. We'll have to implement the orderbook snapshot on our own. """ max_depth = 50 bids = np.zeros((max_depth, 2)) asks = np.zeros((max_depth, 2)) async with websockets.connect('wss://stream.bybit.com/v5/public/linear') as ws: args = [f'orderbook.{max_depth}.{s.replace("/", "").upper()}' for s in self.cex.trading_symbols] subscription = { 'op': 'subscribe', 'args': args, } await ws.send(json.dumps(subscription)) _ = await ws.recv() while True: msg = await asyncio.wait_for(ws.recv(), timeout=15) data = json.loads(msg) print(data) if __name__ == '__main__': trading_symbols = ['BTC/USDT'] cex = CEX(trading_symbols) queue = aioprocessing.AioQueue() cex_streams = CexStream(cex, queue, False) asyncio.run(cex_streams.stream_bybit_usdm_orderbook()) ================================================ FILE: data/dex.py ================================================ import numpy as np from web3 import Web3 from typing import Any, Dict, List from multicall import Call, Multicall from simulation import UniswapV2Simulator, UniswapV3Simulator # Dimension of DEX.storage_array CHAIN = 0 EXCHANGE = 1 TOKEN_IN = 2 TOKEN_OUT = 3 VERSION = 4 STORAGE = 5 V2 = 0 V3 = 1 DECIMALS0 = 0 DECIMALS1 = 1 RESERVE0 = 2 RESERVE1 = 3 SQRT_PRICE = 4 FEE = 5 TOKEN0_IN = 6 POOL_INDEX = 7 class DexBase: def __init__(self, rpc_endpoints: Dict[str, str], tokens: Dict[str, Dict[str, List[str or int]]], pools: List[Dict[str, Any]], trading_symbols: List[str], max_swap_number: int = 3): """ :param rpc_endpoints: ex) {'ethereum': ''} :param tokens: ex) {'ethereum': {'ETH': ['', 18]}} :param pools: ex) [{'chain': 'ethereum', 'exchange': 'uniswap', 'version': 3, 'name': 'ETH/USDT', 'address': '', 'fee': 500, # 0.05% 'token0': 'ETH', 'token1': 'USDT'}] :param trading_symbols: ex) ['BTC/USDT'] :param max_swap_number: the maximum number of swaps in a trade ex) 1, 2, 3, ... """ self.rpc_endpoints = rpc_endpoints self.tokens = tokens self.pools = pools self.trading_symbols = trading_symbols self.max_swap_number = max_swap_number self.sim_v2 = UniswapV2Simulator() self.sim_v3 = UniswapV3Simulator() self.web3 = {k: Web3(Web3.HTTPProvider(v)) for k, v in rpc_endpoints.items()} # extract keys from tokens, pools self.chains_list = sorted(list(tokens.keys())) self.exchanges_list = sorted(set([p['exchange'] for p in pools])) tokens_list = [] for exchange, tokens_dict in tokens.items(): tokens_list.extend(list(tokens_dict.keys())) self.tokens_list = sorted(list(set(tokens_list))) # map chains, exchanges, tokens to int id value # this is used to map chains/exchanges/tokens to numpy array index values self.chain_to_id = {k: i for i, k in enumerate(self.chains_list)} self.exchange_to_id = {k: i for i, k in enumerate(self.exchanges_list)} self.token_to_id = {k: i for i, k in enumerate(self.tokens_list)} """ storage_array : 6-dimensional array that stores storage values from pool contracts """ self.storage_array = np.zeros(( len(self.chains_list), # chains len(self.exchanges_list), # exchanges len(tokens_list), # token in len(tokens_list), # token out 2, # uniswap variant version: 2, 3 8 # decimals0, decimals1, reserve0, reserve1, sqrtPriceX96, # fee, token0_is_input, pool_index )) """ storage_index : Keeps the 5-dimensional index of storage_array by chains : Used to generate swap paths : Filled in from _load_pool_data() ex) {'ethereum': [(0, 0, 1, 2, 0), ...] """ self.storage_index = {c: [] for c in self.chains_list} """ swap_paths : contains information about swap paths, pool indexes, tags, tokens involved, price, fee : Filled in from _generate_swap_paths() ex) {'ETH/USDT': {'path': np.ndarray, 'pool_indexes': List[List[int]], 'tag': List[str], 'tokens': np.ndarray, 'price': np.ndarray 'fee': np.ndarray}, ...} """ self.swap_paths = {s: None for s in self.trading_symbols} def load(self): """ Make sure to call this method in DEX """ self._load_pool_data() self._generate_swap_paths() def _load_pool_data(self): """ Loads all storage values from multiple pool contracts using Multicall this enables users to bulk query data on the blockchain """ calls_by_chain = {c: [] for c in self.chains_list} for pool_idx, pool in enumerate(self.pools): if pool['version'] == 2: """ Reference: https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Pair.sol Return values: reserve0, reserve1, blockTimestampLast """ signature = 'getReserves()((uint112,uint112,uint32))' else: """ Reference: https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Pool.sol Return values: sqrtPriceX96, tick, observationIndex, observationCardinality, observationCardinalityNext, feeProtocol, unlocked """ signature = 'slot0()((uint160,int24,uint16,uint16,uint16,uint8,bool))' call = Call( pool['address'], signature, [(str(pool_idx), lambda x: x)] ) calls_by_chain[pool['chain']].append(call) # Send multicall queries multicall_results = {} for exchange, calls in calls_by_chain.items(): multicall = Multicall(calls, _w3=self.web3[exchange]) multicall_results = { **multicall_results, **multicall() } # Fill in storage_index, storage_array for pool_idx, storage_data in multicall_results.items(): pool: Dict[str, Any] = self.pools[int(pool_idx)] chain_idx = self.chain_to_id[pool['chain']] exchange_idx = self.exchange_to_id[pool['exchange']] token0_idx = self.token_to_id[pool['token0']] token1_idx = self.token_to_id[pool['token1']] version_idx = V2 if pool['version'] == 2 else V3 # We create two indexes to indicate: # - token0 -> token1, # - token1 -> token0 idx_1 = (chain_idx, exchange_idx, token0_idx, token1_idx, version_idx) idx_2 = (chain_idx, exchange_idx, token1_idx, token0_idx, version_idx) # Keep all the indexes that are associated with the specific chain in DEX.storage_index # this is to _generate_swap_paths using the pre-saved indexes self.storage_index[pool['chain']].append(idx_1) self.storage_index[pool['chain']].append(idx_2) decimals0 = self.tokens[pool['chain']][pool['token0']][1] decimals1 = self.tokens[pool['chain']][pool['token1']][1] fee = pool['fee'] / 10000.0 / 100.0 data = [0] * 6 if version_idx == V2: reserve0 = storage_data[0] reserve1 = storage_data[1] data = [decimals0, decimals1, reserve0, reserve1, 0, fee] elif version_idx == V3: sqrt_price = storage_data[0] data = [decimals0, decimals1, 0, 0, sqrt_price, fee] self.storage_array[idx_1] = data + [1, int(pool_idx)] # token_in is token0 self.storage_array[idx_2] = data + [0, int(pool_idx)] # token_in is not token0 def _generate_swap_paths(self): """ Generates all the swap paths up to max_swap_number swaps This internal function has to be called after DEX.storage_index has been filled """ # Dictionary that looks like: {'ETH/USDT': [3, 1], 'BTC/USDT': [3, 0], ...} token_in_out = { symbol: [self.token_to_id[token] for token in reversed(symbol.split('/'))] for symbol in self.trading_symbols } chain_swap_paths = {} for chain, index in self.storage_index.items(): index_arr = np.array(index) symbol_swap_paths = {} """ Loop through each symbol from token_in_out (=symbols in trading_symbols) and generate viable swap paths that can occur within a blockchain. This means that there will be multiple viable swap paths for a trading symbol per chain. """ for symbol, in_out in token_in_out.items(): pool_samples = self.__sample_pools(index_arr, in_out) paths = self.__generate_paths(pool_samples) paths_arr = np.array(paths) if paths_arr.shape[0] != 0: symbol_swap_paths[symbol] = paths_arr else: symbol_swap_paths[symbol] = paths_arr.reshape((0, 3, 5)).astype(np.int64) chain_swap_paths[chain] = symbol_swap_paths def _get_pool_indexes(_symbol_paths: np.ndarray) -> List[int]: indexes = [] for i in np.arange(_symbol_paths.shape[0]): idx = _symbol_paths[i] if np.sum(idx) != 0: pool_idx = self.storage_array[tuple(idx)][POOL_INDEX] indexes.append(int(pool_idx)) return indexes # concatenate the paths generated for each chain for symbol in self.trading_symbols: symbol_paths_list = [chain_swap_paths[chain][symbol] for chain in self.chains_list] symbol_paths_array = np.concatenate(symbol_paths_list) pool_indexes = [ _get_pool_indexes(symbol_paths_array[i]) for i in np.arange(symbol_paths_array.shape[0]) ] # get unique tokens from pool index that isn't all 0's _tokens_involved = symbol_paths_array[:, :, [TOKEN_IN, TOKEN_OUT]].reshape(-1, 2) tokens_involved = np.unique(_tokens_involved[~np.all(_tokens_involved == 0, axis=1)]) price_arr = np.zeros(symbol_paths_array.shape[0]) fee_arr = np.zeros(symbol_paths_array.shape[0]) """ Create a path tag for easy reference/debugging Each tag will look like: ethereum-0, ethereum-1, polygon-0, ... This step is completely unnecessary to the main logic """ chain_path_counter = {chain: 0 for chain in self.chains_list} tags = [] for i in np.arange(symbol_paths_array.shape[0]): chain_idx = symbol_paths_array[i][0][0] chain = self.chains_list[chain_idx] tag = f'{chain}-{chain_path_counter[chain]}' chain_path_counter[chain] += 1 tags.append(tag) self.swap_paths[symbol] = { 'path': symbol_paths_array, # np.ndarray: (n, max_swap_number, 5) 'pool_indexes': pool_indexes, # List[List[int]]: len() == n 'tag': tags, # List[str]: len() == n 'tokens': tokens_involved, # np.ndarray: (1, unique token numbers) 'price': price_arr, # np.ndarray: (1, n) --> n should match the number of paths 'fee': fee_arr, # np.ndarray: (1, n) --> n should match the number of paths } def __sample_pools(self, index_arr: np.ndarray, in_out: List[int]) -> Dict[int, List[List[List[int]]]]: # Step #1 # Sampling pools that can be used in n-hop swaps with token_in, token_out constraints # Sampling before finding swap paths can reduce the time it takes to build viable swap paths swap_nums = range(1, self.max_swap_number + 1) pool_samples = {n: [] for n in swap_nums} for n in swap_nums: token_in, token_out = in_out no_path = False # setup an empty list of pool samples that could be used in each nth step of the swap path filtered_pools = [[]] * self.max_swap_number for i in range(n): if i == 0: # token_in is int here condition_1 = index_arr[:, TOKEN_IN] == token_in else: # token_in in List[int] here condition_1 = np.isin(index_arr[:, TOKEN_IN], token_in) if i == n - 1: condition_2 = index_arr[:, TOKEN_OUT] == token_out else: condition_2 = index_arr[:, TOKEN_OUT] != token_out condition = condition_1 & condition_2 filtered = index_arr[condition] if filtered.shape[0] > 0: filtered_pools[i] = filtered.tolist() else: no_path = True break # set the next token_in to be current token_outs token_in = list(filtered[:, TOKEN_OUT]) if not no_path: pool_samples[n] = filtered_pools return pool_samples def __generate_paths(self, pool_samples: Dict[int, List[List[List[int]]]]) -> List[List[List[int]]]: """ :param pool_samples: returned value of DEX._sample_pools """ def __sample_paths_list(_n_total_hops: int, _nth_hop: int, _prev_pool: None or List[int], _sampled: List[List[int]], _pool_samples: Dict[int, List[List[List[int]]]], _paths: List[List[List[int]]]): """ Uses recursive looping to fill in paths_list :param _n_total_hops: the number of hops you are trying to sample. ex) 1, 2, 3, ... :param _nth_hop: out of the _n_total_hops which _nth_hop are you on :param _prev_pool: the previous pool index. ex) (0, 0, 1, 2, 0) :param _sampled: the sampled pools we should append to _paths :param _pool_samples: pool_samples we made from the above sampling process :param _paths: the list that collects all viable combination of pools """ for _p in _pool_samples[_n_total_hops][_nth_hop]: _sampled[_nth_hop] = _p if _prev_pool is None or _prev_pool[TOKEN_OUT] == _p[TOKEN_IN]: if _nth_hop == _n_total_hops - 1: if _prev_pool: """ Exclude swaps that buy from the previous step, and sells on this one ex) ETH -> USDT -> ETH (ETH/USDT, USDT/ETH pools on the same exchange, version are considered equal) This isn't necessarily true in Uniswap V3 variants, because different fee levels can exist. However, we exclude that scenario for simplicity. """ same_exchange = _prev_pool[EXCHANGE] == _p[EXCHANGE] same_version = _prev_pool[VERSION] == _p[VERSION] same_pool = _prev_pool[TOKEN_IN] == _p[TOKEN_OUT] and _prev_pool[TOKEN_OUT] == _p[ TOKEN_IN] if not (same_exchange and same_version and same_pool): _paths.append(_sampled.copy()) else: _paths.append(_sampled.copy()) else: __sample_paths_list(_n_total_hops, _nth_hop + 1, _p, _sampled, _pool_samples, _paths) # Step #2 # Generate swap paths by applying conditional checks to see if token_in, token_out is in sync paths = [] empty_pool = [0, 0, 0, 0, 0] for i in range(1, self.max_swap_number + 1): if len(pool_samples[i]) > 0: sampled = [empty_pool] * self.max_swap_number prev_pool = None __sample_paths_list(_n_total_hops=i, _nth_hop=0, _prev_pool=prev_pool, _sampled=sampled, _pool_samples=pool_samples, _paths=paths) return paths class NoSymbolError(Exception): def __init__(self, msg: str): self.msg = msg def __str__(self): return self.msg class DEX(DexBase): def __init__(self, rpc_endpoints: Dict[str, str], tokens: Dict[str, Dict[str, List[str or int]]], pools: List[Dict[str, Any]], trading_symbols: List[str], max_swap_number: int = 3): super().__init__(rpc_endpoints, tokens, pools, trading_symbols, max_swap_number) self.load() for chain in self.chains_list: for symbol in self.trading_symbols: self.update_price_for_symbol(chain, symbol) def get_index(self, chain: str, exchange: str, token0: str, token1: str, version: int) -> tuple: c = self.chain_to_id[chain] e = self.exchange_to_id[exchange] t0 = self.token_to_id[token0] t1 = self.token_to_id[token1] v = V2 if version == 2 else V3 return c, e, t0, t1, v def get_price(self, c: int, e: int, t0: int, t1: int, v: int) -> tuple: idx = (c, e, t0, t1, v) dec0, dec1, res0, res1, sqrt, fee, tok0, _ = self.storage_array[idx] if v == V2: price = self.sim_v2.reserves_to_price(res0, res1, dec0, dec1, bool(tok0)) else: price = self.sim_v3.sqrtx96_to_price(sqrt, dec0, dec1, bool(tok0)) return price, fee def get_symbols_to_update(self, token0: str, token1: str) -> List[str]: """ Returns the symbols that need to be updated after token0, token1 storage information have been updated This is used in DexStream to figure out which symbols to update the price for after receiving Sync, Swap events from Uniswap V2, V3 variant exchanges """ token0_id = self.token_to_id[token0] token1_id = self.token_to_id[token1] symbols_to_update = [] for symbol in self.trading_symbols: tokens_involved = self.swap_paths[symbol]['tokens'] if token0_id in tokens_involved or token1_id in tokens_involved: symbols_to_update.append(symbol) return symbols_to_update def update_price_for_symbol(self, chain: str, symbol: str): if symbol not in self.trading_symbols: raise NoSymbolError(f'{symbol} not in {self.trading_symbols}') chain_idx = self.chain_to_id[chain] paths_arr = self.swap_paths[symbol]['path'] for i in np.arange(paths_arr.shape[0]): path = paths_arr[i] # There is always a first pool, so check the first pool's chain id if path[0][0] == chain_idx: price = 1 fee = 1 for p_step in np.arange(path.shape[0]): idx = path[p_step] if np.sum(idx) == 0: break _p, _f = self.get_price(*idx) """ Take the inverse of price. This is needed because if you are trying to BUY ETH with USDT, then token_in will be USDT, and token_out will be ETH. Thus, the quote amount of ETH you get for providing 1 USDT is currently: 0.0005387 ETH. However, we want the price to be in the format of ETH/USDT = 1856.32 USDT. (*This is the equivalent format of CEX price quotes. Binance ETH/USDT = 1856.xx USDT) To get this value, we take the inverse of price. 1 / 0.0005387 = 1856.32 """ price = price * (1 / _p) fee = fee * (1 - _f) self.swap_paths[symbol]['price'][i] = price self.swap_paths[symbol]['fee'][i] = 1 - fee def update_reserves(self, chain: str, exchange: str, token0: str, token1: str, reserve0: float, reserve1: float): idx_1 = self.get_index(chain, exchange, token0, token1, 2) idx_2 = (idx_1[0], idx_1[1], idx_1[3], idx_1[2], idx_1[4]) self.storage_array[idx_1][RESERVE0] = reserve0 self.storage_array[idx_1][RESERVE1] = reserve1 self.storage_array[idx_2][RESERVE0] = reserve0 self.storage_array[idx_2][RESERVE1] = reserve1 def update_sqrt_price(self, chain: str, exchange: str, token0: str, token1: str, sqrt_price: float): idx_1 = self.get_index(chain, exchange, token0, token1, 3) idx_2 = (idx_1[0], idx_1[1], idx_1[3], idx_1[2], idx_1[4]) self.storage_array[idx_1][SQRT_PRICE] = sqrt_price self.storage_array[idx_2][SQRT_PRICE] = sqrt_price def debug_message(self, chain: str, exchange: str, token0: str, token1: str, version: int) -> str: idx = self.get_index(chain, exchange, token0, token1, version) price, _ = self.get_price(*idx) return f'[{chain}] {exchange} V{version}: {token0}/{token1} @{price} & {token1}/{token0} @{1 / price}' if __name__ == '__main__': from configs import RPC_ENDPOINTS, TOKENS, POOLS dex = DEX(RPC_ENDPOINTS, TOKENS, POOLS, ['ETH/USDT']) ================================================ FILE: data/dex_streams.py ================================================ import os import json import time import eth_abi import asyncio import aiohttp import datetime import eth_utils import websockets import numpy as np import aioprocessing from functools import partial from dotenv import load_dotenv from typing import Any, Callable, Dict, Optional from data.dex import DEX from data.utils import reconnecting_websocket_loop, calculate_next_block_base_fee load_dotenv(override=True) BLOCKNATIVE_TOKEN = os.getenv('BLOCKNATIVE_TOKEN') def default_message_format(symbol: str, message: Dict[str, Any], block_number: int = 0) -> Dict[str, Any]: """ :param symbol: BTC/USDT, ETH/USDT, ... :param message: value of DEX.swap_paths[symbol] ex) {'path': np.ndarray, 'pool_indexes': List[List[int]], 'tag': List[str], 'tokens': np.ndarray, 'price': np.ndarray, 'fee': np.ndarray} :param block_number: block number of event :return: """ return { 'source': 'dex', 'type': 'event', 'block': block_number, 'path': message['path'].tolist(), 'pool_indexes': message['pool_indexes'], 'symbol': symbol, 'tag': message['tag'], 'price': message['price'].tolist(), 'fee': message['fee'].tolist(), } class DexStream: def __init__(self, dex: DEX, ws_endpoints: Dict[str, str], publisher: Optional[aioprocessing.AioQueue] = None, message_formatter: Callable = default_message_format, debug: bool = False): """ :param dex: DEX instance :param ws_endpoints: ex) {'ethereum': ''} :param publisher: an instance of aioprocessing.AioQueue, used to send processed market data to strategy :param message_formatter: is used to format message sent through the publisher this data will be accessed from the main process """ self.dex = dex self.ws_endpoints = ws_endpoints self.publisher = publisher self.message_formatter = message_formatter self.debug = debug def publish(self, data: Any): if self.publisher: self.publisher.put(data) def start_streams(self): streams = [] for chain in self.dex.chains_list: block_stream = reconnecting_websocket_loop( partial(self.stream_new_blocks, chain), tag=f'{chain.upper()}_Blocks' ) streams.append(block_stream) for chain in self.dex.chains_list: v2_stream = reconnecting_websocket_loop( partial(self.stream_uniswap_v2_events, chain), tag=f'{chain.upper()}_V2' ) v3_stream = reconnecting_websocket_loop( partial(self.stream_uniswap_v3_events, chain), tag=f'{chain.upper()}_V3' ) streams.extend([asyncio.ensure_future(f) for f in [v2_stream, v3_stream]]) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(streams)) async def stream_uniswap_v2_events(self, chain: str): filtered_pools = [ pool for pool in self.dex.pools if pool['chain'] == chain and pool['version'] == 2 ] pools = {pool['address'].lower(): pool for pool in filtered_pools} sync_event_selector = self.dex.web3[chain].keccak( text='Sync(uint112,uint112)' ).hex() async with websockets.connect(self.ws_endpoints[chain]) as ws: subscription = { 'json': '2.0', 'id': 1, 'method': 'eth_subscribe', 'params': [ 'logs', {'topics': [sync_event_selector]} ] } await ws.send(json.dumps(subscription)) _ = await ws.recv() while True: msg = await asyncio.wait_for(ws.recv(), timeout=60 * 10) event = json.loads(msg)['params']['result'] address = event['address'].lower() if address in pools: s = time.time() block_number = int(event['blockNumber'], base=16) pool = pools[address] data = eth_abi.decode( ['uint112', 'uint112'], eth_utils.decode_hex(event['data']) ) chain = pool['chain'] exchange = pool['exchange'] token0 = pool['token0'] token1 = pool['token1'] self.dex.update_reserves(chain, exchange, token0, token1, data[0], data[1]) symbols = self.dex.get_symbols_to_update(token0, token1) for symbol in symbols: self.dex.update_price_for_symbol(chain, symbol) self.publish(self.message_formatter(symbol, self.dex.swap_paths[symbol], block_number)) e = time.time() if self.debug: dbg_msg = self.dex.debug_message(chain, exchange, token0, token1, 2) print(f'{datetime.datetime.now()} {dbg_msg} -> Update took: {e - s} seconds') async def stream_uniswap_v3_events(self, chain: str): filtered_pools = [ pool for pool in self.dex.pools if pool['chain'] == chain and pool['version'] == 3 ] pools = {pool['address'].lower(): pool for pool in filtered_pools} swap_event_selector = self.dex.web3[chain].keccak( text='Swap(address,address,int256,int256,uint160,uint128,int24)' ).hex() async with websockets.connect(self.ws_endpoints[chain]) as ws: subscription = { 'json': '2.0', 'id': 1, 'method': 'eth_subscribe', 'params': [ 'logs', {'topics': [swap_event_selector]} ] } await ws.send(json.dumps(subscription)) _ = await ws.recv() while True: msg = await asyncio.wait_for(ws.recv(), timeout=60 * 10) event = json.loads(msg)['params']['result'] address = event['address'].lower() if address in pools: # we don't need the sender, recipient data in topics s = time.time() block_number = int(event['blockNumber'], base=16) pool = pools[address] data = eth_abi.decode( ['int256', 'int256', 'uint160', 'uint128', 'int24'], eth_utils.decode_hex(event['data']) ) chain = pool['chain'] exchange = pool['exchange'] token0 = pool['token0'] token1 = pool['token1'] self.dex.update_sqrt_price(chain, exchange, token0, token1, data[2]) symbols = self.dex.get_symbols_to_update(token0, token1) for symbol in symbols: self.dex.update_price_for_symbol(chain, symbol) self.publish(self.message_formatter(symbol, self.dex.swap_paths[symbol], block_number)) e = time.time() if self.debug: dbg_msg = self.dex.debug_message(chain, exchange, token0, token1, 3) print(f'{datetime.datetime.now()} {dbg_msg} -> Update took: {e - s} seconds') async def stream_new_blocks(self, chain: str): """ Retrieves new blocks and calculates base fees accordingly Base fees are calculated adhering to the EIP-1559 implementation and max_price, max_priority_fee_per_gas, max_fee_per_gas are retrieved using Blocknative's gas estimator endpoint - Ethereum: https://api.blocknative.com/gasprices/blockprices?chainId=1 - Polygon: https://api.blocknative.com/gasprices/blockprices?chainId=137 """ """ historical_gas tracks 100 historical base_fee, max_priority_fee_per_gas, max_fee_per_gas However, note that the historical data isn't loaded up from the start, but instead is filled in at real-time Thus, at start-up this data is an empty numpy array with all 0's """ historical_gas = np.zeros((3, 100)) # index of historical_gas BASE_FEE = 0 MAX_PRIORITY_FEE_PER_GAS = 1 MAX_FEE_PER_GAS = 2 async with websockets.connect(self.ws_endpoints[chain]) as ws: subscription = { 'json': '2.0', 'id': 1, 'method': 'eth_subscribe', 'params': ['newHeads'] } await ws.send(json.dumps(subscription)) _ = await ws.recv() gwei = 10 ** 9 while True: msg = await asyncio.wait_for(ws.recv(), timeout=60 * 10) block = json.loads(msg)['params']['result'] block_number = int(block['number'], base=16) base_fee = calculate_next_block_base_fee(block) """ For Ethereum and Polygon, use gas price estimation tools provided by Blocknative https://www.blocknative.com/gas-estimator Run only if a token is given """ if chain in ['ethereum', 'polygon'] and BLOCKNATIVE_TOKEN: chain_id = 1 if 'ethereum' else 137 headers = {'Authorization': BLOCKNATIVE_TOKEN} async with aiohttp.ClientSession(headers=headers) as session: async with session.get(f'https://api.blocknative.com/gasprices/blockprices?chainId={chain_id}') as r: res = await r.json() estimated_price = res['blockPrices'][0]['estimatedPrices'][0] max_priority_fee_per_gas = estimated_price['maxPriorityFeePerGas'] max_fee_per_gas = estimated_price['maxFeePerGas'] new_gas_data = [ base_fee, int(max_priority_fee_per_gas * gwei), int(max_fee_per_gas * gwei) ] else: new_gas_data = [base_fee, 0, 0] """ update historical_gas data as you would an Deque data structure Currently this data does nothing, however, it can be used to optimize gas costs later """ historical_gas[:, 0:-1] = historical_gas[:, 1:] historical_gas[:, -1] = new_gas_data data = { 'source': 'dex', 'type': 'block', 'chain': chain, 'block': block_number, 'base_fee': int(historical_gas[BASE_FEE, -1]), 'max_priority_fee_per_gas': int(historical_gas[MAX_PRIORITY_FEE_PER_GAS, -1]), 'max_fee_per_gas': int(historical_gas[MAX_FEE_PER_GAS, -1]), } self.publish(data) if __name__ == '__main__': from configs import ( RPC_ENDPOINTS, WS_ENDPOINTS, TOKENS, POOLS, ) chain = 'ethereum' rpc_endpoints = {chain: RPC_ENDPOINTS[chain]} ws_endpoints = {chain: WS_ENDPOINTS[chain]} tokens = {chain: TOKENS[chain]} pools = [pool for pool in POOLS if pool['chain'] == chain] dex = DEX(rpc_endpoints, tokens, pools, ['ETH/USDT']) queue = aioprocessing.AioQueue() dex_stream = DexStream(dex, ws_endpoints, queue, default_message_format, False) # dex_stream.start_streams() asyncio.run(dex_stream.stream_new_blocks('ethereum')) ================================================ FILE: data/utils.py ================================================ import random import asyncio import websockets from typing import Any, Callable, Dict async def reconnecting_websocket_loop(stream_fn: Callable, tag: str): while True: try: await stream_fn() except (websockets.ConnectionClosedError, websockets.ConnectionClosedOK) as e: print(f'{tag} websocket connection closed: {e}') print('Reconnecting...') await asyncio.sleep(2) except Exception as e: print(f'An error has occurred with {tag} websocket: {e}') break def calculate_next_block_base_fee(block: Dict[str, Any]): base_fee = int(block['baseFeePerGas'], base=16) gas_used = int(block['gasUsed'], base=16) gas_limit = int(block['gasLimit'], base=16) target_gas_used = gas_limit / 2 target_gas_used = 1 if target_gas_used == 0 else target_gas_used if gas_used > target_gas_used: new_base_fee = base_fee + ((base_fee * (gas_used - target_gas_used)) / target_gas_used) / 8 else: new_base_fee = base_fee - ((base_fee * (target_gas_used - gas_used)) / target_gas_used) / 8 return int(new_base_fee + random.randint(0, 9)) ================================================ FILE: examples/dex.py ================================================ import numpy as np from data.dex import DEX from configs import RPC_ENDPOINTS, TOKENS, POOLS if __name__ == '__main__': dex = DEX(RPC_ENDPOINTS, TOKENS, POOLS, ['ETH/USDT', 'BTC/USDT']) # Retrieving the price of a specific pool idx = dex.get_index('ethereum', 'uniswap', 'ETH', 'USDT', 3) print(idx) price, fee = dex.get_price(*idx) print(price, fee) # Retrieving price of multiple swap paths btc_price = dex.swap_paths['BTC/USDT']['price'] btc_fee = dex.swap_paths['BTC/USDT']['fee'] print(btc_price) print(btc_fee) # Get the lowest price path: best for BUY orders buy_price = np.min(btc_price) # Get the highest price path: best for SELL orders sell_price = np.max(btc_price) # Calculate the spread between the two paths buy_sell_spread = (sell_price / buy_price - 1) * 100 print(f'Buy-Sell price spread: {buy_sell_spread}%') ================================================ FILE: execution/WhackAMoleBotV1.json ================================================ { "abi": [ { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [], "name": "TradeFailed", "type": "error" }, { "inputs": [], "name": "Unauthorized", "type": "error" }, { "inputs": [ { "internalType": "address[]", "name": "tokens", "type": "address[]" }, { "internalType": "address[]", "name": "protocols", "type": "address[]" } ], "name": "approveHandlers", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [ { "internalType": "address[]", "name": "tokens", "type": "address[]" } ], "name": "recoverTokens", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [ { "components": [ { "internalType": "uint8", "name": "protocol", "type": "uint8" }, { "internalType": "address", "name": "handler", "type": "address" }, { "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "internalType": "struct WhackAMoleBotV1.SwapParams[]", "name": "paramsArray", "type": "tuple[]" }, { "internalType": "uint256", "name": "minAmountOut", "type": "uint256" } ], "name": "whack", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "payable", "type": "function" } ], "bytecode": { "object": "0x60a060405234801561001057600080fd5b5033608052608051610f0d61003060003960006102fa0152610f0d6000f3fe6080604052600436106100345760003560e01c80639c832f9314610039578063bc2078fb1461004e578063c0f757c014610061575b600080fd5b61004c610047366004610a9e565b610086565b005b61004c61005c366004610ae0565b61016d565b61007461006f366004610b4c565b61020f565b60405190815260200160405180910390f35b61008e6102ef565b8060005b818110156101675760008484838181106100ae576100ae610bc7565b90506020020160208101906100c39190610bf9565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915061015e9033906001600160a01b038416906370a0823190602401602060405180830381865afa158015610129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061014d9190610c14565b6001600160a01b0384169190610353565b50600101610092565b50505050565b600019838260005b8281101561020557600088888381811061019157610191610bc7565b90506020020160208101906101a69190610bf9565b905060005b838110156101fb5760008888838181106101c7576101c7610bc7565b90506020020160208101906101dc9190610bf9565b90506101f26001600160a01b0384168289610401565b506001016101ab565b5050600101610175565b5050505050505050565b60006102196102ef565b600083815b818110156102ab57600087878381811061023a5761023a610bc7565b905060c002018036038101906102509190610c9d565b905083600003610266578060a00151935061026e565b60a081018490525b805160ff1660000361028a5761028381610554565b93506102a2565b805160ff166001036102a25761029f81610696565b93505b5060010161021e565b50838210156102e6576040517f2d8ef0cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50949350505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610351576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6040516001600160a01b0383166024820152604481018290526103fc9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526107c3565b505050565b80158061049457506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa15801561046e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104929190610c14565b155b61050b5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e63650000000000000000000060648201526084015b60405180910390fd5b6040516001600160a01b0383166024820152604481018290526103fc9084907f095ea7b30000000000000000000000000000000000000000000000000000000090606401610398565b604080516002808252606080830184526000939092919060208301908036833701905050905082604001518160008151811061059257610592610bc7565b60200260200101906001600160a01b031690816001600160a01b0316815250508260600151816001815181106105ca576105ca610bc7565b60200260200101906001600160a01b031690816001600160a01b031681525050600083602001516001600160a01b03166338ed17398560a0015160008530426040518663ffffffff1660e01b8152600401610629959493929190610d2b565b6000604051808303816000875af1158015610648573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106709190810190610d9c565b90508060018151811061068557610685610bc7565b602002602001015192505050919050565b6040805161010081018252828201516001600160a01b0390811682526060808501518216602080850191825260808088015162ffffff908116878901908152309588019586524292880192835260a0808b0151908901908152600060c08a0181815260e08b01828152968d01519b517f414bf3890000000000000000000000000000000000000000000000000000000081528b518b16600482015297518a1660248901529251909316604487015295518716606486015291516084850152935160a48401525160c483015251831660e482015290939091169063414bf38990610104016020604051808303816000875af1158015610798573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107bc9190610c14565b9392505050565b6000610818826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166108ab9092919063ffffffff16565b90508051600014806108395750808060200190518101906108399190610e42565b6103fc5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610502565b60606108ba84846000856108c2565b949350505050565b60608247101561093a5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610502565b600080866001600160a01b031685876040516109569190610e88565b60006040518083038185875af1925050503d8060008114610993576040519150601f19603f3d011682016040523d82523d6000602084013e610998565b606091505b50915091506109a9878383876109b4565b979650505050505050565b60608315610a23578251600003610a1c576001600160a01b0385163b610a1c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610502565b50816108ba565b6108ba8383815115610a385781518083602001fd5b8060405162461bcd60e51b81526004016105029190610ea4565b60008083601f840112610a6457600080fd5b50813567ffffffffffffffff811115610a7c57600080fd5b6020830191508360208260051b8501011115610a9757600080fd5b9250929050565b60008060208385031215610ab157600080fd5b823567ffffffffffffffff811115610ac857600080fd5b610ad485828601610a52565b90969095509350505050565b60008060008060408587031215610af657600080fd5b843567ffffffffffffffff80821115610b0e57600080fd5b610b1a88838901610a52565b90965094506020870135915080821115610b3357600080fd5b50610b4087828801610a52565b95989497509550505050565b600080600060408486031215610b6157600080fd5b833567ffffffffffffffff80821115610b7957600080fd5b818601915086601f830112610b8d57600080fd5b813581811115610b9c57600080fd5b87602060c083028501011115610bb157600080fd5b6020928301989097509590910135949350505050565b634e487b7160e01b600052603260045260246000fd5b80356001600160a01b0381168114610bf457600080fd5b919050565b600060208284031215610c0b57600080fd5b6107bc82610bdd565b600060208284031215610c2657600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff81118282101715610c6657610c66610c2d565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610c9557610c95610c2d565b604052919050565b600060c08284031215610caf57600080fd5b610cb7610c43565b823560ff81168114610cc857600080fd5b8152610cd660208401610bdd565b6020820152610ce760408401610bdd565b6040820152610cf860608401610bdd565b6060820152608083013562ffffff81168114610d1357600080fd5b608082015260a0928301359281019290925250919050565b600060a082018783526020878185015260a0604085015281875180845260c086019150828901935060005b81811015610d7b5784516001600160a01b031683529383019391830191600101610d56565b50506001600160a01b03969096166060850152505050608001529392505050565b60006020808385031215610daf57600080fd5b825167ffffffffffffffff80821115610dc757600080fd5b818501915085601f830112610ddb57600080fd5b815181811115610ded57610ded610c2d565b8060051b9150610dfe848301610c6c565b8181529183018401918481019088841115610e1857600080fd5b938501935b83851015610e3657845182529385019390850190610e1d565b98975050505050505050565b600060208284031215610e5457600080fd5b815180151581146107bc57600080fd5b60005b83811015610e7f578181015183820152602001610e67565b50506000910152565b60008251610e9a818460208701610e64565b9190910192915050565b6020815260008251806020840152610ec3816040850160208701610e64565b601f01601f1916919091016040019291505056fea2646970667358221220e75668af077dbec1fec4d2e8edeec37cd59855fc788594733b9b89d21e48939064736f6c63430008140033", "sourceMap": "433:3979:25:-:0;;;763:49;;;;;;;;;-1:-1:-1;795:10:25;787:18;;433:3979;;;;;;;;;;;;", "linkReferences": {} }, "deployedBytecode": { "object": "0x6080604052600436106100345760003560e01c80639c832f9314610039578063bc2078fb1461004e578063c0f757c014610061575b600080fd5b61004c610047366004610a9e565b610086565b005b61004c61005c366004610ae0565b61016d565b61007461006f366004610b4c565b61020f565b60405190815260200160405180910390f35b61008e6102ef565b8060005b818110156101675760008484838181106100ae576100ae610bc7565b90506020020160208101906100c39190610bf9565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915061015e9033906001600160a01b038416906370a0823190602401602060405180830381865afa158015610129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061014d9190610c14565b6001600160a01b0384169190610353565b50600101610092565b50505050565b600019838260005b8281101561020557600088888381811061019157610191610bc7565b90506020020160208101906101a69190610bf9565b905060005b838110156101fb5760008888838181106101c7576101c7610bc7565b90506020020160208101906101dc9190610bf9565b90506101f26001600160a01b0384168289610401565b506001016101ab565b5050600101610175565b5050505050505050565b60006102196102ef565b600083815b818110156102ab57600087878381811061023a5761023a610bc7565b905060c002018036038101906102509190610c9d565b905083600003610266578060a00151935061026e565b60a081018490525b805160ff1660000361028a5761028381610554565b93506102a2565b805160ff166001036102a25761029f81610696565b93505b5060010161021e565b50838210156102e6576040517f2d8ef0cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50949350505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610351576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6040516001600160a01b0383166024820152604481018290526103fc9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526107c3565b505050565b80158061049457506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa15801561046e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104929190610c14565b155b61050b5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e63650000000000000000000060648201526084015b60405180910390fd5b6040516001600160a01b0383166024820152604481018290526103fc9084907f095ea7b30000000000000000000000000000000000000000000000000000000090606401610398565b604080516002808252606080830184526000939092919060208301908036833701905050905082604001518160008151811061059257610592610bc7565b60200260200101906001600160a01b031690816001600160a01b0316815250508260600151816001815181106105ca576105ca610bc7565b60200260200101906001600160a01b031690816001600160a01b031681525050600083602001516001600160a01b03166338ed17398560a0015160008530426040518663ffffffff1660e01b8152600401610629959493929190610d2b565b6000604051808303816000875af1158015610648573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106709190810190610d9c565b90508060018151811061068557610685610bc7565b602002602001015192505050919050565b6040805161010081018252828201516001600160a01b0390811682526060808501518216602080850191825260808088015162ffffff908116878901908152309588019586524292880192835260a0808b0151908901908152600060c08a0181815260e08b01828152968d01519b517f414bf3890000000000000000000000000000000000000000000000000000000081528b518b16600482015297518a1660248901529251909316604487015295518716606486015291516084850152935160a48401525160c483015251831660e482015290939091169063414bf38990610104016020604051808303816000875af1158015610798573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107bc9190610c14565b9392505050565b6000610818826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166108ab9092919063ffffffff16565b90508051600014806108395750808060200190518101906108399190610e42565b6103fc5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610502565b60606108ba84846000856108c2565b949350505050565b60608247101561093a5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610502565b600080866001600160a01b031685876040516109569190610e88565b60006040518083038185875af1925050503d8060008114610993576040519150601f19603f3d011682016040523d82523d6000602084013e610998565b606091505b50915091506109a9878383876109b4565b979650505050505050565b60608315610a23578251600003610a1c576001600160a01b0385163b610a1c5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610502565b50816108ba565b6108ba8383815115610a385781518083602001fd5b8060405162461bcd60e51b81526004016105029190610ea4565b60008083601f840112610a6457600080fd5b50813567ffffffffffffffff811115610a7c57600080fd5b6020830191508360208260051b8501011115610a9757600080fd5b9250929050565b60008060208385031215610ab157600080fd5b823567ffffffffffffffff811115610ac857600080fd5b610ad485828601610a52565b90969095509350505050565b60008060008060408587031215610af657600080fd5b843567ffffffffffffffff80821115610b0e57600080fd5b610b1a88838901610a52565b90965094506020870135915080821115610b3357600080fd5b50610b4087828801610a52565b95989497509550505050565b600080600060408486031215610b6157600080fd5b833567ffffffffffffffff80821115610b7957600080fd5b818601915086601f830112610b8d57600080fd5b813581811115610b9c57600080fd5b87602060c083028501011115610bb157600080fd5b6020928301989097509590910135949350505050565b634e487b7160e01b600052603260045260246000fd5b80356001600160a01b0381168114610bf457600080fd5b919050565b600060208284031215610c0b57600080fd5b6107bc82610bdd565b600060208284031215610c2657600080fd5b5051919050565b634e487b7160e01b600052604160045260246000fd5b60405160c0810167ffffffffffffffff81118282101715610c6657610c66610c2d565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610c9557610c95610c2d565b604052919050565b600060c08284031215610caf57600080fd5b610cb7610c43565b823560ff81168114610cc857600080fd5b8152610cd660208401610bdd565b6020820152610ce760408401610bdd565b6040820152610cf860608401610bdd565b6060820152608083013562ffffff81168114610d1357600080fd5b608082015260a0928301359281019290925250919050565b600060a082018783526020878185015260a0604085015281875180845260c086019150828901935060005b81811015610d7b5784516001600160a01b031683529383019391830191600101610d56565b50506001600160a01b03969096166060850152505050608001529392505050565b60006020808385031215610daf57600080fd5b825167ffffffffffffffff80821115610dc757600080fd5b818501915085601f830112610ddb57600080fd5b815181811115610ded57610ded610c2d565b8060051b9150610dfe848301610c6c565b8181529183018401918481019088841115610e1857600080fd5b938501935b83851015610e3657845182529385019390850190610e1d565b98975050505050505050565b600060208284031215610e5457600080fd5b815180151581146107bc57600080fd5b60005b83811015610e7f578181015183820152602001610e67565b50506000910152565b60008251610e9a818460208701610e64565b9190910192915050565b6020815260008251806020840152610ec3816040850160208701610e64565b601f01601f1916919091016040019291505056fea2646970667358221220e75668af077dbec1fec4d2e8edeec37cd59855fc788594733b9b89d21e48939064736f6c63430008140033", "sourceMap": "433:3979:25:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1024:404;;;;;;:::i;:::-;;:::i;:::-;;1476:779;;;;;;:::i;:::-;;:::i;2323:876::-;;;;;;:::i;:::-;;:::i;:::-;;;2480:25:33;;;2468:2;2453:18;2323:876:25;;;;;;;1024:404;849:13;:11;:13::i;:::-;1123:6;1109:11:::1;1147:275;1164:6;1160:1;:10;1147:275;;;1188:13;1204:6;;1211:1;1204:9;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;1299:38;::::0;;;;1331:4:::1;1299:38;::::0;::::1;3243:74:33::0;1188:25:25;;-1:-1:-1;1227:124:25::1;::::0;1271:10:::1;::::0;-1:-1:-1;;;;;1299:23:25;::::1;::::0;::::1;::::0;3216:18:33;;1299:38:25::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1::0;;;;;1227:26:25;::::1;::::0;:124;:26:::1;:124::i;:::-;-1:-1:-1::0;1394:3:25::1;;1147:275;;;;1099:329;1024:404:::0;;:::o;1476:779::-;-1:-1:-1;;1788:6:25;1834:9;1726:11;1861:388;1878:12;1874:1;:16;1861:388;;;1908:12;1930:6;;1937:1;1930:9;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;1908:32;;1959:6;1954:225;1971:15;1967:1;:19;1954:225;;;2008:16;2027:9;;2037:1;2027:12;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;2008:31;-1:-1:-1;2057:35:25;-1:-1:-1;;;;;2057:17:25;;2008:31;2085:6;2057:17;:35::i;:::-;-1:-1:-1;2143:3:25;;1954:225;;;-1:-1:-1;;2221:3:25;;1861:388;;;;1595:660;;;1476:779;;;;:::o;2323:876::-;2451:7;849:13;:11;:13::i;:::-;2470:17:::1;2525:11:::0;2470:17;2554:527:::1;2574:17;2570:1;:21;2554:527;;;2609:24;2636:11;;2648:1;2636:14;;;;;;;:::i;:::-;;;;;;2609:41;;;;;;;;;;:::i;:::-;;;2669:9;2682:1;2669:14:::0;2665:142:::1;;2715:6;:13;;;2703:25;;2665:142;;;2767:13;::::0;::::1;:25:::0;;;2665:142:::1;2825:15:::0;;:20:::1;;:15;:20:::0;2821:190:::1;;2877:21;2891:6;2877:13;:21::i;:::-;2865:33;;2821:190;;;2923:15:::0;;:20:::1;;2942:1;2923:20:::0;2919:92:::1;;2975:21;2989:6;2975:13;:21::i;:::-;2963:33;;2919:92;-1:-1:-1::0;3053:3:25::1;;2554:527;;;;3107:12;3095:9;:24;3091:75;;;3142:13;;;;;;;;;;;;;;3091:75;-1:-1:-1::0;3183:9:25;2323:876;-1:-1:-1;;;;2323:876:25:o;886:132::-;945:10;-1:-1:-1;;;;;959:5:25;945:19;;941:71;;987:14;;;;;;;;;;;;;;941:71;886:132::o;941:175:21:-;1050:58;;-1:-1:-1;;;;;5272:55:33;;1050:58:21;;;5254:74:33;5344:18;;;5337:34;;;1023:86:21;;1043:5;;1073:23;;5227:18:33;;1050:58:21;;;;-1:-1:-1;;1050:58:21;;;;;;;;;;;;;;;;;;;;;;;;;;;1023:19;:86::i;:::-;941:175;;;:::o;1818:573::-;2143:10;;;2142:62;;-1:-1:-1;2159:39:21;;;;;2183:4;2159:39;;;5617:34:33;-1:-1:-1;;;;;5687:15:33;;;5667:18;;;5660:43;2159:15:21;;;;;5529:18:33;;2159:39:21;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:44;2142:62;2121:163;;;;-1:-1:-1;;;2121:163:21;;5916:2:33;2121:163:21;;;5898:21:33;5955:2;5935:18;;;5928:30;5994:34;5974:18;;;5967:62;6065:24;6045:18;;;6038:52;6107:19;;2121:163:21;;;;;;;;;2321:62;;-1:-1:-1;;;;;5272:55:33;;2321:62:21;;;5254:74:33;5344:18;;;5337:34;;;2294:90:21;;2314:5;;2344:22;;5227:18:33;;2321:62:21;5080:297:33;3225:523:25;3373:16;;;3387:1;3373:16;;;3335:21;3373:16;;;;;3306:17;;3335:21;;3373:16;3387:1;3373:16;;;;;;;;;;-1:-1:-1;3373:16:25;3366:23;;3409:6;:14;;;3399:4;3404:1;3399:7;;;;;;;;:::i;:::-;;;;;;:24;-1:-1:-1;;;;;3399:24:25;;;-1:-1:-1;;;;;3399:24:25;;;;;3443:6;:15;;;3433:4;3438:1;3433:7;;;;;;;;:::i;:::-;;;;;;:25;-1:-1:-1;;;;;3433:25:25;;;-1:-1:-1;;;;;3433:25:25;;;;;3469:21;3510:6;:14;;;-1:-1:-1;;;;;3493:70:25;;3581:6;:13;;;3612:1;3631:4;3661;3684:15;3493:220;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;3493:220:25;;;;;;;;;;;;:::i;:::-;3469:244;;3731:7;3739:1;3731:10;;;;;;;;:::i;:::-;;;;;;;3724:17;;;;3225:523;;;:::o;3774:636::-;3941:381;;;;;;;;4016:14;;;;-1:-1:-1;;;;;3941:381:25;;;;;4058:15;;;;;3941:381;;;;;;;;;4096:10;;;;;3941:381;;;;;;;;;;4143:4;3941:381;;;;;;4176:15;3941:381;;;;;;;4219:13;;;;3941:381;;;;;;3855:17;3941:381;;;;;;;;;;;;4357:14;;;;4345:58;;;;;8421:13:33;;8417:22;;4345:58:25;;;8399:41:33;8482:24;;8478:33;;8456:20;;;8449:63;8554:24;;8550:39;;;8528:20;;;8521:69;8632:24;;8628:33;;8606:20;;;8599:63;8700:24;;8678:20;;;8671:54;8763:24;;8741:20;;;8734:54;8826:24;8804:20;;;8797:54;8893:24;8889:33;;8867:20;;;8860:63;3855:17:25;;4345:44;;;;;;8310:19:33;;4345:58:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4333:70;3774:636;-1:-1:-1;;;3774:636:25:o;5173:642:21:-;5592:23;5618:69;5646:4;5618:69;;;;;;;;;;;;;;;;;5626:5;-1:-1:-1;;;;;5618:27:21;;;:69;;;;;:::i;:::-;5592:95;;5705:10;:17;5726:1;5705:22;:56;;;;5742:10;5731:30;;;;;;;;;;;;:::i;:::-;5697:111;;;;-1:-1:-1;;;5697:111:21;;9418:2:33;5697:111:21;;;9400:21:33;9457:2;9437:18;;;9430:30;9496:34;9476:18;;;9469:62;9567:12;9547:18;;;9540:40;9597:19;;5697:111:21;9216:406:33;4108:223:22;4241:12;4272:52;4294:6;4302:4;4308:1;4311:12;4272:21;:52::i;:::-;4265:59;4108:223;-1:-1:-1;;;;4108:223:22:o;5165:446::-;5330:12;5387:5;5362:21;:30;;5354:81;;;;-1:-1:-1;;;5354:81:22;;9829:2:33;5354:81:22;;;9811:21:33;9868:2;9848:18;;;9841:30;9907:34;9887:18;;;9880:62;9978:8;9958:18;;;9951:36;10004:19;;5354:81:22;9627:402:33;5354:81:22;5446:12;5460:23;5487:6;-1:-1:-1;;;;;5487:11:22;5506:5;5513:4;5487:31;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5445:73;;;;5535:69;5562:6;5570:7;5579:10;5591:12;5535:26;:69::i;:::-;5528:76;5165:446;-1:-1:-1;;;;;;;5165:446:22:o;7671:628::-;7851:12;7879:7;7875:418;;;7906:10;:17;7927:1;7906:22;7902:286;;-1:-1:-1;;;;;1702:19:22;;;8113:60;;;;-1:-1:-1;;;8113:60:22;;10783:2:33;8113:60:22;;;10765:21:33;10822:2;10802:18;;;10795:30;10861:31;10841:18;;;10834:59;10910:18;;8113:60:22;10581:353:33;8113:60:22;-1:-1:-1;8208:10:22;8201:17;;7875:418;8249:33;8257:10;8269:12;8980:17;;:21;8976:379;;9208:10;9202:17;9264:15;9251:10;9247:2;9243:19;9236:44;8976:379;9331:12;9324:20;;-1:-1:-1;;;9324:20:22;;;;;;;;:::i;14:367:33:-;77:8;87:6;141:3;134:4;126:6;122:17;118:27;108:55;;159:1;156;149:12;108:55;-1:-1:-1;182:20:33;;225:18;214:30;;211:50;;;257:1;254;247:12;211:50;294:4;286:6;282:17;270:29;;354:3;347:4;337:6;334:1;330:14;322:6;318:27;314:38;311:47;308:67;;;371:1;368;361:12;308:67;14:367;;;;;:::o;386:437::-;472:6;480;533:2;521:9;512:7;508:23;504:32;501:52;;;549:1;546;539:12;501:52;589:9;576:23;622:18;614:6;611:30;608:50;;;654:1;651;644:12;608:50;693:70;755:7;746:6;735:9;731:22;693:70;:::i;:::-;782:8;;667:96;;-1:-1:-1;386:437:33;-1:-1:-1;;;;386:437:33:o;828:773::-;950:6;958;966;974;1027:2;1015:9;1006:7;1002:23;998:32;995:52;;;1043:1;1040;1033:12;995:52;1083:9;1070:23;1112:18;1153:2;1145:6;1142:14;1139:34;;;1169:1;1166;1159:12;1139:34;1208:70;1270:7;1261:6;1250:9;1246:22;1208:70;:::i;:::-;1297:8;;-1:-1:-1;1182:96:33;-1:-1:-1;1385:2:33;1370:18;;1357:32;;-1:-1:-1;1401:16:33;;;1398:36;;;1430:1;1427;1420:12;1398:36;;1469:72;1533:7;1522:8;1511:9;1507:24;1469:72;:::i;:::-;828:773;;;;-1:-1:-1;1560:8:33;-1:-1:-1;;;;828:773:33:o;1606:723::-;1732:6;1740;1748;1801:2;1789:9;1780:7;1776:23;1772:32;1769:52;;;1817:1;1814;1807:12;1769:52;1857:9;1844:23;1886:18;1927:2;1919:6;1916:14;1913:34;;;1943:1;1940;1933:12;1913:34;1981:6;1970:9;1966:22;1956:32;;2026:7;2019:4;2015:2;2011:13;2007:27;1997:55;;2048:1;2045;2038:12;1997:55;2088:2;2075:16;2114:2;2106:6;2103:14;2100:34;;;2130:1;2127;2120:12;2100:34;2188:7;2181:4;2173;2165:6;2161:17;2157:2;2153:26;2149:37;2146:50;2143:70;;;2209:1;2206;2199:12;2143:70;2240:4;2232:13;;;;2264:6;;-1:-1:-1;2302:20:33;;;;2289:34;;1606:723;-1:-1:-1;;;;1606:723:33:o;2516:184::-;-1:-1:-1;;;2565:1:33;2558:88;2665:4;2662:1;2655:15;2689:4;2686:1;2679:15;2705:196;2773:20;;-1:-1:-1;;;;;2822:54:33;;2812:65;;2802:93;;2891:1;2888;2881:12;2802:93;2705:196;;;:::o;2906:186::-;2965:6;3018:2;3006:9;2997:7;2993:23;2989:32;2986:52;;;3034:1;3031;3024:12;2986:52;3057:29;3076:9;3057:29;:::i;3328:184::-;3398:6;3451:2;3439:9;3430:7;3426:23;3422:32;3419:52;;;3467:1;3464;3457:12;3419:52;-1:-1:-1;3490:16:33;;3328:184;-1:-1:-1;3328:184:33:o;3517:::-;-1:-1:-1;;;3566:1:33;3559:88;3666:4;3663:1;3656:15;3690:4;3687:1;3680:15;3706:252;3778:2;3772:9;3820:3;3808:16;;3854:18;3839:34;;3875:22;;;3836:62;3833:88;;;3901:18;;:::i;:::-;3937:2;3930:22;3706:252;:::o;3963:275::-;4034:2;4028:9;4099:2;4080:13;;-1:-1:-1;;4076:27:33;4064:40;;4134:18;4119:34;;4155:22;;;4116:62;4113:88;;;4181:18;;:::i;:::-;4217:2;4210:22;3963:275;;-1:-1:-1;3963:275:33:o;4243:832::-;4331:6;4384:3;4372:9;4363:7;4359:23;4355:33;4352:53;;;4401:1;4398;4391:12;4352:53;4427:22;;:::i;:::-;4486:9;4473:23;4540:4;4531:7;4527:18;4518:7;4515:31;4505:59;;4560:1;4557;4550:12;4505:59;4573:22;;4627:38;4661:2;4646:18;;4627:38;:::i;:::-;4622:2;4615:5;4611:14;4604:62;4698:38;4732:2;4721:9;4717:18;4698:38;:::i;:::-;4693:2;4686:5;4682:14;4675:62;4769:38;4803:2;4792:9;4788:18;4769:38;:::i;:::-;4764:2;4757:5;4753:14;4746:62;4860:3;4849:9;4845:19;4832:33;4909:8;4900:7;4896:22;4887:7;4884:35;4874:63;;4933:1;4930;4923:12;4874:63;4964:3;4953:15;;4946:32;5039:3;5024:19;;;5011:33;4994:15;;;4987:58;;;;-1:-1:-1;4957:5:33;4243:832;-1:-1:-1;4243:832:33:o;6137:1026::-;6399:4;6447:3;6436:9;6432:19;6478:6;6467:9;6460:25;6504:2;6542:6;6537:2;6526:9;6522:18;6515:34;6585:3;6580:2;6569:9;6565:18;6558:31;6609:6;6644;6638:13;6675:6;6667;6660:22;6713:3;6702:9;6698:19;6691:26;;6752:2;6744:6;6740:15;6726:29;;6773:1;6783:218;6797:6;6794:1;6791:13;6783:218;;;6862:13;;-1:-1:-1;;;;;6858:62:33;6846:75;;6976:15;;;;6941:12;;;;6819:1;6812:9;6783:218;;;-1:-1:-1;;;;;;;7057:55:33;;;;7052:2;7037:18;;7030:83;-1:-1:-1;;;7144:3:33;7129:19;7122:35;7018:3;6137:1026;-1:-1:-1;;;6137:1026:33:o;7168:936::-;7263:6;7294:2;7337;7325:9;7316:7;7312:23;7308:32;7305:52;;;7353:1;7350;7343:12;7305:52;7386:9;7380:16;7415:18;7456:2;7448:6;7445:14;7442:34;;;7472:1;7469;7462:12;7442:34;7510:6;7499:9;7495:22;7485:32;;7555:7;7548:4;7544:2;7540:13;7536:27;7526:55;;7577:1;7574;7567:12;7526:55;7606:2;7600:9;7628:2;7624;7621:10;7618:36;;;7634:18;;:::i;:::-;7680:2;7677:1;7673:10;7663:20;;7703:28;7727:2;7723;7719:11;7703:28;:::i;:::-;7765:15;;;7835:11;;;7831:20;;;7796:12;;;;7863:19;;;7860:39;;;7895:1;7892;7885:12;7860:39;7919:11;;;;7939:135;7955:6;7950:3;7947:15;7939:135;;;8021:10;;8009:23;;7972:12;;;;8052;;;;7939:135;;;8093:5;7168:936;-1:-1:-1;;;;;;;;7168:936:33:o;8934:277::-;9001:6;9054:2;9042:9;9033:7;9029:23;9025:32;9022:52;;;9070:1;9067;9060:12;9022:52;9102:9;9096:16;9155:5;9148:13;9141:21;9134:5;9131:32;9121:60;;9177:1;9174;9167:12;10034:250;10119:1;10129:113;10143:6;10140:1;10137:13;10129:113;;;10219:11;;;10213:18;10200:11;;;10193:39;10165:2;10158:10;10129:113;;;-1:-1:-1;;10276:1:33;10258:16;;10251:27;10034:250::o;10289:287::-;10418:3;10456:6;10450:13;10472:66;10531:6;10526:3;10519:4;10511:6;10507:17;10472:66;:::i;:::-;10554:16;;;;;10289:287;-1:-1:-1;;10289:287:33:o;10939:396::-;11088:2;11077:9;11070:21;11051:4;11120:6;11114:13;11163:6;11158:2;11147:9;11143:18;11136:34;11179:79;11251:6;11246:2;11235:9;11231:18;11226:2;11218:6;11214:15;11179:79;:::i;:::-;11319:2;11298:15;-1:-1:-1;;11294:29:33;11279:45;;;;11326:2;11275:54;;10939:396;-1:-1:-1;;10939:396:33:o", "linkReferences": {}, "immutableReferences": { "30835": [ { "start": 762, "length": 32 } ] } }, "methodIdentifiers": { "approveHandlers(address[],address[])": "bc2078fb", "recoverTokens(address[])": "9c832f93", "whack((uint8,address,address,address,uint24,uint256)[],uint256)": "c0f757c0" }, "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"TradeFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"Unauthorized\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"},{\"internalType\":\"address[]\",\"name\":\"protocols\",\"type\":\"address[]\"}],\"name\":\"approveHandlers\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"tokens\",\"type\":\"address[]\"}],\"name\":\"recoverTokens\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"protocol\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"handler\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint24\",\"name\":\"fee\",\"type\":\"uint24\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct WhackAMoleBotV1.SwapParams[]\",\"name\":\"paramsArray\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256\",\"name\":\"minAmountOut\",\"type\":\"uint256\"}],\"name\":\"whack\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/WhackAMoleBotV1.sol\":\"WhackAMoleBotV1\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000},\"remappings\":[\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts/contracts/\"]},\"sources\":{\"lib/openzeppelin-contracts/contracts/token/erc20/IERC20.sol\":{\"keccak256\":\"0x287b55befed2961a7eabd7d7b1b2839cbca8a5b80ef8dcbb25ed3d4c2002c305\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://bd39944e8fc06be6dbe2dd1d8449b5336e23c6a7ba3e8e9ae5ae0f37f35283f5\",\"dweb:/ipfs/QmPV3FGYjVwvKSgAXKUN3r9T9GwniZz83CxBpM7vyj2G53\"]},\"lib/openzeppelin-contracts/contracts/token/erc20/extensions/IERC20Permit.sol\":{\"keccak256\":\"0xec63854014a5b4f2b3290ab9103a21bdf902a508d0f41a8573fea49e98bf571a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://bc5b5dc12fbc4002f282eaa7a5f06d8310ed62c1c77c5770f6283e058454c39a\",\"dweb:/ipfs/Qme9rE2wS3yBuyJq9GgbmzbsBQsW2M2sVFqYYLw7bosGrv\"]},\"lib/openzeppelin-contracts/contracts/token/erc20/utils/SafeERC20.sol\":{\"keccak256\":\"0x909d608c2db6eb165ca178c81289a07ed2e118e444d0025b2a85c97d0b44a4fa\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://656cda26512ddd7373c2d5551c8fae759fc30f05b10f0fc2e738e9274199dbd4\",\"dweb:/ipfs/QmTSArSzQRFbQmHgq7U1PZXnsDFhvDZhKVu9CzMG4yo6Lx\"]},\"lib/openzeppelin-contracts/contracts/utils/Address.sol\":{\"keccak256\":\"0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2455248c8ddd9cc6a7af76a13973cddf222072427e7b0e2a7d1aff345145e931\",\"dweb:/ipfs/QmfYjnjRbWqYpuxurqveE6HtzsY1Xx323J428AKQgtBJZm\"]},\"lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol\":{\"keccak256\":\"0x58b21219689909c4f8339af00813760337f7e2e7f169a97fe49e2896dcfb3b9a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://ef8e012e946dec20e59f2d4446f4b44bb098f3fa8bac103b1b5112fff777447b\",\"dweb:/ipfs/QmVTooKWcLkJ9W68yNX4MgdrbAKiAXwuRN9A7f4NkdcdtQ\"]},\"src/WhackAMoleBotV1.sol\":{\"keccak256\":\"0x0eba973ac4a6a9f292beb8c0315d72a9491e28889a4dd4279e1f204d265d80c7\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://3f04c123d12ab9260b71cbf597e32730654c5b8faa026f062035245c1d861533\",\"dweb:/ipfs/QmRV6R2nc7ZUMQM6Cvudb7nSPJx85zXMyUAeDmzFLkMNcf\"]},\"src/protocols/uniswap/IUniswapV2Router.sol\":{\"keccak256\":\"0x2a86f62d96fd59a10d98e361056e3780cb1b94a758157b8f5d79ab865bb30a5c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d055709a7bdc57be7298bafd9a87c60071c06f68192aac1eeb3353615d03ee2b\",\"dweb:/ipfs/Qmdtojg2PJhdzQVtevYJDPG1ggTLmwJgHqcZ555eDC6xbq\"]},\"src/protocols/uniswap/IUniswapV3SwapRouter.sol\":{\"keccak256\":\"0x7ca2f2c38b3d4e2d9fc8fe55ab8ce482b448bb9618910a6ad4e18d8873b7389c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0f5d5e3aeb92e4e163a7dbb39dd66cbd9c9ca9714c48741530a14d57fbab5ed5\",\"dweb:/ipfs/QmaHyVhkBtoTm4VQHu4b3tXSpRDFmP6BDoqCseUbaKfVBT\"]}},\"version\":1}", "metadata": { "compiler": { "version": "0.8.20+commit.a1b79de6" }, "language": "Solidity", "output": { "abi": [ { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [], "type": "error", "name": "TradeFailed" }, { "inputs": [], "type": "error", "name": "Unauthorized" }, { "inputs": [ { "internalType": "address[]", "name": "tokens", "type": "address[]" }, { "internalType": "address[]", "name": "protocols", "type": "address[]" } ], "stateMutability": "payable", "type": "function", "name": "approveHandlers" }, { "inputs": [ { "internalType": "address[]", "name": "tokens", "type": "address[]" } ], "stateMutability": "payable", "type": "function", "name": "recoverTokens" }, { "inputs": [ { "internalType": "struct WhackAMoleBotV1.SwapParams[]", "name": "paramsArray", "type": "tuple[]", "components": [ { "internalType": "uint8", "name": "protocol", "type": "uint8" }, { "internalType": "address", "name": "handler", "type": "address" }, { "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ] }, { "internalType": "uint256", "name": "minAmountOut", "type": "uint256" } ], "stateMutability": "payable", "type": "function", "name": "whack", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ] } ], "devdoc": { "kind": "dev", "methods": {}, "version": 1 }, "userdoc": { "kind": "user", "methods": {}, "version": 1 } }, "settings": { "remappings": [ ":ds-test/=lib/forge-std/lib/ds-test/src/", ":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", ":forge-std/=lib/forge-std/src/", ":openzeppelin-contracts/=lib/openzeppelin-contracts/", ":openzeppelin/=lib/openzeppelin-contracts/contracts/" ], "optimizer": { "enabled": true, "runs": 1000 }, "metadata": { "bytecodeHash": "ipfs" }, "compilationTarget": { "src/WhackAMoleBotV1.sol": "WhackAMoleBotV1" }, "libraries": {} }, "sources": { "lib/openzeppelin-contracts/contracts/token/erc20/IERC20.sol": { "keccak256": "0x287b55befed2961a7eabd7d7b1b2839cbca8a5b80ef8dcbb25ed3d4c2002c305", "urls": [ "bzz-raw://bd39944e8fc06be6dbe2dd1d8449b5336e23c6a7ba3e8e9ae5ae0f37f35283f5", "dweb:/ipfs/QmPV3FGYjVwvKSgAXKUN3r9T9GwniZz83CxBpM7vyj2G53" ], "license": "MIT" }, "lib/openzeppelin-contracts/contracts/token/erc20/extensions/IERC20Permit.sol": { "keccak256": "0xec63854014a5b4f2b3290ab9103a21bdf902a508d0f41a8573fea49e98bf571a", "urls": [ "bzz-raw://bc5b5dc12fbc4002f282eaa7a5f06d8310ed62c1c77c5770f6283e058454c39a", "dweb:/ipfs/Qme9rE2wS3yBuyJq9GgbmzbsBQsW2M2sVFqYYLw7bosGrv" ], "license": "MIT" }, "lib/openzeppelin-contracts/contracts/token/erc20/utils/SafeERC20.sol": { "keccak256": "0x909d608c2db6eb165ca178c81289a07ed2e118e444d0025b2a85c97d0b44a4fa", "urls": [ "bzz-raw://656cda26512ddd7373c2d5551c8fae759fc30f05b10f0fc2e738e9274199dbd4", "dweb:/ipfs/QmTSArSzQRFbQmHgq7U1PZXnsDFhvDZhKVu9CzMG4yo6Lx" ], "license": "MIT" }, "lib/openzeppelin-contracts/contracts/utils/Address.sol": { "keccak256": "0x006dd67219697fe68d7fbfdea512e7c4cb64a43565ed86171d67e844982da6fa", "urls": [ "bzz-raw://2455248c8ddd9cc6a7af76a13973cddf222072427e7b0e2a7d1aff345145e931", "dweb:/ipfs/QmfYjnjRbWqYpuxurqveE6HtzsY1Xx323J428AKQgtBJZm" ], "license": "MIT" }, "lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol": { "keccak256": "0x58b21219689909c4f8339af00813760337f7e2e7f169a97fe49e2896dcfb3b9a", "urls": [ "bzz-raw://ef8e012e946dec20e59f2d4446f4b44bb098f3fa8bac103b1b5112fff777447b", "dweb:/ipfs/QmVTooKWcLkJ9W68yNX4MgdrbAKiAXwuRN9A7f4NkdcdtQ" ], "license": "MIT" }, "src/WhackAMoleBotV1.sol": { "keccak256": "0x0eba973ac4a6a9f292beb8c0315d72a9491e28889a4dd4279e1f204d265d80c7", "urls": [ "bzz-raw://3f04c123d12ab9260b71cbf597e32730654c5b8faa026f062035245c1d861533", "dweb:/ipfs/QmRV6R2nc7ZUMQM6Cvudb7nSPJx85zXMyUAeDmzFLkMNcf" ], "license": "MIT" }, "src/protocols/uniswap/IUniswapV2Router.sol": { "keccak256": "0x2a86f62d96fd59a10d98e361056e3780cb1b94a758157b8f5d79ab865bb30a5c", "urls": [ "bzz-raw://d055709a7bdc57be7298bafd9a87c60071c06f68192aac1eeb3353615d03ee2b", "dweb:/ipfs/Qmdtojg2PJhdzQVtevYJDPG1ggTLmwJgHqcZ555eDC6xbq" ], "license": "MIT" }, "src/protocols/uniswap/IUniswapV3SwapRouter.sol": { "keccak256": "0x7ca2f2c38b3d4e2d9fc8fe55ab8ce482b448bb9618910a6ad4e18d8873b7389c", "urls": [ "bzz-raw://0f5d5e3aeb92e4e163a7dbb39dd66cbd9c9ca9714c48741530a14d57fbab5ed5", "dweb:/ipfs/QmaHyVhkBtoTm4VQHu4b3tXSpRDFmP6BDoqCseUbaKfVBT" ], "license": "MIT" } }, "version": 1 }, "ast": { "absolutePath": "src/WhackAMoleBotV1.sol", "id": 31205, "exportedSymbols": { "Address": [ 30227 ], "IERC20": [ 29521 ], "IERC20Permit": [ 32363 ], "ISwapRouter": [ 31832 ], "IUniswapV2Router": [ 31783 ], "SafeERC20": [ 29897 ], "SafeMath": [ 30539 ], "WhackAMoleBotV1": [ 31204 ] }, "nodeType": "SourceUnit", "src": "32:4381:25", "nodes": [ { "id": 30824, "nodeType": "PragmaDirective", "src": "32:23:25", "nodes": [], "literals": [ "solidity", "^", "0.8", ".9" ] }, { "id": 30825, "nodeType": "ImportDirective", "src": "57:66:25", "nodes": [], "absolutePath": "lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol", "file": "openzeppelin-contracts/contracts/utils/math/SafeMath.sol", "nameLocation": "-1:-1:-1", "scope": 31205, "sourceUnit": 30540, "symbolAliases": [], "unitAlias": "" }, { "id": 30826, "nodeType": "ImportDirective", "src": "124:65:25", "nodes": [], "absolutePath": "lib/openzeppelin-contracts/contracts/token/erc20/IERC20.sol", "file": "openzeppelin-contracts/contracts/token/erc20/IERC20.sol", "nameLocation": "-1:-1:-1", "scope": 31205, "sourceUnit": 29522, "symbolAliases": [], "unitAlias": "" }, { "id": 30827, "nodeType": "ImportDirective", "src": "190:74:25", "nodes": [], "absolutePath": "lib/openzeppelin-contracts/contracts/token/erc20/utils/SafeERC20.sol", "file": "openzeppelin-contracts/contracts/token/erc20/utils/SafeERC20.sol", "nameLocation": "-1:-1:-1", "scope": 31205, "sourceUnit": 29898, "symbolAliases": [], "unitAlias": "" }, { "id": 30828, "nodeType": "ImportDirective", "src": "266:50:25", "nodes": [], "absolutePath": "src/protocols/uniswap/IUniswapV2Router.sol", "file": "./protocols/uniswap/IUniswapV2Router.sol", "nameLocation": "-1:-1:-1", "scope": 31205, "sourceUnit": 31784, "symbolAliases": [], "unitAlias": "" }, { "id": 30829, "nodeType": "ImportDirective", "src": "317:54:25", "nodes": [], "absolutePath": "src/protocols/uniswap/IUniswapV3SwapRouter.sol", "file": "./protocols/uniswap/IUniswapV3SwapRouter.sol", "nameLocation": "-1:-1:-1", "scope": 31205, "sourceUnit": 31833, "symbolAliases": [], "unitAlias": "" }, { "id": 31204, "nodeType": "ContractDefinition", "src": "433:3979:25", "nodes": [ { "id": 30833, "nodeType": "UsingForDirective", "src": "464:27:25", "nodes": [], "global": false, "libraryName": { "id": 30830, "name": "SafeERC20", "nameLocations": [ "470:9:25" ], "nodeType": "IdentifierPath", "referencedDeclaration": 29897, "src": "470:9:25" }, "typeName": { "id": 30832, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 30831, "name": "IERC20", "nameLocations": [ "484:6:25" ], "nodeType": "IdentifierPath", "referencedDeclaration": 29521, "src": "484:6:25" }, "referencedDeclaration": 29521, "src": "484:6:25", "typeDescriptions": { "typeIdentifier": "t_contract$_IERC20_$29521", "typeString": "contract IERC20" } } }, { "id": 30835, "nodeType": "VariableDeclaration", "src": "497:32:25", "nodes": [], "constant": false, "mutability": "immutable", "name": "owner", "nameLocation": "524:5:25", "scope": 31204, "stateVariable": true, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 30834, "name": "address", "nodeType": "ElementaryTypeName", "src": "497:7:25", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "visibility": "internal" }, { "id": 30848, "nodeType": "StructDefinition", "src": "536:169:25", "nodes": [], "canonicalName": "WhackAMoleBotV1.SwapParams", "members": [ { "constant": false, "id": 30837, "mutability": "mutable", "name": "protocol", "nameLocation": "570:8:25", "nodeType": "VariableDeclaration", "scope": 30848, "src": "564:14:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint8", "typeString": "uint8" }, "typeName": { "id": 30836, "name": "uint8", "nodeType": "ElementaryTypeName", "src": "564:5:25", "typeDescriptions": { "typeIdentifier": "t_uint8", "typeString": "uint8" } }, "visibility": "internal" }, { "constant": false, "id": 30839, "mutability": "mutable", "name": "handler", "nameLocation": "596:7:25", "nodeType": "VariableDeclaration", "scope": 30848, "src": "588:15:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 30838, "name": "address", "nodeType": "ElementaryTypeName", "src": "588:7:25", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "visibility": "internal" }, { "constant": false, "id": 30841, "mutability": "mutable", "name": "tokenIn", "nameLocation": "621:7:25", "nodeType": "VariableDeclaration", "scope": 30848, "src": "613:15:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 30840, "name": "address", "nodeType": "ElementaryTypeName", "src": "613:7:25", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "visibility": "internal" }, { "constant": false, "id": 30843, "mutability": "mutable", "name": "tokenOut", "nameLocation": "646:8:25", "nodeType": "VariableDeclaration", "scope": 30848, "src": "638:16:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 30842, "name": "address", "nodeType": "ElementaryTypeName", "src": "638:7:25", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "visibility": "internal" }, { "constant": false, "id": 30845, "mutability": "mutable", "name": "fee", "nameLocation": "671:3:25", "nodeType": "VariableDeclaration", "scope": 30848, "src": "664:10:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint24", "typeString": "uint24" }, "typeName": { "id": 30844, "name": "uint24", "nodeType": "ElementaryTypeName", "src": "664:6:25", "typeDescriptions": { "typeIdentifier": "t_uint24", "typeString": "uint24" } }, "visibility": "internal" }, { "constant": false, "id": 30847, "mutability": "mutable", "name": "amount", "nameLocation": "692:6:25", "nodeType": "VariableDeclaration", "scope": 30848, "src": "684:14:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30846, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "684:7:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "name": "SwapParams", "nameLocation": "543:10:25", "scope": 31204, "visibility": "public" }, { "id": 30850, "nodeType": "ErrorDefinition", "src": "711:21:25", "nodes": [], "errorSelector": "82b42900", "name": "Unauthorized", "nameLocation": "717:12:25", "parameters": { "id": 30849, "nodeType": "ParameterList", "parameters": [], "src": "729:2:25" } }, { "id": 30852, "nodeType": "ErrorDefinition", "src": "737:20:25", "nodes": [], "errorSelector": "2d8ef0cf", "name": "TradeFailed", "nameLocation": "743:11:25", "parameters": { "id": 30851, "nodeType": "ParameterList", "parameters": [], "src": "754:2:25" } }, { "id": 30861, "nodeType": "FunctionDefinition", "src": "763:49:25", "nodes": [], "body": { "id": 30860, "nodeType": "Block", "src": "777:35:25", "nodes": [], "statements": [ { "expression": { "id": 30858, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 30855, "name": "owner", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30835, "src": "787:5:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "expression": { "id": 30856, "name": "msg", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": -15, "src": "795:3:25", "typeDescriptions": { "typeIdentifier": "t_magic_message", "typeString": "msg" } }, "id": 30857, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "799:6:25", "memberName": "sender", "nodeType": "MemberAccess", "src": "795:10:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "src": "787:18:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "id": 30859, "nodeType": "ExpressionStatement", "src": "787:18:25" } ] }, "implemented": true, "kind": "constructor", "modifiers": [], "name": "", "nameLocation": "-1:-1:-1", "parameters": { "id": 30853, "nodeType": "ParameterList", "parameters": [], "src": "774:2:25" }, "returnParameters": { "id": 30854, "nodeType": "ParameterList", "parameters": [], "src": "777:0:25" }, "scope": 31204, "stateMutability": "nonpayable", "virtual": false, "visibility": "public" }, { "id": 30868, "nodeType": "ModifierDefinition", "src": "818:62:25", "nodes": [], "body": { "id": 30867, "nodeType": "Block", "src": "839:41:25", "nodes": [], "statements": [ { "expression": { "arguments": [], "expression": { "argumentTypes": [], "id": 30863, "name": "_checkOwner", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30881, "src": "849:11:25", "typeDescriptions": { "typeIdentifier": "t_function_internal_view$__$returns$__$", "typeString": "function () view" } }, "id": 30864, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "849:13:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_tuple$__$", "typeString": "tuple()" } }, "id": 30865, "nodeType": "ExpressionStatement", "src": "849:13:25" }, { "id": 30866, "nodeType": "PlaceholderStatement", "src": "872:1:25" } ] }, "name": "onlyOwner", "nameLocation": "827:9:25", "parameters": { "id": 30862, "nodeType": "ParameterList", "parameters": [], "src": "836:2:25" }, "virtual": false, "visibility": "internal" }, { "id": 30881, "nodeType": "FunctionDefinition", "src": "886:132:25", "nodes": [], "body": { "id": 30880, "nodeType": "Block", "src": "931:87:25", "nodes": [], "statements": [ { "condition": { "commonType": { "typeIdentifier": "t_address", "typeString": "address" }, "id": 30874, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "expression": { "id": 30871, "name": "msg", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": -15, "src": "945:3:25", "typeDescriptions": { "typeIdentifier": "t_magic_message", "typeString": "msg" } }, "id": 30872, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "949:6:25", "memberName": "sender", "nodeType": "MemberAccess", "src": "945:10:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "BinaryOperation", "operator": "!=", "rightExpression": { "id": 30873, "name": "owner", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30835, "src": "959:5:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "src": "945:19:25", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 30879, "nodeType": "IfStatement", "src": "941:71:25", "trueBody": { "id": 30878, "nodeType": "Block", "src": "966:46:25", "statements": [ { "errorCall": { "arguments": [], "expression": { "argumentTypes": [], "id": 30875, "name": "Unauthorized", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30850, "src": "987:12:25", "typeDescriptions": { "typeIdentifier": "t_function_error_pure$__$returns$__$", "typeString": "function () pure" } }, "id": 30876, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "987:14:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_tuple$__$", "typeString": "tuple()" } }, "id": 30877, "nodeType": "RevertStatement", "src": "980:21:25" } ] } } ] }, "implemented": true, "kind": "function", "modifiers": [], "name": "_checkOwner", "nameLocation": "895:11:25", "parameters": { "id": 30869, "nodeType": "ParameterList", "parameters": [], "src": "906:2:25" }, "returnParameters": { "id": 30870, "nodeType": "ParameterList", "parameters": [], "src": "931:0:25" }, "scope": 31204, "stateMutability": "view", "virtual": true, "visibility": "internal" }, { "id": 30930, "nodeType": "FunctionDefinition", "src": "1024:404:25", "nodes": [], "body": { "id": 30929, "nodeType": "Block", "src": "1099:329:25", "nodes": [], "statements": [ { "assignments": [ 30890 ], "declarations": [ { "constant": false, "id": 30890, "mutability": "mutable", "name": "length", "nameLocation": "1114:6:25", "nodeType": "VariableDeclaration", "scope": 30929, "src": "1109:11:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30889, "name": "uint", "nodeType": "ElementaryTypeName", "src": "1109:4:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30893, "initialValue": { "expression": { "id": 30891, "name": "tokens", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30884, "src": "1123:6:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", "typeString": "address[] calldata" } }, "id": 30892, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "1130:6:25", "memberName": "length", "nodeType": "MemberAccess", "src": "1123:13:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "VariableDeclarationStatement", "src": "1109:27:25" }, { "body": { "id": 30927, "nodeType": "Block", "src": "1174:248:25", "statements": [ { "assignments": [ 30901 ], "declarations": [ { "constant": false, "id": 30901, "mutability": "mutable", "name": "token", "nameLocation": "1196:5:25", "nodeType": "VariableDeclaration", "scope": 30927, "src": "1188:13:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 30900, "name": "address", "nodeType": "ElementaryTypeName", "src": "1188:7:25", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "visibility": "internal" } ], "id": 30905, "initialValue": { "baseExpression": { "id": 30902, "name": "tokens", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30884, "src": "1204:6:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", "typeString": "address[] calldata" } }, "id": 30904, "indexExpression": { "id": 30903, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30895, "src": "1211:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "IndexAccess", "src": "1204:9:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "VariableDeclarationStatement", "src": "1188:25:25" }, { "expression": { "arguments": [ { "expression": { "id": 30910, "name": "msg", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": -15, "src": "1271:3:25", "typeDescriptions": { "typeIdentifier": "t_magic_message", "typeString": "msg" } }, "id": 30911, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "1275:6:25", "memberName": "sender", "nodeType": "MemberAccess", "src": "1271:10:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, { "arguments": [ { "arguments": [ { "id": 30918, "name": "this", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": -28, "src": "1331:4:25", "typeDescriptions": { "typeIdentifier": "t_contract$_WhackAMoleBotV1_$31204", "typeString": "contract WhackAMoleBotV1" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_contract$_WhackAMoleBotV1_$31204", "typeString": "contract WhackAMoleBotV1" } ], "id": 30917, "isConstant": false, "isLValue": false, "isPure": true, "lValueRequested": false, "nodeType": "ElementaryTypeNameExpression", "src": "1323:7:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_address_$", "typeString": "type(address)" }, "typeName": { "id": 30916, "name": "address", "nodeType": "ElementaryTypeName", "src": "1323:7:25", "typeDescriptions": {} } }, "id": 30919, "isConstant": false, "isLValue": false, "isPure": false, "kind": "typeConversion", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1323:13:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" } ], "expression": { "arguments": [ { "id": 30913, "name": "token", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30901, "src": "1306:5:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" } ], "id": 30912, "name": "IERC20", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 29521, "src": "1299:6:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_IERC20_$29521_$", "typeString": "type(contract IERC20)" } }, "id": 30914, "isConstant": false, "isLValue": false, "isPure": false, "kind": "typeConversion", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1299:13:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_contract$_IERC20_$29521", "typeString": "contract IERC20" } }, "id": 30915, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "1313:9:25", "memberName": "balanceOf", "nodeType": "MemberAccess", "referencedDeclaration": 29478, "src": "1299:23:25", "typeDescriptions": { "typeIdentifier": "t_function_external_view$_t_address_$returns$_t_uint256_$", "typeString": "function (address) view external returns (uint256)" } }, "id": 30920, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1299:38:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" }, { "typeIdentifier": "t_uint256", "typeString": "uint256" } ], "expression": { "arguments": [ { "id": 30907, "name": "token", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30901, "src": "1234:5:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" } ], "id": 30906, "name": "IERC20", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 29521, "src": "1227:6:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_IERC20_$29521_$", "typeString": "type(contract IERC20)" } }, "id": 30908, "isConstant": false, "isLValue": false, "isPure": false, "kind": "typeConversion", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1227:13:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_contract$_IERC20_$29521", "typeString": "contract IERC20" } }, "id": 30909, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "1241:12:25", "memberName": "safeTransfer", "nodeType": "MemberAccess", "referencedDeclaration": 29554, "src": "1227:26:25", "typeDescriptions": { "typeIdentifier": "t_function_internal_nonpayable$_t_contract$_IERC20_$29521_$_t_address_$_t_uint256_$returns$__$attached_to$_t_contract$_IERC20_$29521_$", "typeString": "function (contract IERC20,address,uint256)" } }, "id": 30921, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1227:124:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_tuple$__$", "typeString": "tuple()" } }, "id": 30922, "nodeType": "ExpressionStatement", "src": "1227:124:25" }, { "id": 30926, "nodeType": "UncheckedBlock", "src": "1366:46:25", "statements": [ { "expression": { "id": 30924, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "UnaryOperation", "operator": "++", "prefix": true, "src": "1394:3:25", "subExpression": { "id": 30923, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30895, "src": "1396:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30925, "nodeType": "ExpressionStatement", "src": "1394:3:25" } ] } ] }, "condition": { "commonType": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "id": 30899, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 30897, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30895, "src": "1160:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "BinaryOperation", "operator": "<", "rightExpression": { "id": 30898, "name": "length", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30890, "src": "1164:6:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "1160:10:25", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 30928, "initializationExpression": { "assignments": [ 30895 ], "declarations": [ { "constant": false, "id": 30895, "mutability": "mutable", "name": "i", "nameLocation": "1157:1:25", "nodeType": "VariableDeclaration", "scope": 30928, "src": "1152:6:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30894, "name": "uint", "nodeType": "ElementaryTypeName", "src": "1152:4:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30896, "nodeType": "VariableDeclarationStatement", "src": "1152:6:25" }, "nodeType": "ForStatement", "src": "1147:275:25" } ] }, "functionSelector": "9c832f93", "implemented": true, "kind": "function", "modifiers": [ { "id": 30887, "kind": "modifierInvocation", "modifierName": { "id": 30886, "name": "onlyOwner", "nameLocations": [ "1089:9:25" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30868, "src": "1089:9:25" }, "nodeType": "ModifierInvocation", "src": "1089:9:25" } ], "name": "recoverTokens", "nameLocation": "1033:13:25", "parameters": { "id": 30885, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 30884, "mutability": "mutable", "name": "tokens", "nameLocation": "1066:6:25", "nodeType": "VariableDeclaration", "scope": 30930, "src": "1047:25:25", "stateVariable": false, "storageLocation": "calldata", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", "typeString": "address[]" }, "typeName": { "baseType": { "id": 30882, "name": "address", "nodeType": "ElementaryTypeName", "src": "1047:7:25", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "id": 30883, "nodeType": "ArrayTypeName", "src": "1047:9:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_storage_ptr", "typeString": "address[]" } }, "visibility": "internal" } ], "src": "1046:27:25" }, "returnParameters": { "id": 30888, "nodeType": "ParameterList", "parameters": [], "src": "1099:0:25" }, "scope": 31204, "stateMutability": "payable", "virtual": false, "visibility": "public" }, { "id": 31004, "nodeType": "FunctionDefinition", "src": "1476:779:25", "nodes": [], "body": { "id": 31003, "nodeType": "Block", "src": "1595:660:25", "nodes": [], "statements": [ { "assignments": [ 30940 ], "declarations": [ { "constant": false, "id": 30940, "mutability": "mutable", "name": "maxInt", "nameLocation": "1731:6:25", "nodeType": "VariableDeclaration", "scope": 31003, "src": "1726:11:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30939, "name": "uint", "nodeType": "ElementaryTypeName", "src": "1726:4:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30946, "initialValue": { "expression": { "arguments": [ { "id": 30943, "isConstant": false, "isLValue": false, "isPure": true, "lValueRequested": false, "nodeType": "ElementaryTypeNameExpression", "src": "1745:7:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_uint256_$", "typeString": "type(uint256)" }, "typeName": { "id": 30942, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "1745:7:25", "typeDescriptions": {} } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_type$_t_uint256_$", "typeString": "type(uint256)" } ], "id": 30941, "name": "type", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": -27, "src": "1740:4:25", "typeDescriptions": { "typeIdentifier": "t_function_metatype_pure$__$returns$__$", "typeString": "function () pure" } }, "id": 30944, "isConstant": false, "isLValue": false, "isPure": true, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1740:13:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_magic_meta_type_t_uint256", "typeString": "type(uint256)" } }, "id": 30945, "isConstant": false, "isLValue": false, "isPure": true, "lValueRequested": false, "memberLocation": "1754:3:25", "memberName": "max", "nodeType": "MemberAccess", "src": "1740:17:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "VariableDeclarationStatement", "src": "1726:31:25" }, { "assignments": [ 30948 ], "declarations": [ { "constant": false, "id": 30948, "mutability": "mutable", "name": "tokensLength", "nameLocation": "1773:12:25", "nodeType": "VariableDeclaration", "scope": 31003, "src": "1768:17:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30947, "name": "uint", "nodeType": "ElementaryTypeName", "src": "1768:4:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30951, "initialValue": { "expression": { "id": 30949, "name": "tokens", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30933, "src": "1788:6:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", "typeString": "address[] calldata" } }, "id": 30950, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "1795:6:25", "memberName": "length", "nodeType": "MemberAccess", "src": "1788:13:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "VariableDeclarationStatement", "src": "1768:33:25" }, { "assignments": [ 30953 ], "declarations": [ { "constant": false, "id": 30953, "mutability": "mutable", "name": "protocolsLength", "nameLocation": "1816:15:25", "nodeType": "VariableDeclaration", "scope": 31003, "src": "1811:20:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30952, "name": "uint", "nodeType": "ElementaryTypeName", "src": "1811:4:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30956, "initialValue": { "expression": { "id": 30954, "name": "protocols", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30936, "src": "1834:9:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", "typeString": "address[] calldata" } }, "id": 30955, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "1844:6:25", "memberName": "length", "nodeType": "MemberAccess", "src": "1834:16:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "VariableDeclarationStatement", "src": "1811:39:25" }, { "body": { "id": 31001, "nodeType": "Block", "src": "1894:355:25", "statements": [ { "assignments": [ 30965 ], "declarations": [ { "constant": false, "id": 30965, "mutability": "mutable", "name": "token", "nameLocation": "1915:5:25", "nodeType": "VariableDeclaration", "scope": 31001, "src": "1908:12:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_contract$_IERC20_$29521", "typeString": "contract IERC20" }, "typeName": { "id": 30964, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 30963, "name": "IERC20", "nameLocations": [ "1908:6:25" ], "nodeType": "IdentifierPath", "referencedDeclaration": 29521, "src": "1908:6:25" }, "referencedDeclaration": 29521, "src": "1908:6:25", "typeDescriptions": { "typeIdentifier": "t_contract$_IERC20_$29521", "typeString": "contract IERC20" } }, "visibility": "internal" } ], "id": 30971, "initialValue": { "arguments": [ { "baseExpression": { "id": 30967, "name": "tokens", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30933, "src": "1930:6:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", "typeString": "address[] calldata" } }, "id": 30969, "indexExpression": { "id": 30968, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30958, "src": "1937:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "IndexAccess", "src": "1930:9:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" } ], "id": 30966, "name": "IERC20", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 29521, "src": "1923:6:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_IERC20_$29521_$", "typeString": "type(contract IERC20)" } }, "id": 30970, "isConstant": false, "isLValue": false, "isPure": false, "kind": "typeConversion", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1923:17:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_contract$_IERC20_$29521", "typeString": "contract IERC20" } }, "nodeType": "VariableDeclarationStatement", "src": "1908:32:25" }, { "body": { "id": 30995, "nodeType": "Block", "src": "1990:189:25", "statements": [ { "assignments": [ 30979 ], "declarations": [ { "constant": false, "id": 30979, "mutability": "mutable", "name": "protocol", "nameLocation": "2016:8:25", "nodeType": "VariableDeclaration", "scope": 30995, "src": "2008:16:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 30978, "name": "address", "nodeType": "ElementaryTypeName", "src": "2008:7:25", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "visibility": "internal" } ], "id": 30983, "initialValue": { "baseExpression": { "id": 30980, "name": "protocols", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30936, "src": "2027:9:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", "typeString": "address[] calldata" } }, "id": 30982, "indexExpression": { "id": 30981, "name": "j", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30973, "src": "2037:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "IndexAccess", "src": "2027:12:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "VariableDeclarationStatement", "src": "2008:31:25" }, { "expression": { "arguments": [ { "id": 30987, "name": "protocol", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30979, "src": "2075:8:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, { "id": 30988, "name": "maxInt", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30940, "src": "2085:6:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" }, { "typeIdentifier": "t_uint256", "typeString": "uint256" } ], "expression": { "id": 30984, "name": "token", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30965, "src": "2057:5:25", "typeDescriptions": { "typeIdentifier": "t_contract$_IERC20_$29521", "typeString": "contract IERC20" } }, "id": 30986, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "2063:11:25", "memberName": "safeApprove", "nodeType": "MemberAccess", "referencedDeclaration": 29625, "src": "2057:17:25", "typeDescriptions": { "typeIdentifier": "t_function_internal_nonpayable$_t_contract$_IERC20_$29521_$_t_address_$_t_uint256_$returns$__$attached_to$_t_contract$_IERC20_$29521_$", "typeString": "function (contract IERC20,address,uint256)" } }, "id": 30989, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "2057:35:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_tuple$__$", "typeString": "tuple()" } }, "id": 30990, "nodeType": "ExpressionStatement", "src": "2057:35:25" }, { "id": 30994, "nodeType": "UncheckedBlock", "src": "2111:54:25", "statements": [ { "expression": { "id": 30992, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "UnaryOperation", "operator": "++", "prefix": true, "src": "2143:3:25", "subExpression": { "id": 30991, "name": "j", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30973, "src": "2145:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30993, "nodeType": "ExpressionStatement", "src": "2143:3:25" } ] } ] }, "condition": { "commonType": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "id": 30977, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 30975, "name": "j", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30973, "src": "1967:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "BinaryOperation", "operator": "<", "rightExpression": { "id": 30976, "name": "protocolsLength", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30953, "src": "1971:15:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "1967:19:25", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 30996, "initializationExpression": { "assignments": [ 30973 ], "declarations": [ { "constant": false, "id": 30973, "mutability": "mutable", "name": "j", "nameLocation": "1964:1:25", "nodeType": "VariableDeclaration", "scope": 30996, "src": "1959:6:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30972, "name": "uint", "nodeType": "ElementaryTypeName", "src": "1959:4:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30974, "nodeType": "VariableDeclarationStatement", "src": "1959:6:25" }, "nodeType": "ForStatement", "src": "1954:225:25" }, { "id": 31000, "nodeType": "UncheckedBlock", "src": "2193:46:25", "statements": [ { "expression": { "id": 30998, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "UnaryOperation", "operator": "++", "prefix": true, "src": "2221:3:25", "subExpression": { "id": 30997, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30958, "src": "2223:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30999, "nodeType": "ExpressionStatement", "src": "2221:3:25" } ] } ] }, "condition": { "commonType": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "id": 30962, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 30960, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30958, "src": "1874:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "BinaryOperation", "operator": "<", "rightExpression": { "id": 30961, "name": "tokensLength", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30948, "src": "1878:12:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "1874:16:25", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 31002, "initializationExpression": { "assignments": [ 30958 ], "declarations": [ { "constant": false, "id": 30958, "mutability": "mutable", "name": "i", "nameLocation": "1871:1:25", "nodeType": "VariableDeclaration", "scope": 31002, "src": "1866:6:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30957, "name": "uint", "nodeType": "ElementaryTypeName", "src": "1866:4:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30959, "nodeType": "VariableDeclarationStatement", "src": "1866:6:25" }, "nodeType": "ForStatement", "src": "1861:388:25" } ] }, "functionSelector": "bc2078fb", "implemented": true, "kind": "function", "modifiers": [], "name": "approveHandlers", "nameLocation": "1485:15:25", "parameters": { "id": 30937, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 30933, "mutability": "mutable", "name": "tokens", "nameLocation": "1529:6:25", "nodeType": "VariableDeclaration", "scope": 31004, "src": "1510:25:25", "stateVariable": false, "storageLocation": "calldata", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", "typeString": "address[]" }, "typeName": { "baseType": { "id": 30931, "name": "address", "nodeType": "ElementaryTypeName", "src": "1510:7:25", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "id": 30932, "nodeType": "ArrayTypeName", "src": "1510:9:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_storage_ptr", "typeString": "address[]" } }, "visibility": "internal" }, { "constant": false, "id": 30936, "mutability": "mutable", "name": "protocols", "nameLocation": "1564:9:25", "nodeType": "VariableDeclaration", "scope": 31004, "src": "1545:28:25", "stateVariable": false, "storageLocation": "calldata", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_calldata_ptr", "typeString": "address[]" }, "typeName": { "baseType": { "id": 30934, "name": "address", "nodeType": "ElementaryTypeName", "src": "1545:7:25", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "id": 30935, "nodeType": "ArrayTypeName", "src": "1545:9:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_storage_ptr", "typeString": "address[]" } }, "visibility": "internal" } ], "src": "1500:79:25" }, "returnParameters": { "id": 30938, "nodeType": "ParameterList", "parameters": [], "src": "1595:0:25" }, "scope": 31204, "stateMutability": "payable", "virtual": false, "visibility": "public" }, { "id": 31096, "nodeType": "FunctionDefinition", "src": "2323:876:25", "nodes": [], "body": { "id": 31095, "nodeType": "Block", "src": "2460:739:25", "nodes": [], "statements": [ { "assignments": [ 31018 ], "declarations": [ { "constant": false, "id": 31018, "mutability": "mutable", "name": "amountOut", "nameLocation": "2478:9:25", "nodeType": "VariableDeclaration", "scope": 31095, "src": "2470:17:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 31017, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "2470:7:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 31019, "nodeType": "VariableDeclarationStatement", "src": "2470:17:25" }, { "assignments": [ 31021 ], "declarations": [ { "constant": false, "id": 31021, "mutability": "mutable", "name": "paramsArrayLength", "nameLocation": "2505:17:25", "nodeType": "VariableDeclaration", "scope": 31095, "src": "2497:25:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 31020, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "2497:7:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 31024, "initialValue": { "expression": { "id": 31022, "name": "paramsArray", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31008, "src": "2525:11:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_struct$_SwapParams_$30848_calldata_ptr_$dyn_calldata_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams calldata[] calldata" } }, "id": 31023, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "2537:6:25", "memberName": "length", "nodeType": "MemberAccess", "src": "2525:18:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "VariableDeclarationStatement", "src": "2497:46:25" }, { "body": { "id": 31083, "nodeType": "Block", "src": "2595:486:25", "statements": [ { "assignments": [ 31033 ], "declarations": [ { "constant": false, "id": 31033, "mutability": "mutable", "name": "params", "nameLocation": "2627:6:25", "nodeType": "VariableDeclaration", "scope": 31083, "src": "2609:24:25", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams" }, "typeName": { "id": 31032, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 31031, "name": "SwapParams", "nameLocations": [ "2609:10:25" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30848, "src": "2609:10:25" }, "referencedDeclaration": 30848, "src": "2609:10:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_storage_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams" } }, "visibility": "internal" } ], "id": 31037, "initialValue": { "baseExpression": { "id": 31034, "name": "paramsArray", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31008, "src": "2636:11:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_struct$_SwapParams_$30848_calldata_ptr_$dyn_calldata_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams calldata[] calldata" } }, "id": 31036, "indexExpression": { "id": 31035, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31026, "src": "2648:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "IndexAccess", "src": "2636:14:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_calldata_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams calldata" } }, "nodeType": "VariableDeclarationStatement", "src": "2609:41:25" }, { "condition": { "commonType": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "id": 31040, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 31038, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31018, "src": "2669:9:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "BinaryOperation", "operator": "==", "rightExpression": { "hexValue": "30", "id": 31039, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "2682:1:25", "typeDescriptions": { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, "value": "0" }, "src": "2669:14:25", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "falseBody": { "id": 31053, "nodeType": "Block", "src": "2749:58:25", "statements": [ { "expression": { "id": 31051, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "expression": { "id": 31047, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31033, "src": "2767:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31049, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": true, "memberLocation": "2774:6:25", "memberName": "amount", "nodeType": "MemberAccess", "referencedDeclaration": 30847, "src": "2767:13:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "id": 31050, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31018, "src": "2783:9:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "2767:25:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 31052, "nodeType": "ExpressionStatement", "src": "2767:25:25" } ] }, "id": 31054, "nodeType": "IfStatement", "src": "2665:142:25", "trueBody": { "id": 31046, "nodeType": "Block", "src": "2685:58:25", "statements": [ { "expression": { "id": 31044, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 31041, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31018, "src": "2703:9:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "expression": { "id": 31042, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31033, "src": "2715:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31043, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2722:6:25", "memberName": "amount", "nodeType": "MemberAccess", "referencedDeclaration": 30847, "src": "2715:13:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "2703:25:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 31045, "nodeType": "ExpressionStatement", "src": "2703:25:25" } ] } }, { "condition": { "commonType": { "typeIdentifier": "t_uint8", "typeString": "uint8" }, "id": 31058, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "expression": { "id": 31055, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31033, "src": "2825:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31056, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2832:8:25", "memberName": "protocol", "nodeType": "MemberAccess", "referencedDeclaration": 30837, "src": "2825:15:25", "typeDescriptions": { "typeIdentifier": "t_uint8", "typeString": "uint8" } }, "nodeType": "BinaryOperation", "operator": "==", "rightExpression": { "hexValue": "30", "id": 31057, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "2844:1:25", "typeDescriptions": { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, "value": "0" }, "src": "2825:20:25", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "falseBody": { "condition": { "commonType": { "typeIdentifier": "t_uint8", "typeString": "uint8" }, "id": 31069, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "expression": { "id": 31066, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31033, "src": "2923:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31067, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2930:8:25", "memberName": "protocol", "nodeType": "MemberAccess", "referencedDeclaration": 30837, "src": "2923:15:25", "typeDescriptions": { "typeIdentifier": "t_uint8", "typeString": "uint8" } }, "nodeType": "BinaryOperation", "operator": "==", "rightExpression": { "hexValue": "31", "id": 31068, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "2942:1:25", "typeDescriptions": { "typeIdentifier": "t_rational_1_by_1", "typeString": "int_const 1" }, "value": "1" }, "src": "2923:20:25", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 31077, "nodeType": "IfStatement", "src": "2919:92:25", "trueBody": { "id": 31076, "nodeType": "Block", "src": "2945:66:25", "statements": [ { "expression": { "id": 31074, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 31070, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31018, "src": "2963:9:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "arguments": [ { "id": 31072, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31033, "src": "2989:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } ], "id": 31071, "name": "uniswapV3Swap", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31203, "src": "2975:13:25", "typeDescriptions": { "typeIdentifier": "t_function_internal_nonpayable$_t_struct$_SwapParams_$30848_memory_ptr_$returns$_t_uint256_$", "typeString": "function (struct WhackAMoleBotV1.SwapParams memory) returns (uint256)" } }, "id": 31073, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "2975:21:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "2963:33:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 31075, "nodeType": "ExpressionStatement", "src": "2963:33:25" } ] } }, "id": 31078, "nodeType": "IfStatement", "src": "2821:190:25", "trueBody": { "id": 31065, "nodeType": "Block", "src": "2847:66:25", "statements": [ { "expression": { "id": 31063, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 31059, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31018, "src": "2865:9:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "arguments": [ { "id": 31061, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31033, "src": "2891:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } ], "id": 31060, "name": "uniswapV2Swap", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31159, "src": "2877:13:25", "typeDescriptions": { "typeIdentifier": "t_function_internal_nonpayable$_t_struct$_SwapParams_$30848_memory_ptr_$returns$_t_uint256_$", "typeString": "function (struct WhackAMoleBotV1.SwapParams memory) returns (uint256)" } }, "id": 31062, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "2877:21:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "2865:33:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 31064, "nodeType": "ExpressionStatement", "src": "2865:33:25" } ] } }, { "id": 31082, "nodeType": "UncheckedBlock", "src": "3025:46:25", "statements": [ { "expression": { "id": 31080, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "UnaryOperation", "operator": "++", "prefix": true, "src": "3053:3:25", "subExpression": { "id": 31079, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31026, "src": "3055:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 31081, "nodeType": "ExpressionStatement", "src": "3053:3:25" } ] } ] }, "condition": { "commonType": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "id": 31030, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 31028, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31026, "src": "2570:1:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "BinaryOperation", "operator": "<", "rightExpression": { "id": 31029, "name": "paramsArrayLength", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31021, "src": "2574:17:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "2570:21:25", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 31084, "initializationExpression": { "assignments": [ 31026 ], "declarations": [ { "constant": false, "id": 31026, "mutability": "mutable", "name": "i", "nameLocation": "2567:1:25", "nodeType": "VariableDeclaration", "scope": 31084, "src": "2559:9:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 31025, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "2559:7:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 31027, "nodeType": "VariableDeclarationStatement", "src": "2559:9:25" }, "nodeType": "ForStatement", "src": "2554:527:25" }, { "condition": { "commonType": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "id": 31087, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 31085, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31018, "src": "3095:9:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "BinaryOperation", "operator": "<", "rightExpression": { "id": 31086, "name": "minAmountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31010, "src": "3107:12:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "3095:24:25", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 31092, "nodeType": "IfStatement", "src": "3091:75:25", "trueBody": { "id": 31091, "nodeType": "Block", "src": "3121:45:25", "statements": [ { "errorCall": { "arguments": [], "expression": { "argumentTypes": [], "id": 31088, "name": "TradeFailed", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30852, "src": "3142:11:25", "typeDescriptions": { "typeIdentifier": "t_function_error_pure$__$returns$__$", "typeString": "function () pure" } }, "id": 31089, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "3142:13:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_tuple$__$", "typeString": "tuple()" } }, "id": 31090, "nodeType": "RevertStatement", "src": "3135:20:25" } ] } }, { "expression": { "id": 31093, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31018, "src": "3183:9:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "functionReturnParameters": 31016, "id": 31094, "nodeType": "Return", "src": "3176:16:25" } ] }, "functionSelector": "c0f757c0", "implemented": true, "kind": "function", "modifiers": [ { "id": 31013, "kind": "modifierInvocation", "modifierName": { "id": 31012, "name": "onlyOwner", "nameLocations": [ "2432:9:25" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30868, "src": "2432:9:25" }, "nodeType": "ModifierInvocation", "src": "2432:9:25" } ], "name": "whack", "nameLocation": "2332:5:25", "parameters": { "id": 31011, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 31008, "mutability": "mutable", "name": "paramsArray", "nameLocation": "2369:11:25", "nodeType": "VariableDeclaration", "scope": 31096, "src": "2347:33:25", "stateVariable": false, "storageLocation": "calldata", "typeDescriptions": { "typeIdentifier": "t_array$_t_struct$_SwapParams_$30848_calldata_ptr_$dyn_calldata_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams[]" }, "typeName": { "baseType": { "id": 31006, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 31005, "name": "SwapParams", "nameLocations": [ "2347:10:25" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30848, "src": "2347:10:25" }, "referencedDeclaration": 30848, "src": "2347:10:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_storage_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams" } }, "id": 31007, "nodeType": "ArrayTypeName", "src": "2347:12:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_struct$_SwapParams_$30848_storage_$dyn_storage_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams[]" } }, "visibility": "internal" }, { "constant": false, "id": 31010, "mutability": "mutable", "name": "minAmountOut", "nameLocation": "2398:12:25", "nodeType": "VariableDeclaration", "scope": 31096, "src": "2390:20:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 31009, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "2390:7:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "src": "2337:79:25" }, "returnParameters": { "id": 31016, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 31015, "mutability": "mutable", "name": "", "nameLocation": "-1:-1:-1", "nodeType": "VariableDeclaration", "scope": 31096, "src": "2451:7:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 31014, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "2451:7:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "src": "2450:9:25" }, "scope": 31204, "stateMutability": "payable", "virtual": false, "visibility": "public" }, { "id": 31159, "nodeType": "FunctionDefinition", "src": "3225:523:25", "nodes": [], "body": { "id": 31158, "nodeType": "Block", "src": "3325:423:25", "nodes": [], "statements": [ { "assignments": [ 31108 ], "declarations": [ { "constant": false, "id": 31108, "mutability": "mutable", "name": "path", "nameLocation": "3352:4:25", "nodeType": "VariableDeclaration", "scope": 31158, "src": "3335:21:25", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_memory_ptr", "typeString": "address[]" }, "typeName": { "baseType": { "id": 31106, "name": "address", "nodeType": "ElementaryTypeName", "src": "3335:7:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "id": 31107, "nodeType": "ArrayTypeName", "src": "3335:9:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_storage_ptr", "typeString": "address[]" } }, "visibility": "internal" } ], "id": 31109, "nodeType": "VariableDeclarationStatement", "src": "3335:21:25" }, { "expression": { "id": 31116, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 31110, "name": "path", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31108, "src": "3366:4:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_memory_ptr", "typeString": "address[] memory" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "arguments": [ { "hexValue": "32", "id": 31114, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "3387:1:25", "typeDescriptions": { "typeIdentifier": "t_rational_2_by_1", "typeString": "int_const 2" }, "value": "2" } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_rational_2_by_1", "typeString": "int_const 2" } ], "id": 31113, "isConstant": false, "isLValue": false, "isPure": true, "lValueRequested": false, "nodeType": "NewExpression", "src": "3373:13:25", "typeDescriptions": { "typeIdentifier": "t_function_objectcreation_pure$_t_uint256_$returns$_t_array$_t_address_$dyn_memory_ptr_$", "typeString": "function (uint256) pure returns (address[] memory)" }, "typeName": { "baseType": { "id": 31111, "name": "address", "nodeType": "ElementaryTypeName", "src": "3377:7:25", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "id": 31112, "nodeType": "ArrayTypeName", "src": "3377:9:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_storage_ptr", "typeString": "address[]" } } }, "id": 31115, "isConstant": false, "isLValue": false, "isPure": true, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "3373:16:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_memory_ptr", "typeString": "address[] memory" } }, "src": "3366:23:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_memory_ptr", "typeString": "address[] memory" } }, "id": 31117, "nodeType": "ExpressionStatement", "src": "3366:23:25" }, { "expression": { "id": 31123, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "baseExpression": { "id": 31118, "name": "path", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31108, "src": "3399:4:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_memory_ptr", "typeString": "address[] memory" } }, "id": 31120, "indexExpression": { "hexValue": "30", "id": 31119, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "3404:1:25", "typeDescriptions": { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, "value": "0" }, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": true, "nodeType": "IndexAccess", "src": "3399:7:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "expression": { "id": 31121, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31099, "src": "3409:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31122, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "3416:7:25", "memberName": "tokenIn", "nodeType": "MemberAccess", "referencedDeclaration": 30841, "src": "3409:14:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "src": "3399:24:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "id": 31124, "nodeType": "ExpressionStatement", "src": "3399:24:25" }, { "expression": { "id": 31130, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "baseExpression": { "id": 31125, "name": "path", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31108, "src": "3433:4:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_memory_ptr", "typeString": "address[] memory" } }, "id": 31127, "indexExpression": { "hexValue": "31", "id": 31126, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "3438:1:25", "typeDescriptions": { "typeIdentifier": "t_rational_1_by_1", "typeString": "int_const 1" }, "value": "1" }, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": true, "nodeType": "IndexAccess", "src": "3433:7:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "expression": { "id": 31128, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31099, "src": "3443:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31129, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "3450:8:25", "memberName": "tokenOut", "nodeType": "MemberAccess", "referencedDeclaration": 30843, "src": "3443:15:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "src": "3433:25:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "id": 31131, "nodeType": "ExpressionStatement", "src": "3433:25:25" }, { "assignments": [ 31136 ], "declarations": [ { "constant": false, "id": 31136, "mutability": "mutable", "name": "amounts", "nameLocation": "3483:7:25", "nodeType": "VariableDeclaration", "scope": 31158, "src": "3469:21:25", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_array$_t_uint256_$dyn_memory_ptr", "typeString": "uint256[]" }, "typeName": { "baseType": { "id": 31134, "name": "uint", "nodeType": "ElementaryTypeName", "src": "3469:4:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 31135, "nodeType": "ArrayTypeName", "src": "3469:6:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_uint256_$dyn_storage_ptr", "typeString": "uint256[]" } }, "visibility": "internal" } ], "id": 31153, "initialValue": { "arguments": [ { "expression": { "id": 31142, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31099, "src": "3581:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31143, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "3588:6:25", "memberName": "amount", "nodeType": "MemberAccess", "referencedDeclaration": 30847, "src": "3581:13:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, { "hexValue": "30", "id": 31144, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "3612:1:25", "typeDescriptions": { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, "value": "0" }, { "id": 31145, "name": "path", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31108, "src": "3631:4:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_address_$dyn_memory_ptr", "typeString": "address[] memory" } }, { "arguments": [ { "id": 31148, "name": "this", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": -28, "src": "3661:4:25", "typeDescriptions": { "typeIdentifier": "t_contract$_WhackAMoleBotV1_$31204", "typeString": "contract WhackAMoleBotV1" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_contract$_WhackAMoleBotV1_$31204", "typeString": "contract WhackAMoleBotV1" } ], "id": 31147, "isConstant": false, "isLValue": false, "isPure": true, "lValueRequested": false, "nodeType": "ElementaryTypeNameExpression", "src": "3653:7:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_address_$", "typeString": "type(address)" }, "typeName": { "id": 31146, "name": "address", "nodeType": "ElementaryTypeName", "src": "3653:7:25", "typeDescriptions": {} } }, "id": 31149, "isConstant": false, "isLValue": false, "isPure": false, "kind": "typeConversion", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "3653:13:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, { "expression": { "id": 31150, "name": "block", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": -4, "src": "3684:5:25", "typeDescriptions": { "typeIdentifier": "t_magic_block", "typeString": "block" } }, "id": 31151, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "3690:9:25", "memberName": "timestamp", "nodeType": "MemberAccess", "src": "3684:15:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_uint256", "typeString": "uint256" }, { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, { "typeIdentifier": "t_array$_t_address_$dyn_memory_ptr", "typeString": "address[] memory" }, { "typeIdentifier": "t_address", "typeString": "address" }, { "typeIdentifier": "t_uint256", "typeString": "uint256" } ], "expression": { "arguments": [ { "expression": { "id": 31138, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31099, "src": "3510:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31139, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "3517:7:25", "memberName": "handler", "nodeType": "MemberAccess", "referencedDeclaration": 30839, "src": "3510:14:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" } ], "id": 31137, "name": "IUniswapV2Router", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31783, "src": "3493:16:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_IUniswapV2Router_$31783_$", "typeString": "type(contract IUniswapV2Router)" } }, "id": 31140, "isConstant": false, "isLValue": false, "isPure": false, "kind": "typeConversion", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "3493:32:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_contract$_IUniswapV2Router_$31783", "typeString": "contract IUniswapV2Router" } }, "id": 31141, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "3539:24:25", "memberName": "swapExactTokensForTokens", "nodeType": "MemberAccess", "referencedDeclaration": 31765, "src": "3493:70:25", "typeDescriptions": { "typeIdentifier": "t_function_external_nonpayable$_t_uint256_$_t_uint256_$_t_array$_t_address_$dyn_memory_ptr_$_t_address_$_t_uint256_$returns$_t_array$_t_uint256_$dyn_memory_ptr_$", "typeString": "function (uint256,uint256,address[] memory,address,uint256) external returns (uint256[] memory)" } }, "id": 31152, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "3493:220:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_array$_t_uint256_$dyn_memory_ptr", "typeString": "uint256[] memory" } }, "nodeType": "VariableDeclarationStatement", "src": "3469:244:25" }, { "expression": { "baseExpression": { "id": 31154, "name": "amounts", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31136, "src": "3731:7:25", "typeDescriptions": { "typeIdentifier": "t_array$_t_uint256_$dyn_memory_ptr", "typeString": "uint256[] memory" } }, "id": 31156, "indexExpression": { "hexValue": "31", "id": 31155, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "3739:1:25", "typeDescriptions": { "typeIdentifier": "t_rational_1_by_1", "typeString": "int_const 1" }, "value": "1" }, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "nodeType": "IndexAccess", "src": "3731:10:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "functionReturnParameters": 31103, "id": 31157, "nodeType": "Return", "src": "3724:17:25" } ] }, "implemented": true, "kind": "function", "modifiers": [], "name": "uniswapV2Swap", "nameLocation": "3234:13:25", "parameters": { "id": 31100, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 31099, "mutability": "mutable", "name": "params", "nameLocation": "3275:6:25", "nodeType": "VariableDeclaration", "scope": 31159, "src": "3257:24:25", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams" }, "typeName": { "id": 31098, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 31097, "name": "SwapParams", "nameLocations": [ "3257:10:25" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30848, "src": "3257:10:25" }, "referencedDeclaration": 30848, "src": "3257:10:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_storage_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams" } }, "visibility": "internal" } ], "src": "3247:40:25" }, "returnParameters": { "id": 31103, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 31102, "mutability": "mutable", "name": "amountOut", "nameLocation": "3314:9:25", "nodeType": "VariableDeclaration", "scope": 31159, "src": "3306:17:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 31101, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "3306:7:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "src": "3305:19:25" }, "scope": 31204, "stateMutability": "nonpayable", "virtual": false, "visibility": "internal" }, { "id": 31203, "nodeType": "FunctionDefinition", "src": "3774:636:25", "nodes": [], "body": { "id": 31202, "nodeType": "Block", "src": "3874:536:25", "nodes": [], "statements": [ { "assignments": [ 31171 ], "declarations": [ { "constant": false, "id": 31171, "mutability": "mutable", "name": "singleParams", "nameLocation": "3926:12:25", "nodeType": "VariableDeclaration", "scope": 31202, "src": "3884:54:25", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_struct$_ExactInputSingleParams_$31802_memory_ptr", "typeString": "struct ISwapRouter.ExactInputSingleParams" }, "typeName": { "id": 31170, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 31169, "name": "ISwapRouter.ExactInputSingleParams", "nameLocations": [ "3884:11:25", "3896:22:25" ], "nodeType": "IdentifierPath", "referencedDeclaration": 31802, "src": "3884:34:25" }, "referencedDeclaration": 31802, "src": "3884:34:25", "typeDescriptions": { "typeIdentifier": "t_struct$_ExactInputSingleParams_$31802_storage_ptr", "typeString": "struct ISwapRouter.ExactInputSingleParams" } }, "visibility": "internal" } ], "id": 31191, "initialValue": { "arguments": [ { "expression": { "id": 31174, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31162, "src": "4016:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31175, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "4023:7:25", "memberName": "tokenIn", "nodeType": "MemberAccess", "referencedDeclaration": 30841, "src": "4016:14:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, { "expression": { "id": 31176, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31162, "src": "4058:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31177, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "4065:8:25", "memberName": "tokenOut", "nodeType": "MemberAccess", "referencedDeclaration": 30843, "src": "4058:15:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, { "expression": { "id": 31178, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31162, "src": "4096:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31179, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "4103:3:25", "memberName": "fee", "nodeType": "MemberAccess", "referencedDeclaration": 30845, "src": "4096:10:25", "typeDescriptions": { "typeIdentifier": "t_uint24", "typeString": "uint24" } }, { "arguments": [ { "id": 31182, "name": "this", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": -28, "src": "4143:4:25", "typeDescriptions": { "typeIdentifier": "t_contract$_WhackAMoleBotV1_$31204", "typeString": "contract WhackAMoleBotV1" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_contract$_WhackAMoleBotV1_$31204", "typeString": "contract WhackAMoleBotV1" } ], "id": 31181, "isConstant": false, "isLValue": false, "isPure": true, "lValueRequested": false, "nodeType": "ElementaryTypeNameExpression", "src": "4135:7:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_address_$", "typeString": "type(address)" }, "typeName": { "id": 31180, "name": "address", "nodeType": "ElementaryTypeName", "src": "4135:7:25", "typeDescriptions": {} } }, "id": 31183, "isConstant": false, "isLValue": false, "isPure": false, "kind": "typeConversion", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "4135:13:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, { "expression": { "id": 31184, "name": "block", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": -4, "src": "4176:5:25", "typeDescriptions": { "typeIdentifier": "t_magic_block", "typeString": "block" } }, "id": 31185, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "4182:9:25", "memberName": "timestamp", "nodeType": "MemberAccess", "src": "4176:15:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, { "expression": { "id": 31186, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31162, "src": "4219:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31187, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "4226:6:25", "memberName": "amount", "nodeType": "MemberAccess", "referencedDeclaration": 30847, "src": "4219:13:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, { "hexValue": "30", "id": 31188, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "4268:1:25", "typeDescriptions": { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, "value": "0" }, { "hexValue": "30", "id": 31189, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "4306:1:25", "typeDescriptions": { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, "value": "0" } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" }, { "typeIdentifier": "t_address", "typeString": "address" }, { "typeIdentifier": "t_uint24", "typeString": "uint24" }, { "typeIdentifier": "t_address", "typeString": "address" }, { "typeIdentifier": "t_uint256", "typeString": "uint256" }, { "typeIdentifier": "t_uint256", "typeString": "uint256" }, { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" } ], "expression": { "id": 31172, "name": "ISwapRouter", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31832, "src": "3941:11:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_ISwapRouter_$31832_$", "typeString": "type(contract ISwapRouter)" } }, "id": 31173, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "3966:22:25", "memberName": "ExactInputSingleParams", "nodeType": "MemberAccess", "referencedDeclaration": 31802, "src": "3941:47:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_struct$_ExactInputSingleParams_$31802_storage_ptr_$", "typeString": "type(struct ISwapRouter.ExactInputSingleParams storage pointer)" } }, "id": 31190, "isConstant": false, "isLValue": false, "isPure": false, "kind": "structConstructorCall", "lValueRequested": false, "nameLocations": [ "4007:7:25", "4048:8:25", "4091:3:25", "4124:9:25", "4166:8:25", "4209:8:25", "4250:16:25", "4287:17:25" ], "names": [ "tokenIn", "tokenOut", "fee", "recipient", "deadline", "amountIn", "amountOutMinimum", "sqrtPriceLimitX96" ], "nodeType": "FunctionCall", "src": "3941:381:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_struct$_ExactInputSingleParams_$31802_memory_ptr", "typeString": "struct ISwapRouter.ExactInputSingleParams memory" } }, "nodeType": "VariableDeclarationStatement", "src": "3884:438:25" }, { "expression": { "id": 31200, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 31192, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31165, "src": "4333:9:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "arguments": [ { "id": 31198, "name": "singleParams", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31171, "src": "4390:12:25", "typeDescriptions": { "typeIdentifier": "t_struct$_ExactInputSingleParams_$31802_memory_ptr", "typeString": "struct ISwapRouter.ExactInputSingleParams memory" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_struct$_ExactInputSingleParams_$31802_memory_ptr", "typeString": "struct ISwapRouter.ExactInputSingleParams memory" } ], "expression": { "arguments": [ { "expression": { "id": 31194, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31162, "src": "4357:6:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams memory" } }, "id": 31195, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "4364:7:25", "memberName": "handler", "nodeType": "MemberAccess", "referencedDeclaration": 30839, "src": "4357:14:25", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" } ], "id": 31193, "name": "ISwapRouter", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31832, "src": "4345:11:25", "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_ISwapRouter_$31832_$", "typeString": "type(contract ISwapRouter)" } }, "id": 31196, "isConstant": false, "isLValue": false, "isPure": false, "kind": "typeConversion", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "4345:27:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_contract$_ISwapRouter_$31832", "typeString": "contract ISwapRouter" } }, "id": 31197, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "4373:16:25", "memberName": "exactInputSingle", "nodeType": "MemberAccess", "referencedDeclaration": 31811, "src": "4345:44:25", "typeDescriptions": { "typeIdentifier": "t_function_external_payable$_t_struct$_ExactInputSingleParams_$31802_memory_ptr_$returns$_t_uint256_$", "typeString": "function (struct ISwapRouter.ExactInputSingleParams memory) payable external returns (uint256)" } }, "id": 31199, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "4345:58:25", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "4333:70:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 31201, "nodeType": "ExpressionStatement", "src": "4333:70:25" } ] }, "implemented": true, "kind": "function", "modifiers": [], "name": "uniswapV3Swap", "nameLocation": "3783:13:25", "parameters": { "id": 31163, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 31162, "mutability": "mutable", "name": "params", "nameLocation": "3824:6:25", "nodeType": "VariableDeclaration", "scope": 31203, "src": "3806:24:25", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_memory_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams" }, "typeName": { "id": 31161, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 31160, "name": "SwapParams", "nameLocations": [ "3806:10:25" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30848, "src": "3806:10:25" }, "referencedDeclaration": 30848, "src": "3806:10:25", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30848_storage_ptr", "typeString": "struct WhackAMoleBotV1.SwapParams" } }, "visibility": "internal" } ], "src": "3796:40:25" }, "returnParameters": { "id": 31166, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 31165, "mutability": "mutable", "name": "amountOut", "nameLocation": "3863:9:25", "nodeType": "VariableDeclaration", "scope": 31203, "src": "3855:17:25", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 31164, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "3855:7:25", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "src": "3854:19:25" }, "scope": 31204, "stateMutability": "nonpayable", "virtual": false, "visibility": "internal" } ], "abstract": false, "baseContracts": [], "canonicalName": "WhackAMoleBotV1", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, "linearizedBaseContracts": [ 31204 ], "name": "WhackAMoleBotV1", "nameLocation": "442:15:25", "scope": 31205, "usedErrors": [ 30850, 30852 ], "usedEvents": [] } ], "license": "MIT" }, "id": 25 } ================================================ FILE: execution/__init__.py ================================================ from execution.dex_order import DexOrder ================================================ FILE: execution/dex_order.py ================================================ import os import json import asyncio from web3 import Web3 from uuid import uuid4 from pathlib import Path from dotenv import load_dotenv from typing import Any, Dict, List from eth_account.account import Account from flashbots import flashbot, Flashbots from web3.exceptions import TransactionNotFound from flashbots.flashbots import FlashbotsBundleResponse load_dotenv(override=True) PROTOCOL_TO_ID = { 'uniswap_v2': 0, 'sushiswap_v2': 0, 'uniswap_v3': 1, 'sushiswap_v3': 1, } FLASHBOTS_SIGNING_KEY = os.getenv('FLASHBOTS_SIGNING_KEY') FLASHBOTS_PRIVATE_KEY = os.getenv('FLASHBOTS_PRIVATE_KEY') DIR = os.path.dirname(os.path.abspath(__file__)) BOT_ABI_FILE_PATH = Path(DIR) / 'WhackAMoleBotV1.json' BOT_ABI = json.load(open(BOT_ABI_FILE_PATH, 'r'))['abi'] ERC20_ABI_FILE_PATH = Path(DIR).parent / 'abi' / 'ERC20.json' ERC20_ABI = json.load(open(ERC20_ABI_FILE_PATH, 'r')) class DexOrder: """ TODO: Send bundles to multiple private relays to increase probability of being added """ PRIVATE_RELAY = { 'ethereum': 'https://relay.flashbots.net' } def __init__(self, private_key: str = FLASHBOTS_PRIVATE_KEY, signing_key: str = FLASHBOTS_SIGNING_KEY, rpc_endpoints: Dict[str, str] = None, tokens: Dict[str, Dict[str, List[str or int]]] = None, pools: List[Dict[str, Any]] = None, contracts: Dict[str, str] = None, handlers: Dict[str, Dict[str, str]] = None): self.sender = Account.from_key(private_key) self.signer = Account.from_key(signing_key) # used for Flashbots reputation self.rpc_endpoints = rpc_endpoints self.tokens = tokens self.pools = pools self.contracts = contracts self.handlers = handlers # extract keys from tokens, pools self.chains_list = sorted(list(tokens.keys())) self.exchanges_list = sorted(set([p['exchange'] for p in pools])) tokens_list = [] for exchange, tokens_dict in tokens.items(): tokens_list.extend(list(tokens_dict.keys())) self.tokens_list = sorted(list(set(tokens_list))) # map chains, exchanges, tokens to int id value # this is used to map chains/exchanges/tokens to numpy array index values self.chain_to_id = {k: i for i, k in enumerate(self.chains_list)} self.exchange_to_id = {k: i for i, k in enumerate(self.exchanges_list)} self.token_to_id = {k: i for i, k in enumerate(self.tokens_list)} self.web3: Dict[str, Web3] = {k: Web3(Web3.HTTPProvider(v)) for k, v in rpc_endpoints.items()} self.chain_id = {k: v.eth.chain_id for k, v in self.web3.items()} for chain, w3 in self.web3.items(): flashbot(w3, self.signer, self.PRIVATE_RELAY[chain]) self.bot = { chain: w3.eth.contract(address=contracts[chain], abi=BOT_ABI) for chain, w3 in self.web3.items() } async def send_bundle(self, w3: Web3, bundle: List[Dict[str, Any]], retry: int, block_number: int = None) -> list: flashbots: Flashbots = w3.flashbots left_retries = retry if not block_number: block_number = w3.eth.block_number receipts = [] while left_retries > 0: print(f'Sending bundles at: #{block_number}') try: flashbots.simulate(bundle, block_number) except Exception as e: print('Simulation error', e) break replacement_uuid = str(uuid4()) response: FlashbotsBundleResponse = flashbots.send_bundle( bundle, target_block_number=block_number + 1, opts={'replacementUuid': replacement_uuid}, ) while w3.eth.block_number < response.target_block_number: await asyncio.sleep(1) try: receipts = list( map(lambda tx: w3.eth.get_transaction_receipt(tx['hash']), response.bundle) ) print(f'\nBundle was mined in block {receipts[0].blockNumber}\a') break except TransactionNotFound: print(f'Bundle not found in block {block_number + 1}') flashbots.cancel_bundles(replacement_uuid) left_retries -= 1 block_number += 1 return receipts async def transfer_in(self, chain: str, token: str, amount: float, max_priority_fee_per_gas: float, max_fee_per_gas: float, retry: int, block_number: int = None) -> list: w3 = self.web3[chain] token_contract = w3.eth.contract(address=token, abi=ERC20_ABI) nonce = w3.eth.get_transaction_count(self.sender.address) transaction = token_contract.functions.transfer( self.bot[chain].address, int(amount) ).build_transaction({ 'from': self.sender.address, 'gas': 200000, 'nonce': nonce, 'chainId': self.chain_id[chain], 'maxFeePerGas': int(max_fee_per_gas), 'maxPriorityFeePerGas': int(max_priority_fee_per_gas), }) signed = self.sender.sign_transaction(transaction) bundle = [{'signed_transaction': signed.rawTransaction}] return await self.send_bundle(w3, bundle, retry, block_number) async def transfer_out(self, chain: str, tokens: List[str], max_priority_fee_per_gas: float, max_fee_per_gas: float, retry: int, block_number: int = None) -> list: w3 = self.web3[chain] nonce = w3.eth.get_transaction_count(self.sender.address) transaction = self.bot[chain].functions.recoverTokens( tokens ).build_transaction({ 'from': self.sender.address, 'gas': 300000, 'nonce': nonce, 'chainId': self.chain_id[chain], 'maxFeePerGas': int(max_fee_per_gas), 'maxPriorityFeePerGas': int(max_priority_fee_per_gas), }) signed = self.sender.sign_transaction(transaction) bundle = [{'signed_transaction': signed.rawTransaction}] return await self.send_bundle(w3, bundle, retry, block_number) async def approve_handlers(self, chain: str, tokens: List[str], handlers: List[str], max_priority_fee_per_gas: float, max_fee_per_gas: float, retry: int, block_number: int = None): """ You only need to call this once with all the tokens used in your trades This function call will automatically set maxint as the approved amount to all the handlers """ w3 = self.web3[chain] nonce = w3.eth.get_transaction_count(self.sender.address) transaction = self.bot[chain].functions.approveHandlers( tokens, handlers ).build_transaction({ 'from': self.sender.address, 'gas': 400000, 'nonce': nonce, 'chainId': self.chain_id[chain], 'maxFeePerGas': int(max_fee_per_gas), 'maxPriorityFeePerGas': int(max_priority_fee_per_gas), }) signed = self.sender.sign_transaction(transaction) bundle = [{'signed_transaction': signed.rawTransaction}] return await self.send_bundle(w3, bundle, retry, block_number) def make_params(self, amount_in: float, buy_path: List[List[int]], sell_path: List[List[int]], buy_pools: List[int], sell_pools: List[int]) -> List[Dict[str, Any]]: params = [] params.extend(self._make_buy_params(amount_in, buy_path, buy_pools)) params.extend(self._make_sell_params(sell_path, sell_pools)) return params def _make_buy_params(self, amount_in: float, path: List[List[int]], pools: List[int]): params_list = [] for i in range(len(path)): _path = path[i] if not sum(_path): continue _pool_idx = pools[i] pool = self.pools[_pool_idx] chain = pool['chain'] exchange = pool['exchange'] version = pool['version'] exchange_key = f'{exchange}_v{version}' token_in = self.tokens_list[_path[2]] token_out = self.tokens_list[_path[3]] amount_in_scaled = amount_in if i == 0 else 0 params = { 'protocol': PROTOCOL_TO_ID[exchange_key], 'handler': self.handlers[chain][exchange_key], 'tokenIn': self.tokens[chain][token_in][0], 'tokenOut': self.tokens[chain][token_out][0], 'fee': pool['fee'], 'amount': int(amount_in_scaled), } params_list.append(params) return params_list def _make_sell_params(self, path: List[List[int]], pools: List[int]): params_list = [] for i in range(len(path)): _path = path[i] if not sum(_path): continue _pool_idx = pools[i] pool = self.pools[_pool_idx] chain = pool['chain'] exchange = pool['exchange'] version = pool['version'] exchange_key = f'{exchange}_v{version}' # not the index difference with buy_params token_in = self.tokens_list[_path[3]] token_out = self.tokens_list[_path[2]] params = { 'protocol': PROTOCOL_TO_ID[exchange_key], 'handler': self.handlers[chain][exchange_key], 'tokenIn': self.tokens[chain][token_in][0], 'tokenOut': self.tokens[chain][token_out][0], 'fee': pool['fee'], 'amount': 0, # no need to set amount } params_list.append(params) return list(reversed(params_list)) async def send_order(self, chain: str, params: List[Dict[str, Any]], min_amount_out: float, max_priority_fee_per_gas: float, max_fee_per_gas: float, retry: int, block_number: int = None): w3 = self.web3[chain] nonce = w3.eth.get_transaction_count(self.sender.address) transaction = self.bot[chain].functions.whack( params, int(min_amount_out) ).build_transaction({ 'from': self.sender.address, 'gas': 400000, 'nonce': nonce, 'chainId': self.chain_id[chain], 'maxFeePerGas': int(max_fee_per_gas), 'maxPriorityFeePerGas': int(max_priority_fee_per_gas), }) signed = self.sender.sign_transaction(transaction) bundle = [{'signed_transaction': signed.rawTransaction}] return await self.send_bundle(w3, bundle, retry, block_number) if __name__ == '__main__': from configs import RPC_ENDPOINTS from addresses import ETHEREUM_EXECUTION_HANDLERS ETHEREUM_BOT_ADDRESS = os.getenv('ETHEREUM_BOT_ADDRESS') order = DexOrder(private_key=FLASHBOTS_PRIVATE_KEY, signing_key=FLASHBOTS_SIGNING_KEY, rpc_endpoints={'ethereum': RPC_ENDPOINTS['ethereum']}, contracts={'ethereum': ETHEREUM_BOT_ADDRESS}, handlers={'ethereum': ETHEREUM_EXECUTION_HANDLERS}) block_number = order.web3['ethereum'].eth.block_number max_priority_fee_per_gas = 1 * 10 ** 9 max_fee_per_gas = 30 * 10 ** 9 # # example of sending USDT into bot contract # receipts = asyncio.run(order.transfer_in('ethereum', # '0xdAC17F958D2ee523a2206206994597C13D831ec7', # 100 * 10 ** 6, # max_priority_fee_per_gas, # max_fee_per_gas, # block_number)) # # example of recovering USDT from bot contract # receipts = asyncio.run(order.transfer_out('ethereum', # ['0xdAC17F958D2ee523a2206206994597C13D831ec7'], # max_priority_fee_per_gas, # max_fee_per_gas, # 3, # block_number)) usdt = order.web3['ethereum'].eth.contract(address='0xdAC17F958D2ee523a2206206994597C13D831ec7', abi=ERC20_ABI) owner_usdt_balance = usdt.functions.balanceOf(order.sender.address).call() bot_usdt_balance = usdt.functions.balanceOf(order.contracts['ethereum']).call() print(owner_usdt_balance, bot_usdt_balance) ================================================ FILE: external/__init__.py ================================================ from external.influxdb import * from external.telegram_bot import * ================================================ FILE: external/influxdb.py ================================================ import os from typing import Dict from dotenv import load_dotenv from influxdb_client import Point from influxdb_client.client.influxdb_client_async import InfluxDBClientAsync load_dotenv(override=True) INFLUXDB_TOKEN = os.getenv('INFLUXDB_TOKEN') INFLUXDB_URL = os.getenv('INFLUXDB_URL') INFLUXDB_ORG = os.getenv('INFLUXDB_ORG') INFLUXDB_BUCKET = os.getenv('INFLUXDB_BUCKET') class InfluxDB: """ Used to collect real-time data from bot. If all init args are None, InfluxDB won't do anything. """ def __init__(self, token: str = INFLUXDB_TOKEN, url: str = INFLUXDB_URL, org: str = INFLUXDB_ORG, bucket: str = INFLUXDB_BUCKET): self.token = token self.url = url self.org = org self.bucket = bucket if token: self.client = InfluxDBClientAsync( url=url, token=token, org=org ) self.write_api = self.client.write_api() else: self.client = None self.write_api = None async def send(self, measurement: str, data: Dict[str, float]): if self.write_api: points = [ Point(measurement).field(k, v) for k, v in data.items() ] await self.write_api.write( bucket=self.bucket, org=self.org, record=points ) async def close(self): if self.client: await self.client.close() async def test_send(): influxdb = InfluxDB() await influxdb.send('test', {'data1': 1, 'data2': 2}) await influxdb.close() if __name__ == '__main__': import asyncio asyncio.run(test_send()) ================================================ FILE: external/telegram_bot.py ================================================ import os import requests from telegram import Bot from dotenv import load_dotenv load_dotenv(override=True) TELEGRAM_TOKEN = os.getenv('TELEGRAM_TOKEN') TELEGRAM_CHAT_ID = os.getenv('TELEGRAM_CHAT_ID') class Telegram: def __init__(self, token: str = TELEGRAM_TOKEN, chat_id: str or int = TELEGRAM_CHAT_ID): if token and chat_id: self.token = token self.chat_id = int(chat_id) if token: self.bot = Bot(token=self.token) else: self.bot = None def get_updates(self): """ You can call get_updates to get the chat_id """ url = f'https://api.telegram.org/bot{self.token}/getUpdates' updates = requests.get(url) return updates.json() async def send(self, message: str): if self.bot: await self.bot.sendMessage(chat_id=self.chat_id, text=message) async def test_send(): tele = Telegram() await tele.send('hello there') if __name__ == '__main__': import asyncio asyncio.run(test_send()) ================================================ FILE: main.py ================================================ import asyncio import nest_asyncio from strategies.dex_arb_base import main if __name__ == '__main__': nest_asyncio.apply() asyncio.run(main()) ================================================ FILE: requirements.txt ================================================ aiohttp==3.8.5 aioprocessing==2.0.1 aiosignal==1.3.1 anyio==3.7.1 async-timeout==4.0.2 attrs==23.1.0 base58==2.1.1 bitarray==2.8.1 certifi==2023.7.22 charset-normalizer==3.2.0 cytoolz==0.12.2 eth-abi==2.2.0 eth-account==0.5.9 eth-hash==0.5.2 eth-keyfile==0.5.1 eth-keys==0.3.4 eth-retry==0.1.18 eth-rlp==0.2.1 eth-typing==2.3.0 eth-utils==1.9.5 exceptiongroup==1.1.2 flashbots==1.1.1 frozenlist==1.4.0 h11==0.14.0 hexbytes==0.3.1 httpcore==0.17.3 httpx==0.24.1 idna==3.4 influxdb-client==1.37.0 ipfshttpclient==0.8.0a2 jsonschema==4.19.0 jsonschema-specifications==2023.7.1 lru-dict==1.2.0 multiaddr==0.0.9 multicall==0.7.4 multidict==6.0.4 nest-asyncio==1.5.7 netaddr==0.8.0 numpy==1.25.2 parsimonious==0.8.1 protobuf==3.19.5 pycryptodome==3.18.0 python-dateutil==2.8.2 python-dotenv==1.0.0 python-telegram-bot==20.4 pyunormalize==15.0.0 reactivex==4.0.4 referencing==0.30.2 regex==2023.6.3 requests==2.31.0 rlp==2.0.1 rpds-py==0.9.2 six==1.16.0 sniffio==1.3.0 toolz==0.12.0 typing_extensions==4.7.1 urllib3==2.0.4 varint==1.0.2 web3==5.31.4 websockets==11.0.3 yarl==1.9.2 ================================================ FILE: simulation/SimulatorV1.json ================================================ { "abi": [ { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [ { "components": [ { "internalType": "uint8", "name": "protocol", "type": "uint8" }, { "internalType": "address", "name": "handler", "type": "address" }, { "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "internalType": "struct SimulatorV1.SwapParams", "name": "params", "type": "tuple" } ], "name": "simulateCurveSwapIn", "outputs": [ { "internalType": "uint256", "name": "amountOut", "type": "uint256" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "components": [ { "internalType": "uint8", "name": "protocol", "type": "uint8" }, { "internalType": "address", "name": "handler", "type": "address" }, { "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "internalType": "struct SimulatorV1.SwapParams[]", "name": "paramsArray", "type": "tuple[]" } ], "name": "simulateSwapIn", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [ { "components": [ { "internalType": "uint8", "name": "protocol", "type": "uint8" }, { "internalType": "address", "name": "handler", "type": "address" }, { "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "internalType": "struct SimulatorV1.SwapParams", "name": "params", "type": "tuple" } ], "name": "simulateUniswapV2SwapIn", "outputs": [ { "internalType": "uint256", "name": "amountOut", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [ { "components": [ { "internalType": "uint8", "name": "protocol", "type": "uint8" }, { "internalType": "address", "name": "handler", "type": "address" }, { "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ], "internalType": "struct SimulatorV1.SwapParams", "name": "params", "type": "tuple" } ], "name": "simulateUniswapV3SwapIn", "outputs": [ { "internalType": "uint256", "name": "amountOut", "type": "uint256" } ], "stateMutability": "nonpayable", "type": "function" } ], "bytecode": { "object": "0x608060405234801561001057600080fd5b50610b6f806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806355abbafc14610051578063766370281461007657806388dbf051146100895780638e888ccc1461009c575b600080fd5b61006461005f366004610866565b6100af565b60405190815260200160405180910390f35b610064610084366004610927565b61014e565b610064610097366004610927565b610185565b6100646100aa366004610927565b610332565b60008082815b818110156101425760008686838181106100d1576100d16109d0565b905060c002018036038101906100e79190610927565b9050836000036100fd578060a001519350610105565b60a081018490525b805160ff166000036101215761011a8161014e565b9350610139565b805160ff166001036101395761013681610332565b93505b506001016100b5565b50909150505b92915050565b600080600061016a84602001518560400151866060015161043d565b9150915061017d8460a001518383610515565b949350505050565b60208101516000908180805b81600f0b83600f0b0361028c576040517f23746eb8000000000000000000000000000000000000000000000000000000008152600f82900b60048201526000906001600160a01b038616906323746eb8906024016020604051808303816000875af1158015610204573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061022891906109e6565b905086604001516001600160a01b0316816001600160a01b03160361024f57819350610270565b86606001516001600160a01b0316816001600160a01b031603610270578192505b82600f0b84600f0b14610283575061028c565b50600101610191565b60a08601516040517f5e0d443f000000000000000000000000000000000000000000000000000000008152600f85810b600483015284900b602482015260448101919091526001600160a01b03851690635e0d443f906064016020604051808303816000875af1158015610304573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103289190610a03565b9695505050505050565b6020818101516040805160a080820183526000808352948201858152828401868152606080850188815260808087018a8152888c01516001600160a01b039081168952938c015184168652958b015184528a015162ffffff908116825296517fc6a5026a0000000000000000000000000000000000000000000000000000000081528651831660048201529351821660248501529151604484015290519094166064820152905183166084820152909183169063c6a5026a9060a4016080604051808303816000875af115801561040d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104319190610a30565b50919695505050505050565b600080600061044c8585610660565b50905060008061045d88888861076f565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561049a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104be9190610a94565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff169150826001600160a01b0316876001600160a01b031614610503578082610506565b81815b90999098509650505050505050565b60008084116105915760405162461bcd60e51b815260206004820152602b60248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4960448201527f4e5055545f414d4f554e5400000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6000831180156105a15750600082115b6106135760405162461bcd60e51b815260206004820152602860248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4c60448201527f49515549444954590000000000000000000000000000000000000000000000006064820152608401610588565b6000610621856103e5610847565b9050600061062f8285610847565b9050600061064983610643886103e8610847565b9061085a565b90506106558183610aed565b979650505050505050565b600080826001600160a01b0316846001600160a01b0316036106ea5760405162461bcd60e51b815260206004820152602560248201527f556e697377617056324c6962726172793a204944454e544943414c5f4144445260448201527f45535345530000000000000000000000000000000000000000000000000000006064820152608401610588565b826001600160a01b0316846001600160a01b03161061070a57828461070d565b83835b90925090506001600160a01b0382166107685760405162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f4144445245535300006044820152606401610588565b9250929050565b600080600061077e8585610660565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825180830360280181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b60006108538284610b0f565b9392505050565b60006108538284610b26565b6000806020838503121561087957600080fd5b823567ffffffffffffffff8082111561089157600080fd5b818501915085601f8301126108a557600080fd5b8135818111156108b457600080fd5b86602060c0830285010111156108c957600080fd5b60209290920196919550909350505050565b803560ff811681146108ec57600080fd5b919050565b6001600160a01b038116811461090657600080fd5b50565b80356108ec816108f1565b803562ffffff811681146108ec57600080fd5b600060c0828403121561093957600080fd5b60405160c0810181811067ffffffffffffffff8211171561096a57634e487b7160e01b600052604160045260246000fd5b604052610976836108db565b815261098460208401610909565b602082015261099560408401610909565b60408201526109a660608401610909565b60608201526109b760808401610914565b608082015260a083013560a08201528091505092915050565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156109f857600080fd5b8151610853816108f1565b600060208284031215610a1557600080fd5b5051919050565b805163ffffffff811681146108ec57600080fd5b60008060008060808587031215610a4657600080fd5b845193506020850151610a58816108f1565b9250610a6660408601610a1c565b6060959095015193969295505050565b80516dffffffffffffffffffffffffffff811681146108ec57600080fd5b600080600060608486031215610aa957600080fd5b610ab284610a76565b9250610ac060208501610a76565b9150610ace60408501610a1c565b90509250925092565b634e487b7160e01b600052601160045260246000fd5b600082610b0a57634e487b7160e01b600052601260045260246000fd5b500490565b808202811582820484141761014857610148610ad7565b8082018082111561014857610148610ad756fea2646970667358221220e356eadef5c9ba78dc02924fa374e49a14a1c39f7523faef2ea2243ff497574564736f6c63430008140033", "sourceMap": "322:2859:24:-:0;;;748:16;;;;;;;;;;322:2859;;;;;;", "linkReferences": {} }, "deployedBytecode": { "object": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c806355abbafc14610051578063766370281461007657806388dbf051146100895780638e888ccc1461009c575b600080fd5b61006461005f366004610866565b6100af565b60405190815260200160405180910390f35b610064610084366004610927565b61014e565b610064610097366004610927565b610185565b6100646100aa366004610927565b610332565b60008082815b818110156101425760008686838181106100d1576100d16109d0565b905060c002018036038101906100e79190610927565b9050836000036100fd578060a001519350610105565b60a081018490525b805160ff166000036101215761011a8161014e565b9350610139565b805160ff166001036101395761013681610332565b93505b506001016100b5565b50909150505b92915050565b600080600061016a84602001518560400151866060015161043d565b9150915061017d8460a001518383610515565b949350505050565b60208101516000908180805b81600f0b83600f0b0361028c576040517f23746eb8000000000000000000000000000000000000000000000000000000008152600f82900b60048201526000906001600160a01b038616906323746eb8906024016020604051808303816000875af1158015610204573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061022891906109e6565b905086604001516001600160a01b0316816001600160a01b03160361024f57819350610270565b86606001516001600160a01b0316816001600160a01b031603610270578192505b82600f0b84600f0b14610283575061028c565b50600101610191565b60a08601516040517f5e0d443f000000000000000000000000000000000000000000000000000000008152600f85810b600483015284900b602482015260448101919091526001600160a01b03851690635e0d443f906064016020604051808303816000875af1158015610304573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103289190610a03565b9695505050505050565b6020818101516040805160a080820183526000808352948201858152828401868152606080850188815260808087018a8152888c01516001600160a01b039081168952938c015184168652958b015184528a015162ffffff908116825296517fc6a5026a0000000000000000000000000000000000000000000000000000000081528651831660048201529351821660248501529151604484015290519094166064820152905183166084820152909183169063c6a5026a9060a4016080604051808303816000875af115801561040d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104319190610a30565b50919695505050505050565b600080600061044c8585610660565b50905060008061045d88888861076f565b6001600160a01b0316630902f1ac6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561049a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104be9190610a94565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff169150826001600160a01b0316876001600160a01b031614610503578082610506565b81815b90999098509650505050505050565b60008084116105915760405162461bcd60e51b815260206004820152602b60248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4960448201527f4e5055545f414d4f554e5400000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6000831180156105a15750600082115b6106135760405162461bcd60e51b815260206004820152602860248201527f556e697377617056324c6962726172793a20494e53554646494349454e545f4c60448201527f49515549444954590000000000000000000000000000000000000000000000006064820152608401610588565b6000610621856103e5610847565b9050600061062f8285610847565b9050600061064983610643886103e8610847565b9061085a565b90506106558183610aed565b979650505050505050565b600080826001600160a01b0316846001600160a01b0316036106ea5760405162461bcd60e51b815260206004820152602560248201527f556e697377617056324c6962726172793a204944454e544943414c5f4144445260448201527f45535345530000000000000000000000000000000000000000000000000000006064820152608401610588565b826001600160a01b0316846001600160a01b03161061070a57828461070d565b83835b90925090506001600160a01b0382166107685760405162461bcd60e51b815260206004820152601e60248201527f556e697377617056324c6962726172793a205a45524f5f4144445245535300006044820152606401610588565b9250929050565b600080600061077e8585610660565b604080516bffffffffffffffffffffffff19606094851b811660208084019190915293851b81166034830152825180830360280181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b60006108538284610b0f565b9392505050565b60006108538284610b26565b6000806020838503121561087957600080fd5b823567ffffffffffffffff8082111561089157600080fd5b818501915085601f8301126108a557600080fd5b8135818111156108b457600080fd5b86602060c0830285010111156108c957600080fd5b60209290920196919550909350505050565b803560ff811681146108ec57600080fd5b919050565b6001600160a01b038116811461090657600080fd5b50565b80356108ec816108f1565b803562ffffff811681146108ec57600080fd5b600060c0828403121561093957600080fd5b60405160c0810181811067ffffffffffffffff8211171561096a57634e487b7160e01b600052604160045260246000fd5b604052610976836108db565b815261098460208401610909565b602082015261099560408401610909565b60408201526109a660608401610909565b60608201526109b760808401610914565b608082015260a083013560a08201528091505092915050565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156109f857600080fd5b8151610853816108f1565b600060208284031215610a1557600080fd5b5051919050565b805163ffffffff811681146108ec57600080fd5b60008060008060808587031215610a4657600080fd5b845193506020850151610a58816108f1565b9250610a6660408601610a1c565b6060959095015193969295505050565b80516dffffffffffffffffffffffffffff811681146108ec57600080fd5b600080600060608486031215610aa957600080fd5b610ab284610a76565b9250610ac060208501610a76565b9150610ace60408501610a1c565b90509250925092565b634e487b7160e01b600052601160045260246000fd5b600082610b0a57634e487b7160e01b600052601260045260246000fd5b500490565b808202811582820484141761014857610148610ad7565b8082018082111561014857610148610ad756fea2646970667358221220e356eadef5c9ba78dc02924fa374e49a14a1c39f7523faef2ea2243ff497574564736f6c63430008140033", "sourceMap": "322:2859:24:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;770:772;;;;;;:::i;:::-;;:::i;:::-;;;814:25:33;;;802:2;787:18;770:772:24;;;;;;;1548:424;;;;;;:::i;:::-;;:::i;2518:661::-;;;;;;:::i;:::-;;:::i;1978:534::-;;;;;;:::i;:::-;;:::i;770:772::-;859:7;;933:11;859:7;962:547;982:17;978:1;:21;962:547;;;1017:24;1044:11;;1056:1;1044:14;;;;;;;:::i;:::-;;;;;;1017:41;;;;;;;;;;:::i;:::-;;;1077:9;1090:1;1077:14;1073:142;;1123:6;:13;;;1111:25;;1073:142;;;1175:13;;;:25;;;1073:142;1233:15;;:20;;:15;:20;1229:210;;1285:31;1309:6;1285:23;:31::i;:::-;1273:43;;1229:210;;;1341:15;;:20;;1360:1;1341:20;1337:102;;1393:31;1417:6;1393:23;:31::i;:::-;1381:43;;1337:102;-1:-1:-1;1481:3:24;;962:547;;;-1:-1:-1;1526:9:24;;-1:-1:-1;;770:772:24;;;;;:::o;1548:424::-;1642:17;1672:14;1688:15;1707:123;1749:6;:14;;;1777:6;:14;;;1805:6;:15;;;1707:28;:123::i;:::-;1671:159;;;;1852:113;1895:6;:13;;;1922:9;1945:10;1852:29;:113::i;:::-;1840:125;1548:424;-1:-1:-1;;;;1548:424:24:o;2518:661::-;2661:14;;;;2603:17;;;;;2749:369;2761:1;2756:6;;:1;:6;;;2749:369;;2793:19;;;;;2801:2:33;2790:22;;;2793:19:24;;;2772:41:33;2778:12:24;;-1:-1:-1;;;;;2793:10:24;;;;;2745:18:33;;2793:19:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2778:34;;2839:6;:14;;;-1:-1:-1;;;;;2831:22:24;:4;-1:-1:-1;;;;;2831:22:24;;2827:151;;2877:7;2873:11;;2827:151;;;2917:6;:15;;;-1:-1:-1;;;;;2909:23:24;:4;-1:-1:-1;;;;;2909:23:24;;2905:73;;2956:7;2952:11;;2905:73;3001:1;2996:6;;:1;:6;;;2992:50;;3022:5;;;2992:50;-1:-1:-1;3084:9:24;;2749:369;;;3158:13;;;;3140:32;;;;;3307:2:33;3296:22;;;3140:32:24;;;3278:41:33;3355:22;;;3335:18;;;3328:50;3394:18;;;3387:34;;;;-1:-1:-1;;;;;3140:11:24;;;;;3251:18:33;;3140:32:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3128:44;2518:661;-1:-1:-1;;;;;;2518:661:24:o;1978:534::-;2125:14;;;;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2240:14:24;;;;-1:-1:-1;;;;;2217:37:24;;;;;2288:15;;;;2264:39;;;;2337:13;;;;2313:37;;2379:10;;;2360:29;;;;;;2463:42;;;;;3943:13:33;;3939:22;;2463:42:24;;;3921:41:33;4004:24;;4000:33;;3978:20;;;3971:63;4072:24;;4050:20;;;4043:54;4139:24;;4135:39;;;4113:20;;;4106:69;4217:24;;4213:33;;4191:20;;;4184:63;-1:-1:-1;;2463:28:24;;;;;3832:19:33;;2463:42:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;2443:62:24;;1978:534;-1:-1:-1;;;;;;1978:534:24:o;1667:490:31:-;1790:16;1808;1837:14;1857:26;1868:6;1876;1857:10;:26::i;:::-;1836:47;;;1894:16;1912;1962:32;1970:7;1979:6;1987;1962:7;:32::i;:::-;-1:-1:-1;;;;;1934:95:31;;:97;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1893:138;;;;;;;;;2074:6;-1:-1:-1;;;;;2064:16:31;:6;-1:-1:-1;;;;;2064:16:31;;:86;;2131:8;2141;2064:86;;;2096:8;2106;2064:86;2041:109;;;;-1:-1:-1;1667:490:31;-1:-1:-1;;;;;;;1667:490:31:o;2276:595::-;2408:17;2456:1;2445:8;:12;2437:68;;;;-1:-1:-1;;;2437:68:31;;5668:2:33;2437:68:31;;;5650:21:33;5707:2;5687:18;;;5680:30;5746:34;5726:18;;;5719:62;5817:13;5797:18;;;5790:41;5848:19;;2437:68:31;;;;;;;;;2548:1;2536:9;:13;:31;;;;;2566:1;2553:10;:14;2536:31;2515:118;;;;-1:-1:-1;;;2515:118:31;;6080:2:33;2515:118:31;;;6062:21:33;6119:2;6099:18;;;6092:30;6158:34;6138:18;;;6131:62;6229:10;6209:18;;;6202:38;6257:19;;2515:118:31;5878:404:33;2515:118:31;2643:23;2669:17;:8;2682:3;2669:12;:17::i;:::-;2643:43;-1:-1:-1;2696:17:31;2716:31;2643:43;2736:10;2716:19;:31::i;:::-;2696:51;-1:-1:-1;2757:19:31;2779:40;2803:15;2779:19;:9;2793:4;2779:13;:19::i;:::-;:23;;:40::i;:::-;2757:62;-1:-1:-1;2841:23:31;2757:62;2841:9;:23;:::i;:::-;2829:35;2276:595;-1:-1:-1;;;;;;;2276:595:31:o;321:397::-;420:14;436;484:6;-1:-1:-1;;;;;474:16:31;:6;-1:-1:-1;;;;;474:16:31;;466:66;;;;-1:-1:-1;;;466:66:31;;6957:2:33;466:66:31;;;6939:21:33;6996:2;6976:18;;;6969:30;7035:34;7015:18;;;7008:62;7106:7;7086:18;;;7079:35;7131:19;;466:66:31;6755:401:33;466:66:31;570:6;-1:-1:-1;;;;;561:15:31;:6;-1:-1:-1;;;;;561:15:31;;:77;;623:6;631;561:77;;;592:6;600;561:77;542:96;;-1:-1:-1;542:96:31;-1:-1:-1;;;;;;656:20:31;;648:63;;;;-1:-1:-1;;;648:63:31;;7363:2:33;648:63:31;;;7345:21:33;7402:2;7382:18;;;7375:30;7441:32;7421:18;;;7414:60;7491:18;;648:63:31;7161:354:33;648:63:31;321:397;;;;;:::o;807:805::-;926:12;951:14;967;985:26;996:6;1004;985:10;:26::i;:::-;1161:32;;;-1:-1:-1;;7747:2:33;7743:15;;;7739:24;;1161:32:31;;;;7727:37:33;;;;7798:15;;;7794:24;;7780:12;;;7773:46;1161:32:31;;;;;;;;;7835:12:33;;;1161:32:31;;1151:43;;;;;;1368:12;1322:197;;;8069:92:33;8194:15;;;;8190:53;;;8177:11;;;8170:74;8260:12;;;8253:28;;;;1059:66:31;8297:12:33;;;;8290:28;;;;1322:197:31;;;;;;;;;;8334:12:33;;;;1322:197:31;;;1287:254;;;;;;;;;;-1:-1:-1;;;;;807:805:31:o;3465:96:23:-;3523:7;3549:5;3553:1;3549;:5;:::i;:::-;3542:12;3465:96;-1:-1:-1;;;3465:96:23:o;2755:::-;2813:7;2839:5;2843:1;2839;:5;:::i;14:649:33:-;131:6;139;192:2;180:9;171:7;167:23;163:32;160:52;;;208:1;205;198:12;160:52;248:9;235:23;277:18;318:2;310:6;307:14;304:34;;;334:1;331;324:12;304:34;372:6;361:9;357:22;347:32;;417:7;410:4;406:2;402:13;398:27;388:55;;439:1;436;429:12;388:55;479:2;466:16;505:2;497:6;494:14;491:34;;;521:1;518;511:12;491:34;577:7;572:2;564:4;556:6;552:17;548:2;544:26;540:35;537:48;534:68;;;598:1;595;588:12;534:68;629:2;621:11;;;;;651:6;;-1:-1:-1;14:649:33;;-1:-1:-1;;;;14:649:33:o;850:156::-;916:20;;976:4;965:16;;955:27;;945:55;;996:1;993;986:12;945:55;850:156;;;:::o;1011:154::-;-1:-1:-1;;;;;1090:5:33;1086:54;1079:5;1076:65;1066:93;;1155:1;1152;1145:12;1066:93;1011:154;:::o;1170:134::-;1238:20;;1267:31;1238:20;1267:31;:::i;1309:161::-;1376:20;;1436:8;1425:20;;1415:31;;1405:59;;1460:1;1457;1450:12;1475:959;1563:6;1616:3;1604:9;1595:7;1591:23;1587:33;1584:53;;;1633:1;1630;1623:12;1584:53;1666:2;1660:9;1708:3;1700:6;1696:16;1778:6;1766:10;1763:22;1742:18;1730:10;1727:34;1724:62;1721:242;;;-1:-1:-1;;;1816:1:33;1809:88;1920:4;1917:1;1910:15;1948:4;1945:1;1938:15;1721:242;1979:2;1972:22;2018:27;2035:9;2018:27;:::i;:::-;2010:6;2003:43;2079:38;2113:2;2102:9;2098:18;2079:38;:::i;:::-;2074:2;2066:6;2062:15;2055:63;2151:38;2185:2;2174:9;2170:18;2151:38;:::i;:::-;2146:2;2138:6;2134:15;2127:63;2223:38;2257:2;2246:9;2242:18;2223:38;:::i;:::-;2218:2;2210:6;2206:15;2199:63;2296:38;2329:3;2318:9;2314:19;2296:38;:::i;:::-;2290:3;2282:6;2278:16;2271:64;2397:3;2386:9;2382:19;2369:33;2363:3;2355:6;2351:16;2344:59;2422:6;2412:16;;;1475:959;;;;:::o;2439:184::-;-1:-1:-1;;;2488:1:33;2481:88;2588:4;2585:1;2578:15;2612:4;2609:1;2602:15;2824:251;2894:6;2947:2;2935:9;2926:7;2922:23;2918:32;2915:52;;;2963:1;2960;2953:12;2915:52;2995:9;2989:16;3014:31;3039:5;3014:31;:::i;3432:184::-;3502:6;3555:2;3543:9;3534:7;3530:23;3526:32;3523:52;;;3571:1;3568;3561:12;3523:52;-1:-1:-1;3594:16:33;;3432:184;-1:-1:-1;3432:184:33:o;4258:167::-;4336:13;;4389:10;4378:22;;4368:33;;4358:61;;4415:1;4412;4405:12;4430:457;4526:6;4534;4542;4550;4603:3;4591:9;4582:7;4578:23;4574:33;4571:53;;;4620:1;4617;4610:12;4571:53;4649:9;4643:16;4633:26;;4702:2;4691:9;4687:18;4681:25;4715:31;4740:5;4715:31;:::i;:::-;4765:5;-1:-1:-1;4789:48:33;4833:2;4818:18;;4789:48;:::i;:::-;4877:2;4862:18;;;;4856:25;4430:457;;;;-1:-1:-1;;;4430:457:33:o;4892:188::-;4971:13;;5024:30;5013:42;;5003:53;;4993:81;;5070:1;5067;5060:12;5085:376;5172:6;5180;5188;5241:2;5229:9;5220:7;5216:23;5212:32;5209:52;;;5257:1;5254;5247:12;5209:52;5280:40;5310:9;5280:40;:::i;:::-;5270:50;;5339:49;5384:2;5373:9;5369:18;5339:49;:::i;:::-;5329:59;;5407:48;5451:2;5440:9;5436:18;5407:48;:::i;:::-;5397:58;;5085:376;;;;;:::o;6287:184::-;-1:-1:-1;;;6336:1:33;6329:88;6436:4;6433:1;6426:15;6460:4;6457:1;6450:15;6476:274;6516:1;6542;6532:189;;-1:-1:-1;;;6574:1:33;6567:88;6678:4;6675:1;6668:15;6706:4;6703:1;6696:15;6532:189;-1:-1:-1;6735:9:33;;6476:274::o;8357:168::-;8430:9;;;8461;;8478:15;;;8472:22;;8458:37;8448:71;;8499:18;;:::i;8530:125::-;8595:9;;;8616:10;;;8613:36;;;8629:18;;:::i", "linkReferences": {} }, "methodIdentifiers": { "simulateCurveSwapIn((uint8,address,address,address,uint24,uint256))": "88dbf051", "simulateSwapIn((uint8,address,address,address,uint24,uint256)[])": "55abbafc", "simulateUniswapV2SwapIn((uint8,address,address,address,uint24,uint256))": "76637028", "simulateUniswapV3SwapIn((uint8,address,address,address,uint24,uint256))": "8e888ccc" }, "rawMetadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"protocol\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"handler\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint24\",\"name\":\"fee\",\"type\":\"uint24\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct SimulatorV1.SwapParams\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"simulateCurveSwapIn\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"protocol\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"handler\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint24\",\"name\":\"fee\",\"type\":\"uint24\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct SimulatorV1.SwapParams[]\",\"name\":\"paramsArray\",\"type\":\"tuple[]\"}],\"name\":\"simulateSwapIn\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"protocol\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"handler\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint24\",\"name\":\"fee\",\"type\":\"uint24\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct SimulatorV1.SwapParams\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"simulateUniswapV2SwapIn\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"protocol\",\"type\":\"uint8\"},{\"internalType\":\"address\",\"name\":\"handler\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenIn\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"tokenOut\",\"type\":\"address\"},{\"internalType\":\"uint24\",\"name\":\"fee\",\"type\":\"uint24\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct SimulatorV1.SwapParams\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"simulateUniswapV3SwapIn\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"amountOut\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/SimulatorV1.sol\":\"SimulatorV1\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":1000},\"remappings\":[\":ds-test/=lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":openzeppelin/=lib/openzeppelin-contracts/contracts/\"]},\"sources\":{\"lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol\":{\"keccak256\":\"0x58b21219689909c4f8339af00813760337f7e2e7f169a97fe49e2896dcfb3b9a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://ef8e012e946dec20e59f2d4446f4b44bb098f3fa8bac103b1b5112fff777447b\",\"dweb:/ipfs/QmVTooKWcLkJ9W68yNX4MgdrbAKiAXwuRN9A7f4NkdcdtQ\"]},\"src/SimulatorV1.sol\":{\"keccak256\":\"0xabab21e93d94da86614a987cb191c4af0cfd5f7935f93d84707c162b60aa50f1\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a994d2922c656329461adc1448cf9bc75dbdc731a4d8e2a7c404b95baf353e26\",\"dweb:/ipfs/QmYqGBumM6nTN1jk66X7qnfJihSQGJomSNW9WAtSxFvbRY\"]},\"src/protocols/curve/ICurvePool.sol\":{\"keccak256\":\"0x70b4397964caf4fd4b2725bd5f082c4bc241930eee0ee3ec9035550d4edda568\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://17e257d37e0cafc4f3535a8771e10bca33da167fde6671a378c99d407934e193\",\"dweb:/ipfs/QmRcnzAEc269hkjeMofq9gsL57J5SnFFqxZbEmCQBNFxE1\"]},\"src/protocols/uniswap/IQuoterV2.sol\":{\"keccak256\":\"0x8c0ec4a66e8d84bf495a2ff565a36ac36f17ca84b33d5a103f8b42015f866557\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://e18478bae632cc3ff4270b94862dea82306f7d26241ff8d1635175f9c019aef3\",\"dweb:/ipfs/QmbimsEhnAWc8Yakd4x6ph5UzVMchmhf17YMf6inHx8HHe\"]},\"src/protocols/uniswap/IUniswapV2Pair.sol\":{\"keccak256\":\"0x3013d79abe422a873d9e7a74813ce3c84a0340c7d6ef0b6e56806f22e79439f6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://318c65fb06fe1274eb933c4159e8be95eb341f0d43345fe1c6bbacf0bfc6d072\",\"dweb:/ipfs/QmVpeEb9RxHcMfR4sn9Typ3V7PDJjy6nYJcTgiKqv7mzgX\"]},\"src/protocols/uniswap/UniswapV2Library.sol\":{\"keccak256\":\"0x7809ddbb45e565b2ccd8c6840987e98c336647d43d66357bbd661b343eab627d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6f79a573e59848ef66b0d7cc590d7eb86007fc6986654c6dd620741de618e726\",\"dweb:/ipfs/QmYfaKKtd6Kqz3ady9SHUyDJwRKf8co8qP6eh5QNcfdwxt\"]}},\"version\":1}", "metadata": { "compiler": { "version": "0.8.20+commit.a1b79de6" }, "language": "Solidity", "output": { "abi": [ { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "inputs": [ { "internalType": "struct SimulatorV1.SwapParams", "name": "params", "type": "tuple", "components": [ { "internalType": "uint8", "name": "protocol", "type": "uint8" }, { "internalType": "address", "name": "handler", "type": "address" }, { "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ] } ], "stateMutability": "nonpayable", "type": "function", "name": "simulateCurveSwapIn", "outputs": [ { "internalType": "uint256", "name": "amountOut", "type": "uint256" } ] }, { "inputs": [ { "internalType": "struct SimulatorV1.SwapParams[]", "name": "paramsArray", "type": "tuple[]", "components": [ { "internalType": "uint8", "name": "protocol", "type": "uint8" }, { "internalType": "address", "name": "handler", "type": "address" }, { "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ] } ], "stateMutability": "nonpayable", "type": "function", "name": "simulateSwapIn", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ] }, { "inputs": [ { "internalType": "struct SimulatorV1.SwapParams", "name": "params", "type": "tuple", "components": [ { "internalType": "uint8", "name": "protocol", "type": "uint8" }, { "internalType": "address", "name": "handler", "type": "address" }, { "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ] } ], "stateMutability": "view", "type": "function", "name": "simulateUniswapV2SwapIn", "outputs": [ { "internalType": "uint256", "name": "amountOut", "type": "uint256" } ] }, { "inputs": [ { "internalType": "struct SimulatorV1.SwapParams", "name": "params", "type": "tuple", "components": [ { "internalType": "uint8", "name": "protocol", "type": "uint8" }, { "internalType": "address", "name": "handler", "type": "address" }, { "internalType": "address", "name": "tokenIn", "type": "address" }, { "internalType": "address", "name": "tokenOut", "type": "address" }, { "internalType": "uint24", "name": "fee", "type": "uint24" }, { "internalType": "uint256", "name": "amount", "type": "uint256" } ] } ], "stateMutability": "nonpayable", "type": "function", "name": "simulateUniswapV3SwapIn", "outputs": [ { "internalType": "uint256", "name": "amountOut", "type": "uint256" } ] } ], "devdoc": { "kind": "dev", "methods": {}, "version": 1 }, "userdoc": { "kind": "user", "methods": {}, "version": 1 } }, "settings": { "remappings": [ ":ds-test/=lib/forge-std/lib/ds-test/src/", ":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", ":forge-std/=lib/forge-std/src/", ":openzeppelin-contracts/=lib/openzeppelin-contracts/", ":openzeppelin/=lib/openzeppelin-contracts/contracts/" ], "optimizer": { "enabled": true, "runs": 1000 }, "metadata": { "bytecodeHash": "ipfs" }, "compilationTarget": { "src/SimulatorV1.sol": "SimulatorV1" }, "libraries": {} }, "sources": { "lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol": { "keccak256": "0x58b21219689909c4f8339af00813760337f7e2e7f169a97fe49e2896dcfb3b9a", "urls": [ "bzz-raw://ef8e012e946dec20e59f2d4446f4b44bb098f3fa8bac103b1b5112fff777447b", "dweb:/ipfs/QmVTooKWcLkJ9W68yNX4MgdrbAKiAXwuRN9A7f4NkdcdtQ" ], "license": "MIT" }, "src/SimulatorV1.sol": { "keccak256": "0xabab21e93d94da86614a987cb191c4af0cfd5f7935f93d84707c162b60aa50f1", "urls": [ "bzz-raw://a994d2922c656329461adc1448cf9bc75dbdc731a4d8e2a7c404b95baf353e26", "dweb:/ipfs/QmYqGBumM6nTN1jk66X7qnfJihSQGJomSNW9WAtSxFvbRY" ], "license": "MIT" }, "src/protocols/curve/ICurvePool.sol": { "keccak256": "0x70b4397964caf4fd4b2725bd5f082c4bc241930eee0ee3ec9035550d4edda568", "urls": [ "bzz-raw://17e257d37e0cafc4f3535a8771e10bca33da167fde6671a378c99d407934e193", "dweb:/ipfs/QmRcnzAEc269hkjeMofq9gsL57J5SnFFqxZbEmCQBNFxE1" ], "license": "MIT" }, "src/protocols/uniswap/IQuoterV2.sol": { "keccak256": "0x8c0ec4a66e8d84bf495a2ff565a36ac36f17ca84b33d5a103f8b42015f866557", "urls": [ "bzz-raw://e18478bae632cc3ff4270b94862dea82306f7d26241ff8d1635175f9c019aef3", "dweb:/ipfs/QmbimsEhnAWc8Yakd4x6ph5UzVMchmhf17YMf6inHx8HHe" ], "license": "MIT" }, "src/protocols/uniswap/IUniswapV2Pair.sol": { "keccak256": "0x3013d79abe422a873d9e7a74813ce3c84a0340c7d6ef0b6e56806f22e79439f6", "urls": [ "bzz-raw://318c65fb06fe1274eb933c4159e8be95eb341f0d43345fe1c6bbacf0bfc6d072", "dweb:/ipfs/QmVpeEb9RxHcMfR4sn9Typ3V7PDJjy6nYJcTgiKqv7mzgX" ], "license": "MIT" }, "src/protocols/uniswap/UniswapV2Library.sol": { "keccak256": "0x7809ddbb45e565b2ccd8c6840987e98c336647d43d66357bbd661b343eab627d", "urls": [ "bzz-raw://6f79a573e59848ef66b0d7cc590d7eb86007fc6986654c6dd620741de618e726", "dweb:/ipfs/QmYfaKKtd6Kqz3ady9SHUyDJwRKf8co8qP6eh5QNcfdwxt" ], "license": "MIT" } }, "version": 1 }, "ast": { "absolutePath": "src/SimulatorV1.sol", "id": 30823, "exportedSymbols": { "ICurvePool": [ 31411 ], "IQuoterV2": [ 31504 ], "IUniswapV2Pair": [ 31746 ], "SafeMath": [ 30539 ], "SimulatorV1": [ 30822 ], "UniswapV2Library": [ 32281 ] }, "nodeType": "SourceUnit", "src": "32:3150:24", "nodes": [ { "id": 30541, "nodeType": "PragmaDirective", "src": "32:23:24", "nodes": [], "literals": [ "solidity", "^", "0.8", ".9" ] }, { "id": 30542, "nodeType": "ImportDirective", "src": "57:66:24", "nodes": [], "absolutePath": "lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol", "file": "openzeppelin-contracts/contracts/utils/math/SafeMath.sol", "nameLocation": "-1:-1:-1", "scope": 30823, "sourceUnit": 30540, "symbolAliases": [], "unitAlias": "" }, { "id": 30543, "nodeType": "ImportDirective", "src": "125:50:24", "nodes": [], "absolutePath": "src/protocols/uniswap/UniswapV2Library.sol", "file": "./protocols/uniswap/UniswapV2Library.sol", "nameLocation": "-1:-1:-1", "scope": 30823, "sourceUnit": 32282, "symbolAliases": [], "unitAlias": "" }, { "id": 30544, "nodeType": "ImportDirective", "src": "176:43:24", "nodes": [], "absolutePath": "src/protocols/uniswap/IQuoterV2.sol", "file": "./protocols/uniswap/IQuoterV2.sol", "nameLocation": "-1:-1:-1", "scope": 30823, "sourceUnit": 31505, "symbolAliases": [], "unitAlias": "" }, { "id": 30545, "nodeType": "ImportDirective", "src": "220:42:24", "nodes": [], "absolutePath": "src/protocols/curve/ICurvePool.sol", "file": "./protocols/curve/ICurvePool.sol", "nameLocation": "-1:-1:-1", "scope": 30823, "sourceUnit": 31412, "symbolAliases": [], "unitAlias": "" }, { "id": 30822, "nodeType": "ContractDefinition", "src": "322:2859:24", "nodes": [ { "id": 30548, "nodeType": "UsingForDirective", "src": "349:27:24", "nodes": [], "global": false, "libraryName": { "id": 30546, "name": "SafeMath", "nameLocations": [ "355:8:24" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30539, "src": "355:8:24" }, "typeName": { "id": 30547, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "368:7:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } } }, { "id": 30561, "nodeType": "StructDefinition", "src": "382:360:24", "nodes": [], "canonicalName": "SimulatorV1.SwapParams", "members": [ { "constant": false, "id": 30550, "mutability": "mutable", "name": "protocol", "nameLocation": "416:8:24", "nodeType": "VariableDeclaration", "scope": 30561, "src": "410:14:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint8", "typeString": "uint8" }, "typeName": { "id": 30549, "name": "uint8", "nodeType": "ElementaryTypeName", "src": "410:5:24", "typeDescriptions": { "typeIdentifier": "t_uint8", "typeString": "uint8" } }, "visibility": "internal" }, { "constant": false, "id": 30552, "mutability": "mutable", "name": "handler", "nameLocation": "493:7:24", "nodeType": "VariableDeclaration", "scope": 30561, "src": "485:15:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 30551, "name": "address", "nodeType": "ElementaryTypeName", "src": "485:7:24", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "visibility": "internal" }, { "constant": false, "id": 30554, "mutability": "mutable", "name": "tokenIn", "nameLocation": "572:7:24", "nodeType": "VariableDeclaration", "scope": 30561, "src": "564:15:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 30553, "name": "address", "nodeType": "ElementaryTypeName", "src": "564:7:24", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "visibility": "internal" }, { "constant": false, "id": 30556, "mutability": "mutable", "name": "tokenOut", "nameLocation": "597:8:24", "nodeType": "VariableDeclaration", "scope": 30561, "src": "589:16:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 30555, "name": "address", "nodeType": "ElementaryTypeName", "src": "589:7:24", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "visibility": "internal" }, { "constant": false, "id": 30558, "mutability": "mutable", "name": "fee", "nameLocation": "622:3:24", "nodeType": "VariableDeclaration", "scope": 30561, "src": "615:10:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint24", "typeString": "uint24" }, "typeName": { "id": 30557, "name": "uint24", "nodeType": "ElementaryTypeName", "src": "615:6:24", "typeDescriptions": { "typeIdentifier": "t_uint24", "typeString": "uint24" } }, "visibility": "internal" }, { "constant": false, "id": 30560, "mutability": "mutable", "name": "amount", "nameLocation": "670:6:24", "nodeType": "VariableDeclaration", "scope": 30561, "src": "662:14:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30559, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "662:7:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "name": "SwapParams", "nameLocation": "389:10:24", "scope": 30822, "visibility": "public" }, { "id": 30565, "nodeType": "FunctionDefinition", "src": "748:16:24", "nodes": [], "body": { "id": 30564, "nodeType": "Block", "src": "762:2:24", "nodes": [], "statements": [] }, "implemented": true, "kind": "constructor", "modifiers": [], "name": "", "nameLocation": "-1:-1:-1", "parameters": { "id": 30562, "nodeType": "ParameterList", "parameters": [], "src": "759:2:24" }, "returnParameters": { "id": 30563, "nodeType": "ParameterList", "parameters": [], "src": "762:0:24" }, "scope": 30822, "stateMutability": "nonpayable", "virtual": false, "visibility": "public" }, { "id": 30645, "nodeType": "FunctionDefinition", "src": "770:772:24", "nodes": [], "body": { "id": 30644, "nodeType": "Block", "src": "868:674:24", "nodes": [], "statements": [ { "assignments": [ 30575 ], "declarations": [ { "constant": false, "id": 30575, "mutability": "mutable", "name": "amountOut", "nameLocation": "886:9:24", "nodeType": "VariableDeclaration", "scope": 30644, "src": "878:17:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30574, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "878:7:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30576, "nodeType": "VariableDeclarationStatement", "src": "878:17:24" }, { "assignments": [ 30578 ], "declarations": [ { "constant": false, "id": 30578, "mutability": "mutable", "name": "paramsArrayLength", "nameLocation": "913:17:24", "nodeType": "VariableDeclaration", "scope": 30644, "src": "905:25:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30577, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "905:7:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30581, "initialValue": { "expression": { "id": 30579, "name": "paramsArray", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30569, "src": "933:11:24", "typeDescriptions": { "typeIdentifier": "t_array$_t_struct$_SwapParams_$30561_calldata_ptr_$dyn_calldata_ptr", "typeString": "struct SimulatorV1.SwapParams calldata[] calldata" } }, "id": 30580, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "945:6:24", "memberName": "length", "nodeType": "MemberAccess", "src": "933:18:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "VariableDeclarationStatement", "src": "905:46:24" }, { "body": { "id": 30640, "nodeType": "Block", "src": "1003:506:24", "statements": [ { "assignments": [ 30590 ], "declarations": [ { "constant": false, "id": 30590, "mutability": "mutable", "name": "params", "nameLocation": "1035:6:24", "nodeType": "VariableDeclaration", "scope": 30640, "src": "1017:24:24", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams" }, "typeName": { "id": 30589, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 30588, "name": "SwapParams", "nameLocations": [ "1017:10:24" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30561, "src": "1017:10:24" }, "referencedDeclaration": 30561, "src": "1017:10:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_storage_ptr", "typeString": "struct SimulatorV1.SwapParams" } }, "visibility": "internal" } ], "id": 30594, "initialValue": { "baseExpression": { "id": 30591, "name": "paramsArray", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30569, "src": "1044:11:24", "typeDescriptions": { "typeIdentifier": "t_array$_t_struct$_SwapParams_$30561_calldata_ptr_$dyn_calldata_ptr", "typeString": "struct SimulatorV1.SwapParams calldata[] calldata" } }, "id": 30593, "indexExpression": { "id": 30592, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30583, "src": "1056:1:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "IndexAccess", "src": "1044:14:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_calldata_ptr", "typeString": "struct SimulatorV1.SwapParams calldata" } }, "nodeType": "VariableDeclarationStatement", "src": "1017:41:24" }, { "condition": { "commonType": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "id": 30597, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 30595, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30575, "src": "1077:9:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "BinaryOperation", "operator": "==", "rightExpression": { "hexValue": "30", "id": 30596, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "1090:1:24", "typeDescriptions": { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, "value": "0" }, "src": "1077:14:24", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "falseBody": { "id": 30610, "nodeType": "Block", "src": "1157:58:24", "statements": [ { "expression": { "id": 30608, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "expression": { "id": 30604, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30590, "src": "1175:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30606, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": true, "memberLocation": "1182:6:24", "memberName": "amount", "nodeType": "MemberAccess", "referencedDeclaration": 30560, "src": "1175:13:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "id": 30607, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30575, "src": "1191:9:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "1175:25:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30609, "nodeType": "ExpressionStatement", "src": "1175:25:24" } ] }, "id": 30611, "nodeType": "IfStatement", "src": "1073:142:24", "trueBody": { "id": 30603, "nodeType": "Block", "src": "1093:58:24", "statements": [ { "expression": { "id": 30601, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 30598, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30575, "src": "1111:9:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "expression": { "id": 30599, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30590, "src": "1123:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30600, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "1130:6:24", "memberName": "amount", "nodeType": "MemberAccess", "referencedDeclaration": 30560, "src": "1123:13:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "1111:25:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30602, "nodeType": "ExpressionStatement", "src": "1111:25:24" } ] } }, { "condition": { "commonType": { "typeIdentifier": "t_uint8", "typeString": "uint8" }, "id": 30615, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "expression": { "id": 30612, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30590, "src": "1233:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30613, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "1240:8:24", "memberName": "protocol", "nodeType": "MemberAccess", "referencedDeclaration": 30550, "src": "1233:15:24", "typeDescriptions": { "typeIdentifier": "t_uint8", "typeString": "uint8" } }, "nodeType": "BinaryOperation", "operator": "==", "rightExpression": { "hexValue": "30", "id": 30614, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "1252:1:24", "typeDescriptions": { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, "value": "0" }, "src": "1233:20:24", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "falseBody": { "condition": { "commonType": { "typeIdentifier": "t_uint8", "typeString": "uint8" }, "id": 30626, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "expression": { "id": 30623, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30590, "src": "1341:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30624, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "1348:8:24", "memberName": "protocol", "nodeType": "MemberAccess", "referencedDeclaration": 30550, "src": "1341:15:24", "typeDescriptions": { "typeIdentifier": "t_uint8", "typeString": "uint8" } }, "nodeType": "BinaryOperation", "operator": "==", "rightExpression": { "hexValue": "31", "id": 30625, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "1360:1:24", "typeDescriptions": { "typeIdentifier": "t_rational_1_by_1", "typeString": "int_const 1" }, "value": "1" }, "src": "1341:20:24", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 30634, "nodeType": "IfStatement", "src": "1337:102:24", "trueBody": { "id": 30633, "nodeType": "Block", "src": "1363:76:24", "statements": [ { "expression": { "id": 30631, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 30627, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30575, "src": "1381:9:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "arguments": [ { "id": 30629, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30590, "src": "1417:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } ], "id": 30628, "name": "simulateUniswapV3SwapIn", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30743, "src": "1393:23:24", "typeDescriptions": { "typeIdentifier": "t_function_internal_nonpayable$_t_struct$_SwapParams_$30561_memory_ptr_$returns$_t_uint256_$", "typeString": "function (struct SimulatorV1.SwapParams memory) returns (uint256)" } }, "id": 30630, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1393:31:24", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "1381:43:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30632, "nodeType": "ExpressionStatement", "src": "1381:43:24" } ] } }, "id": 30635, "nodeType": "IfStatement", "src": "1229:210:24", "trueBody": { "id": 30622, "nodeType": "Block", "src": "1255:76:24", "statements": [ { "expression": { "id": 30620, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 30616, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30575, "src": "1273:9:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "arguments": [ { "id": 30618, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30590, "src": "1309:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } ], "id": 30617, "name": "simulateUniswapV2SwapIn", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30678, "src": "1285:23:24", "typeDescriptions": { "typeIdentifier": "t_function_internal_view$_t_struct$_SwapParams_$30561_memory_ptr_$returns$_t_uint256_$", "typeString": "function (struct SimulatorV1.SwapParams memory) view returns (uint256)" } }, "id": 30619, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1285:31:24", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "1273:43:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30621, "nodeType": "ExpressionStatement", "src": "1273:43:24" } ] } }, { "id": 30639, "nodeType": "UncheckedBlock", "src": "1453:46:24", "statements": [ { "expression": { "id": 30637, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "UnaryOperation", "operator": "++", "prefix": true, "src": "1481:3:24", "subExpression": { "id": 30636, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30583, "src": "1483:1:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30638, "nodeType": "ExpressionStatement", "src": "1481:3:24" } ] } ] }, "condition": { "commonType": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "id": 30587, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 30585, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30583, "src": "978:1:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "BinaryOperation", "operator": "<", "rightExpression": { "id": 30586, "name": "paramsArrayLength", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30578, "src": "982:17:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "978:21:24", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 30641, "initializationExpression": { "assignments": [ 30583 ], "declarations": [ { "constant": false, "id": 30583, "mutability": "mutable", "name": "i", "nameLocation": "975:1:24", "nodeType": "VariableDeclaration", "scope": 30641, "src": "967:9:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30582, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "967:7:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30584, "nodeType": "VariableDeclarationStatement", "src": "967:9:24" }, "nodeType": "ForStatement", "src": "962:547:24" }, { "expression": { "id": 30642, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30575, "src": "1526:9:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "functionReturnParameters": 30573, "id": 30643, "nodeType": "Return", "src": "1519:16:24" } ] }, "functionSelector": "55abbafc", "implemented": true, "kind": "function", "modifiers": [], "name": "simulateSwapIn", "nameLocation": "779:14:24", "parameters": { "id": 30570, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 30569, "mutability": "mutable", "name": "paramsArray", "nameLocation": "825:11:24", "nodeType": "VariableDeclaration", "scope": 30645, "src": "803:33:24", "stateVariable": false, "storageLocation": "calldata", "typeDescriptions": { "typeIdentifier": "t_array$_t_struct$_SwapParams_$30561_calldata_ptr_$dyn_calldata_ptr", "typeString": "struct SimulatorV1.SwapParams[]" }, "typeName": { "baseType": { "id": 30567, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 30566, "name": "SwapParams", "nameLocations": [ "803:10:24" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30561, "src": "803:10:24" }, "referencedDeclaration": 30561, "src": "803:10:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_storage_ptr", "typeString": "struct SimulatorV1.SwapParams" } }, "id": 30568, "nodeType": "ArrayTypeName", "src": "803:12:24", "typeDescriptions": { "typeIdentifier": "t_array$_t_struct$_SwapParams_$30561_storage_$dyn_storage_ptr", "typeString": "struct SimulatorV1.SwapParams[]" } }, "visibility": "internal" } ], "src": "793:49:24" }, "returnParameters": { "id": 30573, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 30572, "mutability": "mutable", "name": "", "nameLocation": "-1:-1:-1", "nodeType": "VariableDeclaration", "scope": 30645, "src": "859:7:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30571, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "859:7:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "src": "858:9:24" }, "scope": 30822, "stateMutability": "nonpayable", "virtual": false, "visibility": "public" }, { "id": 30678, "nodeType": "FunctionDefinition", "src": "1548:424:24", "nodes": [], "body": { "id": 30677, "nodeType": "Block", "src": "1661:311:24", "nodes": [], "statements": [ { "assignments": [ 30654, 30656 ], "declarations": [ { "constant": false, "id": 30654, "mutability": "mutable", "name": "reserveIn", "nameLocation": "1677:9:24", "nodeType": "VariableDeclaration", "scope": 30677, "src": "1672:14:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30653, "name": "uint", "nodeType": "ElementaryTypeName", "src": "1672:4:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" }, { "constant": false, "id": 30656, "mutability": "mutable", "name": "reserveOut", "nameLocation": "1693:10:24", "nodeType": "VariableDeclaration", "scope": 30677, "src": "1688:15:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30655, "name": "uint", "nodeType": "ElementaryTypeName", "src": "1688:4:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "id": 30666, "initialValue": { "arguments": [ { "expression": { "id": 30659, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30648, "src": "1749:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30660, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "1756:7:24", "memberName": "handler", "nodeType": "MemberAccess", "referencedDeclaration": 30552, "src": "1749:14:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, { "expression": { "id": 30661, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30648, "src": "1777:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30662, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "1784:7:24", "memberName": "tokenIn", "nodeType": "MemberAccess", "referencedDeclaration": 30554, "src": "1777:14:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, { "expression": { "id": 30663, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30648, "src": "1805:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30664, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "1812:8:24", "memberName": "tokenOut", "nodeType": "MemberAccess", "referencedDeclaration": 30556, "src": "1805:15:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" }, { "typeIdentifier": "t_address", "typeString": "address" }, { "typeIdentifier": "t_address", "typeString": "address" } ], "expression": { "id": 30657, "name": "UniswapV2Library", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 32281, "src": "1707:16:24", "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_UniswapV2Library_$32281_$", "typeString": "type(library UniswapV2Library)" } }, "id": 30658, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "1724:11:24", "memberName": "getReserves", "nodeType": "MemberAccess", "referencedDeclaration": 31994, "src": "1707:28:24", "typeDescriptions": { "typeIdentifier": "t_function_internal_view$_t_address_$_t_address_$_t_address_$returns$_t_uint256_$_t_uint256_$", "typeString": "function (address,address,address) view returns (uint256,uint256)" } }, "id": 30665, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1707:123:24", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_tuple$_t_uint256_$_t_uint256_$", "typeString": "tuple(uint256,uint256)" } }, "nodeType": "VariableDeclarationStatement", "src": "1671:159:24" }, { "expression": { "id": 30675, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 30667, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30651, "src": "1840:9:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "arguments": [ { "expression": { "id": 30670, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30648, "src": "1895:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30671, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "1902:6:24", "memberName": "amount", "nodeType": "MemberAccess", "referencedDeclaration": 30560, "src": "1895:13:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, { "id": 30672, "name": "reserveIn", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30654, "src": "1922:9:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, { "id": 30673, "name": "reserveOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30656, "src": "1945:10:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_uint256", "typeString": "uint256" }, { "typeIdentifier": "t_uint256", "typeString": "uint256" }, { "typeIdentifier": "t_uint256", "typeString": "uint256" } ], "expression": { "id": 30668, "name": "UniswapV2Library", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 32281, "src": "1852:16:24", "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_UniswapV2Library_$32281_$", "typeString": "type(library UniswapV2Library)" } }, "id": 30669, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "1869:12:24", "memberName": "getAmountOut", "nodeType": "MemberAccess", "referencedDeclaration": 32054, "src": "1852:29:24", "typeDescriptions": { "typeIdentifier": "t_function_internal_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$", "typeString": "function (uint256,uint256,uint256) pure returns (uint256)" } }, "id": 30674, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "1852:113:24", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "1840:125:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30676, "nodeType": "ExpressionStatement", "src": "1840:125:24" } ] }, "functionSelector": "76637028", "implemented": true, "kind": "function", "modifiers": [], "name": "simulateUniswapV2SwapIn", "nameLocation": "1557:23:24", "parameters": { "id": 30649, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 30648, "mutability": "mutable", "name": "params", "nameLocation": "1608:6:24", "nodeType": "VariableDeclaration", "scope": 30678, "src": "1590:24:24", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams" }, "typeName": { "id": 30647, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 30646, "name": "SwapParams", "nameLocations": [ "1590:10:24" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30561, "src": "1590:10:24" }, "referencedDeclaration": 30561, "src": "1590:10:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_storage_ptr", "typeString": "struct SimulatorV1.SwapParams" } }, "visibility": "internal" } ], "src": "1580:40:24" }, "returnParameters": { "id": 30652, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 30651, "mutability": "mutable", "name": "amountOut", "nameLocation": "1650:9:24", "nodeType": "VariableDeclaration", "scope": 30678, "src": "1642:17:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30650, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "1642:7:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "src": "1641:19:24" }, "scope": 30822, "stateMutability": "view", "virtual": false, "visibility": "public" }, { "id": 30743, "nodeType": "FunctionDefinition", "src": "1978:534:24", "nodes": [], "body": { "id": 30742, "nodeType": "Block", "src": "2086:426:24", "nodes": [], "statements": [ { "assignments": [ 30688 ], "declarations": [ { "constant": false, "id": 30688, "mutability": "mutable", "name": "quoter", "nameLocation": "2106:6:24", "nodeType": "VariableDeclaration", "scope": 30742, "src": "2096:16:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_contract$_IQuoterV2_$31504", "typeString": "contract IQuoterV2" }, "typeName": { "id": 30687, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 30686, "name": "IQuoterV2", "nameLocations": [ "2096:9:24" ], "nodeType": "IdentifierPath", "referencedDeclaration": 31504, "src": "2096:9:24" }, "referencedDeclaration": 31504, "src": "2096:9:24", "typeDescriptions": { "typeIdentifier": "t_contract$_IQuoterV2_$31504", "typeString": "contract IQuoterV2" } }, "visibility": "internal" } ], "id": 30693, "initialValue": { "arguments": [ { "expression": { "id": 30690, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30681, "src": "2125:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30691, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2132:7:24", "memberName": "handler", "nodeType": "MemberAccess", "referencedDeclaration": 30552, "src": "2125:14:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" } ], "id": 30689, "name": "IQuoterV2", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31504, "src": "2115:9:24", "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_IQuoterV2_$31504_$", "typeString": "type(contract IQuoterV2)" } }, "id": 30692, "isConstant": false, "isLValue": false, "isPure": false, "kind": "typeConversion", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "2115:25:24", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_contract$_IQuoterV2_$31504", "typeString": "contract IQuoterV2" } }, "nodeType": "VariableDeclarationStatement", "src": "2096:44:24" }, { "assignments": [ 30698 ], "declarations": [ { "constant": false, "id": 30698, "mutability": "mutable", "name": "quoterParams", "nameLocation": "2195:12:24", "nodeType": "VariableDeclaration", "scope": 30742, "src": "2150:57:24", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_struct$_QuoteExactInputSingleParams_$31444_memory_ptr", "typeString": "struct IQuoterV2.QuoteExactInputSingleParams" }, "typeName": { "id": 30697, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 30696, "name": "IQuoterV2.QuoteExactInputSingleParams", "nameLocations": [ "2150:9:24", "2160:27:24" ], "nodeType": "IdentifierPath", "referencedDeclaration": 31444, "src": "2150:37:24" }, "referencedDeclaration": 31444, "src": "2150:37:24", "typeDescriptions": { "typeIdentifier": "t_struct$_QuoteExactInputSingleParams_$31444_storage_ptr", "typeString": "struct IQuoterV2.QuoteExactInputSingleParams" } }, "visibility": "internal" } ], "id": 30699, "nodeType": "VariableDeclarationStatement", "src": "2150:57:24" }, { "expression": { "id": 30705, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "expression": { "id": 30700, "name": "quoterParams", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30698, "src": "2217:12:24", "typeDescriptions": { "typeIdentifier": "t_struct$_QuoteExactInputSingleParams_$31444_memory_ptr", "typeString": "struct IQuoterV2.QuoteExactInputSingleParams memory" } }, "id": 30702, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": true, "memberLocation": "2230:7:24", "memberName": "tokenIn", "nodeType": "MemberAccess", "referencedDeclaration": 31435, "src": "2217:20:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "expression": { "id": 30703, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30681, "src": "2240:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30704, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2247:7:24", "memberName": "tokenIn", "nodeType": "MemberAccess", "referencedDeclaration": 30554, "src": "2240:14:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "src": "2217:37:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "id": 30706, "nodeType": "ExpressionStatement", "src": "2217:37:24" }, { "expression": { "id": 30712, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "expression": { "id": 30707, "name": "quoterParams", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30698, "src": "2264:12:24", "typeDescriptions": { "typeIdentifier": "t_struct$_QuoteExactInputSingleParams_$31444_memory_ptr", "typeString": "struct IQuoterV2.QuoteExactInputSingleParams memory" } }, "id": 30709, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": true, "memberLocation": "2277:8:24", "memberName": "tokenOut", "nodeType": "MemberAccess", "referencedDeclaration": 31437, "src": "2264:21:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "expression": { "id": 30710, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30681, "src": "2288:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30711, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2295:8:24", "memberName": "tokenOut", "nodeType": "MemberAccess", "referencedDeclaration": 30556, "src": "2288:15:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "src": "2264:39:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "id": 30713, "nodeType": "ExpressionStatement", "src": "2264:39:24" }, { "expression": { "id": 30719, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "expression": { "id": 30714, "name": "quoterParams", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30698, "src": "2313:12:24", "typeDescriptions": { "typeIdentifier": "t_struct$_QuoteExactInputSingleParams_$31444_memory_ptr", "typeString": "struct IQuoterV2.QuoteExactInputSingleParams memory" } }, "id": 30716, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": true, "memberLocation": "2326:8:24", "memberName": "amountIn", "nodeType": "MemberAccess", "referencedDeclaration": 31439, "src": "2313:21:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "expression": { "id": 30717, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30681, "src": "2337:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30718, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2344:6:24", "memberName": "amount", "nodeType": "MemberAccess", "referencedDeclaration": 30560, "src": "2337:13:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "2313:37:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30720, "nodeType": "ExpressionStatement", "src": "2313:37:24" }, { "expression": { "id": 30726, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "expression": { "id": 30721, "name": "quoterParams", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30698, "src": "2360:12:24", "typeDescriptions": { "typeIdentifier": "t_struct$_QuoteExactInputSingleParams_$31444_memory_ptr", "typeString": "struct IQuoterV2.QuoteExactInputSingleParams memory" } }, "id": 30723, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": true, "memberLocation": "2373:3:24", "memberName": "fee", "nodeType": "MemberAccess", "referencedDeclaration": 31441, "src": "2360:16:24", "typeDescriptions": { "typeIdentifier": "t_uint24", "typeString": "uint24" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "expression": { "id": 30724, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30681, "src": "2379:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30725, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2386:3:24", "memberName": "fee", "nodeType": "MemberAccess", "referencedDeclaration": 30558, "src": "2379:10:24", "typeDescriptions": { "typeIdentifier": "t_uint24", "typeString": "uint24" } }, "src": "2360:29:24", "typeDescriptions": { "typeIdentifier": "t_uint24", "typeString": "uint24" } }, "id": 30727, "nodeType": "ExpressionStatement", "src": "2360:29:24" }, { "expression": { "id": 30732, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "expression": { "id": 30728, "name": "quoterParams", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30698, "src": "2399:12:24", "typeDescriptions": { "typeIdentifier": "t_struct$_QuoteExactInputSingleParams_$31444_memory_ptr", "typeString": "struct IQuoterV2.QuoteExactInputSingleParams memory" } }, "id": 30730, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": true, "memberLocation": "2412:17:24", "memberName": "sqrtPriceLimitX96", "nodeType": "MemberAccess", "referencedDeclaration": 31443, "src": "2399:30:24", "typeDescriptions": { "typeIdentifier": "t_uint160", "typeString": "uint160" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "hexValue": "30", "id": 30731, "isConstant": false, "isLValue": false, "isPure": true, "kind": "number", "lValueRequested": false, "nodeType": "Literal", "src": "2432:1:24", "typeDescriptions": { "typeIdentifier": "t_rational_0_by_1", "typeString": "int_const 0" }, "value": "0" }, "src": "2399:34:24", "typeDescriptions": { "typeIdentifier": "t_uint160", "typeString": "uint160" } }, "id": 30733, "nodeType": "ExpressionStatement", "src": "2399:34:24" }, { "expression": { "id": 30740, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "components": [ { "id": 30734, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30684, "src": "2444:9:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, null, null, null ], "id": 30735, "isConstant": false, "isInlineArray": false, "isLValue": true, "isPure": false, "lValueRequested": true, "nodeType": "TupleExpression", "src": "2443:17:24", "typeDescriptions": { "typeIdentifier": "t_tuple$_t_uint256_$__$__$__$", "typeString": "tuple(uint256,,,)" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "arguments": [ { "id": 30738, "name": "quoterParams", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30698, "src": "2492:12:24", "typeDescriptions": { "typeIdentifier": "t_struct$_QuoteExactInputSingleParams_$31444_memory_ptr", "typeString": "struct IQuoterV2.QuoteExactInputSingleParams memory" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_struct$_QuoteExactInputSingleParams_$31444_memory_ptr", "typeString": "struct IQuoterV2.QuoteExactInputSingleParams memory" } ], "expression": { "id": 30736, "name": "quoter", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30688, "src": "2463:6:24", "typeDescriptions": { "typeIdentifier": "t_contract$_IQuoterV2_$31504", "typeString": "contract IQuoterV2" } }, "id": 30737, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "2470:21:24", "memberName": "quoteExactInputSingle", "nodeType": "MemberAccess", "referencedDeclaration": 31459, "src": "2463:28:24", "typeDescriptions": { "typeIdentifier": "t_function_external_nonpayable$_t_struct$_QuoteExactInputSingleParams_$31444_memory_ptr_$returns$_t_uint256_$_t_uint160_$_t_uint32_$_t_uint256_$", "typeString": "function (struct IQuoterV2.QuoteExactInputSingleParams memory) external returns (uint256,uint160,uint32,uint256)" } }, "id": 30739, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "2463:42:24", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_tuple$_t_uint256_$_t_uint160_$_t_uint32_$_t_uint256_$", "typeString": "tuple(uint256,uint160,uint32,uint256)" } }, "src": "2443:62:24", "typeDescriptions": { "typeIdentifier": "t_tuple$__$", "typeString": "tuple()" } }, "id": 30741, "nodeType": "ExpressionStatement", "src": "2443:62:24" } ] }, "functionSelector": "8e888ccc", "implemented": true, "kind": "function", "modifiers": [], "name": "simulateUniswapV3SwapIn", "nameLocation": "1987:23:24", "parameters": { "id": 30682, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 30681, "mutability": "mutable", "name": "params", "nameLocation": "2038:6:24", "nodeType": "VariableDeclaration", "scope": 30743, "src": "2020:24:24", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams" }, "typeName": { "id": 30680, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 30679, "name": "SwapParams", "nameLocations": [ "2020:10:24" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30561, "src": "2020:10:24" }, "referencedDeclaration": 30561, "src": "2020:10:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_storage_ptr", "typeString": "struct SimulatorV1.SwapParams" } }, "visibility": "internal" } ], "src": "2010:40:24" }, "returnParameters": { "id": 30685, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 30684, "mutability": "mutable", "name": "amountOut", "nameLocation": "2075:9:24", "nodeType": "VariableDeclaration", "scope": 30743, "src": "2067:17:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30683, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "2067:7:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "src": "2066:19:24" }, "scope": 30822, "stateMutability": "nonpayable", "virtual": false, "visibility": "public" }, { "id": 30821, "nodeType": "FunctionDefinition", "src": "2518:661:24", "nodes": [], "body": { "id": 30820, "nodeType": "Block", "src": "2622:557:24", "nodes": [], "statements": [ { "assignments": [ 30753 ], "declarations": [ { "constant": false, "id": 30753, "mutability": "mutable", "name": "pool", "nameLocation": "2643:4:24", "nodeType": "VariableDeclaration", "scope": 30820, "src": "2632:15:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_contract$_ICurvePool_$31411", "typeString": "contract ICurvePool" }, "typeName": { "id": 30752, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 30751, "name": "ICurvePool", "nameLocations": [ "2632:10:24" ], "nodeType": "IdentifierPath", "referencedDeclaration": 31411, "src": "2632:10:24" }, "referencedDeclaration": 31411, "src": "2632:10:24", "typeDescriptions": { "typeIdentifier": "t_contract$_ICurvePool_$31411", "typeString": "contract ICurvePool" } }, "visibility": "internal" } ], "id": 30758, "initialValue": { "arguments": [ { "expression": { "id": 30755, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30746, "src": "2661:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30756, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2668:7:24", "memberName": "handler", "nodeType": "MemberAccess", "referencedDeclaration": 30552, "src": "2661:14:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_address", "typeString": "address" } ], "id": 30754, "name": "ICurvePool", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 31411, "src": "2650:10:24", "typeDescriptions": { "typeIdentifier": "t_type$_t_contract$_ICurvePool_$31411_$", "typeString": "type(contract ICurvePool)" } }, "id": 30757, "isConstant": false, "isLValue": false, "isPure": false, "kind": "typeConversion", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "2650:26:24", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_contract$_ICurvePool_$31411", "typeString": "contract ICurvePool" } }, "nodeType": "VariableDeclarationStatement", "src": "2632:44:24" }, { "assignments": [ 30760 ], "declarations": [ { "constant": false, "id": 30760, "mutability": "mutable", "name": "i", "nameLocation": "2694:1:24", "nodeType": "VariableDeclaration", "scope": 30820, "src": "2687:8:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" }, "typeName": { "id": 30759, "name": "int128", "nodeType": "ElementaryTypeName", "src": "2687:6:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "visibility": "internal" } ], "id": 30761, "nodeType": "VariableDeclarationStatement", "src": "2687:8:24" }, { "assignments": [ 30763 ], "declarations": [ { "constant": false, "id": 30763, "mutability": "mutable", "name": "j", "nameLocation": "2712:1:24", "nodeType": "VariableDeclaration", "scope": 30820, "src": "2705:8:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" }, "typeName": { "id": 30762, "name": "int128", "nodeType": "ElementaryTypeName", "src": "2705:6:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "visibility": "internal" } ], "id": 30764, "nodeType": "VariableDeclarationStatement", "src": "2705:8:24" }, { "assignments": [ 30766 ], "declarations": [ { "constant": false, "id": 30766, "mutability": "mutable", "name": "coinIdx", "nameLocation": "2731:7:24", "nodeType": "VariableDeclaration", "scope": 30820, "src": "2724:14:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" }, "typeName": { "id": 30765, "name": "int128", "nodeType": "ElementaryTypeName", "src": "2724:6:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "visibility": "internal" } ], "id": 30767, "nodeType": "VariableDeclarationStatement", "src": "2724:14:24" }, { "body": { "id": 30808, "nodeType": "Block", "src": "2764:354:24", "statements": [ { "assignments": [ 30772 ], "declarations": [ { "constant": false, "id": 30772, "mutability": "mutable", "name": "coin", "nameLocation": "2786:4:24", "nodeType": "VariableDeclaration", "scope": 30808, "src": "2778:12:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" }, "typeName": { "id": 30771, "name": "address", "nodeType": "ElementaryTypeName", "src": "2778:7:24", "stateMutability": "nonpayable", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "visibility": "internal" } ], "id": 30777, "initialValue": { "arguments": [ { "id": 30775, "name": "coinIdx", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30766, "src": "2804:7:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_int128", "typeString": "int128" } ], "expression": { "id": 30773, "name": "pool", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30753, "src": "2793:4:24", "typeDescriptions": { "typeIdentifier": "t_contract$_ICurvePool_$31411", "typeString": "contract ICurvePool" } }, "id": 30774, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "2798:5:24", "memberName": "coins", "nodeType": "MemberAccess", "referencedDeclaration": 31346, "src": "2793:10:24", "typeDescriptions": { "typeIdentifier": "t_function_external_nonpayable$_t_int128_$returns$_t_address_$", "typeString": "function (int128) external returns (address)" } }, "id": 30776, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "2793:19:24", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "VariableDeclarationStatement", "src": "2778:34:24" }, { "condition": { "commonType": { "typeIdentifier": "t_address", "typeString": "address" }, "id": 30781, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 30778, "name": "coin", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30772, "src": "2831:4:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "BinaryOperation", "operator": "==", "rightExpression": { "expression": { "id": 30779, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30746, "src": "2839:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30780, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2846:7:24", "memberName": "tokenIn", "nodeType": "MemberAccess", "referencedDeclaration": 30554, "src": "2839:14:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "src": "2831:22:24", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "falseBody": { "condition": { "commonType": { "typeIdentifier": "t_address", "typeString": "address" }, "id": 30790, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 30787, "name": "coin", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30772, "src": "2909:4:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "nodeType": "BinaryOperation", "operator": "==", "rightExpression": { "expression": { "id": 30788, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30746, "src": "2917:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30789, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "2924:8:24", "memberName": "tokenOut", "nodeType": "MemberAccess", "referencedDeclaration": 30556, "src": "2917:15:24", "typeDescriptions": { "typeIdentifier": "t_address", "typeString": "address" } }, "src": "2909:23:24", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 30796, "nodeType": "IfStatement", "src": "2905:73:24", "trueBody": { "id": 30795, "nodeType": "Block", "src": "2934:44:24", "statements": [ { "expression": { "id": 30793, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 30791, "name": "j", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30763, "src": "2952:1:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "id": 30792, "name": "coinIdx", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30766, "src": "2956:7:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "src": "2952:11:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "id": 30794, "nodeType": "ExpressionStatement", "src": "2952:11:24" } ] } }, "id": 30797, "nodeType": "IfStatement", "src": "2827:151:24", "trueBody": { "id": 30786, "nodeType": "Block", "src": "2855:44:24", "statements": [ { "expression": { "id": 30784, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 30782, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30760, "src": "2873:1:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "id": 30783, "name": "coinIdx", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30766, "src": "2877:7:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "src": "2873:11:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "id": 30785, "nodeType": "ExpressionStatement", "src": "2873:11:24" } ] } }, { "condition": { "commonType": { "typeIdentifier": "t_int128", "typeString": "int128" }, "id": 30800, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 30798, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30760, "src": "2996:1:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "nodeType": "BinaryOperation", "operator": "!=", "rightExpression": { "id": 30799, "name": "j", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30763, "src": "3001:1:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "src": "2996:6:24", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 30803, "nodeType": "IfStatement", "src": "2992:50:24", "trueBody": { "id": 30802, "nodeType": "Block", "src": "3004:38:24", "statements": [ { "id": 30801, "nodeType": "Break", "src": "3022:5:24" } ] } }, { "id": 30807, "nodeType": "UncheckedBlock", "src": "3056:52:24", "statements": [ { "expression": { "id": 30805, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "nodeType": "UnaryOperation", "operator": "++", "prefix": true, "src": "3084:9:24", "subExpression": { "id": 30804, "name": "coinIdx", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30766, "src": "3086:7:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "id": 30806, "nodeType": "ExpressionStatement", "src": "3084:9:24" } ] } ] }, "condition": { "commonType": { "typeIdentifier": "t_int128", "typeString": "int128" }, "id": 30770, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftExpression": { "id": 30768, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30760, "src": "2756:1:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "nodeType": "BinaryOperation", "operator": "==", "rightExpression": { "id": 30769, "name": "j", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30763, "src": "2761:1:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, "src": "2756:6:24", "typeDescriptions": { "typeIdentifier": "t_bool", "typeString": "bool" } }, "id": 30809, "nodeType": "WhileStatement", "src": "2749:369:24" }, { "expression": { "id": 30818, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "leftHandSide": { "id": 30810, "name": "amountOut", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30749, "src": "3128:9:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "nodeType": "Assignment", "operator": "=", "rightHandSide": { "arguments": [ { "id": 30813, "name": "i", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30760, "src": "3152:1:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, { "id": 30814, "name": "j", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30763, "src": "3155:1:24", "typeDescriptions": { "typeIdentifier": "t_int128", "typeString": "int128" } }, { "expression": { "id": 30815, "name": "params", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30746, "src": "3158:6:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams memory" } }, "id": 30816, "isConstant": false, "isLValue": true, "isPure": false, "lValueRequested": false, "memberLocation": "3165:6:24", "memberName": "amount", "nodeType": "MemberAccess", "referencedDeclaration": 30560, "src": "3158:13:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } } ], "expression": { "argumentTypes": [ { "typeIdentifier": "t_int128", "typeString": "int128" }, { "typeIdentifier": "t_int128", "typeString": "int128" }, { "typeIdentifier": "t_uint256", "typeString": "uint256" } ], "expression": { "id": 30811, "name": "pool", "nodeType": "Identifier", "overloadedDeclarations": [], "referencedDeclaration": 30753, "src": "3140:4:24", "typeDescriptions": { "typeIdentifier": "t_contract$_ICurvePool_$31411", "typeString": "contract ICurvePool" } }, "id": 30812, "isConstant": false, "isLValue": false, "isPure": false, "lValueRequested": false, "memberLocation": "3145:6:24", "memberName": "get_dy", "nodeType": "MemberAccess", "referencedDeclaration": 31231, "src": "3140:11:24", "typeDescriptions": { "typeIdentifier": "t_function_external_nonpayable$_t_int128_$_t_int128_$_t_uint256_$returns$_t_uint256_$", "typeString": "function (int128,int128,uint256) external returns (uint256)" } }, "id": 30817, "isConstant": false, "isLValue": false, "isPure": false, "kind": "functionCall", "lValueRequested": false, "nameLocations": [], "names": [], "nodeType": "FunctionCall", "src": "3140:32:24", "tryCall": false, "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "src": "3128:44:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "id": 30819, "nodeType": "ExpressionStatement", "src": "3128:44:24" } ] }, "functionSelector": "88dbf051", "implemented": true, "kind": "function", "modifiers": [], "name": "simulateCurveSwapIn", "nameLocation": "2527:19:24", "parameters": { "id": 30747, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 30746, "mutability": "mutable", "name": "params", "nameLocation": "2574:6:24", "nodeType": "VariableDeclaration", "scope": 30821, "src": "2556:24:24", "stateVariable": false, "storageLocation": "memory", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_memory_ptr", "typeString": "struct SimulatorV1.SwapParams" }, "typeName": { "id": 30745, "nodeType": "UserDefinedTypeName", "pathNode": { "id": 30744, "name": "SwapParams", "nameLocations": [ "2556:10:24" ], "nodeType": "IdentifierPath", "referencedDeclaration": 30561, "src": "2556:10:24" }, "referencedDeclaration": 30561, "src": "2556:10:24", "typeDescriptions": { "typeIdentifier": "t_struct$_SwapParams_$30561_storage_ptr", "typeString": "struct SimulatorV1.SwapParams" } }, "visibility": "internal" } ], "src": "2546:40:24" }, "returnParameters": { "id": 30750, "nodeType": "ParameterList", "parameters": [ { "constant": false, "id": 30749, "mutability": "mutable", "name": "amountOut", "nameLocation": "2611:9:24", "nodeType": "VariableDeclaration", "scope": 30821, "src": "2603:17:24", "stateVariable": false, "storageLocation": "default", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" }, "typeName": { "id": 30748, "name": "uint256", "nodeType": "ElementaryTypeName", "src": "2603:7:24", "typeDescriptions": { "typeIdentifier": "t_uint256", "typeString": "uint256" } }, "visibility": "internal" } ], "src": "2602:19:24" }, "scope": 30822, "stateMutability": "nonpayable", "virtual": false, "visibility": "public" } ], "abstract": false, "baseContracts": [], "canonicalName": "SimulatorV1", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, "linearizedBaseContracts": [ 30822 ], "name": "SimulatorV1", "nameLocation": "331:11:24", "scope": 30823, "usedErrors": [], "usedEvents": [] } ], "license": "MIT" }, "id": 24 } ================================================ FILE: simulation/__init__.py ================================================ from simulation.uniswap_v2 import * from simulation.uniswap_v3 import * from simulation.online_simulator import * ================================================ FILE: simulation/online_simulator.py ================================================ import os import json from web3 import Web3 from pathlib import Path from typing import Any, Dict, List PROTOCOL_TO_ID = { 'uniswap_v2': 0, 'sushiswap_v2': 0, 'uniswap_v3': 1, 'sushiswap_v3': 1, } DIR = os.path.dirname(os.path.abspath(__file__)) ABI_FILE_PATH = Path(DIR) / 'SimulatorV1.json' SIMULATOR_ABI = json.load(open(ABI_FILE_PATH, 'r'))['abi'] class OnlineSimulator: """ This class will be used temporarily before an offline simulator is built. Using an online simulator is easy, but comes with a cost of latency. """ def __init__(self, rpc_endpoints: Dict[str, str], tokens: Dict[str, Dict[str, List[str or int]]], pools: List[Dict[str, Any]], contracts: Dict[str, str], handlers: Dict[str, Dict[str, str]]): """ :param rpc_endpoints: refer to data.dex.DEX :param tokens: refer to data.dex.DEX :param pools: refer to dadta.dex.DEX :param contracts: the dict of address of SimulatorV1 contract deployed ex) {'ethereum': '
', 'polygon': '
', ... } :param handlers: dict of handler addresses for uniswap_v2, sushiswap_v2, uniswap_v3, sushiswap_v3, etc... For simulations an Uniswap V2 variant uses Factory, and an Uniswap V3 variant uses QuoterV2 to simulate swaps. ex) {'ethereum': {'uniswap_v2': '', ... }, ... } """ self.rpc_endpoints = rpc_endpoints self.tokens = tokens self.pools = pools self.contracts = contracts self.handlers = handlers # extract keys from tokens, pools self.chains_list = sorted(list(tokens.keys())) self.exchanges_list = sorted(set([p['exchange'] for p in pools])) tokens_list = [] for exchange, tokens_dict in tokens.items(): tokens_list.extend(list(tokens_dict.keys())) self.tokens_list = sorted(list(set(tokens_list))) # map chains, exchanges, tokens to int id value # this is used to map chains/exchanges/tokens to numpy array index values self.chain_to_id = {k: i for i, k in enumerate(self.chains_list)} self.exchange_to_id = {k: i for i, k in enumerate(self.exchanges_list)} self.token_to_id = {k: i for i, k in enumerate(self.tokens_list)} self.web3 = {k: Web3(Web3.HTTPProvider(v)) for k, v in rpc_endpoints.items()} self.sim = { chain: self.web3[chain].eth.contract(address=self.contracts[chain], abi=SIMULATOR_ABI) for chain in self.chains_list } def make_params(self, amount_in: float, buy_path: List[List[int]], sell_path: List[List[int]], buy_pools: List[int], sell_pools: List[int]) -> List[Dict[str, Any]]: params = [] params.extend(self._make_buy_params(amount_in, buy_path, buy_pools)) params.extend(self._make_sell_params(sell_path, sell_pools)) return params def _make_buy_params(self, amount_in: float, path: List[List[int]], pools: List[int]): params_list = [] for i in range(len(path)): _path = path[i] if not sum(_path): continue _pool_idx = pools[i] pool = self.pools[_pool_idx] chain = pool['chain'] exchange = pool['exchange'] version = pool['version'] exchange_key = f'{exchange}_v{version}' token_in = self.tokens_list[_path[2]] token_out = self.tokens_list[_path[3]] amount_in_scaled = amount_in if i == 0 else 0 params = { 'protocol': PROTOCOL_TO_ID[exchange_key], 'handler': self.handlers[chain][exchange_key], 'tokenIn': self.tokens[chain][token_in][0], 'tokenOut': self.tokens[chain][token_out][0], 'fee': pool['fee'], 'amount': int(amount_in_scaled), } params_list.append(params) return params_list def _make_sell_params(self, path: List[List[int]], pools: List[int]): params_list = [] for i in range(len(path)): _path = path[i] if not sum(_path): continue _pool_idx = pools[i] pool = self.pools[_pool_idx] chain = pool['chain'] exchange = pool['exchange'] version = pool['version'] exchange_key = f'{exchange}_v{version}' # not the index difference with buy_params token_in = self.tokens_list[_path[3]] token_out = self.tokens_list[_path[2]] params = { 'protocol': PROTOCOL_TO_ID[exchange_key], 'handler': self.handlers[chain][exchange_key], 'tokenIn': self.tokens[chain][token_in][0], 'tokenOut': self.tokens[chain][token_out][0], 'fee': pool['fee'], 'amount': 0, # no need to set amount } params_list.append(params) return list(reversed(params_list)) def simulate(self, chain: str, params: List[Dict[str, Any]]) -> int: return self.sim[chain].functions.simulateSwapIn(params).call() if __name__ == '__main__': import os from dotenv import load_dotenv from configs import RPC_ENDPOINTS from addresses.ethereum import TOKENS, POOLS, SIMULATION_HANDLERS load_dotenv(override=True) ETHEREUM_SIMULATOR_ADDRESS = os.getenv('ETHEREUM_SIMULATOR_ADDRESS') chain = 'ethereum' rpc_endpoints = {chain: RPC_ENDPOINTS[chain]} tokens = {chain: TOKENS} pools = [pool for pool in POOLS if pool['chain'] == chain] contracts = {chain: ETHEREUM_SIMULATOR_ADDRESS} handlers = {chain: SIMULATION_HANDLERS} sim = OnlineSimulator(rpc_endpoints, tokens, pools, contracts, handlers) """ ETH/USDT - Buy: USDT -> ETH - Sell: ETH -> USDT Buy, sell should work like CEXs """ for i in range(900, 1300, 100): amount_in = i * 10 ** 6 print('==========') print('Amount in: ', amount_in) buy_path = [[0, 1, 5, 2, 1], [0, 0, 0, 0, 0]] sell_path = [[0, 0, 5, 2, 1], [0, 0, 0, 0, 0]] buy_pools = [0] sell_pools = [9] params = sim.make_params(amount_in, buy_path, sell_path, buy_pools, sell_pools) for param in params: print(param) """ SUS3ETHUSDT/UNI3ETHUSDT - Buy: SUS3ETHUSDT - Sell: UNI3ETHUSDT Output: {'protocol': 1, 'handler': '0x64e8802FE490fa7cc61d3463958199161Bb608A7', 'tokenIn': '0xdAC17F958D2ee523a2206206994597C13D831ec7', 'tokenOut': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 'fee': 500, 'amount': 20000000000} {'protocol': 1, 'handler': '0x61fFE014bA17989E743c5F6cB21bF9697530B21e', 'tokenIn': '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 'tokenOut': '0xdAC17F958D2ee523a2206206994597C13D831ec7', 'fee': 500, 'amount': 0} """ simulated_amount_out = sim.simulate(chain, params) print(f'Simulated amount out: {simulated_amount_out / 10 ** 6} USDT') simulated_profit_in_usdt = (simulated_amount_out - amount_in) / 10 ** 6 print(f'Simulated profit: {simulated_profit_in_usdt} USDT') ================================================ FILE: simulation/uniswap_v2.py ================================================ class UniswapV2Simulator: def __init__(self): pass def reserves_to_price(self, reserve0: float, reserve1: float, decimals0: float, decimals1: float, token0_in: bool): """ Returns the price quote of Uniswap V2 variant pools This is a simple quote, thus, does not account for price impact To get the amount out of amount in, use UniswapV2Simulator.get_amount_out for consideration of both fees and price impact """ price = reserve1 / reserve0 * 10 ** (decimals0 - decimals1) return price if token0_in else 1 / price def get_amount_out(self, amount_in: float, reserve_in: float, reserve_out: float, fee: float = 3000): """ Fee in Uniswap V2 variants are 0.3% However, for variants that have different fee rates, fee can be overrided fee in get_amount_out, get_amount_in are used in the format that is saved into storage_array from dex.DEX class for consistency with Uniswap V3 variant pools """ fee = fee // 1000 amount_in_with_fee = amount_in * (1000 - fee) numerator = amount_in_with_fee * reserve_out denominator = (reserve_in * 1000) + amount_in_with_fee return int(numerator / denominator) def get_amount_in(self, amount_out: float, reserve_in: float, reserve_out: float, fee: float = 3000): fee = fee // 1000 numerator = reserve_in * amount_out * 1000 denominator = (reserve_out - amount_out) * (1000 - fee) return int(numerator / denominator + 1) def get_max_amount_in(self, reserve0: float, reserve1: float, decimals0: float, decimals1: float, fee: float, token0_in: bool, max_amount_in: float, step_size: float, slippage_tolerance_lower: float, slippage_tolerance_upper: float) -> float: """ Calculates the maximum amount_in we can swap to get amount_out This method accounts for both: 1. fee, 2. price impact Also, we calculate the price quote using reserves and use that price to account for slippage tolerance We make sure that: amount_out >= price_quote * (1 - slippage_tolerance) This method uses binary search to find the optimized amount_in value To reduce the search space, we pre-set values such as: max_amount_in, step_size, slippage_tolerance_lower/upper * Slippage tips: 1. Setting slippage_tolerance_lower: 0, slippage_tolerance_upper: 0.001 will find the amount_in with a slippage below 0.1% --> this method is faster 2. However, if you want to fine tune your amount_in, you should set the tolerance level like: slippage_tolerance_lower: 0.0009, slippage_tolerance_upper: 0.001 :param max_amount_in: the max_amount_in used in binary search :param step_size: the order step_size. ex) 0.01, 0.1, 1, 10, etc... :param slippage_tolerance_lower: 0.01 (1%), 0.005 (0.5%), ... :param slippage_tolerance_upper: 0.01 (1%), ... """ fee_pct = fee / 10000.0 / 100.0 price_quote = self.reserves_to_price(reserve0, reserve1, decimals0, decimals1, token0_in) price_quote = price_quote * (1 - fee_pct) if token0_in: decimal_in, decimal_out = decimals0, decimals1 reserve_in, reserve_out = reserve0, reserve1 else: decimal_in, decimal_out = decimals1, decimals0 reserve_in, reserve_out = reserve1, reserve0 optimized_in = 0 left = 0 right = max_amount_in max_amount_out = self.get_amount_out(right * (10 ** decimal_in), reserve_in, reserve_out, fee) amount_out_rate = max_amount_out / right / (10 ** decimal_out) slippage = (price_quote - amount_out_rate) / price_quote if slippage < slippage_tolerance_lower: """ If the maximum amount_in value is within the slippage tolerance level, we simply return that value """ optimized_in = right else: while left <= right: mid = ((left + right) / 2) // step_size / (1 / step_size) amount_out = self.get_amount_out(mid * (10 ** decimal_in), reserve_in, reserve_out, fee) amount_out_rate = amount_out / mid / (10 ** decimal_out) slippage = (price_quote - amount_out_rate) / price_quote if slippage_tolerance_lower <= slippage <= slippage_tolerance_upper: optimized_in = mid break else: if slippage < slippage_tolerance_lower: left = mid else: right = mid return optimized_in if __name__ == '__main__': pass ================================================ FILE: simulation/uniswap_v3.py ================================================ import math import numpy as np class UniswapV3Simulator: """ For the time being, V3 simulator limits swap simulations to single tick swaps for simplicity This is a design to enforce less price impact on swap actions. It also makes sense to limit swaps to single tick swaps, because that is how arbitrage/market making is done in most cases using limit orders. Strategies don't utilize market orders (in the case of CEXs) or multi-tick swaps that much when slippage is an important factor in profitability. TODO: Add TickMap and TickMath to support simulation of multi-tick swaps However, I mark this as TODO. Market orders / Multi-tick swaps can help to increase order size and reduce leg outs, so this is a feature that should be implemented in the future. * Reference: https://blog.uniswap.org/uniswap-v3-math-primer """ def __init__(self): pass def sqrtx96_to_tick(self, sqrtx96: float): return math.floor( math.log( sqrtx96 * 2 ** (-96), math.sqrt(1.0001) ) ) def sqrtx96_to_price(self, sqrtx96: float, decimals0: int, decimals1: int, token0_in: bool): """ Returns the quote price of buying token_out with 1 token_in - token_in_is_token0 == true: price of buying token1 with 1 token0 - token_in_is_token1 == false: price of buying token0 with 1 token1 """ price = ((sqrtx96 / (2 ** 96)) ** 2) * (10 ** (decimals0 - decimals1)) return price if token0_in else 1 / price def tick_to_price(self, tick: float or np.ndarray, decimals0: float, decimals1: float) -> float or np.ndarray: return (1.0001 ** tick) * (10 ** (decimals0 - decimals1)) def tick_to_price_range(self, current_tick: float, tick_spacing: float, decimals0: float, decimals1: float, token0_in: bool): """ Returns the tick price range of a single tick """ lower_tick = tick_spacing * (current_tick // tick_spacing) upper_tick = tick_spacing * (current_tick // tick_spacing + 1) ticks = np.array([lower_tick, upper_tick]) price_range = self.tick_to_price(ticks, decimals0, decimals1) return price_range if token0_in else (1 / price_range)[::-1] def get_amount_out(self): pass def get_amount_in(self): pass if __name__ == '__main__': pass ================================================ FILE: strategies/dex_arb_base.py ================================================ import os import time import asyncio import datetime import aioprocessing from functools import partial from dotenv import load_dotenv from multiprocessing import Process from typing import Any, Dict, Optional, List from configs import * from execution import DexOrder from data import DEX, DexStream from simulation import OnlineSimulator from external import InfluxDB, Telegram load_dotenv(override=True) FLASHBOTS_SIGNING_KEY = os.getenv('FLASHBOTS_SIGNING_KEY') FLASHBOTS_PRIVATE_KEY = os.getenv('FLASHBOTS_PRIVATE_KEY') ETHEREUM_BOT_ADDRESS = os.getenv('ETHEREUM_BOT_ADDRESS') ETHEREUM_SIMULATOR_ADDRESS = os.getenv('ETHEREUM_SIMULATOR_ADDRESS') def cycle_name(pools_1: List[int], pools_2: List[int], pools: List[Dict[str, Any]]) -> str: """ Returns the name for cycle consisting of pools_1 and pools_2 ex) UNI3ETHUSDT/UNI2ETHUSDT, UNI3ETHUSDT/UNI3USDCUSDT-UNI2USDCETH, UNI3USDCUSDT-SUS3USDCETH/SUS3USDCUSDT-SUS2USDCETH This is for logging/debugging purposes. Actual bot logic does not depend on this name. """ def _pool_name(pool: Dict[str, Any]) -> str: exchange = pool['exchange'][:3].upper() version = pool['version'] name = pool['name'].replace('/', '') return f'{exchange}{version}{name}' path_1_name = '-'.join([_pool_name(pools[i]) for i in pools_1]) path_2_name = '-'.join([_pool_name(pools[i]) for i in pools_2]) return f'{path_1_name}/{path_2_name}' def dex_stream_process(publisher: aioprocessing.AioQueue, chain: str, trading_symbols: List[str], max_swaps: int = 3): pools = [pool for pool in POOLS if pool['chain'] == chain] dex = DEX({chain: RPC_ENDPOINTS[chain]}, {chain: TOKENS[chain]}, pools, trading_symbols, max_swaps) dex_stream = DexStream(dex, WS_ENDPOINTS, publisher) """ Trying to find possible cyclic arbitrage paths Say there are paths as such: - [0], [5] Path 0: Uniswap V3 ETH/USDT Path 5: Uniswap V2 ETH/USDT In this case, this is a possible cyclic arbitrage path: 1. Uniswap V3 BUY and Uniswap V2 SELL BUY: Uniswap V3 USDT -> ETH SELL: Uniswap V2 ETH -> USDT 2. Uniswap V2 BUY and Uniswap V3 SELL BUY: Uniswap V2 USDT -> ETH SELL: Uniswap V3 ETH -> USDT This type of cyclic arbitrage paths only work when two conditions are met: Condition #1: the first pool is different Condition #2: the last pool is different compare_paths is a dictionary of possible cyclic arbitrage path pairs """ compare_paths = {s: {} for s in trading_symbols} for symbol in trading_symbols: pool_indexes = dex.swap_paths[symbol]['pool_indexes'] for i in range(len(pool_indexes)): p_1 = pool_indexes[i] for j in range(i + 1, len(pool_indexes)): p_2 = pool_indexes[j] condition_1 = p_1[0] != p_2[0] condition_2 = p_1[-1] != p_2[-1] if condition_1 and condition_2: name = cycle_name(p_1, p_2, pools) compare_paths[symbol][name] = (i, j) # send compare_paths data to data_collector through publisher publisher.put({ 'source': 'dex', 'type': 'setup', 'compare_paths': compare_paths, }) dex_stream.start_streams() class Pending: def __init__(self): self.info = None def can_add(self): no_info = self.info is None if no_info: return True if not self.info['order_processing']: return True def get_pending(self): return self.info def add_pending(self, pending: Dict[str, Any]): self.info = pending def set_order_processing(self): self.info['order_processing'] = True def delete_pending(self): self.info = None async def strategy(subscriber: aioprocessing.AioQueue, chain: str, max_bet_size: float, target_spread: float = 0.0, retry_number: int = 2, debug: bool = False): rpc_endpoints = {chain: RPC_ENDPOINTS[chain]} tokens = {chain: TOKENS[chain]} pools = [pool for pool in POOLS if pool['chain'] == chain] simulator_contracts = {chain: ETHEREUM_SIMULATOR_ADDRESS} simulator_handlers = {chain: SIMULATION_HANDLERS[chain]} execution_contracts = {chain: ETHEREUM_BOT_ADDRESS} execution_handlers = {chain: EXECUTION_HANDLERS[chain]} # If .env vars relavant to InfluxDB, Telegram aren't set, # they'll simply stand there as placeholders, doing nothing on send calls influxdb = InfluxDB() telegram = Telegram() simulator = OnlineSimulator(rpc_endpoints=rpc_endpoints, tokens=tokens, pools=pools, contracts=simulator_contracts, handlers=simulator_handlers) execution = DexOrder(private_key=FLASHBOTS_PRIVATE_KEY, signing_key=FLASHBOTS_SIGNING_KEY, rpc_endpoints=rpc_endpoints, tokens=tokens, pools=pools, contracts=execution_contracts, handlers=execution_handlers) compare_paths = {} gas_info = {} spreads = {} pending = Pending() """ The estimated cost of WhackAMoleBotV1 swaps Current gas amount is a rough estimation, for a optimized result, make sure to find better estimates. These values are overestimated for a more strict/conservative simulation result. """ gas_costs = { 0: 100000, # base cost 2: 40000, # V2 1-hop cost 3: 50000, # V3 1-hop cost } async def _process_pending_order(pending: Pending): """ 1. Simulate using SimulatorV1 2. Execute order using Flashbots """ if pending.can_add(): return pending_info = pending.get_pending() if 'block' not in gas_info or pending_info['block'] != gas_info['block']: return spread = spreads[pending_info['key']] if spread <= target_spread: pending.delete_pending() return """ We simulate using max_fee_per_gas as the gas price This is to conservatively calculate the min_amount_in to be profitable assuming we use gas at the highest cost We also assume the quote of gas_price is at sell_price, which is higher than that of buy_price. This is another mechanism to overestimate the cost for a more realistic simulation result. """ buy_price = pending_info['max_buy_sell_price'][0] sell_price = pending_info['max_buy_sell_price'][1] gas_cost = (pending_info['estimated_gas_used'] * gas_info['max_fee_per_gas']) * 10 ** (-18) # in ETH gas_cost_in_usdt = gas_cost * sell_price # the sell_price has to be price of ETH/USDT usdt_profit_per_unit_of_token = buy_price * (spread / 100.0) min_amount_in_token = gas_cost_in_usdt / usdt_profit_per_unit_of_token min_amount_in_usdt = min_amount_in_token * buy_price """ Since we're not using flashloans yet, we can't over leverage our bets. The max_bet_size should be less than the amount of USDT you have in your WhackAMoleBotV1 contract. This would naturally mean that to cover the gas costs, we can only aim for spreads that are greater than other well optimized bots either 1. using flashloans, or 2. using more capital. """ if min_amount_in_usdt <= max_bet_size: # we simulate using max_bet_size and if the result is promising we send the order to Flashbots usdt_decimals = simulator.tokens[chain]['USDT'][1] # min_amount_in = max_bet_size * 10 ** usdt_decimals min_amount_in = int(min_amount_in_usdt * 1.1) * 10 ** usdt_decimals sim_params = simulator.make_params(amount_in=min_amount_in, buy_path=pending_info['buy_path'], sell_path=pending_info['sell_path'], buy_pools=pending_info['buy_pools'], sell_pools=pending_info['sell_pools']) s = time.time() simulated_amount_out = simulator.simulate(chain, sim_params) e = time.time() simulation_took = e - s simulated_profit_in_usdt = (simulated_amount_out - min_amount_in) / 10 ** usdt_decimals final_profit = simulated_profit_in_usdt - gas_cost_in_usdt print(f'Simulated profit in USDT: {final_profit} (Took: {round(simulation_took, 3)} secs)') if final_profit > 0: pending.set_order_processing() # execute order here if program started as non-debug mode if not debug: exe_params = execution.make_params(amount_in=min_amount_in, buy_path=pending_info['buy_path'], sell_path=pending_info['sell_path'], buy_pools=pending_info['buy_pools'], sell_pools=pending_info['sell_pools']) s = time.time() receipts = execution.send_order(chain=chain, params=exe_params, min_amount_out=int(simulated_amount_out * 0.999), # 0.1% slippage tolerance max_priority_fee_per_gas=gas_info['max_priority_fee_per_gas'], max_fee_per_gas=gas_info['max_fee_per_gas'], retry=retry_number, block_number=gas_info['block']) e = time.time() execution_took = e - s print(f'Execution success. Took: {round(execution_took, 3)} secs: {receipts}') await telegram.send( f'Block #{pending_info["block"]} {pending_info["key"]} ({round(spread, 2)}%): {final_profit} USDT') else: await telegram.send( f'Block #{pending_info["block"]} {pending_info["key"]} ({round(spread, 2)}%) min amount of USDT needed to profit: {round(min_amount_in_usdt, 3)} USDT') pending.delete_pending() # Main strategy loop while True: try: data = await subscriber.coro_get() data_type = data['type'] if data_type == 'setup': # data sent from: strategies.dex_arb_base.dex_stream_process compare_paths = data['compare_paths'] elif data_type == 'block': # data sent from: data.dex_streams.DexStream.stream_new_blocks gas_info = data print('[New block] ', gas_info) await _process_pending_order(pending) elif data_type == 'event': s = time.time() # data sent from: data.dex_streams.DexStream.stream_uniswap_v2_events/stream_uniswap_v3_events """ Take 2-step operation before sending order transaction: 1. Filtering: Calculate simple spread using price quotes (w/o consideration of price impact) Filter out paths that had a spread over 0 2. Simulating: If simple spread calculated above was above 0, it is worth simulating for price impact Thus, use SimulatorV1 contract to simulate the amount_out of the potentially profitable paths But to make this simpler, we simply simulate the most profitable path This means that our simulations are done online. This will take some time, and will need to be ported offline to get the best performance out """ max_spread = -1 max_spread_key = '' max_buy_sell_price = [] max_path_index = None symbol = data['symbol'] for path_name, path_index in compare_paths[symbol].items(): """ If path_name were: UNI3ETHUSDT/UNI2ETHUSDT We want to calculate the spread for both: - UNI3ETHUSDT BUY -> UNI2ETHUSDT SELL - UNI2ETHUSDT SELL -> UNI3ETHUSDT SELL """ name_1, name_2 = path_name.split('/') key_1 = f'{name_1}/{name_2}' key_2 = f'{name_2}/{name_1}' price_1 = data['price'][path_index[0]] price_2 = data['price'][path_index[1]] fee_1 = data['fee'][path_index[0]] fee_2 = data['fee'][path_index[1]] total_fee = fee_1 + fee_2 spread_1 = ((price_1 / price_2 - 1) - total_fee) * 100 spread_2 = ((price_2 / price_1 - 1) - total_fee) * 100 if spread_1 > max_spread: max_spread_key = key_1 max_spread = spread_1 max_buy_sell_price = [price_2, price_1] max_path_index = list(reversed(path_index)) # buy pool index, sell pool index if spread_2 > max_spread: max_spread_key = key_2 max_spread = spread_2 max_buy_sell_price = [price_1, price_2] max_path_index = list(path_index) # buy pool index, sell pool index spreads[key_1] = spread_1 spreads[key_2] = spread_2 await influxdb.send('DEX_ARB_BASE_ETHUSDT', spreads) e = time.time() max_msg = f'{max_spread_key}: {round(spreads[max_spread_key], 3)}%' print(f'[{datetime.datetime.now()}] Update took: {round(e - s, 4)} secs. {max_msg}') # add newly detected edge (positive spread) to pending # we process one edge a time to make our lives easier if pending.can_add() and max_spread > target_spread: # before we add the new max_spread_key, first check if the spread can cover # gas costs with our max_bet_size buy_path = data['path'][max_path_index[0]] sell_path = data['path'][max_path_index[1]] """ Calculate the estimated_gas_used given: - buy_path = [[0, 0, 5, 4, 1], [0, 0, 4, 2, 1]] - sell_path = [[0, 1, 5, 2, 1], [0, 0, 0, 0, 0]] """ estimated_gas_used = gas_costs[0] # base cost for buy_sell_path in [buy_path, sell_path]: for p in buy_sell_path: if sum(p) == 0: continue version = p[4] estimated_gas_used += gas_costs[version + 2] # V2 = 0, V3 = 1 """ The actual simulation will occur in _process_pending_order This is because a newly updated block may not be avaialble yet. We send a REST API call to Blocknative for gas info, so we need to wait for the response to arrive before we run our simulation. """ pending_info = { 'key': max_spread_key, 'max_buy_sell_price': max_buy_sell_price, 'block': data['block'], # block at which the edge was detected 'cancel_at': data['block'] + 1, # cancel after 1 block 'buy_path': buy_path, 'sell_path': sell_path, 'buy_pools': data['pool_indexes'][max_path_index[0]], 'sell_pools': data['pool_indexes'][max_path_index[1]], 'estimated_gas_used': estimated_gas_used, 'order_processing': False, } pending.add_pending(pending_info) print(pending_info) await _process_pending_order(pending) except Exception as e: await influxdb.close() raise e async def main(): chain = 'ethereum' max_swaps = 3 trading_symbols = ['ETH/USDT'] # other symbols won't work at the moment, because of gas cost calculations max_bet_size = 20000 # in USDT, because we are buying ETH with USDT target_spread = 0.15 # minimum target of 0.4% spread retry_number = 2 debug = True # running in debug mode won't execute orders. It'll simply send data to InfluxDB, Telegram queue = aioprocessing.AioQueue() p1 = Process(target=dex_stream_process, args=(queue, chain, trading_symbols, max_swaps,)) p1.start() await strategy(queue, chain, max_bet_size, target_spread, retry_number, debug) if __name__ == '__main__': import nest_asyncio nest_asyncio.apply() asyncio.run(main()) ================================================ FILE: tests/test_WhackAMoleBotV1.py ================================================ import json from web3 import Web3 from unittest import TestCase from eth_account.account import Account POOL_V2_ABI = json.load(open('../abi/UniswapV2Pool.json', 'r')) POOL_V3_ABI = json.load(open('../abi/UniswapV3Pool.json', 'r')) ROUTER2_ABI = json.load(open('../abi/UniswapV2Router2.json', 'r')) QUOTER2_ABI = json.load(open('../abi/UniswapV3Quoter2.json', 'r')) SWAP_ROUTER2_ABI = json.load(open('../abi/UniswapV3SwapRouter2.json', 'r')) WETH_ABI = json.load(open('../abi/WETH.json', 'r')) ERC20_ABI = json.load(open('../abi/ERC20.json', 'r')) BOT_ABI = json.load(open('../contracts/out/WhackAMoleBotV1.sol/WhackAMoleBotV1.json', 'r'))['abi'] SIMULATOR_ABI = json.load(open('../contracts/out/SimulatorV1.sol/SimulatorV1.json', 'r'))['abi'] class WhackAMoleBotV1Tests(TestCase): """ WhackAMoleBotV1 tests are dependent on WhackAMoleBotV1 contract Make sure to deploy the contract to mainnet hardfork before testing forge create --rpc-url --private-key src/WhackAMoleBotV1.sol:WhackAMoleBotV1 """ HARDFORK_RPC_URL = 'http://localhost:8545' FLASHBOTS_URL = 'https://relay.flashbots.net' # Private key is from Anvil TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' WETH_DECIMALS = 18 USDT_DECIMALS = 6 POOL_V3_FEE = 500 # WETH-USDT Uniswap V2 pool POOL_V2 = '0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852' ROUTER2 = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' # WETH-USDT Uniswap V3 pool POOL_V3 = '0x11b815efB8f581194ae79006d24E0d814B7697F6' QUOTER2 = '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' SWAP_ROUTER = '0xE592427A0AEce92De3Edee1F18E0157C05861564' # WhackAMoleBotV1 BOT = '0x9155497EAE31D432C0b13dBCc0615a37f55a2c87' SIMULATOR = '0xfB12F7170FF298CDed84C793dAb9aBBEcc01E798' def setUp(self): self.signer = Account.from_key(self.TEST_PRIVATE_KEY) self.web3 = Web3(Web3.HTTPProvider(self.HARDFORK_RPC_URL)) self.weth = self.web3.eth.contract(address=self.WETH, abi=WETH_ABI) self.usdt = self.web3.eth.contract(address=self.USDT, abi=ERC20_ABI) # V2 contracts self.pool_v2 = self.web3.eth.contract(address=self.POOL_V2, abi=POOL_V2_ABI) self.router2 = self.web3.eth.contract(address=self.ROUTER2, abi=ROUTER2_ABI) # V3 contracts self.pool_v3 = self.web3.eth.contract(address=self.POOL_V3, abi=POOL_V3_ABI) self.quoter2 = self.web3.eth.contract(address=self.QUOTER2, abi=QUOTER2_ABI) self.swap_router = self.web3.eth.contract(address=self.SWAP_ROUTER, abi=SWAP_ROUTER2_ABI) # WhackAMoleBotV1 self.bot = self.web3.eth.contract(address=self.BOT, abi=BOT_ABI) self.sim = self.web3.eth.contract(address=self.SIMULATOR, abi=SIMULATOR_ABI) # Wrap ETH to WETH # Transfer ETH to WETH contract nonce = self.web3.eth.get_transaction_count(self.signer.address) transaction = { 'from': self.signer.address, 'to': self.WETH, 'value': self.web3.to_wei(10, 'ether'), 'chainId': 1, 'nonce': nonce, 'gas': 200000, 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), # set at randomly high gas price 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), # set at random } signed = self.signer.sign_transaction(transaction) tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) _ = self.web3.eth.get_transaction(tx_hash) # transfer WETH to bot contract nonce = self.web3.eth.get_transaction_count(self.signer.address) transfer_transaction = self.weth.functions.transfer( self.BOT, self.web3.to_wei(3, 'ether') ).build_transaction({ 'from': self.signer.address, 'chainId': 1, 'nonce': nonce, 'gas': 200000, 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), }) signed = self.signer.sign_transaction(transfer_transaction) tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) _ = self.web3.eth.get_transaction(tx_hash) bot_weth_balance = self.weth.functions.balanceOf(self.BOT).call() print('Bot WETH balance: ', bot_weth_balance) self.assertTrue(bot_weth_balance >= self.web3.to_wei(3, 'ether')) # approve usage of WETH, USDT to Uniswap V2, V3 routers tokens = [self.WETH, self.USDT] protocols = [self.ROUTER2, self.SWAP_ROUTER] nonce = self.web3.eth.get_transaction_count(self.signer.address) transfer_transaction = self.bot.functions.approveHandlers( tokens, protocols ).build_transaction({ 'from': self.signer.address, 'chainId': 1, 'nonce': nonce, 'gas': 200000, 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), }) signed = self.signer.sign_transaction(transfer_transaction) tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) _ = self.web3.eth.get_transaction(tx_hash) max_int = 2 ** 256 - 1 for protocol in protocols: weth_allowance = self.weth.functions.allowance(self.BOT, protocol).call() self.assertEqual(weth_allowance, max_int) usdt_allowance = self.usdt.functions.allowance(self.BOT, protocol).call() self.assertEqual(usdt_allowance, max_int) def test_recover_tokens(self): # test recoverTokens bot_weth_balance_before = self.weth.functions.balanceOf(self.BOT).call() signer_weth_balance_before = self.weth.functions.balanceOf(self.signer.address).call() tokens = [self.WETH] nonce = self.web3.eth.get_transaction_count(self.signer.address) transfer_transaction = self.bot.functions.recoverTokens( tokens ).build_transaction({ 'from': self.signer.address, 'chainId': 1, 'nonce': nonce, 'gas': 300000, 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), }) signed = self.signer.sign_transaction(transfer_transaction) tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) _ = self.web3.eth.get_transaction(tx_hash) bot_weth_balance_after = self.weth.functions.balanceOf(self.BOT).call() signer_weth_balance_after = self.weth.functions.balanceOf(self.signer.address).call() # print(bot_weth_balance_before, bot_weth_balance_after) # print(signer_weth_balance_before, signer_weth_balance_after) self.assertEqual(bot_weth_balance_before, signer_weth_balance_after) def test_v2_swap(self): factory = self.pool_v2.functions.factory().call() params = { 'protocol': 0, 'handler': factory, 'tokenIn': self.WETH, 'tokenOut': self.USDT, 'fee': 3000, 'amount': self.web3.to_wei(1, 'ether'), } simulated_amount_out = self.sim.functions.simulateSwapIn([params]).call() usdt_balance_before = self.usdt.functions.balanceOf(self.BOT).call() swap_params = { **params, 'handler': self.ROUTER2, } nonce = self.web3.eth.get_transaction_count(self.signer.address) transfer_transaction = self.bot.functions.whack( [swap_params], 0 ).build_transaction({ 'from': self.signer.address, 'chainId': 1, 'nonce': nonce, 'gas': 300000, 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), }) signed = self.signer.sign_transaction(transfer_transaction) tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) _ = self.web3.eth.get_transaction(tx_hash) usdt_balance_after = self.usdt.functions.balanceOf(self.BOT).call() self.assertEqual(simulated_amount_out, usdt_balance_after - usdt_balance_before) def test_v3_swap(self): params = { 'protocol': 1, 'handler': self.QUOTER2, 'tokenIn': self.WETH, 'tokenOut': self.USDT, 'fee': self.POOL_V3_FEE, 'amount': self.web3.to_wei(1, 'ether'), } simulated_amount_out = self.sim.functions.simulateSwapIn([params]).call() usdt_balance_before = self.usdt.functions.balanceOf(self.BOT).call() swap_params = { **params, 'handler': self.SWAP_ROUTER, } nonce = self.web3.eth.get_transaction_count(self.signer.address) transfer_transaction = self.bot.functions.whack( [swap_params], 0 ).build_transaction({ 'from': self.signer.address, 'chainId': 1, 'nonce': nonce, 'gas': 300000, 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), }) signed = self.signer.sign_transaction(transfer_transaction) tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) _ = self.web3.eth.get_transaction(tx_hash) usdt_balance_after = self.usdt.functions.balanceOf(self.BOT).call() self.assertEqual(simulated_amount_out, usdt_balance_after - usdt_balance_before) def test_2_hop_swaps(self): factory = self.pool_v2.functions.factory().call() params_1 = { 'protocol': 0, 'handler': factory, 'tokenIn': self.WETH, 'tokenOut': self.USDT, 'fee': 3000, 'amount': self.web3.to_wei(1, 'ether'), } params_2 = { 'protocol': 1, 'handler': self.QUOTER2, 'tokenIn': self.USDT, 'tokenOut': self.WETH, 'fee': self.POOL_V3_FEE, 'amount': 0, } simulated_amount_out = self.sim.functions.simulateSwapIn( [params_1, params_2] ).call() weth_balance_before = self.weth.functions.balanceOf(self.BOT).call() swap_params_1 = { **params_1, 'handler': self.ROUTER2, } swap_params_2 = { **params_2, 'handler': self.SWAP_ROUTER, } nonce = self.web3.eth.get_transaction_count(self.signer.address) transfer_transaction = self.bot.functions.whack( [swap_params_1, swap_params_2], 0 ).build_transaction({ 'from': self.signer.address, 'chainId': 1, 'nonce': nonce, 'gas': 300000, 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), }) signed = self.signer.sign_transaction(transfer_transaction) tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) _ = self.web3.eth.get_transaction(tx_hash) weth_balance_after = self.weth.functions.balanceOf(self.BOT).call() weth_amount_out = weth_balance_after - (weth_balance_before - self.web3.to_wei(1, 'ether')) self.assertEqual(simulated_amount_out, weth_amount_out) def test_n_hop_swap_gas(self): """ V2 1-hop: 116040 2-hop: 145834 3-hop: 182965 4-hop: 208761 V3 1-hop: 117267 2-hop: 161362 3-hop: 207317 4-hop: 242614 V2 1-hop / V3 1-hop: 186354 V2 2-hop / V3 1-hop: 220666 V2 1-hop / V3 2-hop: 235437 V2 2-hop / V3 2-hop: 262089 For simplicity: Base cost: 100000 V2 1-hop costs: 40000 V3 1-hop costs: 50000 """ params_1 = { 'protocol': 0, 'handler': self.ROUTER2, 'tokenIn': self.WETH, 'tokenOut': self.USDT, 'fee': 3000, 'amount': self.web3.to_wei(0.1, 'ether'), } params_2 = { 'protocol': 1, 'handler': self.SWAP_ROUTER, 'tokenIn': self.USDT, 'tokenOut': self.WETH, 'fee': 500, 'amount': 0, } params_3 = { 'protocol': 1, 'handler': self.SWAP_ROUTER, 'tokenIn': self.WETH, 'tokenOut': self.USDT, 'fee': 500, 'amount': 0, } params_4 = { 'protocol': 0, 'handler': self.ROUTER2, 'tokenIn': self.USDT, 'tokenOut': self.WETH, 'fee': 3000, 'amount': 0, } nonce = self.web3.eth.get_transaction_count(self.signer.address) transfer_transaction = self.bot.functions.whack( [params_1, params_4, params_1, params_4], 0 ).build_transaction({ 'from': self.signer.address, 'chainId': 1, 'nonce': nonce, 'gas': 300000, 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), }) signed = self.signer.sign_transaction(transfer_transaction) tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) tx = self.web3.eth.get_transaction(tx_hash) print(tx) def test_price_impact_simulation(self): uniswap_quoter2 = '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' sushiswap_quoter2 = '0x64e8802FE490fa7cc61d3463958199161Bb608A7' # Scenario 1: WETH -> USDT -> WETH amount_in = 1 * 10 ** self.WETH_DECIMALS params_1 = { 'protocol': 1, 'handler': uniswap_quoter2, 'tokenIn': self.WETH, 'tokenOut': self.USDT, 'fee': 500, 'amount': amount_in, } params_2 = { 'protocol': 1, 'handler': sushiswap_quoter2, 'tokenIn': self.USDT, 'tokenOut': self.WETH, 'fee': 500, 'amount': 0, } simulated_amount_out = self.sim.functions.simulateSwapIn( [params_1, params_2] ).call() # Scenario 2: USDT -> WETH -> USDT amount_in = 1 * 10 ** self.USDT_DECIMALS ================================================ FILE: tests/test_simulation.py ================================================ import json import eth_abi import datetime from web3 import Web3 from unittest import TestCase from eth_account.account import Account from simulation import UniswapV2Simulator, UniswapV3Simulator POOL_V2_ABI = json.load(open('../abi/UniswapV2Pool.json', 'r')) POOL_V3_ABI = json.load(open('../abi/UniswapV3Pool.json', 'r')) ROUTER2_ABI = json.load(open('../abi/UniswapV2Router2.json', 'r')) QUOTER2_ABI = json.load(open('../abi/UniswapV3Quoter2.json', 'r')) SWAP_ROUTER2_ABI = json.load(open('../abi/UniswapV3SwapRouter2.json', 'r')) WETH_ABI = json.load(open('../abi/WETH.json', 'r')) ERC20_ABI = json.load(open('../abi/ERC20.json', 'r')) class SimulationTests(TestCase): """ Simulation tests are dependent on mainnet hardforks Make sure to have a local mainnet hardfork running before testing For people using Foundry, running Anvil Ethereum mainnet hardfork is easy: anvil --fork-url """ HARDFORK_RPC_URL = 'http://localhost:8545' # Private key is from Anvil TEST_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' USDT = '0xdAC17F958D2ee523a2206206994597C13D831ec7' DAI = '0x6B175474E89094C44Da98b954EedeAC495271d0F' WETH_DECIMALS = 18 USDT_DECIMALS = 6 DAI_DECIMALS = 18 POOL_V3_FEE = 500 # WETH-USDT Uniswap V2 pool POOL_V2 = '0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852' ROUTER2 = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D' # WETH-USDT Uniswap V3 pool # POOL_V3 = '0x11b815efB8f581194ae79006d24E0d814B7697F6' POOL_V3 = '0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8' QUOTER2 = '0x61fFE014bA17989E743c5F6cB21bF9697530B21e' SWAP_ROUTER = '0xE592427A0AEce92De3Edee1F18E0157C05861564' # WhackAMoleBotV1 BOT = '0x46d4674578a2daBbD0CEAB0500c6c7867999db34' def setUp(self): self.signer = Account.from_key(self.TEST_PRIVATE_KEY) self.web3 = Web3(Web3.HTTPProvider(self.HARDFORK_RPC_URL)) self.weth = self.web3.eth.contract(address=self.WETH, abi=WETH_ABI) self.usdt = self.web3.eth.contract(address=self.USDT, abi=ERC20_ABI) # V2 contracts self.pool_v2 = self.web3.eth.contract(address=self.POOL_V2, abi=POOL_V2_ABI) self.router2 = self.web3.eth.contract(address=self.ROUTER2, abi=ROUTER2_ABI) # V3 contracts self.pool_v3 = self.web3.eth.contract(address=self.POOL_V3, abi=POOL_V3_ABI) self.quoter2 = self.web3.eth.contract(address=self.QUOTER2, abi=QUOTER2_ABI) self.swap_router = self.web3.eth.contract(address=self.SWAP_ROUTER, abi=SWAP_ROUTER2_ABI) self.sim_v2 = UniswapV2Simulator() self.sim_v3 = UniswapV3Simulator() # def test_sim_v2_get_amount_out(self): # token_in = self.WETH # token0 = self.pool_v2.functions.token0().call() # token0_in = token_in == token0 # # reserve0, reserve1, _ = self.pool_v2.functions.getReserves().call() # # if token0_in: # reserve_in, reserve_out = reserve0, reserve1 # else: # reserve_in, reserve_out = reserve1, reserve0 # # amount_in = self.web3.toWei(1, 'ether') # # # First check that the simulation results equal that of Router2 # router_amount_out = self.router2.functions.getAmountOut( # amount_in, reserve_in, reserve_out # ).call() # # sim_amount_out = self.sim_v2.get_amount_out( # amount_in, reserve_in, reserve_out # ) # # self.assertEqual(router_amount_out, sim_amount_out) # # # Next, check that swapping 1 WETH does in fact result in amount_out USDT # nonce = self.web3.eth.get_transaction_count(self.signer.address) # approve_transaction = self.weth.functions.approve(self.ROUTER2, amount_in).build_transaction({ # 'from': self.signer.address, # 'chainId': 1, # 'nonce': nonce, # 'gas': 200000, # 'maxFeePerGas': self.web3.toWei(100, 'gwei'), # 'maxPriorityFeePerGas': self.web3.toWei(50, 'gwei'), # }) # signed = self.signer.sign_transaction(approve_transaction) # tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) # _ = self.web3.eth.get_transaction(tx_hash) # # allowance_amount = self.weth.functions.allowance(self.signer.address, self.ROUTER2).call() # self.assertEqual(amount_in, allowance_amount) # approve was successful # # usdt_balance_before = self.usdt.functions.balanceOf(self.signer.address).call() # # path = [self.WETH, self.USDT] # nonce = self.web3.eth.get_transaction_count(self.signer.address) # # I'm not including slippage tolerance here, so this transaction # # will most likely fail in real life scenarios unless your transaction is at the top of the block # swap_transaction = self.router2.functions.swapExactTokensForTokens( # amount_in, # sim_amount_out, # path, # self.signer.address, # int(datetime.datetime.now().timestamp()) + 60000 # 1 minute # ).build_transaction({ # 'from': self.signer.address, # 'chainId': 1, # 'nonce': nonce, # 'gas': 200000, # 'maxFeePerGas': self.web3.toWei(100, 'gwei'), # 'maxPriorityFeePerGas': self.web3.toWei(50, 'gwei'), # }) # signed = self.signer.sign_transaction(swap_transaction) # tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) # _ = self.web3.eth.get_transaction(tx_hash) # # usdt_balance_after = self.usdt.functions.balanceOf(self.signer.address).call() # # self.assertEqual(usdt_balance_before + sim_amount_out, usdt_balance_after) def test_sim_v3_get_amount_out(self): """ Currently only implements single tick swap """ import math # # Wrap ETH to WETH # nonce = self.web3.eth.get_transaction_count(self.signer.address) # transaction = { # 'from': self.signer.address, # 'to': self.WETH, # 'value': self.web3.to_wei(100, 'ether'), # 'chainId': 1, # 'nonce': nonce, # 'gas': 200000, # 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), # set at randomly high gas price # 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), # set at random # } # signed = self.signer.sign_transaction(transaction) # tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) # _ = self.web3.eth.get_transaction(tx_hash) # # weth_balance = self.weth.functions.balanceOf(self.signer.address).call() # print('WETH: ', weth_balance) # Approve WETH nonce = self.web3.eth.get_transaction_count(self.signer.address) approve_transaction = self.weth.functions.approve( self.pool_v3.address, 100 * 10 ** 18 ).build_transaction({ 'from': self.signer.address, 'chainId': 1, 'nonce': nonce, 'gas': 200000, 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), }) signed = self.signer.sign_transaction(approve_transaction) tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) _ = self.web3.eth.get_transaction(tx_hash) allowance = self.weth.functions.allowance(self.signer.address, self.SWAP_ROUTER).call() print('WETH allowance: ', allowance) token_in = self.WETH token0 = self.pool_v3.functions.token0().call() token0_in = token_in == token0 slot0 = self.pool_v3.functions.slot0().call() tick_spacing = self.pool_v3.functions.tickSpacing().call() liquidity = self.pool_v3.functions.liquidity().call() sqrt_price, current_tick, *_ = slot0 upper_tick_idx = tick_spacing * (current_tick // tick_spacing + 1) lower_tick_idx = tick_spacing * (current_tick // tick_spacing) print(upper_tick_idx, current_tick, lower_tick_idx) print(slot0) print(tick_spacing) print(liquidity) price_range = self.sim_v3.tick_to_price_range(current_tick, tick_spacing, self.DAI_DECIMALS, self.WETH_DECIMALS, token0_in) print('Price range: ', price_range) curr_price = self.sim_v3.sqrtx96_to_price(sqrt_price, self.DAI_DECIMALS, self.WETH_DECIMALS, token0_in) print('Curr price: ', curr_price) sqrt_price_tick = self.sim_v3.sqrtx96_to_tick(sqrt_price) print(sqrt_price_tick) p = self.sim_v3.tick_to_price(sqrt_price_tick, self.DAI_DECIMALS, self.WETH_DECIMALS) print(p) target_price = self.sim_v3.tick_to_price(lower_tick_idx, self.DAI_DECIMALS, self.WETH_DECIMALS) print('target price: ', 1 / target_price) price_diff = (1 / target_price) - curr_price price_diff_liq = price_diff * liquidity print(price_diff_liq / 10 ** self.DAI_DECIMALS) nonce = self.web3.eth.get_transaction_count(self.signer.address) params = ( self.WETH, self.DAI, 3000, self.signer.address, 99999999999999999, 1 * 10 ** self.WETH_DECIMALS, 0, 0, ) swap_transaction = self.pool_v3.functions.swap( self.signer.address, False, 1 * 10 ** 18, 4400000000, '' ).build_transaction({ 'from': self.signer.address, 'chainId': 1, 'nonce': nonce, 'gas': 1200000, 'maxFeePerGas': self.web3.to_wei(100, 'gwei'), 'maxPriorityFeePerGas': self.web3.to_wei(50, 'gwei'), }) print(swap_transaction) signed = self.signer.sign_transaction(swap_transaction) tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) tx = self.web3.eth.get_transaction(tx_hash) replay_tx = { 'to': tx['to'], 'from': tx['from'], 'value': tx['value'], 'data': tx['input'], } self.web3.eth.call(replay_tx, tx.blockNumber - 1) print('swapped') slot0 = self.pool_v3.functions.slot0().call() liquidity = self.pool_v3.functions.liquidity().call() print('slot0: ', slot0) print('liquidity: ', liquidity) # upper_price = 1.0001 ** (upper_tick_idx / 2) # print(upper_tick_idx, upper_price) # print(sqrt_price * 2 ** (-96)) # max_amount_in = (upper_price - (sqrt_price * 2 ** (-96))) * liquidity # print(max_amount_in) # max_amount_in = max_amount_in / 10 ** self.WETH_DECIMALS # print('max_amount_in: ', max_amount_in) # # q96 = 2 ** 96 # eth = 10 ** self.WETH_DECIMALS # amount_in = int(max_amount_in * eth) # price_diff = int((amount_in * q96) // liquidity) # print('amount_in: ', amount_in) # print('price_diff: ', price_diff) # # sqrt_price_next = sqrt_price + price_diff # print(sqrt_price, price_diff, sqrt_price_next) # price_next = self.sim_v3.sqrtx96_to_price(sqrt_price_next, self.WETH_DECIMALS, self.USDT_DECIMALS, token0_in) # # print('price next: ', sqrt_price_next) # print('new price: ', price_next) # # usdt_balance_before = self.usdt.functions.balanceOf(self.signer.address).call() # path = [self.WETH, self.USDT] # nonce = self.web3.eth.get_transaction_count(self.signer.address) # # I'm not including slippage tolerance here, so this transaction # # will most likely fail in real life scenarios unless your transaction is at the top of the block # swap_transaction = self.router2.functions.swapExactTokensForTokens( # amount_in, # sim_amount_out, # path, # self.signer.address, # int(datetime.datetime.now().timestamp()) + 60000 # 1 minute # ).build_transaction({ # 'from': self.signer.address, # 'chainId': 1, # 'nonce': nonce, # 'gas': 200000, # 'maxFeePerGas': self.web3.toWei(100, 'gwei'), # 'maxPriorityFeePerGas': self.web3.toWei(50, 'gwei'), # }) # signed = self.signer.sign_transaction(swap_transaction) # tx_hash = self.web3.eth.send_raw_transaction(signed.rawTransaction) # _ = self.web3.eth.get_transaction(tx_hash) # def test_sim_v2_get_max_amount_in(self): # token_in = self.WETH # token0 = self.pool_v2.functions.token0().call() # token0_in = token_in == token0 # # reserve0, reserve1, _ = self.pool_v2.functions.getReserves().call() # # self.sim_v2.get_max_amount_in(reserve0, # reserve1, # self.WETH_DECIMALS, # self.USDT_DECIMALS, # 3000, # token0_in, # 100, # 0.1, # 0.0, # 0.0001)