defi-state-querier 0.4.29__py3-none-any.whl → 0.5.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- defi_services/__init__.py +1 -1
- defi_services/abis/multicall_v3_abi.py +440 -0
- defi_services/constants/network_constants.py +387 -0
- defi_services/jobs/processors/multicall_state_processor.py +15 -0
- defi_services/jobs/queriers/multicall_state_querier.py +101 -0
- defi_services/jobs/queriers/state_querier.py +6 -3
- defi_services/services/eth/__init__.py +0 -0
- defi_services/services/eth/eth_services.py +91 -0
- defi_services/services/multicall/__init__.py +0 -0
- defi_services/services/multicall/batch_queries_service.py +102 -0
- defi_services/services/multicall/multicall_v2.py +492 -0
- defi_services/services/multicall/state_query_service.py +549 -0
- defi_services/services/token_services.py +4 -3
- defi_services/utils/dict_utils.py +95 -0
- {defi_state_querier-0.4.29.dist-info → defi_state_querier-0.5.0.dist-info}/METADATA +1 -1
- {defi_state_querier-0.4.29.dist-info → defi_state_querier-0.5.0.dist-info}/RECORD +19 -8
- {defi_state_querier-0.4.29.dist-info → defi_state_querier-0.5.0.dist-info}/LICENSE +0 -0
- {defi_state_querier-0.4.29.dist-info → defi_state_querier-0.5.0.dist-info}/WHEEL +0 -0
- {defi_state_querier-0.4.29.dist-info → defi_state_querier-0.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,387 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
import dotenv
|
4
|
+
|
5
|
+
dotenv.load_dotenv()
|
6
|
+
|
7
|
+
POLYGON_AAVE_ADDRESS = '0x8dff5e27ea6b7ac08ebfdf9eb090f32ee9a30fcf'
|
8
|
+
ETHEREUM_AAVE_ADDRESS = '0x7d2768de32b0b80b7a3454c06bdac94a69ddc7a9'
|
9
|
+
|
10
|
+
BSC_TRAVA_ADDRESS = '0x75de5f7c91a89c16714017c7443eca20c7a8c295'
|
11
|
+
ETH_TRAVA_ADDRESS = '0xd61afaaa8a69ba541bc4db9c9b40d4142b43b9a4'
|
12
|
+
FTM_TRAVA_ADDRESS = '0xd98bb590bdfabf18c164056c185fbb6be5ee643f'
|
13
|
+
|
14
|
+
BSC_VALAS_ADDRESS = '0xe29a55a6aeff5c8b1beede5bcf2f0cb3af8f91f5'
|
15
|
+
|
16
|
+
BSC_VENUS_ADDRESS = '0xfd36e2c2a6789db23113685031d7f16329158384'
|
17
|
+
BSC_CREAM_ADDRESS = '0x589de0f0ccf905477646599bb3e5c622c84cc0ba'
|
18
|
+
|
19
|
+
FTM_GEIST_ADDRESS = '0x9fad24f572045c7869117160a571b2e50b10d068'
|
20
|
+
|
21
|
+
ETH_COMPOUND_ADDRESS = '0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b'
|
22
|
+
ETH_CREAM_ADDRESS = ''
|
23
|
+
|
24
|
+
|
25
|
+
class Chains:
|
26
|
+
bsc = '0x38'
|
27
|
+
ethereum = '0x1'
|
28
|
+
fantom = '0xfa'
|
29
|
+
polygon = '0x89'
|
30
|
+
arbitrum = '0xa4b1'
|
31
|
+
optimism = '0xa'
|
32
|
+
avalanche = '0xa86a'
|
33
|
+
tron = '0x2b6653dc'
|
34
|
+
cronos = '0x19'
|
35
|
+
solana = 'solana'
|
36
|
+
polkadot = 'polkadot'
|
37
|
+
bitcoin = 'bitcoin'
|
38
|
+
cosmos = 'cosmos'
|
39
|
+
base = '0x2105'
|
40
|
+
kava = '0x8ae'
|
41
|
+
gnosis = '0x64'
|
42
|
+
klaytn = '0x2019'
|
43
|
+
mantle = '0x1388'
|
44
|
+
celo = '0xa4ec'
|
45
|
+
moonbeam = '0x504'
|
46
|
+
manta = '0xa9'
|
47
|
+
pulse = '0x171'
|
48
|
+
rootstock = '0x1e'
|
49
|
+
astar = '0x250'
|
50
|
+
metis = '0x440'
|
51
|
+
canto = '0x1e14'
|
52
|
+
heco = '0x80'
|
53
|
+
linea = '0xe708'
|
54
|
+
okc = '0x42'
|
55
|
+
aurora = '0x4e454152'
|
56
|
+
moonriver = '0x505'
|
57
|
+
oasis_sapphire = '0x5afe'
|
58
|
+
oasis_sapphire_testnet = '0x5aff'
|
59
|
+
blast = '0xee'
|
60
|
+
oraichain = 'orai'
|
61
|
+
|
62
|
+
none_wrapped_token = [arbitrum, fantom, optimism]
|
63
|
+
all = [
|
64
|
+
bsc, ethereum, fantom, polygon, arbitrum, optimism,
|
65
|
+
avalanche, tron, cronos, solana, polkadot, bitcoin, cosmos,
|
66
|
+
oraichain
|
67
|
+
]
|
68
|
+
|
69
|
+
mapping = {
|
70
|
+
'bsc': bsc,
|
71
|
+
'ethereum': ethereum,
|
72
|
+
'fantom': fantom,
|
73
|
+
'polygon': polygon,
|
74
|
+
'arbitrum': arbitrum,
|
75
|
+
'optimism': optimism,
|
76
|
+
'avalanche': avalanche,
|
77
|
+
'tron': tron,
|
78
|
+
'cronos': cronos,
|
79
|
+
'solana': solana,
|
80
|
+
'polkadot': polkadot,
|
81
|
+
'oasis_sapphire': oasis_sapphire,
|
82
|
+
'oasis_sapphire_testnet': oasis_sapphire_testnet
|
83
|
+
}
|
84
|
+
|
85
|
+
names = {
|
86
|
+
bsc: 'bsc',
|
87
|
+
ethereum: "ethereum",
|
88
|
+
fantom: 'fantom',
|
89
|
+
polygon: 'polygon',
|
90
|
+
arbitrum: 'arbitrum',
|
91
|
+
optimism: 'optimism',
|
92
|
+
avalanche: 'avalanche',
|
93
|
+
tron: 'tron',
|
94
|
+
cronos: 'cronos',
|
95
|
+
solana: 'solana',
|
96
|
+
polkadot: 'polkadot',
|
97
|
+
oasis_sapphire: 'oasis_sapphire',
|
98
|
+
oasis_sapphire_testnet: 'oasis_sapphire_testnet'
|
99
|
+
}
|
100
|
+
|
101
|
+
abi_mapping = {
|
102
|
+
bsc: 'bep20_abi',
|
103
|
+
ethereum: 'erc20_abi',
|
104
|
+
fantom: 'erc20_abi',
|
105
|
+
polygon: 'erc20_abi',
|
106
|
+
arbitrum: 'erc20_abi',
|
107
|
+
optimism: 'erc20_abi',
|
108
|
+
avalanche: 'erc20_abi',
|
109
|
+
tron: 'trc20_abi',
|
110
|
+
cronos: 'crc20_abi'
|
111
|
+
}
|
112
|
+
|
113
|
+
block_time = {
|
114
|
+
bsc: 3,
|
115
|
+
ethereum: 12,
|
116
|
+
fantom: 1,
|
117
|
+
polygon: 2,
|
118
|
+
arbitrum: 0.3,
|
119
|
+
avalanche: 2
|
120
|
+
}
|
121
|
+
wrapped_native_token = {
|
122
|
+
bsc: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
|
123
|
+
ethereum: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
|
124
|
+
polygon: "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270",
|
125
|
+
fantom: "0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83",
|
126
|
+
arbitrum: "0x82af49447d8a07e3bd95bd0d56f35241523fbab1",
|
127
|
+
optimism: "0x4200000000000000000000000000000000000006",
|
128
|
+
avalanche: "0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7",
|
129
|
+
tron: "0x891cdb91d149f23b1a45d9c5ca78a88d0cb44c18",
|
130
|
+
cronos: "0x5c7f8a570d578ed84e63fdfa7b1ee72deae1ae23"
|
131
|
+
}
|
132
|
+
|
133
|
+
evm = {
|
134
|
+
ethereum: True, bsc: True, polygon: True, fantom: True,
|
135
|
+
arbitrum: True, optimism: True, avalanche: True, tron: True, cronos: True,
|
136
|
+
base: True, kava: True, gnosis: True, klaytn: True, mantle: True, celo: True, moonbeam: True, manta: True,
|
137
|
+
pulse: True, rootstock: True, astar: True, metis: True, canto: True, heco: True, linea: True, okc: True,
|
138
|
+
aurora: True, moonriver: True, oasis_sapphire: True, blast: True
|
139
|
+
}
|
140
|
+
|
141
|
+
def get_abi_name(self, chain_id):
|
142
|
+
"""Map chain_id with the corresponding abi key
|
143
|
+
e.g.: '0x38' -> 'bep20_abi' or '0x1' -> 'erc20_abi'
|
144
|
+
"""
|
145
|
+
return self.abi_mapping.get(chain_id, '')
|
146
|
+
|
147
|
+
|
148
|
+
class ChainsAnkr:
|
149
|
+
"""Chain name for Ankr API"""
|
150
|
+
bsc = '0x38'
|
151
|
+
eth = '0x1'
|
152
|
+
fantom = '0xfa'
|
153
|
+
polygon = '0x89'
|
154
|
+
|
155
|
+
reversed_mapping = {
|
156
|
+
bsc: 'bsc',
|
157
|
+
eth: 'eth',
|
158
|
+
fantom: 'fantom',
|
159
|
+
polygon: 'polygon',
|
160
|
+
}
|
161
|
+
|
162
|
+
def get_chain_name(self, chain_id):
|
163
|
+
return self.reversed_mapping.get(chain_id)
|
164
|
+
|
165
|
+
|
166
|
+
class Scans:
|
167
|
+
mapping = {
|
168
|
+
'bscscan': Chains.bsc,
|
169
|
+
'etherscan': Chains.ethereum,
|
170
|
+
'ftmscan': Chains.fantom,
|
171
|
+
'polygonscan': Chains.polygon,
|
172
|
+
'arbiscan': Chains.arbitrum,
|
173
|
+
'optimistic': Chains.optimism,
|
174
|
+
'snowtrace': Chains.avalanche,
|
175
|
+
'cronoscan': Chains.cronos
|
176
|
+
}
|
177
|
+
|
178
|
+
api_keys = {
|
179
|
+
Chains.bsc: 'GKRVTEQHSGWX335P21994DKNSTJVDYYXJ1',
|
180
|
+
Chains.ethereum: 'YQVXQAJAYNS7AC48ZXS1NT45RKYRPY7P7E',
|
181
|
+
Chains.fantom: 'Y85XY5DDDN4Z7C7Z4ZX8N62M5WET7D8NYC',
|
182
|
+
Chains.polygon: 'EYADI5Q4VK4VF99KFEJURMVE9CNE54YEYW',
|
183
|
+
Chains.arbitrum: 'BU5VN8872FE119RDX898MS5EZHE11FPZSC',
|
184
|
+
Chains.optimism: '1JW9PDV2HZZI3WRFQRVGWUDD2T1RV8GWX3',
|
185
|
+
Chains.avalanche: 'JWNM6Y5UA1499PWVA2T55MEH3TZDGAKJWJ',
|
186
|
+
Chains.cronos: 'F5J6423D42PM21CDP61SUD6H7DE4FXH2DE'
|
187
|
+
}
|
188
|
+
|
189
|
+
api_bases = {
|
190
|
+
Chains.bsc: 'https://api.bscscan.com/api',
|
191
|
+
Chains.ethereum: 'https://api.etherscan.io/api',
|
192
|
+
Chains.fantom: 'https://api.ftmscan.com/api',
|
193
|
+
Chains.polygon: 'https://api.polygonscan.com/api',
|
194
|
+
Chains.arbitrum: 'https://api.arbiscan.io/api',
|
195
|
+
Chains.optimism: 'https://api-optimistic.etherscan.io/api',
|
196
|
+
Chains.avalanche: 'https://api.snowtrace.io/api',
|
197
|
+
Chains.cronos: 'https://cronoscan.com/api'
|
198
|
+
}
|
199
|
+
|
200
|
+
scan_base_urls = {
|
201
|
+
Chains.ethereum: 'https://etherscan.io/',
|
202
|
+
Chains.bsc: 'https://bscscan.com/',
|
203
|
+
Chains.fantom: 'https://ftmscan.com/',
|
204
|
+
Chains.polygon: 'https://polygonscan.com/',
|
205
|
+
Chains.arbitrum: 'https://arbiscan.io/',
|
206
|
+
Chains.optimism: 'https://optimistic.etherscan.io/',
|
207
|
+
Chains.avalanche: 'https://snowtrace.io/',
|
208
|
+
Chains.cronos: 'https://cronoscan.com/'
|
209
|
+
}
|
210
|
+
|
211
|
+
all_base_urls = {
|
212
|
+
Chains.bsc: [
|
213
|
+
'https://bscscan.com/token',
|
214
|
+
'https://bscscan-com.translate.goog/token'
|
215
|
+
],
|
216
|
+
Chains.polygon: [
|
217
|
+
'https://polygonscan.com/token',
|
218
|
+
'https://polygonscan-com.translate.goog/token'
|
219
|
+
],
|
220
|
+
Chains.ethereum: [
|
221
|
+
'https://etherscan.io/token',
|
222
|
+
'https://etherscan-io.translate.goog/token',
|
223
|
+
],
|
224
|
+
Chains.fantom: [
|
225
|
+
'https://ftmscan.com/token',
|
226
|
+
'https://ftmscan-com.translate.goog/token'
|
227
|
+
],
|
228
|
+
Chains.arbitrum: [
|
229
|
+
# 'https://arbiscan-io.translate.goog/token',
|
230
|
+
'https://arbiscan.io/token'
|
231
|
+
],
|
232
|
+
Chains.optimism: [
|
233
|
+
# 'https://optimistic-etherscan-io.translate.goog/token',
|
234
|
+
'https://optimistic.etherscan.io/token'
|
235
|
+
],
|
236
|
+
# Chains.avalanche: [
|
237
|
+
# 'https://snowtrace.io/token'
|
238
|
+
# ],
|
239
|
+
Chains.cronos: ['https://cronoscan.com/token']
|
240
|
+
}
|
241
|
+
|
242
|
+
gg_translate_suffix = '?_x_tr_sl=vi&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=wapp'
|
243
|
+
|
244
|
+
|
245
|
+
class Networks:
|
246
|
+
bsc = 'bsc'
|
247
|
+
ethereum = 'ethereum'
|
248
|
+
fantom = 'fantom'
|
249
|
+
polygon = 'polygon'
|
250
|
+
arbitrum = 'arbitrum'
|
251
|
+
optimism = 'optimism'
|
252
|
+
avalanche = 'avalanche'
|
253
|
+
tron = 'tron'
|
254
|
+
cronos = 'cronos'
|
255
|
+
solana = 'solana'
|
256
|
+
polkadot = 'polkadot'
|
257
|
+
oasis_sapphire = 'oasis_sapphire'
|
258
|
+
oasis_sapphire_testnet = 'oasis_sapphire_testnet'
|
259
|
+
cosmos = 'cosmos'
|
260
|
+
oraichain = 'oraichain'
|
261
|
+
|
262
|
+
providers = {
|
263
|
+
bsc: os.getenv('BSC_PROVIDER_URI', 'https://bsc-dataseed1.binance.org/'),
|
264
|
+
ethereum: os.getenv('ETHEREUM_PROVIDER_URI', 'https://rpc.ankr.com/eth'),
|
265
|
+
fantom: os.getenv('FANTOM_PROVIDER_URI', 'https://rpc.ftm.tools/'),
|
266
|
+
polygon: os.getenv('POLYGON_PROVIDER_URI', 'https://polygon-rpc.com'),
|
267
|
+
arbitrum: os.getenv('ARBITRUM_PROVIDER_URI', 'https://endpoints.omniatech.io/v1/arbitrum/one/public'),
|
268
|
+
optimism: os.getenv('OPTIMISM_PROVIDER_URI', 'https://rpc.ankr.com/optimism'),
|
269
|
+
avalanche: os.getenv('AVALANCHE_PROVIDER_URI', 'https://rpc.ankr.com/avalanche'),
|
270
|
+
tron: os.getenv('TRON_PROVIDER_URI', 'https://rpc.ankr.com/tron_jsonrpc'),
|
271
|
+
cronos: os.getenv('CRONOS_PROVIDER_URI', 'https://evm.cronos.org/'),
|
272
|
+
solana: os.getenv('SOLANA_PROVIDER_URI', 'https://crimson-multi-putty.solana-mainnet.quiknode.pro/997174ce6ab5cc9d42cb037e931d18ae1a98346a/'),
|
273
|
+
polkadot: os.getenv('POLKADOT_PROVIDER_URI', 'https://late-yolo-diagram.dot-mainnet.quiknode.pro/51a1aaf2372854dfd211fca3ab375e5451222be4/'),
|
274
|
+
oasis_sapphire: os.getenv('OASIS_SAPPHIRE_PROVIDER_URI'),
|
275
|
+
oasis_sapphire_testnet: os.getenv('OASIS_SAPPHIRE_PROVIDER_URI')
|
276
|
+
}
|
277
|
+
|
278
|
+
archive_node = {
|
279
|
+
bsc: os.getenv('BSC_PROVIDER_ARCHIVE_URI', 'https://rpc.ankr.com/bsc'),
|
280
|
+
ethereum: os.getenv('ETHEREUM_PROVIDER_ARCHIVE_URI', 'https://rpc.ankr.com/eth'),
|
281
|
+
fantom: os.getenv('FANTOM_PROVIDER_ARCHIVE_URI', 'https://rpc.ankr.com/fantom'),
|
282
|
+
polygon: os.getenv('POLYGON_PROVIDER_ARCHIVE_URI', 'https://rpc.ankr.com/polygon'),
|
283
|
+
arbitrum: os.getenv('ARBITRUM_PROVIDER_ARCHIVE_URI', 'https://rpc.ankr.com/arbitrum'),
|
284
|
+
optimism: os.getenv('OPTIMISM_PROVIDER_ARCHIVE_URI', 'https://rpc.ankr.com/optimism'),
|
285
|
+
avalanche: os.getenv('AVALANCHE_PROVIDER_ARCHIVE_URI', 'https://rpc.ankr.com/avalanche'),
|
286
|
+
tron: os.getenv('TRON_PROVIDER_ARCHIVE_URI', 'https://rpc.ankr.com/tron_jsonrpc'),
|
287
|
+
cronos: os.getenv('CRONOS_PROVIDER_ARCHIVE_URI'),
|
288
|
+
solana: os.getenv('SOLANA_PROVIDER_ARCHIVE_URI'),
|
289
|
+
polkadot: os.getenv('POLKADOT_PROVIDER_ARCHIVE_URI'),
|
290
|
+
oasis_sapphire: os.getenv('OASIS_SAPPHIRE_PROVIDER_ARCHIVE_URI'),
|
291
|
+
oasis_sapphire_testnet: os.getenv('OASIS_SAPPHIRE_PROVIDER_ARCHIVE_URI')
|
292
|
+
}
|
293
|
+
|
294
|
+
|
295
|
+
class DefiLlama:
|
296
|
+
chains = {
|
297
|
+
'Binance': Chains.bsc,
|
298
|
+
'Ethereum': Chains.ethereum,
|
299
|
+
'Fantom': Chains.fantom,
|
300
|
+
'Polygon': Chains.polygon,
|
301
|
+
'Arbitrum': Chains.arbitrum,
|
302
|
+
'Optimism': Chains.optimism,
|
303
|
+
'Avalanche': Chains.avalanche,
|
304
|
+
'Tron': Chains.tron,
|
305
|
+
'Cronos': Chains.cronos,
|
306
|
+
'Solana': Chains.solana,
|
307
|
+
'Polkadot': Chains.polkadot,
|
308
|
+
'Base': Chains.base,
|
309
|
+
'Kava': Chains.kava,
|
310
|
+
'xDai': Chains.gnosis,
|
311
|
+
'Klaytn': Chains.klaytn,
|
312
|
+
'Mantle': Chains.mantle,
|
313
|
+
'Celo': Chains.celo,
|
314
|
+
'Moonbeam': Chains.moonbeam,
|
315
|
+
'Manta': Chains.manta,
|
316
|
+
'Pulse': Chains.pulse,
|
317
|
+
'RSK': Chains.rootstock,
|
318
|
+
'Astar': Chains.astar,
|
319
|
+
'Metis': Chains.metis,
|
320
|
+
'Canto': Chains.canto,
|
321
|
+
'Heco': Chains.heco,
|
322
|
+
'Linea': Chains.linea,
|
323
|
+
'OKExChain': Chains.okc,
|
324
|
+
'Aurora': Chains.aurora,
|
325
|
+
'Moonriver': Chains.moonriver,
|
326
|
+
'Oasis Sapphire': Chains.oasis_sapphire,
|
327
|
+
'Blast': Chains.blast,
|
328
|
+
'Oraichain': Chains.oraichain
|
329
|
+
}
|
330
|
+
|
331
|
+
|
332
|
+
class Opensea:
|
333
|
+
chains = {
|
334
|
+
Chains.bsc: 'BSC',
|
335
|
+
Chains.ethereum: 'ETHEREUM',
|
336
|
+
Chains.polygon: 'MATIC',
|
337
|
+
Chains.fantom: 'FANTOM',
|
338
|
+
Chains.arbitrum: 'ARBITRUM',
|
339
|
+
Chains.optimism: 'OPTIMISM',
|
340
|
+
Chains.avalanche: 'AVALANCHE',
|
341
|
+
Chains.tron: 'TRON',
|
342
|
+
Chains.solana: 'SOLANA',
|
343
|
+
Chains.base: 'BASE',
|
344
|
+
Chains.klaytn: 'KLAYTN'
|
345
|
+
}
|
346
|
+
|
347
|
+
|
348
|
+
NATIVE_TOKEN = '0x0000000000000000000000000000000000000000'
|
349
|
+
|
350
|
+
NATIVE_TOKENS = {
|
351
|
+
Chains.bsc: '0x0000000000000000000000000000000000000000',
|
352
|
+
Chains.ethereum: '0x0000000000000000000000000000000000000000',
|
353
|
+
Chains.fantom: '0x0000000000000000000000000000000000000000',
|
354
|
+
Chains.polygon: '0x0000000000000000000000000000000000000000',
|
355
|
+
Chains.arbitrum: '0x0000000000000000000000000000000000000000',
|
356
|
+
Chains.optimism: '0x0000000000000000000000000000000000000000',
|
357
|
+
Chains.avalanche: '0x0000000000000000000000000000000000000000',
|
358
|
+
Chains.tron: '0x0000000000000000000000000000000000000000'
|
359
|
+
}
|
360
|
+
|
361
|
+
|
362
|
+
class MulticallContract:
|
363
|
+
"""
|
364
|
+
References: https://www.multicall3.com/deployments
|
365
|
+
"""
|
366
|
+
|
367
|
+
on_chains_v3 = {
|
368
|
+
Chains.ethereum: '0xca11bde05977b3631167028862be2a173976ca11',
|
369
|
+
Chains.bsc: '0xca11bde05977b3631167028862be2a173976ca11',
|
370
|
+
Chains.polygon: '0xca11bde05977b3631167028862be2a173976ca11',
|
371
|
+
Chains.avalanche: '0xca11bde05977b3631167028862be2a173976ca11',
|
372
|
+
Chains.fantom: '0xca11bde05977b3631167028862be2a173976ca11',
|
373
|
+
Chains.arbitrum: '0xca11bde05977b3631167028862be2a173976ca11',
|
374
|
+
Chains.optimism: '0xca11bde05977b3631167028862be2a173976ca11',
|
375
|
+
Chains.tron: '0x32a4f47a74a6810bd0bf861cabab99656a75de9e',
|
376
|
+
Chains.oasis_sapphire: '0xca11bde05977b3631167028862be2a173976ca11',
|
377
|
+
Chains.oasis_sapphire_testnet: '0xca11bde05977b3631167028862be2a173976ca11'
|
378
|
+
}
|
379
|
+
|
380
|
+
default_address = '0xca11bde05977b3631167028862be2a173976ca11'
|
381
|
+
|
382
|
+
@classmethod
|
383
|
+
def get_multicall_contract(cls, chain_id):
|
384
|
+
return cls.on_chains_v3.get(chain_id, cls.default_address)
|
385
|
+
|
386
|
+
|
387
|
+
EMPTY_TOKEN_IMG = 'https://firebasestorage.googleapis.com/v0/b/token-c515a.appspot.com/o/tokens_v2%2Fempty-token.png?alt=media&token=2f9dfcc1-88a0-472c-a51f-4babc0c583f0'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from defi_services.constants.chain_constant import Chain
|
4
|
+
from defi_services.constants.query_constant import Query
|
5
|
+
from defi_services.jobs.processors.state_processor import StateProcessor
|
6
|
+
from defi_services.jobs.queriers.multicall_state_querier import MulticallStateQuerier
|
7
|
+
from defi_services.utils.convert_address import base58_to_hex
|
8
|
+
|
9
|
+
logger = logging.getLogger("MulticallStateProcessor")
|
10
|
+
|
11
|
+
|
12
|
+
class MulticallStateProcessor(StateProcessor):
|
13
|
+
def __init__(self, provider_uri: str, chain_id: str):
|
14
|
+
super().__init__(provider_uri, chain_id)
|
15
|
+
self.state_querier = MulticallStateQuerier(provider_uri, chain_id)
|
@@ -0,0 +1,101 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from web3 import Web3
|
4
|
+
|
5
|
+
from defi_services.abis.multicall_v3_abi import MULTICALL_V3_ABI
|
6
|
+
from defi_services.constants.network_constants import MulticallContract
|
7
|
+
from defi_services.constants.query_constant import Query
|
8
|
+
from defi_services.constants.token_constant import Token
|
9
|
+
from defi_services.jobs.queriers.state_querier import StateQuerier
|
10
|
+
from defi_services.services.multicall.batch_queries_service import decode_data_response_ignore_error, \
|
11
|
+
decode_data_response
|
12
|
+
from defi_services.services.multicall.multicall_v2 import W3Multicall, add_rpc_multicall
|
13
|
+
|
14
|
+
logger = logging.getLogger("MulticallStateQuerier")
|
15
|
+
|
16
|
+
|
17
|
+
class MulticallStateQuerier(StateQuerier):
|
18
|
+
def __init__(self, provider_uri, chain_id):
|
19
|
+
super().__init__(provider_uri)
|
20
|
+
self.chain_id = chain_id
|
21
|
+
self.multicall_address = Web3.to_checksum_address(MulticallContract.get_multicall_contract(self.chain_id))
|
22
|
+
|
23
|
+
def query_state_data(self, queries: dict, batch_size: int = 100, workers: int = 5, ignore_error: bool = False):
|
24
|
+
"""
|
25
|
+
Args:
|
26
|
+
queries: dict - defi state queries
|
27
|
+
- key: str - id of query
|
28
|
+
- value: dict - input of query
|
29
|
+
{
|
30
|
+
address: str - address of contract
|
31
|
+
abi: list - abi of contract,
|
32
|
+
function: str - name of the function,
|
33
|
+
params: list - list parameters of function,
|
34
|
+
block_number: int - the block number saving the state data
|
35
|
+
}
|
36
|
+
batch_size: int - number of query in each batch queries
|
37
|
+
workers: int - maximum number of vCPU used in queries
|
38
|
+
ignore_error: bool - ignore error when decode result or not
|
39
|
+
|
40
|
+
Return:
|
41
|
+
+ A dictionary result of queries
|
42
|
+
- key: str - id of query
|
43
|
+
- value: result of query
|
44
|
+
"""
|
45
|
+
w3_multicall = W3Multicall(self._w3, address=self.multicall_address)
|
46
|
+
for key, value in queries.items():
|
47
|
+
fn_paras = value.get(Query.params)
|
48
|
+
block_number = value.get(Query.block_number)
|
49
|
+
items = key.split('_')
|
50
|
+
if Token.native_token == items[2] and "balanceof" == items[0]:
|
51
|
+
w3_multicall.add(W3Multicall.Call(
|
52
|
+
self.multicall_address, MULTICALL_V3_ABI, fn_paras=fn_paras,
|
53
|
+
fn_name="getEthBalance", block_number=block_number, key=key
|
54
|
+
))
|
55
|
+
else:
|
56
|
+
abi = value.get(Query.abi)
|
57
|
+
contract_address = value.get(Query.address)
|
58
|
+
checksum_address = Web3.to_checksum_address(contract_address)
|
59
|
+
fn_name = value.get(Query.function)
|
60
|
+
w3_multicall.add(W3Multicall.Call(
|
61
|
+
checksum_address, abi, fn_name=fn_name,
|
62
|
+
fn_paras=fn_paras,
|
63
|
+
block_number=block_number, key=key
|
64
|
+
))
|
65
|
+
|
66
|
+
list_call_id, list_rpc_call = [], []
|
67
|
+
add_rpc_multicall(
|
68
|
+
w3_multicall,
|
69
|
+
list_rpc_call=list_rpc_call, list_call_id=list_call_id,
|
70
|
+
batch_size=batch_size
|
71
|
+
)
|
72
|
+
response_data = self.client_querier.sent_batch_to_provider(list_rpc_call, batch_size, workers)
|
73
|
+
filtered_response_data = {}
|
74
|
+
# loại bỏ những phần tử không có data
|
75
|
+
for key, value in response_data.items():
|
76
|
+
if value is not None:
|
77
|
+
filtered_response_data[key] = value
|
78
|
+
else:
|
79
|
+
logger.info(key)
|
80
|
+
filtered_keys = list(filtered_response_data.keys())
|
81
|
+
response_data = filtered_response_data
|
82
|
+
list_call_id = [call_id for call_id in list_call_id if call_id in filtered_keys]
|
83
|
+
decoded_data = self.decode_response_data(
|
84
|
+
response_data, list_call_id, ignore_error=ignore_error, w3_multicall=w3_multicall, batch_size=batch_size)
|
85
|
+
return decoded_data
|
86
|
+
|
87
|
+
def decode_response_data(
|
88
|
+
self, response_data: dict, list_call_id: list, ignore_error=False, w3_multicall=None, batch_size=100):
|
89
|
+
if ignore_error:
|
90
|
+
decoded_data = decode_data_response_ignore_error(data_responses=response_data, list_call_id=list_call_id)
|
91
|
+
else:
|
92
|
+
decoded_data = decode_data_response(data_responses=response_data, list_call_id=list_call_id)
|
93
|
+
batch_idx = 0
|
94
|
+
results = {}
|
95
|
+
for block_number, batch_calls in w3_multicall.batch_calls_iterator(batch_size=batch_size):
|
96
|
+
multicall_data = decoded_data.get(f'{batch_idx}_{block_number}')
|
97
|
+
decode_multicall_data = w3_multicall.decode(multicall_data, calls=batch_calls, ignore_error=ignore_error)
|
98
|
+
results.update(decode_multicall_data)
|
99
|
+
batch_idx += 1
|
100
|
+
|
101
|
+
return results
|
@@ -2,9 +2,8 @@ import logging
|
|
2
2
|
|
3
3
|
from query_state_lib.base.mappers.eth_call_mapper import EthCall
|
4
4
|
from query_state_lib.base.mappers.get_balance_mapper import GetBalance
|
5
|
-
from query_state_lib.base.utils.encoder import encode_eth_call_data
|
6
5
|
from query_state_lib.client.client_querier import ClientQuerier
|
7
|
-
from web3 import Web3
|
6
|
+
from web3 import Web3, contract
|
8
7
|
from web3.middleware import geth_poa_middleware
|
9
8
|
|
10
9
|
from defi_services.constants.query_constant import Query
|
@@ -124,7 +123,11 @@ class StateQuerier:
|
|
124
123
|
item = self._w3.to_checksum_address(item)
|
125
124
|
args.append(item)
|
126
125
|
|
127
|
-
|
126
|
+
c = contract.Contract
|
127
|
+
c.w3 = self._w3
|
128
|
+
c.abi = abi
|
129
|
+
data_call = c.encodeABI(fn_name=fn_name, args=args)
|
130
|
+
# data_call = encode_eth_call_data(abi=abi, fn_name=fn_name, args=args)
|
128
131
|
eth_call = EthCall(to=self._w3.to_checksum_address(contract_address), block_number=block_number,
|
129
132
|
data=data_call, abi=abi, fn_name=fn_name, id=call_id)
|
130
133
|
return eth_call
|
File without changes
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# MIT License
|
2
|
+
#
|
3
|
+
# Copyright (c) 2018 Evgeny Medvedev, evge.medvedev@gmail.com
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
13
|
+
# copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
# SOFTWARE.
|
22
|
+
|
23
|
+
|
24
|
+
from datetime import datetime, timezone
|
25
|
+
|
26
|
+
from defi_services.utils.graph_operations import GraphOperations, OutOfBoundsError, Point
|
27
|
+
|
28
|
+
|
29
|
+
class EthService(object):
|
30
|
+
def __init__(self, web3):
|
31
|
+
graph = BlockTimestampGraph(web3)
|
32
|
+
self._graph_operations = GraphOperations(graph)
|
33
|
+
|
34
|
+
def get_block_for_timestamp(self, timestamp):
|
35
|
+
try:
|
36
|
+
block_bounds = self._graph_operations.get_bounds_for_y_coordinate(timestamp)
|
37
|
+
except OutOfBoundsError:
|
38
|
+
return 'latest'
|
39
|
+
return block_bounds[0]
|
40
|
+
|
41
|
+
def get_block_range_for_date(self, date):
|
42
|
+
start_datetime = datetime.combine(date, datetime.min.time().replace(tzinfo=timezone.utc))
|
43
|
+
end_datetime = datetime.combine(date, datetime.max.time().replace(tzinfo=timezone.utc))
|
44
|
+
return self.get_block_range_for_timestamps(start_datetime.timestamp(), end_datetime.timestamp())
|
45
|
+
|
46
|
+
def get_block_range_for_timestamps(self, start_timestamp, end_timestamp):
|
47
|
+
start_timestamp = int(start_timestamp)
|
48
|
+
end_timestamp = int(end_timestamp)
|
49
|
+
if start_timestamp > end_timestamp:
|
50
|
+
raise ValueError('start_timestamp must be greater or equal to end_timestamp')
|
51
|
+
|
52
|
+
try:
|
53
|
+
start_block_bounds = self._graph_operations.get_bounds_for_y_coordinate(start_timestamp)
|
54
|
+
except OutOfBoundsError:
|
55
|
+
start_block_bounds = (0, 0)
|
56
|
+
|
57
|
+
try:
|
58
|
+
end_block_bounds = self._graph_operations.get_bounds_for_y_coordinate(end_timestamp)
|
59
|
+
except OutOfBoundsError as e:
|
60
|
+
raise OutOfBoundsError('The existing blocks do not completely cover the given time range') from e
|
61
|
+
|
62
|
+
if start_block_bounds == end_block_bounds and start_block_bounds[0] != start_block_bounds[1]:
|
63
|
+
raise ValueError('The given timestamp range does not cover any blocks')
|
64
|
+
|
65
|
+
start_block = start_block_bounds[1]
|
66
|
+
end_block = end_block_bounds[0]
|
67
|
+
|
68
|
+
# The genesis block has timestamp 0 but we include it with the 1st block.
|
69
|
+
if start_block == 1:
|
70
|
+
start_block = 0
|
71
|
+
|
72
|
+
return start_block, end_block
|
73
|
+
|
74
|
+
|
75
|
+
class BlockTimestampGraph(object):
|
76
|
+
def __init__(self, web3):
|
77
|
+
self._web3 = web3
|
78
|
+
|
79
|
+
def get_first_point(self):
|
80
|
+
# Ignore the genesis block as its timestamp is 0
|
81
|
+
return block_to_point(self._web3.eth.get_block(1))
|
82
|
+
|
83
|
+
def get_last_point(self):
|
84
|
+
return block_to_point(self._web3.eth.get_block('latest'))
|
85
|
+
|
86
|
+
def get_point(self, x):
|
87
|
+
return block_to_point(self._web3.eth.get_block(x))
|
88
|
+
|
89
|
+
|
90
|
+
def block_to_point(block):
|
91
|
+
return Point(block.number, block.timestamp)
|
File without changes
|