• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

safe-global / safe-eth-py / 13388974656

18 Feb 2025 10:56AM UTC coverage: 93.946% (-0.004%) from 93.95%
13388974656

Pull #1518

github

web-flow
Merge 4ce6371ba into 8b28bc770
Pull Request #1518: Add custom rate limit for aiohtpp

58 of 62 new or added lines in 3 files covered. (93.55%)

8 existing lines in 3 files now uncovered.

9543 of 10158 relevant lines covered (93.95%)

2.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.87
/safe_eth/eth/clients/etherscan_client.py
1
import json
3✔
2
import os
3✔
3
import time
3✔
4
from typing import Any, Dict, List, MutableMapping, Optional, Union
3✔
5
from urllib.parse import urljoin
3✔
6

7
from ...util.http import prepare_http_session
3✔
8
from .. import EthereumNetwork
3✔
9
from .contract_metadata import ContractMetadata
3✔
10

11

12
class EtherscanClientException(Exception):
3✔
13
    pass
3✔
14

15

16
class EtherscanClientConfigurationProblem(Exception):
3✔
17
    pass
3✔
18

19

20
class EtherscanRateLimitError(EtherscanClientException):
3✔
21
    pass
3✔
22

23

24
class EtherscanClient:
3✔
25
    NETWORK_WITH_URL = {
3✔
26
        EthereumNetwork.MAINNET: "https://etherscan.io",
27
        EthereumNetwork.RINKEBY: "https://rinkeby.etherscan.io",
28
        EthereumNetwork.ROPSTEN: "https://ropsten.etherscan.io",
29
        EthereumNetwork.GOERLI: "https://goerli.etherscan.io",
30
        EthereumNetwork.BNB_SMART_CHAIN_MAINNET: "https://bscscan.com",
31
        EthereumNetwork.POLYGON: "https://polygonscan.com",
32
        EthereumNetwork.POLYGON_ZKEVM: "https://zkevm.polygonscan.com",
33
        EthereumNetwork.OPTIMISM: "https://optimistic.etherscan.io",
34
        EthereumNetwork.ARBITRUM_ONE: "https://arbiscan.io",
35
        EthereumNetwork.ARBITRUM_NOVA: "https://nova.arbiscan.io",
36
        EthereumNetwork.ARBITRUM_GOERLI: "https://goerli.arbiscan.io",
37
        EthereumNetwork.AVALANCHE_C_CHAIN: "https://snowtrace.io",
38
        EthereumNetwork.GNOSIS: "https://gnosisscan.io",
39
        EthereumNetwork.MOONBEAM: "https://moonscan.io",
40
        EthereumNetwork.MOONRIVER: "https://moonriver.moonscan.io",
41
        EthereumNetwork.MOONBASE_ALPHA: "https://moonbase.moonscan.io",
42
        EthereumNetwork.CRONOS_MAINNET: "https://cronoscan.com",
43
        EthereumNetwork.CRONOS_TESTNET: "https://testnet.cronoscan.com",
44
        EthereumNetwork.CELO_MAINNET: "https://celoscan.io",
45
        EthereumNetwork.BASE_GOERLI_TESTNET: "https://goerli.basescan.org",
46
        EthereumNetwork.NEON_EVM_DEVNET: "https://devnet.neonscan.org",
47
        EthereumNetwork.NEON_EVM_MAINNET: "https://neonscan.org",
48
        EthereumNetwork.SEPOLIA: "https://sepolia.etherscan.io",
49
        EthereumNetwork.ZKSYNC_MAINNET: "https://era.zksync.network/",
50
        EthereumNetwork.FANTOM_OPERA: "https://ftmscan.com",
51
        EthereumNetwork.FANTOM_TESTNET: "https://testnet.ftmscan.com/",
52
        EthereumNetwork.LINEA: "https://lineascan.build",
53
        EthereumNetwork.LINEA_GOERLI: "https://goerli.lineascan.build",
54
        EthereumNetwork.MANTLE: "https://mantlescan.xyz/",
55
        EthereumNetwork.MANTLE_TESTNET: "https://explorer.testnet.mantle.xyz",
56
        EthereumNetwork.JAPAN_OPEN_CHAIN_MAINNET: "https://mainnet.japanopenchain.org",
57
        EthereumNetwork.JAPAN_OPEN_CHAIN_TESTNET: "https://explorer.testnet.japanopenchain.org",
58
        EthereumNetwork.SCROLL_SEPOLIA_TESTNET: "https://sepolia.scrollscan.com",
59
        EthereumNetwork.SCROLL: "https://scrollscan.com",
60
        EthereumNetwork.KROMA: "https://blockscout.kroma.network",
61
        EthereumNetwork.KROMA_SEPOLIA: "https://blockscout.sepolia.kroma.network",
62
        EthereumNetwork.BLAST_SEPOLIA_TESTNET: "https://sepolia.blastscan.io",
63
        EthereumNetwork.FRAXTAL: "https://fraxscan.com",
64
        EthereumNetwork.BASE: "https://api.basescan.org/",
65
        EthereumNetwork.BLAST: "https://blastscan.io",
66
        EthereumNetwork.TAIKO_MAINNET: "https://taikoscan.io",
67
        EthereumNetwork.BASE_SEPOLIA_TESTNET: "https://sepolia.basescan.org",
68
        EthereumNetwork.HOLESKY: "https://holesky.etherscan.io",
69
        EthereumNetwork.LINEA_SEPOLIA: "https://sepolia.lineascan.build",
70
        EthereumNetwork.METIS_ANDROMEDA_MAINNET: "https://explorer.metis.io",
71
        EthereumNetwork.DOGECHAIN_MAINNET: "https://explorer.dogechain.dog",
72
        EthereumNetwork.FUSE_SPARKNET: "https://explorer.fusespark.io",
73
        EthereumNetwork.PLUME_TESTNET: "https://testnet-explorer.plumenetwork.xyz",
74
        EthereumNetwork.CHILIZ_CHAIN_MAINNET: "https://scan.chiliz.com",
75
        EthereumNetwork.GNOSIS_CHIADO_TESTNET: "https://blockscout.chiadochain.net",
76
        EthereumNetwork.AMOY: "https://amoy.polygonscan.com",
77
        EthereumNetwork.PGN_PUBLIC_GOODS_NETWORK: "https://explorer.publicgoods.network",
78
        EthereumNetwork.EDGEWARE_EDGEEVM_MAINNET: "https://edgscan.live",
79
        EthereumNetwork.BEAR_NETWORK_CHAIN_MAINNET: "https://brnkscan.bearnetwork.net",
80
        EthereumNetwork.THAICHAIN: "https://exp.thaichain.org",
81
        EthereumNetwork.FUSE_MAINNET: "https://explorer.fuse.io",
82
        EthereumNetwork.FLUENCE_TESTNET: "https://blockscout.testnet.fluence.dev",
83
        EthereumNetwork.PULSECHAIN: "https://scan.pulsechain.com",
84
        EthereumNetwork.IMMUTABLE_ZKEVM: "https://explorer.immutable.com",
85
        EthereumNetwork.BAHAMUT: "https://www.ftnscan.com",
86
        EthereumNetwork.ASSET_CHAIN_TESTNET: "https://scan-testnet.assetchain.org",
87
        EthereumNetwork.LISK: "https://blockscout.lisk.com",
88
        EthereumNetwork.NEXI_MAINNET: "https://www.nexiscan.com",
89
        EthereumNetwork.MINATO: "https://explorer-testnet.soneium.org",
90
        EthereumNetwork.EOS_EVM_NETWORK_TESTNET: "https://explorer.testnet.evm.eosnetwork.com",
91
        EthereumNetwork.BITTORRENT_CHAIN_MAINNET: "https://bttcscan.com",
92
        EthereumNetwork.RSS3_VSL_SEPOLIA_TESTNET: "https://scan.testnet.rss3.io",
93
        EthereumNetwork.BITKUB_CHAIN: "https://www.bkcscan.com",
94
        EthereumNetwork.METAL_L2: "https://explorer.metall2.com",
95
        EthereumNetwork.SHIMMEREVM: "https://explorer.evm.shimmer.network",
96
        EthereumNetwork.HAQQ_CHAIN_TESTNET: "https://explorer.testedge2.haqq.network",
97
        EthereumNetwork.OASYS_MAINNET: "https://scan.oasys.games",
98
        EthereumNetwork.MANTA_PACIFIC_MAINNET: "https://pacific-explorer.manta.network",
99
        EthereumNetwork.FRAXTAL_TESTNET: "https://holesky.fraxscan.com",
100
        EthereumNetwork.ACALA_NETWORK: "https://blockscout.acala.network",
101
        EthereumNetwork.ANCIENT8_TESTNET: "https://scanv2-testnet.ancient8.gg",
102
        EthereumNetwork.FLARE_TESTNET_COSTON2: "https://coston2-explorer.flare.network",
103
        EthereumNetwork.PUPPYNET: "https://puppyscan.shib.io",
104
        EthereumNetwork.ROLLUX_MAINNET: "https://explorer.rollux.com",
105
        EthereumNetwork.ZKSYNC_SEPOLIA_TESTNET: "https://sepolia-era.zksync.network/",
106
        EthereumNetwork.ATLETA_OLYMPIA: "https://blockscout.atleta.network",
107
        EthereumNetwork.CELO_ALFAJORES_TESTNET: "https://celo-alfajores.blockscout.com",
108
        EthereumNetwork.GRAVITY_ALPHA_MAINNET: "https://explorer.gravity.xyz",
109
        EthereumNetwork.ANCIENT8: "https://scan.ancient8.gg",
110
        EthereumNetwork.DCHAIN_TESTNET: "https://dchaintestnet-2713017997578000-1.testnet.sagaexplorer.io",
111
        EthereumNetwork.SYSCOIN_MAINNET: "https://explorer.syscoin.org",
112
        EthereumNetwork.TENET: "https://tenetscan.io",
113
        EthereumNetwork.JIBCHAIN_L1: "https://exp-l1.jibchain.net",
114
        EthereumNetwork.IOTA_EVM_TESTNET: "https://explorer.evm.testnet.iotaledger.net",
115
        EthereumNetwork.IOTA_EVM: "https://explorer.evm.iota.org",
116
        EthereumNetwork.BITKUB_CHAIN_TESTNET: "https://testnet.bkcscan.com",
117
        EthereumNetwork.ROLLUX_TESTNET: "https://rollux.tanenbaum.io",
118
        EthereumNetwork.MANTA_PACIFIC_TESTNET: "https://pacific-explorer.testnet.manta.network",
119
        EthereumNetwork.HAQQ_NETWORK: "https://explorer.haqq.network",
120
        EthereumNetwork.ACALA_MANDALA_TESTNET_TC9: "https://blockscout.mandala.aca-staging.network",
121
        EthereumNetwork.BERESHEET_BEREEVM_TESTNET: "https://testnet.edgscan.live",
122
        EthereumNetwork.SEPOLIA_PGN_PUBLIC_GOODS_NETWORK: "https://explorer.sepolia.publicgoods.network",
123
        EthereumNetwork.LISK_SEPOLIA_TESTNET: "https://sepolia-blockscout.lisk.com",
124
        EthereumNetwork.ZORA: "https://explorer.zora.energy",
125
        EthereumNetwork.SATOSHIVM_ALPHA_MAINNET: "https://svmscan.io",
126
        EthereumNetwork.MANTLE_SEPOLIA_TESTNET: "https://explorer.sepolia.mantle.xyz/",
127
        EthereumNetwork.SHIMMEREVM_TESTNET: "https://explorer.evm.testnet.shimmer.network",
128
        EthereumNetwork.BEVM_MAINNET: "https://scan-mainnet.bevm.io",
129
        EthereumNetwork.CHILIZ_SPICY_TESTNET: "http://spicy-explorer.chiliz.com",
130
        EthereumNetwork.DCHAIN: "https://dchain-2716446429837000-1.sagaexplorer.io",
131
        EthereumNetwork.ZORA_SEPOLIA_TESTNET: "https://sepolia.explorer.zora.energy/",
132
        EthereumNetwork.BLACKFORT_EXCHANGE_NETWORK_TESTNET: "https://testnet-explorer.blackfort.network",
133
        EthereumNetwork.OP_SEPOLIA_TESTNET: "https://optimism-sepolia.blockscout.com",
134
        EthereumNetwork.SATOSHIVM_TESTNET: "https://testnet.svmscan.io",
135
        EthereumNetwork.LUKSO_TESTNET: "https://explorer.execution.testnet.lukso.network",
136
        EthereumNetwork.CROSSBELL: "https://scan.crossbell.io",
137
        EthereumNetwork.LUKSO_MAINNET: "https://explorer.execution.mainnet.lukso.network",
138
        EthereumNetwork.FLUENCE: "https://blockscout.mainnet.fluence.dev",
139
        EthereumNetwork.SONGBIRD_CANARY_NETWORK: "https://songbird-explorer.flare.network",
140
        EthereumNetwork.BLACKFORT_EXCHANGE_NETWORK: "https://explorer.blackfort.network",
141
        EthereumNetwork.RSS3_VSL_MAINNET: "https://scan.rss3.io",
142
        EthereumNetwork.MANTA_PACIFIC_SEPOLIA_TESTNET: "https://pacific-explorer.sepolia-testnet.manta.network",
143
        EthereumNetwork.KARURA_NETWORK: "https://blockscout.karura.network",
144
        EthereumNetwork.APEX_TESTNET: "https://exp-testnet.apexlayer.xyz",
145
        EthereumNetwork.MODE_TESTNET: "https://sepolia.explorer.mode.network",
146
        EthereumNetwork.ARBITRUM_SEPOLIA: "https://sepolia.arbiscan.io",
147
        EthereumNetwork.SONGBIRD_TESTNET_COSTON: "https://coston-explorer.flare.network",
148
        EthereumNetwork.FLARE_MAINNET: "https://flare-explorer.flare.network",
149
        EthereumNetwork.FLUENCE_STAGE: "https://blockscout.stage.fluence.dev",
150
        EthereumNetwork.Q_TESTNET: "https://explorer.qtestnet.org",
151
        EthereumNetwork.ARTELA_TESTNET: "https://betanet-scan.artela.network",
152
        EthereumNetwork.EOS_EVM_NETWORK: "https://explorer.evm.eosnetwork.com",
153
        EthereumNetwork.SHAPE_SEPOLIA_TESTNET: "https://explorer-sepolia.shape.network",
154
        EthereumNetwork.SHAPE: "https://shapescan.xyz",
155
        EthereumNetwork.FASTEX_CHAIN_BAHAMUT_OASIS_TESTNET: "https://oasis.ftnscan.com",
156
        EthereumNetwork.ASSET_CHAIN_MAINNET: "https://scan.assetchain.org",
157
        EthereumNetwork.PHOENIX_MAINNET: "https://phoenixplorer.com",
158
        EthereumNetwork.SNAXCHAIN: "https://explorer.snaxchain.io",
159
        EthereumNetwork.ZKFAIR_MAINNET: "https://scan.zkfair.io",
160
        EthereumNetwork.SONIC_MAINNET: "https://sonicscan.org",
161
        EthereumNetwork.BERACHAIN: "https://berascan.com",
162
    }
163

164
    NETWORK_WITH_API_URL = {
3✔
165
        EthereumNetwork.MAINNET: "https://api.etherscan.io",
166
        EthereumNetwork.RINKEBY: "https://api-rinkeby.etherscan.io",
167
        EthereumNetwork.ROPSTEN: "https://api-ropsten.etherscan.io",
168
        EthereumNetwork.GOERLI: "https://api-goerli.etherscan.io",
169
        EthereumNetwork.BNB_SMART_CHAIN_MAINNET: "https://api.bscscan.com",
170
        EthereumNetwork.POLYGON: "https://api.polygonscan.com",
171
        EthereumNetwork.POLYGON_ZKEVM: "https://api-zkevm.polygonscan.com",
172
        EthereumNetwork.OPTIMISM: "https://api-optimistic.etherscan.io",
173
        EthereumNetwork.ARBITRUM_ONE: "https://api.arbiscan.io",
174
        EthereumNetwork.ARBITRUM_NOVA: "https://api-nova.arbiscan.io",
175
        EthereumNetwork.ARBITRUM_GOERLI: "https://api-goerli.arbiscan.io",
176
        EthereumNetwork.ARBITRUM_SEPOLIA: "https://api-sepolia.arbiscan.io",
177
        EthereumNetwork.AVALANCHE_C_CHAIN: "https://api.snowtrace.io",
178
        EthereumNetwork.GNOSIS: "https://api.gnosisscan.io",
179
        EthereumNetwork.MOONBEAM: "https://api-moonbeam.moonscan.io",
180
        EthereumNetwork.MOONRIVER: "https://api-moonriver.moonscan.io",
181
        EthereumNetwork.MOONBASE_ALPHA: "https://api-moonbase.moonscan.io",
182
        EthereumNetwork.CRONOS_MAINNET: "https://api.cronoscan.com",
183
        EthereumNetwork.CRONOS_TESTNET: "https://api-testnet.cronoscan.com",
184
        EthereumNetwork.CELO_MAINNET: "https://api.celoscan.io",
185
        EthereumNetwork.BASE_GOERLI_TESTNET: "https://api-goerli.basescan.org",
186
        EthereumNetwork.NEON_EVM_DEVNET: "https://devnet-api.neonscan.org",
187
        EthereumNetwork.NEON_EVM_MAINNET: "https://api.neonscan.org",
188
        EthereumNetwork.SEPOLIA: "https://api-sepolia.etherscan.io",
189
        EthereumNetwork.ZKSYNC_MAINNET: "https://api-era.zksync.network",
190
        EthereumNetwork.FANTOM_OPERA: "https://api.ftmscan.com",
191
        EthereumNetwork.FANTOM_TESTNET: "https://api-testnet.ftmscan.com",
192
        EthereumNetwork.LINEA: "https://api.lineascan.build",
193
        EthereumNetwork.LINEA_GOERLI: "https://api-testnet.lineascan.build",
194
        EthereumNetwork.MANTLE: "https://api.mantlescan.xyz",
195
        EthereumNetwork.MANTLE_TESTNET: "https://explorer.testnet.mantle.xyz",
196
        EthereumNetwork.JAPAN_OPEN_CHAIN_MAINNET: "https://mainnet.japanopenchain.org/api",
197
        EthereumNetwork.JAPAN_OPEN_CHAIN_TESTNET: "https://explorer.testnet.japanopenchain.org/api",
198
        EthereumNetwork.SCROLL_SEPOLIA_TESTNET: "https://api-sepolia.scrollscan.com",
199
        EthereumNetwork.SCROLL: "https://api.scrollscan.com",
200
        EthereumNetwork.KROMA: "https://blockscout.kroma.network",
201
        EthereumNetwork.KROMA_SEPOLIA: "https://blockscout.sepolia.kroma.network",
202
        EthereumNetwork.BLAST_SEPOLIA_TESTNET: "https://api-sepolia.blastscan.io",
203
        EthereumNetwork.FRAXTAL: "https://api.fraxscan.com",
204
        EthereumNetwork.BASE: "https://api.basescan.org",
205
        EthereumNetwork.BLAST: "https://api.blastscan.io",
206
        EthereumNetwork.TAIKO_MAINNET: "https://api.taikoscan.io",
207
        EthereumNetwork.BASE_SEPOLIA_TESTNET: "https://api-sepolia.basescan.org",
208
        EthereumNetwork.HOLESKY: "https://api-holesky.etherscan.io",
209
        EthereumNetwork.LINEA_SEPOLIA: "https://api-sepolia.lineascan.build",
210
        EthereumNetwork.METIS_ANDROMEDA_MAINNET: "https://api.routescan.io/v2/network/mainnet/evm/1088/etherscan",
211
        EthereumNetwork.DOGECHAIN_MAINNET: "https://explorer.dogechain.dog",
212
        EthereumNetwork.FUSE_SPARKNET: "https://explorer.fusespark.io",
213
        EthereumNetwork.PLUME_TESTNET: "https://testnet-explorer.plumenetwork.xyz",
214
        EthereumNetwork.CHILIZ_CHAIN_MAINNET: "https://scan.chiliz.com",
215
        EthereumNetwork.GNOSIS_CHIADO_TESTNET: "https://blockscout.chiadochain.net",
216
        EthereumNetwork.AMOY: "https://api-amoy.polygonscan.com",
217
        EthereumNetwork.PGN_PUBLIC_GOODS_NETWORK: "https://explorer.publicgoods.network",
218
        EthereumNetwork.EDGEWARE_EDGEEVM_MAINNET: "https://edgscan.live",
219
        EthereumNetwork.BEAR_NETWORK_CHAIN_MAINNET: "https://brnkscan.bearnetwork.net",
220
        EthereumNetwork.THAICHAIN: "https://exp.thaichain.org",
221
        EthereumNetwork.FUSE_MAINNET: "https://explorer.fuse.io",
222
        EthereumNetwork.FLUENCE_TESTNET: "https://blockscout.testnet.fluence.dev",
223
        EthereumNetwork.PULSECHAIN: "https://api.scan.pulsechain.com",
224
        EthereumNetwork.IMMUTABLE_ZKEVM: "https://explorer.immutable.com",
225
        EthereumNetwork.BAHAMUT: "https://www.ftnscan.com",
226
        EthereumNetwork.ASSET_CHAIN_TESTNET: "https://scan-testnet.assetchain.org",
227
        EthereumNetwork.LISK: "https://blockscout.lisk.com",
228
        EthereumNetwork.NEXI_MAINNET: "https://www.nexiscan.com",
229
        EthereumNetwork.MINATO: "https://explorer-testnet.soneium.org",
230
        EthereumNetwork.EOS_EVM_NETWORK_TESTNET: "https://explorer.testnet.evm.eosnetwork.com",
231
        EthereumNetwork.BITTORRENT_CHAIN_MAINNET: "https://api.bttcscan.com",
232
        EthereumNetwork.RSS3_VSL_SEPOLIA_TESTNET: "https://scan.testnet.rss3.io",
233
        EthereumNetwork.BITKUB_CHAIN: "https://www.bkcscan.com",
234
        EthereumNetwork.METAL_L2: "https://explorer.metall2.com",
235
        EthereumNetwork.SHIMMEREVM: "https://explorer.evm.shimmer.network",
236
        EthereumNetwork.HAQQ_CHAIN_TESTNET: "https://explorer.testedge2.haqq.network",
237
        EthereumNetwork.OASYS_MAINNET: "https://scan.oasys.games",
238
        EthereumNetwork.MANTA_PACIFIC_MAINNET: "https://pacific-explorer.manta.network",
239
        EthereumNetwork.FRAXTAL_TESTNET: "https://api-holesky.fraxscan.com",
240
        EthereumNetwork.ACALA_NETWORK: "https://blockscout.acala.network",
241
        EthereumNetwork.ANCIENT8_TESTNET: "https://scanv2-testnet.ancient8.gg",
242
        EthereumNetwork.FLARE_TESTNET_COSTON2: "https://coston2-explorer.flare.network",
243
        EthereumNetwork.PUPPYNET: "https://puppyscan.shib.io",
244
        EthereumNetwork.ROLLUX_MAINNET: "https://explorer.rollux.com",
245
        EthereumNetwork.ZKSYNC_SEPOLIA_TESTNET: "https://api-sepolia-era.zksync.network",
246
        EthereumNetwork.ATLETA_OLYMPIA: "https://blockscout.atleta.network",
247
        EthereumNetwork.CELO_ALFAJORES_TESTNET: "https://celo-alfajores.blockscout.com",
248
        EthereumNetwork.GRAVITY_ALPHA_MAINNET: "https://explorer.gravity.xyz",
249
        EthereumNetwork.ANCIENT8: "https://scan.ancient8.gg",
250
        EthereumNetwork.DCHAIN_TESTNET: "https://api-dchaintestnet-2713017997578000-1.testnet.sagaexplorer.io",
251
        EthereumNetwork.SYSCOIN_MAINNET: "https://explorer.syscoin.org",
252
        EthereumNetwork.TENET: "https://tenetscan.io",
253
        EthereumNetwork.JIBCHAIN_L1: "https://exp-l1.jibchain.net",
254
        EthereumNetwork.IOTA_EVM_TESTNET: "https://explorer.evm.testnet.iotaledger.net",
255
        EthereumNetwork.IOTA_EVM: "https://explorer.evm.iota.org",
256
        EthereumNetwork.BITKUB_CHAIN_TESTNET: "https://testnet.bkcscan.com",
257
        EthereumNetwork.ROLLUX_TESTNET: "https://rollux.tanenbaum.io",
258
        EthereumNetwork.MANTA_PACIFIC_TESTNET: "https://pacific-explorer.testnet.manta.network",
259
        EthereumNetwork.HAQQ_NETWORK: "https://explorer.haqq.network",
260
        EthereumNetwork.ACALA_MANDALA_TESTNET_TC9: "https://blockscout.mandala.aca-staging.network",
261
        EthereumNetwork.BERESHEET_BEREEVM_TESTNET: "https://testnet.edgscan.live",
262
        EthereumNetwork.SEPOLIA_PGN_PUBLIC_GOODS_NETWORK: "https://explorer.sepolia.publicgoods.network",
263
        EthereumNetwork.LISK_SEPOLIA_TESTNET: "https://sepolia-blockscout.lisk.com",
264
        EthereumNetwork.ZORA: "https://explorer.zora.energy",
265
        EthereumNetwork.SATOSHIVM_ALPHA_MAINNET: "https://svmscan.io",
266
        EthereumNetwork.MANTLE_SEPOLIA_TESTNET: "https://explorer.sepolia.mantle.xyz",
267
        EthereumNetwork.SHIMMEREVM_TESTNET: "https://explorer.evm.testnet.shimmer.network",
268
        EthereumNetwork.BEVM_MAINNET: "https://scan-mainnet-api.bevm.io",
269
        EthereumNetwork.CHILIZ_SPICY_TESTNET: "http://spicy-explorer.chiliz.com",
270
        EthereumNetwork.DCHAIN: "https://api-dchain-2716446429837000-1.sagaexplorer.io",
271
        EthereumNetwork.ZORA_SEPOLIA_TESTNET: "https://sepolia.explorer.zora.energy",
272
        EthereumNetwork.BLACKFORT_EXCHANGE_NETWORK_TESTNET: "https://testnet-explorer.blackfort.network",
273
        EthereumNetwork.OP_SEPOLIA_TESTNET: "https://optimism-sepolia.blockscout.com",
274
        EthereumNetwork.SATOSHIVM_TESTNET: "https://testnet.svmscan.io",
275
        EthereumNetwork.LUKSO_TESTNET: "https://api.explorer.execution.testnet.lukso.network",
276
        EthereumNetwork.CROSSBELL: "https://scan.crossbell.io",
277
        EthereumNetwork.LUKSO_MAINNET: "https://api.explorer.execution.mainnet.lukso.network",
278
        EthereumNetwork.FLUENCE: "https://blockscout.mainnet.fluence.dev",
279
        EthereumNetwork.SONGBIRD_CANARY_NETWORK: "https://songbird-explorer.flare.network",
280
        EthereumNetwork.BLACKFORT_EXCHANGE_NETWORK: "https://explorer.blackfort.network",
281
        EthereumNetwork.RSS3_VSL_MAINNET: "https://scan.rss3.io",
282
        EthereumNetwork.MANTA_PACIFIC_SEPOLIA_TESTNET: "https://pacific-explorer.sepolia-testnet.manta.network",
283
        EthereumNetwork.KARURA_NETWORK: "https://blockscout.karura.network",
284
        EthereumNetwork.APEX_TESTNET: "https://exp-testnet.apexlayer.xyz",
285
        EthereumNetwork.MODE_TESTNET: "https://sepolia.explorer.mode.network",
286
        EthereumNetwork.SONGBIRD_TESTNET_COSTON: "https://coston-explorer.flare.network",
287
        EthereumNetwork.FLARE_MAINNET: "https://flare-explorer.flare.network",
288
        EthereumNetwork.FLUENCE_STAGE: "https://blockscout.stage.fluence.dev",
289
        EthereumNetwork.Q_TESTNET: "https://explorer.qtestnet.org",
290
        EthereumNetwork.ARTELA_TESTNET: "https://betanet-scan.artela.network",
291
        EthereumNetwork.EOS_EVM_NETWORK: "https://explorer.evm.eosnetwork.com",
292
        EthereumNetwork.SHAPE_SEPOLIA_TESTNET: "https://explorer-sepolia.shape.network",
293
        EthereumNetwork.SHAPE: "https://shapescan.xyz",
294
        EthereumNetwork.FASTEX_CHAIN_BAHAMUT_OASIS_TESTNET: "https://oasis.ftnscan.com",
295
        EthereumNetwork.ASSET_CHAIN_MAINNET: "https://scan.assetchain.org",
296
        EthereumNetwork.PHOENIX_MAINNET: "https://phoenixplorer.com",
297
        EthereumNetwork.SNAXCHAIN: "https://explorer.snaxchain.io",
298
        EthereumNetwork.ZKFAIR_MAINNET: "https://scan.zkfair.io",
299
        EthereumNetwork.SONIC_MAINNET: "https://api.sonicscan.org",
300
        EthereumNetwork.BERACHAIN: "https://api.berascan.com",
301
    }
302
    HTTP_HEADERS: MutableMapping[str, Union[str, bytes]] = {
3✔
303
        "User-Agent": "curl/7.77.0",
304
    }
305

306
    def __init__(
3✔
307
        self,
308
        network: EthereumNetwork,
309
        api_key: Optional[str] = None,
310
        request_timeout: int = int(
311
            os.environ.get("ETHERSCAN_CLIENT_REQUEST_TIMEOUT", 10)
312
        ),
313
    ):
314
        self.network = network
3✔
315
        self.api_key = api_key
3✔
316
        self.base_url = self.NETWORK_WITH_URL.get(network, "")
3✔
317
        self.base_api_url = self.NETWORK_WITH_API_URL.get(network, "")
3✔
318
        if not self.base_api_url:
3✔
319
            raise EtherscanClientConfigurationProblem(
×
320
                f"Network {network.name} - {network.value} not supported"
321
            )
322
        self.http_session = prepare_http_session(10, 100)
3✔
323
        self.http_session.headers = self.HTTP_HEADERS
3✔
324
        self.request_timeout = request_timeout
3✔
325

326
    def build_url(self, query: str):
3✔
327
        url = urljoin(self.base_api_url, f"api?{query}")
3✔
328
        if self.api_key:
3✔
329
            url += f"&apikey={self.api_key}"
3✔
330
        return url
3✔
331

332
    def _do_request(self, url: str) -> Optional[Union[Dict[str, Any], List[Any], str]]:
3✔
333
        response = self.http_session.get(url, timeout=self.request_timeout)
3✔
334

335
        if response.ok:
3✔
336
            response_json = response.json()
3✔
337
            result = response_json["result"]
3✔
338
            if "Max rate limit reached" in result:
3✔
339
                # Max rate limit reached, please use API Key for higher rate limit
340
                raise EtherscanRateLimitError
×
341
            if response_json["status"] == "1":
3✔
342
                return result
3✔
343
        return None
3✔
344

345
    def _retry_request(
3✔
346
        self, url: str, retry: bool = True
347
    ) -> Optional[Union[Dict[str, Any], List[Any], str]]:
348
        for _ in range(3):
3✔
349
            try:
3✔
350
                return self._do_request(url)
3✔
351
            except EtherscanRateLimitError as exc:
×
352
                if not retry:
×
353
                    raise exc
×
354
                else:
355
                    time.sleep(5)
×
356
        return None
×
357

358
    @staticmethod
3✔
359
    def _process_contract_metadata(
3✔
360
        contract_data: Dict[str, Any]
361
    ) -> Optional[ContractMetadata]:
362
        contract_name = contract_data["ContractName"]
3✔
363
        contract_abi = contract_data["ABI"]
3✔
364
        contract_proxy_implementation_address = (
3✔
365
            contract_data.get("Implementation") or None
366
        )
367
        if contract_abi:
3✔
368
            return ContractMetadata(
3✔
369
                contract_name,
370
                contract_abi,
371
                False,
372
                contract_proxy_implementation_address,
373
            )
374
        return None
3✔
375

376
    def get_contract_metadata(
3✔
377
        self, contract_address: str, retry: bool = True
378
    ) -> Optional[ContractMetadata]:
379
        contract_source_code = self.get_contract_source_code(
3✔
380
            contract_address, retry=retry
381
        )
382
        if contract_source_code:
3✔
383
            return self._process_contract_metadata(contract_source_code)
3✔
UNCOV
384
        return None
×
385

386
    @staticmethod
3✔
387
    def _process_get_contract_source_code_response(response):
3✔
388
        if response and isinstance(response, list):
3✔
389
            result = response[0]
3✔
390
            abi_str = result.get("ABI")
3✔
391

392
            if isinstance(abi_str, str) and abi_str.startswith("["):
3✔
393
                try:
3✔
394
                    result["ABI"] = json.loads(abi_str)
3✔
395
                except json.JSONDecodeError:
×
396
                    result["ABI"] = None  # Handle the case where JSON decoding fails
×
397
            else:
398
                result["ABI"] = None
3✔
399

400
            return result
3✔
401

402
    def get_contract_source_code(self, contract_address: str, retry: bool = True):
3✔
403
        """
404
        Get source code for a contract. Source code query also returns:
405

406
            - ContractName: "",
407
            - CompilerVersion: "",
408
            - OptimizationUsed: "",
409
            - Runs: "",
410
            - ConstructorArguments: ""
411
            - EVMVersion: "Default",
412
            - Library: "",
413
            - LicenseType: "",
414
            - Proxy: "0",
415
            - Implementation: "",
416
            - SwarmSource: ""
417

418
        :param contract_address:
419
        :param retry: if ``True``, try again if there's Rate Limit Error
420
        :return:
421
        """
422
        url = self.build_url(
3✔
423
            f"module=contract&action=getsourcecode&address={contract_address}"
424
        )
425
        response = self._retry_request(url, retry=retry)  # Returns a list
3✔
426
        return self._process_get_contract_source_code_response(response)
3✔
427

428
    def get_contract_abi(self, contract_address: str, retry: bool = True):
3✔
429
        url = self.build_url(
3✔
430
            f"module=contract&action=getabi&address={contract_address}"
431
        )
432
        result = self._retry_request(url, retry=retry)
3✔
433
        if isinstance(result, dict):
3✔
434
            return result
×
435
        elif isinstance(result, str):
3✔
436
            try:
3✔
437
                return json.loads(result)
3✔
438
            except json.JSONDecodeError:
×
439
                pass
×
440
        return None
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc