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)