defi-state-querier 0.4.30__py3-none-any.whl → 0.5.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,12 @@
1
+ import logging
2
+
3
+ from defi_services.jobs.processors.state_processor import StateProcessor
4
+ from defi_services.jobs.queriers.call_state_querier import CallStateQuerier
5
+
6
+ logger = logging.getLogger("CallStateProcessor")
7
+
8
+
9
+ class CallStateProcessor(StateProcessor):
10
+ def __init__(self, provider_uri: str, chain_id: str):
11
+ super().__init__(provider_uri, chain_id)
12
+ self.state_querier = CallStateQuerier(provider_uri, chain_id)
@@ -0,0 +1,12 @@
1
+ import logging
2
+
3
+ from defi_services.jobs.processors.multi_state_processor import MultiStateProcessor
4
+ from defi_services.jobs.queriers.call_state_querier import CallStateQuerier
5
+
6
+ logger = logging.getLogger("MultiCallStateProcessor")
7
+
8
+
9
+ class MultiCallStateProcessor(MultiStateProcessor):
10
+ def __init__(self, provider_uri: str, chain_id: str):
11
+ super().__init__(provider_uri, chain_id)
12
+ self.state_querier = CallStateQuerier(provider_uri, chain_id)
@@ -10,7 +10,7 @@ from defi_services.services.protocol_services import ProtocolServices
10
10
  from defi_services.services.token_services import TokenServices
11
11
  from defi_services.utils.init_services import init_services
12
12
 
13
- logger = logging.getLogger("StateProcessor")
13
+ logger = logging.getLogger("MultiStateProcessor")
14
14
 
15
15
 
16
16
  class MultiStateProcessor:
@@ -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("CallStateQuerier")
15
+
16
+
17
+ class CallStateQuerier(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
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