diff --git a/.github/workflows/generate_addressbooks.yaml b/.github/workflows/generate_addressbooks.yaml index 95f3f307..fdabe2f5 100644 --- a/.github/workflows/generate_addressbooks.yaml +++ b/.github/workflows/generate_addressbooks.yaml @@ -24,6 +24,7 @@ jobs: git clone https://github.com/balancer/balancer-deployments.git export DEPLOYMENTS_REPO_ROOT_URL=`pwd`/balancer-deployments pip3 install -r bal_addresses/requirements.txt + python3 gen_core_pools.py python3 gen_pools_and_gauges.py python3 transform-deployments.py python3 gen_addresses.py diff --git a/.gitignore b/.gitignore index 0cc8b460..e43789d8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ venv/ __pycache__/ .env Pipfile* +.pytest_cache diff --git a/config/core_pools_blacklist.json b/config/core_pools_blacklist.json new file mode 100644 index 00000000..ef691ec3 --- /dev/null +++ b/config/core_pools_blacklist.json @@ -0,0 +1,10 @@ +{ + "mainnet": { "0xxxx": "BLACKLIST-ME" }, + "polygon": {}, + "zkevm": {}, + "arbitrum": {}, + "gnosis": {}, + "base": {}, + "avalanche": {}, + "optimism": {} +} diff --git a/config/core_pools_whitelist.json b/config/core_pools_whitelist.json new file mode 100644 index 00000000..2c927f08 --- /dev/null +++ b/config/core_pools_whitelist.json @@ -0,0 +1,44 @@ +{ + "mainnet": { + "0xxxx": "WHITELIST-ME", + "0xf16aee6a71af1a9bc8f56975a4c2705ca7a782bc0002000000000000000004bb": "20WETH-80ALCX", + "0x8353157092ed8be69a9df8f95af097bbf33cb2af0000000000000000000005d9": "GHO/USDT/USDC", + "0x5f1f4e50ba51d723f12385a8a9606afc3a0555f5000200000000000000000465": "50wstETH-50LDO", + "0x1ee442b5326009bb18f2f472d3e0061513d1a0ff000200000000000000000464": "50rETH-50BADGER", + "0x3ff3a210e57cfe679d9ad1e9ba6453a716c56a2e0002000000000000000005d5": "STG/USDC", + "0xf01b0684c98cd7ada480bfdf6e43876422fa1fc10002000000000000000005de": "ECLP-wstETH-wETH", + "0x37b18b10ce5635a84834b26095a0ae5639dcb7520000000000000000000005cb": "ETHx-WETH", + "0x127ecc2318d002664cc4515c9f2b22b09b6aea85000200000000000000000602": "ECLP-swETH-wstETH", + "0xcf7b51ce5755513d4be016b0e28d6edeffa1d52a000200000000000000000617": "80RDNT-20wETH", + "0xb3b675a9a3cb0df8f66caf08549371bfb76a9867000200000000000000000611": "ECLP-mevETH-wETH", + "0x58b645fa247b60f2cb896991fd8956146c9fcb4a00020000000000000000061d": "80mevETH-20FOLD", + "0x35c5c8c7b77942f9d44b535fa590d8b503b2b00c00000000000000000000060d": "sDAI-DUSD", + "0xb9debddf1d894c79d2b2d09f819ff9b856fca55200000000000000000000062a": "weETH-WETH" + }, + "polygon": { + "0x89b753153678bc434c610b7e9182297cada8ff29000000000000000000000c21": "stMATIC-WMATIC", + "0xdc31233e09f3bf5bfe5c10da2014677c23b6894c000000000000000000000c23": "wstETH-WETH" + }, + "zkevm": { + "0x65da876a95cd5b6a5880710628c436409b1b29fa00000000000000000000005b": "ankrETH-wstETH", + "0xdf725fde6e89981fb30d9bf999841ac2c160b512000000000000000000000010": "wstETH/rETH" + }, + "arbitrum": { + "0x423a1323c871abc9d89eb06855bf5347048fc4a5000000000000000000000496": "4POOL", + "0x32df62dc3aed2cd6224193052ce665dc181658410002000000000000000003bd": "RDNT-WETH", + "0x6b9f3f6b9054a45702d3f2c6e3d32a60204934cb0000000000000000000004a7": "USDF-USDC", + "0x9f8ed1acfe0c863381b9081aff2144fc867aa7730002000000000000000004d4": "ANKR:ankrETH", + "0xa1a8bf131571a2139feb79401aa4a2e9482df6270002000000000000000004b4": "wstETH:4pool" + }, + "gnosis": {}, + "base": { + "0x0c659734f1eef9c63b7ebdf78a164cdd745586db000000000000000000000046": "USDC/USDbC/axlUSDC" + }, + "avalanche": { + "0xb26f0e66317846bd5fe0cbaa1d269f0efeb05c9600000000000000000000001e": "USDC-USDT", + "0x55bec22f8f6c69137ceaf284d9b441db1b9bfedc000200000000000000000011": "EURe/USDC" + }, + "optimism": { + "0xa71021492a3966eec735ed1b505afa097c7cfe6f00000000000000000000010d": "frxETH/sfrxETH" + } +} diff --git a/gen_core_pools.py b/gen_core_pools.py new file mode 100644 index 00000000..73fce324 --- /dev/null +++ b/gen_core_pools.py @@ -0,0 +1,151 @@ +import json + +import requests + +from gen_pools_and_gauges import get_subgraph_url + + +def get_pools_with_rate_provider(chain: str = None): + """ + for every chain, query the official balancer subgraph and retrieve pools that meets + all three of the following conditions: + - have a rate provider different from address(0) + - have a liquidity greater than $250k + - either: + - have a yield fee > 0 + - be a meta stable pool with swap fee > 0 + - be a gyro pool + """ + core_pools = {} + if chain: + chains = {"CHAIN_IDS_BY_NAME": [chain]} + else: + with open("extras/chains.json", "r") as f: + chains = json.load(f) + for chain in chains["CHAIN_IDS_BY_NAME"]: + if chain in ["sepolia", "goerli"]: + continue + core_pools[chain] = {} + url = get_subgraph_url(chain) + query = """{ + pools( + first: 1000, + where: { + and: [ + { + priceRateProviders_: { + address_not: "0x0000000000000000000000000000000000000000" + } + }, + { + totalLiquidity_gt: 250000 + }, + { or: [ + { protocolYieldFeeCache_gt: 0 }, + { and: [ + { swapFee_gt: 0 }, + { poolType_contains: "MetaStable" }, + { poolTypeVersion: 1 } + ] }, + { poolType_contains_nocase: "Gyro" }, + ] } + ] + } + ) { + id, + symbol + } + }""" + r = requests.post(url, json={"query": query}) + r.raise_for_status() + try: + for pool in r.json()["data"]["pools"]: + core_pools[chain][pool["id"]] = pool["symbol"] + except KeyError: + # no results for this chain + pass + return core_pools + + +def has_alive_preferential_gauge(chain: str, pool_id: str) -> bool: + """ + return True if the pool has a preferential gauge which is not killed + """ + url = get_subgraph_url(chain, "gauges") + query = f"""{{ + liquidityGauges( + where: {{ + poolId: "{pool_id}", + isKilled: false, + isPreferentialGauge: true + }} + ) {{ + id + }} + }}""" + r = requests.post(url, json={"query": query}) + r.raise_for_status() + try: + result = r.json()["data"]["liquidityGauges"] + except KeyError: + result = [] + if len(result) > 0: + return True + else: + print(f"Pool {pool_id} on {chain} has no alive preferential gauge") + + +def build_core_pools(chain: str = None): + """ + note: chain string format is the same as in extras/chains.json + """ + core_pools = get_pools_with_rate_provider(chain) + + # make sure the pools have an alive preferential gauge + for chain in core_pools: + for pool_id in list(core_pools[chain]): + if not has_alive_preferential_gauge(chain, pool_id): + del core_pools[chain][pool_id] + + # add pools from whitelist + with open("config/core_pools_whitelist.json", "r") as f: + whitelist = json.load(f) + for chain in whitelist: + try: + for pool, symbol in whitelist[chain].items(): + if pool not in core_pools[chain]: + core_pools[chain][pool] = symbol + except KeyError: + # no results for this chain + pass + + # remove pools from blacklist + with open("config/core_pools_blacklist.json", "r") as f: + blacklist = json.load(f) + for chain in blacklist: + try: + for pool in blacklist[chain]: + if pool in core_pools[chain]: + del core_pools[chain][pool] + except KeyError: + # no results for this chain + pass + + return core_pools + + +def is_core_pool(chain: str, pool_id: str) -> bool: + """ + check if a pool is a core pool using a fresh query to the subgraph + + params: + chain: string format is the same as in extras/chains.json + pool_id: this is the long version of a pool id, so contract address + suffix + """ + core_pools = build_core_pools(chain) + return pool_id in core_pools[chain] + + +if __name__ == "__main__": + # dump result to json + json.dump(build_core_pools(), open("outputs/core_pools.json", "w"), indent=2) diff --git a/outputs/core_pools.json b/outputs/core_pools.json new file mode 100644 index 00000000..a1a085b8 --- /dev/null +++ b/outputs/core_pools.json @@ -0,0 +1,91 @@ +{ + "mainnet": { + "0x05ff47afada98a98982113758878f9a8b9fdda0a000000000000000000000645": "weETH/rETH", + "0x1cce5169bde03f3d5ad0206f6bd057953539dae600020000000000000000062b": "ECLP-GYD-sDAI", + "0x1e19cf2d73a72ef1332c882f20534b6519be0276000200000000000000000112": "B-rETH-STABLE", + "0x2e848426aec6dbf2260535a5bea048ed94d9ff3d000000000000000000000536": "wbETH-wstETH", + "0x36be1e97ea98ab43b4debf92742517266f5731a3000200000000000000000466": "50wstETH-50ACX", + "0x40c806394d03d350420d13cd7d1de1c806f349560000000000000000000005f5": "qETH/WETH", + "0x42ed016f826165c2e5976fe5bc3df540c5ad0af700000000000000000000058b": "wstETH-rETH-sfrxETH-BPT", + "0x49cbd67651fbabce12d1df18499896ec87bef46f00000000000000000000064a": "sDAI/3Pool", + "0x596192bb6e41802428ac943d2f1476c1af25cc0e000000000000000000000659": "ezETH-WETH-BPT", + "0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512": "B-wjAura-wETH", + "0x70d5e3234f6329c1d5a26796dcf4e109d69a34880000000000000000000005e7": "uniETH/wstETH/rETH", + "0x93d199263632a4ef4bb438f1feb99e57b4b5f0bd0000000000000000000005c2": "wstETH-WETH-BPT", + "0x9f9d900462492d4c21e9523ca95a7cd86142f298000200000000000000000462": "50rETH-50RPL", + "0xb08885e6026bab4333a80024ec25a1a3e1ff2b8a000200000000000000000445": "B-staFiETH-WETH-Stable", + "0xb3b675a9a3cb0df8f66caf08549371bfb76a9867000200000000000000000611": "ECLP-mevETH-wETH", + "0xdfe6e7e18f6cc65fa13c8d8966013d4fda74b6ba000000000000000000000558": "ankrETH/wstETH", + "0xe7e2c68d3b13d905bbb636709cf4dfd21076b9d20000000000000000000005ca": "swETH-WETH-BPT", + "0xf01b0684c98cd7ada480bfdf6e43876422fa1fc10002000000000000000005de": "ECLP-wstETH-wETH", + "0xf7a826d47c8e02835d94fb0aa40f0cc9505cb1340002000000000000000005e0": "ECLP-wstETH-cbETH", + "0xf16aee6a71af1a9bc8f56975a4c2705ca7a782bc0002000000000000000004bb": "20WETH-80ALCX", + "0x8353157092ed8be69a9df8f95af097bbf33cb2af0000000000000000000005d9": "GHO/USDT/USDC", + "0x5f1f4e50ba51d723f12385a8a9606afc3a0555f5000200000000000000000465": "50wstETH-50LDO", + "0x1ee442b5326009bb18f2f472d3e0061513d1a0ff000200000000000000000464": "50rETH-50BADGER", + "0x3ff3a210e57cfe679d9ad1e9ba6453a716c56a2e0002000000000000000005d5": "STG/USDC", + "0x37b18b10ce5635a84834b26095a0ae5639dcb7520000000000000000000005cb": "ETHx-WETH", + "0x127ecc2318d002664cc4515c9f2b22b09b6aea85000200000000000000000602": "ECLP-swETH-wstETH", + "0xcf7b51ce5755513d4be016b0e28d6edeffa1d52a000200000000000000000617": "80RDNT-20wETH", + "0x58b645fa247b60f2cb896991fd8956146c9fcb4a00020000000000000000061d": "80mevETH-20FOLD", + "0x35c5c8c7b77942f9d44b535fa590d8b503b2b00c00000000000000000000060d": "sDAI-DUSD", + "0xb9debddf1d894c79d2b2d09f819ff9b856fca55200000000000000000000062a": "weETH-WETH" + }, + "polygon": { + "0xcd78a20c597e367a4e478a2411ceb790604d7c8f000000000000000000000c22": "maticX-WMATIC-BPT", + "0xee278d943584dd8640eaf4cc6c7a5c80c0073e85000200000000000000000bc7": "ECLP-WMATIC-MATICX", + "0xf0ad209e2e969eaaa8c882aac71f02d8a047d5c2000200000000000000000b49": "ECLP-WMATIC-stMATIC", + "0x89b753153678bc434c610b7e9182297cada8ff29000000000000000000000c21": "stMATIC-WMATIC", + "0xdc31233e09f3bf5bfe5c10da2014677c23b6894c000000000000000000000c23": "wstETH-WETH" + }, + "arbitrum": { + "0x0c8972437a38b389ec83d1e666b69b8a4fcf8bfd00000000000000000000049e": "wstETH/rETH/sfrxETH", + "0x2ce4457acac29da4736ae6f5cd9f583a6b335c270000000000000000000004dc": "sFRAX/4POOL", + "0x2e8ea681fd59c9dc5f32b29de31f782724ef4dcb0001000000000000000004bc": "50GOLD-25USDC-25WSTETH", + "0x36bf227d6bac96e2ab1ebb5492ecec69c691943f000200000000000000000316": "B-wstETH-WETH-Stable", + "0x3fd4954a851ead144c2ff72b1f5a38ea5976bd54000000000000000000000480": "ankrETH/wstETH-BPT", + "0x451b0afd69ace11ec0ac339033d54d2543b088a80000000000000000000004d5": "plsRDNT-Stable", + "0x49b2de7d214070893c038299a57bac5acb8b8a340001000000000000000004be": "GOLD-BAL-AURA-wstETH", + "0x4a2f6ae7f3e5d715689530873ec35593dc28951b000000000000000000000481": "wstETH/rETH/cbETH", + "0x9791d590788598535278552eecd4b211bfc790cb000000000000000000000498": "wstETH-WETH-BPT", + "0xade4a71bb62bec25154cfc7e6ff49a513b491e81000000000000000000000497": "rETH-WETH-BPT", + "0x423a1323c871abc9d89eb06855bf5347048fc4a5000000000000000000000496": "4POOL", + "0x32df62dc3aed2cd6224193052ce665dc181658410002000000000000000003bd": "RDNT-WETH", + "0x6b9f3f6b9054a45702d3f2c6e3d32a60204934cb0000000000000000000004a7": "USDF-USDC", + "0x9f8ed1acfe0c863381b9081aff2144fc867aa7730002000000000000000004d4": "ANKR:ankrETH", + "0xa1a8bf131571a2139feb79401aa4a2e9482df6270002000000000000000004b4": "wstETH:4pool" + }, + "optimism": { + "0x004700ba0a4f5f22e1e78a277fca55e36f47e09c000000000000000000000104": "bpt-ankrgalaharm", + "0x4fd63966879300cafafbb35d157dc5229278ed2300020000000000000000002b": "BPT-rETH-ETH", + "0x5f8893506ddc4c271837187d14a9c87964a074dc000000000000000000000106": "bpt-ethtri", + "0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb200020000000000000000008b": "BPT-WSTETH-WETH", + "0x7ca75bdea9dede97f8b13c6641b768650cb837820002000000000000000000d5": "ECLP-wstETH-WETH", + "0xa71021492a3966eec735ed1b505afa097c7cfe6f00000000000000000000010d": "frxETH/sfrxETH" + }, + "gnosis": { + "0x06135a9ae830476d3a941bae9010b63732a055f4000000000000000000000065": "stEUR/EURe", + "0x7644fa5d0ea14fcf3e813fdf93ca9544f8567655000000000000000000000066": "sBAL3", + "0xbad20c15a773bf03ab973302f61fabcea5101f0a000000000000000000000034": "bb-WETH-wstETH", + "0xdd439304a77f54b1f7854751ac1169b279591ef7000000000000000000000064": "EURe/sDAI" + }, + "zkevm": { + "0x1d0a8a31cdb04efac3153237526fb15cc65a252000000000000000000000000f": "B-rETH-STABLE", + "0xb1557cfea06de5a1601a7f0ccf3b515ef431a50d000200000000000000000059": "ECLP-wETH-rETH", + "0xe1f2c039a68a216de6dd427be6c60decf405762a00000000000000000000000e": "B-wstETH-STABLE", + "0x65da876a95cd5b6a5880710628c436409b1b29fa00000000000000000000005b": "ankrETH-wstETH", + "0xdf725fde6e89981fb30d9bf999841ac2c160b512000000000000000000000010": "wstETH/rETH" + }, + "avalanche": { + "0x9fa6ab3d78984a69e712730a2227f20bcc8b5ad900000000000000000000001f": "yyAVAX-WAVAX-BPT", + "0xc13546b97b9b1b15372368dc06529d7191081f5b00000000000000000000001d": "ggAVAX-WAVAX-BPT", + "0xfd2620c9cfcec7d152467633b3b0ca338d3d78cc00000000000000000000001c": "sAVAX-WAVAX-BPT", + "0xb26f0e66317846bd5fe0cbaa1d269f0efeb05c9600000000000000000000001e": "USDC-USDT", + "0x55bec22f8f6c69137ceaf284d9b441db1b9bfedc000200000000000000000011": "EURe/USDC" + }, + "base": { + "0xc771c1a5905420daec317b154eb13e4198ba97d0000000000000000000000023": "rETH-WETH-BPT", + "0xfb4c2e6e6e27b5b4a07a36360c89ede29bb3c9b6000000000000000000000026": "cbETH/WETH", + "0x0c659734f1eef9c63b7ebdf78a164cdd745586db000000000000000000000046": "USDC/USDbC/axlUSDC" + } +} \ No newline at end of file diff --git a/tests/test_core_pools.py b/tests/test_core_pools.py new file mode 100644 index 00000000..70e14a20 --- /dev/null +++ b/tests/test_core_pools.py @@ -0,0 +1,10 @@ +from gen_core_pools import is_core_pool + + +def test_is_core_pool(): + """ + confirm wstETH-WETH is a core pool + """ + assert is_core_pool( + "mainnet", "0x93d199263632a4ef4bb438f1feb99e57b4b5f0bd0000000000000000000005c2" + )