Source code for arbitrum_py.data_entities.retryable_data

from typing import Dict, List, Optional

from eth_abi import abi
from eth_typing import HexAddress, HexStr
from web3 import Web3

from arbitrum_py.utils.helper import CaseDict


[docs]class RetryableData: """Represents a retryable ticket's data structure on Arbitrum. This class mirrors the RetryableData error struct from Inbox.sol and contains all parameters needed to create or estimate a retryable ticket transaction. Attributes: fromAddress (HexAddress): The L1 address creating the retryable ticket to (HexAddress): Destination address on L2 l2CallValue (int): The amount of ETH (or token) supplied on L2 deposit (int): The total deposit on L1 covering L2 gas + L2 call value maxSubmissionCost (int): The base submission fee for sending the message excessFeeRefundAddress (HexAddress): L2 address for any leftover gas funds callValueRefundAddress (HexAddress): L2 address for leftover callValue if canceled gasLimit (int): The L2 gas limit for execution maxFeePerGas (int): The max L2 gas price data (bytes): The calldata for the L2 contract call """ # Types expected in the custom error's signature, in order abi_types: List[str] = [ "address", # from "address", # to "uint256", # l2CallValue "uint256", # deposit "uint256", # maxSubmissionCost "address", # excessFeeRefundAddress "address", # callValueRefundAddress "uint256", # gasLimit "uint256", # maxFeePerGas "bytes", # data ]
[docs] def __init__( self, from_address: HexAddress, to: HexAddress, l2_call_value: int, deposit: int, max_submission_cost: int, excess_fee_refund_address: HexAddress, call_value_refund_address: HexAddress, gas_limit: int, max_fee_per_gas: int, data: bytes, ) -> None: """Initialize a new RetryableData instance. Args: from_address: The L1 address creating the retryable ticket to: Destination address on L2 l2_call_value: The amount of ETH (or token) supplied on L2 deposit: The total deposit on L1 covering L2 gas + L2 call value max_submission_cost: The base submission fee for sending the message excess_fee_refund_address: L2 address for any leftover gas funds call_value_refund_address: L2 address for leftover callValue if canceled gas_limit: The L2 gas limit for execution max_fee_per_gas: The max L2 gas price data: The calldata for the L2 contract call """ self.fromAddress = from_address self.to = to self.l2CallValue = l2_call_value self.deposit = deposit self.maxSubmissionCost = max_submission_cost self.excessFeeRefundAddress = excess_fee_refund_address self.callValueRefundAddress = call_value_refund_address self.gasLimit = gas_limit self.maxFeePerGas = max_fee_per_gas self.data = data
[docs]class RetryableDataTools: """Tools for parsing retryable data from revert errors. When calling createRetryableTicket on Inbox.sol, special values can be passed for gasLimit and maxFeePerGas. This causes the call to revert with the info needed to estimate the gas needed for a retryable ticket. Attributes: ErrorTriggeringParams (Dict[str, int]): Parameters that should be passed to createRetryableTicket to induce a revert with retryable data. Contains gasLimit=1 and maxFeePerGas=1. """ ErrorTriggeringParams: Dict[str, int] = { "gasLimit": 1, "maxFeePerGas": 1, }
[docs] @staticmethod def try_parse_error(error_data_hex: HexStr) -> Optional[CaseDict]: """Parse RetryableData struct from revert error data. Attempts to parse a RetryableData struct from the given hex string. The input should be the revert data from a transaction that reverts with `error RetryableData(...)`. Args: error_data_hex: The raw revert data as a hex string (with or without '0x' prefix) Returns: A CaseDict containing the parsed RetryableData fields if successful, or None if parsing fails. The returned dict will have the following fields: - from (HexAddress): The L1 sender address - to (HexAddress): The L2 destination address - l2CallValue (int): Amount of ETH/tokens for L2 - deposit (int): Total L1 deposit amount - maxSubmissionCost (int): Base submission fee - excessFeeRefundAddress (HexAddress): Refund address for excess gas - callValueRefundAddress (HexAddress): Refund address for call value - gasLimit (int): L2 gas limit - maxFeePerGas (int): Max L2 gas price - data (bytes): L2 call data Example: >>> error_data = "0x..." # Revert data from failed tx >>> retryable = RetryableDataTools.try_parse_error(error_data) >>> if retryable: ... print(f"L2 destination: {retryable['to']}") ... print(f"Gas limit: {retryable['gasLimit']}") """ try: # Ensure we strip '0x' if present if error_data_hex.startswith("0x"): error_data_hex = error_data_hex[2:] # The first 4 bytes (8 hex digits) in the revert data is the error selector # We skip those to decode the actual parameters error_data_hex = error_data_hex[8:] # Decode the raw hex into the 10 fields decoded_data = abi.decode(RetryableData.abi_types, bytes.fromhex(error_data_hex)) if len(decoded_data) != len(RetryableData.abi_types): # Something is off with the length return None # Build a dictionary that matches the TS 'RetryableData' shape # Use checksummed addresses for 'from', 'to', etc. return CaseDict( { "from": Web3.to_checksum_address(decoded_data[0]), "to": Web3.to_checksum_address(decoded_data[1]), "l2CallValue": decoded_data[2], "deposit": decoded_data[3], "maxSubmissionCost": decoded_data[4], "excessFeeRefundAddress": Web3.to_checksum_address(decoded_data[5]), "callValueRefundAddress": Web3.to_checksum_address(decoded_data[6]), "gasLimit": decoded_data[7], "maxFeePerGas": decoded_data[8], "data": decoded_data[9], # bytes } ) except Exception as e: # Return None if we fail to decode return None