defi-state-querier 0.4.29__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|