solver-multirpc 3.1.8__py3-none-any.whl → 3.1.11__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.
@@ -34,12 +34,13 @@ class AsyncMultiRpc(BaseMultiRpc):
34
34
  is_proof_authority: bool = False,
35
35
  multicall_custom_address: str = None,
36
36
  log_level: logging = logging.WARN,
37
- setup_on_init: bool = True
37
+ setup_on_init: bool = True,
38
+ is_flash_block_aware: Optional[bool] = None
38
39
  ):
39
40
  super().__init__(rpc_urls, contract_address, contract_abi, rpcs_supporting_tx_trace,
40
41
  view_policy, gas_estimation, gas_limit,
41
42
  gas_upper_bound, apm, enable_estimate_gas_limit,
42
- is_proof_authority, multicall_custom_address, log_level)
43
+ is_proof_authority, multicall_custom_address, log_level, is_flash_block_aware)
43
44
 
44
45
  for func_abi in self.contract_abi:
45
46
  if func_abi.get("stateMutability") in ("view", "pure"):
@@ -55,13 +56,14 @@ class AsyncMultiRpc(BaseMultiRpc):
55
56
  if setup_on_init:
56
57
  asyncio.run(self.setup())
57
58
 
58
- async def get_nonce(self, address: Union[Address, ChecksumAddress, str]) -> int:
59
- return await super()._get_nonce(address)
59
+ async def get_nonce(self, address: Union[Address, ChecksumAddress, str],
60
+ block_identifier: BlockIdentifier = None) -> int:
61
+ return await super()._get_nonce(address, block_identifier)
60
62
 
61
63
  async def get_tx_receipt(self, tx_hash) -> TxReceipt:
62
64
  return await super().get_tx_receipt(tx_hash)
63
65
 
64
- async def get_block(self, block_identifier: BlockIdentifier = 'latest',
66
+ async def get_block(self, block_identifier: BlockIdentifier = None,
65
67
  full_transactions: bool = False) -> BlockData:
66
68
  return await super().get_block(block_identifier, full_transactions)
67
69
 
@@ -84,7 +86,7 @@ class AsyncMultiRpc(BaseMultiRpc):
84
86
  wait_for_receipt: int = 90,
85
87
  priority: TxPriority = TxPriority.Low,
86
88
  gas_estimation_method: GasEstimationMethod = None,
87
- block_identifier: Union[str, int] = 'latest',
89
+ block_identifier: Union[str, int] = None,
88
90
  enable_estimate_gas_limit: Optional[bool] = None,
89
91
  ):
90
92
  if self.mr.providers.get(self.typ) is None:
@@ -110,7 +112,7 @@ class AsyncMultiRpc(BaseMultiRpc):
110
112
 
111
113
  async def multicall(
112
114
  self,
113
- block_identifier: Union[str, int] = 'latest',
115
+ block_identifier: Union[str, int] = None,
114
116
  ):
115
117
  if self.mr.providers.get(self.typ) is None:
116
118
  raise DontHaveThisRpcType(f"Doesn't have {self.typ} RPCs")
@@ -16,15 +16,16 @@ from requests import ConnectionError, HTTPError, ReadTimeout
16
16
  from web3 import AsyncWeb3, Web3
17
17
  from web3._utils.contracts import encode_transaction_data # noqa
18
18
  from web3.contract import Contract
19
- from web3.exceptions import BadResponseFormat, BlockNotFound, TimeExhausted, TransactionNotFound
19
+ from web3.exceptions import BadResponseFormat, BlockNotFound, TimeExhausted, TransactionNotFound, Web3RPCError
20
20
  from web3.types import BlockData, BlockIdentifier, TxReceipt
21
21
 
22
- from .constants import EstimateGasLimitBuffer, GasLimit, GasUpperBound, MultiRPCLogger, ViewPolicy
22
+ from .constants import EstimateGasLimitBuffer, FlashBlockSupportedChains, GasLimit, GasUpperBound, MultiRPCLogger, \
23
+ ViewPolicy
23
24
  from .exceptions import (DontHaveThisRpcType, FailedOnAllRPCs, GetBlockFailed, NotValidViewPolicy,
24
25
  TransactionFailedStatus, TransactionValueError, Web3InterfaceException)
25
26
  from .gas_estimation import GasEstimation, GasEstimationMethod
26
27
  from .tx_trace import TxTrace
27
- from .utils import NestedDict, ResultEvent, TxPriority, get_chain_id, create_web3_from_rpc, \
28
+ from .utils import NestedDict, ResultEvent, TxPriority, create_web3_from_rpc, get_chain_id, \
28
29
  get_span_proper_label_from_provider, get_unix_time, reduce_list_of_list
29
30
 
30
31
  T = TypeVar("T")
@@ -49,7 +50,8 @@ class BaseMultiRpc(ABC):
49
50
  enable_estimate_gas_limit: bool = False,
50
51
  is_proof_authority: bool = False,
51
52
  multicall_custom_address: str = None,
52
- log_level: logging = logging.WARN
53
+ log_level: logging = logging.WARN,
54
+ is_flash_block_aware: Optional[bool] = None
53
55
  ):
54
56
  """
55
57
  Args:
@@ -90,7 +92,7 @@ class BaseMultiRpc(ABC):
90
92
  self.address = None
91
93
  self.private_key = None
92
94
  self.chain_id = None
93
-
95
+ self.is_flash_block_aware = is_flash_block_aware
94
96
  MultiRPCLogger.setLevel(log_level)
95
97
 
96
98
  def _logger_params(self, **kwargs) -> None:
@@ -99,6 +101,9 @@ class BaseMultiRpc(ABC):
99
101
  else:
100
102
  MultiRPCLogger.info(f'params={kwargs}')
101
103
 
104
+ def get_block_identifier(self, block_identifier=None):
105
+ return block_identifier or ('pending' if self.is_flash_block_aware else 'latest')
106
+
102
107
  def set_account(self, address: Union[ChecksumAddress, str], private_key: str) -> None:
103
108
  """
104
109
  Set public key and private key for sending transactions. If these values set, there is no need to pass address,
@@ -114,6 +119,11 @@ class BaseMultiRpc(ABC):
114
119
  self.providers = await create_web3_from_rpc(self.rpc_urls, self.is_proof_authority)
115
120
  self.chain_id = await get_chain_id(self.providers)
116
121
 
122
+ if self.is_flash_block_aware is None:
123
+ self.is_flash_block_aware = self.chain_id in FlashBlockSupportedChains
124
+
125
+ MultiRPCLogger.debug(f"{self.chain_id=}, {self.is_flash_block_aware=}")
126
+
117
127
  if self.gas_estimation is None and self.providers.get('transaction'):
118
128
  self.gas_estimation = GasEstimation(
119
129
  self.chain_id,
@@ -196,7 +206,7 @@ class BaseMultiRpc(ABC):
196
206
 
197
207
  async def _call_view_function(self,
198
208
  func_name: str,
199
- block_identifier: Union[str, int] = 'latest',
209
+ block_identifier: Union[str, int] = None,
200
210
  use_multicall=False,
201
211
  *args, **kwargs):
202
212
  """
@@ -222,6 +232,7 @@ class BaseMultiRpc(ABC):
222
232
  return results[max_index][2]
223
233
  return results[max_index][2][0]
224
234
 
235
+ block_identifier = self.get_block_identifier(block_identifier)
225
236
  last_error = None
226
237
  for contracts, multi_calls in zip(self.contracts['view'].values(),
227
238
  self.multi_calls['view'].values()): # type: any, List[AsyncMulticall]
@@ -246,7 +257,8 @@ class BaseMultiRpc(ABC):
246
257
  last_error = None
247
258
  for providers in providers_4_nonce.values():
248
259
  execution_list = [
249
- prov.eth.get_transaction_count(address, block_identifier=block_identifier) for prov in providers
260
+ prov.eth.get_transaction_count(address, block_identifier=self.get_block_identifier(block_identifier))
261
+ for prov in providers
250
262
  ]
251
263
  try:
252
264
  return await self.__gather_tasks(execution_list, max)
@@ -295,7 +307,7 @@ class BaseMultiRpc(ABC):
295
307
  account: LocalAccount = Account.from_key(signer_private_key)
296
308
  if enable_estimate_gas_limit:
297
309
  del tx['gas']
298
- estimate_gas = await provider.eth.estimate_gas(tx)
310
+ estimate_gas = await provider.eth.estimate_gas(tx, block_identifier=self.get_block_identifier())
299
311
  MultiRPCLogger.info(f"gas_estimation({estimate_gas} gas needed) is successful")
300
312
  return account.sign_transaction({**tx, 'gas': int(estimate_gas * EstimateGasLimitBuffer)})
301
313
  return account.sign_transaction(tx)
@@ -311,7 +323,7 @@ class BaseMultiRpc(ABC):
311
323
  self._logger_params(**{f"{rpc_label_prefix}_post_send_time": get_unix_time()})
312
324
  self._logger_params(tx_send_time=int(time.time() * 1000))
313
325
  return provider, transaction
314
- except ValueError as e:
326
+ except (ValueError, Web3RPCError) as e:
315
327
  MultiRPCLogger.error(f"RPC({rpc_url}) value error: {str(e)}")
316
328
  t_bnb_flag = "transaction would cause overdraft" in str(e).lower() and (await provider.eth.chain_id) == 97
317
329
  if not (
@@ -492,7 +504,7 @@ class BaseMultiRpc(ABC):
492
504
  ]
493
505
  result = await self.__execute_batch_tasks(
494
506
  execution_tx_list,
495
- [ValueError, ConnectionError, ReadTimeout, HTTPError],
507
+ [ValueError, ConnectionError, ReadTimeout, HTTPError, Web3RPCError],
496
508
  FailedOnAllRPCs
497
509
  )
498
510
  provider, tx = result
@@ -577,14 +589,15 @@ class BaseMultiRpc(ABC):
577
589
  raise
578
590
  raise last_exception
579
591
 
580
- async def get_block(self, block_identifier: BlockIdentifier = 'latest',
581
- full_transactions: bool = False) -> BlockData:
592
+ async def get_block(self, block_identifier: BlockIdentifier = None, full_transactions: bool = False) -> BlockData:
582
593
  self.check_for_view()
583
594
 
584
595
  exceptions = (HTTPError, ConnectionError, ReadTimeout, ValueError, TimeExhausted, BlockNotFound)
585
596
  last_exception = None
586
597
  for provider in self.providers['view'].values(): # type: List[AsyncWeb3]
587
- execution_tx_params_list = [p.eth.get_block(block_identifier, full_transactions) for p in provider]
598
+ execution_tx_params_list = [
599
+ p.eth.get_block(self.get_block_identifier(block_identifier), full_transactions) for p in provider
600
+ ]
588
601
  try:
589
602
  return await self.__execute_batch_tasks(
590
603
  execution_tx_params_list,
multirpc/constants.py CHANGED
@@ -36,6 +36,10 @@ ChainIdToGas = {
36
36
  GasFromRpcChainIds = [] # for this chain ids use rpc to estimate gas
37
37
  FixedValueGas = 30
38
38
 
39
+ FlashBlockSupportedChains = [
40
+ 8453 # base
41
+ ]
42
+
39
43
  MultiRPCLoggerName = 'Multi-RPC'
40
44
  GasEstimationLoggerName = MultiRPCLoggerName + '.Gas-Estimation'
41
45
 
multirpc/exceptions.py CHANGED
@@ -5,6 +5,8 @@ BaseException_ = Exception
5
5
 
6
6
  class Web3InterfaceException(BaseException_):
7
7
  def __str__(self):
8
+ if not self.args:
9
+ return f"{self.__class__.__name__}()"
8
10
  return f"{self.__class__.__name__}({self.args[0]})"
9
11
 
10
12
 
@@ -52,6 +54,9 @@ class MaximumRPCInEachBracketReached(Web3InterfaceException):
52
54
  class AtLastProvideOneValidRPCInEachBracket(Web3InterfaceException):
53
55
  pass
54
56
 
57
+ class AllRPCShouldSupportFlashBlockOrNot(Web3InterfaceException):
58
+ pass
59
+
55
60
 
56
61
  class TransactionValueError(Web3InterfaceException):
57
62
  pass
@@ -34,12 +34,13 @@ class MultiRpc(BaseMultiRpc):
34
34
  is_proof_authority: bool = False,
35
35
  multicall_custom_address: str = None,
36
36
  log_level: logging = logging.WARN,
37
- setup_on_init: bool = True
37
+ setup_on_init: bool = True,
38
+ is_flash_block_aware: Optional[bool] = None
38
39
  ):
39
40
  super().__init__(rpc_urls, contract_address, contract_abi, rpcs_supporting_tx_trace,
40
41
  view_policy, gas_estimation, gas_limit,
41
42
  gas_upper_bound, apm, enable_estimate_gas_limit,
42
- is_proof_authority, multicall_custom_address, log_level)
43
+ is_proof_authority, multicall_custom_address, log_level, is_flash_block_aware)
43
44
 
44
45
  for func_abi in self.contract_abi:
45
46
  if func_abi.get("stateMutability") in ("view", "pure"):
@@ -60,15 +61,15 @@ class MultiRpc(BaseMultiRpc):
60
61
  return asyncio.run(super().setup())
61
62
 
62
63
  @thread_safe
63
- def get_nonce(self, address: Union[Address, ChecksumAddress, str]) -> int:
64
- return asyncio.run(super()._get_nonce(address))
64
+ def get_nonce(self, address: Union[Address, ChecksumAddress, str], block_identifier: BlockIdentifier = None) -> int:
65
+ return asyncio.run(super()._get_nonce(address, block_identifier))
65
66
 
66
67
  @thread_safe
67
68
  def get_tx_receipt(self, tx_hash) -> TxReceipt:
68
69
  return asyncio.run(super().get_tx_receipt(tx_hash))
69
70
 
70
71
  @thread_safe
71
- def get_block(self, block_identifier: BlockIdentifier = 'latest', full_transactions: bool = False) -> BlockData:
72
+ def get_block(self, block_identifier: BlockIdentifier = None, full_transactions: bool = False) -> BlockData:
72
73
  return asyncio.run(super().get_block(block_identifier, full_transactions))
73
74
 
74
75
  @thread_safe
@@ -92,7 +93,7 @@ class MultiRpc(BaseMultiRpc):
92
93
  wait_for_receipt: int = 90,
93
94
  priority: TxPriority = TxPriority.Low,
94
95
  gas_estimation_method: GasEstimationMethod = None,
95
- block_identifier: Union[str, int] = 'latest',
96
+ block_identifier: Union[str, int] = None,
96
97
  enable_estimate_gas_limit: Optional[bool] = None,
97
98
  use_multicall=False,
98
99
  ):
@@ -100,7 +101,7 @@ class MultiRpc(BaseMultiRpc):
100
101
  raise DontHaveThisRpcType(f"Doesn't have {self.typ} RPCs")
101
102
  if self.typ == ContractFunctionType.View:
102
103
  return asyncio.run(self.mr._call_view_function(
103
- self.name, block_identifier, use_multicall, *self.args, **self.kwargs,
104
+ self.name, block_identifier, use_multicall, *self.args, **self.kwargs
104
105
  ))
105
106
  elif self.typ == ContractFunctionType.Transaction:
106
107
  return asyncio.run(self.mr._call_tx_function(
@@ -114,13 +115,13 @@ class MultiRpc(BaseMultiRpc):
114
115
  wait_for_receipt=wait_for_receipt,
115
116
  priority=priority,
116
117
  gas_estimation_method=gas_estimation_method,
117
- enable_estimate_gas_limit=enable_estimate_gas_limit
118
+ enable_estimate_gas_limit=enable_estimate_gas_limit,
118
119
  ))
119
120
 
120
121
  @thread_safe
121
122
  def multicall(
122
123
  self,
123
- block_identifier: Union[str, int] = 'latest',
124
+ block_identifier: Union[str, int] = None,
124
125
  ):
125
126
  if self.mr.providers.get(self.typ) is None:
126
127
  raise DontHaveThisRpcType(f"Doesn't have {self.typ} RPCs")
@@ -128,7 +129,7 @@ class MultiRpc(BaseMultiRpc):
128
129
  raise KwargsNotSupportedInMultiCall
129
130
  if self.typ == ContractFunctionType.View:
130
131
  return asyncio.run(self.mr._call_view_function(
131
- self.name, block_identifier, True, *self.args, **self.kwargs,
132
+ self.name, block_identifier, True, *self.args, **self.kwargs
132
133
  ))
133
134
  elif self.typ == ContractFunctionType.Transaction:
134
135
  raise TransactionTypeNotSupportedInMultiCall
multirpc/utils.py CHANGED
@@ -17,7 +17,8 @@ from web3._utils.http_session_manager import HTTPSessionManager
17
17
  from web3.middleware import ExtraDataToPOAMiddleware
18
18
 
19
19
  from .constants import MaxRPCInEachBracket, MultiRPCLogger
20
- from .exceptions import AtLastProvideOneValidRPCInEachBracket, MaximumRPCInEachBracketReached
20
+ from .exceptions import AtLastProvideOneValidRPCInEachBracket, \
21
+ MaximumRPCInEachBracketReached
21
22
 
22
23
 
23
24
  def get_span_proper_label_from_provider(endpoint_uri):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: solver-multirpc
3
- Version: 3.1.8
3
+ Version: 3.1.11
4
4
  Summary: A robust Python library for interacting with Ethereum smart contracts via multiple RPC endpoints, ensuring reliability, availability, and load distribution with automatic retries on failure.
5
5
  License: MIT
6
6
  Author: rorschach
@@ -0,0 +1,12 @@
1
+ multirpc/__init__.py,sha256=f7h3txKkb_WQX6u-PzbgMpHt1VGqA0OjhrSe4Vdsk5I,100
2
+ multirpc/async_multi_rpc_interface.py,sha256=NTwdOyMbghekredlYR64kr0eiPRxzvUirGBZpFzRgig,5699
3
+ multirpc/base_multi_rpc_interface.py,sha256=9TPQABrutwe8yHu05Nt5Uv5_jZlMdlu8XyY1PIQoUiU,29238
4
+ multirpc/constants.py,sha256=B21pS85Vnm65vvjt25_9TTxviRHYCUlgpIDGOrs1rmg,1262
5
+ multirpc/exceptions.py,sha256=Tfi4aHeOiH1OaYeNBEDX3fJNAUyPgs42VXrRG62IgJs,1937
6
+ multirpc/gas_estimation.py,sha256=k0FVO9FgSRuRQhEkQ9YZZ-rSpW_zxaLLFdMAYOF3xco,7276
7
+ multirpc/sync_multi_rpc_interface.py,sha256=Zs3FVSGNJDhrFBi3m9sn4pMuYGzLwaiz0qy_R9VIo04,5888
8
+ multirpc/tx_trace.py,sha256=G7sTVMzALtTeC1F2JjxMSGRRFlmAAZG-Sdhd4UQtfw8,7885
9
+ multirpc/utils.py,sha256=bhrP15IY_TOB1dK8HP35rRxs9FIIWKoSUY6EVOA4FEA,9073
10
+ solver_multirpc-3.1.11.dist-info/METADATA,sha256=6AwwApO4kTI_q0SDqCKh4rb9wmi3IBj66oISHIwz2t4,8287
11
+ solver_multirpc-3.1.11.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
12
+ solver_multirpc-3.1.11.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- multirpc/__init__.py,sha256=f7h3txKkb_WQX6u-PzbgMpHt1VGqA0OjhrSe4Vdsk5I,100
2
- multirpc/async_multi_rpc_interface.py,sha256=2YyGXst5jyxZ7ro7z7qeOAF2wKC4JEgbwkR-ADCa-9w,5548
3
- multirpc/base_multi_rpc_interface.py,sha256=I6WP0e3hcJ1IMVipuWdrZFNjoZBdL4BGZ4eXQVT11oI,28502
4
- multirpc/constants.py,sha256=h0njFnxTtWU8-NO_3q7sMrLAqQAv4JxM3nZcc2WOqEc,1210
5
- multirpc/exceptions.py,sha256=V8WDmDWeeTLQ1TOJ9_IWXt0z-qefJiDfQ6pAdhuwdcA,1785
6
- multirpc/gas_estimation.py,sha256=k0FVO9FgSRuRQhEkQ9YZZ-rSpW_zxaLLFdMAYOF3xco,7276
7
- multirpc/sync_multi_rpc_interface.py,sha256=T9x5bLbLJF_1XorkkDqzc8pZmbdNs6OE9trGjVWQBdg,5762
8
- multirpc/tx_trace.py,sha256=G7sTVMzALtTeC1F2JjxMSGRRFlmAAZG-Sdhd4UQtfw8,7885
9
- multirpc/utils.py,sha256=V0hosPMFqsvZy1OtWUOR-4xk1U4bPmGUfviBivygZJw,9067
10
- solver_multirpc-3.1.8.dist-info/METADATA,sha256=iqs2MY1Ay79oo1yEnVDcSnB3QYxO9NlOhVdeRoPgGRA,8286
11
- solver_multirpc-3.1.8.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
12
- solver_multirpc-3.1.8.dist-info/RECORD,,