[docs]classNonceMixin:client:Clientaccount_contract_address:Optional[int]nonce_status:Dict[int,TransactionStatus]={}nonce_dict:Dict[int,int]={}pending_nonce:Optional[int]=Noneasyncdef_get_nonce(self)->int:""" Returns the nonce that should be used for the next transaction. Steps: 1. Update nonces' statuses. 2. Cleanup nonce_dict. 3. Return the pending nonce if there is one. 4. Return the latest nonce if there are no pending nonces. """awaitself.update_nonce_dict()self.cleanup_nonce_dict()ifself.pending_nonce:returnself.pending_nonceifself.nonce_status:returnmax(self.nonce_status)+1latest_nonce=awaitself.get_nonce()returnlatest_nonce
[docs]defcleanup_nonce_dict(self):""" Cleanup the nonce_dict by removing all txs that have been either rejected or accepted. """nonce_seq=[xforxinself.nonce_dictifself.nonce_status[x]in[TransactionStatus.ACCEPTED_ON_L1,TransactionStatus.ACCEPTED_ON_L2,TransactionStatus.REJECTED,]]ifnonce_seq:max_accepted=max(nonce_seq)fornonceinlist(self.nonce_dict):ifnonce<=max_accepted:delself.nonce_dict[nonce]ifnonceinself.nonce_status:delself.nonce_status[nonce]ifself.pending_nonceandself.pending_nonce<max_accepted:self.pending_nonce=None
[docs]asyncdeftrack_nonce(self,nonce:int,transaction_hash:int,)->None:""" Callback function to track the nonce of a transaction. Will update the nonce_dict and pending_nonce attributes. We track the nonce used for a given transaction hash. :param nonce: The nonce of the transaction. :param transaction_hash: The hash of the transaction. """self.nonce_dict[nonce]=transaction_hashnonce_min=min(self.nonce_dict)nonce_max=max(self.nonce_dict)foriinrange(nonce_min,nonce_max+1):ifinotinself.nonce_dict:self.pending_nonce=ibreak
[docs]asyncdefupdate_nonce_dict(self,):""" Update the statuses of the nonces in the nonce_dict. """fornonceinlist(self.nonce_dict):self.nonce_status[nonce]=awaitself.get_status(self.nonce_dict[nonce])ifself.nonce_status[nonce]in[TransactionStatus.REJECTED,]:# assume all later transaction will fail because this nonce was skippedself.pending_nonce=nonceself.nonce_dict={}self.nonce_status={}return
[docs]asyncdefget_nonce(self,include_pending=True,block_number:Optional[int|str|Literal["pending","latest"]]=None,)->int:""" Get the nonce of the account contract address. Just a wrapper around the client's get_contract_nonce method. :param include_pending: Whether to include pending transactions in the nonce calculation. :param block_number: Custom block number to get the nonce from. """ifnotblock_number:block_number="pending"ifinclude_pendingelse"latest"nonce=awaitself.client.get_contract_nonce(self.account_contract_address,block_number=block_number,)returnnonce# type: ignore[no-any-return]
[docs]asyncdefget_status(self,transaction_hash:int,check_interval:int=2,retries:int=500,)->TransactionStatus:""" Tries to get the status of a transaction by its hash. :param transaction_hash: The hash of the transaction. :param check_interval: The interval in seconds between each check. :param retries: The number of retries before giving up. :return: The status of the transaction. """ifcheck_interval<=0:raiseValueError("Argument check_interval has to be greater than 0.")ifretries<=0:raiseValueError("Argument retries has to be greater than 0.")whileTrue:retries-=1try:tx_status=awaitself.client.get_transaction_status(tx_hash=transaction_hash)returntx_status.finality_statusexceptasyncio.CancelledErrorasexc:raiseTransactionNotReceivedErrorfromexcexceptClientErrorasexc:if"Transaction hash not found"notinexc.message:raiseexcawaitasyncio.sleep(check_interval)