|
| 1 | +import requests |
| 2 | +from eth_utils import to_checksum_address |
| 3 | +from hexbytes import HexBytes |
| 4 | +from web3.types import FilterParams, LogReceipt |
| 5 | + |
| 6 | +ENDPOINTS = { |
| 7 | + 16600: 'https://v2.archive.subsquid.io/network/0g-testnet', |
| 8 | + 41455: 'https://v2.archive.subsquid.io/network/aleph-zero-evm-mainnet', |
| 9 | + 42161: 'https://v2.archive.subsquid.io/network/arbitrum-one', |
| 10 | + 42170: 'https://v2.archive.subsquid.io/network/arbitrum-nova', |
| 11 | + 421614: 'https://v2.archive.subsquid.io/network/arbitrum-sepolia', |
| 12 | + 10242: 'https://v2.archive.subsquid.io/network/arthera-mainnet', |
| 13 | + 592: 'https://v2.archive.subsquid.io/network/astar-mainnet', |
| 14 | + 3776: 'https://v2.archive.subsquid.io/network/astar-zkevm-mainnet', |
| 15 | + 6038361: 'https://v2.archive.subsquid.io/network/astar-zkyoto', |
| 16 | + 43114: 'https://v2.archive.subsquid.io/network/avalanche-mainnet', |
| 17 | + 43113: 'https://v2.archive.subsquid.io/network/avalanche-testnet', |
| 18 | + 8333: 'https://v2.archive.subsquid.io/network/b3-mainnet', |
| 19 | + 1993: 'https://v2.archive.subsquid.io/network/b3-sepolia', |
| 20 | + 8453: 'https://v2.archive.subsquid.io/network/base-mainnet', |
| 21 | + 84532: 'https://v2.archive.subsquid.io/network/base-sepolia', |
| 22 | + 80084: 'https://v2.archive.subsquid.io/network/berachain-bartio', |
| 23 | + 56: 'https://v2.archive.subsquid.io/network/binance-mainnet', |
| 24 | + 97: 'https://v2.archive.subsquid.io/network/binance-testnet', |
| 25 | + 355110: 'https://v2.archive.subsquid.io/network/bitfinity-mainnet', |
| 26 | + 355113: 'https://v2.archive.subsquid.io/network/bitfinity-testnet', |
| 27 | + 32520: 'https://v2.archive.subsquid.io/network/bitgert-mainnet', |
| 28 | + 64668: 'https://v2.archive.subsquid.io/network/bitgert-testnet', |
| 29 | + 81457: 'https://v2.archive.subsquid.io/network/blast-l2-mainnet', |
| 30 | + 168587773: 'https://v2.archive.subsquid.io/network/blast-sepolia', |
| 31 | + 60808: 'https://v2.archive.subsquid.io/network/bob-mainnet', |
| 32 | + 7700: 'https://v2.archive.subsquid.io/network/canto', |
| 33 | + 7701: 'https://v2.archive.subsquid.io/network/canto-testnet', |
| 34 | + 44787: 'https://v2.archive.subsquid.io/network/celo-alfajores-testnet', |
| 35 | + 42220: 'https://v2.archive.subsquid.io/network/celo-mainnet', |
| 36 | + 1116: 'https://v2.archive.subsquid.io/network/core-mainnet', |
| 37 | + 4157: 'https://v2.archive.subsquid.io/network/crossfi-testnet', |
| 38 | + 7560: 'https://v2.archive.subsquid.io/network/cyber-mainnet', |
| 39 | + 111557560: 'https://v2.archive.subsquid.io/network/cyberconnect-l2-testnet', |
| 40 | + 666666666: 'https://v2.archive.subsquid.io/network/degen-chain', |
| 41 | + 53935: 'https://v2.archive.subsquid.io/network/dfk-chain', |
| 42 | + 2000: 'https://v2.archive.subsquid.io/network/dogechain-mainnet', |
| 43 | + 568: 'https://v2.archive.subsquid.io/network/dogechain-testnet', |
| 44 | + 17000: 'https://v2.archive.subsquid.io/network/ethereum-holesky', |
| 45 | + 1: 'https://v2.archive.subsquid.io/network/ethereum-mainnet', |
| 46 | + 11155111: 'https://v2.archive.subsquid.io/network/ethereum-sepolia', |
| 47 | + 42793: 'https://v2.archive.subsquid.io/network/etherlink-mainnet', |
| 48 | + 128123: 'https://v2.archive.subsquid.io/network/etherlink-testnet', |
| 49 | + 2109: 'https://v2.archive.subsquid.io/network/exosama', |
| 50 | + 250: 'https://v2.archive.subsquid.io/network/fantom-mainnet', |
| 51 | + 4002: 'https://v2.archive.subsquid.io/network/fantom-testnet', |
| 52 | + 14: 'https://v2.archive.subsquid.io/network/flare-mainnet', |
| 53 | + 1625: 'https://v2.archive.subsquid.io/network/galxe-gravity', |
| 54 | + 88153591557: 'https://v2.archive.subsquid.io/network/gelato-arbitrum-blueberry', |
| 55 | + 123420111: 'https://v2.archive.subsquid.io/network/gelato-opcelestia-raspberry', |
| 56 | + 100: 'https://v2.archive.subsquid.io/network/gnosis-mainnet', |
| 57 | + 13371: 'https://v2.archive.subsquid.io/network/immutable-zkevm-mainnet', |
| 58 | + 13473: 'https://v2.archive.subsquid.io/network/immutable-zkevm-testnet', |
| 59 | + 1998: 'https://v2.archive.subsquid.io/network/kyoto-testnet', |
| 60 | + 59144: 'https://v2.archive.subsquid.io/network/linea-mainnet', |
| 61 | + 169: 'https://v2.archive.subsquid.io/network/manta-pacific', |
| 62 | + 3441006: 'https://v2.archive.subsquid.io/network/manta-pacific-sepolia', |
| 63 | + 5000: 'https://v2.archive.subsquid.io/network/mantle-mainnet', |
| 64 | + 5003: 'https://v2.archive.subsquid.io/network/mantle-sepolia', |
| 65 | + 4200: 'https://v2.archive.subsquid.io/network/merlin-mainnet', |
| 66 | + 686868: 'https://v2.archive.subsquid.io/network/merlin-testnet', |
| 67 | + 1088: 'https://v2.archive.subsquid.io/network/metis-mainnet', |
| 68 | + 34443: 'https://v2.archive.subsquid.io/network/mode-mainnet', |
| 69 | + 1287: 'https://v2.archive.subsquid.io/network/moonbase-testnet', |
| 70 | + 1284: 'https://v2.archive.subsquid.io/network/moonbeam-mainnet', |
| 71 | + 1285: 'https://v2.archive.subsquid.io/network/moonriver-mainnet', |
| 72 | + 42225: 'https://v2.archive.subsquid.io/network/nakachain', |
| 73 | + 245022934: 'https://v2.archive.subsquid.io/network/neon-devnet', |
| 74 | + 245022926: 'https://v2.archive.subsquid.io/network/neon-mainnet', |
| 75 | + 204: 'https://v2.archive.subsquid.io/network/opbnb-mainnet', |
| 76 | + 5611: 'https://v2.archive.subsquid.io/network/opbnb-testnet', |
| 77 | + 10: 'https://v2.archive.subsquid.io/network/optimism-mainnet', |
| 78 | + 11155420: 'https://v2.archive.subsquid.io/network/optimism-sepolia', |
| 79 | + 3338: 'https://v2.archive.subsquid.io/network/peaq-mainnet', |
| 80 | + 98864: 'https://v2.archive.subsquid.io/network/plume-testnet', |
| 81 | + 137: 'https://v2.archive.subsquid.io/network/polygon-mainnet', |
| 82 | + 80002: 'https://v2.archive.subsquid.io/network/polygon-amoy-testnet', |
| 83 | + 1101: 'https://v2.archive.subsquid.io/network/polygon-zkevm-mainnet', |
| 84 | + 2442: 'https://v2.archive.subsquid.io/network/polygon-zkevm-cardona-testnet', |
| 85 | + 97072271: 'https://v2.archive.subsquid.io/network/prom-testnet', |
| 86 | + 584548796: 'https://v2.archive.subsquid.io/network/prom-testnet-v2', |
| 87 | + 157: 'https://v2.archive.subsquid.io/network/puppynet', |
| 88 | + 534352: 'https://v2.archive.subsquid.io/network/scroll-mainnet', |
| 89 | + 534351: 'https://v2.archive.subsquid.io/network/scroll-sepolia', |
| 90 | + 109: 'https://v2.archive.subsquid.io/network/shibarium', |
| 91 | + 81: 'https://v2.archive.subsquid.io/network/shibuya-testnet', |
| 92 | + 336: 'https://v2.archive.subsquid.io/network/shiden-mainnet', |
| 93 | + 1482601649: 'https://v2.archive.subsquid.io/network/skale-nebula', |
| 94 | + 64165: 'https://v2.archive.subsquid.io/network/sonic-testnet', |
| 95 | + 93747: 'https://v2.archive.subsquid.io/network/stratovm-sepolia', |
| 96 | + 53302: 'https://v2.archive.subsquid.io/network/superseed-sepolia', |
| 97 | + 167000: 'https://v2.archive.subsquid.io/network/taiko-mainnet', |
| 98 | + 5678: 'https://v2.archive.subsquid.io/network/tanssi', |
| 99 | + 196: 'https://v2.archive.subsquid.io/network/xlayer-mainnet', |
| 100 | + 195: 'https://v2.archive.subsquid.io/network/xlayer-testnet', |
| 101 | + 810180: 'https://v2.archive.subsquid.io/network/zklink-nova-mainnet', |
| 102 | + 324: 'https://v2.archive.subsquid.io/network/zksync-mainnet', |
| 103 | + 300: 'https://v2.archive.subsquid.io/network/zksync-sepolia', |
| 104 | + 7777777: 'https://v2.archive.subsquid.io/network/zora-mainnet', |
| 105 | + 999999999: 'https://v2.archive.subsquid.io/network/zora-sepolia', |
| 106 | +} |
| 107 | + |
| 108 | +def get_text(url: str) -> str: |
| 109 | + res = requests.get(url) |
| 110 | + res.raise_for_status() |
| 111 | + return res.text |
| 112 | + |
| 113 | +def get_filter( |
| 114 | + chain_id: int, |
| 115 | + filter_params: FilterParams, |
| 116 | + partial_allowed=False |
| 117 | +) -> tuple[int, list[LogReceipt]]: |
| 118 | + if chain_id not in ENDPOINTS: |
| 119 | + raise ValueError(f"Subsquid does not support Chain ID {chain_id}") |
| 120 | + |
| 121 | + gateway_url = ENDPOINTS[chain_id] |
| 122 | + |
| 123 | + assert isinstance(filter_params['fromBlock'], int) |
| 124 | + assert isinstance(filter_params['toBlock'], int) |
| 125 | + from_block: int = filter_params['fromBlock'] |
| 126 | + to_block: int = filter_params['toBlock'] |
| 127 | + |
| 128 | + latest_block = int(get_text(f"{gateway_url}/height")) |
| 129 | + |
| 130 | + if from_block > latest_block: |
| 131 | + raise ValueError(f"Subsquid has only indexed till block {latest_block}") |
| 132 | + |
| 133 | + if to_block > latest_block: |
| 134 | + if partial_allowed: |
| 135 | + to_block = latest_block |
| 136 | + else: |
| 137 | + raise ValueError(f"Subsquid has only indexed till block {latest_block}") |
| 138 | + |
| 139 | + query = { |
| 140 | + "toBlock": to_block, |
| 141 | + "logs": [{}], |
| 142 | + "fields": { |
| 143 | + "log": { |
| 144 | + "address": True, |
| 145 | + "topics": True, |
| 146 | + "data": True, |
| 147 | + "transactionHash": True, |
| 148 | + } |
| 149 | + } |
| 150 | + } |
| 151 | + if "address" in filter_params: |
| 152 | + addresses = filter_params['address'] |
| 153 | + if isinstance(addresses, str): |
| 154 | + addresses = [addresses] |
| 155 | + query["logs"][0]["address"] = [address.lower() for address in addresses] |
| 156 | + if "topics" in filter_params: |
| 157 | + topics = filter_params["topics"] |
| 158 | + assert len(topics) <= 4 |
| 159 | + for i in range(len(topics)): |
| 160 | + topic: str | list[str] |
| 161 | + if isinstance(topics[i], str): |
| 162 | + topic = [topics[i]] |
| 163 | + else: |
| 164 | + try: |
| 165 | + topic = [single_topic.hex() for single_topic in topics[i]] |
| 166 | + except Exception: |
| 167 | + topic = [topics[i].hex()] |
| 168 | + query["logs"][0][f"topic{i}"] = topic |
| 169 | + |
| 170 | + logs: list[LogReceipt] = [] |
| 171 | + while from_block <= to_block: |
| 172 | + worker_url = get_text(f'{gateway_url}/{from_block}/worker') |
| 173 | + |
| 174 | + query['fromBlock'] = from_block |
| 175 | + res = requests.post(worker_url, json=query) |
| 176 | + res.raise_for_status() |
| 177 | + blocks = res.json() |
| 178 | + |
| 179 | + last_processed_block = blocks[-1]['header']['number'] |
| 180 | + from_block = last_processed_block + 1 |
| 181 | + |
| 182 | + for block in blocks: |
| 183 | + for log in block['logs']: |
| 184 | + logs.append(LogReceipt( |
| 185 | + address=to_checksum_address(log['address']), |
| 186 | + blockHash=block["header"]["hash"], |
| 187 | + blockNumber=block["header"]["number"], |
| 188 | + data=HexBytes(log["data"]), |
| 189 | + logIndex=log["logIndex"], |
| 190 | + topics=[HexBytes(topic) for topic in log["topics"]], |
| 191 | + transactionHash=HexBytes(log["transactionHash"]), |
| 192 | + transactionIndex=log["transactionIndex"], |
| 193 | + removed=False, |
| 194 | + )) |
| 195 | + return from_block, logs |
0 commit comments