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.
@@ -0,0 +1,549 @@
1
+ import itertools
2
+
3
+ from query_state_lib.base.mappers.eth_call_balance_of_mapper import EthCallBalanceOf
4
+ from query_state_lib.base.mappers.get_balance_mapper import GetBalance
5
+ from query_state_lib.client.client_querier import ClientQuerier
6
+ from web3 import Web3, HTTPProvider
7
+ from web3.middleware import geth_poa_middleware
8
+
9
+ from defi_services.abis.dex.pancakeswap.pancakeswap_lp_token_abi import LP_TOKEN_ABI
10
+ from defi_services.abis.token.erc20_abi import ERC20_ABI
11
+ from defi_services.abis.token.erc721_abi import ERC721_ABI
12
+ from defi_services.constants.network_constants import NATIVE_TOKEN
13
+ from defi_services.services.eth.eth_services import EthService
14
+ from defi_services.services.multicall.batch_queries_service import add_rpc_call, decode_data_response_ignore_error
15
+ from defi_services.utils.logger_utils import get_logger
16
+
17
+ logger = get_logger('State Query Service')
18
+
19
+
20
+ class StateQueryService:
21
+ def __init__(self, provider_uri):
22
+ self._w3 = Web3(HTTPProvider(provider_uri))
23
+ self._w3.middleware_onion.inject(geth_poa_middleware, layer=0)
24
+
25
+ self.client_querier = ClientQuerier(provider_url=provider_uri)
26
+
27
+ def get_web3(self):
28
+ return self._w3
29
+
30
+ def to_checksum(self, address):
31
+ return self._w3.to_checksum_address(address)
32
+
33
+ def get_contract(self, address, abi):
34
+ try:
35
+ return self._w3.eth.contract(self.to_checksum(address), abi=abi)
36
+ except TypeError:
37
+ print('a')
38
+
39
+ def is_contract(self, address):
40
+ code = self._w3.eth.get_code(self.to_checksum(address))
41
+ code_str = code.hex()
42
+ if code_str == '0x':
43
+ return False
44
+ else:
45
+ return True
46
+
47
+ def decode_tx(self, address, abi, tx_input):
48
+ contract = self.get_contract(address, abi)
49
+ func_obj, func_params = contract.decode_function_input(tx_input)
50
+ return func_obj, func_params
51
+
52
+ def is_address(self, address):
53
+ return self._w3.is_address(address)
54
+
55
+ def balance_of(self, address, token, abi=ERC20_ABI, block_number='latest'):
56
+ token_contract = self._w3.eth.contract(self._w3.to_checksum_address(token), abi=abi)
57
+ decimals = token_contract.functions.decimals().call()
58
+ balance = token_contract.functions.balanceOf(self._w3.to_checksum_address(address)).call(
59
+ block_identifier=block_number)
60
+ return balance / 10 ** decimals
61
+
62
+ def batch_balance_of(self, address, tokens, block_number: int = 'latest', batch_size=100):
63
+ balances = {}
64
+ tokens = {token['address']: token['decimals'] or 18 for token in tokens}
65
+ rpc_requests = []
66
+ for token in tokens:
67
+ if token != "0x" and token != NATIVE_TOKEN:
68
+ query_id = f'{address}_{token}'
69
+ call_balance_of = EthCallBalanceOf(
70
+ contract_address=Web3.to_checksum_address(token),
71
+ address=Web3.to_checksum_address(address),
72
+ block_number=block_number,
73
+ id=query_id
74
+ )
75
+ else:
76
+ query_id = f"{address}_{NATIVE_TOKEN}"
77
+ call_balance_of = GetBalance(
78
+ address=Web3.to_checksum_address(address),
79
+ block_number=block_number,
80
+ id=query_id
81
+ )
82
+ rpc_requests.append(call_balance_of)
83
+ rpc_responses = self.client_querier.sent_batch_to_provider(rpc_requests, batch_size=batch_size)
84
+ for token, decimals in tokens.items():
85
+ if token == '0x':
86
+ token = NATIVE_TOKEN
87
+ balance = rpc_responses.get(f'{address}_{token}').result or 0
88
+ balance = balance / 10 ** decimals
89
+ balances[token] = balance
90
+
91
+ return balances
92
+
93
+ def batch_balance_of_multiple_addresses(self, addresses, tokens, block_number='latest', batch_size=100):
94
+ tokens_decimals = {token['address']: token.get('decimals') or 18 for token in tokens}
95
+
96
+ rpc_requests = []
97
+ for address, interacted_tokens in addresses.items():
98
+ for token_address in interacted_tokens:
99
+ if token_address != "0x" and token_address != NATIVE_TOKEN:
100
+ query_id = f'{address}_{token_address}'
101
+ call_balance_of = EthCallBalanceOf(
102
+ contract_address=Web3.to_checksum_address(token_address),
103
+ address=Web3.to_checksum_address(address),
104
+ block_number=block_number,
105
+ id=query_id
106
+ )
107
+ else:
108
+ query_id = f"{address}_{NATIVE_TOKEN}"
109
+ call_balance_of = GetBalance(
110
+ address=Web3.to_checksum_address(address),
111
+ block_number=block_number,
112
+ id=query_id
113
+ )
114
+ rpc_requests.append(call_balance_of)
115
+
116
+ rpc_responses = self.client_querier.sent_batch_to_provider(rpc_requests, batch_size=batch_size)
117
+
118
+ results = {}
119
+ for address, interacted_tokens in addresses.items():
120
+ results[address] = {}
121
+ for token_address in interacted_tokens:
122
+ balance = rpc_responses.get(f'{address}_{token_address}').result or 0
123
+
124
+ decimals = tokens_decimals.get(token_address) or 18
125
+ balance = balance / 10 ** decimals
126
+ results[address][token_address] = balance
127
+
128
+ return results, len(rpc_requests)
129
+
130
+ def get_block_number_by_timestamp(self, block_timestamp):
131
+ eth_service = EthService(self._w3)
132
+ block_number = eth_service.get_block_for_timestamp(block_timestamp)
133
+ return block_number
134
+
135
+ def batch_native_balance_of_wallets(self, addresses, blocks, decimals=18, batch_size=100):
136
+ rpc_requests = []
137
+ for address in addresses:
138
+ for block_number in blocks:
139
+ query_id = f"{address}_{block_number}"
140
+ call_balance_of = GetBalance(
141
+ address=Web3.to_checksum_address(address),
142
+ block_number=block_number,
143
+ id=query_id
144
+ )
145
+ rpc_requests.append(call_balance_of)
146
+
147
+ rpc_responses = self.client_querier.sent_batch_to_provider(rpc_requests, batch_size=batch_size)
148
+
149
+ data = {}
150
+ for address in addresses:
151
+ data[address] = {}
152
+ for block_number in blocks:
153
+ balance = rpc_responses.get(f'{address}_{block_number}').result or 0
154
+ balance = balance / 10 ** decimals
155
+ data[address][block_number] = balance
156
+
157
+ return data
158
+
159
+ def batch_balance_of_wallets(self, addresses, tokens, blocks, batch_size=100):
160
+ rpc_requests = []
161
+
162
+ for address, token, block_number in itertools.product(addresses, tokens, blocks):
163
+ token_address = token['address']
164
+ query_id = f'{address}_{token_address}_{block_number}'
165
+ if token_address != "0x" and token_address != NATIVE_TOKEN:
166
+ call_balance_of = EthCallBalanceOf(
167
+ contract_address=Web3.to_checksum_address(token_address),
168
+ address=Web3.to_checksum_address(address),
169
+ block_number=block_number,
170
+ id=query_id
171
+ )
172
+ else:
173
+ call_balance_of = GetBalance(
174
+ address=Web3.to_checksum_address(address),
175
+ block_number=block_number,
176
+ id=query_id
177
+ )
178
+ rpc_requests.append(call_balance_of)
179
+
180
+ rpc_responses = self.client_querier.sent_batch_to_provider(rpc_requests, batch_size=batch_size)
181
+
182
+ data = {}
183
+ for address, token, block_number in itertools.product(addresses, tokens, blocks):
184
+ token_address = token['address']
185
+ query_id = f'{address}_{token_address}_{block_number}'
186
+ balance = rpc_responses.get(query_id).result or 0
187
+
188
+ decimals = token.get('decimals') or 18
189
+
190
+ if address not in data:
191
+ data[address] = {}
192
+ if token_address not in data[address]:
193
+ data[address][token_address] = {}
194
+ data[address][token_address][block_number] = balance / 10 ** decimals
195
+
196
+ return data
197
+
198
+ def batch_balance_of_wallets_block(self, addresses, tokens, block_number='latest', batch_size=100):
199
+ rpc_requests = []
200
+ for address, token in itertools.product(addresses, tokens):
201
+ token_address = token['address']
202
+ query_id = f'{address}_{token_address}_{block_number}'
203
+ if token_address != "0x" and token_address != NATIVE_TOKEN:
204
+ call_balance_of = EthCallBalanceOf(
205
+ contract_address=Web3.to_checksum_address(token_address),
206
+ address=Web3.to_checksum_address(address),
207
+ block_number=block_number,
208
+ id=query_id
209
+ )
210
+ else:
211
+ call_balance_of = GetBalance(
212
+ address=Web3.to_checksum_address(address),
213
+ block_number=block_number,
214
+ id=query_id
215
+ )
216
+ rpc_requests.append(call_balance_of)
217
+
218
+ rpc_responses = self.client_querier.sent_batch_to_provider(rpc_requests, batch_size=batch_size)
219
+
220
+ data = {}
221
+ for address, token in itertools.product(addresses, tokens):
222
+ token_address = token['address']
223
+ query_id = f'{address}_{token_address}_{block_number}'
224
+ balance = rpc_responses.get(query_id).result or 0
225
+ decimals = token.get('decimals') or 18
226
+ if address not in data:
227
+ data[address] = {}
228
+ if token_address not in data[address]:
229
+ data[address][token_address] = {}
230
+ data[address][token_address] = balance / 10 ** decimals
231
+
232
+ return data
233
+
234
+ def batch_balance_of_wallet_info(self, wallet_info: dict, block_number='latest', batch_size=100):
235
+ rpc_requests = []
236
+ for address, tokens in wallet_info.items():
237
+ for token in tokens:
238
+ token_address = token['address']
239
+ query_id = f'{address}_{token_address}_{block_number}'
240
+ if token_address != "0x" and token_address != NATIVE_TOKEN:
241
+ call_balance_of = EthCallBalanceOf(
242
+ contract_address=Web3.to_checksum_address(token_address),
243
+ address=Web3.to_checksum_address(address),
244
+ block_number=block_number,
245
+ id=query_id
246
+ )
247
+ else:
248
+ call_balance_of = GetBalance(
249
+ address=Web3.to_checksum_address(address),
250
+ block_number=block_number,
251
+ id=query_id
252
+ )
253
+ rpc_requests.append(call_balance_of)
254
+
255
+ rpc_responses = self.client_querier.sent_batch_to_provider(rpc_requests, batch_size=batch_size)
256
+
257
+ data = {}
258
+ for address, tokens in wallet_info.items():
259
+ for token in tokens:
260
+ token_address = token['address']
261
+ query_id = f'{address}_{token_address}_{block_number}'
262
+ balance = rpc_responses.get(query_id).result or 0
263
+ decimals = token.get('decimals') or 18
264
+ if address not in data:
265
+ data[address] = {}
266
+ if token_address not in data[address]:
267
+ data[address][token_address] = {}
268
+ data[address][token_address] = balance / 10 ** decimals
269
+
270
+ return data
271
+
272
+ def batch_balance_of_wallet_info_with_block_number(self, wallets_info: list, batch_size=100):
273
+ rpc_requests = []
274
+ for wallet_info in wallets_info:
275
+ address = wallet_info['address']
276
+ tokens = wallet_info.get('tokens', [])
277
+ block_number = wallet_info.get('block_number', 'latest')
278
+
279
+ for token in tokens:
280
+ token_address = token['address']
281
+ query_id = f'{address}_{token_address}_{block_number}'
282
+ if token_address != "0x" and token_address != NATIVE_TOKEN:
283
+ call_balance_of = EthCallBalanceOf(
284
+ contract_address=Web3.to_checksum_address(token_address),
285
+ address=Web3.to_checksum_address(address),
286
+ block_number=block_number,
287
+ id=query_id
288
+ )
289
+ else:
290
+ call_balance_of = GetBalance(
291
+ address=Web3.to_checksum_address(address),
292
+ block_number=block_number,
293
+ id=query_id
294
+ )
295
+ rpc_requests.append(call_balance_of)
296
+
297
+ rpc_responses = self.client_querier.sent_batch_to_provider(rpc_requests, batch_size=batch_size)
298
+
299
+ data = {}
300
+ for wallet_info in wallets_info:
301
+ address = wallet_info['address']
302
+ tokens = wallet_info.get('tokens', [])
303
+ block_number = wallet_info.get('block_number', 'latest')
304
+
305
+ for token in tokens:
306
+ token_address = token['address']
307
+ query_id = f'{address}_{token_address}_{block_number}'
308
+ balance = rpc_responses.get(query_id).result or 0
309
+ decimals = token.get('decimals')
310
+ if decimals is None:
311
+ continue
312
+
313
+ if address not in data:
314
+ data[address] = {}
315
+ if token_address not in data[address]:
316
+ data[address][token_address] = {}
317
+ data[address][token_address] = balance / 10 ** decimals
318
+
319
+ return data
320
+
321
+ def recheck_liquidity_pool_v2_reserves(self, liquidity_pools: list, batch_size=100):
322
+ list_rpc_call = []
323
+ list_call_id = []
324
+
325
+ for liquidity_pool in liquidity_pools:
326
+ address = liquidity_pool['address']
327
+ block_number = liquidity_pool.get('block_number', 'latest')
328
+
329
+ add_rpc_call(
330
+ abi=LP_TOKEN_ABI, contract_address=Web3.to_checksum_address(address),
331
+ fn_name="getReserves", block_number=block_number,
332
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
333
+ )
334
+
335
+ responses = self.client_querier.sent_batch_to_provider(list_rpc_call, batch_size=batch_size)
336
+ decoded_data = decode_data_response_ignore_error(data_responses=responses, list_call_id=list_call_id)
337
+
338
+ data = {}
339
+ for liquidity_pool in liquidity_pools:
340
+ address = liquidity_pool['address']
341
+ tokens = liquidity_pool.get('tokens', [])
342
+ block_number = liquidity_pool.get('block_number', 'latest')
343
+
344
+ reserves = decoded_data.get(f'getReserves_{address}_{block_number}'.lower())
345
+ if not reserves:
346
+ continue
347
+ for token in tokens:
348
+ token_address = token['address']
349
+ balance = reserves[token['idx']]
350
+ decimals = token.get('decimals')
351
+ if decimals is None:
352
+ continue
353
+
354
+ if address not in data:
355
+ data[address] = {}
356
+ if token_address not in data[address]:
357
+ data[address][token_address] = {}
358
+ data[address][token_address] = balance / 10 ** decimals
359
+
360
+ return data
361
+
362
+ def batch_total_supply_pool_info_with_block_number(self, liquidity_pools: list, batch_size=100):
363
+ list_rpc_call = []
364
+ list_call_id = []
365
+ for liquidity_pool in liquidity_pools:
366
+ address = liquidity_pool['address']
367
+ block_number = liquidity_pool['block_number']
368
+
369
+ add_rpc_call(
370
+ abi=LP_TOKEN_ABI, contract_address=Web3.to_checksum_address(address),
371
+ fn_name="totalSupply", block_number=block_number,
372
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
373
+ )
374
+ add_rpc_call(
375
+ abi=LP_TOKEN_ABI, contract_address=Web3.to_checksum_address(address),
376
+ fn_name="decimals", block_number="latest",
377
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
378
+ )
379
+ try:
380
+ responses = self.client_querier.sent_batch_to_provider(list_rpc_call, batch_size=batch_size)
381
+ decoded_data = decode_data_response_ignore_error(data_responses=responses, list_call_id=list_call_id)
382
+ except Exception as ex:
383
+ err_detail = str(ex)
384
+ if err_detail.strip().startswith('Response data err'):
385
+ return
386
+ raise ex
387
+ data = {}
388
+ for liquidity_pool in liquidity_pools:
389
+ try:
390
+ address = liquidity_pool['address']
391
+ block_number = liquidity_pool['block_number']
392
+ total_supply = decoded_data.get(f'totalSupply_{address}_{block_number}'.lower())
393
+ decimals = decoded_data.get(f'decimals_{address}_latest'.lower())
394
+ data[address] = {
395
+ "liquidityAmount": total_supply / 10 ** decimals,
396
+ 'decimals': decimals,
397
+ }
398
+
399
+ except Exception:
400
+ continue
401
+
402
+ return data
403
+
404
+ def batch_nfts_info(self, addresses, batch_size=100):
405
+ list_rpc_call = []
406
+ list_call_id = []
407
+ for address in addresses:
408
+ add_rpc_call(
409
+ abi=ERC721_ABI, contract_address=Web3.to_checksum_address(address),
410
+ fn_name="name", block_number='latest',
411
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
412
+ )
413
+
414
+ add_rpc_call(
415
+ abi=ERC721_ABI, contract_address=Web3.to_checksum_address(address),
416
+ fn_name="symbol", block_number='latest',
417
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
418
+ )
419
+
420
+ responses = self.client_querier.sent_batch_to_provider(list_rpc_call, batch_size=batch_size)
421
+ decoded_data = decode_data_response_ignore_error(data_responses=responses, list_call_id=list_call_id)
422
+
423
+ data = {}
424
+ for address in addresses:
425
+ query_name_id = f'name_{address}_latest'
426
+ query_symbol_id = f'symbol_{address}_latest'
427
+
428
+ name = decoded_data.get(query_name_id)
429
+ symbol = decoded_data.get(query_symbol_id)
430
+ if name and symbol:
431
+ data[address] = {
432
+ "name": name,
433
+ "symbol": symbol
434
+ }
435
+
436
+ return data
437
+
438
+ def batch_liquidity_pools_tokens(self, liquidity_pools, batch_size=100):
439
+ list_rpc_call = []
440
+ list_call_id = []
441
+ for liquidity_pool in liquidity_pools:
442
+ address = liquidity_pool['address']
443
+
444
+ add_rpc_call(
445
+ abi=LP_TOKEN_ABI, contract_address=Web3.to_checksum_address(address),
446
+ fn_name="token0", block_number="latest",
447
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
448
+ )
449
+ add_rpc_call(
450
+ abi=LP_TOKEN_ABI, contract_address=Web3.to_checksum_address(address),
451
+ fn_name="token1", block_number="latest",
452
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
453
+ )
454
+ add_rpc_call(
455
+ abi=LP_TOKEN_ABI, contract_address=Web3.to_checksum_address(address),
456
+ fn_name="factory", block_number="latest",
457
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
458
+ )
459
+
460
+ try:
461
+ responses = self.client_querier.sent_batch_to_provider(list_rpc_call, batch_size=batch_size)
462
+ decoded_data = decode_data_response_ignore_error(data_responses=responses, list_call_id=list_call_id)
463
+ except Exception as ex:
464
+ err_detail = str(ex)
465
+ if err_detail.strip().startswith('Response data err'):
466
+ return
467
+ raise ex
468
+
469
+ for liquidity_pool in liquidity_pools:
470
+ address = liquidity_pool['address']
471
+
472
+ token0_address = decoded_data.get(f'token0_{address}_latest'.lower())
473
+ token1_address = decoded_data.get(f'token1_{address}_latest'.lower())
474
+ factory = decoded_data.get(f'factory_{address}_latest'.lower())
475
+
476
+ if token0_address and token1_address:
477
+ liquidity_pool.update({
478
+ "address": address,
479
+ "factoryAddress": factory,
480
+ "tokens": [
481
+ {
482
+ "idx": 0,
483
+ "address": token0_address.lower()
484
+ },
485
+ {
486
+ "idx": 1,
487
+ "address": token1_address.lower()
488
+ }
489
+ ]
490
+ })
491
+
492
+ return liquidity_pools
493
+
494
+ def batch_liquidity_pools_info(self, liquidity_pools, batch_size=100):
495
+ list_rpc_call = []
496
+ list_call_id = []
497
+ for liquidity_pool in liquidity_pools:
498
+ address = liquidity_pool['address']
499
+ block_number = liquidity_pool['block_number']
500
+
501
+ for token in liquidity_pool.get('tokens', []):
502
+ add_rpc_call(
503
+ abi=ERC20_ABI, contract_address=Web3.to_checksum_address(token['address']),
504
+ fn_name="decimals", block_number='latest',
505
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
506
+ )
507
+ add_rpc_call(
508
+ abi=ERC20_ABI, contract_address=Web3.to_checksum_address(token['address']),
509
+ fn_name="symbol", block_number='latest',
510
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
511
+ )
512
+ add_rpc_call(
513
+ abi=ERC20_ABI, contract_address=Web3.to_checksum_address(token['address']),
514
+ fn_name="balanceOf", fn_paras=address, block_number=block_number,
515
+ list_call_id=list_call_id, list_rpc_call=list_rpc_call
516
+ )
517
+
518
+ try:
519
+ responses = self.client_querier.sent_batch_to_provider(list_rpc_call, batch_size=batch_size)
520
+ decoded_data = decode_data_response_ignore_error(data_responses=responses, list_call_id=list_call_id)
521
+ except Exception as ex:
522
+ err_detail = str(ex)
523
+ if err_detail.strip().startswith('Response data err'):
524
+ return
525
+ raise ex
526
+
527
+ for liquidity_pool in liquidity_pools:
528
+ address = liquidity_pool['address']
529
+ block_number = liquidity_pool['block_number']
530
+
531
+ try:
532
+ for token in liquidity_pool.get('tokens', []):
533
+ symbol = decoded_data.get(f'symbol_{token["address"]}_latest'.lower())
534
+ symbol = symbol.upper() if symbol else ""
535
+ decimals = decoded_data.get(f'decimals_{token["address"]}_latest'.lower())
536
+
537
+ liquidity_amount = decoded_data.get(f'balanceOf_{token["address"]}_{address}_{block_number}'.lower(), 0)
538
+ token['liquidityAmount'] = liquidity_amount / 10 ** decimals
539
+ token['symbol'] = symbol
540
+ token['decimals'] = decimals
541
+
542
+ liquidity_pool['symbol'] = ' - '.join([t['symbol'] for t in liquidity_pool.get('tokens', [])])
543
+ except Exception as ex:
544
+ logger.exception(ex)
545
+
546
+ # Add to invalid liquidity pools
547
+ liquidity_pool.pop('tokens')
548
+
549
+ return liquidity_pools
@@ -21,7 +21,7 @@ class TokenServices:
21
21
  def get_data(wallet: str, token: str, decoded_data: dict, block_number: int = "latest", **kwargs):
22
22
  token_prices = kwargs.get("token_prices", {})
23
23
  decimals_key = f"decimals_{token}_{block_number}".lower()
24
- balance_key = f"balanceOf_{wallet}_{token}_{block_number}".lower()
24
+ balance_key = f"balanceOf_{token}_{[wallet]}_{block_number}".lower()
25
25
  if balance_key in decoded_data:
26
26
  balance = decoded_data.get(balance_key) or 0
27
27
  decimals = decoded_data.get(decimals_key) or 18
@@ -50,11 +50,12 @@ class TokenServices:
50
50
  return result
51
51
 
52
52
  def get_function_balance_info(self, wallet: str, token: str, block_number: int = "latest"):
53
- key = f"balanceOf_{wallet}_{token}_{block_number}".lower()
53
+ fn_paras = [wallet]
54
+ key = f"balanceOf_{token}_{fn_paras}_{block_number}".lower()
54
55
  if token == Token.native_token:
55
56
  return {key: self.state_service.get_native_token_balance_info(wallet, block_number)}
56
57
 
57
- return {key: self.state_service.get_function_info(token, ERC20_ABI, "balanceOf", [wallet], block_number)}
58
+ return {key: self.state_service.get_function_info(token, ERC20_ABI, "balanceOf", fn_paras, block_number)}
58
59
 
59
60
  def get_decimals_info(self, token: str, block_number: int = "latest"):
60
61
  decimals_token = token
@@ -0,0 +1,95 @@
1
+ import copy
2
+ import inspect
3
+
4
+
5
+ def flatten_dict(d):
6
+ out = {}
7
+ for key, val in d.items():
8
+ if isinstance(val, dict):
9
+ val = [val]
10
+ if isinstance(val, list):
11
+ array = []
12
+ for subdict in val:
13
+ if not isinstance(subdict, dict):
14
+ array.append(subdict)
15
+ else:
16
+ deeper = flatten_dict(subdict).items()
17
+ out.update({str(key) + '.' + str(key2): val2 for key2, val2 in deeper})
18
+ if array:
19
+ out.update({str(key): array})
20
+ else:
21
+ out[str(key)] = val
22
+ return out
23
+
24
+
25
+ def reverse_flatten_dict(d: dict) -> dict:
26
+ result = {}
27
+ for key, val in d.items():
28
+ nested_keys = key.split('.')
29
+ d_ = result
30
+ for k in nested_keys[:-1]:
31
+ if k not in d_:
32
+ d_[k] = {}
33
+ d_ = d_[k]
34
+ d_[nested_keys[-1]] = val
35
+
36
+ return result
37
+
38
+
39
+ def add_dict(first_dict: dict, second_dict: dict):
40
+ ans_dict = dict()
41
+ for key in first_dict.keys():
42
+ ans_dict[key] = first_dict[key] + second_dict[key]
43
+ return ans_dict
44
+
45
+
46
+ def remove_none_value_dict(a_dict):
47
+ copy_dict = a_dict.copy()
48
+ for key, value in copy_dict.items():
49
+ if value is None:
50
+ a_dict.pop(key)
51
+ return a_dict
52
+
53
+
54
+ def delete_none(_dict):
55
+ """Delete None values recursively from all the dictionaries"""
56
+ for key, value in list(_dict.items()):
57
+ if isinstance(value, dict):
58
+ delete_none(value)
59
+ elif value is None:
60
+ del _dict[key]
61
+ elif isinstance(value, list):
62
+ for v_i in value:
63
+ if isinstance(v_i, dict):
64
+ delete_none(v_i)
65
+
66
+ return _dict
67
+
68
+
69
+ def filter_doc_by_keys(doc, keys):
70
+ doc_ = copy.deepcopy(doc)
71
+ if keys is None:
72
+ return doc_
73
+ return dict(filter(lambda x: x[0] in keys, doc_.items()))
74
+
75
+
76
+ def get_class_constant_keys(cls):
77
+ keys = []
78
+ for attr in inspect.getmembers(cls):
79
+ # to remove private and protected functions
80
+ if not attr[0].startswith('_'):
81
+ # To remove function
82
+ if not inspect.ismethod(attr[1]):
83
+ keys.append(attr[1])
84
+
85
+ return keys
86
+
87
+
88
+ def short_address(address: str, n_start=3, n_end=4):
89
+ if not address:
90
+ return ''
91
+ return address[:n_start + 2] + '...' + address[-n_end:]
92
+
93
+
94
+ def all_equal(array):
95
+ return (not array) or (array.count(array[0]) == len(array))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: defi-state-querier
3
- Version: 0.4.29
3
+ Version: 0.5.0
4
4
  Summary: Calculate apy, apr, and wallet information,... in decentralized applications.
5
5
  Home-page: https://github.com/Centic-io/defi-state-querier
6
6
  Author: Viet-Bang Pham