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.
@@ -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
- data_call = encode_eth_call_data(abi=abi, fn_name=fn_name, args=args)
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