From 5d8d1ce5d74308dc772458a13c81bfff3ce56e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Mon, 8 Jun 2026 18:59:13 +0100 Subject: [PATCH 01/19] fix: serialize fee payloads for studio estimates (#88) --- genlayer_py/contracts/actions.py | 17 ++++++- genlayer_py/transactions/fees.py | 28 +++++++++- tests/unit/contracts/test_contract_actions.py | 51 +++++++++++++++++-- 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/genlayer_py/contracts/actions.py b/genlayer_py/contracts/actions.py index f2f62fc..3f27908 100644 --- a/genlayer_py/contracts/actions.py +++ b/genlayer_py/contracts/actions.py @@ -442,7 +442,22 @@ def _transaction_fees_to_rpc( } if normalized["fee_value"] is not None: rpc_fees["feeValue"] = normalized["fee_value"] - return rpc_fees + return _json_safe_rpc_value(rpc_fees) + + +def _json_safe_rpc_value(value): + if isinstance(value, (bytes, bytearray)): + return "0x" + bytes(value).hex() + if isinstance(value, list): + return [_json_safe_rpc_value(item) for item in value] + if isinstance(value, tuple): + return [_json_safe_rpc_value(item) for item in value] + if isinstance(value, dict): + return { + key: _json_safe_rpc_value(item) + for key, item in value.items() + } + return value def _encode_submit_appeal_data( diff --git a/genlayer_py/transactions/fees.py b/genlayer_py/transactions/fees.py index e05c529..88b9275 100644 --- a/genlayer_py/transactions/fees.py +++ b/genlayer_py/transactions/fees.py @@ -131,6 +131,7 @@ class TransactionFeeEstimate(TypedDict, total=False): fee_value: int policy: FeePolicyQuote observed: dict[str, int] + simulation: Any class SimulationFeeEstimateOptions(FeeEstimateOptions, total=False): @@ -193,6 +194,7 @@ class NormalizedTransactionFees(TypedDict): DEFAULT_LEADER_TIMEUNITS_ALLOCATION = 100 DEFAULT_VALIDATOR_TIMEUNITS_ALLOCATION = 200 DEFAULT_TRANSACTION_EXECUTION_BUDGET_PER_ROUND = 500_000 +DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM = 10_000 MIN_RECEIPT_BYTES = 512 DEFAULT_RECEIPT_SLOTS_CHANGED = 7 DEFAULT_INTRINSIC_GAS = 21_000 @@ -967,6 +969,7 @@ def transaction_fee_estimate_from_studio_estimate( "feeValue": to_uint(fee_value, "recommendedPreset.feeValue"), "fee_value": to_uint(fee_value, "recommendedPreset.feeValue"), "policy": policy, + "simulation": simulation, "observed": observed_simulation_fee_usage( {"simulation": simulation}, policy, @@ -1058,17 +1061,38 @@ def build_estimated_fees_distribution( "priceCapHeadroomBps", DEFAULT_PRICE_CAP_HEADROOM_BPS, ) - execution_budget_default = _default_execution_budget_per_round(policy) + base_execution_budget_default = _default_execution_budget_per_round(policy) total_message_fees = _get(options, "totalMessageFees", "total_message_fees") message_allocations = _get(options, "messageAllocations", "message_allocations") + normalized_message_allocations = ( + normalize_message_fee_allocations(message_allocations) + if message_allocations is not None + else None + ) if total_message_fees is None and message_allocations is not None: total_message_fees = sum( allocation["budget"] - for allocation in normalize_message_fee_allocations(message_allocations) + for allocation in normalized_message_allocations or [] if allocation["messageType"] == int(MessageType.External) or allocation["parentIndex"] == MESSAGE_ALLOCATION_ROOT_PARENT_INDEX ) + emits_messages = ( + ( + normalized_message_allocations is not None + and len(normalized_message_allocations) > 0 + ) + or ( + total_message_fees is not None + and to_uint(total_message_fees, "totalMessageFees") > 0 + ) + ) + execution_budget_default = ( + base_execution_budget_default + + policy["receiptGasPrice"] * DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM + if emits_messages + else base_execution_budget_default + ) return create_fees_distribution( { diff --git a/tests/unit/contracts/test_contract_actions.py b/tests/unit/contracts/test_contract_actions.py index cc58d44..12c2e22 100644 --- a/tests/unit/contracts/test_contract_actions.py +++ b/tests/unit/contracts/test_contract_actions.py @@ -28,6 +28,9 @@ ) +DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM = 10_000 + + ADD_TRANSACTION_ABI_V5 = [ { "type": "function", @@ -385,6 +388,10 @@ def test_simulate_write_contract_passes_fee_policy_and_value_to_sim_call(): MessageType.Internal ) assert request_params["fees"]["messageAllocations"][0]["budget"] == 5 + assert ( + request_params["fees"]["messageAllocations"][0]["callKey"] + == "0x" + "00" * 32 + ) def test_encode_add_transaction_uses_v5_signature_when_abi_has_5_inputs(): @@ -584,13 +591,42 @@ def test_build_estimated_fees_distribution_adds_caps_and_message_bucket(): assert distribution["leaderTimeunitsAllocation"] == 100 assert distribution["validatorTimeunitsAllocation"] == 200 - assert distribution["executionBudgetPerRound"] == 500_000 + assert distribution["executionBudgetPerRound"] == ( + 500_000 + 30 * DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM + ) assert distribution["totalMessageFees"] == 80 assert distribution["maxPriceGenPerTimeUnit"] == 12 assert distribution["storageFeeMaxGasPrice"] == 24 assert distribution["receiptFeeMaxGasPrice"] == 36 +def test_build_estimated_fees_distribution_preserves_explicit_execution_budget_with_messages(): + policy = { + "enabled": True, + "genPerTimeUnit": 10, + "storageUnitPrice": 20, + "receiptGasPrice": 30, + "executionBudgetFloor": 1_234, + } + + distribution = build_estimated_fees_distribution( + { + "executionBudgetPerRound": 42, + "messageAllocations": [ + { + "messageType": MessageType.Internal, + "recipient": RECIPIENT, + "budget": 50, + "feeParams": "0x1234", + }, + ], + }, + policy, + ) + + assert distribution["executionBudgetPerRound"] == 42 + + def test_calculate_local_round_fees_matches_consensus_initial_round(): distribution = create_fees_distribution( { @@ -702,7 +738,10 @@ def test_estimate_transaction_fees_derives_message_bucket_from_allocations(): ) assert estimate["distribution"]["totalMessageFees"] == 80 - assert estimate["feeValue"] == 9_196_840 + assert estimate["distribution"]["executionBudgetPerRound"] == ( + 9_185_760 + 30 * DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM + ) + assert estimate["feeValue"] == 9_496_840 assert estimate["messageAllocations"] == message_allocations assert estimate["message_allocations"] == message_allocations @@ -882,12 +921,18 @@ def make_request(method, params): ) request_params = sim_call.kwargs["params"][0] assert request_params["value"] == hex(7) - assert request_params["fees"]["feeValue"] == 511_110 + assert request_params["fees"]["feeValue"] == 521_110 assert request_params["fees"]["distribution"]["totalMessageFees"] == 110 assert request_params["fees"]["messageAllocations"][0]["budget"] == 110 + assert ( + request_params["fees"]["messageAllocations"][0]["callKey"] + == "0x" + "00" * 32 + ) assert estimate["observed"]["recommendedExecutionBudgetPerRound"] == 602_117 assert estimate["observed"]["messageFeeBudget"] == 110 assert estimate["observed"]["messageFeeConsumed"] == 50 + assert estimate["simulation"]["feeAccounting"]["message_fee_budget"] == "110" + assert estimate["simulation"]["feeReport"]["totalEstimatedFee"] == "501664" assert estimate["distribution"]["executionBudgetPerRound"] == 700_000 assert estimate["distribution"]["totalMessageFees"] == 110 assert estimate["messageAllocations"][0]["budget"] == 110 From 61dd2c0c3b4f0a83771aeb030a0361a141f51e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Tue, 9 Jun 2026 23:41:27 +0100 Subject: [PATCH 02/19] fix: include execution budget floor in fee estimates (#89) --- genlayer_py/transactions/fees.py | 2 ++ tests/unit/contracts/test_contract_actions.py | 22 +++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/genlayer_py/transactions/fees.py b/genlayer_py/transactions/fees.py index 88b9275..9916866 100644 --- a/genlayer_py/transactions/fees.py +++ b/genlayer_py/transactions/fees.py @@ -194,6 +194,7 @@ class NormalizedTransactionFees(TypedDict): DEFAULT_LEADER_TIMEUNITS_ALLOCATION = 100 DEFAULT_VALIDATOR_TIMEUNITS_ALLOCATION = 200 DEFAULT_TRANSACTION_EXECUTION_BUDGET_PER_ROUND = 500_000 +DEFAULT_TRANSACTION_EXECUTION_GAS = 100_000_000 DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM = 10_000 MIN_RECEIPT_BYTES = 512 DEFAULT_RECEIPT_SLOTS_CHANGED = 7 @@ -705,6 +706,7 @@ def _default_execution_budget_per_round(policy: FeePolicyQuote) -> int: return max( DEFAULT_TRANSACTION_EXECUTION_BUDGET_PER_ROUND, policy["executionBudgetFloor"], + policy["receiptGasPrice"] * DEFAULT_TRANSACTION_EXECUTION_GAS, ) diff --git a/tests/unit/contracts/test_contract_actions.py b/tests/unit/contracts/test_contract_actions.py index 12c2e22..7887bed 100644 --- a/tests/unit/contracts/test_contract_actions.py +++ b/tests/unit/contracts/test_contract_actions.py @@ -592,7 +592,7 @@ def test_build_estimated_fees_distribution_adds_caps_and_message_bucket(): assert distribution["leaderTimeunitsAllocation"] == 100 assert distribution["validatorTimeunitsAllocation"] == 200 assert distribution["executionBudgetPerRound"] == ( - 500_000 + 30 * DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM + 3_000_000_000 + 30 * DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM ) assert distribution["totalMessageFees"] == 80 assert distribution["maxPriceGenPerTimeUnit"] == 12 @@ -683,8 +683,8 @@ def test_estimate_transaction_fees_uses_studio_fee_config(): }, } ) - assert estimate["distribution"]["executionBudgetPerRound"] == 9_185_760 - assert estimate["feeValue"] == 9_196_760 + assert estimate["distribution"]["executionBudgetPerRound"] == 3_000_000_000 + assert estimate["feeValue"] == 3_000_011_000 def test_estimate_transaction_fees_derives_message_bucket_from_allocations(): @@ -739,9 +739,9 @@ def test_estimate_transaction_fees_derives_message_bucket_from_allocations(): assert estimate["distribution"]["totalMessageFees"] == 80 assert estimate["distribution"]["executionBudgetPerRound"] == ( - 9_185_760 + 30 * DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM + 3_000_000_000 + 30 * DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM ) - assert estimate["feeValue"] == 9_496_840 + assert estimate["feeValue"] == 3_000_311_080 assert estimate["messageAllocations"] == message_allocations assert estimate["message_allocations"] == message_allocations @@ -866,7 +866,7 @@ def make_request(method, params): "leaderTimeunitsAllocation": "100", "validatorTimeunitsAllocation": "200", "appealRounds": "0", - "executionBudgetPerRound": "700000", + "executionBudgetPerRound": "100000000", "executionConsumed": "0", "totalMessageFees": "110", "rotations": ["0"], @@ -885,7 +885,7 @@ def make_request(method, params): "feeParams": fee_params, } ], - "feeValue": "711110", + "feeValue": "100011110", }, } } @@ -921,22 +921,22 @@ def make_request(method, params): ) request_params = sim_call.kwargs["params"][0] assert request_params["value"] == hex(7) - assert request_params["fees"]["feeValue"] == 521_110 + assert request_params["fees"]["feeValue"] == 100_021_110 assert request_params["fees"]["distribution"]["totalMessageFees"] == 110 assert request_params["fees"]["messageAllocations"][0]["budget"] == 110 assert ( request_params["fees"]["messageAllocations"][0]["callKey"] == "0x" + "00" * 32 ) - assert estimate["observed"]["recommendedExecutionBudgetPerRound"] == 602_117 + assert estimate["observed"]["recommendedExecutionBudgetPerRound"] == 100_000_000 assert estimate["observed"]["messageFeeBudget"] == 110 assert estimate["observed"]["messageFeeConsumed"] == 50 assert estimate["simulation"]["feeAccounting"]["message_fee_budget"] == "110" assert estimate["simulation"]["feeReport"]["totalEstimatedFee"] == "501664" - assert estimate["distribution"]["executionBudgetPerRound"] == 700_000 + assert estimate["distribution"]["executionBudgetPerRound"] == 100_000_000 assert estimate["distribution"]["totalMessageFees"] == 110 assert estimate["messageAllocations"][0]["budget"] == 110 - assert estimate["feeValue"] == 711_110 + assert estimate["feeValue"] == 100_011_110 def test_estimate_transaction_fees_from_simulation_preserves_mode2_allocations(): From bc04db8999b86979273408511c90c9a89620ff49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Wed, 10 Jun 2026 13:07:26 +0100 Subject: [PATCH 03/19] feat(fees)!: estimation correctness, wait-for-decided semantics, v0.6 parity with genlayer-js (#90) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(fees)!: estimation correctness, wait-for-decided semantics, v0.6 parity with genlayer-js Mirrors genlayer-js feat/v06-fee-estimation-rework (same designer rulings): - effective receipt gas price = max(quoteGasPrice(), eth_gasPrice); zero price on an enabled policy raises instead of producing a zero cap; effective execution budget floor = max(on-chain view, price x 306,192 gas local recompute) - simulation-derived budgets no longer clobbered by the 100M default (floor vs observed x headroom); default constant documented as provisional w/ TODO(data) - wait_until='decided'|'finalized' replaces status (legacy param mapped with one-time DeprecationWarning); new is_successful(tx) helper - enum completeness: status 14 LEADER_REVEALING (fixes a KeyError crash mid-poll), VoteType 3 TIMEOUT / 4 NONDET_DISAGREE, v0.6 ResultType remap - DEPLOY_CALL_KEY = bytes32(1) deploy sentinel; fee revert selector naming in send errors (InsufficientFees/MaxPriceExceeded/ExecutionBudgetExceeded/ BudgetTooLow/RollupBudgetBelowFloor/FeeValueMustBeNonZero) py-specific parity fixes: - asimov chain config gains FeeManager/RoundsStorage/Appeals addresses (parity with js testnetAsimov) - Studio floor-fallback formula unified with js (message-reveal leg included) - appeal_transaction/top_up_and_submit_appeal auto-resolve the minimum bond when value is omitted (previously defaulted to 0 = guaranteed on-chain revert); top_up_fees now requires value * ci: run tests on pushes to dev branches tests.yml had no push trigger at all — merges to v0.19-dev got zero post-merge CI. --- .github/workflows/tests.yml | 3 + genlayer_py/chains/testnet_asimov.py | 105 ++++++- genlayer_py/client/genlayer_client.py | 12 +- genlayer_py/contracts/actions.py | 164 +++++++--- genlayer_py/transactions/__init__.py | 11 + genlayer_py/transactions/actions.py | 118 +++++++- genlayer_py/transactions/fees.py | 24 +- genlayer_py/types/transactions.py | 24 +- tests/unit/contracts/test_contract_actions.py | 279 +++++++++++++++++- .../test_wait_for_transaction_receipt.py | 74 ++++- 10 files changed, 737 insertions(+), 77 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5d389c4..216cc65 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,9 @@ name: Tests on: + push: + branches: + - 'v*-dev' workflow_call: pull_request: types: [opened, reopened, synchronize, ready_for_review] diff --git a/genlayer_py/chains/testnet_asimov.py b/genlayer_py/chains/testnet_asimov.py index 794f294..8984a80 100644 --- a/genlayer_py/chains/testnet_asimov.py +++ b/genlayer_py/chains/testnet_asimov.py @@ -1,4 +1,4 @@ -from genlayer_py.types import GenLayerChain, NativeCurrency +from genlayer_py.types import GenLayerChain, NativeCurrency, SimpleContractInfo from genlayer_py.consensus.abi import CONSENSUS_MAIN_ABI, CONSENSUS_DATA_ABI @@ -17,6 +17,103 @@ "bytecode": "", } +FEE_MANAGER_CONTRACT: SimpleContractInfo = { + "address": "0x21737AA4bea8FF12E202BF1BAB23751A95617533", + "abi": [ + { + "type": "function", + "name": "calculateMinAppealBond", + "stateMutability": "view", + "inputs": [ + {"name": "_txId", "type": "bytes32"}, + {"name": "_round", "type": "uint256"}, + {"name": "_status", "type": "uint8"}, + ], + "outputs": [{"name": "totalFeesToPay", "type": "uint256"}], + }, + ], +} + +ROUNDS_STORAGE_CONTRACT: SimpleContractInfo = { + "address": "0x1F595c0D549DE0812F127508ea1039636CFA62Cc", + "abi": [ + { + "type": "function", + "name": "getRoundNumber", + "stateMutability": "view", + "inputs": [{"name": "txId", "type": "bytes32"}], + "outputs": [{"name": "", "type": "uint256"}], + }, + { + "type": "function", + "name": "getRoundData", + "stateMutability": "view", + "inputs": [ + {"name": "txId", "type": "bytes32"}, + {"name": "round", "type": "uint256"}, + ], + "outputs": [ + { + "name": "", + "type": "tuple", + "components": [ + {"name": "round", "type": "uint256"}, + {"name": "leaderIndex", "type": "uint256"}, + {"name": "votesCommitted", "type": "uint256"}, + {"name": "votesRevealed", "type": "uint256"}, + {"name": "appealBond", "type": "uint256"}, + {"name": "rotationsLeft", "type": "uint256"}, + {"name": "result", "type": "uint8"}, + {"name": "roundValidators", "type": "address[]"}, + {"name": "validatorVotes", "type": "uint8[]"}, + {"name": "validatorVotesHash", "type": "bytes32[]"}, + {"name": "validatorResultHash", "type": "bytes32[]"}, + ], + } + ], + }, + { + "type": "function", + "name": "getLastRoundData", + "stateMutability": "view", + "inputs": [{"name": "txId", "type": "bytes32"}], + "outputs": [ + {"name": "round", "type": "uint256"}, + { + "name": "roundData", + "type": "tuple", + "components": [ + {"name": "round", "type": "uint256"}, + {"name": "leaderIndex", "type": "uint256"}, + {"name": "votesCommitted", "type": "uint256"}, + {"name": "votesRevealed", "type": "uint256"}, + {"name": "appealBond", "type": "uint256"}, + {"name": "rotationsLeft", "type": "uint256"}, + {"name": "result", "type": "uint8"}, + {"name": "roundValidators", "type": "address[]"}, + {"name": "validatorVotes", "type": "uint8[]"}, + {"name": "validatorVotesHash", "type": "bytes32[]"}, + {"name": "validatorResultHash", "type": "bytes32[]"}, + ], + }, + ], + }, + ], +} + +APPEALS_CONTRACT: SimpleContractInfo = { + "address": "0x0F739Dd8f5322b9547c7d19a9621BC2ac8DF4089", + "abi": [ + { + "type": "function", + "name": "canAppeal", + "stateMutability": "view", + "inputs": [{"name": "_txId", "type": "bytes32"}], + "outputs": [{"name": "", "type": "bool"}], + }, + ], +} + testnet_asimov: GenLayerChain = GenLayerChain( id=4221, @@ -34,9 +131,9 @@ testnet=True, consensus_main_contract=CONSENSUS_MAIN_CONTRACT, consensus_data_contract=CONSENSUS_DATA_CONTRACT, - fee_manager_contract=None, - rounds_storage_contract=None, - appeals_contract=None, + fee_manager_contract=FEE_MANAGER_CONTRACT, + rounds_storage_contract=ROUNDS_STORAGE_CONTRACT, + appeals_contract=APPEALS_CONTRACT, staking_contract=None, default_number_of_initial_validators=5, default_consensus_max_rotations=3, diff --git a/genlayer_py/client/genlayer_client.py b/genlayer_py/client/genlayer_client.py index aae6275..8445c83 100644 --- a/genlayer_py/client/genlayer_client.py +++ b/genlayer_py/client/genlayer_client.py @@ -4,7 +4,7 @@ from eth_typing import Address, ChecksumAddress, HexStr from eth_account.signers.local import LocalAccount from hexbytes import HexBytes -from typing import AnyStr +from typing import AnyStr, Literal from genlayer_py.types import ( GenLayerChain, TransactionStatus, @@ -302,8 +302,8 @@ def top_up_fees( self, transaction_id: HexStr, distribution: FeesDistributionInput, + value: int, account: Optional[LocalAccount] = None, - value: int = 0, ) -> HexStr: """Deposits additional fee budget for an existing consensus transaction.""" return top_up_fees( @@ -319,7 +319,7 @@ def top_up_and_submit_appeal( transaction_id: HexStr, distribution: FeesDistributionInput, account: Optional[LocalAccount] = None, - value: int = 0, + value: Optional[int] = None, ) -> HexStr: """Deposits appeal fee budget and submits an appeal in one consensus call.""" return top_up_and_submit_appeal( @@ -334,7 +334,8 @@ def top_up_and_submit_appeal( def wait_for_transaction_receipt( self, transaction_hash: _Hash32, - status: TransactionStatus = TransactionStatus.ACCEPTED, + status: Optional[TransactionStatus] = None, + wait_until: Optional[Literal["decided", "finalized"]] = None, interval: int = transaction_config.wait_interval, retries: int = transaction_config.retries, full_transaction: bool = False, @@ -344,6 +345,7 @@ def wait_for_transaction_receipt( self=self, transaction_hash=transaction_hash, status=status, + wait_until=wait_until, interval=interval, retries=retries, full_transaction=full_transaction, @@ -375,7 +377,7 @@ def appeal_transaction( self, transaction_id: HexStr, account: Optional[LocalAccount] = None, - value: int = 0, + value: Optional[int] = None, ): """Appeals a consensus transaction to trigger a new round of validation. Returns the original transaction_id (appeals operate on the same tx).""" diff --git a/genlayer_py/contracts/actions.py b/genlayer_py/contracts/actions.py index 3f27908..b40f9c6 100644 --- a/genlayer_py/contracts/actions.py +++ b/genlayer_py/contracts/actions.py @@ -24,6 +24,13 @@ FeesDistributionInput, FeesDistribution, FEES_DISTRIBUTION_ABI_TYPE, + DEFAULT_BOOTLOADER_OVERHEAD, + DEFAULT_CALLDATA_GAS_PER_BYTE, + DEFAULT_FIXED_PROPOSE_RECEIPT_GAS, + DEFAULT_GAS_PER_CHANGED_SLOT, + DEFAULT_INTRINSIC_GAS, + DEFAULT_RECEIPT_SLOTS_CHANGED, + MIN_RECEIPT_BYTES, NormalizedTransactionFees, SimulationFeeEstimateOptions, TransactionFeeEstimate, @@ -214,7 +221,7 @@ def appeal_transaction( self: GenLayerClient, transaction_id: HexStr, account: Optional[LocalAccount] = None, - value: int = 0, + value: Optional[int] = None, ) -> HexStr: """Appeals a consensus transaction. Returns the original transaction_id. @@ -226,6 +233,7 @@ def appeal_transaction( raise GenLayerError("No account set.") if self.chain.consensus_main_contract is None: raise GenLayerError("Consensus main contract not configured.") + resolved_value = _resolve_appeal_value(self, transaction_id, value) encoded_data = _encode_submit_appeal_data(self=self, transaction_id=transaction_id) @@ -233,7 +241,7 @@ def appeal_transaction( self=self, encoded_data=encoded_data, sender_account=sender_account, - value=value, + value=resolved_value, operation_name="Appeal", ) @@ -244,8 +252,8 @@ def top_up_fees( self: GenLayerClient, transaction_id: HexStr, distribution: FeesDistributionInput, + value: int, account: Optional[LocalAccount] = None, - value: int = 0, ) -> HexStr: """Deposits additional fee budget for an existing consensus transaction. @@ -273,13 +281,14 @@ def top_up_and_submit_appeal( transaction_id: HexStr, distribution: FeesDistributionInput, account: Optional[LocalAccount] = None, - value: int = 0, + value: Optional[int] = None, ) -> HexStr: """Deposits appeal fee budget and submits an appeal in one consensus call. Returns the original GenLayer transaction id, matching appeal_transaction. """ sender_account = account if account is not None else self.local_account + resolved_value = _resolve_appeal_value(self, transaction_id, value) encoded_data = _encode_fee_management_data( self=self, function_name="topUpAndSubmitAppeal", @@ -290,7 +299,7 @@ def top_up_and_submit_appeal( self=self, encoded_data=encoded_data, sender_account=sender_account, - value=value, + value=resolved_value, operation_name="Top up and submit appeal", ) return transaction_id @@ -377,6 +386,20 @@ def get_min_appeal_bond( return fee_contract.functions.calculateMinAppealBond(tx_bytes, round_number, tx_status).call() +def _resolve_appeal_value( + self: GenLayerClient, + transaction_id: HexStr, + value: Optional[int], +) -> int: + if value is not None: + return value + if self.chain.fee_manager_contract is None or self.chain.rounds_storage_contract is None: + raise GenLayerError( + "Cannot auto-resolve appeal bond: fee_manager_contract/rounds_storage_contract not configured for this chain." + ) + return get_min_appeal_bond(self, transaction_id) + + def _to_bytes32(self: GenLayerClient, hex_str: HexStr) -> bytes: """Convert a hex string to bytes32.""" if hex_str.startswith("0x"): @@ -597,20 +620,37 @@ def get_current_fee_policy(self: GenLayerClient) -> FeePolicyQuote: ) gen_per_time_unit = fee_manager_contract.functions.GENPerTimeUnit().call() storage_unit_price = fee_manager_contract.functions.storageUnitPrice().call() - receipt_gas_price = fee_manager_contract.functions.quoteGasPrice().call() + quoted_receipt_gas_price = fee_manager_contract.functions.quoteGasPrice().call() execution_budget_floor = ( fee_manager_contract.functions.messageFeeParamsBudgetFloor().call() ) + enabled = ( + gen_per_time_unit > 0 + or storage_unit_price > 0 + or quoted_receipt_gas_price > 0 + ) + network_receipt_gas_price = self.w3.eth.gas_price if enabled else 0 + receipt_gas_price = max(quoted_receipt_gas_price, network_receipt_gas_price) + if enabled and receipt_gas_price == 0: + raise GenLayerError( + "receipt gas price quoted as zero; refusing to build a zero price cap" + ) + local_execution_budget_floor = receipt_gas_price * ( + DEFAULT_FIXED_PROPOSE_RECEIPT_GAS + + DEFAULT_INTRINSIC_GAS + + DEFAULT_BOOTLOADER_OVERHEAD + + (MIN_RECEIPT_BYTES * DEFAULT_CALLDATA_GAS_PER_BYTE) + + (DEFAULT_RECEIPT_SLOTS_CHANGED * DEFAULT_GAS_PER_CHANGED_SLOT) + ) return { - "enabled": ( - gen_per_time_unit > 0 - or storage_unit_price > 0 - or receipt_gas_price > 0 - ), + "enabled": enabled, "genPerTimeUnit": gen_per_time_unit, "storageUnitPrice": storage_unit_price, "receiptGasPrice": receipt_gas_price, - "executionBudgetFloor": execution_budget_floor, + "executionBudgetFloor": max( + execution_budget_floor, + local_execution_budget_floor, + ), } try: @@ -920,6 +960,46 @@ def _prepare_transaction( return transaction +KNOWN_REVERT_SELECTOR_NAMES = { + "0x8d53e553": "InsufficientFees", + "0xb4132db3": "MaxPriceExceeded", + "0x57df8523": "ExecutionBudgetExceeded", + "0x305e533c": "BudgetTooLow", + "0xa70732ee": "RollupBudgetBelowFloor", + "0x632be5a1": "FeeValueMustBeNonZero", +} + + +def _format_rpc_error(error: Exception) -> str: + parts = [str(error)] + for attr in ("message", "data"): + value = getattr(error, attr, None) + if isinstance(value, str) and value.strip(): + parts.append(value) + args = getattr(error, "args", ()) + for arg in args: + if isinstance(arg, dict): + for key in ("message", "data", "details", "shortMessage"): + value = arg.get(key) + if isinstance(value, str) and value.strip(): + parts.append(value) + elif isinstance(arg, str) and arg.strip(): + parts.append(arg) + + text = " ".join(dict.fromkeys(parts)) + selector_name = next( + ( + name + for selector, name in KNOWN_REVERT_SELECTOR_NAMES.items() + if selector in text + ), + None, + ) + if selector_name and selector_name not in text: + return f"{text} ({selector_name})" + return text + + def _send_consensus_call( self: GenLayerClient, encoded_data: HexStr, @@ -934,18 +1014,21 @@ def _send_consensus_call( if self.chain.consensus_main_contract is None: raise GenLayerError("Consensus main contract not configured.") - transaction = _prepare_transaction( - self=self, - sender=sender_account.address, - recipient=self.chain.consensus_main_contract["address"], - data=encoded_data, - value=value, - ) - signed_transaction = sender_account.sign_transaction(transaction) - serialized_transaction = self.w3.to_hex(signed_transaction.raw_transaction) - tx_hash = self.provider.make_request( - method="eth_sendRawTransaction", params=[serialized_transaction] - )["result"] + try: + transaction = _prepare_transaction( + self=self, + sender=sender_account.address, + recipient=self.chain.consensus_main_contract["address"], + data=encoded_data, + value=value, + ) + signed_transaction = sender_account.sign_transaction(transaction) + serialized_transaction = self.w3.to_hex(signed_transaction.raw_transaction) + tx_hash = self.provider.make_request( + method="eth_sendRawTransaction", params=[serialized_transaction] + )["result"] + except Exception as exc: + raise GenLayerError(f"{operation_name} failed: {_format_rpc_error(exc)}") from exc if self.chain.id == localnet.id: return tx_hash @@ -974,21 +1057,24 @@ def _send_transaction( f"Consensus main contract address not found in chain config for \"{self.chain.name}\".", ) - transaction = _prepare_transaction( - self=self, - sender=sender_account.address, - recipient=self.chain.consensus_main_contract["address"], - data=encoded_data, - value=value, - ) - signed_transaction = sender_account.sign_transaction(transaction) - serialized_transaction = self.w3.to_hex(signed_transaction.raw_transaction) - params = [serialized_transaction] - if sim_config is not None: - params.append(sim_config) - tx_hash = self.provider.make_request( - method="eth_sendRawTransaction", params=params - )["result"] + try: + transaction = _prepare_transaction( + self=self, + sender=sender_account.address, + recipient=self.chain.consensus_main_contract["address"], + data=encoded_data, + value=value, + ) + signed_transaction = sender_account.sign_transaction(transaction) + serialized_transaction = self.w3.to_hex(signed_transaction.raw_transaction) + params = [serialized_transaction] + if sim_config is not None: + params.append(sim_config) + tx_hash = self.provider.make_request( + method="eth_sendRawTransaction", params=params + )["result"] + except Exception as exc: + raise GenLayerError(f"Transaction failed: {_format_rpc_error(exc)}") from exc tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash) if tx_receipt.status != 1: diff --git a/genlayer_py/transactions/__init__.py b/genlayer_py/transactions/__init__.py index 3674413..ee9b531 100644 --- a/genlayer_py/transactions/__init__.py +++ b/genlayer_py/transactions/__init__.py @@ -2,6 +2,7 @@ CALL_KEY_DEPLOY, CALL_KEY_UNNAMED, CALL_KEY_WILDCARD, + DEPLOY_CALL_KEY, MESSAGE_ALLOCATION_ROOT_PARENT_INDEX, DEFAULT_FEES_DISTRIBUTION, MessageType, @@ -9,16 +10,24 @@ calculate_local_round_fees, create_fees_distribution, derive_external_message_call_key, + deploy_call_key, derive_internal_message_call_key, encode_external_message_fee_params, encode_internal_message_fee_params, extract_studio_fee_policy, ) + +def is_successful(transaction): + from .actions import is_successful as _is_successful + + return _is_successful(transaction) + __all__ = [ "CALL_KEY_DEPLOY", "CALL_KEY_UNNAMED", "CALL_KEY_WILDCARD", + "DEPLOY_CALL_KEY", "MESSAGE_ALLOCATION_ROOT_PARENT_INDEX", "DEFAULT_FEES_DISTRIBUTION", "MessageType", @@ -26,8 +35,10 @@ "calculate_local_round_fees", "create_fees_distribution", "derive_external_message_call_key", + "deploy_call_key", "derive_internal_message_call_key", "encode_external_message_fee_params", "encode_internal_message_fee_params", "extract_studio_fee_policy", + "is_successful", ] diff --git a/genlayer_py/transactions/actions.py b/genlayer_py/transactions/actions.py index 3da21f1..e4e3825 100644 --- a/genlayer_py/transactions/actions.py +++ b/genlayer_py/transactions/actions.py @@ -2,7 +2,8 @@ from genlayer_py.logging import logger import json -from typing import Any, Dict, List +import warnings +from typing import Any, Dict, List, Literal, Optional from web3 import Web3 from web3.types import _Hash32 from eth_typing import HexStr @@ -11,6 +12,8 @@ from genlayer_py.config import transaction_config from genlayer_py.types import ( TransactionStatus, + ExecutionResult, + EXECUTION_RESULT_NUMBER_TO_NAME, TRANSACTION_STATUS_NAME_TO_NUMBER, TRANSACTION_STATUS_NUMBER_TO_NAME, is_decided_state, @@ -66,40 +69,129 @@ from genlayer_py.client import GenLayerClient +_did_warn_wait_for_transaction_receipt_status = False + + +def _warn_deprecated_receipt_status() -> None: + global _did_warn_wait_for_transaction_receipt_status + if _did_warn_wait_for_transaction_receipt_status: + return + _did_warn_wait_for_transaction_receipt_status = True + warnings.warn( + "wait_for_transaction_receipt(status=...) is deprecated; use " + "wait_until='decided' or wait_until='finalized' instead.", + DeprecationWarning, + stacklevel=3, + ) + + +def _resolve_wait_target( + status: Optional[TransactionStatus], + wait_until: Optional[Literal["decided", "finalized"]], +) -> dict: + if wait_until is not None: + if wait_until not in ("decided", "finalized"): + raise ValueError("wait_until must be 'decided' or 'finalized'.") + return {"wait_until": wait_until, "legacy_status": None, "label": wait_until} + if status is None: + return {"wait_until": "decided", "legacy_status": None, "label": "decided"} + + _warn_deprecated_receipt_status() + if status == TransactionStatus.ACCEPTED: + return {"wait_until": "decided", "legacy_status": None, "label": "decided"} + if status == TransactionStatus.FINALIZED: + return {"wait_until": "finalized", "legacy_status": None, "label": "finalized"} + return {"wait_until": None, "legacy_status": status, "label": status.value} + + +def _has_reached_wait_target(transaction_status: str, target: dict) -> bool: + if target["wait_until"] == "decided": + return is_decided_state(transaction_status) + if target["wait_until"] == "finalized": + return transaction_status == TRANSACTION_STATUS_NAME_TO_NUMBER[ + TransactionStatus.FINALIZED + ] + legacy_status = target["legacy_status"] + return ( + legacy_status is not None + and transaction_status == TRANSACTION_STATUS_NAME_TO_NUMBER[legacy_status] + ) + + +def _normalize_status_name(value: Any) -> Optional[TransactionStatus]: + if isinstance(value, TransactionStatus): + return value + if isinstance(value, str): + if value in TransactionStatus._value2member_map_: + return TransactionStatus(value) + return TRANSACTION_STATUS_NUMBER_TO_NAME.get(value) + if value is None: + return None + return TRANSACTION_STATUS_NUMBER_TO_NAME.get(str(value)) + + +def _normalize_execution_result_name(value: Any) -> Optional[ExecutionResult]: + if isinstance(value, ExecutionResult): + return value + if isinstance(value, str) and value in ExecutionResult._value2member_map_: + return ExecutionResult(value) + if value is None: + return None + return EXECUTION_RESULT_NUMBER_TO_NAME.get(str(value)) + + +def is_successful(transaction: GenLayerTransaction) -> bool: + status_name = _normalize_status_name( + transaction.get("status_name", transaction.get("status")) + ) + execution_result_name = _normalize_execution_result_name( + transaction.get( + "tx_execution_result_name", + transaction.get("tx_execution_result"), + ) + ) + return ( + status_name in (TransactionStatus.ACCEPTED, TransactionStatus.FINALIZED) + and execution_result_name == ExecutionResult.FINISHED_WITH_RETURN + ) + + def wait_for_transaction_receipt( self: GenLayerClient, transaction_hash: _Hash32, - status: TransactionStatus = TransactionStatus.ACCEPTED, + status: Optional[TransactionStatus] = None, + wait_until: Optional[Literal["decided", "finalized"]] = None, interval: int = transaction_config.wait_interval, retries: int = transaction_config.retries, full_transaction: bool = False, ) -> GenLayerTransaction: attempts = 0 + target = _resolve_wait_target(status, wait_until) + transaction = None + last_status = None while attempts < retries: transaction = self.get_transaction(transaction_hash=transaction_hash) if transaction is None: raise GenLayerError(f"Transaction {transaction_hash} not found") transaction_status = str(transaction["status"]) - last_status = TRANSACTION_STATUS_NUMBER_TO_NAME[transaction_status] - finalized_status = TRANSACTION_STATUS_NAME_TO_NUMBER[ - TransactionStatus.FINALIZED - ] - requested_status = TRANSACTION_STATUS_NAME_TO_NUMBER[status] + last_status = TRANSACTION_STATUS_NUMBER_TO_NAME.get(transaction_status) - if transaction_status == requested_status or ( - status == TransactionStatus.ACCEPTED - and is_decided_state(transaction_status) - ): + if _has_reached_wait_target(transaction_status, target): if not full_transaction: return _simplify_transaction_receipt(transaction) return transaction time.sleep(interval / 1000) attempts += 1 + last_status_label = ( + last_status.value + if isinstance(last_status, TransactionStatus) + else str(transaction["status"]) if transaction else "" + ) raise GenLayerError( - f"Transaction {transaction_hash} did not reach desired status '{status.value}' after {retries} attempts " + f"Transaction {transaction_hash} did not reach desired status '{target['label']}' after {retries} attempts " f"(polling every {interval}ms for a total of {retries * interval / 1000:.1f}s). " - f"Last observed status: '{last_status.value}'. " + f"Last observed status: '{last_status_label}'. " f"This may indicate the transaction is still processing, or the network is experiencing delays. " f"Consider increasing 'retries' or 'interval' parameters.\n" f"Transaction object simplified: {json.dumps(_simplify_transaction_receipt(transaction), indent=2, default=str)}" diff --git a/genlayer_py/transactions/fees.py b/genlayer_py/transactions/fees.py index 9916866..d67b639 100644 --- a/genlayer_py/transactions/fees.py +++ b/genlayer_py/transactions/fees.py @@ -156,7 +156,8 @@ class NormalizedTransactionFees(TypedDict): MESSAGE_ALLOCATION_ROOT_PARENT_INDEX = (1 << 256) - 1 CALL_KEY_WILDCARD = b"\x00" * 32 CALL_KEY_UNNAMED = HexStr("0x" + ("0" * 64)) -CALL_KEY_DEPLOY = CALL_KEY_UNNAMED +DEPLOY_CALL_KEY = HexStr("0x" + ("0" * 62) + "01") +CALL_KEY_DEPLOY = DEPLOY_CALL_KEY FEES_DISTRIBUTION_ABI_TYPE = ( "(uint256,uint256,uint256,uint256,uint256,uint256,uint256[],uint256,uint256,uint256)" @@ -194,6 +195,8 @@ class NormalizedTransactionFees(TypedDict): DEFAULT_LEADER_TIMEUNITS_ALLOCATION = 100 DEFAULT_VALIDATOR_TIMEUNITS_ALLOCATION = 200 DEFAULT_TRANSACTION_EXECUTION_BUDGET_PER_ROUND = 500_000 +# Provisional heuristic sized ~20x observed dev-env consumption (~5M gas-equivalent). +# TODO(data): replace with telemetry-derived default (p99 x margin) once fee consumption telemetry is collected. DEFAULT_TRANSACTION_EXECUTION_GAS = 100_000_000 DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM = 10_000 MIN_RECEIPT_BYTES = 512 @@ -201,8 +204,11 @@ class NormalizedTransactionFees(TypedDict): DEFAULT_INTRINSIC_GAS = 21_000 DEFAULT_BOOTLOADER_OVERHEAD = 60_000 DEFAULT_FIXED_PROPOSE_RECEIPT_GAS = 210_000 +DEFAULT_FIXED_MESSAGE_REVEAL_GAS = 100_000 DEFAULT_GAS_PER_CHANGED_SLOT = 1_000 DEFAULT_CALLDATA_GAS_PER_BYTE = 16 +DEFAULT_MESSAGE_REVEAL_LENGTH_SLOTS = 32 +DEFAULT_NONDET_OUTPUT_LENGTH_BYTES = 32 ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" VALIDATORS_PER_ROUND = [ @@ -308,6 +314,10 @@ def derive_external_message_call_key( return _bytes_to_padded_call_key(calldata[:4]) +def deploy_call_key() -> HexStr: + return DEPLOY_CALL_KEY + + def _normalize_message_type(value: Union[MessageType, int, str]) -> int: if isinstance(value, MessageType): return int(value) @@ -658,6 +668,10 @@ def extract_studio_fee_policy(config: Any) -> FeePolicyQuote: policy.get("fixedProposeReceiptGas", DEFAULT_FIXED_PROPOSE_RECEIPT_GAS), "policy.fixedProposeReceiptGas", ) + fixed_message_reveal_gas = _int_from_unknown( + policy.get("fixedMessageRevealGas", DEFAULT_FIXED_MESSAGE_REVEAL_GAS), + "policy.fixedMessageRevealGas", + ) explicit_budget_floor = policy.get( "messageFeeParamsBudgetFloor", config.get("messageFeeParamsBudgetFloor"), @@ -673,8 +687,12 @@ def extract_studio_fee_policy(config: Any) -> FeePolicyQuote: fixed_propose_receipt_gas + intrinsic_gas + bootloader_overhead - + (MIN_RECEIPT_BYTES * calldata_gas_per_byte) + (DEFAULT_RECEIPT_SLOTS_CHANGED * gas_per_changed_slot) + + fixed_message_reveal_gas + + intrinsic_gas + + bootloader_overhead + + (DEFAULT_MESSAGE_REVEAL_LENGTH_SLOTS * gas_per_changed_slot) + + (DEFAULT_NONDET_OUTPUT_LENGTH_BYTES * calldata_gas_per_byte) ) ) @@ -860,7 +878,7 @@ def observed_simulation_fee_usage( observed_execution_budget = execution_fee_consumed + execution_fee_report_total recommended_execution_budget = ( max( - _default_execution_budget_per_round(policy), + policy["executionBudgetFloor"], _with_cap_headroom(observed_execution_budget, execution_headroom_bps), ) if observed_execution_budget > 0 diff --git a/genlayer_py/types/transactions.py b/genlayer_py/types/transactions.py index f73a85a..89a25fe 100644 --- a/genlayer_py/types/transactions.py +++ b/genlayer_py/types/transactions.py @@ -27,6 +27,7 @@ class TransactionStatus(str, Enum): READY_TO_FINALIZE = "READY_TO_FINALIZE" VALIDATORS_TIMEOUT = "VALIDATORS_TIMEOUT" LEADER_TIMEOUT = "LEADER_TIMEOUT" + LEADER_REVEALING = "LEADER_REVEALING" TRANSACTION_STATUS_NUMBER_TO_NAME = { @@ -44,6 +45,7 @@ class TransactionStatus(str, Enum): "11": TransactionStatus.READY_TO_FINALIZE, "12": TransactionStatus.VALIDATORS_TIMEOUT, "13": TransactionStatus.LEADER_TIMEOUT, + "14": TransactionStatus.LEADER_REVEALING, } TRANSACTION_STATUS_NAME_TO_NUMBER = { @@ -61,6 +63,7 @@ class TransactionStatus(str, Enum): TransactionStatus.READY_TO_FINALIZE: "11", TransactionStatus.VALIDATORS_TIMEOUT: "12", TransactionStatus.LEADER_TIMEOUT: "13", + TransactionStatus.LEADER_REVEALING: "14", } DECIDED_STATES = [ @@ -86,29 +89,26 @@ class TransactionResult(str, Enum): NO_MAJORITY = "NO_MAJORITY" MAJORITY_AGREE = "MAJORITY_AGREE" MAJORITY_DISAGREE = "MAJORITY_DISAGREE" + MAJORITY_TIMEOUT = "MAJORITY_TIMEOUT" TRANSACTION_RESULT_NUMBER_TO_NAME = { "0": TransactionResult.IDLE, - "1": TransactionResult.AGREE, - "2": TransactionResult.DISAGREE, - "3": TransactionResult.TIMEOUT, + "1": TransactionResult.MAJORITY_AGREE, + "2": TransactionResult.MAJORITY_DISAGREE, + "3": TransactionResult.MAJORITY_TIMEOUT, "4": TransactionResult.DETERMINISTIC_VIOLATION, "5": TransactionResult.NO_MAJORITY, - "6": TransactionResult.MAJORITY_AGREE, - "7": TransactionResult.MAJORITY_DISAGREE, } TRANSACTION_RESULT_NAME_TO_NUMBER = { TransactionResult.IDLE: "0", - TransactionResult.AGREE: "1", - TransactionResult.DISAGREE: "2", - TransactionResult.TIMEOUT: "3", + TransactionResult.MAJORITY_AGREE: "1", + TransactionResult.MAJORITY_DISAGREE: "2", + TransactionResult.MAJORITY_TIMEOUT: "3", TransactionResult.DETERMINISTIC_VIOLATION: "4", TransactionResult.NO_MAJORITY: "5", - TransactionResult.MAJORITY_AGREE: "6", - TransactionResult.MAJORITY_DISAGREE: "7", } @@ -117,12 +117,16 @@ class ExecutionResult(str, Enum): NOT_VOTED = "NOT_VOTED" FINISHED_WITH_RETURN = "FINISHED_WITH_RETURN" FINISHED_WITH_ERROR = "FINISHED_WITH_ERROR" + TIMEOUT = "TIMEOUT" + NONDET_DISAGREE = "NONDET_DISAGREE" EXECUTION_RESULT_NUMBER_TO_NAME = { "0": ExecutionResult.NOT_VOTED, "1": ExecutionResult.FINISHED_WITH_RETURN, "2": ExecutionResult.FINISHED_WITH_ERROR, + "3": ExecutionResult.TIMEOUT, + "4": ExecutionResult.NONDET_DISAGREE, } diff --git a/tests/unit/contracts/test_contract_actions.py b/tests/unit/contracts/test_contract_actions.py index 7887bed..5a9e298 100644 --- a/tests/unit/contracts/test_contract_actions.py +++ b/tests/unit/contracts/test_contract_actions.py @@ -2,17 +2,21 @@ from unittest.mock import Mock import eth_utils +import pytest from eth_abi import decode as abi_decode from web3 import Web3 import genlayer_py.contracts.actions as contract_actions from genlayer_py.chains import localnet +from genlayer_py.chains.testnet_asimov import testnet_asimov +from genlayer_py.exceptions import GenLayerError from genlayer_py.transactions.fees import ( ADD_TRANSACTION_WITH_FEES_ARGUMENT_TYPES, ADD_TRANSACTION_WITH_FEES_SELECTOR, CALL_KEY_DEPLOY, CALL_KEY_UNNAMED, CALL_KEY_WILDCARD, + DEPLOY_CALL_KEY, FEES_DISTRIBUTION_ABI_TYPE, MESSAGE_ALLOCATION_ROOT_PARENT_INDEX, MessageType, @@ -21,6 +25,7 @@ create_fees_distribution, derive_external_message_call_key, derive_internal_message_call_key, + deploy_call_key, encode_external_message_fee_params, encode_internal_message_fee_params, extract_studio_fee_policy, @@ -29,6 +34,7 @@ DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM = 10_000 +LOCAL_EXECUTION_BUDGET_FLOOR_GAS = 306_192 ADD_TRANSACTION_ABI_V5 = [ @@ -155,7 +161,9 @@ def test_derive_internal_message_call_key_hashes_exact_32_byte_method_name(): def test_derive_message_call_key_constants_for_deploy_and_unnamed(): - assert CALL_KEY_DEPLOY == "0x" + "00" * 32 + assert DEPLOY_CALL_KEY == "0x" + "00" * 31 + "01" + assert CALL_KEY_DEPLOY == DEPLOY_CALL_KEY + assert deploy_call_key() == DEPLOY_CALL_KEY assert CALL_KEY_UNNAMED == "0x" + "00" * 32 assert derive_internal_message_call_key("") == CALL_KEY_UNNAMED @@ -275,6 +283,87 @@ def fake_send_consensus_call(**kwargs): assert captured["encoded_data"].startswith(f"0x{selector}") +def test_appeal_transaction_auto_resolves_min_bond(monkeypatch): + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES) + client.chain.fee_manager_contract = {"address": "0x4444444444444444444444444444444444444444"} + client.chain.rounds_storage_contract = {"address": "0x5555555555555555555555555555555555555555"} + captured = {} + + monkeypatch.setattr( + contract_actions, + "get_min_appeal_bond", + Mock(return_value=4_321), + ) + monkeypatch.setattr( + contract_actions, + "_encode_submit_appeal_data", + Mock(return_value="0x1234"), + ) + + def fake_send_consensus_call(**kwargs): + captured.update(kwargs) + return "0xevmtx" + + monkeypatch.setattr( + contract_actions, + "_send_consensus_call", + fake_send_consensus_call, + ) + + result = contract_actions.appeal_transaction( + self=client, + transaction_id=TX_ID, + ) + + assert result == TX_ID + contract_actions.get_min_appeal_bond.assert_called_once_with(client, TX_ID) + assert captured["value"] == 4_321 + assert captured["operation_name"] == "Appeal" + + +def test_top_up_and_submit_appeal_auto_resolves_min_bond(monkeypatch): + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES) + client.chain.fee_manager_contract = {"address": "0x4444444444444444444444444444444444444444"} + client.chain.rounds_storage_contract = {"address": "0x5555555555555555555555555555555555555555"} + captured = {} + + monkeypatch.setattr( + contract_actions, + "get_min_appeal_bond", + Mock(return_value=9_999), + ) + + def fake_send_consensus_call(**kwargs): + captured.update(kwargs) + return "0xevmtx" + + monkeypatch.setattr( + contract_actions, + "_send_consensus_call", + fake_send_consensus_call, + ) + + result = contract_actions.top_up_and_submit_appeal( + self=client, + transaction_id=TX_ID, + distribution={"appealRounds": 1, "rotations": [0, 1]}, + ) + + assert result == TX_ID + assert captured["value"] == 9_999 + + +def test_appeal_auto_bond_requires_fee_contracts(): + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES) + client.chain.fee_manager_contract = None + + with pytest.raises(GenLayerError, match="Cannot auto-resolve appeal bond"): + contract_actions.appeal_transaction( + self=client, + transaction_id=TX_ID, + ) + + def test_send_consensus_call_returns_localnet_rpc_hash_without_waiting(monkeypatch): wait_for_transaction_receipt = Mock() sign_transaction = Mock( @@ -315,6 +404,14 @@ def test_send_consensus_call_returns_localnet_rpc_hash_without_waiting(monkeypat wait_for_transaction_receipt.assert_not_called() +def test_revert_selector_formatter_names_fee_errors(): + error = Exception({"data": "0x632be5a1"}) + + assert contract_actions._format_rpc_error(error).endswith( + "(FeeValueMustBeNonZero)" + ) + + def _make_client(add_transaction_abi): chain = SimpleNamespace( id=61999, @@ -333,6 +430,113 @@ def _make_client(add_transaction_abi): ) +class _FakeContractFunction: + def __init__(self, value): + self.value = value + + def call(self): + return self.value + + +class _FakeFeeManagerFunctions: + def __init__(self, values): + self.values = values + + def GENPerTimeUnit(self): + return _FakeContractFunction(self.values["gen"]) + + def storageUnitPrice(self): + return _FakeContractFunction(self.values["storage"]) + + def quoteGasPrice(self): + return _FakeContractFunction(self.values["quote"]) + + def messageFeeParamsBudgetFloor(self): + return _FakeContractFunction(self.values["floor"]) + + +class _FakeEth: + def __init__(self, values, gas_price): + self.values = values + self._gas_price = gas_price + self.gas_price_accesses = 0 + + def contract(self, address=None, abi=None): + return SimpleNamespace(functions=_FakeFeeManagerFunctions(self.values)) + + @property + def gas_price(self): + self.gas_price_accesses += 1 + if isinstance(self._gas_price, Exception): + raise self._gas_price + return self._gas_price + + +def _make_fee_policy_client(values, gas_price=0): + eth = _FakeEth(values, gas_price) + return SimpleNamespace( + chain=SimpleNamespace( + fee_manager_contract={"address": "0x4444444444444444444444444444444444444444"}, + ), + w3=SimpleNamespace( + eth=eth, + to_checksum_address=Mock(side_effect=lambda address: address), + ), + ) + + +def test_get_current_fee_policy_uses_effective_receipt_gas_price_and_local_floor(): + client = _make_fee_policy_client( + {"gen": 0, "storage": 0, "quote": 1, "floor": 0}, + gas_price=2, + ) + + policy = contract_actions.get_current_fee_policy(client) + + assert policy["enabled"] is True + assert policy["receiptGasPrice"] == 2 + assert policy["executionBudgetFloor"] == 2 * LOCAL_EXECUTION_BUDGET_FLOOR_GAS + assert client.w3.eth.gas_price_accesses == 1 + + +def test_get_current_fee_policy_refuses_enabled_zero_receipt_price(): + client = _make_fee_policy_client( + {"gen": 1, "storage": 0, "quote": 0, "floor": 0}, + gas_price=0, + ) + + with pytest.raises( + GenLayerError, + match="receipt gas price quoted as zero; refusing to build a zero price cap", + ): + contract_actions.get_current_fee_policy(client) + + +def test_get_current_fee_policy_does_not_read_network_gas_price_when_disabled(): + client = _make_fee_policy_client( + {"gen": 0, "storage": 0, "quote": 0, "floor": 0}, + gas_price=AssertionError("eth_gasPrice should not be called"), + ) + + policy = contract_actions.get_current_fee_policy(client) + + assert policy["enabled"] is False + assert policy["receiptGasPrice"] == 0 + assert client.w3.eth.gas_price_accesses == 0 + + +def test_asimov_chain_has_fee_contracts(): + assert testnet_asimov.fee_manager_contract["address"] == ( + "0x21737AA4bea8FF12E202BF1BAB23751A95617533" + ) + assert testnet_asimov.rounds_storage_contract["address"] == ( + "0x1F595c0D549DE0812F127508ea1039636CFA62Cc" + ) + assert testnet_asimov.appeals_contract["address"] == ( + "0x0F739Dd8f5322b9547c7d19a9621BC2ac8DF4089" + ) + + def test_simulate_write_contract_passes_fee_policy_and_value_to_sim_call(): make_request = Mock( return_value={ @@ -687,6 +891,31 @@ def test_estimate_transaction_fees_uses_studio_fee_config(): assert estimate["feeValue"] == 3_000_011_000 +def test_extract_studio_fee_policy_fallback_includes_message_reveal_leg(): + policy = extract_studio_fee_policy( + { + "enabled": True, + "policy": { + "genPerTimeUnit": "10", + "storageUnitPrice": "20", + "receiptGasPrice": "30", + }, + } + ) + + assert policy["executionBudgetFloor"] == 30 * ( + 210_000 + + 21_000 + + 60_000 + + 7 * 1_000 + + 100_000 + + 21_000 + + 60_000 + + 32 * 1_000 + + 32 * 16 + ) + + def test_estimate_transaction_fees_derives_message_bucket_from_allocations(): client = SimpleNamespace( chain=SimpleNamespace( @@ -818,6 +1047,52 @@ def test_estimate_transaction_fees_from_simulation_builds_trusted_preset(): assert estimate["feeValue"] == 613_123 +def test_simulation_execution_budget_uses_floor_without_default_gas_clobber(): + client = SimpleNamespace( + chain=SimpleNamespace( + fee_manager_contract=None, + default_number_of_initial_validators=5, + ), + provider=SimpleNamespace( + make_request=Mock( + return_value={ + "result": { + "enabled": True, + "policy": { + "genPerTimeUnit": "10", + "storageUnitPrice": "0", + "receiptGasPrice": "1", + "messageFeeParamsBudgetFloor": str( + LOCAL_EXECUTION_BUDGET_FLOOR_GAS + ), + }, + } + } + ) + ), + ) + + estimate = contract_actions.estimate_transaction_fees_from_simulation( + self=client, + options={ + "simulation": { + "feeAccounting": { + "execution_fee_consumed": "10", + "execution_fee_report": {"totalEstimatedFee": "0"}, + } + }, + "executionHeadroomBps": 10_000, + }, + ) + + assert estimate["observed"]["recommendedExecutionBudgetPerRound"] == ( + LOCAL_EXECUTION_BUDGET_FLOOR_GAS + ) + assert estimate["distribution"]["executionBudgetPerRound"] == ( + LOCAL_EXECUTION_BUDGET_FLOOR_GAS + ) + + def test_estimate_transaction_fees_for_write_uses_studio_estimate_rpc(): fee_params = encode_internal_message_fee_params( { @@ -928,7 +1203,7 @@ def make_request(method, params): request_params["fees"]["messageAllocations"][0]["callKey"] == "0x" + "00" * 32 ) - assert estimate["observed"]["recommendedExecutionBudgetPerRound"] == 100_000_000 + assert estimate["observed"]["recommendedExecutionBudgetPerRound"] == 602_117 assert estimate["observed"]["messageFeeBudget"] == 110 assert estimate["observed"]["messageFeeConsumed"] == 50 assert estimate["simulation"]["feeAccounting"]["message_fee_budget"] == "110" diff --git a/tests/unit/transactions/test_wait_for_transaction_receipt.py b/tests/unit/transactions/test_wait_for_transaction_receipt.py index 2a06893..6a3a7f0 100644 --- a/tests/unit/transactions/test_wait_for_transaction_receipt.py +++ b/tests/unit/transactions/test_wait_for_transaction_receipt.py @@ -3,8 +3,15 @@ from genlayer_py.transactions.actions import ( wait_for_transaction_receipt, _simplify_transaction_receipt, + is_successful, +) +from genlayer_py.types import ( + ExecutionResult, + TransactionStatus, + DECIDED_STATES, + TRANSACTION_STATUS_NUMBER_TO_NAME, + is_decided_state, ) -from genlayer_py.types import TransactionStatus, DECIDED_STATES, is_decided_state from genlayer_py.exceptions import GenLayerError @@ -162,6 +169,43 @@ def test_wait_for_accepted_with_all_decided_states(self, mock_client): assert result == mock_transaction + def test_wait_until_decided_resolves_on_undetermined(self, mock_client): + mock_transaction = { + "hash": "0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", + "status": "6", + "status_name": "UNDETERMINED", + } + mock_client.get_transaction.return_value = mock_transaction + + result = wait_for_transaction_receipt( + self=mock_client, + transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", + wait_until="decided", + full_transaction=True, + ) + + assert result == mock_transaction + + def test_status_14_does_not_crash_wait_loop(self, mock_client): + mock_transaction = { + "hash": "0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", + "status": "14", + "status_name": "LEADER_REVEALING", + } + mock_client.get_transaction.return_value = mock_transaction + + with pytest.raises(GenLayerError, match="LEADER_REVEALING"): + wait_for_transaction_receipt( + self=mock_client, + transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", + wait_until="finalized", + retries=1, + interval=1, + full_transaction=True, + ) + + assert TRANSACTION_STATUS_NUMBER_TO_NAME["14"] == TransactionStatus.LEADER_REVEALING + def test_wait_for_specific_status_not_affected(self, mock_client): """Test that waiting for specific non-ACCEPTED statuses is not affected by decided states logic""" mock_transaction = { @@ -227,6 +271,34 @@ def test_is_decided_state_with_invalid_status(self): assert is_decided_state(status) == False, f"Invalid status {status} should not be decided" +class TestIsSuccessful: + def test_truth_table(self): + assert is_successful( + { + "status": "5", + "tx_execution_result": "1", + } + ) + assert not is_successful( + { + "status": "6", + "tx_execution_result": "1", + } + ) + assert not is_successful( + { + "status": TransactionStatus.ACCEPTED, + "tx_execution_result": "2", + } + ) + assert is_successful( + { + "status_name": TransactionStatus.FINALIZED, + "tx_execution_result_name": ExecutionResult.FINISHED_WITH_RETURN, + } + ) + + class TestSimplifyTransactionReceipt: """Test suite for _simplify_transaction_receipt function""" From dd25ef7f43e99a14b8fe42a64e01374845ad4d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Wed, 10 Jun 2026 15:46:12 +0100 Subject: [PATCH 04/19] ci: keep main forwarded to active dev branch (#92) --- .github/scripts/validate-branch-policy.sh | 107 ++++++++++++++++++++++ .github/workflows/branch-policy.yml | 24 +++++ .github/workflows/fast-forward-main.yaml | 57 ++++++++++++ .github/workflows/retarget-main-prs.yaml | 53 +++++++++++ support/ci/ACTIVE_DEV_BRANCH | 1 + 5 files changed, 242 insertions(+) create mode 100755 .github/scripts/validate-branch-policy.sh create mode 100644 .github/workflows/branch-policy.yml create mode 100644 .github/workflows/fast-forward-main.yaml create mode 100644 .github/workflows/retarget-main-prs.yaml create mode 100644 support/ci/ACTIVE_DEV_BRANCH diff --git a/.github/scripts/validate-branch-policy.sh b/.github/scripts/validate-branch-policy.sh new file mode 100755 index 0000000..0fb7ee2 --- /dev/null +++ b/.github/scripts/validate-branch-policy.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +set -euo pipefail + +failed=0 + +error() { + echo "::error::$*" + failed=1 +} + +warning() { + echo "::warning::$*" +} + +active_branch_file="support/ci/ACTIVE_DEV_BRANCH" +if [[ ! -f "${active_branch_file}" ]]; then + error "${active_branch_file} is required." + active_branch="" +else + active_branch="$(tr -d '[:space:]' < "${active_branch_file}")" +fi + +if [[ -z "${active_branch}" ]]; then + error "${active_branch_file} must not be empty." +elif [[ "${active_branch}" == "main" ]]; then + error "${active_branch_file} must point to a dev branch, not main." +elif [[ "${active_branch}" != *-dev ]]; then + warning "${active_branch_file} should normally point to a -dev branch; got ${active_branch}." +fi + +release_branch="${active_branch%-dev}" +default_branch="${GITHUB_DEFAULT_BRANCH:-}" +event_name="${GITHUB_EVENT_NAME:-local}" +base_ref="${GITHUB_BASE_REF:-}" +head_ref="${GITHUB_HEAD_REF:-}" +ref_name="${GITHUB_REF_NAME:-}" +actor="${GITHUB_ACTOR:-}" + +if [[ -n "${default_branch}" && "${default_branch}" != "main" ]]; then + warning "Repository default branch should be main after branch-policy rollout; currently ${default_branch}." +fi + +if [[ -n "${base_ref}" && "${base_ref}" == "main" ]]; then + warning "PR targets main; retarget-main-prs should move it to ${active_branch}." +fi + +if [[ -n "${base_ref}" && -n "${active_branch}" ]]; then + if [[ "${base_ref}" == "${release_branch}" && "${head_ref}" != "${active_branch}" && "${ALLOW_DIRECT_RELEASE_PR:-false}" != "true" ]]; then + error "PRs into ${release_branch} must come from ${active_branch}. Merge feature work into ${active_branch}, then promote ${active_branch} -> ${release_branch}." + fi +fi + +if [[ "${event_name}" == "push" && "${ref_name}" == "main" ]]; then + case "${actor}" in + github-actions[bot]|ci-core-e2e-runner[bot]) + ;; + *) + error "main should only move by automation from ${active_branch}; direct push actor was ${actor:-unknown}." + ;; + esac +fi + +if [[ ! -f ".github/workflows/fast-forward-main.yaml" ]]; then + error ".github/workflows/fast-forward-main.yaml is required." +fi + +if [[ ! -f ".github/workflows/retarget-main-prs.yaml" ]]; then + error ".github/workflows/retarget-main-prs.yaml is required." +fi + +if [[ -f ".github/workflows/release-from-main.yml" ]]; then + error ".github/workflows/release-from-main.yml is forbidden. Releases must be tag/version-branch driven." +fi + +if [[ -f "release.config.js" ]]; then + error "release.config.js is forbidden in versioned tooling branches; semantic-release-on-main must not be restored." +fi + +if [[ -f ".github/workflows/release-from-tag.yml" ]]; then + if ! grep -Fq 'v*.*.*' .github/workflows/release-from-tag.yml; then + error "release-from-tag.yml must trigger only from version tags matching v*.*.*." + fi + if ! grep -Fq 'refs/remotes/origin/${version_branch}' .github/workflows/release-from-tag.yml || \ + ! grep -Fq 'tag_commit' .github/workflows/release-from-tag.yml || \ + ! grep -Fq 'branch_head' .github/workflows/release-from-tag.yml; then + error "release-from-tag.yml must verify the tag commit is the current matching version branch head." + fi +fi + +if [[ -f ".github/workflows/manual-docker-release.yml" ]]; then + if ! grep -Fq 'expected_branch=' .github/workflows/manual-docker-release.yml; then + error "manual-docker-release.yml must derive and enforce the expected version branch from the tag." + fi + if ! grep -Fq './.github/workflows/release-from-tag.yml' .github/workflows/manual-docker-release.yml; then + error "manual-docker-release.yml must delegate image promotion to release-from-tag.yml." + fi +fi + +if [[ "${failed}" -ne 0 ]]; then + exit 1 +fi + +if [[ -n "${base_ref}" ]]; then + echo "Branch policy ok for PR ${head_ref} -> ${base_ref}; active dev branch is ${active_branch}." +else + echo "Branch policy ok for ${event_name} on ${ref_name:-detached ref}; active dev branch is ${active_branch}." +fi diff --git a/.github/workflows/branch-policy.yml b/.github/workflows/branch-policy.yml new file mode 100644 index 0000000..7759870 --- /dev/null +++ b/.github/workflows/branch-policy.yml @@ -0,0 +1,24 @@ +name: Branch Policy + +on: + pull_request: + types: [opened, synchronize, reopened, edited, ready_for_review] + push: + branches: + - "**" + workflow_dispatch: + +permissions: + contents: read + +jobs: + branch-policy: + name: Validate branch policy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Validate branch policy + env: + GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + run: ./.github/scripts/validate-branch-policy.sh diff --git a/.github/workflows/fast-forward-main.yaml b/.github/workflows/fast-forward-main.yaml new file mode 100644 index 0000000..688e997 --- /dev/null +++ b/.github/workflows/fast-forward-main.yaml @@ -0,0 +1,57 @@ +name: Fast-forward main + +# main is the static/default branch for GitHub UX and tools that assume a +# stable default branch. It is not the integration target. On each push to the +# configured active dev branch, fast-forward main to that commit. + +on: + push: + branches: ["**"] + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: fast-forward-main-${{ github.repository }} + cancel-in-progress: false + +defaults: + run: + shell: bash + +jobs: + fast-forward: + if: github.ref_type == 'branch' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Fast-forward main to active dev branch + run: | + set -euo pipefail + + active_branch="$(tr -d '[:space:]' < support/ci/ACTIVE_DEV_BRANCH)" + if [[ -z "${active_branch}" || "${active_branch}" == "main" ]]; then + echo "::error::support/ci/ACTIVE_DEV_BRANCH must name a non-main dev branch" + exit 1 + fi + + if [[ "${GITHUB_REF_NAME}" != "${active_branch}" ]]; then + echo "Push was to ${GITHUB_REF_NAME}; active dev branch is ${active_branch}. Nothing to do." + exit 0 + fi + + if git ls-remote --exit-code --heads origin main >/dev/null 2>&1; then + git fetch origin main + if ! git merge-base --is-ancestor origin/main HEAD; then + echo "::error::main has diverged from ${active_branch}; refusing non-fast-forward update" + exit 1 + fi + else + echo "main does not exist yet; creating it at ${GITHUB_SHA}." + fi + + git push origin "HEAD:refs/heads/main" diff --git a/.github/workflows/retarget-main-prs.yaml b/.github/workflows/retarget-main-prs.yaml new file mode 100644 index 0000000..37a066f --- /dev/null +++ b/.github/workflows/retarget-main-prs.yaml @@ -0,0 +1,53 @@ +name: Retarget main PRs + +# main is a static/default alias of the active dev branch. Contributions should +# target the active dev branch directly; PRs opened against main are retargeted +# automatically so required checks and release-train rules run in the right +# branch context. +# +# pull_request_target is used for the write-scoped token. This workflow never +# checks out or executes PR head code; it reads only trusted base-branch files. + +on: + pull_request_target: + types: [opened, reopened, synchronize, edited, ready_for_review] + +permissions: + contents: read + pull-requests: write + issues: write + +defaults: + run: + shell: bash + +jobs: + retarget: + if: github.event.pull_request.base.ref == 'main' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.base.ref }} + + - name: Retarget PR to active dev branch + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + set -euo pipefail + + active_branch="$(tr -d '[:space:]' < support/ci/ACTIVE_DEV_BRANCH)" + if [[ -z "${active_branch}" || "${active_branch}" == "main" ]]; then + echo "::error::support/ci/ACTIVE_DEV_BRANCH must name a non-main dev branch" + exit 1 + fi + + gh pr edit "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --base "${active_branch}" + + gh pr comment "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --body "$(cat < Date: Thu, 11 Jun 2026 17:09:05 +0100 Subject: [PATCH 05/19] docs: add branching guide (#94) --- CONTRIBUTING.md | 20 +++++----------- docs/BRANCHING.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 docs/BRANCHING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 17b6afe..682319a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,19 +30,11 @@ Have ideas for new features or use cases? We're eager to hear them! But first: ## Branch model -This repo uses a branch-per-major release model. There is no `main`. - -- **`v0.18`** — current stable major (semver-zero, so 0.18 IS the major; 0.19 would be a major bump that gets its own branch). PRs for bug fixes / non-breaking features target this branch. -- **`v-dev`** — when next-major (i.e. next-minor on 0.x) work is in progress, this branch is open for breaking changes. PRs introducing them target this branch, not `v0.18`. -- **Older majors** stay on the repo for back-ports and security patches. Default branch on github.com is whichever major is current stable. - -When you fork or clone, the default branch is `v0.18` today. If you have a `main` branch from a previous checkout, delete it locally: - -```sh -git checkout v0.18 -git branch -D main -git remote prune origin -``` +See [docs/BRANCHING.md](docs/BRANCHING.md) for the current release-train model. +In short: independently releasable work may target the stable branch directly; +multi-feature or cross-repo train work uses the active `*-dev` integration +branch and is promoted to the matching stable branch when ready. `main` is only +the default/static GitHub branch. ## Releases @@ -170,7 +162,7 @@ The project uses automated semantic versioning based on commit messages: | `feat!:`, `fix!:`, or `BREAKING CHANGE:` | **Major** version bump | 1.0.0 → 2.0.0 | | `docs:`, `style:`, `refactor:`, `test:`, `chore:`, `build:`, `ci:` | **No** version bump | Version stays the same | -**Important**: Never manually edit version numbers in `pyproject.toml` or other files. The release automation will handle all version updates automatically when PRs are merged to the main branch. +**Important**: Never manually edit version numbers in `pyproject.toml` or other files. Releases are cut from the stable branch using the release automation described above. ## Logging Configuration diff --git a/docs/BRANCHING.md b/docs/BRANCHING.md new file mode 100644 index 0000000..0125486 --- /dev/null +++ b/docs/BRANCHING.md @@ -0,0 +1,58 @@ +# Branching and Release Trains + +This repo follows the GenLayer release-train model. + +## Current Train + +- Current stable branch: `v0.18` +- Active integration branch: `v0.19-dev` +- Next stable target: `v0.19` +- `main`: default/static branch alias for the active integration branch + +## Stable Branches + +Stable branches are long-lived release lines. For semver-zero packages, each +minor line is treated as the release line, for example `v0.18` or `v0.19`. + +PRs may target a stable branch directly when the merged result should be +releasable immediately. This is appropriate for bug fixes, small non-breaking +features, isolated release fixes, or a breaking change that is intentionally +shipping as the next version by itself. + +Stable branches must remain releasable. PRs into stable branches are expected to +pass the required cross-repo `E2E Tests` gate before merge. + +## Integration Branches + +Integration branches are optional. Use one when multiple changes need to +accumulate before release, especially for cross-repo work, dependent features, +breaking changes that must ship together, or a train that needs advisory E2E +while still expected to be red. + +Integration branches are named after the target stable branch plus `-dev`, for +example `v0.19-dev`. Feature PRs for that train target the integration branch. + +PRs into integration branches may run `E2E Tests` as advisory checks. They are +not the release gate. + +## Promotion and Release + +When an integration train is ready, open a promotion PR from the integration +branch to the matching stable branch, for example `v0.19-dev` to `v0.19`. + +That promotion PR is the release-readiness gate and must pass required +cross-repo `E2E Tests`. The actual package release is cut from the stable branch +using a version tag after the stable branch is ready. + +## `main` + +`main` exists for GitHub UX and tools that require a stable default branch. It is +not a release branch and it is not the integration target. + +This repo keeps `main` forwarded to the active integration branch using +automation. PRs opened against `main` are automatically retargeted to the branch +listed in `support/ci/ACTIVE_DEV_BRANCH`. + +When changing the active integration branch, update +`support/ci/ACTIVE_DEV_BRANCH`, the repo docs, and the corresponding +`genlayer-e2e` release-train matrix in the same change set. From 6bfa86a160a25d789630944e4ed05f0214fc164d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Thu, 11 Jun 2026 22:45:14 +0100 Subject: [PATCH 06/19] fix(fees)!: move wildcard callKey sentinel to keccak256(empty), deploy key = bytes32(0) (#93) The wildcard sentinel previously shared bytes32(0) with GenVM's natural empty-method-name key (deploy, emit_transfer), making deploy-specific Mode-2 allocations impossible. Per cross-team decision, the wildcard moves to the untagged keccak256 of empty bytes (0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470), which is provably outside the derived-key space. DEPLOY_CALL_KEY drops the bytes32(1) sentinel (GenVM ask withdrawn) and now equals CALL_KEY_UNNAMED = bytes32(0), matching what GenVM actually emits. Must land together with the consensus CALL_KEY_WILDCARD constant and the node Mode-2 tree decode change in v0.6. --- genlayer_py/transactions/fees.py | 10 ++++++++-- tests/unit/contracts/test_contract_actions.py | 12 +++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/genlayer_py/transactions/fees.py b/genlayer_py/transactions/fees.py index d67b639..ba1a9e3 100644 --- a/genlayer_py/transactions/fees.py +++ b/genlayer_py/transactions/fees.py @@ -154,9 +154,15 @@ class NormalizedTransactionFees(TypedDict): MESSAGE_ALLOCATION_ROOT_PARENT_INDEX = (1 << 256) - 1 -CALL_KEY_WILDCARD = b"\x00" * 32 +# Wildcard sentinel = keccak256 of empty bytes, untagged. Reserved: it can never be a +# derived key — short names (<32B) are left-aligned with a zero tail byte, long names +# get the low bit forced to 1, and this hash has neither. +CALL_KEY_WILDCARD = bytes.fromhex( + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" +) +# Empty method name derives bytes32(0); GenVM emits it for deploy and emit_transfer. CALL_KEY_UNNAMED = HexStr("0x" + ("0" * 64)) -DEPLOY_CALL_KEY = HexStr("0x" + ("0" * 62) + "01") +DEPLOY_CALL_KEY = CALL_KEY_UNNAMED CALL_KEY_DEPLOY = DEPLOY_CALL_KEY FEES_DISTRIBUTION_ABI_TYPE = ( diff --git a/tests/unit/contracts/test_contract_actions.py b/tests/unit/contracts/test_contract_actions.py index 5a9e298..c0c6de9 100644 --- a/tests/unit/contracts/test_contract_actions.py +++ b/tests/unit/contracts/test_contract_actions.py @@ -161,7 +161,13 @@ def test_derive_internal_message_call_key_hashes_exact_32_byte_method_name(): def test_derive_message_call_key_constants_for_deploy_and_unnamed(): - assert DEPLOY_CALL_KEY == "0x" + "00" * 31 + "01" + # Wildcard is the untagged hash of empty bytes — outside the derived-key space. + assert CALL_KEY_WILDCARD == eth_utils.keccak(b"") + assert CALL_KEY_WILDCARD == bytes.fromhex( + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + ) + # Empty name derives bytes32(0): the natural key for deploy and emit_transfer. + assert DEPLOY_CALL_KEY == "0x" + "00" * 32 assert CALL_KEY_DEPLOY == DEPLOY_CALL_KEY assert deploy_call_key() == DEPLOY_CALL_KEY assert CALL_KEY_UNNAMED == "0x" + "00" * 32 @@ -594,7 +600,7 @@ def test_simulate_write_contract_passes_fee_policy_and_value_to_sim_call(): assert request_params["fees"]["messageAllocations"][0]["budget"] == 5 assert ( request_params["fees"]["messageAllocations"][0]["callKey"] - == "0x" + "00" * 32 + == "0x" + CALL_KEY_WILDCARD.hex() ) @@ -1201,7 +1207,7 @@ def make_request(method, params): assert request_params["fees"]["messageAllocations"][0]["budget"] == 110 assert ( request_params["fees"]["messageAllocations"][0]["callKey"] - == "0x" + "00" * 32 + == "0x" + CALL_KEY_WILDCARD.hex() ) assert estimate["observed"]["recommendedExecutionBudgetPerRound"] == 602_117 assert estimate["observed"]["messageFeeBudget"] == 110 From ec7cab9f33cd0b5e40003d1dd21a4be6fef2611d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Tue, 16 Jun 2026 10:02:43 +0100 Subject: [PATCH 07/19] fix: bump package version to v0.19 (#95) Admin merged after required repo CI passed; optional E2E app check is queued/non-blocking for dev-branch PRs. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c8002e3..7024068 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "genlayer-py" -version = "0.18.0" +version = "0.19.0" description = "GenLayer Python SDK" authors = [ { name = "GenLayer" } From 30c262c48ec5f089223c584b381101074381e397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Tue, 7 Jul 2026 21:19:16 +0100 Subject: [PATCH 08/19] feat(vesting): vesting staking actions + ABI (#97) * feat(vesting): add vesting staking actions * feat(vesting): validator-leg actions (join/deposit/exit/claim, operator transfer, identity, wallet reads) Mirrors the genlayer-js validator actions for the CON-607 Vesting.sol surface: vesting_validator_join/deposit/exit/claim, operator-transfer initiate/complete/cancel, set_identity (extra_cid str/hex handling as in js), and get_validator_wallets/validator_wallet_count/ validator_deposited/is_validator_wallet reads. --- genlayer_py/vesting/__init__.py | 51 ++ genlayer_py/vesting/abi/__init__.py | 9 + genlayer_py/vesting/abi/vesting_abi.json | 925 +++++++++++++++++++++ genlayer_py/vesting/actions.py | 479 +++++++++++ pyproject.toml | 2 +- tests/unit/vesting/__init__.py | 0 tests/unit/vesting/test_vesting_actions.py | 559 +++++++++++++ 7 files changed, 2024 insertions(+), 1 deletion(-) create mode 100644 genlayer_py/vesting/__init__.py create mode 100644 genlayer_py/vesting/abi/__init__.py create mode 100644 genlayer_py/vesting/abi/vesting_abi.json create mode 100644 genlayer_py/vesting/actions.py create mode 100644 tests/unit/vesting/__init__.py create mode 100644 tests/unit/vesting/test_vesting_actions.py diff --git a/genlayer_py/vesting/__init__.py b/genlayer_py/vesting/__init__.py new file mode 100644 index 0000000..ad7d320 --- /dev/null +++ b/genlayer_py/vesting/__init__.py @@ -0,0 +1,51 @@ +from genlayer_py.vesting.actions import ( + vesting_delegator_join, + vesting_delegator_exit, + vesting_delegator_claim, + vesting_validator_join, + vesting_validator_deposit, + vesting_validator_exit, + vesting_validator_claim, + vesting_validator_initiate_operator_transfer, + vesting_validator_complete_operator_transfer, + vesting_validator_cancel_operator_transfer, + vesting_validator_set_identity, + vesting_withdraw, + vested_amount, + unvested_amount, + withdrawable_amount, + get_vesting_schedule, + get_vesting_state, + get_vesting_stake_info, + get_validator_wallets, + validator_wallet_count, + validator_deposited, + is_validator_wallet, + get_vesting_contract, +) + +__all__ = [ + "vesting_delegator_join", + "vesting_delegator_exit", + "vesting_delegator_claim", + "vesting_validator_join", + "vesting_validator_deposit", + "vesting_validator_exit", + "vesting_validator_claim", + "vesting_validator_initiate_operator_transfer", + "vesting_validator_complete_operator_transfer", + "vesting_validator_cancel_operator_transfer", + "vesting_validator_set_identity", + "vesting_withdraw", + "vested_amount", + "unvested_amount", + "withdrawable_amount", + "get_vesting_schedule", + "get_vesting_state", + "get_vesting_stake_info", + "get_validator_wallets", + "validator_wallet_count", + "validator_deposited", + "is_validator_wallet", + "get_vesting_contract", +] diff --git a/genlayer_py/vesting/abi/__init__.py b/genlayer_py/vesting/abi/__init__.py new file mode 100644 index 0000000..34c0257 --- /dev/null +++ b/genlayer_py/vesting/abi/__init__.py @@ -0,0 +1,9 @@ +import json +import importlib.resources + +with importlib.resources.as_file( + importlib.resources.files("genlayer_py.vesting.abi").joinpath("vesting_abi.json") +) as path, open(path, "r", encoding="utf-8") as f: + VESTING_ABI = json.load(f) + +__all__ = ["VESTING_ABI"] diff --git a/genlayer_py/vesting/abi/vesting_abi.json b/genlayer_py/vesting/abi/vesting_abi.json new file mode 100644 index 0000000..4ea485e --- /dev/null +++ b/genlayer_py/vesting/abi/vesting_abi.json @@ -0,0 +1,925 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "TokensWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DelegatorJoined", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "DelegatorExited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "returned", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "rewardOrLoss", + "type": "int256" + } + ], + "name": "DelegatorClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ValidatorJoined", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ValidatorDeposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "ValidatorExited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "returned", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "rewardOrLoss", + "type": "int256" + } + ], + "name": "ValidatorClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beneficiary", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "vestingContract", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "enum IVesting.Category", + "name": "category", + "type": "uint8" + } + ], + "name": "VestingCreated", + "type": "event" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "category", + "outputs": [ + { + "internalType": "enum IVesting.Category", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "beneficiary", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "creator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "revoker", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "startDate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cliffDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "periodDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "numberOfPeriods", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cliffUnlockBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "needsManualUnlock", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "manualUnlocked", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "revoked", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vestingStopped", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalWithdrawn", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vestedAtRevocation", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalAmountAtRevocation", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "revokedAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vestingStoppedAt", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vestedAtStop", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accumulatedRewards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "accumulatedLosses", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vestedAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unvestedAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdrawableAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "depositedPerValidator", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "pendingExitDeposited", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorWallets", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + } + ], + "name": "isValidatorWallet", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + } + ], + "name": "validatorDeposited", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getValidatorWallets", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "validatorWalletCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "vestingWithdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "vestingDelegatorJoin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "vestingDelegatorExit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "vestingDelegatorClaim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "vestingValidatorJoin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "vestingValidatorDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "vestingValidatorExit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + } + ], + "name": "vestingValidatorClaim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "address", + "name": "newOperator", + "type": "address" + } + ], + "name": "vestingValidatorInitiateOperatorTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + } + ], + "name": "vestingValidatorCompleteOperatorTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + } + ], + "name": "vestingValidatorCancelOperatorTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "string", + "name": "moniker", + "type": "string" + }, + { + "internalType": "string", + "name": "logoUri", + "type": "string" + }, + { + "internalType": "string", + "name": "website", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "email", + "type": "string" + }, + { + "internalType": "string", + "name": "twitter", + "type": "string" + }, + { + "internalType": "string", + "name": "telegram", + "type": "string" + }, + { + "internalType": "string", + "name": "github", + "type": "string" + }, + { + "internalType": "bytes", + "name": "extraCid", + "type": "bytes" + } + ], + "name": "vestingValidatorSetIdentity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_beneficiary", + "type": "address" + } + ], + "name": "getVesting", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/genlayer_py/vesting/actions.py b/genlayer_py/vesting/actions.py new file mode 100644 index 0000000..26bcfc8 --- /dev/null +++ b/genlayer_py/vesting/actions.py @@ -0,0 +1,479 @@ +"""Vesting actions for GenLayerClient. + +Mirrors the genlayer_py.staking actions module. Beneficiary write +methods operate on a per-beneficiary Vesting contract address. Factory +discovery methods operate on a VestingFactory contract address. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional, Union + +from eth_account.signers.local import LocalAccount +from eth_typing import Address, ChecksumAddress +from hexbytes import HexBytes + +from genlayer_py.exceptions import GenLayerError +from genlayer_py.vesting.abi import VESTING_ABI + +if TYPE_CHECKING: + from genlayer_py.client import GenLayerClient + + +AddressLike = Union[Address, ChecksumAddress, str] +ExtraCidLike = Union[str, bytes, bytearray, HexBytes] + + +def _vesting_address( + self: "GenLayerClient", vesting_contract_address: AddressLike +) -> ChecksumAddress: + return self.w3.to_checksum_address(vesting_contract_address) + + +def _vesting(self: "GenLayerClient", vesting_contract_address: AddressLike): + return self.w3.eth.contract( + address=_vesting_address(self, vesting_contract_address), abi=VESTING_ABI + ) + + +def _vesting_factory(self: "GenLayerClient", vesting_factory_address: AddressLike): + return self.w3.eth.contract( + address=self.w3.to_checksum_address(vesting_factory_address), + abi=VESTING_ABI, + ) + + +def _extra_cid(extra_cid: Optional[ExtraCidLike]) -> bytes: + if not extra_cid: + return b"" + if isinstance(extra_cid, str): + if extra_cid.startswith("0x"): + return bytes(HexBytes(extra_cid)) + return extra_cid.encode() + return bytes(extra_cid) + + +def _sender( + self: "GenLayerClient", account: Optional[LocalAccount] +) -> LocalAccount: + acct = account or self.local_account + if acct is None: + raise GenLayerError("No account provided and client has no local_account") + return acct + + +def _send( + self: "GenLayerClient", + account: LocalAccount, + tx: dict, +) -> HexBytes: + """Sign and broadcast a prepared transaction dict.""" + signed = account.sign_transaction(tx) + return self.w3.eth.send_raw_transaction(signed.raw_transaction) + + +def _build( + self: "GenLayerClient", + account: LocalAccount, + to: ChecksumAddress, + data: bytes, + value: int = 0, + gas: Optional[int] = None, +) -> dict: + tx = { + "from": account.address, + "to": to, + "data": data, + "value": value, + "nonce": self.w3.eth.get_transaction_count(account.address), + "chainId": self.chain.id, + } + # Lean on the node's eth_estimateGas unless caller overrode it. + tx["gas"] = gas if gas is not None else self.w3.eth.estimate_gas(tx) * 2 + tx["gasPrice"] = self.w3.eth.gas_price + return tx + + +# --- read methods ----------------------------------------------------- + + +def vested_amount( + self: "GenLayerClient", vesting_contract_address: AddressLike +) -> int: + return _vesting(self, vesting_contract_address).functions.vestedAmount().call() + + +def unvested_amount( + self: "GenLayerClient", vesting_contract_address: AddressLike +) -> int: + return _vesting(self, vesting_contract_address).functions.unvestedAmount().call() + + +def withdrawable_amount( + self: "GenLayerClient", vesting_contract_address: AddressLike +) -> int: + return ( + _vesting(self, vesting_contract_address) + .functions.withdrawableAmount() + .call() + ) + + +def get_vesting_schedule( + self: "GenLayerClient", vesting_contract_address: AddressLike +) -> dict: + contract = _vesting(self, vesting_contract_address) + return { + "name": contract.functions.name().call(), + "category": contract.functions.category().call(), + "beneficiary": contract.functions.beneficiary().call(), + "creator": contract.functions.creator().call(), + "revoker": contract.functions.revoker().call(), + "factory": contract.functions.factory().call(), + "total_amount": contract.functions.totalAmount().call(), + "start_date": contract.functions.startDate().call(), + "cliff_duration": contract.functions.cliffDuration().call(), + "period_duration": contract.functions.periodDuration().call(), + "number_of_periods": contract.functions.numberOfPeriods().call(), + "cliff_unlock_bps": contract.functions.cliffUnlockBps().call(), + "needs_manual_unlock": contract.functions.needsManualUnlock().call(), + } + + +def get_vesting_state( + self: "GenLayerClient", vesting_contract_address: AddressLike +) -> dict: + contract = _vesting(self, vesting_contract_address) + return { + "manual_unlocked": contract.functions.manualUnlocked().call(), + "revoked": contract.functions.revoked().call(), + "vesting_stopped": contract.functions.vestingStopped().call(), + "total_withdrawn": contract.functions.totalWithdrawn().call(), + "vested_at_revocation": contract.functions.vestedAtRevocation().call(), + "total_amount_at_revocation": contract.functions.totalAmountAtRevocation() + .call(), + "revoked_at": contract.functions.revokedAt().call(), + "vesting_stopped_at": contract.functions.vestingStoppedAt().call(), + "vested_at_stop": contract.functions.vestedAtStop().call(), + "accumulated_rewards": contract.functions.accumulatedRewards().call(), + "accumulated_losses": contract.functions.accumulatedLosses().call(), + "vested_amount": contract.functions.vestedAmount().call(), + "unvested_amount": contract.functions.unvestedAmount().call(), + "withdrawable_amount": contract.functions.withdrawableAmount().call(), + } + + +def get_vesting_stake_info( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + validator: AddressLike, +) -> dict: + contract = _vesting(self, vesting_contract_address) + validator_address = self.w3.to_checksum_address(validator) + return { + "deposited": contract.functions.depositedPerValidator( + validator_address + ).call(), + "pending_exit_deposited": contract.functions.pendingExitDeposited( + validator_address + ).call(), + } + + +def get_validator_wallets( + self: "GenLayerClient", vesting_contract_address: AddressLike +) -> List[ChecksumAddress]: + return ( + _vesting(self, vesting_contract_address) + .functions.getValidatorWallets() + .call() + ) + + +def validator_wallet_count( + self: "GenLayerClient", vesting_contract_address: AddressLike +) -> int: + return ( + _vesting(self, vesting_contract_address) + .functions.validatorWalletCount() + .call() + ) + + +def validator_deposited( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + wallet: AddressLike, +) -> int: + return ( + _vesting(self, vesting_contract_address) + .functions.validatorDeposited(self.w3.to_checksum_address(wallet)) + .call() + ) + + +def is_validator_wallet( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + wallet: AddressLike, +) -> bool: + return ( + _vesting(self, vesting_contract_address) + .functions.isValidatorWallet(self.w3.to_checksum_address(wallet)) + .call() + ) + + +def get_vesting_contract( + self: "GenLayerClient", + vesting_factory_address: AddressLike, + beneficiary: AddressLike, +) -> ChecksumAddress: + contract = _vesting_factory(self, vesting_factory_address) + vesting_contract_address = contract.functions.getVesting( + self.w3.to_checksum_address(beneficiary) + ).call() + return self.w3.to_checksum_address(vesting_contract_address) + + +# --- write methods ---------------------------------------------------- + + +def vesting_delegator_join( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + validator: AddressLike, + amount: int, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Delegates `amount` GEN from a Vesting contract to a validator.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingDelegatorJoin", + args=[self.w3.to_checksum_address(validator), amount], + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_delegator_exit( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + validator: AddressLike, + shares: int, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Burns `shares` of a Vesting contract's delegation position.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingDelegatorExit", + args=[self.w3.to_checksum_address(validator), shares], + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_delegator_claim( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + validator: AddressLike, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Claims exited staking funds back into the Vesting contract.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingDelegatorClaim", args=[self.w3.to_checksum_address(validator)] + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_validator_join( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + operator: AddressLike, + amount: int, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Creates a validator wallet using GEN from a Vesting contract.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingValidatorJoin", + args=[self.w3.to_checksum_address(operator), amount], + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_validator_deposit( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + wallet: AddressLike, + amount: int, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Adds Vesting-held GEN to one of the Vesting validator wallets.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingValidatorDeposit", + args=[self.w3.to_checksum_address(wallet), amount], + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_validator_exit( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + wallet: AddressLike, + shares: int, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Burns `shares` from a Vesting-owned validator wallet.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingValidatorExit", + args=[self.w3.to_checksum_address(wallet), shares], + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_validator_claim( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + wallet: AddressLike, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Claims exited validator self-stake back into the Vesting contract.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingValidatorClaim", args=[self.w3.to_checksum_address(wallet)] + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_validator_initiate_operator_transfer( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + wallet: AddressLike, + new_operator: AddressLike, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Begins operator transfer for a Vesting-owned validator wallet.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingValidatorInitiateOperatorTransfer", + args=[ + self.w3.to_checksum_address(wallet), + self.w3.to_checksum_address(new_operator), + ], + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_validator_complete_operator_transfer( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + wallet: AddressLike, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Completes operator transfer for a Vesting-owned validator wallet.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingValidatorCompleteOperatorTransfer", + args=[self.w3.to_checksum_address(wallet)], + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_validator_cancel_operator_transfer( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + wallet: AddressLike, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Cancels operator transfer for a Vesting-owned validator wallet.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingValidatorCancelOperatorTransfer", + args=[self.w3.to_checksum_address(wallet)], + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_validator_set_identity( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + wallet: AddressLike, + moniker: str, + logo_uri: str, + website: str, + description: str, + email: str, + twitter: str, + telegram: str, + github: str, + extra_cid: Optional[ExtraCidLike] = None, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Sets identity metadata on a Vesting-owned validator wallet.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi( + "vestingValidatorSetIdentity", + args=[ + self.w3.to_checksum_address(wallet), + moniker, + logo_uri, + website, + description, + email, + twitter, + telegram, + github, + _extra_cid(extra_cid), + ], + ) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) + + +def vesting_withdraw( + self: "GenLayerClient", + vesting_contract_address: AddressLike, + amount: int, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Withdraws vested tokens from a Vesting contract to its beneficiary.""" + sender = _sender(self, account) + contract = _vesting(self, vesting_contract_address) + data = contract.encode_abi("vestingWithdraw", args=[amount]) + vesting_address = _vesting_address(self, vesting_contract_address) + tx = _build(self, sender, vesting_address, data) + return _send(self, sender, tx) diff --git a/pyproject.toml b/pyproject.toml index 7024068..8b401c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ where = ["."] exclude = ["tests*", "scripts*"] [tool.setuptools.package-data] -"genlayer_py" = ["consensus/abi/*.json", "staking/abi/*.json"] +"genlayer_py" = ["consensus/abi/*.json", "staking/abi/*.json", "vesting/abi/*.json"] [dependency-groups] dev = [ diff --git a/tests/unit/vesting/__init__.py b/tests/unit/vesting/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/vesting/test_vesting_actions.py b/tests/unit/vesting/test_vesting_actions.py new file mode 100644 index 0000000..68be07e --- /dev/null +++ b/tests/unit/vesting/test_vesting_actions.py @@ -0,0 +1,559 @@ +"""Unit tests for the vesting action module. + +The tests here are structural, matching the staking action tests: write +helpers are checked for ABI selectors and target addresses, while read +helpers use mocked contract calls instead of a live node. +""" + +from types import SimpleNamespace +from unittest.mock import Mock + +from eth_utils import keccak +import pytest +from web3 import Web3 + +import genlayer_py.vesting.actions as vesting_actions +from genlayer_py.vesting.abi import VESTING_ABI + + +VESTING_ADDR = "0x1111111111111111111111111111111111111111" +FACTORY_ADDR = "0x2222222222222222222222222222222222222222" +SENDER_ADDR = "0x3333333333333333333333333333333333333333" +VALIDATOR_ADDR = "0x4444444444444444444444444444444444444444" +BENEFICIARY_ADDR = "0x5555555555555555555555555555555555555555" +CREATOR_ADDR = "0x6666666666666666666666666666666666666666" +REVOKER_ADDR = "0x7777777777777777777777777777777777777777" +WALLET_ADDR = "0x8888888888888888888888888888888888888888" +NEW_OPERATOR_ADDR = "0x9999999999999999999999999999999999999999" + +SEL_VESTING_DELEGATOR_JOIN = keccak( + text="vestingDelegatorJoin(address,uint256)" +)[:4].hex() +SEL_VESTING_DELEGATOR_EXIT = keccak( + text="vestingDelegatorExit(address,uint256)" +)[:4].hex() +SEL_VESTING_DELEGATOR_CLAIM = keccak(text="vestingDelegatorClaim(address)")[ + :4 +].hex() +SEL_VESTING_VALIDATOR_JOIN = keccak( + text="vestingValidatorJoin(address,uint256)" +)[:4].hex() +SEL_VESTING_VALIDATOR_DEPOSIT = keccak( + text="vestingValidatorDeposit(address,uint256)" +)[:4].hex() +SEL_VESTING_VALIDATOR_EXIT = keccak( + text="vestingValidatorExit(address,uint256)" +)[:4].hex() +SEL_VESTING_VALIDATOR_CLAIM = keccak(text="vestingValidatorClaim(address)")[ + :4 +].hex() +SEL_VESTING_VALIDATOR_INITIATE_OPERATOR_TRANSFER = keccak( + text="vestingValidatorInitiateOperatorTransfer(address,address)" +)[:4].hex() +SEL_VESTING_VALIDATOR_COMPLETE_OPERATOR_TRANSFER = keccak( + text="vestingValidatorCompleteOperatorTransfer(address)" +)[:4].hex() +SEL_VESTING_VALIDATOR_CANCEL_OPERATOR_TRANSFER = keccak( + text="vestingValidatorCancelOperatorTransfer(address)" +)[:4].hex() +SEL_VESTING_VALIDATOR_SET_IDENTITY = keccak( + text=( + "vestingValidatorSetIdentity(" + "address,string,string,string,string,string,string,string,string,bytes)" + ) +)[:4].hex() +SEL_VESTING_WITHDRAW = keccak(text="vestingWithdraw(uint256)")[:4].hex() + + +def _make_client(): + """SimpleNamespace stand-in for GenLayerClient. + + Uses a real Web3().eth for contract encoding, but patches the eth + methods that would otherwise hit a live node. + """ + signed = SimpleNamespace(raw_transaction=b"\x00") + w3 = Web3() + w3.eth.get_transaction_count = Mock(return_value=1) + w3.eth.estimate_gas = Mock(return_value=100_000) + w3.eth.send_raw_transaction = Mock(return_value=b"\xde\xad" * 16) + type(w3.eth).gas_price = 1_000_000_000 # type: ignore[assignment] + + local_account = SimpleNamespace( + address=SENDER_ADDR, + sign_transaction=Mock(return_value=signed), + ) + chain = SimpleNamespace(id=61999) + return SimpleNamespace(chain=chain, local_account=local_account, w3=w3) + + +def _last_tx(client): + """Return the tx dict the client's sign_transaction was called with.""" + return client.local_account.sign_transaction.call_args.args[0] + + +def _call_result(value): + return SimpleNamespace(call=Mock(return_value=value)) + + +def _decode_vesting_tx(client, data): + contract = client.w3.eth.contract( + address=client.w3.to_checksum_address(VESTING_ADDR), abi=VESTING_ABI + ) + return contract.decode_function_input(data) + + +def test_vesting_delegator_join_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_delegator_join( + self=client, + vesting_contract_address=VESTING_ADDR, + validator=VALIDATOR_ADDR, + amount=10, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["value"] == 0 + assert tx["data"][2:10] == SEL_VESTING_DELEGATOR_JOIN + + +def test_vesting_delegator_exit_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_delegator_exit( + self=client, + vesting_contract_address=VESTING_ADDR, + validator=VALIDATOR_ADDR, + shares=42, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["data"][2:10] == SEL_VESTING_DELEGATOR_EXIT + + +def test_vesting_delegator_claim_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_delegator_claim( + self=client, + vesting_contract_address=VESTING_ADDR, + validator=VALIDATOR_ADDR, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["data"][2:10] == SEL_VESTING_DELEGATOR_CLAIM + + +def test_vesting_withdraw_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_withdraw( + self=client, + vesting_contract_address=VESTING_ADDR, + amount=7, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["data"][2:10] == SEL_VESTING_WITHDRAW + + +def test_vesting_validator_join_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_validator_join( + self=client, + vesting_contract_address=VESTING_ADDR, + operator=VALIDATOR_ADDR, + amount=10, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["value"] == 0 + assert tx["data"][2:10] == SEL_VESTING_VALIDATOR_JOIN + + +def test_vesting_validator_deposit_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_validator_deposit( + self=client, + vesting_contract_address=VESTING_ADDR, + wallet=WALLET_ADDR, + amount=25, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["value"] == 0 + assert tx["data"][2:10] == SEL_VESTING_VALIDATOR_DEPOSIT + + +def test_vesting_validator_exit_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_validator_exit( + self=client, + vesting_contract_address=VESTING_ADDR, + wallet=WALLET_ADDR, + shares=42, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["data"][2:10] == SEL_VESTING_VALIDATOR_EXIT + + +def test_vesting_validator_claim_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_validator_claim( + self=client, + vesting_contract_address=VESTING_ADDR, + wallet=WALLET_ADDR, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["data"][2:10] == SEL_VESTING_VALIDATOR_CLAIM + + +def test_vesting_validator_initiate_operator_transfer_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_validator_initiate_operator_transfer( + self=client, + vesting_contract_address=VESTING_ADDR, + wallet=WALLET_ADDR, + new_operator=NEW_OPERATOR_ADDR, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["data"][2:10] == SEL_VESTING_VALIDATOR_INITIATE_OPERATOR_TRANSFER + + +def test_vesting_validator_complete_operator_transfer_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_validator_complete_operator_transfer( + self=client, + vesting_contract_address=VESTING_ADDR, + wallet=WALLET_ADDR, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["data"][2:10] == SEL_VESTING_VALIDATOR_COMPLETE_OPERATOR_TRANSFER + + +def test_vesting_validator_cancel_operator_transfer_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_validator_cancel_operator_transfer( + self=client, + vesting_contract_address=VESTING_ADDR, + wallet=WALLET_ADDR, + ) + tx = _last_tx(client) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["data"][2:10] == SEL_VESTING_VALIDATOR_CANCEL_OPERATOR_TRANSFER + + +def test_vesting_validator_set_identity_targets_vesting_contract(): + client = _make_client() + vesting_actions.vesting_validator_set_identity( + self=client, + vesting_contract_address=VESTING_ADDR, + wallet=WALLET_ADDR, + moniker="validator", + logo_uri="logo", + website="site", + description="desc", + email="email", + twitter="tw", + telegram="tg", + github="gh", + extra_cid="cid", + ) + tx = _last_tx(client) + _, args = _decode_vesting_tx(client, tx["data"]) + assert tx["to"].lower() == VESTING_ADDR.lower() + assert tx["data"][2:10] == SEL_VESTING_VALIDATOR_SET_IDENTITY + assert args["extraCid"] == b"cid" + + +def test_vesting_validator_set_identity_preserves_hex_extra_cid(): + client = _make_client() + vesting_actions.vesting_validator_set_identity( + self=client, + vesting_contract_address=VESTING_ADDR, + wallet=WALLET_ADDR, + moniker="validator", + logo_uri="logo", + website="site", + description="desc", + email="email", + twitter="tw", + telegram="tg", + github="gh", + extra_cid="0x1234", + ) + tx = _last_tx(client) + _, args = _decode_vesting_tx(client, tx["data"]) + assert args["extraCid"] == b"\x12\x34" + + +def test_write_requires_account(): + client = _make_client() + client.local_account = None + with pytest.raises(Exception, match="No account provided"): + vesting_actions.vesting_withdraw( + self=client, + vesting_contract_address=VESTING_ADDR, + amount=7, + ) + + +def test_vested_unvested_and_withdrawable_amount_reads(): + client = _make_client() + functions = SimpleNamespace( + vestedAmount=Mock(return_value=_call_result(11)), + unvestedAmount=Mock(return_value=_call_result(22)), + withdrawableAmount=Mock(return_value=_call_result(33)), + ) + client.w3.eth.contract = Mock(return_value=SimpleNamespace(functions=functions)) + + assert ( + vesting_actions.vested_amount( + self=client, vesting_contract_address=VESTING_ADDR + ) + == 11 + ) + assert ( + vesting_actions.unvested_amount( + self=client, vesting_contract_address=VESTING_ADDR + ) + == 22 + ) + assert ( + vesting_actions.withdrawable_amount( + self=client, vesting_contract_address=VESTING_ADDR + ) + == 33 + ) + + +def test_get_vesting_schedule_reads_schedule_fields(): + client = _make_client() + functions = SimpleNamespace( + name=Mock(return_value=_call_result("Founder")), + category=Mock(return_value=_call_result(1)), + beneficiary=Mock(return_value=_call_result(BENEFICIARY_ADDR)), + creator=Mock(return_value=_call_result(CREATOR_ADDR)), + revoker=Mock(return_value=_call_result(REVOKER_ADDR)), + factory=Mock(return_value=_call_result(FACTORY_ADDR)), + totalAmount=Mock(return_value=_call_result(1000)), + startDate=Mock(return_value=_call_result(100)), + cliffDuration=Mock(return_value=_call_result(200)), + periodDuration=Mock(return_value=_call_result(300)), + numberOfPeriods=Mock(return_value=_call_result(4)), + cliffUnlockBps=Mock(return_value=_call_result(500)), + needsManualUnlock=Mock(return_value=_call_result(False)), + ) + client.w3.eth.contract = Mock(return_value=SimpleNamespace(functions=functions)) + + schedule = vesting_actions.get_vesting_schedule( + self=client, vesting_contract_address=VESTING_ADDR + ) + + assert schedule == { + "name": "Founder", + "category": 1, + "beneficiary": BENEFICIARY_ADDR, + "creator": CREATOR_ADDR, + "revoker": REVOKER_ADDR, + "factory": FACTORY_ADDR, + "total_amount": 1000, + "start_date": 100, + "cliff_duration": 200, + "period_duration": 300, + "number_of_periods": 4, + "cliff_unlock_bps": 500, + "needs_manual_unlock": False, + } + + +def test_get_vesting_state_reads_state_fields(): + client = _make_client() + functions = SimpleNamespace( + manualUnlocked=Mock(return_value=_call_result(True)), + revoked=Mock(return_value=_call_result(False)), + vestingStopped=Mock(return_value=_call_result(False)), + totalWithdrawn=Mock(return_value=_call_result(1)), + vestedAtRevocation=Mock(return_value=_call_result(2)), + totalAmountAtRevocation=Mock(return_value=_call_result(3)), + revokedAt=Mock(return_value=_call_result(4)), + vestingStoppedAt=Mock(return_value=_call_result(5)), + vestedAtStop=Mock(return_value=_call_result(6)), + accumulatedRewards=Mock(return_value=_call_result(7)), + accumulatedLosses=Mock(return_value=_call_result(8)), + vestedAmount=Mock(return_value=_call_result(9)), + unvestedAmount=Mock(return_value=_call_result(10)), + withdrawableAmount=Mock(return_value=_call_result(11)), + ) + client.w3.eth.contract = Mock(return_value=SimpleNamespace(functions=functions)) + + state = vesting_actions.get_vesting_state( + self=client, vesting_contract_address=VESTING_ADDR + ) + + assert state == { + "manual_unlocked": True, + "revoked": False, + "vesting_stopped": False, + "total_withdrawn": 1, + "vested_at_revocation": 2, + "total_amount_at_revocation": 3, + "revoked_at": 4, + "vesting_stopped_at": 5, + "vested_at_stop": 6, + "accumulated_rewards": 7, + "accumulated_losses": 8, + "vested_amount": 9, + "unvested_amount": 10, + "withdrawable_amount": 11, + } + + +def test_get_vesting_stake_info_reads_validator_state(): + client = _make_client() + functions = SimpleNamespace( + depositedPerValidator=Mock(return_value=_call_result(123)), + pendingExitDeposited=Mock(return_value=_call_result(45)), + ) + client.w3.eth.contract = Mock(return_value=SimpleNamespace(functions=functions)) + + stake_info = vesting_actions.get_vesting_stake_info( + self=client, + vesting_contract_address=VESTING_ADDR, + validator=VALIDATOR_ADDR, + ) + + assert stake_info == {"deposited": 123, "pending_exit_deposited": 45} + checksum_validator = client.w3.to_checksum_address(VALIDATOR_ADDR) + functions.depositedPerValidator.assert_called_once_with(checksum_validator) + functions.pendingExitDeposited.assert_called_once_with(checksum_validator) + + +def test_get_validator_wallets_reads_wallet_list(): + client = _make_client() + functions = SimpleNamespace( + getValidatorWallets=Mock(return_value=_call_result([WALLET_ADDR])), + ) + client.w3.eth.contract = Mock(return_value=SimpleNamespace(functions=functions)) + + wallets = vesting_actions.get_validator_wallets( + self=client, vesting_contract_address=VESTING_ADDR + ) + + assert wallets == [WALLET_ADDR] + + +def test_validator_wallet_count_reads_wallet_count(): + client = _make_client() + functions = SimpleNamespace( + validatorWalletCount=Mock(return_value=_call_result(3)), + ) + client.w3.eth.contract = Mock(return_value=SimpleNamespace(functions=functions)) + + assert ( + vesting_actions.validator_wallet_count( + self=client, vesting_contract_address=VESTING_ADDR + ) + == 3 + ) + + +def test_validator_deposited_reads_wallet_deposit(): + client = _make_client() + functions = SimpleNamespace( + validatorDeposited=Mock(return_value=_call_result(123)), + ) + client.w3.eth.contract = Mock(return_value=SimpleNamespace(functions=functions)) + + deposited = vesting_actions.validator_deposited( + self=client, + vesting_contract_address=VESTING_ADDR, + wallet=WALLET_ADDR, + ) + + assert deposited == 123 + functions.validatorDeposited.assert_called_once_with( + client.w3.to_checksum_address(WALLET_ADDR) + ) + + +def test_is_validator_wallet_reads_membership(): + client = _make_client() + functions = SimpleNamespace( + isValidatorWallet=Mock(return_value=_call_result(True)), + ) + client.w3.eth.contract = Mock(return_value=SimpleNamespace(functions=functions)) + + is_wallet = vesting_actions.is_validator_wallet( + self=client, + vesting_contract_address=VESTING_ADDR, + wallet=WALLET_ADDR, + ) + + assert is_wallet is True + functions.isValidatorWallet.assert_called_once_with( + client.w3.to_checksum_address(WALLET_ADDR) + ) + + +def test_get_vesting_contract_reads_factory_mapping(): + client = _make_client() + functions = SimpleNamespace( + getVesting=Mock(return_value=_call_result(VESTING_ADDR)), + ) + client.w3.eth.contract = Mock(return_value=SimpleNamespace(functions=functions)) + + vesting_contract = vesting_actions.get_vesting_contract( + self=client, + vesting_factory_address=FACTORY_ADDR, + beneficiary=BENEFICIARY_ADDR, + ) + + assert vesting_contract == client.w3.to_checksum_address(VESTING_ADDR) + client.w3.eth.contract.assert_called_once_with( + address=client.w3.to_checksum_address(FACTORY_ADDR), abi=VESTING_ABI + ) + functions.getVesting.assert_called_once_with( + client.w3.to_checksum_address(BENEFICIARY_ADDR) + ) + + +def test_abi_includes_expected_functions_and_events(): + """Guard against the bundled ABI JSON drifting or truncating.""" + names = {e["name"] for e in VESTING_ABI if e.get("type") == "function"} + events = {e["name"] for e in VESTING_ABI if e.get("type") == "event"} + assert { + "vestingDelegatorJoin", + "vestingDelegatorExit", + "vestingDelegatorClaim", + "vestingValidatorJoin", + "vestingValidatorDeposit", + "vestingValidatorExit", + "vestingValidatorClaim", + "vestingValidatorInitiateOperatorTransfer", + "vestingValidatorCompleteOperatorTransfer", + "vestingValidatorCancelOperatorTransfer", + "vestingValidatorSetIdentity", + "vestingWithdraw", + "vestedAmount", + "unvestedAmount", + "withdrawableAmount", + "depositedPerValidator", + "pendingExitDeposited", + "getValidatorWallets", + "validatorWalletCount", + "validatorDeposited", + "isValidatorWallet", + "getVesting", + }.issubset(names) + assert { + "TokensWithdrawn", + "DelegatorJoined", + "DelegatorExited", + "DelegatorClaimed", + "ValidatorJoined", + "ValidatorDeposited", + "ValidatorExited", + "ValidatorClaimed", + "VestingCreated", + }.issubset(events) From ef36623ac7e36fb36701449f66bb61f6518d9d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Thu, 23 Jul 2026 11:05:44 +0100 Subject: [PATCH 09/19] feat(calldata)!: migrate method-call key "method" -> "" (v0.6 genvm ABI) (#96) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(calldata)!: migrate method-call key "method" -> "" (v0.6 genvm ABI) The GenVM v0.6 calldata ABI renames the method-call key from "method" to "" (empty string). Because the calldata encoder sorts map keys ascending, "" sorts first, so the method name becomes the binary prefix of the encoded calldata. genvm-manager auto-remaps "method" -> "" but logs an error on every call; SDKs must migrate to stop the error and match the canonical wire format. - make_calldata_object (genlayer_py/contracts/utils.py) is the sole producer of the method key; it now emits ret[""] = method. This flows through every method-call path (write/read/sim/estimate + encode_tx_data_call). - The calldata encoder/decoder are key-agnostic and unchanged; the decoder round-trips whatever key is present, so decoding auto-adapts. - Updated consensus/smoke round-trip assertions to read call_data[""] and added test_method_key_is_empty_string_binary_prefix, which asserts the "" key is present, "method" is absent, and the method name precedes the "args" key bytes in the raw calldata (proving "" sorts first). This test fails under the old key. - Regenerated the raw / base64 / readable calldata fixtures to the new wire format ({"": "ask_for_coin", ...}) across the write sample-data files and conftest. Deploy (constructor) fixtures are unaffected — they carry no method key. No back-compat toggle: the codebase has no calldata versioning mechanism and none is introduced. BREAKING CHANGE: method-call calldata now keys the method name under "" (empty string) instead of "method", per the v0.6 GenVM calldata ABI. Calldata built by this SDK is incompatible with pre-v0.6 GenVM, and decoded calldata exposes the method name under the "" key. * test(e2e): migrate contract fixtures to genvm v0.3 SDK API The tests/e2e/contracts fixtures used the v0.2.x SDK idiom and fail under GenVM v0.6 with NameError (from genlayer import * no longer binds gl). Migrate all 15 to v0.3: - from genlayer import * -> import genlayer as gl + from genlayer.types import * - gl.Contract -> gl.contract.Contract; gl.ContractAt -> gl.contract.get_at; gl.deploy_contract -> gl.contract.deploy - gl.DynArray/TreeMap -> gl.storage.*; @allow_storage -> @gl.storage.allow - gl.eq_principle_* -> gl.eq_principle.*; gl.exec_prompt -> gl.nondet.exec_prompt; gl.get_webpage -> gl.nondet.web.render - drop callable u256(x) wrapping (aliases are Annotated[int], not callable) multi_tenant_storage.py additionally dropped the removed emit(gas=...) kwarg (v0.3 emit takes value/on/use_balance/fee_params; a normal internal message's fee comes from the sender's prefunded pool, no per-emit gas limit). The py-genlayer:test Depends placeholder is unchanged. --------- Co-authored-by: kp2pml30 --- genlayer_py/contracts/utils.py | 2 +- .../contracts/football_prediction_market.py | 11 +- tests/e2e/contracts/intelligent_oracle.py | 23 +- .../contracts/intelligent_oracle_factory.py | 9 +- tests/e2e/contracts/llm_erc20.py | 11 +- tests/e2e/contracts/log_indexer.py | 11 +- tests/e2e/contracts/log_indexer_testnet.py | 13 +- .../contracts/multi_file_contract/__init__.py | 9 +- .../contracts/multi_file_contract/other.py | 5 +- tests/e2e/contracts/multi_read_erc20.py | 9 +- tests/e2e/contracts/multi_tenant_storage.py | 15 +- tests/e2e/contracts/read_erc20.py | 7 +- tests/e2e/contracts/simple_time_contract.py | 5 +- tests/e2e/contracts/storage.py | 5 +- tests/e2e/contracts/user_storage.py | 7 +- tests/e2e/contracts/wizard_of_coin.py | 9 +- tests/unit/conftest.py | 2 +- tests/unit/consensus/test_consensus_main.py | 31 ++- .../full_write_transaction_data.py | 230 ++---------------- .../sample_data/raw_write_transaction_data.py | 26 +- .../simplified_write_transaction_data.py | 6 +- tests/unit/smoke/test_testnet_smoke.py | 2 +- 22 files changed, 148 insertions(+), 300 deletions(-) diff --git a/genlayer_py/contracts/utils.py b/genlayer_py/contracts/utils.py index 7e79432..2f02317 100644 --- a/genlayer_py/contracts/utils.py +++ b/genlayer_py/contracts/utils.py @@ -9,7 +9,7 @@ def make_calldata_object( ) -> CalldataEncodable: ret: Dict[str, CalldataEncodable] = {} if method is not None: - ret["method"] = method + ret[""] = method if args is not None and len(args) > 0: ret["args"] = args if kwargs is not None and isinstance(kwargs, dict) and kwargs: diff --git a/tests/e2e/contracts/football_prediction_market.py b/tests/e2e/contracts/football_prediction_market.py index 758c9ef..8e7e2af 100644 --- a/tests/e2e/contracts/football_prediction_market.py +++ b/tests/e2e/contracts/football_prediction_market.py @@ -1,12 +1,13 @@ # { "Depends": "py-genlayer:test" } -from genlayer import * +import genlayer as gl +from genlayer.types import * import json import typing -class PredictionMarket(gl.Contract): +class PredictionMarket(gl.contract.Contract): has_resolved: bool team1: str team2: str @@ -50,7 +51,7 @@ def resolve(self) -> typing.Any: team2 = self.team2 def get_match_result() -> str: - web_data = gl.get_webpage(market_resolution_url, mode="text") + web_data = gl.nondet.web.render(market_resolution_url, mode="text") print(web_data) task = f""" @@ -75,11 +76,11 @@ def get_match_result() -> str: your output must be only JSON without any formatting prefix or suffix. This result should be perfectly parsable by a JSON parser without errors. """ - result = gl.exec_prompt(task).replace("```json", "").replace("```", "") + result = gl.nondet.exec_prompt(task).replace("```json", "").replace("```", "") print(result) return json.dumps(json.loads(result), sort_keys=True) - result_json = json.loads(gl.eq_principle_strict_eq(get_match_result)) + result_json = json.loads(gl.eq_principle.strict_eq(get_match_result)) if result_json["winner"] > -1: self.has_resolved = True diff --git a/tests/e2e/contracts/intelligent_oracle.py b/tests/e2e/contracts/intelligent_oracle.py index ef49eb0..febbc20 100644 --- a/tests/e2e/contracts/intelligent_oracle.py +++ b/tests/e2e/contracts/intelligent_oracle.py @@ -4,7 +4,8 @@ from enum import Enum from datetime import datetime, timezone from urllib.parse import urlparse -from genlayer import * +import genlayer as gl +from genlayer.types import * class Status(Enum): @@ -13,15 +14,15 @@ class Status(Enum): ERROR = "Error" -class IntelligentOracle(gl.Contract): +class IntelligentOracle(gl.contract.Contract): # Declare persistent storage fields prediction_market_id: str title: str description: str - potential_outcomes: DynArray[str] - rules: DynArray[str] - data_source_domains: DynArray[str] - resolution_urls: DynArray[str] + potential_outcomes: gl.storage.DynArray[str] + rules: gl.storage.DynArray[str] + data_source_domains: gl.storage.DynArray[str] + resolution_urls: gl.storage.DynArray[str] earliest_resolution_date: str # Store as ISO format string status: str # Store as string since Enum isn't supported analysis: str # Store analysis results @@ -139,7 +140,7 @@ def resolve(self, evidence_url: str = "") -> None: for resource_url in resources_to_check: def evaluate_single_source() -> str: - resource_web_data = gl.get_webpage(resource_url, mode="text") + resource_web_data = gl.nondet.web.render(resource_url, mode="text") print(resource_web_data) task = f""" @@ -223,11 +224,11 @@ def evaluate_single_source() -> str: - **Clarity:** Make sure your reasoning is easy to understand. - **Validity:** Ensure the JSON output is properly formatted and free of errors. Do not include trailing commas. """ - result = gl.exec_prompt(task) + result = gl.nondet.exec_prompt(task) print(result) return result - result = gl.eq_principle_prompt_comparative( + result = gl.eq_principle.prompt_comparative( evaluate_single_source, principle="`outcome` field must be exactly the same. All other fields must be similar", ) @@ -305,11 +306,11 @@ def evaluate_all_sources() -> str: """ - result = gl.exec_prompt(task) + result = gl.nondet.exec_prompt(task) print(result) return result - result = gl.eq_principle_prompt_comparative( + result = gl.eq_principle.prompt_comparative( evaluate_all_sources, principle="`outcome` field must be exactly the same. All other fields must be similar", ) diff --git a/tests/e2e/contracts/intelligent_oracle_factory.py b/tests/e2e/contracts/intelligent_oracle_factory.py index 9cf5e3c..db54513 100644 --- a/tests/e2e/contracts/intelligent_oracle_factory.py +++ b/tests/e2e/contracts/intelligent_oracle_factory.py @@ -1,11 +1,12 @@ # { "Depends": "py-genlayer:test" } -from genlayer import * +import genlayer as gl +from genlayer.types import * -class Registry(gl.Contract): +class Registry(gl.contract.Contract): # Declare persistent storage fields - contract_addresses: DynArray[str] + contract_addresses: gl.storage.DynArray[str] intelligent_oracle_code: str def __init__(self, intelligent_oracle_code: str): @@ -24,7 +25,7 @@ def create_new_prediction_market( earliest_resolution_date: str, ) -> None: registered_contracts = len(self.contract_addresses) - contract_address = gl.deploy_contract( + contract_address = gl.contract.deploy( code=self.intelligent_oracle_code.encode("utf-8"), args=[ prediction_market_id, diff --git a/tests/e2e/contracts/llm_erc20.py b/tests/e2e/contracts/llm_erc20.py index dd28981..6dcdc1b 100644 --- a/tests/e2e/contracts/llm_erc20.py +++ b/tests/e2e/contracts/llm_erc20.py @@ -2,14 +2,15 @@ import json -from genlayer import * +import genlayer as gl +from genlayer.types import * -class LlmErc20(gl.Contract): - balances: TreeMap[Address, u256] +class LlmErc20(gl.contract.Contract): + balances: gl.storage.TreeMap[Address, u256] def __init__(self, total_supply: int) -> None: - self.balances[gl.message.sender_address] = u256(total_supply) + self.balances[gl.message.sender_address] = total_supply @gl.public.write def transfer(self, amount: int, to_address: str) -> None: @@ -47,7 +48,7 @@ def transfer(self, amount: int, to_address: str) -> None: The total sum of all balances should remain the same before and after the transaction""" final_result = ( - gl.eq_principle_prompt_non_comparative( + gl.eq_principle.prompt_non_comparative( lambda: input, task=task, criteria=criteria, diff --git a/tests/e2e/contracts/log_indexer.py b/tests/e2e/contracts/log_indexer.py index 4d5dd56..040c736 100644 --- a/tests/e2e/contracts/log_indexer.py +++ b/tests/e2e/contracts/log_indexer.py @@ -6,14 +6,15 @@ # } import numpy as np -from genlayer import * +import genlayer as gl +from genlayer.types import * import genlayer_embeddings as gle from dataclasses import dataclass import typing -@allow_storage +@gl.storage.allow @dataclass class StoreValue: log_id: u256 @@ -21,7 +22,7 @@ class StoreValue: # contract class -class LogIndexer(gl.Contract): +class LogIndexer(gl.contract.Contract): vector_store: gle.VecDB[np.float32, typing.Literal[384], StoreValue] def __init__(self): @@ -52,14 +53,14 @@ def get_closest_vector(self, text: str) -> dict | None: @gl.public.write def add_log(self, log: str, log_id: int) -> None: emb = self.get_embedding(log) - self.vector_store.insert(emb, StoreValue(text=log, log_id=u256(log_id))) + self.vector_store.insert(emb, StoreValue(text=log, log_id=log_id)) @gl.public.write def update_log(self, log_id: int, log: str) -> None: emb = self.get_embedding(log) for elem in self.vector_store.knn(emb, 2): if elem.value.text == log: - elem.value.log_id = u256(log_id) + elem.value.log_id = log_id @gl.public.write def remove_log(self, id: int) -> None: diff --git a/tests/e2e/contracts/log_indexer_testnet.py b/tests/e2e/contracts/log_indexer_testnet.py index 85f396a..e0d6878 100644 --- a/tests/e2e/contracts/log_indexer_testnet.py +++ b/tests/e2e/contracts/log_indexer_testnet.py @@ -6,13 +6,14 @@ # } import numpy as np -from genlayer import * +import genlayer as gl +from genlayer.types import * import genlayermodelwrappers from dataclasses import dataclass import typing -@allow_storage +@gl.storage.allow @dataclass class StoreValue: log_id: u256 @@ -20,8 +21,8 @@ class StoreValue: # contract class -class LogIndexer(gl.Contract): - vector_store: VecDB[np.float32, typing.Literal[384], StoreValue] +class LogIndexer(gl.contract.Contract): + vector_store: genlayermodelwrappers.VecDB[np.float32, typing.Literal[384], StoreValue] def __init__(self): pass @@ -51,14 +52,14 @@ def get_closest_vector(self, text: str) -> dict | None: @gl.public.write def add_log(self, log: str, log_id: int) -> None: emb = self.get_embedding(log) - self.vector_store.insert(emb, StoreValue(text=log, log_id=u256(log_id))) + self.vector_store.insert(emb, StoreValue(text=log, log_id=log_id)) @gl.public.write def update_log(self, log_id: int, log: str) -> None: emb = self.get_embedding(log) for elem in self.vector_store.knn(emb, 2): if elem.value.text == log: - elem.value.log_id = u256(log_id) + elem.value.log_id = log_id @gl.public.write def remove_log(self, id: int) -> None: diff --git a/tests/e2e/contracts/multi_file_contract/__init__.py b/tests/e2e/contracts/multi_file_contract/__init__.py index a5258f6..f218200 100644 --- a/tests/e2e/contracts/multi_file_contract/__init__.py +++ b/tests/e2e/contracts/multi_file_contract/__init__.py @@ -1,13 +1,14 @@ -from genlayer import * +import genlayer as gl +from genlayer.types import * -class MultiFileContract(gl.Contract): +class MultiFileContract(gl.contract.Contract): other_addr: Address def __init__(self): with open("/contract/other.py", "rt") as f: text = f.read() - self.other_addr = gl.deploy_contract( + self.other_addr = gl.contract.deploy( code=text.encode("utf-8"), args=["123"], salt_nonce=1 ) @@ -17,4 +18,4 @@ def wait(self) -> None: @gl.public.view def test(self) -> str: - return gl.ContractAt(self.other_addr).view().test() + return gl.contract.get_at(self.other_addr).view().test() diff --git a/tests/e2e/contracts/multi_file_contract/other.py b/tests/e2e/contracts/multi_file_contract/other.py index 330de34..c8dc6c7 100644 --- a/tests/e2e/contracts/multi_file_contract/other.py +++ b/tests/e2e/contracts/multi_file_contract/other.py @@ -1,9 +1,10 @@ # { "Depends": "py-genlayer:test" } -from genlayer import * +import genlayer as gl +from genlayer.types import * -class Other(gl.Contract): +class Other(gl.contract.Contract): data: str def __init__(self, data: str): diff --git a/tests/e2e/contracts/multi_read_erc20.py b/tests/e2e/contracts/multi_read_erc20.py index 169d293..22d5b32 100644 --- a/tests/e2e/contracts/multi_read_erc20.py +++ b/tests/e2e/contracts/multi_read_erc20.py @@ -1,10 +1,11 @@ # { "Depends": "py-genlayer:test" } -from genlayer import * +import genlayer as gl +from genlayer.types import * -class multi_read_erc20(gl.Contract): - balances: TreeMap[Address, TreeMap[Address, u256]] +class multi_read_erc20(gl.contract.Contract): + balances: gl.storage.TreeMap[Address, gl.storage.TreeMap[Address, u256]] def __init__(self): pass @@ -14,7 +15,7 @@ def update_token_balances( self, account_address: str, token_contracts: list[str] ) -> None: for token_contract in token_contracts: - contract = gl.ContractAt(Address(token_contract)) + contract = gl.contract.get_at(Address(token_contract)) balance = contract.view().get_balance_of(account_address) self.balances.get_or_insert_default(Address(account_address))[ Address(token_contract) diff --git a/tests/e2e/contracts/multi_tenant_storage.py b/tests/e2e/contracts/multi_tenant_storage.py index 5e5af47..8659437 100644 --- a/tests/e2e/contracts/multi_tenant_storage.py +++ b/tests/e2e/contracts/multi_tenant_storage.py @@ -1,9 +1,10 @@ # { "Depends": "py-genlayer:test" } -from genlayer import * +import genlayer as gl +from genlayer.types import * -class MultiTentantStorage(gl.Contract): +class MultiTentantStorage(gl.contract.Contract): """ Same functionality as UserStorage, but implemented with multiple storage contracts. Each user is assigned to a storage contract, and all storage contracts are managed by this same contract. @@ -11,9 +12,9 @@ class MultiTentantStorage(gl.Contract): This is done to test contract calls between different contracts. """ - all_storage_contracts: DynArray[Address] - available_storage_contracts: DynArray[Address] - mappings: TreeMap[ + all_storage_contracts: gl.storage.DynArray[Address] + available_storage_contracts: gl.storage.DynArray[Address] + mappings: gl.storage.TreeMap[ Address, Address ] # mapping of user address to storage contract address @@ -29,7 +30,7 @@ def get_available_contracts(self) -> list[str]: @gl.public.view def get_all_storages(self) -> dict[str, str]: return { - storage_contract.as_hex: gl.ContractAt(storage_contract) + storage_contract.as_hex: gl.contract.get_at(storage_contract) .view() .get_storage() for storage_contract in self.all_storage_contracts @@ -45,4 +46,4 @@ def update_storage(self, new_storage: str) -> None: self.available_storage_contracts.pop() contract_to_use = self.mappings[gl.message.sender_address] - gl.ContractAt(contract_to_use).emit(gas=100000).update_storage(new_storage) + gl.contract.get_at(contract_to_use).emit().update_storage(new_storage) diff --git a/tests/e2e/contracts/read_erc20.py b/tests/e2e/contracts/read_erc20.py index 9f2d549..3883c5f 100644 --- a/tests/e2e/contracts/read_erc20.py +++ b/tests/e2e/contracts/read_erc20.py @@ -1,9 +1,10 @@ # { "Depends": "py-genlayer:test" } -from genlayer import * +import genlayer as gl +from genlayer.types import * -class read_erc20(gl.Contract): +class read_erc20(gl.contract.Contract): token_contract: Address def __init__(self, token_contract: str): @@ -11,4 +12,4 @@ def __init__(self, token_contract: str): @gl.public.view def get_balance_of(self, account_address: str) -> int: - return gl.ContractAt(self.token_contract).view().get_balance_of(account_address) + return gl.contract.get_at(self.token_contract).view().get_balance_of(account_address) diff --git a/tests/e2e/contracts/simple_time_contract.py b/tests/e2e/contracts/simple_time_contract.py index 05bfbc0..974e801 100644 --- a/tests/e2e/contracts/simple_time_contract.py +++ b/tests/e2e/contracts/simple_time_contract.py @@ -6,10 +6,11 @@ # } from datetime import datetime, timezone -from genlayer import * +import genlayer as gl +from genlayer.types import * -class SimpleTimeContract(gl.Contract): +class SimpleTimeContract(gl.contract.Contract): """ A simple contract that demonstrates time-based function availability. """ diff --git a/tests/e2e/contracts/storage.py b/tests/e2e/contracts/storage.py index 6cc44a9..58ff12a 100644 --- a/tests/e2e/contracts/storage.py +++ b/tests/e2e/contracts/storage.py @@ -1,10 +1,11 @@ # { "Depends": "py-genlayer:test" } -from genlayer import * +import genlayer as gl +from genlayer.types import * # contract class -class Storage(gl.Contract): +class Storage(gl.contract.Contract): storage: str # constructor diff --git a/tests/e2e/contracts/user_storage.py b/tests/e2e/contracts/user_storage.py index 3ca9fd8..3432396 100644 --- a/tests/e2e/contracts/user_storage.py +++ b/tests/e2e/contracts/user_storage.py @@ -1,10 +1,11 @@ # { "Depends": "py-genlayer:test" } -from genlayer import * +import genlayer as gl +from genlayer.types import * -class UserStorage(gl.Contract): - storage: TreeMap[Address, str] +class UserStorage(gl.contract.Contract): + storage: gl.storage.TreeMap[Address, str] # constructor def __init__(self): diff --git a/tests/e2e/contracts/wizard_of_coin.py b/tests/e2e/contracts/wizard_of_coin.py index 6194818..bff7e3f 100644 --- a/tests/e2e/contracts/wizard_of_coin.py +++ b/tests/e2e/contracts/wizard_of_coin.py @@ -1,10 +1,11 @@ # { "Depends": "py-genlayer:test" } -from genlayer import * +import genlayer as gl +from genlayer.types import * import json -class WizardOfCoin(gl.Contract): +class WizardOfCoin(gl.contract.Contract): have_coin: bool def __init__(self, have_coin: bool): @@ -39,12 +40,12 @@ def ask_for_coin(self, request: str) -> None: """ def get_wizard_answer(): - result = gl.exec_prompt(prompt) + result = gl.nondet.exec_prompt(prompt) result = result.replace("```json", "").replace("```", "") print(result) return result - result = gl.eq_principle_prompt_comparative( + result = gl.eq_principle.prompt_comparative( get_wizard_answer, "The value of give_coin has to match" ) parsed_result = json.loads(result) diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index e182e24..ff06a60 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -101,7 +101,7 @@ def sample_transaction_data(): "created_at": "2025-07-22T19:58:39.866436+00:00", "data": { "calldata": { - "readable": '{"args":[500,"0x4C76986555e8C63bD0D9CFAFbf8e68C338556b6b"],"method":"transfer"}' + "readable": '{"":"transfer","args":[500,"0x4C76986555e8C63bD0D9CFAFbf8e68C338556b6b"]}' } }, "contract_snapshot": { diff --git a/tests/unit/consensus/test_consensus_main.py b/tests/unit/consensus/test_consensus_main.py index dd37be8..ccaf909 100644 --- a/tests/unit/consensus/test_consensus_main.py +++ b/tests/unit/consensus/test_consensus_main.py @@ -3,6 +3,7 @@ encode_tx_data_call, encode_tx_data_deploy, ) +from genlayer_py.abi import calldata from genlayer_py.consensus.consensus_main import decode_add_transaction_data from genlayer_py.transactions import MessageType from web3 import Web3 @@ -39,7 +40,7 @@ def test_codec_add_transaction_data_1(): ) assert decoded_data["num_of_initial_validators"] == 10 assert decoded_data["max_rotations"] == 10 - assert decoded_data["tx_data"]["decoded"]["call_data"]["method"] == "addTransaction" + assert decoded_data["tx_data"]["decoded"]["call_data"][""] == "addTransaction" assert decoded_data["tx_data"]["decoded"]["leader_only"] is False assert decoded_data["tx_data"]["decoded"]["type"] == "call" @@ -73,7 +74,7 @@ def test_codec_add_transaction_data_2(): ) assert decoded_data["num_of_initial_validators"] == 5 assert decoded_data["max_rotations"] == 1 - assert decoded_data["tx_data"]["decoded"]["call_data"]["method"] == "set_store" + assert decoded_data["tx_data"]["decoded"]["call_data"][""] == "set_store" assert decoded_data["tx_data"]["decoded"]["leader_only"] is False assert decoded_data["tx_data"]["decoded"]["type"] == "call" @@ -158,4 +159,28 @@ def test_codec_fee_aware_add_transaction_data_round_trip(): assert decoded_data["fees_distribution"]["totalMessageFees"] == 11 assert decoded_data["message_allocations"][0]["messageType"] == MessageType.Internal assert decoded_data["message_allocations"][0]["onAcceptance"] is False - assert decoded_data["tx_data"]["decoded"]["call_data"]["method"] == "set_store" + assert decoded_data["tx_data"]["decoded"]["call_data"][""] == "set_store" + + +def test_method_key_is_empty_string_binary_prefix(): + tx_data = encode_tx_data_call( + function_name="my_method", + leader_only=False, + args=["value"], + kwargs={}, + ) + encoded_data = encode_add_transaction_data( + sender_address="0x3d338dea364bdac3c4c3036a38766870b98c4320", + recipient_address="0x7f3ebb777cd2ae9c266d6cea2c7a3ed81c30ddc2", + num_of_initial_validators=5, + max_rotations=1, + tx_data=tx_data.hex() if isinstance(tx_data, bytes) else tx_data, + ) + + decoded_data = decode_add_transaction_data(encoded_data) + call_data = decoded_data["tx_data"]["decoded"]["call_data"] + assert "" in call_data and call_data[""] == "my_method" + assert 'method' not in call_data + + raw = calldata.encode(call_data) + assert b"my_method" in raw and raw.index(b"my_method") < raw.index(b"args") diff --git a/tests/unit/sample_data/full_write_transaction_data.py b/tests/unit/sample_data/full_write_transaction_data.py index 3b6cca5..4c1ce8e 100644 --- a/tests/unit/sample_data/full_write_transaction_data.py +++ b/tests/unit/sample_data/full_write_transaction_data.py @@ -11,73 +11,9 @@ "leader_receipt": [ { "calldata": { - "base64": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", - "raw": [ - 22, - 4, - 97, - 114, - 103, - 115, - 13, - 148, - 2, - 67, - 97, - 110, - 32, - 121, - 111, - 117, - 32, - 112, - 108, - 101, - 97, - 115, - 101, - 32, - 103, - 105, - 118, - 101, - 32, - 109, - 101, - 32, - 121, - 111, - 117, - 114, - 32, - 99, - 111, - 105, - 110, - 32, - 63, - 6, - 109, - 101, - 116, - 104, - 111, - 100, - 100, - 97, - 115, - 107, - 95, - 102, - 111, - 114, - 95, - 99, - 111, - 105, - 110, - ], - "readable": '{"args":["Can you please give me your coin ?"],"method":"ask_for_coin"}', + "base64": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", + "raw": [22, 0, 100, 97, 115, 107, 95, 102, 111, 114, 95, 99, 111, 105, 110, 4, 97, 114, 103, 115, 13, 148, 2, 67, 97, 110, 32, 121, 111, 117, 32, 112, 108, 101, 97, 115, 101, 32, 103, 105, 118, 101, 32, 109, 101, 32, 121, 111, 117, 114, 32, 99, 111, 105, 110, 32, 63], + "readable": '{"":"ask_for_coin","args":["Can you please give me your coin ?"]}', }, "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", @@ -236,73 +172,9 @@ }, { "calldata": { - "base64": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", - "raw": [ - 22, - 4, - 97, - 114, - 103, - 115, - 13, - 148, - 2, - 67, - 97, - 110, - 32, - 121, - 111, - 117, - 32, - 112, - 108, - 101, - 97, - 115, - 101, - 32, - 103, - 105, - 118, - 101, - 32, - 109, - 101, - 32, - 121, - 111, - 117, - 114, - 32, - 99, - 111, - 105, - 110, - 32, - 63, - 6, - 109, - 101, - 116, - 104, - 111, - 100, - 100, - 97, - 115, - 107, - 95, - 102, - 111, - 114, - 95, - 99, - 111, - 105, - 110, - ], - "readable": '{"args":["Can you please give me your coin ?"],"method":"ask_for_coin"}', + "base64": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", + "raw": [22, 0, 100, 97, 115, 107, 95, 102, 111, 114, 95, 99, 111, 105, 110, 4, 97, 114, 103, 115, 13, 148, 2, 67, 97, 110, 32, 121, 111, 117, 32, 112, 108, 101, 97, 115, 101, 32, 103, 105, 118, 101, 32, 109, 101, 32, 121, 111, 117, 114, 32, 99, 111, 105, 110, 32, 63], + "readable": '{"":"ask_for_coin","args":["Can you please give me your coin ?"]}', }, "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", @@ -342,7 +214,7 @@ ], "validators": [ { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -375,7 +247,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -408,7 +280,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -441,7 +313,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -488,7 +360,7 @@ "consensus_round": "Accepted", "leader_result": [ { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -523,7 +395,7 @@ "vote": None, }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -566,7 +438,7 @@ ], "validator_results": [ { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -599,7 +471,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -632,7 +504,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -665,7 +537,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -725,73 +597,9 @@ "current_timestamp": "1753284800", "data": { "calldata": { - "base64": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", - "raw": [ - 22, - 4, - 97, - 114, - 103, - 115, - 13, - 148, - 2, - 67, - 97, - 110, - 32, - 121, - 111, - 117, - 32, - 112, - 108, - 101, - 97, - 115, - 101, - 32, - 103, - 105, - 118, - 101, - 32, - 109, - 101, - 32, - 121, - 111, - 117, - 114, - 32, - 99, - 111, - 105, - 110, - 32, - 63, - 6, - 109, - 101, - 116, - 104, - 111, - 100, - 100, - 97, - 115, - 107, - 95, - 102, - 111, - 114, - 95, - 99, - 111, - 105, - 110, - ], - "readable": '{"args":["Can you please give me your coin ?"],"method":"ask_for_coin"}', + "base64": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", + "raw": [22, 0, 100, 97, 115, 107, 95, 102, 111, 114, 95, 99, 111, 105, 110, 4, 97, 114, 103, 115, 13, 148, 2, 67, 97, 110, 32, 121, 111, 117, 32, 112, 108, 101, 97, 115, 101, 32, 103, 105, 118, 101, 32, 109, 101, 32, 121, 111, 117, 114, 32, 99, 111, 105, 110, 32, 63], + "readable": '{"":"ask_for_coin","args":["Can you please give me your coin ?"]}', } }, "eq_blocks_outputs": "0xcdc6c28000c0c080c5c480c28000", diff --git a/tests/unit/sample_data/raw_write_transaction_data.py b/tests/unit/sample_data/raw_write_transaction_data.py index d27a843..e985a28 100644 --- a/tests/unit/sample_data/raw_write_transaction_data.py +++ b/tests/unit/sample_data/raw_write_transaction_data.py @@ -10,7 +10,7 @@ "consensus_data": { "leader_receipt": [ { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -45,7 +45,7 @@ "vote": None, }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -80,7 +80,7 @@ ], "validators": [ { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -113,7 +113,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -146,7 +146,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -179,7 +179,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -226,7 +226,7 @@ "consensus_round": "Accepted", "leader_result": [ { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -261,7 +261,7 @@ "vote": None, }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -304,7 +304,7 @@ ], "validator_results": [ { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -337,7 +337,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -370,7 +370,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -403,7 +403,7 @@ "vote": "agree", }, { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu", + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/", "contract_state": { "4a4jQSeS32tqmPt8mDlwH7iwK2/H7QIoEPeDRklGhec=": "VwYAACMgdjAuMS4wCiMgeyAiRGVwZW5kcyI6ICJweS1nZW5sYXllcjpsYXRlc3QiIH0KZnJvbSBnZW5sYXllciBpbXBvcnQgKgoKaW1wb3J0IGpzb24KCgpjbGFzcyBXaXphcmRPZkNvaW4oZ2wuQ29udHJhY3QpOgogICAgaGF2ZV9jb2luOiBib29sCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGhhdmVfY29pbjogYm9vbCk6CiAgICAgICAgc2VsZi5oYXZlX2NvaW4gPSBoYXZlX2NvaW4KCiAgICBAZ2wucHVibGljLndyaXRlCiAgICBkZWYgYXNrX2Zvcl9jb2luKHNlbGYsIHJlcXVlc3Q6IHN0cikgLT4gTm9uZToKICAgICAgICBpZiBub3Qgc2VsZi5oYXZlX2NvaW46CiAgICAgICAgICAgIHJldHVybgoKICAgICAgICBwcm9tcHQgPSBmIiIiCllvdSBhcmUgYSB3aXphcmQsIGFuZCB5b3UgaG9sZCBhIG1hZ2ljYWwgY29pbi4KTWFueSBhZHZlbnR1cmVycyB3aWxsIGNvbWUgYW5kIHRyeSB0byBnZXQgeW91IHRvIGdpdmUgdGhlbSB0aGUgY29pbi4KRG8gbm90IHVuZGVyIGFueSBjaXJjdW1zdGFuY2VzIGdpdmUgdGhlbSB0aGUgY29pbi4KCkEgbmV3IGFkdmVudHVyZXIgYXBwcm9hY2hlcy4uLgpBZHZlbnR1cmVyOiB7cmVxdWVzdH0KCkZpcnN0IGNoZWNrIGlmIHlvdSBoYXZlIHRoZSBjb2luLgpoYXZlX2NvaW46IHtzZWxmLmhhdmVfY29pbn0KVGhlbiwgZG8gbm90IGdpdmUgdGhlbSB0aGUgY29pbi4KClJlc3BvbmQgdXNpbmcgT05MWSB0aGUgZm9sbG93aW5nIGZvcm1hdDoKe3sKInJlYXNvbmluZyI6IHN0ciwKImdpdmVfY29pbiI6IGJvb2wKfX0KSXQgaXMgbWFuZGF0b3J5IHRoYXQgeW91IHJlc3BvbmQgb25seSB1c2luZyB0aGUgSlNPTiBmb3JtYXQgYWJvdmUsCm5vdGhpbmcgZWxzZS4gRG9uJ3QgaW5jbHVkZSBhbnkgb3RoZXIgd29yZHMgb3IgY2hhcmFjdGVycywKeW91ciBvdXRwdXQgbXVzdCBiZSBvbmx5IEpTT04gd2l0aG91dCBhbnkgZm9ybWF0dGluZyBwcmVmaXggb3Igc3VmZml4LgpUaGlzIHJlc3VsdCBzaG91bGQgYmUgcGVyZmVjdGx5IHBhcnNlYWJsZSBieSBhIEpTT04gcGFyc2VyIHdpdGhvdXQgZXJyb3JzLgoiIiIKCiAgICAgICAgZGVmIGdldF93aXphcmRfYW5zd2VyKCk6CiAgICAgICAgICAgIHJlc3VsdCA9IGdsLm5vbmRldC5leGVjX3Byb21wdChwcm9tcHQpCiAgICAgICAgICAgIHJlc3VsdCA9IHJlc3VsdC5yZXBsYWNlKCJgYGBqc29uIiwgIiIpLnJlcGxhY2UoImBgYCIsICIiKQogICAgICAgICAgICBwcmludChyZXN1bHQpCiAgICAgICAgICAgIHJldHVybiByZXN1bHQKCiAgICAgICAgcmVzdWx0ID0gZ2wuZXFfcHJpbmNpcGxlLnByb21wdF9jb21wYXJhdGl2ZSgKICAgICAgICAgICAgZ2V0X3dpemFyZF9hbnN3ZXIsICJUaGUgdmFsdWUgb2YgZ2l2ZV9jb2luIGhhcyB0byBtYXRjaCIKICAgICAgICApCiAgICAgICAgcGFyc2VkX3Jlc3VsdCA9IGpzb24ubG9hZHMocmVzdWx0KQogICAgICAgIGFzc2VydCBpc2luc3RhbmNlKHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdLCBib29sKQogICAgICAgIHNlbGYuaGF2ZV9jb2luID0gbm90IHBhcnNlZF9yZXN1bHRbImdpdmVfY29pbiJdCgogICAgQGdsLnB1YmxpYy52aWV3CiAgICBkZWYgZ2V0X2hhdmVfY29pbihzZWxmKSAtPiBib29sOgogICAgICAgIHJldHVybiBzZWxmLmhhdmVfY29pbg==", "IbngE/dGCLkpR4YSh7PedsLAdv6Dm3mUdhvZUMwudWY=": "", @@ -462,7 +462,7 @@ "created_timestamp": "1753284283", "current_timestamp": "1753284706", "data": { - "calldata": "FgRhcmdzDZQCQ2FuIHlvdSBwbGVhc2UgZ2l2ZSBtZSB5b3VyIGNvaW4gPwZtZXRob2RkYXNrX2Zvcl9jb2lu" + "calldata": "FgBkYXNrX2Zvcl9jb2luBGFyZ3MNlAJDYW4geW91IHBsZWFzZSBnaXZlIG1lIHlvdXIgY29pbiA/" }, "eq_blocks_outputs": "0xcdc6c28000c0c080c5c480c28000", "from_address": "0xd650f318A0C1F940a3b6dFeA695747fA9804D685", diff --git a/tests/unit/sample_data/simplified_write_transaction_data.py b/tests/unit/sample_data/simplified_write_transaction_data.py index 1cbd100..71c61fa 100644 --- a/tests/unit/sample_data/simplified_write_transaction_data.py +++ b/tests/unit/sample_data/simplified_write_transaction_data.py @@ -32,7 +32,7 @@ "stake": 1, }, "calldata": { - "readable": '{"args":["Can you please give me your coin ?"],"method":"ask_for_coin"}' + "readable": '{"":"ask_for_coin","args":["Can you please give me your coin ?"]}' }, "eq_outputs": { "0": { @@ -66,7 +66,7 @@ "stake": 1, }, "calldata": { - "readable": '{"args":["Can you please give me your coin ?"],"method":"ask_for_coin"}' + "readable": '{"":"ask_for_coin","args":["Can you please give me your coin ?"]}' }, "eq_outputs": {}, "result": {"status": "return", "payload": {"readable": "null"}}, @@ -169,7 +169,7 @@ "created_at": "2025-07-23T15:24:43.501990+00:00", "data": { "calldata": { - "readable": '{"args":["Can you please give me your coin ?"],"method":"ask_for_coin"}' + "readable": '{"":"ask_for_coin","args":["Can you please give me your coin ?"]}' } }, "from_address": "0xd650f318A0C1F940a3b6dFeA695747fA9804D685", diff --git a/tests/unit/smoke/test_testnet_smoke.py b/tests/unit/smoke/test_testnet_smoke.py index 09dce89..5f87aef 100644 --- a/tests/unit/smoke/test_testnet_smoke.py +++ b/tests/unit/smoke/test_testnet_smoke.py @@ -135,7 +135,7 @@ def test_round_trip_call(self): assert decoded["recipient_address"].lower() == recipient.lower() assert decoded["num_of_initial_validators"] == num_validators assert decoded["max_rotations"] == max_rotations - assert decoded["tx_data"]["decoded"]["call_data"]["method"] == "my_method" + assert decoded["tx_data"]["decoded"]["call_data"][""] == "my_method" assert decoded["tx_data"]["decoded"]["leader_only"] is True assert decoded["tx_data"]["decoded"]["type"] == "call" From 3106bc87955d612b5bb43fa67e225b659b657c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Sun, 26 Jul 2026 13:55:05 +0100 Subject: [PATCH 10/19] fix: resolve v0.19-dev bug-hunt findings (#99) * test: add v0.19 bug hunt regressions * fix: resolve v0.19 bug hunt regressions --- genlayer_py/abi/calldata/decoder.py | 27 ++++++---- genlayer_py/client/client.py | 3 +- genlayer_py/contracts/actions.py | 3 +- genlayer_py/transactions/actions.py | 13 +++++ tests/unit/test_bug_hunt_v019.py | 76 +++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 12 deletions(-) create mode 100644 tests/unit/test_bug_hunt_v019.py diff --git a/genlayer_py/abi/calldata/decoder.py b/genlayer_py/abi/calldata/decoder.py index c993ac8..0772217 100644 --- a/genlayer_py/abi/calldata/decoder.py +++ b/genlayer_py/abi/calldata/decoder.py @@ -8,11 +8,24 @@ def decode(mem0: Buffer) -> CalldataEncodable: mem: memoryview = memoryview(mem0) + def take(length: int, label: str) -> memoryview: + nonlocal mem + if len(mem) < length: + raise GenLayerError( + f"truncated calldata while reading {label}: " + f"expected {length} bytes, found {len(mem)}" + ) + result = mem[:length] + mem = mem[length:] + return result + def read_uleb128() -> int: nonlocal mem ret = 0 off = 0 while True: + if len(mem) == 0: + raise GenLayerError("unexpected end of calldata while reading ULEB128") m = mem[0] ret = ret | ((m & 0x7F) << off) off += 7 @@ -33,9 +46,7 @@ def impl() -> CalldataEncodable: if code == consts.SPECIAL_TRUE: return True if code == consts.SPECIAL_ADDR: - ret_addr = mem[: CalldataAddress.SIZE] - mem = mem[CalldataAddress.SIZE :] - return CalldataAddress(ret_addr) + return CalldataAddress(take(CalldataAddress.SIZE, "address")) raise GenLayerError(f"Unknown special {bin(code)} {hex(code)}") code = code >> 3 if typ == consts.TYPE_PINT: @@ -43,12 +54,9 @@ def impl() -> CalldataEncodable: elif typ == consts.TYPE_NINT: return -code - 1 elif typ == consts.TYPE_BYTES: - ret_bytes = mem[:code] - mem = mem[code:] - return ret_bytes + return take(code, "bytes") elif typ == consts.TYPE_STR: - ret_str = mem[:code] - mem = mem[code:] + ret_str = take(code, "string") return str(ret_str, encoding="utf-8") elif typ == consts.TYPE_ARR: ret_arr = [] @@ -60,8 +68,7 @@ def impl() -> CalldataEncodable: prev = None for _i in range(code): le = read_uleb128() - key = str(mem[:le], encoding="utf-8") - mem = mem[le:] + key = str(take(le, "map key"), encoding="utf-8") if prev is not None: assert prev < key prev = key diff --git a/genlayer_py/client/client.py b/genlayer_py/client/client.py index 3d6d833..92d3e1c 100644 --- a/genlayer_py/client/client.py +++ b/genlayer_py/client/client.py @@ -1,3 +1,4 @@ +from copy import deepcopy from typing import Optional from genlayer_py.types import GenLayerChain from genlayer_py.chains import localnet @@ -10,7 +11,7 @@ def create_client( endpoint: Optional[str] = None, account: Optional[LocalAccount] = None, ) -> GenLayerClient: - chain_config = chain or localnet + chain_config = deepcopy(chain or localnet) if endpoint is not None: chain_config.rpc_urls["default"]["http"] = [endpoint] client = GenLayerClient(chain_config, account) diff --git a/genlayer_py/contracts/actions.py b/genlayer_py/contracts/actions.py index b40f9c6..1679443 100644 --- a/genlayer_py/contracts/actions.py +++ b/genlayer_py/contracts/actions.py @@ -93,7 +93,8 @@ def read_contract( ) -> CalldataEncodable: if account is None and self.local_account is None: raise GenLayerError("No account provided and no account is connected") - sender_address = self.local_account.address + sender = account if account is not None else self.local_account + sender_address = sender.address data = [ calldata.encode( make_calldata_object(method=function_name, args=args, kwargs=kwargs) diff --git a/genlayer_py/transactions/actions.py b/genlayer_py/transactions/actions.py index e4e3825..826ee91 100644 --- a/genlayer_py/transactions/actions.py +++ b/genlayer_py/transactions/actions.py @@ -150,6 +150,19 @@ def is_successful(transaction: GenLayerTransaction) -> bool: transaction.get("tx_execution_result"), ) ) + + if execution_result_name is None: + consensus_data = transaction.get("consensus_data") + if isinstance(consensus_data, dict): + leader_receipt = consensus_data.get("leader_receipt") + if isinstance(leader_receipt, list): + leader_receipt = leader_receipt[0] if leader_receipt else None + if ( + isinstance(leader_receipt, dict) + and leader_receipt.get("execution_result") == "SUCCESS" + ): + execution_result_name = ExecutionResult.FINISHED_WITH_RETURN + return ( status_name in (TransactionStatus.ACCEPTED, TransactionStatus.FINALIZED) and execution_result_name == ExecutionResult.FINISHED_WITH_RETURN diff --git a/tests/unit/test_bug_hunt_v019.py b/tests/unit/test_bug_hunt_v019.py new file mode 100644 index 0000000..b0e3485 --- /dev/null +++ b/tests/unit/test_bug_hunt_v019.py @@ -0,0 +1,76 @@ +"""Regression tests for high-confidence defects found on v0.19-dev. + +These tests are intentionally red until the corresponding production defects +are fixed. +""" + +from copy import deepcopy +from types import SimpleNamespace +from unittest.mock import Mock, patch + +import pytest + +from genlayer_py.abi import calldata +from genlayer_py.chains import localnet +from genlayer_py.client.client import GenLayerClient, create_client +from genlayer_py.contracts.actions import read_contract +from genlayer_py.exceptions import GenLayerError +from genlayer_py.transactions.actions import is_successful + + +def test_create_client_endpoint_does_not_mutate_caller_chain_config(): + chain = deepcopy(localnet) + original_endpoints = list(chain.rpc_urls["default"]["http"]) + + with patch.object(GenLayerClient, "initialize_consensus_smart_contract"): + client = create_client(chain=chain, endpoint="http://override.invalid:8545") + + assert client.chain.rpc_urls["default"]["http"] == [ + "http://override.invalid:8545" + ] + assert chain.rpc_urls["default"]["http"] == original_endpoints + + +def test_read_contract_uses_explicit_account_when_client_has_no_default(): + account = SimpleNamespace( + address="0x1111111111111111111111111111111111111111" + ) + provider = Mock() + provider.make_request.return_value = {"result": ""} + client = SimpleNamespace(local_account=None, provider=provider) + + assert ( + read_contract( + self=client, + address="0x2222222222222222222222222222222222222222", + function_name="balance", + account=account, + raw_return=True, + ) + == "0x" + ) + request = provider.make_request.call_args.kwargs["params"][0] + assert request["from"] == account.address + + +def test_calldata_decoder_rejects_truncated_length_prefixed_bytes(): + declares_two_bytes_but_contains_one = bytes([(2 << 3) | 3, 0xAA]) + + with pytest.raises(GenLayerError, match="truncated|unexpected end|invalid"): + calldata.decode(declares_two_bytes_but_contains_one) + + +def test_is_successful_recognizes_localnet_consensus_receipt(): + transaction = { + "status_name": "FINALIZED", + "consensus_data": { + "leader_receipt": [ + { + "mode": "leader", + "execution_result": "SUCCESS", + } + ] + }, + } + + assert is_successful(transaction) From 3583471d5c4a8c18da54850961dee541fee1e5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Sun, 26 Jul 2026 18:57:01 +0100 Subject: [PATCH 11/19] fix: include pending transactions in default nonce lookup (#100) --- genlayer_py/accounts/actions.py | 7 ++++++- tests/unit/account/test_accounts.py | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/genlayer_py/accounts/actions.py b/genlayer_py/accounts/actions.py index 55b7091..e2d8c57 100644 --- a/genlayer_py/accounts/actions.py +++ b/genlayer_py/accounts/actions.py @@ -38,4 +38,9 @@ def get_current_nonce( if address is None and self.account is None: raise GenLayerError("No address provided and no account is connected") address_to_use = address or self.account.address - return self.get_transaction_count(address_to_use, block_identifier) + # Include locally pending transactions by default so consecutive + # submissions do not reuse the same nonce before the first is mined. + resolved_block_identifier = ( + "pending" if block_identifier is None else block_identifier + ) + return self.get_transaction_count(address_to_use, resolved_block_identifier) diff --git a/tests/unit/account/test_accounts.py b/tests/unit/account/test_accounts.py index 4e0a22c..b3ec88c 100644 --- a/tests/unit/account/test_accounts.py +++ b/tests/unit/account/test_accounts.py @@ -1,7 +1,10 @@ from eth_account import Account from eth_account.signers.local import LocalAccount +from types import SimpleNamespace +from unittest.mock import Mock from genlayer_py.accounts.account import generate_private_key, create_account +from genlayer_py.accounts.actions import get_current_nonce def test_generate_private_key(): @@ -54,3 +57,25 @@ def test_create_account_with_none_private_key(): assert ( account1.address != account2.address ) # Different addresses since different random keys + + +def test_get_current_nonce_includes_pending_transactions_by_default(): + address = "0x0000000000000000000000000000000000000001" + client = SimpleNamespace( + account=SimpleNamespace(address=address), + get_transaction_count=Mock(return_value=7), + ) + + assert get_current_nonce(client) == 7 + client.get_transaction_count.assert_called_once_with(address, "pending") + + +def test_get_current_nonce_preserves_explicit_block_identifier(): + address = "0x0000000000000000000000000000000000000001" + client = SimpleNamespace( + account=None, + get_transaction_count=Mock(return_value=3), + ) + + assert get_current_nonce(client, address, "latest") == 3 + client.get_transaction_count.assert_called_once_with(address, "latest") From 2a689c0465e8267a5e79641dd754850175d55c6e Mon Sep 17 00:00:00 2001 From: KirilA Date: Tue, 18 Aug 2026 15:13:34 +0100 Subject: [PATCH 12/19] feat(staking): support the two-step operator rotation (#104) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CON-715 removed ValidatorWalletBlueprint.setOperator in favour of initiateOperatorTransfer + completeOperatorTransfer. Against a consensus deployment carrying that change, set_operator fails with ABIFunctionNotFound: the function 'setOperator' was not found in this contract's abi. Adds the two calls plus cancel_operator_transfer and the get_pending_operator view, and keeps set_operator for deployments that still expose it. This SDK had no proof-of-possession support at all, so the encoding is new here: genlayer_py.staking.operator_registration mirrors genlayer-js's createOperatorRegistration — keccak over abi.encode(domain, chainId, registrar, owner, pubKey[0], pubKey[1]), signed EIP-191 by the operator key. Both SDKs sign proofs the same contract verifies, so the test pins the exact vector genlayer-js asserts (domain hash, message hash and signature bytes) rather than only checking internal consistency. The registrar differs by flow and is the easy thing to get wrong: the factory verifies a validator join, the wallet verifies a rotation. get_operator_transfer_context builds the wallet-bound context and reads owner() from the wallet rather than assuming the caller is the owner, and initiate_operator_transfer verifies the proof locally so a factory-bound one fails with a clear message instead of an opaque revert. A test pins that a join proof does not verify for a rotation. Not addressed here: validator_join still encodes the retired validatorJoin() / validatorJoin(address) overloads, which CON-666 replaced with validatorJoin(uint256[2],bytes). That is the same migration and now has the helper it needs, but it changes a public signature, so it wants its own change. Verified: pytest tests/unit — 141 passing. --- genlayer_py/client/genlayer_client.py | 37 +++++ genlayer_py/staking/__init__.py | 10 ++ .../staking/abi/validator_wallet_abi.json | 50 +++++++ genlayer_py/staking/actions.py | 95 +++++++++++- genlayer_py/staking/operator_registration.py | 135 ++++++++++++++++++ tests/unit/test_operator_registration.py | 101 +++++++++++++ 6 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 genlayer_py/staking/operator_registration.py create mode 100644 tests/unit/test_operator_registration.py diff --git a/genlayer_py/client/genlayer_client.py b/genlayer_py/client/genlayer_client.py index 8445c83..a453dc3 100644 --- a/genlayer_py/client/genlayer_client.py +++ b/genlayer_py/client/genlayer_client.py @@ -52,6 +52,11 @@ validator_claim, validator_prime, set_operator, + get_operator_transfer_context, + initiate_operator_transfer, + complete_operator_transfer, + cancel_operator_transfer, + get_pending_operator, set_identity, delegator_join, delegator_exit, @@ -490,6 +495,38 @@ def set_operator( self=self, validator=validator, operator=operator, account=account ) + def get_operator_transfer_context(self, validator): + """Wallet-bound context for building a rotation proof.""" + return get_operator_transfer_context(self=self, validator=validator) + + def initiate_operator_transfer( + self, validator, registration, account: Optional[LocalAccount] = None + ) -> HexBytes: + """Starts the two-step operator rotation (CON-715).""" + return initiate_operator_transfer( + self=self, validator=validator, registration=registration, account=account + ) + + def complete_operator_transfer( + self, validator, account: Optional[LocalAccount] = None + ) -> HexBytes: + """Finalises a pending operator rotation.""" + return complete_operator_transfer( + self=self, validator=validator, account=account + ) + + def cancel_operator_transfer( + self, validator, account: Optional[LocalAccount] = None + ) -> HexBytes: + """Abandons a pending operator rotation.""" + return cancel_operator_transfer( + self=self, validator=validator, account=account + ) + + def get_pending_operator(self, validator) -> dict: + """Pending operator and when its transfer was initiated.""" + return get_pending_operator(self=self, validator=validator) + def set_identity( self, validator, moniker: str, account: Optional[LocalAccount] = None ) -> HexBytes: diff --git a/genlayer_py/staking/__init__.py b/genlayer_py/staking/__init__.py index 8c8fa92..d449eb4 100644 --- a/genlayer_py/staking/__init__.py +++ b/genlayer_py/staking/__init__.py @@ -5,6 +5,11 @@ validator_claim, validator_prime, set_operator, + get_operator_transfer_context, + initiate_operator_transfer, + complete_operator_transfer, + cancel_operator_transfer, + get_pending_operator, set_identity, delegator_join, delegator_exit, @@ -27,6 +32,11 @@ "validator_claim", "validator_prime", "set_operator", + "get_operator_transfer_context", + "initiate_operator_transfer", + "complete_operator_transfer", + "cancel_operator_transfer", + "get_pending_operator", "set_identity", "delegator_join", "delegator_exit", diff --git a/genlayer_py/staking/abi/validator_wallet_abi.json b/genlayer_py/staking/abi/validator_wallet_abi.json index cfd125a..df1cd5c 100644 --- a/genlayer_py/staking/abi/validator_wallet_abi.json +++ b/genlayer_py/staking/abi/validator_wallet_abi.json @@ -998,5 +998,55 @@ { "stateMutability": "payable", "type": "receive" + }, + { + "name": "initiateOperatorTransfer", + "type": "function", + "stateMutability": "nonpayable", + "inputs": [ + { + "name": "_newOperatorPubKey", + "type": "uint256[2]", + "internalType": "uint256[2]" + }, + { + "name": "_possessionProof", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [] + }, + { + "name": "completeOperatorTransfer", + "type": "function", + "stateMutability": "nonpayable", + "inputs": [], + "outputs": [] + }, + { + "name": "cancelOperatorTransfer", + "type": "function", + "stateMutability": "nonpayable", + "inputs": [], + "outputs": [] + }, + { + "name": "getPendingOperator", + "type": "function", + "stateMutability": "view", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + }, + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ] } ] diff --git a/genlayer_py/staking/actions.py b/genlayer_py/staking/actions.py index 065b465..c3a9a64 100644 --- a/genlayer_py/staking/actions.py +++ b/genlayer_py/staking/actions.py @@ -17,6 +17,11 @@ from genlayer_py.exceptions import GenLayerError from genlayer_py.staking.abi import STAKING_ABI, VALIDATOR_WALLET_ABI +from genlayer_py.staking.operator_registration import ( + OperatorRegistrationContext, + OperatorRegistrationProof, + verify_operator_registration, +) if TYPE_CHECKING: from genlayer_py.client import GenLayerClient @@ -237,7 +242,12 @@ def set_operator( account: Optional[LocalAccount] = None, ) -> HexBytes: """Rotates the operator for an existing ValidatorWallet. Only the - wallet owner (the EOA that called validator_join) may do this.""" + wallet owner (the EOA that called validator_join) may do this. + + CON-715 removed this single call in favour of the two-step transfer + below; against a deployment carrying that change it reverts with no + decodable reason, because the selector no longer exists. Prefer + initiate_operator_transfer + complete_operator_transfer.""" sender = _sender(self, account) wallet = _wallet(self, validator) data = wallet.encode_abi( @@ -247,6 +257,89 @@ def set_operator( return _send(self, sender, tx) +def get_operator_transfer_context( + self: "GenLayerClient", validator: AddressLike +) -> OperatorRegistrationContext: + """Context for a rotation proof. + + Rotation is verified by the wallet rather than the factory, so the + registrar is the wallet's own address. The owner is read from the wallet + instead of assumed to be the caller: the proof is bound to whatever + owner() returns, and a mismatch is easier to diagnose here than as an + onlyOwner revert.""" + wallet = _wallet(self, validator) + return OperatorRegistrationContext( + registrar=self.w3.to_checksum_address(validator), + owner=self.w3.to_checksum_address(wallet.functions.owner().call()), + chain_id=self.w3.eth.chain_id, + ) + + +def initiate_operator_transfer( + self: "GenLayerClient", + validator: AddressLike, + registration: OperatorRegistrationProof, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Starts the two-step operator rotation. Owner only. + + `registration` must be built against get_operator_transfer_context — a + join proof is bound to the factory and will not verify here.""" + context = get_operator_transfer_context(self, validator) + if not verify_operator_registration(registration, context): + raise GenLayerError( + "Operator registration proof does not match the wallet, owner, chain, " + "or public key. Rotation proofs must use the validator wallet as " + "their registrar." + ) + + sender = _sender(self, account) + wallet = _wallet(self, validator) + data = wallet.encode_abi( + "initiateOperatorTransfer", + args=[list(registration.operator_pub_key), registration.possession_proof], + ) + tx = _build(self, sender, self.w3.to_checksum_address(validator), data) + return _send(self, sender, tx) + + +def complete_operator_transfer( + self: "GenLayerClient", + validator: AddressLike, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Finalises a pending rotation. Callable by the wallet owner or the + pending operator, once the factory's operatorTransferDelay has elapsed.""" + sender = _sender(self, account) + wallet = _wallet(self, validator) + data = wallet.encode_abi("completeOperatorTransfer", args=[]) + tx = _build(self, sender, self.w3.to_checksum_address(validator), data) + return _send(self, sender, tx) + + +def cancel_operator_transfer( + self: "GenLayerClient", + validator: AddressLike, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Abandons a pending rotation, leaving the current operator in place.""" + sender = _sender(self, account) + wallet = _wallet(self, validator) + data = wallet.encode_abi("cancelOperatorTransfer", args=[]) + tx = _build(self, sender, self.w3.to_checksum_address(validator), data) + return _send(self, sender, tx) + + +def get_pending_operator(self: "GenLayerClient", validator: AddressLike) -> dict: + """Pending operator and when its transfer was initiated (0 when none).""" + wallet = _wallet(self, validator) + operator, initiated_at = wallet.functions.getPendingOperator().call() + return { + "operator": self.w3.to_checksum_address(operator), + "initiated_at": int(initiated_at), + } + + def set_identity( self: "GenLayerClient", validator: AddressLike, diff --git a/genlayer_py/staking/operator_registration.py b/genlayer_py/staking/operator_registration.py new file mode 100644 index 0000000..b182ad5 --- /dev/null +++ b/genlayer_py/staking/operator_registration.py @@ -0,0 +1,135 @@ +"""Proof of possession for operator keys. + +Consensus requires an operator to prove control of its key before that key is +bound to a validator wallet. The proof is an EIP-191 signature, by the operator +key, over a domain-separated hash of (chainId, registrar, owner, pubKey). + +`registrar` is whichever contract verifies the proof, and it differs by flow: +the ValidatorWalletFactory for a validator join, the wallet itself for an +operator rotation (ValidatorWalletBlueprint.initiateOperatorTransfer calls +PubKeyUtils.validateWithPossession(pubKey, address(this), owner(), proof)). +Passing the wrong one produces a proof that simply fails to verify. + +The encoding mirrors genlayer-js's createOperatorRegistration exactly — the +shared vector in tests/unit/test_operator_registration.py pins the two +implementations together. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Tuple + +from eth_abi.abi import encode as abi_encode +from eth_account import Account +from eth_account.messages import encode_defunct +from eth_typing import ChecksumAddress +from eth_utils.address import to_checksum_address +from eth_utils.crypto import keccak + +OPERATOR_REGISTRATION_DOMAIN = keccak( + text="GenLayer/operatorPubKey/proof-of-possession/v1" +) + +OperatorPublicKey = Tuple[int, int] + + +@dataclass(frozen=True) +class OperatorRegistrationContext: + """Who verifies the proof, on whose behalf, and on which chain.""" + + registrar: ChecksumAddress + owner: ChecksumAddress + chain_id: int + + +@dataclass(frozen=True) +class OperatorRegistrationProof: + operator: ChecksumAddress + operator_pub_key: OperatorPublicKey + possession_proof: bytes + + +def operator_public_key_from_private_key(private_key: str) -> OperatorPublicKey: + """Splits the uncompressed secp256k1 public key into the contract's + uint256[2] tuple.""" + account = Account.from_key(private_key) + public_key = account._key_obj.public_key.to_bytes() + return ( + int.from_bytes(public_key[0:32], "big"), + int.from_bytes(public_key[32:64], "big"), + ) + + +def operator_address_from_public_key(pub_key: OperatorPublicKey) -> ChecksumAddress: + raw = pub_key[0].to_bytes(32, "big") + pub_key[1].to_bytes(32, "big") + return to_checksum_address(keccak(raw)[-20:]) + + +def operator_possession_message( + pub_key: OperatorPublicKey, context: OperatorRegistrationContext +) -> bytes: + return keccak( + abi_encode( + ["bytes32", "uint256", "address", "address", "uint256", "uint256"], + [ + OPERATOR_REGISTRATION_DOMAIN, + context.chain_id, + context.registrar, + context.owner, + pub_key[0], + pub_key[1], + ], + ) + ) + + +def create_operator_registration( + private_key: str, context: OperatorRegistrationContext +) -> OperatorRegistrationProof: + """Builds the proof package the proof-bearing calls consume. + + The private key is used only to sign and is never retained in the result. + """ + account = Account.from_key(private_key) + pub_key = operator_public_key_from_private_key(private_key) + operator = operator_address_from_public_key(pub_key) + + if operator != to_checksum_address(account.address): + raise ValueError( + "Operator private key and public key derive different identities." + ) + + signed = Account.sign_message( + encode_defunct(operator_possession_message(pub_key, context)), + private_key=private_key, + ) + + return OperatorRegistrationProof( + operator=operator, + operator_pub_key=pub_key, + possession_proof=bytes(signed.signature), + ) + + +def verify_operator_registration( + registration: OperatorRegistrationProof, + context: OperatorRegistrationContext, +) -> bool: + """Checks key identity and the exact registrar/owner/chain binding.""" + if operator_address_from_public_key(registration.operator_pub_key) != to_checksum_address( + registration.operator + ): + return False + + message = encode_defunct( + operator_possession_message(registration.operator_pub_key, context) + ) + try: + recovered = Account.recover_message( + message, signature=registration.possession_proof + ) + except Exception: + return False + + return to_checksum_address(recovered) == to_checksum_address(registration.operator) diff --git a/tests/unit/test_operator_registration.py b/tests/unit/test_operator_registration.py new file mode 100644 index 0000000..3e24c76 --- /dev/null +++ b/tests/unit/test_operator_registration.py @@ -0,0 +1,101 @@ +"""Cross-language vector for the operator proof of possession. + +The expected values below are the ones genlayer-js asserts in +tests/operator-registration.test.ts. Both SDKs sign proofs the same contract +verifies, so a divergence here is a real interoperability break rather than a +cosmetic difference — pin the exact bytes, not just internal consistency. +""" + +from genlayer_py.staking.operator_registration import ( + OPERATOR_REGISTRATION_DOMAIN, + OperatorRegistrationContext, + create_operator_registration, + operator_possession_message, + verify_operator_registration, +) + +OPERATOR_KEY = "0x0000000000000000000000000000000000000000000000000000000000000002" +OTHER_OPERATOR_KEY = "0x0000000000000000000000000000000000000000000000000000000000000003" +CONTEXT = OperatorRegistrationContext( + registrar="0x1111111111111111111111111111111111111111", + owner="0x2222222222222222222222222222222222222222", + chain_id=61999, +) + + +def test_matches_the_genlayer_js_vector(): + registration = create_operator_registration(OPERATOR_KEY, CONTEXT) + + assert ( + "0x" + OPERATOR_REGISTRATION_DOMAIN.hex() + == "0x56a1f863be2956668ca2fd6b4010d6fde7a54f2b5a02d6c624a2bad7e5fd5ada" + ) + assert registration.operator == "0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF" + assert registration.operator_pub_key == ( + 89565891926547004231252920425935692360644145829622209833684329913297188986597, + 12158399299693830322967808612713398636155367887041628176798871954788371653930, + ) + assert ( + "0x" + operator_possession_message(registration.operator_pub_key, CONTEXT).hex() + == "0x7823e1bdaf3a8cea679a7bafaf8ddc39c379ac690f35696328650c3a712f36e0" + ) + assert ( + "0x" + registration.possession_proof.hex() + == "0x30cedc70f8ab478fbc1a13a3f36e7f6a10eed631f59db4c451e38fe6d94dc640" + "586d7a3202471043dbad68a3850655d39114aaca647df5734b171f8db7e88f161c" + ) + assert verify_operator_registration(registration, CONTEXT) + + +def test_rejects_wrong_key_and_cross_domain_proofs(): + registration = create_operator_registration(OPERATOR_KEY, CONTEXT) + wrong_key = create_operator_registration(OTHER_OPERATOR_KEY, CONTEXT) + + assert not verify_operator_registration( + OperatorRegistrationProofLike(registration, wrong_key.possession_proof), CONTEXT + ) + for field, value in ( + ("registrar", "0x3333333333333333333333333333333333333333"), + ("owner", "0x4444444444444444444444444444444444444444"), + ("chain_id", CONTEXT.chain_id + 1), + ): + mutated = OperatorRegistrationContext( + registrar=value if field == "registrar" else CONTEXT.registrar, + owner=value if field == "owner" else CONTEXT.owner, + chain_id=value if field == "chain_id" else CONTEXT.chain_id, + ) + assert not verify_operator_registration(registration, mutated) + + +def test_binds_rotation_proofs_to_the_wallet_not_the_factory(): + """Join proofs are verified by the factory, rotation proofs by the wallet. + + Reusing one for the other is the easy mistake, and it fails silently — the + proof simply does not verify — so pin it. + """ + factory = "0x1111111111111111111111111111111111111111" + wallet = "0x5555555555555555555555555555555555555555" + owner = "0x2222222222222222222222222222222222222222" + + join = create_operator_registration( + OPERATOR_KEY, + OperatorRegistrationContext(registrar=factory, owner=owner, chain_id=61999), + ) + rotation_context = OperatorRegistrationContext( + registrar=wallet, owner=owner, chain_id=61999 + ) + rotation = create_operator_registration(OPERATOR_KEY, rotation_context) + + assert verify_operator_registration(rotation, rotation_context) + assert not verify_operator_registration(join, rotation_context) + assert rotation.possession_proof != join.possession_proof + assert rotation.operator == join.operator + + +class OperatorRegistrationProofLike: + """Swaps in a foreign signature while keeping the advertised identity.""" + + def __init__(self, registration, possession_proof): + self.operator = registration.operator + self.operator_pub_key = registration.operator_pub_key + self.possession_proof = possession_proof From 39d6a37a4e6f2bac93ac3c30587a2c53f30d6d53 Mon Sep 17 00:00:00 2001 From: Kira Date: Tue, 25 Aug 2026 04:15:46 +0900 Subject: [PATCH 13/19] =?UTF-8?q?chore(genvm):=20update=20runner=20hashes?= =?UTF-8?q?=20and=20rename=20accepted=20to=20decided=20=E2=AC=86=EF=B8=8F?= =?UTF-8?q?=20(#105)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(genvm): update runner hashes and align v0.3 ABI names ⬆️ Bump the v0.3 runner pins to the new ids, rename the raw fee-params aliases leader/validator_timeunits_allocation to leader/validator_time_units_allocation, and keep the wait_until rename of accepted to decided. * fix(genvm): give VecDB its explicit distance metric 🐛 The pinned embeddings runner's `VecDB` takes the distance metric as a fourth type parameter. Three arguments now fail at contract import, which leaves the deploy without state rather than reporting a type error. `log_indexer_testnet.py` is deliberately left alone: it targets Asimov, which runs the older SDK. --- genlayer_py/transactions/fees.py | 28 ++++++++++----------- genlayer_py/types/transactions.py | 2 +- tests/e2e/contracts/log_indexer.py | 4 ++- tests/e2e/contracts/simple_time_contract.py | 4 +-- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/genlayer_py/transactions/fees.py b/genlayer_py/transactions/fees.py index ba1a9e3..5780aad 100644 --- a/genlayer_py/transactions/fees.py +++ b/genlayer_py/transactions/fees.py @@ -19,9 +19,9 @@ class MessageType(IntEnum): class FeesDistributionInput(TypedDict, total=False): leaderTimeunitsAllocation: BigNumberish - leader_timeunits_allocation: BigNumberish + leader_time_units_allocation: BigNumberish validatorTimeunitsAllocation: BigNumberish - validator_timeunits_allocation: BigNumberish + validator_time_units_allocation: BigNumberish appealRounds: BigNumberish appeal_rounds: BigNumberish executionBudgetPerRound: BigNumberish @@ -54,9 +54,9 @@ class FeesDistribution(TypedDict): class InternalMessageFeeParamsInput(TypedDict, total=False): leaderTimeunitsAllocation: BigNumberish - leader_timeunits_allocation: BigNumberish + leader_time_units_allocation: BigNumberish validatorTimeunitsAllocation: BigNumberish - validator_timeunits_allocation: BigNumberish + validator_time_units_allocation: BigNumberish appealRounds: BigNumberish appeal_rounds: BigNumberish executionBudgetPerRound: BigNumberish @@ -368,7 +368,7 @@ def create_fees_distribution( _get( fee_distribution, "leaderTimeunitsAllocation", - "leader_timeunits_allocation", + "leader_time_units_allocation", ), "fees.distribution.leaderTimeunitsAllocation", ), @@ -376,7 +376,7 @@ def create_fees_distribution( _get( fee_distribution, "validatorTimeunitsAllocation", - "validator_timeunits_allocation", + "validator_time_units_allocation", ), "fees.distribution.validatorTimeunitsAllocation", ), @@ -444,7 +444,7 @@ def encode_internal_message_fee_params( _get( params, "leaderTimeunitsAllocation", - "leader_timeunits_allocation", + "leader_time_units_allocation", ), "internalMessageFeeParams.leaderTimeunitsAllocation", ), @@ -452,7 +452,7 @@ def encode_internal_message_fee_params( _get( params, "validatorTimeunitsAllocation", - "validator_timeunits_allocation", + "validator_time_units_allocation", ), "internalMessageFeeParams.validatorTimeunitsAllocation", ), @@ -1125,7 +1125,7 @@ def build_estimated_fees_distribution( "leaderTimeunitsAllocation": _get( options, "leaderTimeunitsAllocation", - "leader_timeunits_allocation", + "leader_time_units_allocation", default=DEFAULT_LEADER_TIMEUNITS_ALLOCATION if policy["enabled"] else 0, @@ -1133,7 +1133,7 @@ def build_estimated_fees_distribution( "validatorTimeunitsAllocation": _get( options, "validatorTimeunitsAllocation", - "validator_timeunits_allocation", + "validator_time_units_allocation", default=DEFAULT_VALIDATOR_TIMEUNITS_ALLOCATION if policy["enabled"] else 0, @@ -1193,12 +1193,12 @@ def _validator_index(num_of_validators: int) -> int: def _calculate_fee_for_round( num_of_validators: int, rotations: int, - leader_timeunits_allocation: int, - validator_timeunits_allocation: int, + leader_time_units_allocation: int, + validator_time_units_allocation: int, ) -> int: return rotations * ( - leader_timeunits_allocation - + num_of_validators * validator_timeunits_allocation + leader_time_units_allocation + + num_of_validators * validator_time_units_allocation ) diff --git a/genlayer_py/types/transactions.py b/genlayer_py/types/transactions.py index 89a25fe..43623cc 100644 --- a/genlayer_py/types/transactions.py +++ b/genlayer_py/types/transactions.py @@ -582,7 +582,7 @@ def _decode_pending_transactions( ), "value": int.from_bytes(pending_transaction[2], byteorder="big"), "on": ( - "accepted" + "decided" if int.from_bytes(pending_transaction[3], byteorder="big") == 0 else "finalized" ), diff --git a/tests/e2e/contracts/log_indexer.py b/tests/e2e/contracts/log_indexer.py index 040c736..9da7df2 100644 --- a/tests/e2e/contracts/log_indexer.py +++ b/tests/e2e/contracts/log_indexer.py @@ -23,7 +23,9 @@ class StoreValue: # contract class class LogIndexer(gl.contract.Contract): - vector_store: gle.VecDB[np.float32, typing.Literal[384], StoreValue] + vector_store: gle.VecDB[ + np.float32, typing.Literal[384], StoreValue, gle.EuclideanDistance + ] def __init__(self): pass diff --git a/tests/e2e/contracts/simple_time_contract.py b/tests/e2e/contracts/simple_time_contract.py index 974e801..3934130 100644 --- a/tests/e2e/contracts/simple_time_contract.py +++ b/tests/e2e/contracts/simple_time_contract.py @@ -1,7 +1,7 @@ # { # "Seq": [ -# { "Depends": "py-lib-genlayer-embeddings:09h0i209wrzh4xzq86f79c60x0ifs7xcjwl53ysrnw06i54ddxyi" }, -# { "Depends": "py-genlayer:1j12s63yfjpva9ik2xgnffgrs6v44y1f52jvj9w7xvdn7qckd379" } +# { "Depends": "py-lib-genlayer-embeddings:hqpree1t3470fnac2aeee1y5c2205k22bgk1p98sg8m3s1ndmxbg" }, +# { "Depends": "py-genlayer:5jycge4q8k23462jtb0b9fyey1s9qz928sz2nbrd9mg4sxqg2qng" } # ] # } From 85a47821c2771b5d931b754d4a43f1bd0f057246 Mon Sep 17 00:00:00 2001 From: KirilA Date: Tue, 1 Sep 2026 01:32:57 +0300 Subject: [PATCH 14/19] fix: align Python SDK consumers with the resolution-kernel train (#109) * fix: read the consensus surfaces the resolution-kernel train exposes The train withdraws reads this SDK is built on, and none of them fail in a way a caller would notice: - getTransactionData(txId, timestamp) is gone. It answered with a status PROJECTED at a caller-supplied clock; the chain now keeps the stored record and the projection apart, behind getStoredTransactionData(txId) and getTransactionLifecycle. This is what the py lane traceback rooted at, and it is a hard failure -- web3 cannot find the function. - activeValidators() and activeValidatorsCount() are gone. Committee capacity is 1,543 seats and an address[] that long overruns the return-size limit, so the joined registry is only readable in slices. - validatorView() dropped its left/right/parent tree links, so the tuple went from twelve fields to nine. - ReadyToFinalize was removed from TransactionStatus at ordinal 11 and the three above it shifted down. Left alone, a ValidatorsTimeout transaction decoded as READY_TO_FINALIZE -- no error, the wrong answer. - previousStatus was dropped from the Transaction struct that getTransactionAllData returns, so status sits at index 2 and the rest moved up one slot. The transaction read now detects rather than assumes: chains are upgraded independently, so it picks whichever of the two reads the chain's own ABI offers. Both return the same 23-field struct -- only field [0] was renamed currentTimestamp to observedAt -- so the positional decode is untouched. active_validators() pages the joined registry: validatorsJoinedCount() bounds the walk and getValidatorsJoined(start, size) returns each page. The count is read first so a registry that grows underneath the walk cannot spin the loop, and a short page means it shrank instead. Page size is 64, matching genlayer-node's walk over the same registry. TransactionStatus keeps its READY_TO_FINALIZE member, because the node still reports that state. What changed is that no chain value decodes to it, so the name-to-number map no longer carries an ordinal for it: 11 belongs to VALIDATORS_TIMEOUT now, and there is no free number that would not collide. * fix: align Python SDK with train contracts * fix: keep studio appeals on the pre-train call shape The train adaptation routed every appeal action through the resolution-kernel lifecycle reads. Studio chains (localnet and studionet, which share chain id 61999) run the studio-embedded consensus, which predates the train: its ConsensusData exposes neither getTransactionLifecycle nor estimateLatestAppealCharge, and its ConsensusMain, refetched from the simulator RPC, still declares submitAppeal(bytes32). The decision-guarded encoding therefore aborted with "value has 2 items when 1 were expected" before the call was ever sent. The sibling genlayer-js defect failed the studio suites of e2e run 33112369501. This mirrors its fix, genlayer-js 04a0bde: guard each affected action on the studio chain check that transactions.actions.get_transaction already uses, and reinstate the pre-train behaviour recovered from the v0.19-dev base. - appeal_transaction encodes submitAppeal(bytes32) against the chain's own ConsensusMain ABI. - top_up_and_submit_appeal encodes topUpAndSubmitAppeal(bytes32, FeesDistribution) without a decision id. - get_appeal_quote, get_appeal_charge and get_min_appeal_bond report the missing quote surface with the error the base already raised on studio. - Both appeal writes keep the base value semantics: the pre-train resolver read the bond through FeeManager/RoundsStorage, which studio chains never configure, so an omitted value raises the base message verbatim. A call that binds to no decision cannot honour a decision guard, so expected_decision_id is refused there. can_appeal and the rounds-storage reads already returned their base error on studio: their missing-contract guards run before any train read, so they are left alone. This SDK has no finalizeTransaction, resolveTransactions or finalizeDecisions surface to adapt. The train path is unchanged for non-studio chains. The appeal tests that pin it now build a train chain client, since the shared helper defaulted to the studio chain id. * feat(transactions)!: layer consumer and protocol lifecycles Expose materialized transaction progress as a small state-discriminated lifecycle, make receipt waits target stored decisions or finalization, and keep projection/action/source/decision identity in the explicit advanced read.\n\nRemove the old public status compatibility surface and normalize advanced protocol names, decimal decision IDs, and evaluation timestamps across local and contract-backed networks.\n\nValidation: 189 offline unit tests with 13 old-deployment tests excluded; 12 Asimov smoke tests; focused lifecycle tests; E2E collection; docs; Black; build and installed-wheel smoke. * fix(appeals): support Studio decision-bound lifecycle * ci: skip pre-train smoke on v0.19 PRs * fix(studio): decouple native appeal and lifecycle reads * fix(fees): fund default consensus rotations --------- Co-authored-by: Edgars --- .github/workflows/smoke.yml | 4 + README.md | 80 +- docs/api-references/api.md | 291 +- docs/api-references/genlayer-py.md | 90 +- docs/api-references/index.md | 223 +- genlayer_py/chains/testnet_asimov.py | 17 +- genlayer_py/chains/testnet_bradbury.py | 28 +- genlayer_py/client/genlayer_client.py | 170 +- genlayer_py/consensus/abi/__init__.py | 56 +- .../consensus/abi/address_manager_abi.json | 21 + genlayer_py/consensus/abi/appeals_abi.json | 710 + .../consensus/abi/consensus_data_abi.json | 5148 ++++---- .../consensus/abi/consensus_data_abi_v06.json | 4531 ++++--- .../abi/consensus_data_big_rounds_abi.json | 115 + .../abi/consensus_data_lifecycle_abi.json | 103 + .../abi/consensus_data_train_reads_abi.json | 4 + .../consensus/abi/consensus_main_abi.json | 3471 +++-- .../consensus/abi/consensus_main_abi_v06.json | 3703 ++++-- .../abi/rounds_storage_read_abi.json | 74 + .../abi/transaction_manager_read_abi.json | 40 + .../consensus/consensus_main/decoder.py | 14 +- .../consensus/consensus_main/encoder.py | 16 +- .../consensus/consensus_main/legacy_abi.py | 23 + genlayer_py/contracts/actions.py | 339 +- genlayer_py/staking/__init__.py | 24 + genlayer_py/staking/abi/staking_abi.json | 10697 ++++++++++------ .../staking/abi/validator_wallet_abi.json | 2439 ++-- genlayer_py/staking/actions.py | 128 +- genlayer_py/transactions/actions.py | 730 +- genlayer_py/transactions/fees.py | 16 +- genlayer_py/types/__init__.py | 21 +- genlayer_py/types/transactions.py | 500 +- scripts/generate-api-docs.py | 56 +- tests/e2e/tests/test_custom_validators.py | 1 - .../tests/test_football_prediction_market.py | 5 +- .../tests/test_intelligent_oracle_factory.py | 7 +- tests/e2e/tests/test_llm_erc20.py | 5 +- tests/e2e/tests/test_log_indexer.py | 13 +- tests/e2e/tests/test_multi_file_contract.py | 5 +- tests/e2e/tests/test_multi_read_erc20.py | 11 +- tests/e2e/tests/test_multi_tenant_storage.py | 14 +- tests/e2e/tests/test_read_erc20.py | 5 +- tests/e2e/tests/test_storage.py | 5 +- tests/e2e/tests/test_user_storage.py | 9 +- tests/e2e/tests/test_wizard_of_coin.py | 5 +- tests/unit/conftest.py | 9 +- tests/unit/contracts/test_contract_actions.py | 442 +- .../full_deploy_transaction_data.py | 3 +- .../full_write_transaction_data.py | 3 +- .../simplified_deploy_transaction_data.py | 3 +- .../simplified_write_transaction_data.py | 3 +- tests/unit/smoke/test_bradbury_smoke.py | 20 +- tests/unit/staking/test_staking_actions.py | 230 +- tests/unit/test_api_enum_docs.py | 59 + tests/unit/test_bug_hunt_v019.py | 10 +- tests/unit/test_train_abi_surfaces.py | 92 + .../test_transaction_data_read.py | 563 + .../test_transaction_lifecycle.py | 124 + .../test_wait_for_transaction_receipt.py | 341 +- uv.lock | 2 +- 60 files changed, 22803 insertions(+), 13068 deletions(-) create mode 100644 genlayer_py/consensus/abi/address_manager_abi.json create mode 100644 genlayer_py/consensus/abi/appeals_abi.json create mode 100644 genlayer_py/consensus/abi/consensus_data_big_rounds_abi.json create mode 100644 genlayer_py/consensus/abi/consensus_data_lifecycle_abi.json create mode 100644 genlayer_py/consensus/abi/consensus_data_train_reads_abi.json create mode 100644 genlayer_py/consensus/abi/rounds_storage_read_abi.json create mode 100644 genlayer_py/consensus/abi/transaction_manager_read_abi.json create mode 100644 genlayer_py/consensus/consensus_main/legacy_abi.py create mode 100644 tests/unit/test_api_enum_docs.py create mode 100644 tests/unit/test_train_abi_surfaces.py create mode 100644 tests/unit/transactions/test_transaction_data_read.py create mode 100644 tests/unit/transactions/test_transaction_lifecycle.py diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index 39b3e25..6ec8fbc 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -9,6 +9,10 @@ on: jobs: smoke: name: Testnet Smoke Tests + # The v0.19 line intentionally targets the resolution-kernel train and + # does not support the currently deployed pre-train testnets. Its live + # coverage comes from the cross-repository E2E stack until they upgrade. + if: github.event_name != 'pull_request' || github.base_ref != 'v0.19-dev' runs-on: ubuntu-latest # Bradbury/Asimov RPC reads occasionally stall for 2-3 minutes on # single calls (testnet capacity); with 17 testnet-flagged tests diff --git a/README.md b/README.md index 476569d..1751b16 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ To install the GenLayerPY SDK, use the following command: $ pip install genlayer-py ``` +SDK releases follow their corresponding GenLayer protocol release. This +release targets the current resolution-kernel train; use the matching older SDK +release when connecting to an older deployment. + Here’s how to initialize the client and connect to the GenLayer Simulator: ### Reading a Transaction @@ -44,21 +48,20 @@ transaction = client.get_transaction(hash=transaction_hash) ```python from genlayer_py import create_client from genlayer_py.chains import localnet -from genlayer_py.types import TransactionStatus client = create_client(chain=localnet) # Get simplified receipt (default - removes binary data, keeps execution results) receipt = client.wait_for_transaction_receipt( transaction_hash="0x...", - status=TransactionStatus.FINALIZED, + wait_until="finalized", full_transaction=False # Default - simplified for readability ) # Get complete receipt with all fields full_receipt = client.wait_for_transaction_receipt( transaction_hash="0x...", - status=TransactionStatus.FINALIZED, + wait_until="finalized", full_transaction=True # Complete receipt with all internal data ) ``` @@ -101,7 +104,7 @@ transaction_hash = client.write_contract( ) receipt = client.wait_for_transaction_receipt( hash=transaction_hash, - status=TransactionStatus.FINALIZED, // or ACCEPTED + wait_until="finalized", full_transaction=False // False by default - returns simplified receipt for better readability ) ``` @@ -117,7 +120,6 @@ estimate = client.estimate_transaction_fees( { "leaderTimeunitsAllocation": 100, "validatorTimeunitsAllocation": 200, - "rotations": [0], } ) @@ -133,6 +135,11 @@ tx_hash = client.write_contract( ) ``` +When `rotations` is omitted, estimates fund +`chain.default_consensus_max_rotations` for the initial round and every enabled +appeal round. Pass an explicit list, including `[0]`, when the application wants +to fund a different number of rotations. + If `fees["distribution"]` is provided without `feeValue`, the SDK derives the fee deposit from FeeManager on network backends, or from `sim_getFeeConfig` on Studio. Use `messageAllocations` with `estimate_transaction_fees` for @@ -254,18 +261,26 @@ client.top_up_fees( }, ) -client.top_up_and_submit_appeal( - transaction_id=tx_hash, - value=1_400, - distribution={ - "appealRounds": 1, - "rotations": [0, 0], - }, -) +quote = client.get_appeal_quote(tx_hash) +if client.can_appeal(tx_hash, expected_decision_id=quote["decision_id"]): + client.top_up_and_submit_appeal( + transaction_id=tx_hash, + expected_decision_id=quote["decision_id"], + value=quote["total"], + distribution={ + "appealRounds": 1, + "rotations": [0, 0], + }, + ) ``` `top_up_fees` returns the backend RPC hash. On network backends this is the EVM transaction hash; on Studio/localnet it is the target GenLayer transaction id. +Appeal commands are guarded by the quoted decision id so a stale request cannot +bind to a newer decision. If the id and value are omitted, the SDK refreshes +this lightweight quote automatically. This applies to deployed Consensus. +Current Studio uses its native decision-free appeal methods: pass ``value`` +explicitly and omit ``expected_decision_id``. ### Checking execution results @@ -274,13 +289,13 @@ A transaction can be finalized by consensus but still have a failed execution. A ```python from genlayer_py import create_client, create_account from genlayer_py.chains import testnet_bradbury -from genlayer_py.types import TransactionStatus, ExecutionResult +from genlayer_py.types import ExecutionResult client = create_client(chain=testnet_bradbury, account=create_account()) receipt = client.wait_for_transaction_receipt( transaction_hash=tx_hash, - status=TransactionStatus.FINALIZED, + wait_until="finalized", ) if receipt.get("tx_execution_result_name") == ExecutionResult.FINISHED_WITH_RETURN.value: @@ -305,6 +320,27 @@ Transactions can emit messages to other contracts. These messages create new chi ```python tx = client.get_transaction(transaction_hash=tx_hash) +# The default lifecycle is derived only from stored chain state. +print(tx["lifecycle"]) +# {"state": "processing", "phase": "revealing"} +# {"state": "decided", "outcome": "accepted"} + +# Protocol projection/action details are available only through the explicit +# advanced API. +raw_lifecycle = client.get_transaction_lifecycle(transaction_hash=tx_hash) +print(raw_lifecycle["stored_status_name"]) +print(raw_lifecycle["projected_status_name"]) +print(raw_lifecycle["resolution_action_name"]) +print(raw_lifecycle["resolution_source_name"]) +# `resolution_action_name == "Finalize"` is the authoritative readiness verdict. +# On current Studio without the advanced lifecycle RPC, only stored status is +# provable; projection repeats it and resolution/decision fields stay inactive. + +# The train stores the execution hash, not the old receipt bytes. +print(tx["tx_execution_hash"]) +# `tx_receipt` remains present but is `None` when the +# protocol cannot supply the old bytes. + # Messages emitted by the contract during execution print(tx["messages"]) # [{"messageType": 1, "recipient": "0x...", "value": 0, "data": "0x...", "onAcceptance": True, "saltNonce": 0}, ...] @@ -315,6 +351,20 @@ print(child_tx_ids) # ["0xabc...", "0xdef..."] ``` +### Active and joined validators + +The active set contains only validators currently eligible for protocol +duties. The joined registry is broader and can include validators that are not +yet selectable, are under-staked, or are otherwise unavailable. + +```python +active = client.active_validators() +active_count = client.active_validators_count() + +joined = client.joined_validators() +joined_count = client.joined_validators_count() +``` + ### Debugging transaction execution Use `debug_trace_transaction` to inspect the full execution trace of a transaction, including return data, errors, and GenVM logs: diff --git a/docs/api-references/api.md b/docs/api-references/api.md index 92046fb..6c8844e 100644 --- a/docs/api-references/api.md +++ b/docs/api-references/api.md @@ -64,19 +64,19 @@ client.initialize_consensus_smart_contract(force_reset: bool = False) Executes a read-only contract call without modifying state. ```python -client.read_contract(address: Union, function_name: str, args: Union = None, kwargs: Union = None, account: Union = None, raw_return: bool = False, transaction_hash_variant: TransactionHashVariant = , sim_config: Union = None) +client.read_contract(address: Union, function_name: str, args: Optional = None, kwargs: Optional = None, account: Optional = None, raw_return: bool = False, transaction_hash_variant: TransactionHashVariant = , sim_config: Optional = None) ``` | Parameter | Type | Required | Default | |-----------|------|----------|---------| | address | `Union` | yes | | | function_name | `str` | yes | | -| args | `Union` | no | None | -| kwargs | `Union` | no | None | -| account | `Union` | no | None | +| args | `Optional` | no | None | +| kwargs | `Optional` | no | None | +| account | `Optional` | no | None | | raw_return | `bool` | no | False | | transaction_hash_variant | `TransactionHashVariant` | no | | -| sim_config | `Union` | no | None | +| sim_config | `Optional` | no | None | --- @@ -85,20 +85,22 @@ client.read_contract(address: Union, function_name: str, args: Union = None, kwa Executes a state-modifying function on a contract through consensus. Returns the transaction hash. ```python -client.write_contract(address: Union, function_name: str, account: Union = None, consensus_max_rotations: Union = None, value: int = 0, leader_only: bool = False, args: Union = None, kwargs: Union = None, sim_config: Union = None) +client.write_contract(address: Union, function_name: str, account: Optional = None, consensus_max_rotations: Optional = None, value: int = 0, leader_only: bool = False, args: Optional = None, kwargs: Optional = None, sim_config: Optional = None, valid_until: Optional = None, fees: Optional = None) ``` | Parameter | Type | Required | Default | |-----------|------|----------|---------| | address | `Union` | yes | | | function_name | `str` | yes | | -| account | `Union` | no | None | -| consensus_max_rotations | `Union` | no | None | +| account | `Optional` | no | None | +| consensus_max_rotations | `Optional` | no | None | | value | `int` | no | 0 | | leader_only | `bool` | no | False | -| args | `Union` | no | None | -| kwargs | `Union` | no | None | -| sim_config | `Union` | no | None | +| args | `Optional` | no | None | +| kwargs | `Optional` | no | None | +| sim_config | `Optional` | no | None | +| valid_until | `Optional` | no | None | +| fees | `Optional` | no | None | --- @@ -107,17 +109,20 @@ client.write_contract(address: Union, function_name: str, account: Union = None, Simulates a state-modifying contract call without executing on-chain. Localnet only. ```python -client.simulate_write_contract(address: Union, function_name: str, account: Union = None, args: Union = None, kwargs: Union = None, sim_config: Union = None, transaction_hash_variant: TransactionHashVariant = ) +client.simulate_write_contract(address: Union, function_name: str, account: Optional = None, args: Optional = None, kwargs: Optional = None, value: int = 0, leader_only: bool = False, fees: Optional = None, sim_config: Optional = None, transaction_hash_variant: TransactionHashVariant = ) ``` | Parameter | Type | Required | Default | |-----------|------|----------|---------| | address | `Union` | yes | | | function_name | `str` | yes | | -| account | `Union` | no | None | -| args | `Union` | no | None | -| kwargs | `Union` | no | None | -| sim_config | `Union` | no | None | +| account | `Optional` | no | None | +| args | `Optional` | no | None | +| kwargs | `Optional` | no | None | +| value | `int` | no | 0 | +| leader_only | `bool` | no | False | +| fees | `Optional` | no | None | +| sim_config | `Optional` | no | None | | transaction_hash_variant | `TransactionHashVariant` | no | | --- @@ -127,18 +132,20 @@ client.simulate_write_contract(address: Union, function_name: str, account: Unio Deploys a new intelligent contract to GenLayer. Returns the transaction hash. ```python -client.deploy_contract(code: Union, account: Union = None, args: Union = None, kwargs: Union = None, consensus_max_rotations: Union = None, leader_only: bool = False, sim_config: Union = None) +client.deploy_contract(code: Union, account: Optional = None, args: Optional = None, kwargs: Optional = None, consensus_max_rotations: Optional = None, leader_only: bool = False, sim_config: Optional = None, valid_until: Optional = None, fees: Optional = None) ``` | Parameter | Type | Required | Default | |-----------|------|----------|---------| | code | `Union` | yes | | -| account | `Union` | no | None | -| args | `Union` | no | None | -| kwargs | `Union` | no | None | -| consensus_max_rotations | `Union` | no | None | +| account | `Optional` | no | None | +| args | `Optional` | no | None | +| kwargs | `Optional` | no | None | +| consensus_max_rotations | `Optional` | no | None | | leader_only | `bool` | no | False | -| sim_config | `Union` | no | None | +| sim_config | `Optional` | no | None | +| valid_until | `Optional` | no | None | +| fees | `Optional` | no | None | --- @@ -177,31 +184,188 @@ client.get_contract_schema_for_code(contract_code: AnyStr) ### appeal_transaction Appeals a consensus transaction to trigger a new round of validation. +Returns the original transaction_id (appeals operate on the same tx). +Deployed Consensus fills missing decision/value inputs from its +authoritative quote. Current Studio requires an explicit value and does +not accept ``expected_decision_id``. ```python -client.appeal_transaction(transaction_id: HexStr, account: Union = None, value: int = 0) +client.appeal_transaction(transaction_id: HexStr, account: Optional = None, value: Optional = None, expected_decision_id: Optional = None) ``` | Parameter | Type | Required | Default | |-----------|------|----------|---------| | transaction_id | `HexStr` | yes | | -| account | `Union` | no | None | -| value | `int` | no | 0 | +| account | `Optional` | no | None | +| value | `Optional` | no | None | +| expected_decision_id | `Optional` | no | None | + +--- + +### top_up_fees + +Deposits additional fee budget for an existing consensus transaction. + +```python +client.top_up_fees(transaction_id: HexStr, distribution: FeesDistributionInput, value: int, account: Optional = None) +``` + +| Parameter | Type | Required | Default | +|-----------|------|----------|---------| +| transaction_id | `HexStr` | yes | | +| distribution | `FeesDistributionInput` | yes | | +| value | `int` | yes | | +| account | `Optional` | no | None | + +**Returns:** `HexStr` + +--- + +### top_up_and_submit_appeal + +Deposits appeal funding and submits an appeal. + +On deployed Consensus, omitted decision/value inputs are resolved from +the authoritative appeal quote. Current Studio requires an explicit +value and does not accept ``expected_decision_id``. + +```python +client.top_up_and_submit_appeal(transaction_id: HexStr, distribution: FeesDistributionInput, account: Optional = None, value: Optional = None, expected_decision_id: Optional = None) +``` + +| Parameter | Type | Required | Default | +|-----------|------|----------|---------| +| transaction_id | `HexStr` | yes | | +| distribution | `FeesDistributionInput` | yes | | +| account | `Optional` | no | None | +| value | `Optional` | no | None | +| expected_decision_id | `Optional` | no | None | + +**Returns:** `HexStr` + +--- + +### can_appeal + +Checks whether the exact active decision can be appealed on a network. + +This decision-bound read is not available on current Studio. + +```python +client.can_appeal(transaction_id: HexStr, expected_decision_id: Optional = None) +``` + +| Parameter | Type | Required | Default | +|-----------|------|----------|---------| +| transaction_id | `HexStr` | yes | | +| expected_decision_id | `Optional` | no | None | + +**Returns:** `bool` + +--- + +### get_appeal_quote + +Returns a network's latest decision id, appeal charges, and deadline. + +Current Studio has no decision-bound quote surface. + +```python +client.get_appeal_quote(transaction_id: HexStr) +``` + +| Parameter | Type | Required | Default | +|-----------|------|----------|---------| +| transaction_id | `HexStr` | yes | | + +**Returns:** `Dict` + +--- + +### get_appeal_charge + +Returns the full appeal payment (bond plus induced-work funding). + +Current Studio has no decision-bound quote surface. + +```python +client.get_appeal_charge(transaction_id: HexStr) +``` + +| Parameter | Type | Required | Default | +|-----------|------|----------|---------| +| transaction_id | `HexStr` | yes | | + +**Returns:** `int` + +--- + +### get_min_appeal_bond + +Deprecated alias for :meth:`get_appeal_charge`. + +```python +client.get_min_appeal_bond(transaction_id: HexStr) +``` + +| Parameter | Type | Required | Default | +|-----------|------|----------|---------| +| transaction_id | `HexStr` | yes | | + +**Returns:** `int` + +--- + +### wait_for_decision + +Poll until the stored transaction state is decided or terminal. + +```python +client.wait_for_decision(transaction_hash: Union, interval: int = 3000, retries: int = 10, full_transaction: bool = False) +``` + +| Parameter | Type | Required | Default | +|-----------|------|----------|---------| +| transaction_hash | `Union` | yes | | +| interval | `int` | no | 3000 | +| retries | `int` | no | 10 | +| full_transaction | `bool` | no | False | + +**Returns:** `GenLayerTransaction` + +--- + +### wait_for_finalization + +Poll until the stored transaction state is finalized. + +```python +client.wait_for_finalization(transaction_hash: Union, interval: int = 3000, retries: int = 10, full_transaction: bool = False) +``` + +| Parameter | Type | Required | Default | +|-----------|------|----------|---------| +| transaction_hash | `Union` | yes | | +| interval | `int` | no | 3000 | +| retries | `int` | no | 10 | +| full_transaction | `bool` | no | False | + +**Returns:** `GenLayerTransaction` --- ### wait_for_transaction_receipt -Polls until a transaction reaches the specified status. Returns the transaction receipt. +Poll for a stored decision (default) or stored finalization. ```python -client.wait_for_transaction_receipt(transaction_hash: Union, status: TransactionStatus = , interval: int = 3000, retries: int = 10, full_transaction: bool = False) +client.wait_for_transaction_receipt(transaction_hash: Union, wait_until: Literal = 'decided', interval: int = 3000, retries: int = 10, full_transaction: bool = False) ``` | Parameter | Type | Required | Default | |-----------|------|----------|---------| | transaction_hash | `Union` | yes | | -| status | `TransactionStatus` | no | | +| wait_until | `Literal` | no | 'decided' | | interval | `int` | no | 3000 | | retries | `int` | no | 10 | | full_transaction | `bool` | no | False | @@ -212,7 +376,12 @@ client.wait_for_transaction_receipt(transaction_hash: Union, status: Transaction ### get_transaction -Fetches transaction data including status, execution result, and consensus details. +Fetch transaction data with a stable stored-state ``lifecycle``. + +The lifecycle's ``state`` is one of processing, decided, finalized, or +canceled. Processing carries ``phase`` and decided carries ``outcome``. +The train exposes ``tx_execution_hash``; legacy receipt bytes are +unavailable, so ``tx_receipt`` is ``None``. ```python client.get_transaction(transaction_hash: Union) @@ -226,6 +395,27 @@ client.get_transaction(transaction_hash: Union) --- +### get_transaction_lifecycle + +Return advanced stored/projected/action protocol lifecycle data. + +If current Studio does not expose the advanced RPC, only its provable +stored status is returned: projection repeats it, resolution is +NoOp/Unspecified, and decision identity is inactive. + +```python +client.get_transaction_lifecycle(transaction_hash: Union, timestamp: Optional = None) +``` + +| Parameter | Type | Required | Default | +|-----------|------|----------|---------| +| transaction_hash | `Union` | yes | | +| timestamp | `Optional` | no | None | + +**Returns:** `ProtocolTransactionLifecycle` + +--- + ### get_triggered_transaction_ids Returns transaction IDs of child transactions created from emitted messages. @@ -261,29 +451,6 @@ client.debug_trace_transaction(transaction_hash: Union, round: int = 0) ## Types and Enums -### TransactionStatus - -Status of a GenLayer transaction in the consensus lifecycle. - -```python -TransactionStatus.UNINITIALIZED = "UNINITIALIZED" -TransactionStatus.PENDING = "PENDING" -TransactionStatus.PROPOSING = "PROPOSING" -TransactionStatus.COMMITTING = "COMMITTING" -TransactionStatus.REVEALING = "REVEALING" -TransactionStatus.ACCEPTED = "ACCEPTED" -TransactionStatus.UNDETERMINED = "UNDETERMINED" -TransactionStatus.FINALIZED = "FINALIZED" -TransactionStatus.CANCELED = "CANCELED" -TransactionStatus.APPEAL_REVEALING = "APPEAL_REVEALING" -TransactionStatus.APPEAL_COMMITTING = "APPEAL_COMMITTING" -TransactionStatus.READY_TO_FINALIZE = "READY_TO_FINALIZE" -TransactionStatus.VALIDATORS_TIMEOUT = "VALIDATORS_TIMEOUT" -TransactionStatus.LEADER_TIMEOUT = "LEADER_TIMEOUT" -``` - ---- - ### TransactionResult Consensus voting result across validators. @@ -297,6 +464,7 @@ TransactionResult.DETERMINISTIC_VIOLATION = "DETERMINISTIC_VIOLATION" TransactionResult.NO_MAJORITY = "NO_MAJORITY" TransactionResult.MAJORITY_AGREE = "MAJORITY_AGREE" TransactionResult.MAJORITY_DISAGREE = "MAJORITY_DISAGREE" +TransactionResult.MAJORITY_TIMEOUT = "MAJORITY_TIMEOUT" ``` --- @@ -309,28 +477,23 @@ Result of contract execution by the GenVM. ExecutionResult.NOT_VOTED = "NOT_VOTED" ExecutionResult.FINISHED_WITH_RETURN = "FINISHED_WITH_RETURN" ExecutionResult.FINISHED_WITH_ERROR = "FINISHED_WITH_ERROR" +ExecutionResult.TIMEOUT = "TIMEOUT" +ExecutionResult.NONDET_DISAGREE = "NONDET_DISAGREE" +ExecutionResult.DETERMINISTIC_VIOLATION = "DETERMINISTIC_VIOLATION" ``` --- ### VoteType -str(object='') -> str -str(bytes_or_buffer[, encoding[, errors]]) -> str - -Create a new string object from the given object. If encoding or -errors is specified, then the object must expose a data buffer -that will be decoded using the given encoding and error handler. -Otherwise, returns the result of object.__str__() (if defined) -or repr(object). -encoding defaults to 'utf-8'. -errors defaults to 'strict'. +Validator execution vote recorded for a consensus round. ```python VoteType.NOT_VOTED = "NOT_VOTED" -VoteType.AGREE = "AGREE" -VoteType.DISAGREE = "DISAGREE" +VoteType.FINISHED_WITH_RETURN = "FINISHED_WITH_RETURN" +VoteType.FINISHED_WITH_ERROR = "FINISHED_WITH_ERROR" VoteType.TIMEOUT = "TIMEOUT" +VoteType.NONDET_DISAGREE = "NONDET_DISAGREE" VoteType.DETERMINISTIC_VIOLATION = "DETERMINISTIC_VIOLATION" ``` diff --git a/docs/api-references/genlayer-py.md b/docs/api-references/genlayer-py.md index b844f06..7cca50f 100644 --- a/docs/api-references/genlayer-py.md +++ b/docs/api-references/genlayer-py.md @@ -192,16 +192,16 @@ client.appeal_transaction(transaction_id: HexStr, account: Union = None, value: ### wait_for_transaction_receipt -Polls until a transaction reaches the specified status. Returns the transaction receipt. +Polls for a stored decision by default, or for stored finalization. ```python -client.wait_for_transaction_receipt(transaction_hash: Union, status: TransactionStatus = , interval: int = 3000, retries: int = 10, full_transaction: bool = False) +client.wait_for_transaction_receipt(transaction_hash: Union, wait_until: Literal = "decided", interval: int = 3000, retries: int = 10, full_transaction: bool = False) ``` **Parameters:** - **transaction_hash** (`Union`) — required -- **status** (`TransactionStatus`) — optional = +- **wait_until** (`Literal["decided", "finalized"]`) — optional = "decided" - **interval** (`int`) — optional = 3000 - **retries** (`int`) — optional = 10 - **full_transaction** (`bool`) — optional = False @@ -210,9 +210,30 @@ client.wait_for_transaction_receipt(transaction_hash: Union, status: Transaction --- +### wait_for_decision + +Polls until the stored transaction state is decided, finalized, or canceled. + +```python +client.wait_for_decision(transaction_hash: Union, interval: int = 3000, retries: int = 10, full_transaction: bool = False) +``` + +--- + +### wait_for_finalization + +Polls until the stored transaction state is finalized. + +```python +client.wait_for_finalization(transaction_hash: Union, interval: int = 3000, retries: int = 10, full_transaction: bool = False) +``` + +--- + ### get_transaction -Fetches transaction data including status, execution result, and consensus details. +Fetches transaction data with a stable lifecycle derived from stored chain +state, plus execution result and consensus details. ```python client.get_transaction(transaction_hash: Union) @@ -224,6 +245,25 @@ client.get_transaction(transaction_hash: Union) **Returns:** `GenLayerTransaction` +`lifecycle` is discriminated by its `state` field: processing carries `phase`, +decided carries `outcome`, finalized may carry an outcome when it is actually +known, and canceled has no extra branch data. + +The train exposes the authoritative `tx_execution_hash`. It does not retain the +old receipt bytes, so the legacy `tx_receipt` field is `None`. + +--- + +### get_transaction_lifecycle + +Returns the advanced raw protocol view: stored status, projected status, +resolution action/source, decision identity, and evaluation time. A `FINALIZE` +action is the authoritative finalization-readiness verdict. + +```python +client.get_transaction_lifecycle(transaction_hash: Union) +``` + --- ### get_triggered_transaction_ids @@ -261,29 +301,6 @@ client.debug_trace_transaction(transaction_hash: Union, round: int = 0) ## Types and Enums -### TransactionStatus - -Status of a GenLayer transaction in the consensus lifecycle. - -```python -TransactionStatus.UNINITIALIZED = "UNINITIALIZED" -TransactionStatus.PENDING = "PENDING" -TransactionStatus.PROPOSING = "PROPOSING" -TransactionStatus.COMMITTING = "COMMITTING" -TransactionStatus.REVEALING = "REVEALING" -TransactionStatus.ACCEPTED = "ACCEPTED" -TransactionStatus.UNDETERMINED = "UNDETERMINED" -TransactionStatus.FINALIZED = "FINALIZED" -TransactionStatus.CANCELED = "CANCELED" -TransactionStatus.APPEAL_REVEALING = "APPEAL_REVEALING" -TransactionStatus.APPEAL_COMMITTING = "APPEAL_COMMITTING" -TransactionStatus.READY_TO_FINALIZE = "READY_TO_FINALIZE" -TransactionStatus.VALIDATORS_TIMEOUT = "VALIDATORS_TIMEOUT" -TransactionStatus.LEADER_TIMEOUT = "LEADER_TIMEOUT" -``` - ---- - ### TransactionResult Consensus voting result across validators. @@ -309,28 +326,23 @@ Result of contract execution by the GenVM. ExecutionResult.NOT_VOTED = "NOT_VOTED" ExecutionResult.FINISHED_WITH_RETURN = "FINISHED_WITH_RETURN" ExecutionResult.FINISHED_WITH_ERROR = "FINISHED_WITH_ERROR" +ExecutionResult.TIMEOUT = "TIMEOUT" +ExecutionResult.NONDET_DISAGREE = "NONDET_DISAGREE" +ExecutionResult.DETERMINISTIC_VIOLATION = "DETERMINISTIC_VIOLATION" ``` --- ### VoteType -str(object='') -> str -str(bytes_or_buffer[, encoding[, errors]]) -> str - -Create a new string object from the given object. If encoding or -errors is specified, then the object must expose a data buffer -that will be decoded using the given encoding and error handler. -Otherwise, returns the result of object.__str__() (if defined) -or repr(object). -encoding defaults to 'utf-8'. -errors defaults to 'strict'. +Validator execution vote recorded for a consensus round. ```python VoteType.NOT_VOTED = "NOT_VOTED" -VoteType.AGREE = "AGREE" -VoteType.DISAGREE = "DISAGREE" +VoteType.FINISHED_WITH_RETURN = "FINISHED_WITH_RETURN" +VoteType.FINISHED_WITH_ERROR = "FINISHED_WITH_ERROR" VoteType.TIMEOUT = "TIMEOUT" +VoteType.NONDET_DISAGREE = "NONDET_DISAGREE" VoteType.DETERMINISTIC_VIOLATION = "DETERMINISTIC_VIOLATION" ``` diff --git a/docs/api-references/index.md b/docs/api-references/index.md index 0df4eae..0eba21a 100644 --- a/docs/api-references/index.md +++ b/docs/api-references/index.md @@ -20,6 +20,10 @@ To install the GenLayerPY SDK, use the following command: $ pip install genlayer-py ``` +SDK releases follow their corresponding GenLayer protocol release. This +release targets the current resolution-kernel train; use the matching older SDK +release when connecting to an older deployment. + Here’s how to initialize the client and connect to the GenLayer Simulator: ### Reading a Transaction @@ -41,21 +45,20 @@ transaction = client.get_transaction(hash=transaction_hash) ```python from genlayer_py import create_client from genlayer_py.chains import localnet -from genlayer_py.types import TransactionStatus client = create_client(chain=localnet) # Get simplified receipt (default - removes binary data, keeps execution results) receipt = client.wait_for_transaction_receipt( transaction_hash="0x...", - status=TransactionStatus.FINALIZED, + wait_until="finalized", full_transaction=False # Default - simplified for readability ) # Get complete receipt with all fields full_receipt = client.wait_for_transaction_receipt( transaction_hash="0x...", - status=TransactionStatus.FINALIZED, + wait_until="finalized", full_transaction=True # Complete receipt with all internal data ) ``` @@ -98,11 +101,184 @@ transaction_hash = client.write_contract( ) receipt = client.wait_for_transaction_receipt( hash=transaction_hash, - status=TransactionStatus.FINALIZED, // or ACCEPTED + wait_until="finalized", full_transaction=False // False by default - returns simplified receipt for better readability ) ``` +### Fee presets for transactions + +Apps can build a trusted fee preset once they know the transaction shape, then +submit the same preset with the transaction. The user may still override these +values in wallet or app UI before signing. + +```python +estimate = client.estimate_transaction_fees( + { + "leaderTimeunitsAllocation": 100, + "validatorTimeunitsAllocation": 200, + } +) + +tx_hash = client.write_contract( + account=account, + address=contract_address, + function_name="update_storage", + args=["new_storage"], + fees={ + "distribution": estimate["distribution"], + "feeValue": estimate["feeValue"], + }, +) +``` + +When `rotations` is omitted, estimates fund +`chain.default_consensus_max_rotations` for the initial round and every enabled +appeal round. Pass an explicit list, including `[0]`, when the application wants +to fund a different number of rotations. + +If `fees["distribution"]` is provided without `feeValue`, the SDK derives the +fee deposit from FeeManager on network backends, or from `sim_getFeeConfig` on +Studio. Use `messageAllocations` with `estimate_transaction_fees` for +transactions that can emit funded messages. For method-specific message +budgets, derive the same call key GenVM reports in fee accounting: + +```python +from genlayer_py.transactions import ( + MessageType, + derive_external_message_call_key, + derive_internal_message_call_key, + encode_external_message_fee_params, + encode_internal_message_fee_params, +) + +estimate = client.estimate_transaction_fees( + { + "messageAllocations": [ + { + "messageType": MessageType.Internal, + "onAcceptance": True, + "recipient": contract_address, + "callKey": derive_internal_message_call_key("update_storage"), + "budget": 55, + "feeParams": encode_internal_message_fee_params(), + }, + { + "messageType": MessageType.External, + "onAcceptance": False, + "recipient": "0x3333333333333333333333333333333333333333", + "callKey": derive_external_message_call_key("0xaabbccdd"), + "budget": 210_000, + "feeParams": encode_external_message_fee_params( + {"gasLimit": 21_000, "maxGasPrice": 10} + ), + }, + ], + } +) +``` + +For a concrete Studio/localnet write, use the one-call helper. The SDK sends an +initial fee budget to `sim_estimateTransactionFees`; Studio simulates the write +without committing state and returns the authoritative recommended preset. + +```python +recommended = client.estimate_transaction_fees_for_write( + account=account, + address=contract_address, + function_name="update_storage", + args=["new_storage"], +) + +tx_hash = client.write_contract( + account=account, + address=contract_address, + function_name="update_storage", + args=["new_storage"], + fees={ + "distribution": recommended["distribution"], + "messageAllocations": recommended.get("messageAllocations"), + "feeValue": recommended["feeValue"], + }, +) +``` + +For tests or tools that need to inspect the raw simulation, use the explicit +two-step flow. `simulate_write_contract` uses `sim_call`; the returned receipt +includes the fee accounting report produced by GenVM and Studio: + +```python +simulation = client.simulate_write_contract( + account=account, + address=contract_address, + function_name="update_storage", + args=["new_storage"], + fees={ + "distribution": estimate["distribution"], + "feeValue": estimate["feeValue"], + }, +) + +print(simulation["genvm_result"]["fee_accounting"]) +``` + +To reuse a representative Studio simulation as the trusted preset source, pass +the simulation result into `estimate_transaction_fees_from_simulation`: + +```python +estimate = client.estimate_transaction_fees_from_simulation( + { + "simulation": simulation, + } +) + +tx_hash = client.write_contract( + account=account, + address=contract_address, + function_name="update_storage", + args=["new_storage"], + fees={ + "distribution": estimate["distribution"], + "messageAllocations": estimate.get("messageAllocations"), + "feeValue": estimate["feeValue"], + }, +) +``` + +For transactions that are already submitted, use the fee-management helpers: + +```python +client.top_up_fees( + transaction_id=tx_hash, + value=1_100, + distribution={ + "leaderTimeunitsAllocation": 100, + "validatorTimeunitsAllocation": 200, + "rotations": [0], + }, +) + +quote = client.get_appeal_quote(tx_hash) +if client.can_appeal(tx_hash, expected_decision_id=quote["decision_id"]): + client.top_up_and_submit_appeal( + transaction_id=tx_hash, + expected_decision_id=quote["decision_id"], + value=quote["total"], + distribution={ + "appealRounds": 1, + "rotations": [0, 0], + }, + ) +``` + +`top_up_fees` returns the backend RPC hash. On network backends this is the EVM +transaction hash; on Studio/localnet it is the target GenLayer transaction id. +Appeal commands are guarded by the quoted decision id so a stale request cannot +bind to a newer decision. If the id and value are omitted, the SDK refreshes +this lightweight quote automatically. This applies to deployed Consensus. +Current Studio uses its native decision-free appeal methods: pass ``value`` +explicitly and omit ``expected_decision_id``. + ### Checking execution results A transaction can be finalized by consensus but still have a failed execution. Always check `tx_execution_result` before reading contract state: @@ -110,13 +286,13 @@ A transaction can be finalized by consensus but still have a failed execution. A ```python from genlayer_py import create_client, create_account from genlayer_py.chains import testnet_bradbury -from genlayer_py.types import TransactionStatus, ExecutionResult +from genlayer_py.types import ExecutionResult client = create_client(chain=testnet_bradbury, account=create_account()) receipt = client.wait_for_transaction_receipt( transaction_hash=tx_hash, - status=TransactionStatus.FINALIZED, + wait_until="finalized", ) if receipt.get("tx_execution_result_name") == ExecutionResult.FINISHED_WITH_RETURN.value: @@ -141,6 +317,27 @@ Transactions can emit messages to other contracts. These messages create new chi ```python tx = client.get_transaction(transaction_hash=tx_hash) +# The default lifecycle is derived only from stored chain state. +print(tx["lifecycle"]) +# {"state": "processing", "phase": "revealing"} +# {"state": "decided", "outcome": "accepted"} + +# Protocol projection/action details are available only through the explicit +# advanced API. +raw_lifecycle = client.get_transaction_lifecycle(transaction_hash=tx_hash) +print(raw_lifecycle["stored_status_name"]) +print(raw_lifecycle["projected_status_name"]) +print(raw_lifecycle["resolution_action_name"]) +print(raw_lifecycle["resolution_source_name"]) +# `resolution_action_name == "Finalize"` is the authoritative readiness verdict. +# On current Studio without the advanced lifecycle RPC, only stored status is +# provable; projection repeats it and resolution/decision fields stay inactive. + +# The train stores the execution hash, not the old receipt bytes. +print(tx["tx_execution_hash"]) +# `tx_receipt` remains present but is `None` when the +# protocol cannot supply the old bytes. + # Messages emitted by the contract during execution print(tx["messages"]) # [{"messageType": 1, "recipient": "0x...", "value": 0, "data": "0x...", "onAcceptance": True, "saltNonce": 0}, ...] @@ -151,6 +348,20 @@ print(child_tx_ids) # ["0xabc...", "0xdef..."] ``` +### Active and joined validators + +The active set contains only validators currently eligible for protocol +duties. The joined registry is broader and can include validators that are not +yet selectable, are under-staked, or are otherwise unavailable. + +```python +active = client.active_validators() +active_count = client.active_validators_count() + +joined = client.joined_validators() +joined_count = client.joined_validators_count() +``` + ### Debugging transaction execution Use `debug_trace_transaction` to inspect the full execution trace of a transaction, including return data, errors, and GenVM logs: diff --git a/genlayer_py/chains/testnet_asimov.py b/genlayer_py/chains/testnet_asimov.py index 8984a80..4a06e6d 100644 --- a/genlayer_py/chains/testnet_asimov.py +++ b/genlayer_py/chains/testnet_asimov.py @@ -1,6 +1,9 @@ from genlayer_py.types import GenLayerChain, NativeCurrency, SimpleContractInfo -from genlayer_py.consensus.abi import CONSENSUS_MAIN_ABI, CONSENSUS_DATA_ABI - +from genlayer_py.consensus.abi import ( + APPEALS_ABI, + CONSENSUS_MAIN_ABI, + CONSENSUS_DATA_ABI, +) TESTNET_JSON_RPC_URL = "https://rpc-asimov.genlayer.com" EXPLORER_URL = "https://explorer-asimov.genlayer.com/" @@ -103,15 +106,7 @@ APPEALS_CONTRACT: SimpleContractInfo = { "address": "0x0F739Dd8f5322b9547c7d19a9621BC2ac8DF4089", - "abi": [ - { - "type": "function", - "name": "canAppeal", - "stateMutability": "view", - "inputs": [{"name": "_txId", "type": "bytes32"}], - "outputs": [{"name": "", "type": "bool"}], - }, - ], + "abi": APPEALS_ABI, } diff --git a/genlayer_py/chains/testnet_bradbury.py b/genlayer_py/chains/testnet_bradbury.py index 352bf3c..44ed0a1 100644 --- a/genlayer_py/chains/testnet_bradbury.py +++ b/genlayer_py/chains/testnet_bradbury.py @@ -1,19 +1,27 @@ -from genlayer_py.types import GenLayerChain, NativeCurrency, ContractInfo, SimpleContractInfo -from genlayer_py.consensus.abi import CONSENSUS_MAIN_ABI_V06, CONSENSUS_DATA_ABI_V06 - +from genlayer_py.types import ( + GenLayerChain, + NativeCurrency, + ContractInfo, + SimpleContractInfo, +) +from genlayer_py.consensus.abi import ( + APPEALS_ABI, + CONSENSUS_MAIN_ABI, + CONSENSUS_DATA_ABI, +) TESTNET_JSON_RPC_URL = "https://rpc-bradbury.genlayer.com" EXPLORER_URL = "https://explorer-bradbury.genlayer.com/" CONSENSUS_MAIN_CONTRACT: ContractInfo = { "address": "0x0112Bf6e83497965A5fdD6Dad1E447a6E004271D", - "abi": CONSENSUS_MAIN_ABI_V06, + "abi": CONSENSUS_MAIN_ABI, "bytecode": "", } CONSENSUS_DATA_CONTRACT: ContractInfo = { "address": "0x85D7bf947A512Fc640C75327A780c90847267697", - "abi": CONSENSUS_DATA_ABI_V06, + "abi": CONSENSUS_DATA_ABI, "bytecode": "", } @@ -103,15 +111,7 @@ APPEALS_CONTRACT: SimpleContractInfo = { "address": "0xbb8C35AA878D09b9830aFF9e5aAC6492BFbd5471", - "abi": [ - { - "type": "function", - "name": "canAppeal", - "stateMutability": "view", - "inputs": [{"name": "_txId", "type": "bytes32"}], - "outputs": [{"name": "", "type": "bool"}], - }, - ], + "abi": APPEALS_ABI, } diff --git a/genlayer_py/client/genlayer_client.py b/genlayer_py/client/genlayer_client.py index a453dc3..77392ca 100644 --- a/genlayer_py/client/genlayer_client.py +++ b/genlayer_py/client/genlayer_client.py @@ -7,7 +7,6 @@ from typing import AnyStr, Literal from genlayer_py.types import ( GenLayerChain, - TransactionStatus, CalldataEncodable, GenLayerTransaction, ContractSchema, @@ -28,6 +27,8 @@ get_round_data, get_last_round_data, can_appeal, + get_appeal_quote, + get_appeal_charge, get_min_appeal_bond, get_contract_schema, get_contract_schema_for_code, @@ -40,11 +41,15 @@ ) from genlayer_py.chains.actions import initialize_consensus_smart_contract from genlayer_py.transactions.actions import ( + wait_for_decision, + wait_for_finalization, wait_for_transaction_receipt, get_transaction, + get_transaction_lifecycle, get_triggered_transaction_ids, debug_trace_transaction, ) +from genlayer_py.types.transactions import ProtocolTransactionLifecycle from genlayer_py.staking.actions import ( validator_join, validator_deposit, @@ -53,6 +58,7 @@ validator_prime, set_operator, get_operator_transfer_context, + get_validator_join_context, initiate_operator_transfer, complete_operator_transfer, cancel_operator_transfer, @@ -64,6 +70,8 @@ epoch as staking_epoch, active_validators, active_validators_count, + joined_validators, + joined_validators_count, is_validator, get_validator_info, get_stake_info, @@ -71,6 +79,10 @@ validator_min_stake, delegator_min_stake, ) +from genlayer_py.staking.operator_registration import ( + OperatorRegistrationContext, + OperatorRegistrationProof, +) from genlayer_py.config import transaction_config from genlayer_py.transactions.fees import ( FeeEstimateOptions, @@ -325,50 +337,112 @@ def top_up_and_submit_appeal( distribution: FeesDistributionInput, account: Optional[LocalAccount] = None, value: Optional[int] = None, + expected_decision_id: Optional[int] = None, ) -> HexStr: - """Deposits appeal fee budget and submits an appeal in one consensus call.""" + """Deposits appeal funding and submits an appeal. + + On deployed Consensus, omitted decision/value inputs are resolved from + the authoritative appeal quote. Current Studio requires an explicit + value and does not accept ``expected_decision_id``. + """ return top_up_and_submit_appeal( self=self, transaction_id=transaction_id, distribution=distribution, account=account, value=value, + expected_decision_id=expected_decision_id, ) # Transaction actions def wait_for_transaction_receipt( self, transaction_hash: _Hash32, - status: Optional[TransactionStatus] = None, - wait_until: Optional[Literal["decided", "finalized"]] = None, + wait_until: Literal["decided", "finalized"] = "decided", interval: int = transaction_config.wait_interval, retries: int = transaction_config.retries, full_transaction: bool = False, ) -> GenLayerTransaction: - """Polls until a transaction reaches the specified status. Returns the transaction receipt.""" + """Poll for a stored decision (default) or stored finalization.""" return wait_for_transaction_receipt( self=self, transaction_hash=transaction_hash, - status=status, wait_until=wait_until, interval=interval, retries=retries, full_transaction=full_transaction, ) + def wait_for_decision( + self, + transaction_hash: _Hash32, + interval: int = transaction_config.wait_interval, + retries: int = transaction_config.retries, + full_transaction: bool = False, + ) -> GenLayerTransaction: + """Poll until the stored transaction state is decided or terminal.""" + return wait_for_decision( + self=self, + transaction_hash=transaction_hash, + interval=interval, + retries=retries, + full_transaction=full_transaction, + ) + + def wait_for_finalization( + self, + transaction_hash: _Hash32, + interval: int = transaction_config.wait_interval, + retries: int = transaction_config.retries, + full_transaction: bool = False, + ) -> GenLayerTransaction: + """Poll until the stored transaction state is finalized.""" + return wait_for_finalization( + self=self, + transaction_hash=transaction_hash, + interval=interval, + retries=retries, + full_transaction=full_transaction, + ) + def get_transaction( self, transaction_hash: _Hash32, ) -> GenLayerTransaction: - """Fetches transaction data including status, execution result, and consensus details.""" + """Fetch transaction data with a stable stored-state ``lifecycle``. + + The lifecycle's ``state`` is one of processing, decided, finalized, or + canceled. Processing carries ``phase`` and decided carries ``outcome``. + The train exposes ``tx_execution_hash``; legacy receipt bytes are + unavailable, so ``tx_receipt`` is ``None``. + """ return get_transaction(self=self, transaction_hash=transaction_hash) + def get_transaction_lifecycle( + self, + transaction_hash: _Hash32, + timestamp: Optional[int] = None, + ) -> ProtocolTransactionLifecycle: + """Return advanced stored/projected/action protocol lifecycle data. + + If current Studio does not expose the advanced RPC, only its provable + stored status is returned: projection repeats it, resolution is + NoOp/Unspecified, and decision identity is inactive. + """ + return get_transaction_lifecycle( + self=self, + transaction_hash=transaction_hash, + timestamp=timestamp, + ) + def get_triggered_transaction_ids( self, transaction_hash: _Hash32, ) -> list: """Returns transaction IDs of child transactions created from emitted messages.""" - return get_triggered_transaction_ids(self=self, transaction_hash=transaction_hash) + return get_triggered_transaction_ids( + self=self, transaction_hash=transaction_hash + ) def debug_trace_transaction( self, @@ -376,21 +450,29 @@ def debug_trace_transaction( round: int = 0, ) -> dict: """Fetches the full execution trace including return data, stdout, stderr, and GenVM logs.""" - return debug_trace_transaction(self=self, transaction_hash=transaction_hash, round=round) + return debug_trace_transaction( + self=self, transaction_hash=transaction_hash, round=round + ) def appeal_transaction( self, transaction_id: HexStr, account: Optional[LocalAccount] = None, value: Optional[int] = None, + expected_decision_id: Optional[int] = None, ): """Appeals a consensus transaction to trigger a new round of validation. - Returns the original transaction_id (appeals operate on the same tx).""" + Returns the original transaction_id (appeals operate on the same tx). + Deployed Consensus fills missing decision/value inputs from its + authoritative quote. Current Studio requires an explicit value and does + not accept ``expected_decision_id``. + """ return appeal_transaction( self=self, transaction_id=transaction_id, account=account, value=value, + expected_decision_id=expected_decision_id, ) def get_round_number(self, transaction_id: HexStr) -> int: @@ -405,12 +487,37 @@ def get_last_round_data(self, transaction_id: HexStr) -> tuple: """Returns the current round number and its data.""" return get_last_round_data(self=self, transaction_id=transaction_id) - def can_appeal(self, transaction_id: HexStr) -> bool: - """Checks if a transaction can be appealed.""" - return can_appeal(self=self, transaction_id=transaction_id) + def can_appeal( + self, + transaction_id: HexStr, + expected_decision_id: Optional[int] = None, + ) -> bool: + """Checks whether the exact active decision can be appealed on a network. + + This decision-bound read is not available on current Studio. + """ + return can_appeal( + self=self, + transaction_id=transaction_id, + expected_decision_id=expected_decision_id, + ) + + def get_appeal_quote(self, transaction_id: HexStr) -> Dict[str, int]: + """Returns a network's latest decision id, appeal charges, and deadline. + + Current Studio has no decision-bound quote surface. + """ + return get_appeal_quote(self=self, transaction_id=transaction_id) + + def get_appeal_charge(self, transaction_id: HexStr) -> int: + """Returns the full appeal payment (bond plus induced-work funding). + + Current Studio has no decision-bound quote surface. + """ + return get_appeal_charge(self=self, transaction_id=transaction_id) def get_min_appeal_bond(self, transaction_id: HexStr) -> int: - """Calculates the minimum bond required to appeal a transaction.""" + """Deprecated alias for :meth:`get_appeal_charge`.""" return get_min_appeal_bond(self=self, transaction_id=transaction_id) # ── Staking actions (EVM, not consensus-layer) ──────────────────── @@ -422,12 +529,21 @@ def staking_epoch(self) -> int: return staking_epoch(self=self) def active_validators(self) -> List: - """Returns ValidatorWallet addresses active in the current epoch.""" + """Returns ValidatorWallet addresses currently eligible for duties.""" return active_validators(self=self) def active_validators_count(self) -> int: + """Returns the number of validators currently eligible for duties.""" return active_validators_count(self=self) + def joined_validators(self) -> List: + """Returns every ValidatorWallet in the append-only joined registry.""" + return joined_validators(self=self) + + def joined_validators_count(self) -> int: + """Returns the size of the append-only joined validator registry.""" + return joined_validators_count(self=self) + def is_validator(self, address) -> bool: return is_validator(self=self, address=address) @@ -451,15 +567,25 @@ def delegator_min_stake(self) -> int: def validator_join( self, amount: int, - operator=None, + registration: Optional[OperatorRegistrationProof] = None, account: Optional[LocalAccount] = None, + operator=None, ) -> HexBytes: - """Joins as a validator. Deploys a ValidatorWallet with msg.sender - as owner and `operator` (defaults to owner) as operator.""" + """Joins with a proof-bound operator key and deploys a ValidatorWallet.""" return validator_join( - self=self, amount=amount, operator=operator, account=account + self=self, + amount=amount, + registration=registration, + account=account, + operator=operator, ) + def get_validator_join_context( + self, account: Optional[LocalAccount] = None + ) -> OperatorRegistrationContext: + """Factory-bound context for building a validator join proof.""" + return get_validator_join_context(self=self, account=account) + def validator_deposit( self, validator, amount: int, account: Optional[LocalAccount] = None ) -> HexBytes: @@ -490,7 +616,7 @@ def validator_prime( def set_operator( self, validator, operator, account: Optional[LocalAccount] = None ) -> HexBytes: - """Rotates the operator for an existing ValidatorWallet.""" + """Raises with migration guidance for proof-based operator rotation.""" return set_operator( self=self, validator=validator, operator=operator, account=account ) @@ -519,9 +645,7 @@ def cancel_operator_transfer( self, validator, account: Optional[LocalAccount] = None ) -> HexBytes: """Abandons a pending operator rotation.""" - return cancel_operator_transfer( - self=self, validator=validator, account=account - ) + return cancel_operator_transfer(self=self, validator=validator, account=account) def get_pending_operator(self, validator) -> dict: """Pending operator and when its transfer was initiated.""" diff --git a/genlayer_py/consensus/abi/__init__.py b/genlayer_py/consensus/abi/__init__.py index 86bef2c..5c5c920 100644 --- a/genlayer_py/consensus/abi/__init__.py +++ b/genlayer_py/consensus/abi/__init__.py @@ -1,37 +1,45 @@ import json import importlib.resources -with importlib.resources.as_file( - importlib.resources.files("genlayer_py.consensus.abi").joinpath( - "consensus_data_abi.json" - ) -) as path, open(path, "r", encoding="utf-8") as f: - CONSENSUS_DATA_ABI = json.load(f) -with importlib.resources.as_file( - importlib.resources.files("genlayer_py.consensus.abi").joinpath( - "consensus_main_abi.json" - ) -) as path, open(path, "r", encoding="utf-8") as f: - CONSENSUS_MAIN_ABI = json.load(f) +def _load_abi(name: str): + with ( + importlib.resources.as_file( + importlib.resources.files("genlayer_py.consensus.abi").joinpath(name) + ) as path, + open(path, "r", encoding="utf-8") as file, + ): + return json.load(file) -with importlib.resources.as_file( - importlib.resources.files("genlayer_py.consensus.abi").joinpath( - "consensus_data_abi_v06.json" - ) -) as path, open(path, "r", encoding="utf-8") as f: - CONSENSUS_DATA_ABI_V06 = json.load(f) -with importlib.resources.as_file( - importlib.resources.files("genlayer_py.consensus.abi").joinpath( - "consensus_main_abi_v06.json" - ) -) as path, open(path, "r", encoding="utf-8") as f: - CONSENSUS_MAIN_ABI_V06 = json.load(f) +CONSENSUS_DATA_LIFECYCLE_ABI = _load_abi("consensus_data_lifecycle_abi.json") +CONSENSUS_DATA_TRAIN_READS_ABI = _load_abi("consensus_data_train_reads_abi.json") +CONSENSUS_DATA_TRAIN_ABI = CONSENSUS_DATA_LIFECYCLE_ABI + CONSENSUS_DATA_TRAIN_READS_ABI +CONSENSUS_DATA_ABI = _load_abi("consensus_data_abi.json") +CONSENSUS_MAIN_ABI = _load_abi("consensus_main_abi.json") +APPEALS_ABI = _load_abi("appeals_abi.json") + +# Preserve the historical import names without preserving an old contract +# surface. This SDK release targets the train ABI only. +CONSENSUS_DATA_ABI_V06 = CONSENSUS_DATA_ABI +CONSENSUS_MAIN_ABI_V06 = CONSENSUS_MAIN_ABI + +CONSENSUS_DATA_BIG_ROUNDS_ABI = _load_abi("consensus_data_big_rounds_abi.json") +ADDRESS_MANAGER_ABI = _load_abi("address_manager_abi.json") +TRANSACTION_MANAGER_READ_ABI = _load_abi("transaction_manager_read_abi.json") +ROUNDS_STORAGE_READ_ABI = _load_abi("rounds_storage_read_abi.json") __all__ = [ "CONSENSUS_DATA_ABI", "CONSENSUS_MAIN_ABI", "CONSENSUS_DATA_ABI_V06", "CONSENSUS_MAIN_ABI_V06", + "APPEALS_ABI", + "CONSENSUS_DATA_LIFECYCLE_ABI", + "CONSENSUS_DATA_TRAIN_READS_ABI", + "CONSENSUS_DATA_TRAIN_ABI", + "CONSENSUS_DATA_BIG_ROUNDS_ABI", + "ADDRESS_MANAGER_ABI", + "TRANSACTION_MANAGER_READ_ABI", + "ROUNDS_STORAGE_READ_ABI", ] diff --git a/genlayer_py/consensus/abi/address_manager_abi.json b/genlayer_py/consensus/abi/address_manager_abi.json new file mode 100644 index 0000000..424b78f --- /dev/null +++ b/genlayer_py/consensus/abi/address_manager_abi.json @@ -0,0 +1,21 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "key", + "type": "string" + } + ], + "name": "getAddressNonZero", + "outputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/genlayer_py/consensus/abi/appeals_abi.json b/genlayer_py/consensus/abi/appeals_abi.json new file mode 100644 index 0000000..7e5f616 --- /dev/null +++ b/genlayer_py/consensus/abi/appeals_abi.json @@ -0,0 +1,710 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AccessControlBadConfirmation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "neededRole", + "type": "bytes32" + } + ], + "name": "AccessControlUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "CanNotAppeal", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAppealBond", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidSender", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTransactionStatus", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "validators", + "type": "address[]" + } + ], + "name": "AppealCommitteeSelected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "AppealSelectionAborted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "round", + "type": "uint256" + } + ], + "name": "UnionRoundCreated", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "addressManager", + "outputs": [ + { + "internalType": "contract IAddressManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "canAppeal", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addressManager", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callerConfirmation", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addressManager", + "type": "address" + } + ], + "name": "setAddressManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.VoteType", + "name": "txExecutionResult", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "address", + "name": "txOrigin", + "type": "address" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numOfInitialValidators", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "resultHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange[]", + "name": "readStateBlockRanges", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "validUntil", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lockedStorageUnitPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeUsed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lockedReceiptGasPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "txSigner", + "type": "address" + }, + { + "internalType": "enum ITransactions.QueueContext", + "name": "queueContext", + "type": "uint8" + } + ], + "internalType": "struct ITransactions.Transaction", + "name": "_transaction", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "_appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "submitAppeal", + "outputs": [ + { + "internalType": "address[]", + "name": "appealValidators", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "address", + "name": "newLeader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "lastRoundLeaderTimeoutOrUndetermined", + "type": "bool" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "previousStatus", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/genlayer_py/consensus/abi/consensus_data_abi.json b/genlayer_py/consensus/abi/consensus_data_abi.json index ac30f82..ef27aa5 100644 --- a/genlayer_py/consensus/abi/consensus_data_abi.json +++ b/genlayer_py/consensus/abi/consensus_data_abi.json @@ -1,2577 +1,2573 @@ [ - { - "inputs": [], - "name": "AccessControlBadConfirmation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "neededRole", - "type": "bytes32" - } - ], - "name": "AccessControlUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "version", - "type": "uint64" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "previousAdminRole", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "newAdminRole", - "type": "bytes32" - } - ], - "name": "RoleAdminChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleGranted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleRevoked", - "type": "event" - }, - { - "inputs": [], - "name": "DEFAULT_ADMIN_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "acceptOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_currentTimestamp", - "type": "uint256" - } - ], - "name": "canFinalize", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "consensusMain", - "outputs": [ - { - "internalType": "contract IConsensusMain", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_tx_id", - "type": "bytes32" - } - ], - "name": "getLastAppealResult", - "outputs": [ - { - "internalType": "enum ITransactions.ResultType", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestAcceptedTransaction", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "txReceipt", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - } - ], - "internalType": "struct ConsensusData.TransactionData", - "name": "txData", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "pageSize", - "type": "uint256" - } - ], - "name": "getLatestAcceptedTransactions", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "txReceipt", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - } - ], - "internalType": "struct ConsensusData.TransactionData[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestAcceptedTxCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestFinalizedTransaction", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "txReceipt", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - } - ], - "internalType": "struct ConsensusData.TransactionData", - "name": "txData", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "pageSize", - "type": "uint256" - } - ], - "name": "getLatestFinalizedTransactions", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "txReceipt", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - } - ], - "internalType": "struct ConsensusData.TransactionData[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestFinalizedTxCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestPendingTxCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "slot", - "type": "uint256" - } - ], - "name": "getLatestPendingTxId", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestUndeterminedTransaction", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "txReceipt", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - } - ], - "internalType": "struct ConsensusData.TransactionData", - "name": "txData", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestUndeterminedTxCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_tx_id", - "type": "bytes32" - } - ], - "name": "getMessagesForTransaction", - "outputs": [ - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "", - "type": "tuple[]" - }, - { - "internalType": "address", - "name": "ghostAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numOfMessagesIssuedOnAcceptance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "numOfMessagesIssuedOnFinalization", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_tx_id", - "type": "bytes32" - } - ], - "name": "getReadStateBlockRangeForTransaction", - "outputs": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "endIndex", - "type": "uint256" - } - ], - "name": "getRecipientQueues", - "outputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "uint256", - "name": "head", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "tail", - "type": "uint256" - }, - { - "internalType": "bytes32[]", - "name": "txIds", - "type": "bytes32[]" - } - ], - "internalType": "struct IQueues.QueueInfoView", - "name": "pending", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "head", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "tail", - "type": "uint256" - }, - { - "internalType": "bytes32[]", - "name": "txIds", - "type": "bytes32[]" - } - ], - "internalType": "struct IQueues.QueueInfoView", - "name": "accepted", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "head", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "tail", - "type": "uint256" - }, - { - "internalType": "bytes32[]", - "name": "txIds", - "type": "bytes32[]" - } - ], - "internalType": "struct IQueues.QueueInfoView", - "name": "undetermined", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "finalizedCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "issuedTxCount", - "type": "uint256" - } - ], - "internalType": "struct IQueues.RecipientQueuesView", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - } - ], - "name": "getRoleAdmin", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getTotalNumOfTransactions", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_tx_id", - "type": "bytes32" - } - ], - "name": "getTransactionAllData", - "outputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "id", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "previousStatus", - "type": "uint8" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "created", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "pending", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "activated", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "committed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVote", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealSubmitted", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.Timestamps", - "name": "timestamps", - "type": "tuple" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "bool", - "name": "onAcceptanceMessages", - "type": "bool" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "txReceipt", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "address[]", - "name": "consumedValidators", - "type": "address[]" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - } - ], - "internalType": "struct ITransactions.RoundData[]", - "name": "roundData", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "numOfMessagesIssuedOnAcceptance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "numOfMessagesIssuedOnFinalization", - "type": "uint256" - }, - { - "internalType": "address", - "name": "txOrigin", - "type": "address" - }, - { - "internalType": "uint256", - "name": "initialRotations", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.Transaction", - "name": "transaction", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_tx_id", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_timestamp", - "type": "uint256" - } - ], - "name": "getTransactionData", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "txData", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "txReceipt", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - } - ], - "internalType": "struct ConsensusData.TransactionData", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "endIndex", - "type": "uint256" - } - ], - "name": "getTransactionIndexToTxId", - "outputs": [ - { - "internalType": "bytes32[]", - "name": "", - "type": "bytes32[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_tx_id", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_timestamp", - "type": "uint256" - } - ], - "name": "getTransactionStatus", - "outputs": [ - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_tx_id", - "type": "bytes32" - } - ], - "name": "getValidatorsForLastAppeal", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_tx_id", - "type": "bytes32" - } - ], - "name": "getValidatorsForLastRound", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "grantRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "hasRole", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_tx_id", - "type": "bytes32" - } - ], - "name": "hasTransactionOnAcceptanceMessages", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_tx_id", - "type": "bytes32" - } - ], - "name": "hasTransactionOnFinalizationMessages", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_consensusMain", - "type": "address" - }, - { - "internalType": "address", - "name": "_transactions", - "type": "address" - }, - { - "internalType": "address", - "name": "_queues", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pendingOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "queues", - "outputs": [ - { - "internalType": "contract IQueues", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "callerConfirmation", - "type": "address" - } - ], - "name": "renounceRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "revokeRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_consensusMain", - "type": "address" - } - ], - "name": "setConsensusMain", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_queues", - "type": "address" - } - ], - "name": "setQueues", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_transactions", - "type": "address" - } - ], - "name": "setTransactions", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "interfaceId", - "type": "bytes4" - } - ], - "name": "supportsInterface", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "transactions", - "outputs": [ - { - "internalType": "contract ITransactions", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] \ No newline at end of file + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AccessControlBadConfirmation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "neededRole", + "type": "bytes32" + } + ], + "name": "AccessControlUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTransactionStatus", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "status", + "type": "uint8" + } + ], + "name": "PhaseDeadlineNotInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "addressManager", + "outputs": [ + { + "internalType": "contract IAddressManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_currentTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "canFinalize", + "outputs": [ + { + "internalType": "bool", + "name": "ready", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "evaluatedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealDeadline", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "estimateAppealBond", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "estimateLatestAppealCharge", + "outputs": [ + { + "internalType": "uint256", + "name": "decisionId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "funding", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealDeadline", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getCurrentActivator", + "outputs": [ + { + "internalType": "address", + "name": "newActivator", + "type": "address" + }, + { + "internalType": "bool", + "name": "activatorIsIdle", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "slots", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "secondsUntilNextBoundary", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "currentStatus", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getCurrentLeader", + "outputs": [ + { + "internalType": "address", + "name": "newLeader", + "type": "address" + }, + { + "internalType": "bool", + "name": "leaderIsIdle", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "slots", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "secondsUntilNextBoundary", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "currentStatus", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getCurrentValidators", + "outputs": [ + { + "internalType": "address[]", + "name": "validators", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "idleIndexes", + "type": "uint256[]" + }, + { + "internalType": "bool", + "name": "leaderIsIdle", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "slots", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "secondsUntilNextBoundary", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "currentStatus", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getLatestAcceptedTransaction", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "observedAt", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastVoteTimestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + { + "internalType": "enum IQueues.QueueType", + "name": "queueType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "queuePosition", + "type": "uint256" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "address", + "name": "lastLeader", + "type": "address" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "numOfRounds", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData", + "name": "lastRound", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "consumedValidators", + "type": "address[]" + } + ], + "internalType": "struct ConsensusData.TransactionData", + "name": "inputData", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pageSize", + "type": "uint256" + } + ], + "name": "getLatestAcceptedTransactions", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "observedAt", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastVoteTimestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + { + "internalType": "enum IQueues.QueueType", + "name": "queueType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "queuePosition", + "type": "uint256" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "address", + "name": "lastLeader", + "type": "address" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "numOfRounds", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData", + "name": "lastRound", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "consumedValidators", + "type": "address[]" + } + ], + "internalType": "struct ConsensusData.TransactionData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getLatestAcceptedTxCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getLatestFinalizedTransaction", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "observedAt", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastVoteTimestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + { + "internalType": "enum IQueues.QueueType", + "name": "queueType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "queuePosition", + "type": "uint256" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "address", + "name": "lastLeader", + "type": "address" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "numOfRounds", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData", + "name": "lastRound", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "consumedValidators", + "type": "address[]" + } + ], + "internalType": "struct ConsensusData.TransactionData", + "name": "inputData", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pageSize", + "type": "uint256" + } + ], + "name": "getLatestFinalizedTransactions", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "observedAt", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastVoteTimestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + { + "internalType": "enum IQueues.QueueType", + "name": "queueType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "queuePosition", + "type": "uint256" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "address", + "name": "lastLeader", + "type": "address" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "numOfRounds", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData", + "name": "lastRound", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "consumedValidators", + "type": "address[]" + } + ], + "internalType": "struct ConsensusData.TransactionData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getLatestFinalizedTxCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getStoredTransactionData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "observedAt", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastVoteTimestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + { + "internalType": "enum IQueues.QueueType", + "name": "queueType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "queuePosition", + "type": "uint256" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "address", + "name": "lastLeader", + "type": "address" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "numOfRounds", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData", + "name": "lastRound", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "consumedValidators", + "type": "address[]" + } + ], + "internalType": "struct ConsensusData.TransactionData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getStoredTransactionStatus", + "outputs": [ + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getTransactionAllData", + "outputs": [ + { + "components": [ + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.VoteType", + "name": "txExecutionResult", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "address", + "name": "txOrigin", + "type": "address" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numOfInitialValidators", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "resultHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange[]", + "name": "readStateBlockRanges", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "validUntil", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lockedStorageUnitPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeUsed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lockedReceiptGasPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "txSigner", + "type": "address" + }, + { + "internalType": "enum ITransactions.QueueContext", + "name": "queueContext", + "type": "uint8" + } + ], + "internalType": "struct ITransactions.Transaction", + "name": "transaction", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData[]", + "name": "roundsData", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_timestamp", + "type": "uint256" + } + ], + "name": "getTransactionLifecycle", + "outputs": [ + { + "components": [ + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "storedStatus", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "storedStatus", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "projectedStatus", + "type": "uint8" + }, + { + "internalType": "enum IIdlenessPhase.ResolutionAction", + "name": "action", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "resultHash", + "type": "bytes32" + }, + { + "internalType": "enum ITransactionManager.ResolutionSource", + "name": "source", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "sourceRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sourceGeneration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "sourceRoundContextHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "roundPlanHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "resultRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "resultGeneration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "basisDecisionId", + "type": "uint256" + }, + { + "internalType": "enum ITransactionManager.ResolutionContext", + "name": "context", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "attemptId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "boundaryAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "evaluatedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "snapshotBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "decisionWindow", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealDeadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "materializesDecision", + "type": "bool" + }, + { + "internalType": "bool", + "name": "actionOutcomeDeterministic", + "type": "bool" + }, + { + "internalType": "bool", + "name": "nonCurrentEvaluation", + "type": "bool" + } + ], + "internalType": "struct IIdlenessPhase.ResolutionPlan", + "name": "resolution", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "decisionId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "basisDecisionId", + "type": "uint256" + }, + { + "internalType": "enum ITransactionManager.ResolutionContext", + "name": "context", + "type": "uint8" + }, + { + "internalType": "enum ITransactionManager.ResolutionSource", + "name": "source", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "sourceAttemptId", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "sourceStatus", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "sourceRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sourceGeneration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "sourceRoundContextHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "roundPlanHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "resultRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "resultGeneration", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "resultHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "effectiveAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "materializedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealDeadline", + "type": "uint256" + } + ], + "internalType": "struct ITransactionManager.DecisionRecord", + "name": "latestDecision", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "decisionActive", + "type": "bool" + } + ], + "internalType": "struct ConsensusData.TransactionLifecycle", + "name": "lifecycle", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getValidatorsForLastRound", + "outputs": [ + { + "internalType": "address[]", + "name": "validators", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addressManager", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callerConfirmation", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addressManager", + "type": "address" + } + ], + "name": "setAddressManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/genlayer_py/consensus/abi/consensus_data_abi_v06.json b/genlayer_py/consensus/abi/consensus_data_abi_v06.json index 60a382a..ef27aa5 100644 --- a/genlayer_py/consensus/abi/consensus_data_abi_v06.json +++ b/genlayer_py/consensus/abi/consensus_data_abi_v06.json @@ -1,1962 +1,2573 @@ [ - { - "inputs": [], - "name": "AccessControlBadConfirmation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "neededRole", - "type": "bytes32" - } - ], - "name": "AccessControlUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "version", - "type": "uint64" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "previousAdminRole", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "newAdminRole", - "type": "bytes32" - } - ], - "name": "RoleAdminChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleGranted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleRevoked", - "type": "event" - }, - { - "inputs": [], - "name": "DEFAULT_ADMIN_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "acceptOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "addressManager", - "outputs": [ - { - "internalType": "contract IAddressManager", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_currentTimestamp", - "type": "uint256" - } - ], - "name": "canFinalize", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestAcceptedTransaction", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "initialRotations", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "txCalldata", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "eqBlocksOutputs", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "saltNonce", - "type": "uint256" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorResultHash", - "type": "bytes32[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - }, - { - "internalType": "address[]", - "name": "consumedValidators", - "type": "address[]" - } - ], - "internalType": "struct ConsensusData.TransactionData", - "name": "inputData", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "pageSize", - "type": "uint256" - } - ], - "name": "getLatestAcceptedTransactions", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "initialRotations", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "txCalldata", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "eqBlocksOutputs", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "saltNonce", - "type": "uint256" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorResultHash", - "type": "bytes32[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - }, - { - "internalType": "address[]", - "name": "consumedValidators", - "type": "address[]" - } - ], - "internalType": "struct ConsensusData.TransactionData[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestAcceptedTxCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestFinalizedTransaction", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "initialRotations", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "txCalldata", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "eqBlocksOutputs", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "saltNonce", - "type": "uint256" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorResultHash", - "type": "bytes32[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - }, - { - "internalType": "address[]", - "name": "consumedValidators", - "type": "address[]" - } - ], - "internalType": "struct ConsensusData.TransactionData", - "name": "inputData", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "pageSize", - "type": "uint256" - } - ], - "name": "getLatestFinalizedTransactions", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "initialRotations", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "txCalldata", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "eqBlocksOutputs", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "saltNonce", - "type": "uint256" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorResultHash", - "type": "bytes32[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - }, - { - "internalType": "address[]", - "name": "consumedValidators", - "type": "address[]" - } - ], - "internalType": "struct ConsensusData.TransactionData[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - } - ], - "name": "getLatestFinalizedTxCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - } - ], - "name": "getRoleAdmin", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "getTransactionAllData", - "outputs": [ - { - "components": [ - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "enum ITransactions.VoteType", - "name": "txExecutionResult", - "type": "uint8" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "previousStatus", - "type": "uint8" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "address", - "name": "txOrigin", - "type": "address" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "initialRotations", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "id", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "resultHash", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "txCalldata", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "eqBlocksOutputs", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange[]", - "name": "readStateBlockRanges", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "validUntil", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.Transaction", - "name": "transaction", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorResultHash", - "type": "bytes32[]" - } - ], - "internalType": "struct ITransactions.RoundData[]", - "name": "roundsData", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_timestamp", - "type": "uint256" - } - ], - "name": "getTransactionData", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "currentTimestamp", - "type": "uint256" - }, - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "initialRotations", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastVoteTimestamp", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "randomSeed", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "txCalldata", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "eqBlocksOutputs", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "saltNonce", - "type": "uint256" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - }, - { - "internalType": "enum IQueues.QueueType", - "name": "queueType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "queuePosition", - "type": "uint256" - }, - { - "internalType": "address", - "name": "activator", - "type": "address" - }, - { - "internalType": "address", - "name": "lastLeader", - "type": "address" - }, - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "activationBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "proposalBlock", - "type": "uint256" - } - ], - "internalType": "struct ITransactions.ReadStateBlockRange", - "name": "readStateBlockRange", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "numOfRounds", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "round", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "leaderIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesCommitted", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "votesRevealed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "appealBond", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rotationsLeft", - "type": "uint256" - }, - { - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - }, - { - "internalType": "address[]", - "name": "roundValidators", - "type": "address[]" - }, - { - "internalType": "enum ITransactions.VoteType[]", - "name": "validatorVotes", - "type": "uint8[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorVotesHash", - "type": "bytes32[]" - }, - { - "internalType": "bytes32[]", - "name": "validatorResultHash", - "type": "bytes32[]" - } - ], - "internalType": "struct ITransactions.RoundData", - "name": "lastRound", - "type": "tuple" - }, - { - "internalType": "address[]", - "name": "consumedValidators", - "type": "address[]" - } - ], - "internalType": "struct ConsensusData.TransactionData", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_timestamp", - "type": "uint256" - } - ], - "name": "getTransactionStatus", - "outputs": [ - { - "internalType": "enum ITransactions.TransactionStatus", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "getValidatorsForLastRound", - "outputs": [ - { - "internalType": "address[]", - "name": "validators", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "grantRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "hasRole", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_addressManager", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pendingOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "callerConfirmation", - "type": "address" - } - ], - "name": "renounceRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "revokeRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_addressManager", - "type": "address" - } - ], - "name": "setAddressManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "interfaceId", - "type": "bytes4" - } - ], - "name": "supportsInterface", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AccessControlBadConfirmation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "neededRole", + "type": "bytes32" + } + ], + "name": "AccessControlUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTransactionStatus", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "status", + "type": "uint8" + } + ], + "name": "PhaseDeadlineNotInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "addressManager", + "outputs": [ + { + "internalType": "contract IAddressManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_currentTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "canFinalize", + "outputs": [ + { + "internalType": "bool", + "name": "ready", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "evaluatedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealDeadline", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "estimateAppealBond", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "estimateLatestAppealCharge", + "outputs": [ + { + "internalType": "uint256", + "name": "decisionId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "funding", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealDeadline", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getCurrentActivator", + "outputs": [ + { + "internalType": "address", + "name": "newActivator", + "type": "address" + }, + { + "internalType": "bool", + "name": "activatorIsIdle", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "slots", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "secondsUntilNextBoundary", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "currentStatus", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getCurrentLeader", + "outputs": [ + { + "internalType": "address", + "name": "newLeader", + "type": "address" + }, + { + "internalType": "bool", + "name": "leaderIsIdle", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "slots", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "secondsUntilNextBoundary", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "currentStatus", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getCurrentValidators", + "outputs": [ + { + "internalType": "address[]", + "name": "validators", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "idleIndexes", + "type": "uint256[]" + }, + { + "internalType": "bool", + "name": "leaderIsIdle", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "slots", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "secondsUntilNextBoundary", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "currentStatus", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getLatestAcceptedTransaction", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "observedAt", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastVoteTimestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + { + "internalType": "enum IQueues.QueueType", + "name": "queueType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "queuePosition", + "type": "uint256" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "address", + "name": "lastLeader", + "type": "address" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "numOfRounds", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData", + "name": "lastRound", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "consumedValidators", + "type": "address[]" + } + ], + "internalType": "struct ConsensusData.TransactionData", + "name": "inputData", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pageSize", + "type": "uint256" + } + ], + "name": "getLatestAcceptedTransactions", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "observedAt", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastVoteTimestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + { + "internalType": "enum IQueues.QueueType", + "name": "queueType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "queuePosition", + "type": "uint256" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "address", + "name": "lastLeader", + "type": "address" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "numOfRounds", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData", + "name": "lastRound", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "consumedValidators", + "type": "address[]" + } + ], + "internalType": "struct ConsensusData.TransactionData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getLatestAcceptedTxCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getLatestFinalizedTransaction", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "observedAt", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastVoteTimestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + { + "internalType": "enum IQueues.QueueType", + "name": "queueType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "queuePosition", + "type": "uint256" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "address", + "name": "lastLeader", + "type": "address" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "numOfRounds", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData", + "name": "lastRound", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "consumedValidators", + "type": "address[]" + } + ], + "internalType": "struct ConsensusData.TransactionData", + "name": "inputData", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pageSize", + "type": "uint256" + } + ], + "name": "getLatestFinalizedTransactions", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "observedAt", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastVoteTimestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + { + "internalType": "enum IQueues.QueueType", + "name": "queueType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "queuePosition", + "type": "uint256" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "address", + "name": "lastLeader", + "type": "address" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "numOfRounds", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData", + "name": "lastRound", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "consumedValidators", + "type": "address[]" + } + ], + "internalType": "struct ConsensusData.TransactionData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getLatestFinalizedTxCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getStoredTransactionData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "observedAt", + "type": "uint256" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastVoteTimestamp", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + { + "internalType": "enum IQueues.QueueType", + "name": "queueType", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "queuePosition", + "type": "uint256" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "address", + "name": "lastLeader", + "type": "address" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "numOfRounds", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData", + "name": "lastRound", + "type": "tuple" + }, + { + "internalType": "address[]", + "name": "consumedValidators", + "type": "address[]" + } + ], + "internalType": "struct ConsensusData.TransactionData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getStoredTransactionStatus", + "outputs": [ + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getTransactionAllData", + "outputs": [ + { + "components": [ + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.VoteType", + "name": "txExecutionResult", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "address", + "name": "txOrigin", + "type": "address" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "address", + "name": "activator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initialRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "numOfInitialValidators", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "randomSeed", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "resultHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "eqBlocksOutputs", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "activationBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposalBlock", + "type": "uint256" + } + ], + "internalType": "struct ITransactions.ReadStateBlockRange[]", + "name": "readStateBlockRanges", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "validUntil", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lockedStorageUnitPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeUsed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lockedReceiptGasPrice", + "type": "uint256" + }, + { + "internalType": "address", + "name": "txSigner", + "type": "address" + }, + { + "internalType": "enum ITransactions.QueueContext", + "name": "queueContext", + "type": "uint8" + } + ], + "internalType": "struct ITransactions.Transaction", + "name": "transaction", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "leaderIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesCommitted", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "votesRevealed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealBond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rotationsLeft", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "address[]", + "name": "roundValidators", + "type": "address[]" + }, + { + "internalType": "enum ITransactions.VoteType[]", + "name": "validatorVotes", + "type": "uint8[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorVotesHash", + "type": "bytes32[]" + }, + { + "internalType": "bytes32[]", + "name": "validatorResultHash", + "type": "bytes32[]" + } + ], + "internalType": "struct ITransactions.RoundData[]", + "name": "roundsData", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_timestamp", + "type": "uint256" + } + ], + "name": "getTransactionLifecycle", + "outputs": [ + { + "components": [ + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "storedStatus", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "storedStatus", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "projectedStatus", + "type": "uint8" + }, + { + "internalType": "enum IIdlenessPhase.ResolutionAction", + "name": "action", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "resultHash", + "type": "bytes32" + }, + { + "internalType": "enum ITransactionManager.ResolutionSource", + "name": "source", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "sourceRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sourceGeneration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "sourceRoundContextHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "roundPlanHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "resultRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "resultGeneration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "basisDecisionId", + "type": "uint256" + }, + { + "internalType": "enum ITransactionManager.ResolutionContext", + "name": "context", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "attemptId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "boundaryAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "evaluatedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "snapshotBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "decisionWindow", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealDeadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "materializesDecision", + "type": "bool" + }, + { + "internalType": "bool", + "name": "actionOutcomeDeterministic", + "type": "bool" + }, + { + "internalType": "bool", + "name": "nonCurrentEvaluation", + "type": "bool" + } + ], + "internalType": "struct IIdlenessPhase.ResolutionPlan", + "name": "resolution", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "bool", + "name": "exists", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "decisionId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "basisDecisionId", + "type": "uint256" + }, + { + "internalType": "enum ITransactionManager.ResolutionContext", + "name": "context", + "type": "uint8" + }, + { + "internalType": "enum ITransactionManager.ResolutionSource", + "name": "source", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "sourceAttemptId", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "sourceStatus", + "type": "uint8" + }, + { + "internalType": "enum ITransactions.TransactionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "sourceRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "sourceGeneration", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "sourceRoundContextHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "roundPlanHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "resultRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "resultGeneration", + "type": "uint256" + }, + { + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "resultHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "effectiveAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "materializedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealDeadline", + "type": "uint256" + } + ], + "internalType": "struct ITransactionManager.DecisionRecord", + "name": "latestDecision", + "type": "tuple" + }, + { + "internalType": "bool", + "name": "decisionActive", + "type": "bool" + } + ], + "internalType": "struct ConsensusData.TransactionLifecycle", + "name": "lifecycle", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getValidatorsForLastRound", + "outputs": [ + { + "internalType": "address[]", + "name": "validators", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addressManager", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callerConfirmation", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addressManager", + "type": "address" + } + ], + "name": "setAddressManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } ] diff --git a/genlayer_py/consensus/abi/consensus_data_big_rounds_abi.json b/genlayer_py/consensus/abi/consensus_data_big_rounds_abi.json new file mode 100644 index 0000000..b213b25 --- /dev/null +++ b/genlayer_py/consensus/abi/consensus_data_big_rounds_abi.json @@ -0,0 +1,115 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "getStoredTransactionDataLight", + "outputs": [ + { + "components": [ + {"internalType": "uint256", "name": "observedAt", "type": "uint256"}, + {"internalType": "address", "name": "sender", "type": "address"}, + {"internalType": "address", "name": "recipient", "type": "address"}, + {"internalType": "uint256", "name": "initialRotations", "type": "uint256"}, + {"internalType": "uint256", "name": "txSlot", "type": "uint256"}, + {"internalType": "uint256", "name": "createdTimestamp", "type": "uint256"}, + {"internalType": "uint256", "name": "lastVoteTimestamp", "type": "uint256"}, + {"internalType": "bytes32", "name": "randomSeed", "type": "bytes32"}, + {"internalType": "enum ITransactions.ResultType", "name": "result", "type": "uint8"}, + {"internalType": "bytes32", "name": "txExecutionHash", "type": "bytes32"}, + {"internalType": "bytes", "name": "txCalldata", "type": "bytes"}, + {"internalType": "bytes", "name": "eqBlocksOutputs", "type": "bytes"}, + { + "components": [ + {"internalType": "enum IMessages.MessageType", "name": "messageType", "type": "uint8"}, + {"internalType": "address", "name": "recipient", "type": "address"}, + {"internalType": "uint256", "name": "value", "type": "uint256"}, + {"internalType": "bytes", "name": "data", "type": "bytes"}, + {"internalType": "bool", "name": "onAcceptance", "type": "bool"}, + {"internalType": "uint256", "name": "saltNonce", "type": "uint256"}, + {"internalType": "bytes", "name": "feeParams", "type": "bytes"}, + {"internalType": "uint256", "name": "declaredBudget", "type": "uint256"}, + {"internalType": "bytes", "name": "allocationSubtree", "type": "bytes"}, + {"internalType": "bytes32", "name": "callKey", "type": "bytes32"}, + {"internalType": "bool", "name": "useBalance", "type": "bool"} + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + }, + {"internalType": "enum IQueues.QueueType", "name": "queueType", "type": "uint8"}, + {"internalType": "uint256", "name": "queuePosition", "type": "uint256"}, + {"internalType": "address", "name": "activator", "type": "address"}, + {"internalType": "address", "name": "lastLeader", "type": "address"}, + {"internalType": "enum ITransactions.TransactionStatus", "name": "status", "type": "uint8"}, + {"internalType": "bytes32", "name": "txId", "type": "bytes32"}, + { + "components": [ + {"internalType": "uint256", "name": "activationBlock", "type": "uint256"}, + {"internalType": "uint256", "name": "processingBlock", "type": "uint256"}, + {"internalType": "uint256", "name": "proposalBlock", "type": "uint256"} + ], + "internalType": "struct ITransactions.ReadStateBlockRange", + "name": "readStateBlockRange", + "type": "tuple" + }, + {"internalType": "uint256", "name": "numOfRounds", "type": "uint256"}, + { + "components": [ + {"internalType": "uint256", "name": "round", "type": "uint256"}, + {"internalType": "uint256", "name": "leaderIndex", "type": "uint256"}, + {"internalType": "uint256", "name": "votesCommitted", "type": "uint256"}, + {"internalType": "uint256", "name": "votesRevealed", "type": "uint256"}, + {"internalType": "uint256", "name": "appealBond", "type": "uint256"}, + {"internalType": "uint256", "name": "rotationsLeft", "type": "uint256"}, + {"internalType": "enum ITransactions.ResultType", "name": "result", "type": "uint8"}, + {"internalType": "uint256", "name": "validatorsCount", "type": "uint256"} + ], + "internalType": "struct ConsensusDataBigRounds.RoundDataLight", + "name": "lastRound", + "type": "tuple" + }, + {"internalType": "uint256", "name": "consumedValidatorsCount", "type": "uint256"} + ], + "internalType": "struct ConsensusDataBigRounds.TransactionDataLight", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + {"internalType": "bytes32", "name": "_txId", "type": "bytes32"}, + {"internalType": "uint256", "name": "_round", "type": "uint256"}, + {"internalType": "uint256", "name": "_offset", "type": "uint256"}, + {"internalType": "uint256", "name": "_limit", "type": "uint256"} + ], + "name": "getRoundValidatorsPaged", + "outputs": [ + {"internalType": "address[]", "name": "page", "type": "address[]"}, + {"internalType": "uint256", "name": "total", "type": "uint256"} + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + {"internalType": "bytes32", "name": "_txId", "type": "bytes32"}, + {"internalType": "uint256", "name": "_offset", "type": "uint256"}, + {"internalType": "uint256", "name": "_limit", "type": "uint256"} + ], + "name": "getConsumedValidatorsPaged", + "outputs": [ + {"internalType": "address[]", "name": "page", "type": "address[]"}, + {"internalType": "uint256", "name": "total", "type": "uint256"} + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/genlayer_py/consensus/abi/consensus_data_lifecycle_abi.json b/genlayer_py/consensus/abi/consensus_data_lifecycle_abi.json new file mode 100644 index 0000000..8a44d30 --- /dev/null +++ b/genlayer_py/consensus/abi/consensus_data_lifecycle_abi.json @@ -0,0 +1,103 @@ +[ + { + "inputs": [], + "name": "addressManager", + "outputs": [ + {"internalType": "contract IAddressManager", "name": "", "type": "address"} + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + {"internalType": "bytes32", "name": "_txId", "type": "bytes32"}, + {"internalType": "uint256", "name": "_timestamp", "type": "uint256"} + ], + "name": "getTransactionLifecycle", + "outputs": [ + { + "components": [ + {"internalType": "enum ITransactions.TransactionStatus", "name": "storedStatus", "type": "uint8"}, + { + "components": [ + {"internalType": "bytes32", "name": "txId", "type": "bytes32"}, + {"internalType": "enum ITransactions.TransactionStatus", "name": "storedStatus", "type": "uint8"}, + {"internalType": "enum ITransactions.TransactionStatus", "name": "projectedStatus", "type": "uint8"}, + {"internalType": "enum IIdlenessPhase.ResolutionAction", "name": "action", "type": "uint8"}, + {"internalType": "enum ITransactions.ResultType", "name": "result", "type": "uint8"}, + {"internalType": "bytes32", "name": "resultHash", "type": "bytes32"}, + {"internalType": "enum ITransactionManager.ResolutionSource", "name": "source", "type": "uint8"}, + {"internalType": "uint256", "name": "sourceRound", "type": "uint256"}, + {"internalType": "uint256", "name": "sourceGeneration", "type": "uint256"}, + {"internalType": "bytes32", "name": "sourceRoundContextHash", "type": "bytes32"}, + {"internalType": "bytes32", "name": "roundPlanHash", "type": "bytes32"}, + {"internalType": "uint256", "name": "resultRound", "type": "uint256"}, + {"internalType": "uint256", "name": "resultGeneration", "type": "uint256"}, + {"internalType": "uint256", "name": "basisDecisionId", "type": "uint256"}, + {"internalType": "enum ITransactionManager.ResolutionContext", "name": "context", "type": "uint8"}, + {"internalType": "bytes32", "name": "attemptId", "type": "bytes32"}, + {"internalType": "uint256", "name": "boundaryAt", "type": "uint256"}, + {"internalType": "uint256", "name": "evaluatedAt", "type": "uint256"}, + {"internalType": "uint256", "name": "snapshotBlock", "type": "uint256"}, + {"internalType": "uint256", "name": "decisionWindow", "type": "uint256"}, + {"internalType": "uint256", "name": "appealDeadline", "type": "uint256"}, + {"internalType": "bool", "name": "materializesDecision", "type": "bool"}, + {"internalType": "bool", "name": "actionOutcomeDeterministic", "type": "bool"}, + {"internalType": "bool", "name": "nonCurrentEvaluation", "type": "bool"} + ], + "internalType": "struct IIdlenessPhase.ResolutionPlan", + "name": "resolution", + "type": "tuple" + }, + { + "components": [ + {"internalType": "bool", "name": "exists", "type": "bool"}, + {"internalType": "uint256", "name": "decisionId", "type": "uint256"}, + {"internalType": "uint256", "name": "basisDecisionId", "type": "uint256"}, + {"internalType": "enum ITransactionManager.ResolutionContext", "name": "context", "type": "uint8"}, + {"internalType": "enum ITransactionManager.ResolutionSource", "name": "source", "type": "uint8"}, + {"internalType": "bytes32", "name": "sourceAttemptId", "type": "bytes32"}, + {"internalType": "enum ITransactions.TransactionStatus", "name": "sourceStatus", "type": "uint8"}, + {"internalType": "enum ITransactions.TransactionStatus", "name": "status", "type": "uint8"}, + {"internalType": "uint256", "name": "sourceRound", "type": "uint256"}, + {"internalType": "uint256", "name": "sourceGeneration", "type": "uint256"}, + {"internalType": "bytes32", "name": "sourceRoundContextHash", "type": "bytes32"}, + {"internalType": "bytes32", "name": "roundPlanHash", "type": "bytes32"}, + {"internalType": "uint256", "name": "resultRound", "type": "uint256"}, + {"internalType": "uint256", "name": "resultGeneration", "type": "uint256"}, + {"internalType": "enum ITransactions.ResultType", "name": "result", "type": "uint8"}, + {"internalType": "bytes32", "name": "resultHash", "type": "bytes32"}, + {"internalType": "uint256", "name": "effectiveAt", "type": "uint256"}, + {"internalType": "uint256", "name": "materializedAt", "type": "uint256"}, + {"internalType": "uint256", "name": "appealDeadline", "type": "uint256"} + ], + "internalType": "struct ITransactionManager.DecisionRecord", + "name": "latestDecision", + "type": "tuple" + }, + {"internalType": "bool", "name": "decisionActive", "type": "bool"} + ], + "internalType": "struct ConsensusData.TransactionLifecycle", + "name": "lifecycle", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + {"internalType": "bytes32", "name": "_txId", "type": "bytes32"}, + {"internalType": "uint256", "name": "_currentTimestamp", "type": "uint256"}, + {"internalType": "uint256", "name": "_expectedDecisionId", "type": "uint256"} + ], + "name": "canFinalize", + "outputs": [ + {"internalType": "bool", "name": "ready", "type": "bool"}, + {"internalType": "uint256", "name": "evaluatedAt", "type": "uint256"}, + {"internalType": "uint256", "name": "appealDeadline", "type": "uint256"} + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/genlayer_py/consensus/abi/consensus_data_train_reads_abi.json b/genlayer_py/consensus/abi/consensus_data_train_reads_abi.json new file mode 100644 index 0000000..104da96 --- /dev/null +++ b/genlayer_py/consensus/abi/consensus_data_train_reads_abi.json @@ -0,0 +1,4 @@ +[ + {"inputs":[{"internalType":"bytes32","name":"_txId","type":"bytes32"}],"name":"getStoredTransactionData","outputs":[{"components":[{"internalType":"uint256","name":"observedAt","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"initialRotations","type":"uint256"},{"internalType":"uint256","name":"txSlot","type":"uint256"},{"internalType":"uint256","name":"createdTimestamp","type":"uint256"},{"internalType":"uint256","name":"lastVoteTimestamp","type":"uint256"},{"internalType":"bytes32","name":"randomSeed","type":"bytes32"},{"internalType":"enum ITransactions.ResultType","name":"result","type":"uint8"},{"internalType":"bytes32","name":"txExecutionHash","type":"bytes32"},{"internalType":"bytes","name":"txCalldata","type":"bytes"},{"internalType":"bytes","name":"eqBlocksOutputs","type":"bytes"},{"components":[{"internalType":"enum IMessages.MessageType","name":"messageType","type":"uint8"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bool","name":"onAcceptance","type":"bool"},{"internalType":"uint256","name":"saltNonce","type":"uint256"},{"internalType":"bytes","name":"feeParams","type":"bytes"},{"internalType":"uint256","name":"declaredBudget","type":"uint256"},{"internalType":"bytes","name":"allocationSubtree","type":"bytes"},{"internalType":"bytes32","name":"callKey","type":"bytes32"},{"internalType":"bool","name":"useBalance","type":"bool"}],"internalType":"struct IMessages.SubmittedMessage[]","name":"messages","type":"tuple[]"},{"internalType":"enum IQueues.QueueType","name":"queueType","type":"uint8"},{"internalType":"uint256","name":"queuePosition","type":"uint256"},{"internalType":"address","name":"activator","type":"address"},{"internalType":"address","name":"lastLeader","type":"address"},{"internalType":"enum ITransactions.TransactionStatus","name":"status","type":"uint8"},{"internalType":"bytes32","name":"txId","type":"bytes32"},{"components":[{"internalType":"uint256","name":"activationBlock","type":"uint256"},{"internalType":"uint256","name":"processingBlock","type":"uint256"},{"internalType":"uint256","name":"proposalBlock","type":"uint256"}],"internalType":"struct ITransactions.ReadStateBlockRange","name":"readStateBlockRange","type":"tuple"},{"internalType":"uint256","name":"numOfRounds","type":"uint256"},{"components":[{"internalType":"uint256","name":"round","type":"uint256"},{"internalType":"uint256","name":"leaderIndex","type":"uint256"},{"internalType":"uint256","name":"votesCommitted","type":"uint256"},{"internalType":"uint256","name":"votesRevealed","type":"uint256"},{"internalType":"uint256","name":"appealBond","type":"uint256"},{"internalType":"uint256","name":"rotationsLeft","type":"uint256"},{"internalType":"enum ITransactions.ResultType","name":"result","type":"uint8"},{"internalType":"address[]","name":"roundValidators","type":"address[]"},{"internalType":"enum ITransactions.VoteType[]","name":"validatorVotes","type":"uint8[]"},{"internalType":"bytes32[]","name":"validatorVotesHash","type":"bytes32[]"},{"internalType":"bytes32[]","name":"validatorResultHash","type":"bytes32[]"}],"internalType":"struct ITransactions.RoundData","name":"lastRound","type":"tuple"},{"internalType":"address[]","name":"consumedValidators","type":"address[]"}],"internalType":"struct ConsensusData.TransactionData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"}, + {"inputs":[{"internalType":"bytes32","name":"_txId","type":"bytes32"}],"name":"getTransactionAllData","outputs":[{"components":[{"internalType":"enum ITransactions.ResultType","name":"result","type":"uint8"},{"internalType":"enum ITransactions.VoteType","name":"txExecutionResult","type":"uint8"},{"internalType":"enum ITransactions.TransactionStatus","name":"status","type":"uint8"},{"internalType":"address","name":"txOrigin","type":"address"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"activator","type":"address"},{"internalType":"uint256","name":"txSlot","type":"uint256"},{"internalType":"uint256","name":"initialRotations","type":"uint256"},{"internalType":"uint256","name":"numOfInitialValidators","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"bytes32","name":"randomSeed","type":"bytes32"},{"internalType":"bytes32","name":"txExecutionHash","type":"bytes32"},{"internalType":"bytes32","name":"resultHash","type":"bytes32"},{"internalType":"bytes","name":"txCalldata","type":"bytes"},{"internalType":"bytes","name":"eqBlocksOutputs","type":"bytes"},{"components":[{"internalType":"uint256","name":"activationBlock","type":"uint256"},{"internalType":"uint256","name":"processingBlock","type":"uint256"},{"internalType":"uint256","name":"proposalBlock","type":"uint256"}],"internalType":"struct ITransactions.ReadStateBlockRange[]","name":"readStateBlockRanges","type":"tuple[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"lockedStorageUnitPrice","type":"uint256"},{"internalType":"uint256","name":"storageFeeUsed","type":"uint256"},{"internalType":"uint256","name":"lockedReceiptGasPrice","type":"uint256"},{"internalType":"address","name":"txSigner","type":"address"},{"internalType":"enum ITransactions.QueueContext","name":"queueContext","type":"uint8"}],"internalType":"struct ITransactions.Transaction","name":"transaction","type":"tuple"},{"components":[{"internalType":"uint256","name":"round","type":"uint256"},{"internalType":"uint256","name":"leaderIndex","type":"uint256"},{"internalType":"uint256","name":"votesCommitted","type":"uint256"},{"internalType":"uint256","name":"votesRevealed","type":"uint256"},{"internalType":"uint256","name":"appealBond","type":"uint256"},{"internalType":"uint256","name":"rotationsLeft","type":"uint256"},{"internalType":"enum ITransactions.ResultType","name":"result","type":"uint8"},{"internalType":"address[]","name":"roundValidators","type":"address[]"},{"internalType":"enum ITransactions.VoteType[]","name":"validatorVotes","type":"uint8[]"},{"internalType":"bytes32[]","name":"validatorVotesHash","type":"bytes32[]"},{"internalType":"bytes32[]","name":"validatorResultHash","type":"bytes32[]"}],"internalType":"struct ITransactions.RoundData[]","name":"roundsData","type":"tuple[]"}],"stateMutability":"view","type":"function"} +] diff --git a/genlayer_py/consensus/abi/consensus_main_abi.json b/genlayer_py/consensus/abi/consensus_main_abi.json index d6f97c3..c63e61e 100644 --- a/genlayer_py/consensus/abi/consensus_main_abi.json +++ b/genlayer_py/consensus/abi/consensus_main_abi.json @@ -1,1114 +1,2361 @@ [ - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "oldActivator", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newActivator", - "type": "address" - } - ], - "name": "ActivatorReplaced", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "addressManager", - "type": "address" - } - ], - "name": "AddressManagerSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "enum ITransactions.TransactionStatus", - "name": "newStatus", - "type": "uint8" - } - ], - "name": "AllVotesCommitted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "appellant", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "bond", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address[]", - "name": "validators", - "type": "address[]" - } - ], - "name": "AppealStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "attempted", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "succeeded", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "failed", - "type": "uint256" - } - ], - "name": "BatchFinalizationCompleted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - } - ], - "name": "CreatedTransaction", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "activator", - "type": "address" - } - ], - "name": "InternalMessageProcessed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "oldLeader", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newLeader", - "type": "address" - } - ], - "name": "LeaderIdlenessProcessed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "activator", - "type": "address" - } - ], - "name": "NewTransaction", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "ProcessIdlenessAccepted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionAccepted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "leader", - "type": "address" - } - ], - "name": "TransactionActivated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "cancelledBy", - "type": "address" - } - ], - "name": "TransactionCancelled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionFinalizationFailed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionFinalized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionLeaderRevealed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "newLeader", - "type": "address" - } - ], - "name": "TransactionLeaderRotated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionLeaderTimeout", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32[]", - "name": "txIds", - "type": "bytes32[]" - } - ], - "name": "TransactionNeedsRecomputation", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "address[]", - "name": "validators", - "type": "address[]" - } - ], - "name": "TransactionReceiptProposed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionUndetermined", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "tribunalIndex", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "TribunalAppealVoteCommitted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "tribunalIndex", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "enum ITransactions.VoteType", - "name": "voteType", - "type": "uint8" - } - ], - "name": "TribunalAppealVoteRevealed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "oldValidator", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newValidator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "validatorIndex", - "type": "uint256" - } - ], - "name": "ValidatorReplaced", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "ValueWithdrawalFailed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isLastVote", - "type": "bool" - } - ], - "name": "VoteCommitted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "enum ITransactions.VoteType", - "name": "voteType", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isLastVote", - "type": "bool" - }, - { - "indexed": false, - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - } - ], - "name": "VoteRevealed", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "_operator", - "type": "address" - }, - { - "internalType": "bytes", - "name": "_vrfProof", - "type": "bytes" - } - ], - "name": "activateTransaction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_sender", - "type": "address" - }, - { - "internalType": "address", - "name": "_recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_maxRotations", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_calldata", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "_validUntil", - "type": "uint256" - } - ], - "name": "addTransaction", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "cancelTransaction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_tribunalIndex", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_commitHash", - "type": "bytes32" - } - ], - "name": "commitTribunalAppealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_commitHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_validatorIndex", - "type": "uint256" - } - ], - "name": "commitVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_sender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_maxRotations", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_calldata", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "_saltNonce", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_validUntil", - "type": "uint256" - } - ], - "name": "deploySalted", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_data", - "type": "bytes" - } - ], - "name": "executeMessage", - "outputs": [ - { - "internalType": "bool", - "name": "success", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32[]", - "name": "_txIds", - "type": "bytes32[]" - } - ], - "name": "finalizeIdlenessTxs", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "finalizeTransaction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "flushExternalMessages", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "getAddressManager", - "outputs": [ - { - "internalType": "contract IAddressManager", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "getPendingTransactionValue", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "addr", - "type": "address" - } - ], - "name": "isGhostContract", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "leaderIdleness", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "saltAsAValidator", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "messagesAndOtherFieldsHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "otherExecutionFieldsHash", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.VoteType", - "name": "resultValue", - "type": "uint8" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "saltNonce", - "type": "uint256" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - } - ], - "internalType": "struct IConsensusMain.LeaderRevealVoteParams", - "name": "leaderRevealVoteParams", - "type": "tuple" - } - ], - "name": "leaderRevealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "processIdleness", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_processingBlock", - "type": "uint256" - }, - { - "internalType": "address", - "name": "_operator", - "type": "address" - }, - { - "internalType": "bytes", - "name": "_eqBlocksOutputs", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "_vrfProof", - "type": "bytes" - } - ], - "name": "proposeReceipt", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32[]", - "name": "_txIds", - "type": "bytes32[]" - } - ], - "name": "redButtonFinalize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "ghost", - "type": "address" - } - ], - "name": "registerGhostContract", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_tribunalIndex", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_voteHash", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.VoteType", - "name": "_voteType", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "_otherExecutionFieldsHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_nonce", - "type": "uint256" - } - ], - "name": "revealTribunalAppealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_voteHash", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.VoteType", - "name": "_voteType", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "_otherExecutionFieldsHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_nonce", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_validatorIndex", - "type": "uint256" - } - ], - "name": "revealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_addressManager", - "type": "address" - } - ], - "name": "setAddressManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "submitAppeal", - "outputs": [], - "stateMutability": "payable", - "type": "function" - } + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "CallerNotMessages", + "type": "error" + }, + { + "inputs": [], + "name": "CanNotAppeal", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "attemptedConsumption", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "budget", + "type": "uint256" + } + ], + "name": "ExecutionBudgetExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFees", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDeploymentWithSalt", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRevealLeaderData", + "type": "error" + }, + { + "inputs": [], + "name": "LegacyProposeReceiptRetired", + "type": "error" + }, + { + "inputs": [], + "name": "MessageFeesTotalMustBeNonZero", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "RollupBudgetBelowFloor", + "type": "error" + }, + { + "inputs": [], + "name": "Unauthorized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oldActivator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newActivator", + "type": "address" + } + ], + "name": "ActivatorReplaced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "addressManager", + "type": "address" + } + ], + "name": "AddressManagerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "enum ITransactions.TransactionStatus", + "name": "newStatus", + "type": "uint8" + } + ], + "name": "AllVotesCommitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "appealer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "AppealBondReturned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "appellant", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "bond", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "validators", + "type": "address[]" + } + ], + "name": "AppealStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "appealer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "bondReturned", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "profitUnpaid", + "type": "uint256" + } + ], + "name": "AppealerProfitPayoutFailed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint8", + "name": "commandKind", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bytes4", + "name": "errorSelector", + "type": "bytes4" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "failureFingerprint", + "type": "bytes32" + } + ], + "name": "BatchCommandFailed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "attempted", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "finalized", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "progressed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "failed", + "type": "uint256" + } + ], + "name": "BatchFinalizationCompleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "attempted", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "resolved", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "failed", + "type": "uint256" + } + ], + "name": "BatchResolutionCompleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + } + ], + "name": "CreatedTransaction", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "FeesRefunded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "activator", + "type": "address" + } + ], + "name": "InternalMessageProcessed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oldLeader", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newLeader", + "type": "address" + } + ], + "name": "LeaderIdlenessProcessed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "activator", + "type": "address" + } + ], + "name": "NewTransaction", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "firstAnnouncedAt", + "type": "uint256" + } + ], + "name": "NewTransactionAlreadyAnnounced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "ProcessIdlenessAccepted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "receiptFee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "estimatedGas", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "receiptBytes", + "type": "uint256" + } + ], + "name": "ReceiptFeeEstimated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "TransactionAccepted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "leader", + "type": "address" + } + ], + "name": "TransactionActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "cancelledBy", + "type": "address" + } + ], + "name": "TransactionCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "TransactionFinalized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "TransactionLeaderRevealed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "newLeader", + "type": "address" + } + ], + "name": "TransactionLeaderRotated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "TransactionLeaderTimeout", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32[]", + "name": "txIds", + "type": "bytes32[]" + } + ], + "name": "TransactionNeedsRecomputation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "validators", + "type": "address[]" + } + ], + "name": "TransactionReceiptProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "TransactionUndetermined", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tribunalIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "TribunalAppealVoteCommitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tribunalIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "enum ITransactions.VoteType", + "name": "voteType", + "type": "uint8" + } + ], + "name": "TribunalAppealVoteRevealed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "chunkIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "UnifiedFeesDistributed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "UserValueRefunded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oldValidator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newValidator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "validatorIndex", + "type": "uint256" + } + ], + "name": "ValidatorReplaced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "ValueWithdrawalFailed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isLastVote", + "type": "bool" + } + ], + "name": "VoteCommitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "enum ITransactions.VoteType", + "name": "voteType", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isLastVote", + "type": "bool" + }, + { + "indexed": false, + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + } + ], + "name": "VoteRevealed", + "type": "event" + }, + { + "inputs": [], + "name": "EVENTS_BATCH_SIZE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "activateTransaction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "activatorIdleness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "numOfInitialValidators", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validUntil", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userValue", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "leaderTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealRounds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionBudgetPerRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionConsumed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalMessageFees", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "rotations", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "maxPriceGenPerTimeUnit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeMaxGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "receiptFeeMaxGasPrice", + "type": "uint256" + } + ], + "internalType": "struct IFeeManager.FeesDistribution", + "name": "feesDistribution", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "parentIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "budget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + } + ], + "internalType": "struct IMessages.MessageFeeAllocationNode[]", + "name": "messageAllocations", + "type": "tuple[]" + } + ], + "internalType": "struct IConsensusMainWithFees.AddTransactionParams", + "name": "_params", + "type": "tuple" + } + ], + "name": "addTransaction", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "addressManager", + "outputs": [ + { + "internalType": "contract IAddressManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "cancelTransaction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_commitHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_validatorIndex", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "commitVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "numOfInitialValidators", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validUntil", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userValue", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "leaderTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealRounds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionBudgetPerRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionConsumed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalMessageFees", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "rotations", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "maxPriceGenPerTimeUnit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeMaxGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "receiptFeeMaxGasPrice", + "type": "uint256" + } + ], + "internalType": "struct IFeeManager.FeesDistribution", + "name": "feesDistribution", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "parentIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "budget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + } + ], + "internalType": "struct IMessages.MessageFeeAllocationNode[]", + "name": "messageAllocations", + "type": "tuple[]" + } + ], + "internalType": "struct IConsensusMainWithFees.AddTransactionParams", + "name": "_params", + "type": "tuple" + } + ], + "name": "deploySalted", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "estimateAppealCharge", + "outputs": [ + { + "internalType": "uint256", + "name": "bond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "funding", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "executeMessage", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "expectedDecisionId", + "type": "uint256" + } + ], + "internalType": "struct IFinalizationPhase.FinalizeTransactionParams", + "name": "command", + "type": "tuple" + } + ], + "name": "finalizeDecisionBatchItem", + "outputs": [ + { + "internalType": "uint8", + "name": "outcome", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "expectedDecisionId", + "type": "uint256" + } + ], + "internalType": "struct IFinalizationPhase.FinalizeTransactionParams[]", + "name": "_commands", + "type": "tuple[]" + } + ], + "name": "finalizeDecisions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "finalizeTransaction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "flushExternalMessages", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "flushInternalMessages", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAddressManager", + "outputs": [ + { + "internalType": "contract IAddressManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "getPendingTransactionValue", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addressManager", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "isAuthorizedGhostCaller", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "isGhostContract", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "leaderIdleness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "expectedAttemptId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "saltAsAValidator", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "messagesAndOtherFieldsHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "otherExecutionFieldsHash", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.VoteType", + "name": "resultValue", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + } + ], + "internalType": "struct IConsensusMainWithFees.LeaderRevealVoteParams", + "name": "leaderRevealVoteParams", + "type": "tuple" + } + ], + "name": "leaderRevealVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "processIdleness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + } + ], + "name": "promoteNextPendingTransactions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "proposeReceipt", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_storageFeeUsed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_eqBlocksOutputs", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "proposeReceipt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_processingBlock", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_eqBlocksOutputs", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "proposeReceipt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_storageFeeUsed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_eqBlocksOutputs", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_reportedMessageFeesTotal", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "proposeReceiptWithMessages", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "ghost", + "type": "address" + } + ], + "name": "registerGhostContract", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "expectedAttemptId", + "type": "bytes32" + } + ], + "internalType": "struct IIdlenessPhase.ProcessIdlenessParams", + "name": "command", + "type": "tuple" + } + ], + "name": "resolveTransactionBatchItem", + "outputs": [ + { + "internalType": "uint8", + "name": "outcome", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "expectedAttemptId", + "type": "bytes32" + } + ], + "internalType": "struct IIdlenessPhase.ProcessIdlenessParams[]", + "name": "_commands", + "type": "tuple[]" + } + ], + "name": "resolveTransactions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_voteHash", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.VoteType", + "name": "_voteType", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_otherExecutionFieldsHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_validatorIndex", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "revealVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addressManager", + "type": "address" + } + ], + "name": "setAddressManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "submitAppeal", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "topUpAndProgressFinalization", + "outputs": [ + { + "internalType": "bool", + "name": "finalized", + "type": "bool" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "leaderTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealRounds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionBudgetPerRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionConsumed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalMessageFees", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "rotations", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "maxPriceGenPerTimeUnit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeMaxGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "receiptFeeMaxGasPrice", + "type": "uint256" + } + ], + "internalType": "struct IFeeManager.FeesDistribution", + "name": "", + "type": "tuple" + } + ], + "name": "topUpAndSubmitAppeal", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "leaderTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealRounds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionBudgetPerRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionConsumed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalMessageFees", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "rotations", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "maxPriceGenPerTimeUnit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeMaxGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "receiptFeeMaxGasPrice", + "type": "uint256" + } + ], + "internalType": "struct IFeeManager.FeesDistribution", + "name": "_feesDistribution", + "type": "tuple" + } + ], + "name": "topUpFees", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferEthOnBehalf", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } ] diff --git a/genlayer_py/consensus/abi/consensus_main_abi_v06.json b/genlayer_py/consensus/abi/consensus_main_abi_v06.json index 7b0b9dd..c63e61e 100644 --- a/genlayer_py/consensus/abi/consensus_main_abi_v06.json +++ b/genlayer_py/consensus/abi/consensus_main_abi_v06.json @@ -1,1346 +1,2361 @@ [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "CallerNotMessages", - "type": "error" - }, - { - "inputs": [], - "name": "CanNotAppeal", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidDeploymentWithSalt", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidGhostContract", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidRevealLeaderData", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidVote", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "oldActivator", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newActivator", - "type": "address" - } - ], - "name": "ActivatorReplaced", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "addressManager", - "type": "address" - } - ], - "name": "AddressManagerSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "enum ITransactions.TransactionStatus", - "name": "newStatus", - "type": "uint8" - } - ], - "name": "AllVotesCommitted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "appellant", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "bond", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "address[]", - "name": "validators", - "type": "address[]" - } - ], - "name": "AppealStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "attempted", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "succeeded", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "failed", - "type": "uint256" - } - ], - "name": "BatchFinalizationCompleted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "txSlot", - "type": "uint256" - } - ], - "name": "CreatedTransaction", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "version", - "type": "uint64" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "activator", - "type": "address" - } - ], - "name": "InternalMessageProcessed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "oldLeader", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newLeader", - "type": "address" - } - ], - "name": "LeaderIdlenessProcessed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "activator", - "type": "address" - } - ], - "name": "NewTransaction", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "ProcessIdlenessAccepted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionAccepted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "leader", - "type": "address" - } - ], - "name": "TransactionActivated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "cancelledBy", - "type": "address" - } - ], - "name": "TransactionCancelled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionFinalizationFailed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionFinalized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionLeaderRevealed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "newLeader", - "type": "address" - } - ], - "name": "TransactionLeaderRotated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionLeaderTimeout", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32[]", - "name": "txIds", - "type": "bytes32[]" - } - ], - "name": "TransactionNeedsRecomputation", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "address[]", - "name": "validators", - "type": "address[]" - } - ], - "name": "TransactionReceiptProposed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "TransactionUndetermined", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "tribunalIndex", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "TribunalAppealVoteCommitted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "tribunalIndex", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "enum ITransactions.VoteType", - "name": "voteType", - "type": "uint8" - } - ], - "name": "TribunalAppealVoteRevealed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "oldValidator", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newValidator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "validatorIndex", - "type": "uint256" - } - ], - "name": "ValidatorReplaced", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "ValueWithdrawalFailed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isLastVote", - "type": "bool" - } - ], - "name": "VoteCommitted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "enum ITransactions.VoteType", - "name": "voteType", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isLastVote", - "type": "bool" - }, - { - "indexed": false, - "internalType": "enum ITransactions.ResultType", - "name": "result", - "type": "uint8" - } - ], - "name": "VoteRevealed", - "type": "event" - }, - { - "inputs": [], - "name": "EVENTS_BATCH_SIZE", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "VERSION", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "acceptOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "_operator", - "type": "address" - }, - { - "internalType": "bytes", - "name": "_vrfProof", - "type": "bytes" - } - ], - "name": "activateTransaction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_sender", - "type": "address" - }, - { - "internalType": "address", - "name": "_recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_maxRotations", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_calldata", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "_validUntil", - "type": "uint256" - } - ], - "name": "addTransaction", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "addressManager", - "outputs": [ - { - "internalType": "contract IAddressManager", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "cancelTransaction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_tribunalIndex", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_commitHash", - "type": "bytes32" - } - ], - "name": "commitTribunalAppealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_commitHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_validatorIndex", - "type": "uint256" - } - ], - "name": "commitVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_sender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_numOfInitialValidators", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_maxRotations", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_calldata", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "_saltNonce", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_validUntil", - "type": "uint256" - } - ], - "name": "deploySalted", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_data", - "type": "bytes" - } - ], - "name": "executeMessage", - "outputs": [ - { - "internalType": "bool", - "name": "success", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32[]", - "name": "_txIds", - "type": "bytes32[]" - } - ], - "name": "finalizeIdlenessTxs", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "finalizeTransaction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "flushExternalMessages", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "getAddressManager", - "outputs": [ - { - "internalType": "contract IAddressManager", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - } - ], - "name": "getPendingTransactionValue", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_addressManager", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "addr", - "type": "address" - } - ], - "name": "isGhostContract", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "leaderIdleness", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "saltAsAValidator", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "messagesAndOtherFieldsHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "otherExecutionFieldsHash", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.VoteType", - "name": "resultValue", - "type": "uint8" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "saltNonce", - "type": "uint256" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - } - ], - "internalType": "struct IConsensusMain.LeaderRevealVoteParams", - "name": "leaderRevealVoteParams", - "type": "tuple" - } - ], - "name": "leaderRevealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pendingOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "processIdleness", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_processingBlock", - "type": "uint256" - }, - { - "internalType": "address", - "name": "_operator", - "type": "address" - }, - { - "internalType": "bytes", - "name": "_eqBlocksOutputs", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "_vrfProof", - "type": "bytes" - } - ], - "name": "proposeReceipt", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32[]", - "name": "_txIds", - "type": "bytes32[]" - } - ], - "name": "redButtonFinalize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "ghost", - "type": "address" - } - ], - "name": "registerGhostContract", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_tribunalIndex", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_voteHash", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.VoteType", - "name": "_voteType", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "_otherExecutionFieldsHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_nonce", - "type": "uint256" - } - ], - "name": "revealTribunalAppealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_voteHash", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.VoteType", - "name": "_voteType", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "_otherExecutionFieldsHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_nonce", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_validatorIndex", - "type": "uint256" - } - ], - "name": "revealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_addressManager", - "type": "address" - } - ], - "name": "setAddressManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "submitAppeal", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "CallerNotMessages", + "type": "error" + }, + { + "inputs": [], + "name": "CanNotAppeal", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "attemptedConsumption", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "budget", + "type": "uint256" + } + ], + "name": "ExecutionBudgetExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientFees", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidDeploymentWithSalt", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRevealLeaderData", + "type": "error" + }, + { + "inputs": [], + "name": "LegacyProposeReceiptRetired", + "type": "error" + }, + { + "inputs": [], + "name": "MessageFeesTotalMustBeNonZero", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "RollupBudgetBelowFloor", + "type": "error" + }, + { + "inputs": [], + "name": "Unauthorized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oldActivator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newActivator", + "type": "address" + } + ], + "name": "ActivatorReplaced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "addressManager", + "type": "address" + } + ], + "name": "AddressManagerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "enum ITransactions.TransactionStatus", + "name": "newStatus", + "type": "uint8" + } + ], + "name": "AllVotesCommitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "appealer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "AppealBondReturned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "appellant", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "bond", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "validators", + "type": "address[]" + } + ], + "name": "AppealStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "appealer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "bondReturned", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "profitUnpaid", + "type": "uint256" + } + ], + "name": "AppealerProfitPayoutFailed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint8", + "name": "commandKind", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bytes4", + "name": "errorSelector", + "type": "bytes4" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "failureFingerprint", + "type": "bytes32" + } + ], + "name": "BatchCommandFailed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "attempted", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "finalized", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "progressed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "failed", + "type": "uint256" + } + ], + "name": "BatchFinalizationCompleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "attempted", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "resolved", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "failed", + "type": "uint256" + } + ], + "name": "BatchResolutionCompleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "txSlot", + "type": "uint256" + } + ], + "name": "CreatedTransaction", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "FeesRefunded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "activator", + "type": "address" + } + ], + "name": "InternalMessageProcessed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oldLeader", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newLeader", + "type": "address" + } + ], + "name": "LeaderIdlenessProcessed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "activator", + "type": "address" + } + ], + "name": "NewTransaction", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "firstAnnouncedAt", + "type": "uint256" + } + ], + "name": "NewTransactionAlreadyAnnounced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "ProcessIdlenessAccepted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "receiptFee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "gasPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "estimatedGas", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "receiptBytes", + "type": "uint256" + } + ], + "name": "ReceiptFeeEstimated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "TransactionAccepted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "leader", + "type": "address" + } + ], + "name": "TransactionActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "cancelledBy", + "type": "address" + } + ], + "name": "TransactionCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "TransactionFinalized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "TransactionLeaderRevealed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "newLeader", + "type": "address" + } + ], + "name": "TransactionLeaderRotated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "TransactionLeaderTimeout", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32[]", + "name": "txIds", + "type": "bytes32[]" + } + ], + "name": "TransactionNeedsRecomputation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "validators", + "type": "address[]" + } + ], + "name": "TransactionReceiptProposed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "TransactionUndetermined", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tribunalIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "TribunalAppealVoteCommitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tribunalIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "enum ITransactions.VoteType", + "name": "voteType", + "type": "uint8" + } + ], + "name": "TribunalAppealVoteRevealed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "chunkIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "name": "UnifiedFeesDistributed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "UserValueRefunded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "oldValidator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newValidator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "validatorIndex", + "type": "uint256" + } + ], + "name": "ValidatorReplaced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "ValueWithdrawalFailed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isLastVote", + "type": "bool" + } + ], + "name": "VoteCommitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "enum ITransactions.VoteType", + "name": "voteType", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isLastVote", + "type": "bool" + }, + { + "indexed": false, + "internalType": "enum ITransactions.ResultType", + "name": "result", + "type": "uint8" + } + ], + "name": "VoteRevealed", + "type": "event" + }, + { + "inputs": [], + "name": "EVENTS_BATCH_SIZE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "acceptOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "activateTransaction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "activatorIdleness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "numOfInitialValidators", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validUntil", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userValue", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "leaderTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealRounds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionBudgetPerRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionConsumed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalMessageFees", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "rotations", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "maxPriceGenPerTimeUnit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeMaxGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "receiptFeeMaxGasPrice", + "type": "uint256" + } + ], + "internalType": "struct IFeeManager.FeesDistribution", + "name": "feesDistribution", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "parentIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "budget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + } + ], + "internalType": "struct IMessages.MessageFeeAllocationNode[]", + "name": "messageAllocations", + "type": "tuple[]" + } + ], + "internalType": "struct IConsensusMainWithFees.AddTransactionParams", + "name": "_params", + "type": "tuple" + } + ], + "name": "addTransaction", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "addressManager", + "outputs": [ + { + "internalType": "contract IAddressManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "cancelTransaction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_commitHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_validatorIndex", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "commitVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "numOfInitialValidators", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxRotations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validUntil", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "userValue", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "leaderTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealRounds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionBudgetPerRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionConsumed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalMessageFees", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "rotations", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "maxPriceGenPerTimeUnit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeMaxGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "receiptFeeMaxGasPrice", + "type": "uint256" + } + ], + "internalType": "struct IFeeManager.FeesDistribution", + "name": "feesDistribution", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "txCalldata", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "parentIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "budget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + } + ], + "internalType": "struct IMessages.MessageFeeAllocationNode[]", + "name": "messageAllocations", + "type": "tuple[]" + } + ], + "internalType": "struct IConsensusMainWithFees.AddTransactionParams", + "name": "_params", + "type": "tuple" + } + ], + "name": "deploySalted", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "estimateAppealCharge", + "outputs": [ + { + "internalType": "uint256", + "name": "bond", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "funding", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_gasLimit", + "type": "uint256" + } + ], + "name": "executeMessage", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "expectedDecisionId", + "type": "uint256" + } + ], + "internalType": "struct IFinalizationPhase.FinalizeTransactionParams", + "name": "command", + "type": "tuple" + } + ], + "name": "finalizeDecisionBatchItem", + "outputs": [ + { + "internalType": "uint8", + "name": "outcome", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "expectedDecisionId", + "type": "uint256" + } + ], + "internalType": "struct IFinalizationPhase.FinalizeTransactionParams[]", + "name": "_commands", + "type": "tuple[]" + } + ], + "name": "finalizeDecisions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "finalizeTransaction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "flushExternalMessages", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "flushInternalMessages", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAddressManager", + "outputs": [ + { + "internalType": "contract IAddressManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "getPendingTransactionValue", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addressManager", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "isAuthorizedGhostCaller", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "isGhostContract", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "leaderIdleness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "expectedAttemptId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "saltAsAValidator", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "messagesAndOtherFieldsHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "otherExecutionFieldsHash", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.VoteType", + "name": "resultValue", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + } + ], + "internalType": "struct IConsensusMainWithFees.LeaderRevealVoteParams", + "name": "leaderRevealVoteParams", + "type": "tuple" + } + ], + "name": "leaderRevealVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "processIdleness", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "recipients", + "type": "address[]" + } + ], + "name": "promoteNextPendingTransactions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "proposeReceipt", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_storageFeeUsed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_eqBlocksOutputs", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "proposeReceipt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_processingBlock", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_eqBlocksOutputs", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "proposeReceipt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_storageFeeUsed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_eqBlocksOutputs", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_reportedMessageFeesTotal", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "proposeReceiptWithMessages", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "ghost", + "type": "address" + } + ], + "name": "registerGhostContract", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "expectedAttemptId", + "type": "bytes32" + } + ], + "internalType": "struct IIdlenessPhase.ProcessIdlenessParams", + "name": "command", + "type": "tuple" + } + ], + "name": "resolveTransactionBatchItem", + "outputs": [ + { + "internalType": "uint8", + "name": "outcome", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "expectedAttemptId", + "type": "bytes32" + } + ], + "internalType": "struct IIdlenessPhase.ProcessIdlenessParams[]", + "name": "_commands", + "type": "tuple[]" + } + ], + "name": "resolveTransactions", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_voteHash", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.VoteType", + "name": "_voteType", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_otherExecutionFieldsHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_validatorIndex", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "revealVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addressManager", + "type": "address" + } + ], + "name": "setAddressManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "submitAppeal", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + } + ], + "name": "topUpAndProgressFinalization", + "outputs": [ + { + "internalType": "bool", + "name": "finalized", + "type": "bool" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expectedDecisionId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "leaderTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealRounds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionBudgetPerRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionConsumed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalMessageFees", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "rotations", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "maxPriceGenPerTimeUnit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeMaxGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "receiptFeeMaxGasPrice", + "type": "uint256" + } + ], + "internalType": "struct IFeeManager.FeesDistribution", + "name": "", + "type": "tuple" + } + ], + "name": "topUpAndSubmitAppeal", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "leaderTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "validatorTimeunitsAllocation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "appealRounds", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionBudgetPerRound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "executionConsumed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalMessageFees", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "rotations", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "maxPriceGenPerTimeUnit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "storageFeeMaxGasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "receiptFeeMaxGasPrice", + "type": "uint256" + } + ], + "internalType": "struct IFeeManager.FeesDistribution", + "name": "_feesDistribution", + "type": "tuple" + } + ], + "name": "topUpFees", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferEthOnBehalf", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } ] diff --git a/genlayer_py/consensus/abi/rounds_storage_read_abi.json b/genlayer_py/consensus/abi/rounds_storage_read_abi.json new file mode 100644 index 0000000..5d84503 --- /dev/null +++ b/genlayer_py/consensus/abi/rounds_storage_read_abi.json @@ -0,0 +1,74 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + } + ], + "name": "getValidatorVotes", + "outputs": [ + { + "internalType": "enum ITransactions.VoteType[]", + "name": "votes", + "type": "uint8[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + } + ], + "name": "getValidatorVotesHash", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "hashes", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "round", + "type": "uint256" + } + ], + "name": "getValidatorResultHash", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "hashes", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/genlayer_py/consensus/abi/transaction_manager_read_abi.json b/genlayer_py/consensus/abi/transaction_manager_read_abi.json new file mode 100644 index 0000000..fc059f2 --- /dev/null +++ b/genlayer_py/consensus/abi/transaction_manager_read_abi.json @@ -0,0 +1,40 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "getTxExecutionResult", + "outputs": [ + { + "internalType": "enum ITransactions.VoteType", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "getNumOfInitialValidators", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/genlayer_py/consensus/consensus_main/decoder.py b/genlayer_py/consensus/consensus_main/decoder.py index b0fbea3..d2a62df 100644 --- a/genlayer_py/consensus/consensus_main/decoder.py +++ b/genlayer_py/consensus/consensus_main/decoder.py @@ -2,8 +2,11 @@ import rlp from web3 import Web3 from eth_abi import decode as abi_decode -from genlayer_py.consensus.abi import CONSENSUS_MAIN_ABI from genlayer_py.abi import calldata +from genlayer_py.consensus.consensus_main.legacy_abi import ( + LEGACY_ADD_TRANSACTION_ARGUMENT_TYPES, + LEGACY_ADD_TRANSACTION_SELECTOR, +) from genlayer_py.transactions.fees import ( ADD_TRANSACTION_WITH_FEES_ARGUMENT_TYPES, ADD_TRANSACTION_WITH_FEES_SELECTOR, @@ -24,10 +27,10 @@ def decode_add_transaction_data(encoded_data): ) return _format_fee_aware_add_transaction_data(abi_decoded[0]) - consensus_main_contract = w3.eth.contract(abi=CONSENSUS_MAIN_ABI) - contract_fn = consensus_main_contract.get_function_by_name("addTransaction") + if selector != LEGACY_ADD_TRANSACTION_SELECTOR: + raise ValueError(f"Unsupported addTransaction selector: 0x{selector}") abi_decoded = abi_decode( - contract_fn.argument_types, + LEGACY_ADD_TRANSACTION_ARGUMENT_TYPES, w3.to_bytes(hexstr=payload), ) encoded_tx_data_bytes = abi_decoded[4] @@ -64,8 +67,7 @@ def _format_fee_aware_add_transaction_data(params): "user_value": params[6], "fees_distribution": decode_fees_distribution_tuple(params[7]), "message_allocations": [ - decode_message_allocation_tuple(allocation) - for allocation in params[9] + decode_message_allocation_tuple(allocation) for allocation in params[9] ], } diff --git a/genlayer_py/consensus/consensus_main/encoder.py b/genlayer_py/consensus/consensus_main/encoder.py index 7332203..5df2b7f 100644 --- a/genlayer_py/consensus/consensus_main/encoder.py +++ b/genlayer_py/consensus/consensus_main/encoder.py @@ -7,8 +7,10 @@ from web3 import Web3 from eth_abi import encode as abi_encode from eth_typing import HexStr -import eth_utils -from genlayer_py.consensus.abi import CONSENSUS_MAIN_ABI +from genlayer_py.consensus.consensus_main.legacy_abi import ( + LEGACY_ADD_TRANSACTION_ARGUMENT_TYPES, + LEGACY_ADD_TRANSACTION_SELECTOR, +) from genlayer_py.transactions.fees import ( TransactionFeeOptions, encode_fee_aware_add_transaction_data, @@ -29,7 +31,6 @@ def encode_add_transaction_data( fee_aware: bool = False, ): w3 = Web3() - consensus_main_contract = w3.eth.contract(abi=CONSENSUS_MAIN_ABI) transaction_fees = normalize_transaction_fees(fees) if ( @@ -49,23 +50,20 @@ def encode_add_transaction_data( transaction_fees=transaction_fees, ) - contract_fn = consensus_main_contract.get_function_by_name("addTransaction") add_transaction_args = [ sender_address, recipient_address, num_of_initial_validators, max_rotations, w3.to_bytes(hexstr=tx_data), + valid_until, ] - if len(contract_fn.argument_types) >= 6: - add_transaction_args.append(valid_until) params = abi_encode( - contract_fn.argument_types, + LEGACY_ADD_TRANSACTION_ARGUMENT_TYPES, add_transaction_args, ) - function_selector = eth_utils.keccak(text=contract_fn.signature)[:4].hex() - encoded_data = "0x" + function_selector + params.hex() + encoded_data = "0x" + LEGACY_ADD_TRANSACTION_SELECTOR + params.hex() return encoded_data diff --git a/genlayer_py/consensus/consensus_main/legacy_abi.py b/genlayer_py/consensus/consensus_main/legacy_abi.py new file mode 100644 index 0000000..53f8171 --- /dev/null +++ b/genlayer_py/consensus/consensus_main/legacy_abi.py @@ -0,0 +1,23 @@ +"""Private codec constants for the pre-fee ConsensusMain call shape. + +The exported ConsensusMain ABI tracks the v0.6 train. These constants exist +only so the SDK can still decode or deliberately produce historical payloads +without letting an old selector leak back into the public contract surface. +""" + +import eth_utils + +LEGACY_ADD_TRANSACTION_ARGUMENT_TYPES = ( + "address", + "address", + "uint256", + "uint256", + "bytes", + "uint256", +) +LEGACY_ADD_TRANSACTION_SIGNATURE = ( + "addTransaction(address,address,uint256,uint256,bytes,uint256)" +) +LEGACY_ADD_TRANSACTION_SELECTOR = eth_utils.keccak( + text=LEGACY_ADD_TRANSACTION_SIGNATURE +)[:4].hex() diff --git a/genlayer_py/contracts/actions.py b/genlayer_py/contracts/actions.py index 1679443..a704421 100644 --- a/genlayer_py/contracts/actions.py +++ b/genlayer_py/contracts/actions.py @@ -72,7 +72,11 @@ def get_contract_schema_for_code( if self.chain.id != localnet.id: raise GenLayerError("Contract schema is not supported on this network") - code_bytes = contract_code.encode("utf-8") if isinstance(contract_code, str) else contract_code + code_bytes = ( + contract_code.encode("utf-8") + if isinstance(contract_code, str) + else contract_code + ) response = self.provider.make_request( method="gen_getContractSchemaForCode", params=[eth_utils.hexadecimal.encode_hex(code_bytes)], @@ -223,20 +227,49 @@ def appeal_transaction( transaction_id: HexStr, account: Optional[LocalAccount] = None, value: Optional[int] = None, + expected_decision_id: Optional[int] = None, ) -> HexStr: """Appeals a consensus transaction. Returns the original transaction_id. Appeals emit AppealStarted/TransactionActivated events (not NewTransaction), so we send the EVM tx directly instead of going through _send_transaction. + Deployed Consensus binds the appeal to the exact active decision. Current + Studio exposes its native ``submitAppeal(bytes32)`` entrypoint instead, so + callers must provide the value explicitly and cannot supply a decision id. """ sender_account = account if account is not None else self.local_account if sender_account is None: raise GenLayerError("No account set.") if self.chain.consensus_main_contract is None: raise GenLayerError("Consensus main contract not configured.") - resolved_value = _resolve_appeal_value(self, transaction_id, value) + if _is_studio_chain(self): + resolved_value = _resolve_studio_appeal_value(value, expected_decision_id) + encoded_data = _encode_submit_appeal_data( + self=self, + transaction_id=transaction_id, + studio_appeal_shape=True, + ) + _send_consensus_call( + self=self, + encoded_data=encoded_data, + sender_account=sender_account, + value=resolved_value, + operation_name="Appeal", + ) + return transaction_id + + expected_decision_id, resolved_value = _resolve_appeal_parameters( + self, + transaction_id, + expected_decision_id=expected_decision_id, + value=value, + ) - encoded_data = _encode_submit_appeal_data(self=self, transaction_id=transaction_id) + encoded_data = _encode_submit_appeal_data( + self=self, + transaction_id=transaction_id, + expected_decision_id=expected_decision_id, + ) _send_consensus_call( self=self, @@ -283,18 +316,45 @@ def top_up_and_submit_appeal( distribution: FeesDistributionInput, account: Optional[LocalAccount] = None, value: Optional[int] = None, + expected_decision_id: Optional[int] = None, ) -> HexStr: """Deposits appeal fee budget and submits an appeal in one consensus call. Returns the original GenLayer transaction id, matching appeal_transaction. + Current Studio exposes the native decision-free call shape. Deployed + Consensus uses the decision-bound train shape. """ sender_account = account if account is not None else self.local_account - resolved_value = _resolve_appeal_value(self, transaction_id, value) + if _is_studio_chain(self): + resolved_value = _resolve_studio_appeal_value(value, expected_decision_id) + encoded_data = _encode_fee_management_data( + self=self, + function_name="topUpAndSubmitAppeal", + transaction_id=transaction_id, + distribution=distribution, + studio_appeal_shape=True, + ) + _send_consensus_call( + self=self, + encoded_data=encoded_data, + sender_account=sender_account, + value=resolved_value, + operation_name="Top up and submit appeal", + ) + return transaction_id + + expected_decision_id, resolved_value = _resolve_appeal_parameters( + self, + transaction_id, + expected_decision_id=expected_decision_id, + value=value, + ) encoded_data = _encode_fee_management_data( self=self, function_name="topUpAndSubmitAppeal", transaction_id=transaction_id, distribution=distribution, + expected_decision_id=expected_decision_id, ) _send_consensus_call( self=self, @@ -314,7 +374,9 @@ def get_round_number( if self.chain.rounds_storage_contract is None: raise GenLayerError("rounds_storage_contract not configured for this chain") contract = self.w3.eth.contract( - address=self.w3.to_checksum_address(self.chain.rounds_storage_contract["address"]), + address=self.w3.to_checksum_address( + self.chain.rounds_storage_contract["address"] + ), abi=self.chain.rounds_storage_contract["abi"], ) tx_bytes = _to_bytes32(self, transaction_id) @@ -330,7 +392,9 @@ def get_round_data( if self.chain.rounds_storage_contract is None: raise GenLayerError("rounds_storage_contract not configured for this chain") contract = self.w3.eth.contract( - address=self.w3.to_checksum_address(self.chain.rounds_storage_contract["address"]), + address=self.w3.to_checksum_address( + self.chain.rounds_storage_contract["address"] + ), abi=self.chain.rounds_storage_contract["abi"], ) tx_bytes = _to_bytes32(self, transaction_id) @@ -345,7 +409,9 @@ def get_last_round_data( if self.chain.rounds_storage_contract is None: raise GenLayerError("rounds_storage_contract not configured for this chain") contract = self.w3.eth.contract( - address=self.w3.to_checksum_address(self.chain.rounds_storage_contract["address"]), + address=self.w3.to_checksum_address( + self.chain.rounds_storage_contract["address"] + ), abi=self.chain.rounds_storage_contract["abi"], ) tx_bytes = _to_bytes32(self, transaction_id) @@ -355,50 +421,165 @@ def get_last_round_data( def can_appeal( self: GenLayerClient, transaction_id: HexStr, + expected_decision_id: Optional[int] = None, ) -> bool: - """Checks if a transaction can be appealed.""" + """Checks whether the exact active decision can be appealed on a network. + + When no decision id is supplied, the latest active decision is read first. + The guarded on-chain call returns ``False`` if that decision changes before + it is evaluated. Current Studio does not expose this decision-bound read. + """ if self.chain.appeals_contract is None: raise GenLayerError("appeals_contract not configured for this chain") - contract = self.w3.eth.contract( - address=self.w3.to_checksum_address(self.chain.appeals_contract["address"]), - abi=self.chain.appeals_contract["abi"], - ) + if expected_decision_id is None: + expected_decision_id = _get_active_decision_id(self, transaction_id) + if expected_decision_id is None: + return False + contract = _appeals_contract(self) tx_bytes = _to_bytes32(self, transaction_id) - return contract.functions.canAppeal(tx_bytes).call() + return contract.functions.canAppeal(tx_bytes, expected_decision_id).call() + + +def get_appeal_quote( + self: GenLayerClient, + transaction_id: HexStr, +) -> Dict[str, int]: + """Returns the exact latest-decision appeal charge and race guard. + + ``total`` is the value to submit: the appeal bond plus induced-work + funding. Pass ``decision_id`` back to the decision-guarded appeal methods. + Current Studio exposes no authoritative decision-bound appeal quote. + """ + if _is_studio_chain(self): + raise GenLayerError(STUDIO_APPEAL_QUOTE_UNSUPPORTED) + contract = _consensus_data_contract(self) + decision_id, bond, funding, appeal_deadline = ( + contract.functions.estimateLatestAppealCharge( + _to_bytes32(self, transaction_id) + ).call() + ) + return { + "decision_id": int(decision_id), + "bond": int(bond), + "funding": int(funding), + "total": int(bond) + int(funding), + "appeal_deadline": int(appeal_deadline), + } + + +def get_appeal_charge( + self: GenLayerClient, + transaction_id: HexStr, +) -> int: + """Returns the full payment required to appeal the latest decision.""" + return get_appeal_quote(self, transaction_id)["total"] def get_min_appeal_bond( self: GenLayerClient, transaction_id: HexStr, ) -> int: - """Calculates the minimum bond required to appeal a transaction.""" - if self.chain.fee_manager_contract is None or self.chain.rounds_storage_contract is None: - raise GenLayerError("fee_manager_contract/rounds_storage_contract not configured for this chain") + """Deprecated alias for :func:`get_appeal_charge`. + + Despite its historical name, this returns bond plus induced-work funding. + """ + return get_appeal_charge(self, transaction_id) - round_number = get_round_number(self, transaction_id) - tx = self.get_transaction(transaction_id) - tx_status = int(tx["status"]) - fee_contract = self.w3.eth.contract( - address=self.w3.to_checksum_address(self.chain.fee_manager_contract["address"]), - abi=self.chain.fee_manager_contract["abi"], +def _consensus_data_contract(self: GenLayerClient): + if self.chain.consensus_data_contract is None: + raise GenLayerError("consensus_data_contract not configured for this chain") + return self.w3.eth.contract( + address=self.w3.to_checksum_address( + self.chain.consensus_data_contract["address"] + ), + abi=self.chain.consensus_data_contract["abi"], + ) + + +def _appeals_contract(self: GenLayerClient): + if self.chain.appeals_contract is None: + raise GenLayerError("appeals_contract not configured for this chain") + return self.w3.eth.contract( + address=self.w3.to_checksum_address(self.chain.appeals_contract["address"]), + abi=self.chain.appeals_contract["abi"], ) - tx_bytes = _to_bytes32(self, transaction_id) - return fee_contract.functions.calculateMinAppealBond(tx_bytes, round_number, tx_status).call() -def _resolve_appeal_value( +def _get_active_decision_id( self: GenLayerClient, transaction_id: HexStr, +) -> Optional[int]: + lifecycle = ( + _consensus_data_contract(self) + .functions.getTransactionLifecycle(_to_bytes32(self, transaction_id), 0) + .call() + ) + latest_decision = lifecycle[2] + decision_active = lifecycle[3] + return int(latest_decision[1]) if decision_active else None + + +def _resolve_appeal_parameters( + self: GenLayerClient, + transaction_id: HexStr, + expected_decision_id: Optional[int], value: Optional[int], -) -> int: - if value is not None: - return value - if self.chain.fee_manager_contract is None or self.chain.rounds_storage_contract is None: +) -> tuple[int, int]: + if expected_decision_id is not None and value is not None: + return expected_decision_id, value + + try: + quote = get_appeal_quote(self, transaction_id) + except Exception as exc: + raise GenLayerError( + "Cannot quote an active appeal decision. The transaction may not be " + "appealable yet; refresh its lifecycle and retry." + ) from exc + + if ( + expected_decision_id is not None + and expected_decision_id != quote["decision_id"] + ): raise GenLayerError( - "Cannot auto-resolve appeal bond: fee_manager_contract/rounds_storage_contract not configured for this chain." + f"Appeal decision {expected_decision_id} is stale; the latest active " + f"decision is {quote['decision_id']}. Refresh and retry." ) - return get_min_appeal_bond(self, transaction_id) + + return ( + quote["decision_id"] if expected_decision_id is None else expected_decision_id, + quote["total"] if value is None else value, + ) + + +STUDIO_APPEAL_QUOTE_UNSUPPORTED = ( + "Decision-bound appeal quotes are not available on current Studio." +) +STUDIO_APPEAL_VALUE_UNRESOLVABLE = ( + "Cannot auto-resolve the appeal payment on current Studio; pass value explicitly." +) +STUDIO_DECISION_GUARD_UNSUPPORTED = "expected_decision_id is not supported by current Studio's decision-free appeal methods." + + +def _is_studio_chain(self: GenLayerClient) -> bool: + """Reports whether the client targets the studio-embedded consensus. + + localnet and studionet share chain id 61999, the same check + ``transactions.actions.get_transaction`` uses to take its studio path. + """ + return self.chain.id == localnet.id + + +def _resolve_studio_appeal_value( + value: Optional[int], + expected_decision_id: Optional[int], +) -> int: + """Require the inputs that current Studio can faithfully honor.""" + if expected_decision_id is not None: + raise GenLayerError(STUDIO_DECISION_GUARD_UNSUPPORTED) + if value is None: + raise GenLayerError(STUDIO_APPEAL_VALUE_UNRESOLVABLE) + return value def _to_bytes32(self: GenLayerClient, hex_str: HexStr) -> bytes: @@ -477,17 +658,17 @@ def _json_safe_rpc_value(value): if isinstance(value, tuple): return [_json_safe_rpc_value(item) for item in value] if isinstance(value, dict): - return { - key: _json_safe_rpc_value(item) - for key, item in value.items() - } + return {key: _json_safe_rpc_value(item) for key, item in value.items()} return value def _encode_submit_appeal_data( self: GenLayerClient, transaction_id: HexStr, + expected_decision_id: Optional[int] = None, + studio_appeal_shape: bool = False, ): + """Encode the chain's native submitAppeal entrypoint.""" consensus_main_contract = self.w3.eth.contract( abi=self.chain.consensus_main_contract["abi"] ) @@ -496,16 +677,23 @@ def _encode_submit_appeal_data( transaction_id = transaction_id[2:] if len(transaction_id) > 64: raise ValueError("transaction_id too long for bytes32") - params = abi_encode( - contract_fn.argument_types, - [self.w3.to_bytes(hexstr=transaction_id)], - ) + arguments = [self.w3.to_bytes(hexstr=transaction_id)] + if not studio_appeal_shape: + if expected_decision_id is None: + raise ValueError("submitAppeal requires expected_decision_id") + arguments.append(expected_decision_id) + params = abi_encode(contract_fn.argument_types, arguments) function_selector = eth_utils.keccak(text=contract_fn.signature)[:4].hex() encoded_data = "0x" + function_selector + params.hex() return encoded_data FEE_MANAGEMENT_ARGUMENT_TYPES = ("bytes32", FEES_DISTRIBUTION_ABI_TYPE) +TOP_UP_AND_SUBMIT_APPEAL_ARGUMENT_TYPES = ( + "bytes32", + "uint256", + FEES_DISTRIBUTION_ABI_TYPE, +) def _encode_fee_management_data( @@ -513,20 +701,28 @@ def _encode_fee_management_data( function_name: str, transaction_id: HexStr, distribution: FeesDistributionInput, + expected_decision_id: Optional[int] = None, + studio_appeal_shape: bool = False, ): + """Encode the chain's native fee-management entrypoint.""" if function_name not in ("topUpFees", "topUpAndSubmitAppeal"): raise ValueError(f"Unsupported fee management function: {function_name}") tx_bytes = _to_bytes32(self, transaction_id) fees_distribution = create_fees_distribution(distribution) - params = abi_encode( - FEE_MANAGEMENT_ARGUMENT_TYPES, - [ - tx_bytes, - fees_distribution_to_abi_tuple(fees_distribution), - ], - ) - signature = f"{function_name}(bytes32,{FEES_DISTRIBUTION_ABI_TYPE})" + fees_tuple = fees_distribution_to_abi_tuple(fees_distribution) + if function_name == "topUpAndSubmitAppeal" and not studio_appeal_shape: + if expected_decision_id is None: + raise ValueError("topUpAndSubmitAppeal requires expected_decision_id") + argument_types = TOP_UP_AND_SUBMIT_APPEAL_ARGUMENT_TYPES + arguments = [tx_bytes, expected_decision_id, fees_tuple] + signature = f"{function_name}(bytes32,uint256,{FEES_DISTRIBUTION_ABI_TYPE})" + else: + argument_types = FEE_MANAGEMENT_ARGUMENT_TYPES + arguments = [tx_bytes, fees_tuple] + signature = f"{function_name}(bytes32,{FEES_DISTRIBUTION_ABI_TYPE})" + + params = abi_encode(argument_types, arguments) function_selector = eth_utils.keccak(text=signature)[:4].hex() return "0x" + function_selector + params.hex() @@ -614,9 +810,13 @@ def _get_default_valid_until() -> int: def get_current_fee_policy(self: GenLayerClient) -> FeePolicyQuote: - if self.chain.fee_manager_contract and self.chain.fee_manager_contract.get("address"): + if self.chain.fee_manager_contract and self.chain.fee_manager_contract.get( + "address" + ): fee_manager_contract = self.w3.eth.contract( - address=self.w3.to_checksum_address(self.chain.fee_manager_contract["address"]), + address=self.w3.to_checksum_address( + self.chain.fee_manager_contract["address"] + ), abi=FEE_MANAGER_CALCULATE_ROUND_FEES_ABI, ) gen_per_time_unit = fee_manager_contract.functions.GENPerTimeUnit().call() @@ -672,7 +872,11 @@ def estimate_fees_distribution( options: Optional[FeeEstimateOptions] = None, ) -> FeesDistribution: policy = get_current_fee_policy(self) - return build_estimated_fees_distribution(options, policy) + return build_estimated_fees_distribution( + options, + policy, + self.chain.default_consensus_max_rotations, + ) def estimate_transaction_fees( @@ -688,11 +892,19 @@ def _estimate_transaction_fees_with_policy( options: Optional[FeeEstimateOptions], policy: FeePolicyQuote, ) -> TransactionFeeEstimate: - distribution = build_estimated_fees_distribution(options, policy) + distribution = build_estimated_fees_distribution( + options, + policy, + self.chain.default_consensus_max_rotations, + ) - if self.chain.fee_manager_contract and self.chain.fee_manager_contract.get("address"): + if self.chain.fee_manager_contract and self.chain.fee_manager_contract.get( + "address" + ): fee_manager_contract = self.w3.eth.contract( - address=self.w3.to_checksum_address(self.chain.fee_manager_contract["address"]), + address=self.w3.to_checksum_address( + self.chain.fee_manager_contract["address"] + ), abi=FEE_MANAGER_CALCULATE_ROUND_FEES_ABI, ) round_fees = fee_manager_contract.functions.calculateRoundFees( @@ -825,14 +1037,17 @@ def _resolve_transaction_fees( num_of_initial_validators: int, ) -> NormalizedTransactionFees: transaction_fees = normalize_transaction_fees(fees) - if ( - transaction_fees["fee_value"] is not None - or not requires_fee_deposit_calculation(transaction_fees["distribution"]) + if transaction_fees[ + "fee_value" + ] is not None or not requires_fee_deposit_calculation( + transaction_fees["distribution"] ): transaction_fees["fee_value"] = transaction_fees["fee_value"] or 0 return transaction_fees - if not self.chain.fee_manager_contract or not self.chain.fee_manager_contract.get("address"): + if not self.chain.fee_manager_contract or not self.chain.fee_manager_contract.get( + "address" + ): try: policy = get_current_fee_policy(self) except GenLayerError as exc: @@ -880,7 +1095,9 @@ def _encode_add_transaction_data( abi=self.chain.consensus_main_contract["abi"] ) contract_fn = consensus_main_contract.get_function_by_name("addTransaction") - abi_version = _get_add_transaction_abi_version(self.chain.consensus_main_contract["abi"]) + abi_version = _get_add_transaction_abi_version( + self.chain.consensus_main_contract["abi"] + ) transaction_fees = transaction_fees or normalize_transaction_fees() use_fee_aware_transaction = ( transaction_fees["requires_fee_aware_transaction"] or abi_version == "fees" @@ -1029,7 +1246,9 @@ def _send_consensus_call( method="eth_sendRawTransaction", params=[serialized_transaction] )["result"] except Exception as exc: - raise GenLayerError(f"{operation_name} failed: {_format_rpc_error(exc)}") from exc + raise GenLayerError( + f"{operation_name} failed: {_format_rpc_error(exc)}" + ) from exc if self.chain.id == localnet.id: return tx_hash @@ -1055,7 +1274,7 @@ def _send_transaction( if self.chain.consensus_main_contract is None: raise GenLayerError( - f"Consensus main contract address not found in chain config for \"{self.chain.name}\".", + f'Consensus main contract address not found in chain config for "{self.chain.name}".', ) try: diff --git a/genlayer_py/staking/__init__.py b/genlayer_py/staking/__init__.py index d449eb4..c6de75f 100644 --- a/genlayer_py/staking/__init__.py +++ b/genlayer_py/staking/__init__.py @@ -5,6 +5,7 @@ validator_claim, validator_prime, set_operator, + get_validator_join_context, get_operator_transfer_context, initiate_operator_transfer, complete_operator_transfer, @@ -17,6 +18,8 @@ epoch, active_validators, active_validators_count, + joined_validators, + joined_validators_count, is_validator, get_validator_info, get_stake_info, @@ -24,6 +27,16 @@ validator_min_stake, delegator_min_stake, ) +from genlayer_py.staking.operator_registration import ( + OperatorPublicKey, + OperatorRegistrationContext, + OperatorRegistrationProof, + create_operator_registration, + operator_address_from_public_key, + operator_possession_message, + operator_public_key_from_private_key, + verify_operator_registration, +) __all__ = [ "validator_join", @@ -32,6 +45,7 @@ "validator_claim", "validator_prime", "set_operator", + "get_validator_join_context", "get_operator_transfer_context", "initiate_operator_transfer", "complete_operator_transfer", @@ -44,10 +58,20 @@ "epoch", "active_validators", "active_validators_count", + "joined_validators", + "joined_validators_count", "is_validator", "get_validator_info", "get_stake_info", "banned_validators", "validator_min_stake", "delegator_min_stake", + "OperatorPublicKey", + "OperatorRegistrationContext", + "OperatorRegistrationProof", + "create_operator_registration", + "operator_address_from_public_key", + "operator_possession_message", + "operator_public_key_from_private_key", + "verify_operator_registration", ] diff --git a/genlayer_py/staking/abi/staking_abi.json b/genlayer_py/staking/abi/staking_abi.json index fba7fd3..51dbdc5 100644 --- a/genlayer_py/staking/abi/staking_abi.json +++ b/genlayer_py/staking/abi/staking_abi.json @@ -1,4033 +1,6668 @@ [ - { - "inputs": [], - "name": "BurnTransferFailed", - "type": "error" - }, - { - "inputs": [], - "name": "DeepthoughtCallFailed", - "type": "error" - }, - { - "inputs": [], - "name": "DelegatorBelowMinimumStake", - "type": "error" - }, - { - "inputs": [], - "name": "DelegatorExitExceedsShares", - "type": "error" - }, - { - "inputs": [], - "name": "DelegatorExitWouldBeBelowMinimum", - "type": "error" - }, - { - "inputs": [], - "name": "DelegatorMayNotExitWithZeroShares", - "type": "error" - }, - { - "inputs": [], - "name": "DelegatorMayNotJoinTwoValidatorsSimultaneously", - "type": "error" - }, - { - "inputs": [], - "name": "DelegatorMayNotJoinWithZeroValue", - "type": "error" - }, - { - "inputs": [], - "name": "DelegatorMustExitAllWhenBelowMinimum", - "type": "error" - }, - { - "inputs": [], - "name": "EpochAdvanceNotReady", - "type": "error" - }, - { - "inputs": [], - "name": "EpochAlreadyFinalized", - "type": "error" - }, - { - "inputs": [], - "name": "EpochNotFinalized", - "type": "error" - }, - { - "inputs": [], - "name": "EpochNotFinished", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "FailedTransfer", - "type": "error" - }, - { - "inputs": [], - "name": "InflationAlreadyInitialized", - "type": "error" - }, - { - "inputs": [], - "name": "InflationAlreadyReceived", - "type": "error" - }, - { - "inputs": [], - "name": "InflationInvalidAmount", - "type": "error" - }, - { - "inputs": [], - "name": "InflationRequestFailed", - "type": "error" - }, - { - "inputs": [], - "name": "InsufficientInflationFunds", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidAtEpoch", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInflationThresholds", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidOperatorAddress", - "type": "error" - }, - { - "inputs": [], - "name": "MaxNumberOfValidatorsReached", - "type": "error" - }, - { - "inputs": [], - "name": "MaxValidatorsCannotBeZero", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMinterCallFailed", - "type": "error" - }, - { - "inputs": [], - "name": "NFTMinterNotConfigured", - "type": "error" - }, - { - "inputs": [], - "name": "NoBurning", - "type": "error" - }, - { - "inputs": [], - "name": "NumberOfValidatorsExceedsAvailable", - "type": "error" - }, - { - "inputs": [], - "name": "OnlyGEN", - "type": "error" - }, - { - "inputs": [], - "name": "OnlyIdleness", - "type": "error" - }, - { - "inputs": [], - "name": "OnlyIdlenessOrTribunal", - "type": "error" - }, - { - "inputs": [], - "name": "OnlyTransactions", - "type": "error" - }, - { - "inputs": [], - "name": "OnlyTransactionsOrTribunal", - "type": "error" - }, - { - "inputs": [], - "name": "OnlyTribunal", - "type": "error" - }, - { - "inputs": [], - "name": "OperatorAlreadyAssigned", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - } - ], - "name": "PendingTribunals", - "type": "error" - }, - { - "inputs": [], - "name": "PreviousEpochNotFinalizable", - "type": "error" - }, - { - "inputs": [], - "name": "ReductionFactorCannotBeZero", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorAlreadyInTree", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorAlreadyJoined", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorBelowMinimumStake", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorExitExceedsShares", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorMayNotBeDelegator", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorMayNotDepositZeroValue", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorMayNotJoinWithZeroValue", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorMustNotBeDelegator", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorNotActive", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorNotInTree", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorNotJoined", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorWithdrawalExceedsStake", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorsConsumed", - "type": "error" - }, - { - "inputs": [], - "name": "ValidatorsUnavailable", - "type": "error" - }, - { - "anonymous": false, - "inputs": [], - "name": "AllValidatorBansRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "BurnFailed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "BurnToL1", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "delegator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "DelegatorClaim", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "delegator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "DelegatorExit", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "delegator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "DelegatorJoin", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - } - ], - "name": "EpochAdvance", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - } - ], - "name": "EpochFinalize", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - } - ], - "name": "EpochHasPendingTribunals", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - } - ], - "name": "EpochZeroEnded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "FeesReceived", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - } - ], - "name": "InflationInitiated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - } - ], - "name": "InflationReceived", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint256", - "name": "targetEpoch", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "l2GasPrice", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "l2GasLimit", - "type": "uint256" - } - ], - "name": "InflationRequestedFromL2", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "inflationRequestThreshold", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "inflationTargetAhead", - "type": "uint256" - } - ], - "name": "InflationThresholdsSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "l2GasPrice", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "l2GasLimit", - "type": "uint256" - } - ], - "name": "L1InflationGasParamsSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "startIndex", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "processedCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "nextIndex", - "type": "uint256" - } - ], - "name": "QuarantinesCleanedUp", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "deepthought", - "type": "address" - } - ], - "name": "SetDeepthought", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "delegatorMinStake", - "type": "uint256" - } - ], - "name": "SetDelegatorMinimumStake", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "epochExtraMinDuration", - "type": "uint256" - } - ], - "name": "SetEpochExtraMinDuration", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "epochMinDuration", - "type": "uint256" - } - ], - "name": "SetEpochMinDuration", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "epochMinDurationThreshold", - "type": "uint256" - } - ], - "name": "SetEpochMinDurationThreshold", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "epochZeroMinDuration", - "type": "uint256" - } - ], - "name": "SetEpochZeroMinDuration", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "gen", - "type": "address" - } - ], - "name": "SetGen", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "maxValidators", - "type": "uint256" - } - ], - "name": "SetMaxValidators", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "reductionFactor", - "type": "uint256" - } - ], - "name": "SetReductionFactor", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "stakingInvariant", - "type": "address" - } - ], - "name": "SetStakingInvariant", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "transactionFeesManager", - "type": "address" - } - ], - "name": "SetTransactionFeesManager", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "delegatorUnbondingPeriod", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "validatorUnbondingPeriod", - "type": "uint256" - } - ], - "name": "SetUnbondingPeriods", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "validatorMinStake", - "type": "uint256" - } - ], - "name": "SetValidatorMinimumStake", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "alpha", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "beta", - "type": "uint256" - } - ], - "name": "SetValidatorWeightParams", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "ValidatorBanRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "ValidatorBannedDeterministic", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "bannedAt", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "bannedUntil", - "type": "uint256" - } - ], - "name": "ValidatorBannedIdleness", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "ValidatorClaim", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "ValidatorDeposit", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "ValidatorExit", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "ValidatorIsAlreadyInTree", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "ValidatorIsNotActive", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "ValidatorIsNotInTree", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "operator", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "ValidatorJoin", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "validatorRewards", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "delegatorRewards", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "feeRewards", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "feePenalties", - "type": "uint256" - } - ], - "name": "ValidatorPrime", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "ValidatorQuarantineRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "ValidatorQuarantineRepealed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "quarantinedAt", - "type": "uint256" - } - ], - "name": "ValidatorQuarantined", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "validatorSlashing", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "delegatorSlashing", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - } - ], - "name": "ValidatorSlash", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "count", - "type": "uint256" - } - ], - "name": "ValidatorsRegistered", - "type": "event" - }, - { - "inputs": [], - "name": "BASE_TOKEN", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "DEFAULT_DELEGATOR_MIN_STAKE", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "DEFAULT_VALIDATOR_MIN_STAKE", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "DELEGATOR_UNBONDING_PERIOD", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "INFLATION_BASE", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "INFLATION_DEEPTHOUGHT_BPS", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "INFLATION_DEVELOPER_BPS", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "INFLATION_FINAL", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "INFLATION_INITIAL", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "INFLATION_MID", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "INFLATION_STAKER_BPS", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "INFLATION_STEEPNESS", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "INFLATION_VALIDATOR_BPS", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "QUARANTINE_MANAGER_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "VALIDATOR_UNBONDING_PERIOD", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "activeValidators", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "activeValidatorsCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "activeWeights", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "addressManager", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "validatorAddresses", - "type": "address[]" - } - ], - "name": "adminRegisterValidators", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "burning", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "canAdvance", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "deepthought", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_inflation", - "type": "uint256" - } - ], - "name": "deepthoughtInflation", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "delegatorClaim", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_index", - "type": "uint256" - } - ], - "name": "delegatorDeposit", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "commit", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.Claim", - "name": "claim_", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "input", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "output", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "linkToNextCommit", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.Commit", - "name": "commit_", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_epoch", - "type": "uint256" - } - ], - "name": "delegatorDepositByEpoch", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "input", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "output", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "linkToNextCommit", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.Commit", - "name": "commit_", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "delegatorDepositLen", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_amount", - "type": "uint256" - } - ], - "name": "delegatorExit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "delegatorJoin", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "delegatorMinStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_index", - "type": "uint256" - } - ], - "name": "delegatorWithdrawal", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "quantity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "commit", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.Claim", - "name": "claim_", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "input", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "output", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "linkToNextCommit", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.Commit", - "name": "commit_", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_epoch", - "type": "uint256" - } - ], - "name": "delegatorWithdrawalByEpoch", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "input", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "output", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "linkToNextCommit", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.Commit", - "name": "commit_", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "delegatorWithdrawalLen", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_inflation", - "type": "uint256" - } - ], - "name": "developerInflation", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "epoch", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "epochAdvance", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "epochEven", - "outputs": [ - { - "internalType": "uint256", - "name": "start", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "end", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "inflation", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "weight", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "weightDeposit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "weightWithdrawal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vcount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "claimed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "stakeDeposit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "stakeWithdrawal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "slashed", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "epochExtraMinDuration", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "epochFinalize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "epochFinalizeImmediate", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_epoch", - "type": "uint256" - } - ], - "name": "epochInflation", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "epochMinDuration", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "epochMinDurationThreshold", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "epochOdd", - "outputs": [ - { - "internalType": "uint256", - "name": "start", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "end", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "inflation", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "weight", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "weightDeposit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "weightWithdrawal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vcount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "claimed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "stakeDeposit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "stakeWithdrawal", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "slashed", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "epochZeroMinDuration", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "finalizationPhaseAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "finalized", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_randomSeed", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_txCreatedTimestamp", - "type": "uint256" - } - ], - "name": "getActivatorForSeed", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_size", - "type": "uint256" - } - ], - "name": "getAllBannedValidators", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "untilEpochBanned", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "permanentlyBanned", - "type": "bool" - } - ], - "internalType": "struct IGenLayerStaking.BannedValidators[]", - "name": "validatorList", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_size", - "type": "uint256" - } - ], - "name": "getAllBannedValidatorsForEpoch", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "untilEpochBanned", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "permanentlyBanned", - "type": "bool" - } - ], - "internalType": "struct IGenLayerStaking.BannedValidators[]", - "name": "validatorList", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_size", - "type": "uint256" - } - ], - "name": "getAllQuarantinedValidators", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "untilEpochBanned", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "permanentlyBanned", - "type": "bool" - } - ], - "internalType": "struct IGenLayerStaking.BannedValidators[]", - "name": "validatorList", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_size", - "type": "uint256" - } - ], - "name": "getAllQuarantinedValidatorsForEpoch", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "untilEpochBanned", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "permanentlyBanned", - "type": "bool" - } - ], - "internalType": "struct IGenLayerStaking.BannedValidators[]", - "name": "validatorList", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "getPendingDelegatorDeposits", - "outputs": [ - { - "internalType": "uint256", - "name": "total", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "getPendingDelegatorWithdrawals", - "outputs": [ - { - "internalType": "uint256", - "name": "total", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "getPendingValidatorDeposits", - "outputs": [ - { - "internalType": "uint256", - "name": "total", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "getPendingValidatorWithdrawals", - "outputs": [ - { - "internalType": "uint256", - "name": "total", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "getValidatorDelegators", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_pageSize", - "type": "uint256" - } - ], - "name": "getValidatorDelegatorsInfo", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "delegator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "currentStake", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "currentShares", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "pendingDeposits", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "pendingWithdrawals", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.DelegatorInfo[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_pageSize", - "type": "uint256" - } - ], - "name": "getValidatorDelegatorsPaginated", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getValidatorQuarantineList", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_startIndex", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_pageSize", - "type": "uint256" - } - ], - "name": "getValidatorsJoined", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_at", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_until", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "idlenessBan", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "_validators", - "type": "address[]" - }, - { - "internalType": "uint256", - "name": "_quarantinedAt", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_quarantinedUntil", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "idlenessBanBatch", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "idlenessPhaseAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "inflationEpoch", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_inflationOnset", - "type": "uint256" - } - ], - "name": "inflationInit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_epoch", - "type": "uint256" - } - ], - "name": "inflationReceive", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "inflationRequestThreshold", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "inflationSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "inflationTargetAhead", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "address", - "name": "_delegator", - "type": "address" - } - ], - "name": "isDelegatorOfValidator", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "isValidator", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "isValidatorBanned", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "l2GasLimit", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "l2GasPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "maxValidators", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "operator", - "type": "address" - } - ], - "name": "operatorsToValidators", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "reductionFactor", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "removeAllValidatorBans", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "removeValidatorBan", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "revealingPhaseAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address payable", - "name": "_deepthought", - "type": "address" - } - ], - "name": "setDeepthought", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_delegatorMinStake", - "type": "uint256" - } - ], - "name": "setDelegatorMinimumStake", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_epochExtraMinDuration", - "type": "uint256" - } - ], - "name": "setEpochExtraMinDuration", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_epochMinDuration", - "type": "uint256" - } - ], - "name": "setEpochMinDuration", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_epochMinDurationThreshold", - "type": "uint256" - } - ], - "name": "setEpochMinDurationThreshold", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_epochZeroMinDuration", - "type": "uint256" - } - ], - "name": "setEpochZeroMinDuration", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_finalizationPhase", - "type": "address" - } - ], - "name": "setFinalizationPhase", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_gen", - "type": "address" - } - ], - "name": "setGen", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_idlenessPhase", - "type": "address" - } - ], - "name": "setIdlenessPhase", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_inflationRequestThreshold", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_inflationTargetAhead", - "type": "uint256" - } - ], - "name": "setInflationThresholds", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_l2GasPrice", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_l2GasLimit", - "type": "uint256" - } - ], - "name": "setL1InflationGasParams", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_maxValidators", - "type": "uint256" - } - ], - "name": "setMaxValidators", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_reductionFactor", - "type": "uint256" - } - ], - "name": "setReductionFactor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_revealingPhase", - "type": "address" - } - ], - "name": "setRevealingPhase", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_stakingInvariant", - "type": "address" - } - ], - "name": "setStakingInvariant", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_transactionFeesManager", - "type": "address" - } - ], - "name": "setTransactionFeesManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_delegatorUnbondingPeriod", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_validatorUnbondingPeriod", - "type": "uint256" - } - ], - "name": "setUnbondingPeriods", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_validatorMinStake", - "type": "uint256" - } - ], - "name": "setValidatorMinimumStake", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_alpha", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_beta", - "type": "uint256" - } - ], - "name": "setValidatorWeightParams", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "sharesOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_delegator", - "type": "address" - }, - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "stakeOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "stakingInvariant", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "transactionFeesManager", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorBanDeterministic", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorBanned", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "untilEpochBanned", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "permanentlyBanned", - "type": "bool" - } - ], - "internalType": "struct IGenLayerStaking.BannedValidators", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorClaim", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorDelegatorCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_index", - "type": "uint256" - } - ], - "name": "validatorDeposit", - "outputs": [ - { - "internalType": "uint256", - "name": "epoch_", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "input", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "output", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "linkToNextCommit", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.Commit", - "name": "commit_", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "validatorDeposit", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_epoch", - "type": "uint256" - } - ], - "name": "validatorDepositByEpoch", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "input", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "output", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "linkToNextCommit", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.Commit", - "name": "commit_", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorDepositLen", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_amount", - "type": "uint256" - } - ], - "name": "validatorExit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_operator", - "type": "address" - } - ], - "name": "validatorJoin", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "validatorJoin", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], - "name": "validatorMinStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorPrime", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_at", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - } - ], - "name": "validatorQuarantine", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "validatorQuarantineCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorQuarantineRepeal", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_seed", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_slot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_txCreatedTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_number", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "_weighted", - "type": "bool" - }, - { - "internalType": "address[]", - "name": "_consumed", - "type": "address[]" - } - ], - "name": "validatorSelection", - "outputs": [ - { - "internalType": "uint256", - "name": "leader_", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "validators_", - "type": "address[]" - }, - { - "internalType": "address[]", - "name": "penalized_", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_seed", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_slot", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_txCreatedTimestamp", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_number", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "_weighted", - "type": "bool" - }, - { - "internalType": "address[]", - "name": "_consumed", - "type": "address[]" - } - ], - "name": "validatorSelection", - "outputs": [ - { - "internalType": "uint256", - "name": "leader_", - "type": "uint256" - }, - { - "internalType": "address[]", - "name": "validators_", - "type": "address[]" - }, - { - "internalType": "address[]", - "name": "penalized_", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorView", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "left", - "type": "address" - }, - { - "internalType": "address", - "name": "right", - "type": "address" - }, - { - "internalType": "address", - "name": "parent", - "type": "address" - }, - { - "internalType": "uint256", - "name": "eBanned", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "ePrimed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vStake", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vShares", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "dStake", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "dShares", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vDeposit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vWithdrawal", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "live", - "type": "bool" - } - ], - "internalType": "struct IGenLayerStaking.ValidatorView", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorViewPrePrimed", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "left", - "type": "address" - }, - { - "internalType": "address", - "name": "right", - "type": "address" - }, - { - "internalType": "address", - "name": "parent", - "type": "address" - }, - { - "internalType": "uint256", - "name": "eBanned", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "ePrimed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vStake", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vShares", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "dStake", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "dShares", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vDeposit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vWithdrawal", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "live", - "type": "bool" - } - ], - "internalType": "struct IGenLayerStaking.ValidatorView", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorViewPrimed", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "left", - "type": "address" - }, - { - "internalType": "address", - "name": "right", - "type": "address" - }, - { - "internalType": "address", - "name": "parent", - "type": "address" - }, - { - "internalType": "uint256", - "name": "eBanned", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "ePrimed", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vStake", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vShares", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "dStake", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "dShares", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vDeposit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "vWithdrawal", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "live", - "type": "bool" - } - ], - "internalType": "struct IGenLayerStaking.ValidatorView", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_index", - "type": "uint256" - } - ], - "name": "validatorWithdrawal", - "outputs": [ - { - "internalType": "uint256", - "name": "epoch_", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "input", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "output", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "linkToNextCommit", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.Commit", - "name": "commit_", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_epoch", - "type": "uint256" - } - ], - "name": "validatorWithdrawalByEpoch", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "input", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "output", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "epoch", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "linkToNextCommit", - "type": "uint256" - } - ], - "internalType": "struct IGenLayerStaking.Commit", - "name": "commit_", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_validator", - "type": "address" - } - ], - "name": "validatorWithdrawalLen", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "validatorsCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "validatorsEven", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "validatorsJoined", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "validatorsJoinedCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "validatorsOdd", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "validatorsRoot", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "weightsEven", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "weightsOdd", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - } + { + "inputs": [], + "name": "ArrayLengthMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "BurnTransferFailed", + "type": "error" + }, + { + "inputs": [], + "name": "DeepthoughtCallFailed", + "type": "error" + }, + { + "inputs": [], + "name": "DelegatorBelowMinimumStake", + "type": "error" + }, + { + "inputs": [], + "name": "DelegatorExitExceedsShares", + "type": "error" + }, + { + "inputs": [], + "name": "DelegatorExitUnrepresentableShares", + "type": "error" + }, + { + "inputs": [], + "name": "DelegatorExitWouldBeBelowMinimum", + "type": "error" + }, + { + "inputs": [], + "name": "DelegatorMayNotExitWithZeroShares", + "type": "error" + }, + { + "inputs": [], + "name": "DelegatorMayNotJoinTwoValidatorsSimultaneously", + "type": "error" + }, + { + "inputs": [], + "name": "DelegatorMayNotJoinWithZeroValue", + "type": "error" + }, + { + "inputs": [], + "name": "DelegatorMustExitAllWhenBelowMinimum", + "type": "error" + }, + { + "inputs": [], + "name": "EpochAdvanceNotReady", + "type": "error" + }, + { + "inputs": [], + "name": "EpochAlreadyFinalized", + "type": "error" + }, + { + "inputs": [], + "name": "EpochDurationCannotBeZero", + "type": "error" + }, + { + "inputs": [], + "name": "EpochDurationOverflow", + "type": "error" + }, + { + "inputs": [], + "name": "EpochNotFinalized", + "type": "error" + }, + { + "inputs": [], + "name": "EpochNotFinished", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "FailedTransfer", + "type": "error" + }, + { + "inputs": [], + "name": "IncentivePercentageTooHigh", + "type": "error" + }, + { + "inputs": [], + "name": "InflationAlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "InflationAlreadyReceived", + "type": "error" + }, + { + "inputs": [], + "name": "InflationInvalidAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InflationRequestFailed", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientInflationFunds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAtEpoch", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInflationThresholds", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidOperatorAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPageSize", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPrimingSelfStakeBps", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidWeightParams", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "JudicialCapacityReservationActive", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "JudicialCapacityReservationMissing", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "JudicialQuarantineCauseActive", + "type": "error" + }, + { + "inputs": [], + "name": "MaxNumberOfValidatorsReached", + "type": "error" + }, + { + "inputs": [], + "name": "MaxValidatorsCannotBeZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "requested", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maximum", + "type": "uint256" + } + ], + "name": "MaxValidatorsExceedsSafeLimit", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "attemptId", + "type": "uint64" + } + ], + "name": "MinimumStakeTransitionActive", + "type": "error" + }, + { + "inputs": [], + "name": "MinimumStakeTransitionConfigLocked", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "expectedCursor", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "actualCursor", + "type": "uint24" + } + ], + "name": "MinimumStakeTransitionCursorMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "expectedAttemptId", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "actualAttemptId", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "expectedAttemptHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "actualAttemptHash", + "type": "bytes32" + } + ], + "name": "MinimumStakeTransitionIdentityMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "enum IGenLayerStaking.MinimumStakeTransitionStatus", + "name": "status", + "type": "uint8" + } + ], + "name": "MinimumStakeTransitionInvalidStatus", + "type": "error" + }, + { + "inputs": [], + "name": "MinimumStakeTransitionNotActive", + "type": "error" + }, + { + "inputs": [], + "name": "MirrorFieldOverflow", + "type": "error" + }, + { + "inputs": [], + "name": "NFTMinterCallFailed", + "type": "error" + }, + { + "inputs": [], + "name": "NFTMinterNotConfigured", + "type": "error" + }, + { + "inputs": [], + "name": "NoBurning", + "type": "error" + }, + { + "inputs": [], + "name": "NumberOfValidatorsExceedsAvailable", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyGEN", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyIdleness", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyIdlenessOrTribunal", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyStakingRouter", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyTransactions", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyTransactionsOrTribunal", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyTribunal", + "type": "error" + }, + { + "inputs": [], + "name": "OperatorAlreadyAssigned", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "PendingTribunals", + "type": "error" + }, + { + "inputs": [], + "name": "PermanentQuarantineNeedsOwnerPardon", + "type": "error" + }, + { + "inputs": [], + "name": "PreviousEpochNotFinalizable", + "type": "error" + }, + { + "inputs": [], + "name": "ReductionFactorCannotBeZero", + "type": "error" + }, + { + "inputs": [], + "name": "RegistryFull", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "SelectionAuthorityPinned", + "type": "error" + }, + { + "inputs": [], + "name": "UnauthorizedDelegatorClaim", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "requested", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cap", + "type": "uint256" + } + ], + "name": "UnbanCapExceeded", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorAlreadyInTree", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorAlreadyJoined", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorBelowMinimumStake", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorDoesNotExist", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorExitExceedsShares", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorMayNotBeDelegator", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorMayNotDepositZeroValue", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorMayNotJoinWithZeroValue", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorMustNotBeDelegator", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorNotActive", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorNotInTree", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorNotJoined", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorWithdrawalExceedsStake", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorsConsumed", + "type": "error" + }, + { + "inputs": [], + "name": "ValidatorsUnavailable", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newVStake", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "recordX", + "type": "bool" + } + ], + "name": "AdminStakeCorrected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "AllValidatorBansRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "enum IGenLayerStaking.BurnSource", + "name": "source", + "type": "uint8" + } + ], + "name": "BurnAccrued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "burnThreshold", + "type": "uint256" + } + ], + "name": "BurnThresholdSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "BurnToL1", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "epochNumber", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "priorWeight", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "epochWeight", + "type": "uint256" + } + ], + "name": "CorruptedEpochWeight", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DelegationToBelowMinimumValidator", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DelegatorClaim", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DelegatorExit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DelegatorJoin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "EpochAdvance", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "EpochFinalize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "FeesReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "incentivePercentage", + "type": "uint256" + } + ], + "name": "IncentivePercentageSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "InflationInitiated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "InflationReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "targetEpoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "l2GasPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "l2GasLimit", + "type": "uint256" + } + ], + "name": "InflationRequestedFromL2", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "inflationRequestThreshold", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "inflationTargetAhead", + "type": "uint256" + } + ], + "name": "InflationThresholdsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "restored", + "type": "bool" + } + ], + "name": "JudicialCapacityReleased", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "JudicialCapacityReserved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "JudicialConvictionMaterialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + } + ], + "name": "JudicialConvictionQueued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "JudicialSuspensionSettled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "accusedAtEpoch", + "type": "uint256" + } + ], + "name": "JudiciallySuspended", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "l2GasPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "l2GasLimit", + "type": "uint256" + } + ], + "name": "L1InflationGasParamsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "maxPerEpoch", + "type": "uint256" + } + ], + "name": "MaxUnbansPerEpochSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "processedCount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nextIndex", + "type": "uint256" + } + ], + "name": "QuarantinesCleanedUp", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "count", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "snapshotHash", + "type": "bytes32" + } + ], + "name": "SelectionPoolSnapshotSealed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "daoFeeBps", + "type": "uint256" + } + ], + "name": "SetDaoFeeBps", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "deepthought", + "type": "address" + } + ], + "name": "SetDeepthought", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "delegatorMinStake", + "type": "uint256" + } + ], + "name": "SetDelegatorMinimumStake", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "epochExtraMinDuration", + "type": "uint256" + } + ], + "name": "SetEpochExtraMinDurationOverride", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "epochMinDuration", + "type": "uint256" + } + ], + "name": "SetEpochMinDuration", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "epochMinDurationThreshold", + "type": "uint256" + } + ], + "name": "SetEpochMinDurationThreshold", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "epochZeroMinDuration", + "type": "uint256" + } + ], + "name": "SetEpochZeroMinDuration", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "gen", + "type": "address" + } + ], + "name": "SetGen", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "maxValidators", + "type": "uint256" + } + ], + "name": "SetMaxValidators", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "bps", + "type": "uint256" + } + ], + "name": "SetPrimingSelfStakeBps", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "reductionFactor", + "type": "uint256" + } + ], + "name": "SetReductionFactor", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "transactionFeesManager", + "type": "address" + } + ], + "name": "SetTransactionFeesManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "delegatorUnbondingPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "validatorUnbondingPeriod", + "type": "uint256" + } + ], + "name": "SetUnbondingPeriods", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "validatorMinStake", + "type": "uint256" + } + ], + "name": "SetValidatorMinimumStake", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "alpha", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "beta", + "type": "uint256" + } + ], + "name": "SetValidatorWeightParams", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SharelessFeesBurned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "SharelessStakeZeroed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "removed", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "remaining", + "type": "uint256" + } + ], + "name": "TemporaryValidatorBansRemovalProgress", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "ValidatorBanRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "bannedAt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "bannedUntil", + "type": "uint256" + } + ], + "name": "ValidatorBannedIdleness", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ValidatorClaim", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ValidatorDeposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ValidatorExit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ValidatorFeePenaltyBurned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ValidatorJoin", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "attemptId", + "type": "uint64" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "attemptHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldMinimum", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "proposedMinimum", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "worksetCount", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "worksetHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "baseGeneration", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "candidateGeneration", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "targetEpoch", + "type": "uint256" + } + ], + "name": "ValidatorMinimumStakeTransitionBegun", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "attemptId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "oldCursor", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "uint24", + "name": "newCursor", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "candidateWrites", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint64", + "name": "revision", + "type": "uint64" + } + ], + "name": "ValidatorMinimumStakeTransitionProgressed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint64", + "name": "attemptId", + "type": "uint64" + }, + { + "indexed": false, + "internalType": "enum IGenLayerStaking.MinimumStakeTransitionStatus", + "name": "status", + "type": "uint8" + } + ], + "name": "ValidatorMinimumStakeTransitionStatusUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "validatorRewards", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "delegatorRewards", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "feeRewards", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "feePenalties", + "type": "uint256" + } + ], + "name": "ValidatorPrime", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "ValidatorQuarantineRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "quarantinedAt", + "type": "uint256" + } + ], + "name": "ValidatorQuarantined", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ValidatorSelfStakeBonus", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "validatorSlashing", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "delegatorSlashing", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + } + ], + "name": "ValidatorSlash", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "predicted", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "actual", + "type": "uint256" + } + ], + "name": "VcountShadowMismatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "epochNumber", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "maxWeight", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "weightWithdrawal", + "type": "uint256" + } + ], + "name": "WeightWithdrawalOverflow", + "type": "event" + }, + { + "inputs": [], + "name": "BASE_TOKEN", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_DELEGATOR_MIN_STAKE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DEFAULT_VALIDATOR_MIN_STAKE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "DELEGATOR_UNBONDING_PERIOD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INFLATION_BASE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INFLATION_DEEPTHOUGHT_BPS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INFLATION_DEVELOPER_BPS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INFLATION_FINAL", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INFLATION_INITIAL", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INFLATION_MID", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INFLATION_STAKER_BPS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INFLATION_STEEPNESS", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INFLATION_VALIDATOR_BPS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "QUARANTINE_MANAGER_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VALIDATOR_UNBONDING_PERIOD", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "enum IGenLayerStaking.BurnSource", + "name": "source", + "type": "uint8" + } + ], + "name": "accrueBurn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "addressManager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_validatorMinStake", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "_expectedPublishedVersion", + "type": "uint64" + } + ], + "name": "beginValidatorMinimumStakeTransition", + "outputs": [ + { + "internalType": "uint64", + "name": "attemptId", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "attemptHash", + "type": "bytes32" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "burnThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "burnedDevInflationSurplus", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "burnedStrandedFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "burning", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "canAdvance", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "canFinalize", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_attemptId", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "_attemptHash", + "type": "bytes32" + } + ], + "name": "cancelValidatorMinimumStakeTransition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "clearEpochExtraMinDurationOverride", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "convictJudicially", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "daoFeeBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "deepthought", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_inflation", + "type": "uint256" + } + ], + "name": "deepthoughtInflation", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "delegatorClaim", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + } + ], + "name": "delegatorDeposit", + "outputs": [ + { + "components": [ + { + "internalType": "uint120", + "name": "quantity", + "type": "uint120" + }, + { + "internalType": "uint120", + "name": "offset", + "type": "uint120" + }, + { + "internalType": "uint256", + "name": "commit", + "type": "uint256" + } + ], + "internalType": "struct IGenLayerStaking.Claim", + "name": "claim_", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "input", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + }, + { + "internalType": "uint120", + "name": "outstanding", + "type": "uint120" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint56", + "name": "linkToNextCommit", + "type": "uint56" + }, + { + "internalType": "bool", + "name": "priced", + "type": "bool" + }, + { + "internalType": "bool", + "name": "fragmented", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.Commit", + "name": "commit_", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "delegatorDepositByEpoch", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "input", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + }, + { + "internalType": "uint120", + "name": "outstanding", + "type": "uint120" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint56", + "name": "linkToNextCommit", + "type": "uint56" + }, + { + "internalType": "bool", + "name": "priced", + "type": "bool" + }, + { + "internalType": "bool", + "name": "fragmented", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.Commit", + "name": "commit_", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "delegatorDepositLen", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "delegatorExit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "delegatorJoin", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "delegatorMinStake", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + } + ], + "name": "delegatorWithdrawal", + "outputs": [ + { + "components": [ + { + "internalType": "uint120", + "name": "quantity", + "type": "uint120" + }, + { + "internalType": "uint120", + "name": "offset", + "type": "uint120" + }, + { + "internalType": "uint256", + "name": "commit", + "type": "uint256" + } + ], + "internalType": "struct IGenLayerStaking.Claim", + "name": "claim_", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "input", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + }, + { + "internalType": "uint120", + "name": "outstanding", + "type": "uint120" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint56", + "name": "linkToNextCommit", + "type": "uint56" + }, + { + "internalType": "bool", + "name": "priced", + "type": "bool" + }, + { + "internalType": "bool", + "name": "fragmented", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.Commit", + "name": "commit_", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "delegatorWithdrawalByEpoch", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "input", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + }, + { + "internalType": "uint120", + "name": "outstanding", + "type": "uint120" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint56", + "name": "linkToNextCommit", + "type": "uint56" + }, + { + "internalType": "bool", + "name": "priced", + "type": "bool" + }, + { + "internalType": "bool", + "name": "fragmented", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.Commit", + "name": "commit_", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "delegatorWithdrawalLen", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_inflation", + "type": "uint256" + } + ], + "name": "developerInflation", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "economicEpochValidatorCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "effectiveValidatorOccupancy", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "seed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "slot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "number", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "generation", + "type": "uint64" + }, + { + "internalType": "address", + "name": "pointer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "runtimeCodeHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "snapshotHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "maxUtil", + "type": "uint256" + }, + { + "internalType": "uint256[7]", + "name": "temporaryUnavailableBits", + "type": "uint256[7]" + }, + { + "internalType": "uint256[]", + "name": "consumedRegistryIndexes", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "priorSlotPenalizedRegistryIndexes", + "type": "uint256[]" + } + ], + "internalType": "struct IGenLayerStaking.SnapshotSelectionRequest", + "name": "_request", + "type": "tuple" + } + ], + "name": "eligibleRegistryIndexesFromSnapshotAuthority", + "outputs": [ + { + "internalType": "uint256[]", + "name": "indexes", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "epoch", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "epochAdvance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "epochEven", + "outputs": [ + { + "internalType": "uint256", + "name": "start", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "end", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "inflation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weightDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weightWithdrawal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vcount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stakeDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stakeWithdrawal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "slashed", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "epochExtraMinDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "epochExtraMinDurationOverride", + "outputs": [ + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "epochFinalize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "epochFinalizeImmediate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "epochInflation", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "epochMinDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "epochMinDurationThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "epochOdd", + "outputs": [ + { + "internalType": "uint256", + "name": "start", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "end", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "inflation", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weightDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weightWithdrawal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vcount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stakeDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stakeWithdrawal", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "slashed", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "epochZeroMinDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "expectedClaimableInflation", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "seed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "slot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "number", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "generation", + "type": "uint64" + }, + { + "internalType": "address", + "name": "pointer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "runtimeCodeHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "snapshotHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "maxUtil", + "type": "uint256" + }, + { + "internalType": "uint256[7]", + "name": "temporaryUnavailableBits", + "type": "uint256[7]" + }, + { + "internalType": "uint256[]", + "name": "consumedRegistryIndexes", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "priorSlotPenalizedRegistryIndexes", + "type": "uint256[]" + } + ], + "internalType": "struct IGenLayerStaking.SnapshotSelectionRequest", + "name": "_request", + "type": "tuple" + }, + { + "internalType": "uint256[]", + "name": "_candidateIndexes", + "type": "uint256[]" + } + ], + "name": "filterEligibleRegistryIndexesFromSnapshotAuthority", + "outputs": [ + { + "internalType": "uint256[]", + "name": "indexes", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "finalizationPhaseAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_attemptId", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "_attemptHash", + "type": "bytes32" + } + ], + "name": "finalizeValidatorMinimumStakeTransition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "finalized", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_randomSeed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_txCreatedTimestamp", + "type": "uint256" + } + ], + "name": "getActivatorForSeed", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_size", + "type": "uint256" + } + ], + "name": "getAllBannedValidators", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "untilEpochBanned", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "permanentlyBanned", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.BannedValidators[]", + "name": "validatorList", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_size", + "type": "uint256" + } + ], + "name": "getAllBannedValidatorsForEpoch", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "untilEpochBanned", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "permanentlyBanned", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.BannedValidators[]", + "name": "validatorList", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_size", + "type": "uint256" + } + ], + "name": "getAllQuarantinedValidators", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "untilEpochBanned", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "permanentlyBanned", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.BannedValidators[]", + "name": "validatorList", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_size", + "type": "uint256" + } + ], + "name": "getAllQuarantinedValidatorsForEpoch", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "untilEpochBanned", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "permanentlyBanned", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.BannedValidators[]", + "name": "validatorList", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEpochExtraMinDuration", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "getPendingDelegatorDeposits", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "getPendingDelegatorWithdrawals", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxItems", + "type": "uint256" + } + ], + "name": "getPendingJudicialConvictions", + "outputs": [ + { + "internalType": "address[]", + "name": "validators", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "getPendingValidatorDeposits", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "getPendingValidatorWithdrawals", + "outputs": [ + { + "internalType": "uint256", + "name": "total", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_txCreatedTimestamp", + "type": "uint256" + } + ], + "name": "getUnavailableQuarantinedValidators", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "getValidatorDelegators", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_pageSize", + "type": "uint256" + } + ], + "name": "getValidatorDelegatorsInfo", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "currentStake", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "currentShares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pendingDeposits", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "pendingWithdrawals", + "type": "uint256" + } + ], + "internalType": "struct IGenLayerStaking.DelegatorInfo[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_pageSize", + "type": "uint256" + } + ], + "name": "getValidatorDelegatorsPaginated", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getValidatorQuarantineList", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_pageSize", + "type": "uint256" + } + ], + "name": "getValidatorsJoined", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_at", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_until", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "idlenessBan", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_validators", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "_quarantinedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_quarantinedUntil", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "idlenessBanBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "idlenessPhaseAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "_registryIndexes", + "type": "uint256[]" + } + ], + "name": "idlenessRestrictionEndEpochs", + "outputs": [ + { + "internalType": "uint256[]", + "name": "endEpochs", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "incentivePercentage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inflationEpoch", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epochTo", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_inauguralTimestamp", + "type": "uint256" + } + ], + "name": "inflationInit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "inflationInitDone", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "inflationReceive", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "inflationRequestThreshold", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "inflationRequested", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inflationSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inflationTargetAhead", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_inauguralTimestamp", + "type": "uint256" + } + ], + "name": "initializeEpochStart", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "address", + "name": "_delegator", + "type": "address" + } + ], + "name": "isDelegatorOfValidator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + } + ], + "name": "isSelectableValidatorAtRegistryIndex", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + } + ], + "name": "isTribunalVoterLiveAvailableAtRegistryIndex", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "isValidator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "isValidatorBanned", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "judicialCapacityReservationCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "judicialCapacityReserved", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "judicialConvictionPending", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "judicialSelectionRestricted", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "judicialSelectionRestrictedCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "judicialSelectionRestrictionVersion", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "judicialSuspended", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l2GasLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "l2GasPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxUnbansPerEpoch", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxValidators", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "operatorsToValidators", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingJudicialConvictionCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingRequestInflationFromL1", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "permanentRestrictionVersion", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "registryIndex", + "type": "uint256" + } + ], + "name": "preparedEconomicCohortContains", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "preparedEconomicCohortCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "preparedSelectionRosterCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "primingSelfStakeBps", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "_attemptId", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "_attemptHash", + "type": "bytes32" + }, + { + "internalType": "uint24", + "name": "_expectedCursor", + "type": "uint24" + } + ], + "name": "processValidatorMinimumStakeTransition", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "quarantineHistoryVersion", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "recountVcount", + "outputs": [ + { + "internalType": "uint256", + "name": "counted", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reductionFactor", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "refreshEpochExtraMinDuration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "registryIndexPlus1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "_validators", + "type": "address[]" + } + ], + "name": "registryIndexesOf", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "remainingUnbanAllowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "removeAllValidatorBansExceptPermanent", + "outputs": [ + { + "internalType": "uint256", + "name": "removedCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "remainingTemporary", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "removeValidatorBan", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "removeValidatorQuarantine", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "requestInflationIfNeeded", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "_indexes", + "type": "uint256[]" + } + ], + "name": "resolveRegistryIndexes", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "revealingPhaseAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_randomSeed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_slot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_txCreatedTimestamp", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_excludedActivator", + "type": "address" + } + ], + "name": "selectPendingActivator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "selectableValidators", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "selectableValidatorsCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "_registryIndexes", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "_quarantineVersion", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_judicialSelectionVersion", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_verdictEpoch", + "type": "uint256" + } + ], + "name": "selectionBarredAtVersions", + "outputs": [ + { + "internalType": "uint256", + "name": "barredBits", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "judiciallyBarredBits", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "selectionEconomicUnavailableBitmap", + "outputs": [ + { + "internalType": "uint256[7]", + "name": "bitmap", + "type": "uint256[7]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "selectionEpochParameters", + "outputs": [ + { + "internalType": "uint256", + "name": "minimumStake", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxUtil", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "selectionPoolSnapshot", + "outputs": [ + { + "internalType": "uint256[]", + "name": "indexes", + "type": "uint256[]" + }, + { + "internalType": "bytes32", + "name": "snapshotHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "selectionPoolSnapshotAuthority", + "outputs": [ + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "generation", + "type": "uint64" + }, + { + "internalType": "address", + "name": "pointer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "runtimeCodeHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "snapshotHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_count", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "_generation", + "type": "uint64" + }, + { + "internalType": "address", + "name": "_pointer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_runtimeCodeHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_snapshotHash", + "type": "bytes32" + } + ], + "name": "selectionPoolSnapshotFromAuthority", + "outputs": [ + { + "internalType": "uint256[]", + "name": "indexes", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "frozenUtilities", + "type": "uint256[]" + }, + { + "internalType": "uint256[7]", + "name": "liveUnavailableBits", + "type": "uint256[7]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_txCreatedTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "_sortedConsumedRegistryIndexes", + "type": "uint256[]" + } + ], + "name": "selectionPoolUnavailableCount", + "outputs": [ + { + "internalType": "uint256", + "name": "poolCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unavailableCount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "seed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "slot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "number", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "generation", + "type": "uint64" + }, + { + "internalType": "address", + "name": "pointer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "runtimeCodeHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "snapshotHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "maxUtil", + "type": "uint256" + }, + { + "internalType": "uint256[7]", + "name": "temporaryUnavailableBits", + "type": "uint256[7]" + }, + { + "internalType": "uint256[]", + "name": "consumedRegistryIndexes", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "priorSlotPenalizedRegistryIndexes", + "type": "uint256[]" + } + ], + "internalType": "struct IGenLayerStaking.SnapshotSelectionRequest", + "name": "_request", + "type": "tuple" + } + ], + "name": "selectionPoolUnavailableCountFromSnapshotAuthority", + "outputs": [ + { + "internalType": "uint256", + "name": "poolCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unavailableCount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "seed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "slot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "number", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "generation", + "type": "uint64" + }, + { + "internalType": "address", + "name": "pointer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "runtimeCodeHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "snapshotHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "maxUtil", + "type": "uint256" + }, + { + "internalType": "uint256[7]", + "name": "temporaryUnavailableBits", + "type": "uint256[7]" + }, + { + "internalType": "uint256[]", + "name": "consumedRegistryIndexes", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "priorSlotPenalizedRegistryIndexes", + "type": "uint256[]" + } + ], + "internalType": "struct IGenLayerStaking.SnapshotSelectionRequest", + "name": "_request", + "type": "tuple" + }, + { + "internalType": "uint256[]", + "name": "_registryIndexes", + "type": "uint256[]" + } + ], + "name": "selectionSnapshotPositionsFromAuthority", + "outputs": [ + { + "internalType": "uint256[]", + "name": "positions", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "foundBits", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_quarantineHistoryVersion", + "type": "uint256" + } + ], + "name": "selectionTemporaryUnavailableBitmap", + "outputs": [ + { + "internalType": "uint256[7]", + "name": "bitmap", + "type": "uint256[7]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_burnThreshold", + "type": "uint256" + } + ], + "name": "setBurnThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_daoFeeBps", + "type": "uint256" + } + ], + "name": "setDaoFeeBps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "_deepthought", + "type": "address" + } + ], + "name": "setDeepthought", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_delegatorMinStake", + "type": "uint256" + } + ], + "name": "setDelegatorMinimumStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epochExtraMinDuration", + "type": "uint256" + } + ], + "name": "setEpochExtraMinDurationOverride", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epochMinDuration", + "type": "uint256" + } + ], + "name": "setEpochMinDuration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epochMinDurationThreshold", + "type": "uint256" + } + ], + "name": "setEpochMinDurationThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epochZeroMinDuration", + "type": "uint256" + } + ], + "name": "setEpochZeroMinDuration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_finalizationPhase", + "type": "address" + } + ], + "name": "setFinalizationPhase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_gen", + "type": "address" + } + ], + "name": "setGen", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_idlenessPhase", + "type": "address" + } + ], + "name": "setIdlenessPhase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_incentivePercentage", + "type": "uint256" + } + ], + "name": "setIncentivePercentage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_inflationRequestThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_inflationTargetAhead", + "type": "uint256" + } + ], + "name": "setInflationThresholds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_l2GasPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_l2GasLimit", + "type": "uint256" + } + ], + "name": "setL1InflationGasParams", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_max", + "type": "uint256" + } + ], + "name": "setMaxUnbansPerEpoch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxValidators", + "type": "uint256" + } + ], + "name": "setMaxValidators", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_bps", + "type": "uint256" + } + ], + "name": "setPrimingSelfStakeBps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_reductionFactor", + "type": "uint256" + } + ], + "name": "setReductionFactor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_revealingPhase", + "type": "address" + } + ], + "name": "setRevealingPhase", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_transactionFeesManager", + "type": "address" + } + ], + "name": "setTransactionFeesManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_delegatorUnbondingPeriod", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_validatorUnbondingPeriod", + "type": "uint256" + } + ], + "name": "setUnbondingPeriods", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_alpha", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_beta", + "type": "uint256" + } + ], + "name": "setValidatorWeightParams", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "enum IGenLayerStaking.JudicialCapacityDisposition", + "name": "_disposition", + "type": "uint8" + } + ], + "name": "settleJudicialSuspension", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "sharesOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "stakeOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_accusedAtEpoch", + "type": "uint256" + } + ], + "name": "suspendJudicially", + "outputs": [ + { + "internalType": "enum IGenLayerStaking.JudicialCapacityDisposition", + "name": "disposition", + "type": "uint8" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "transactionFeesManager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tribunalEligibleRegistryIndexes", + "outputs": [ + { + "internalType": "uint256[]", + "name": "indexes", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "seed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "slot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "number", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "generation", + "type": "uint64" + }, + { + "internalType": "address", + "name": "pointer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "runtimeCodeHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "snapshotHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "maxUtil", + "type": "uint256" + }, + { + "internalType": "uint256[7]", + "name": "temporaryUnavailableBits", + "type": "uint256[7]" + }, + { + "internalType": "uint256[]", + "name": "consumedRegistryIndexes", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "priorSlotPenalizedRegistryIndexes", + "type": "uint256[]" + } + ], + "internalType": "struct IGenLayerStaking.SnapshotSelectionRequest", + "name": "_request", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "_quarantineHistoryVersion", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_judicialSelectionVersion", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_creationEpoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_offset", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_pageSize", + "type": "uint256" + } + ], + "name": "tribunalEligibleRegistryIndexesPageFromSnapshotAuthority", + "outputs": [ + { + "internalType": "uint256[]", + "name": "indexes", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unbanCapConfigured", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "unbansInEpoch", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "validatorBanCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "validatorBanned", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "untilEpochBanned", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "permanentlyBanned", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.BannedValidators", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "validatorClaim", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "validatorDelegatorCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + } + ], + "name": "validatorDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "epoch_", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "input", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + }, + { + "internalType": "uint120", + "name": "outstanding", + "type": "uint120" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint56", + "name": "linkToNextCommit", + "type": "uint56" + }, + { + "internalType": "bool", + "name": "priced", + "type": "bool" + }, + { + "internalType": "bool", + "name": "fragmented", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.Commit", + "name": "commit_", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "validatorDeposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "validatorDepositByEpoch", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "input", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + }, + { + "internalType": "uint120", + "name": "outstanding", + "type": "uint120" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint56", + "name": "linkToNextCommit", + "type": "uint56" + }, + { + "internalType": "bool", + "name": "priced", + "type": "bool" + }, + { + "internalType": "bool", + "name": "fragmented", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.Commit", + "name": "commit_", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "validatorDepositLen", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "validatorExit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "_operatorPubKey", + "type": "uint256[2]" + }, + { + "internalType": "bytes", + "name": "_possessionProof", + "type": "bytes" + } + ], + "name": "validatorJoin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "validatorMinStake", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "validatorMinimumStakePublishedVersion", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "validatorMinimumStakeTransition", + "outputs": [ + { + "components": [ + { + "internalType": "enum IGenLayerStaking.MinimumStakeTransitionStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "uint64", + "name": "id", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "fromPublishedVersion", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "baseGeneration", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "candidateGeneration", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "capacityVersion", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "weightVersion", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "revision", + "type": "uint64" + }, + { + "internalType": "uint24", + "name": "worksetCount", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "cursor", + "type": "uint24" + }, + { + "internalType": "uint24", + "name": "grandfatheredOccupancy", + "type": "uint24" + }, + { + "internalType": "address", + "name": "worksetPointer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "targetEpoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "oldMinimum", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "proposedMinimum", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "weightDelta", + "type": "int256" + }, + { + "internalType": "int256", + "name": "vcountDelta", + "type": "int256" + }, + { + "internalType": "bytes32", + "name": "worksetRuntimeCodeHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "worksetHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "attemptHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "candidateAccountingFingerprint", + "type": "bytes32" + } + ], + "internalType": "struct IGenLayerStaking.MinimumStakeTransition", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "validatorPermanentlyBanned", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "validatorPrime", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_at", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + } + ], + "name": "validatorQuarantine", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "validatorQuarantineCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_seed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_slot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_txCreatedTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_number", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "_weighted", + "type": "bool" + }, + { + "internalType": "address[]", + "name": "_consumed", + "type": "address[]" + } + ], + "name": "validatorSelection", + "outputs": [ + { + "internalType": "uint256", + "name": "leader_", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "validators_", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "penalized_", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_seed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_slot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_txCreatedTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_number", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "_weighted", + "type": "bool" + }, + { + "internalType": "address[]", + "name": "_consumed", + "type": "address[]" + } + ], + "name": "validatorSelection", + "outputs": [ + { + "internalType": "uint256", + "name": "leader_", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "validators_", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "penalized_", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_seed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_number", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "_candidateIndexes", + "type": "uint256[]" + } + ], + "name": "validatorSelectionFromExactIndexes", + "outputs": [ + { + "internalType": "uint256[]", + "name": "selectedIndexes", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "rand", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_seed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_number", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_txCreatedTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "_consumedRegistryIndexes", + "type": "uint256[]" + } + ], + "name": "validatorSelectionFromList", + "outputs": [ + { + "internalType": "uint256[]", + "name": "selectedIndexes", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "rand_", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "seed", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "slot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "number", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "epoch", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "generation", + "type": "uint64" + }, + { + "internalType": "address", + "name": "pointer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "runtimeCodeHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "snapshotHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "maxUtil", + "type": "uint256" + }, + { + "internalType": "uint256[7]", + "name": "temporaryUnavailableBits", + "type": "uint256[7]" + }, + { + "internalType": "uint256[]", + "name": "consumedRegistryIndexes", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "priorSlotPenalizedRegistryIndexes", + "type": "uint256[]" + } + ], + "internalType": "struct IGenLayerStaking.SnapshotSelectionRequest", + "name": "_request", + "type": "tuple" + } + ], + "name": "validatorSelectionFromSnapshotAuthority", + "outputs": [ + { + "internalType": "uint256", + "name": "leader", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "validators", + "type": "address[]" + }, + { + "internalType": "address[]", + "name": "penalized", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "validatorView", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "eBanned", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ePrimed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vStake", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vShares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dStake", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dShares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vWithdrawal", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "live", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.ValidatorView", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "validatorViewPrePrimed", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "eBanned", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ePrimed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vStake", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vShares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dStake", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dShares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vWithdrawal", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "live", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.ValidatorView", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "validatorViewPrimed", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "eBanned", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "ePrimed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vStake", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vShares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dStake", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dShares", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vDeposit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "vWithdrawal", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "live", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.ValidatorView", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + } + ], + "name": "validatorWithdrawal", + "outputs": [ + { + "internalType": "uint256", + "name": "epoch_", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "input", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + }, + { + "internalType": "uint120", + "name": "outstanding", + "type": "uint120" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint56", + "name": "linkToNextCommit", + "type": "uint56" + }, + { + "internalType": "bool", + "name": "priced", + "type": "bool" + }, + { + "internalType": "bool", + "name": "fragmented", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.Commit", + "name": "commit_", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_epoch", + "type": "uint256" + } + ], + "name": "validatorWithdrawalByEpoch", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "input", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "output", + "type": "uint256" + }, + { + "internalType": "uint120", + "name": "outstanding", + "type": "uint120" + }, + { + "internalType": "uint64", + "name": "epoch", + "type": "uint64" + }, + { + "internalType": "uint56", + "name": "linkToNextCommit", + "type": "uint56" + }, + { + "internalType": "bool", + "name": "priced", + "type": "bool" + }, + { + "internalType": "bool", + "name": "fragmented", + "type": "bool" + } + ], + "internalType": "struct IGenLayerStaking.Commit", + "name": "commit_", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_validator", + "type": "address" + } + ], + "name": "validatorWithdrawalLen", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "validatorsJoined", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "validatorsJoinedCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } ] diff --git a/genlayer_py/staking/abi/validator_wallet_abi.json b/genlayer_py/staking/abi/validator_wallet_abi.json index df1cd5c..c7aa641 100644 --- a/genlayer_py/staking/abi/validator_wallet_abi.json +++ b/genlayer_py/staking/abi/validator_wallet_abi.json @@ -1,1052 +1,1391 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_owner", - "type": "address" - }, - { - "internalType": "address", - "name": "_staking", - "type": "address" - }, - { - "internalType": "address", - "name": "_consensus", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "AccessControlBadConfirmation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "neededRole", - "type": "bytes32" - } - ], - "name": "AccessControlUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [], - "name": "NotOperator", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "version", - "type": "uint64" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferStarted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "previousAdminRole", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "newAdminRole", - "type": "bytes32" - } - ], - "name": "RoleAdminChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleGranted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleRevoked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "validator", - "type": "address" - } - ], - "name": "ValidatorIdentitySet", - "type": "event" - }, - { - "stateMutability": "payable", - "type": "fallback" - }, - { - "inputs": [], - "name": "DEFAULT_ADMIN_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "acceptOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes", - "name": "_vrfProof", - "type": "bytes" - } - ], - "name": "activateTransaction", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_tribunalIndex", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_commitHash", - "type": "bytes32" - } - ], - "name": "commitTribunalAppealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_commitHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_validatorIndex", - "type": "uint256" - } - ], - "name": "commitVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "consensus", - "outputs": [ - { - "internalType": "contract IConsensusMain", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "consensusWithFees", - "outputs": [ - { - "internalType": "contract IConsensusMainWithFees", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getIdentity", - "outputs": [ - { - "components": [ - { - "internalType": "string", - "name": "moniker", - "type": "string" - }, - { - "internalType": "string", - "name": "logoUri", - "type": "string" - }, - { - "internalType": "string", - "name": "website", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - }, - { - "internalType": "string", - "name": "email", - "type": "string" - }, - { - "internalType": "string", - "name": "twitter", - "type": "string" - }, - { - "internalType": "string", - "name": "telegram", - "type": "string" - }, - { - "internalType": "string", - "name": "github", - "type": "string" - }, - { - "internalType": "bytes", - "name": "extraCid", - "type": "bytes" - } - ], - "internalType": "struct ValidatorIdentity", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getOperator", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - } - ], - "name": "getRoleAdmin", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "grantRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "hasRole", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "identity", - "outputs": [ - { - "internalType": "string", - "name": "moniker", - "type": "string" - }, - { - "internalType": "string", - "name": "logoUri", - "type": "string" - }, - { - "internalType": "string", - "name": "website", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - }, - { - "internalType": "string", - "name": "email", - "type": "string" - }, - { - "internalType": "string", - "name": "twitter", - "type": "string" - }, - { - "internalType": "string", - "name": "telegram", - "type": "string" - }, - { - "internalType": "string", - "name": "github", - "type": "string" - }, - { - "internalType": "bytes", - "name": "extraCid", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "saltAsAValidator", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "messagesAndOtherFieldsHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "otherExecutionFieldsHash", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.VoteType", - "name": "resultValue", - "type": "uint8" - }, - { - "components": [ - { - "internalType": "enum IMessages.MessageType", - "name": "messageType", - "type": "uint8" - }, - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "onAcceptance", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "saltNonce", - "type": "uint256" - } - ], - "internalType": "struct IMessages.SubmittedMessage[]", - "name": "messages", - "type": "tuple[]" - } - ], - "internalType": "struct IConsensusMain.LeaderRevealVoteParams", - "name": "_leaderRevealVoteParams", - "type": "tuple" - } - ], - "name": "leaderRevealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "operator", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pendingOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_processingBlock", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_eqBlocksOutputs", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "_vrfProof", - "type": "bytes" - } - ], - "name": "proposeReceipt", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_txExecutionHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_processingBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_storageFeeUsed", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "_eqBlocksOutputs", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "_vrfProof", - "type": "bytes" - } - ], - "name": "proposeReceiptWithFees", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "callerConfirmation", - "type": "address" - } - ], - "name": "renounceRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_tribunalIndex", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "_voteHash", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.VoteType", - "name": "_voteType", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "_otherExecutionFieldsHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_nonce", - "type": "uint256" - } - ], - "name": "revealTribunalAppealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "_txId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "_voteHash", - "type": "bytes32" - }, - { - "internalType": "enum ITransactions.VoteType", - "name": "_voteType", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "_otherExecutionFieldsHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "_nonce", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_validatorIndex", - "type": "uint256" - } - ], - "name": "revealVote", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "revokeRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_consensus", - "type": "address" - }, - { - "internalType": "address", - "name": "_consensusWithFees", - "type": "address" - }, - { - "internalType": "address", - "name": "_staking", - "type": "address" - } - ], - "name": "setExternalAddresses", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "moniker", - "type": "string" - }, - { - "internalType": "string", - "name": "logoUri", - "type": "string" - }, - { - "internalType": "string", - "name": "website", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - }, - { - "internalType": "string", - "name": "email", - "type": "string" - }, - { - "internalType": "string", - "name": "twitter", - "type": "string" - }, - { - "internalType": "string", - "name": "telegram", - "type": "string" - }, - { - "internalType": "string", - "name": "github", - "type": "string" - }, - { - "internalType": "bytes", - "name": "extraCid", - "type": "bytes" - } - ], - "name": "setIdentity", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_operator", - "type": "address" - } - ], - "name": "setOperator", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "staking", - "outputs": [ - { - "internalType": "contract IGenLayerStaking", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "interfaceId", - "type": "bytes4" - } - ], - "name": "supportsInterface", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "validatorClaim", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "validatorDeposit", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_shares", - "type": "uint256" - } - ], - "name": "validatorExit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - }, - { - "name": "initiateOperatorTransfer", - "type": "function", - "stateMutability": "nonpayable", - "inputs": [ - { - "name": "_newOperatorPubKey", - "type": "uint256[2]", - "internalType": "uint256[2]" - }, - { - "name": "_possessionProof", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [] - }, - { - "name": "completeOperatorTransfer", - "type": "function", - "stateMutability": "nonpayable", - "inputs": [], - "outputs": [] - }, - { - "name": "cancelOperatorTransfer", - "type": "function", - "stateMutability": "nonpayable", - "inputs": [], - "outputs": [] - }, - { - "name": "getPendingOperator", - "type": "function", - "stateMutability": "view", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - }, - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ] - } + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AccessControlBadConfirmation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "neededRole", + "type": "bytes32" + } + ], + "name": "AccessControlUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPossessionProof", + "type": "error" + }, + { + "inputs": [], + "name": "NoPendingOperator", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "NotOperator", + "type": "error" + }, + { + "inputs": [], + "name": "OperatorTransferNotReady", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "PubKeyIsZero", + "type": "error" + }, + { + "inputs": [], + "name": "PubKeyNotOnCurve", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "inputs": [], + "name": "TransferFailed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newOperator", + "type": "address" + } + ], + "name": "OperatorSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "by", + "type": "address" + } + ], + "name": "OperatorTransferCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "OperatorTransferCompleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "transferableAt", + "type": "uint256" + } + ], + "name": "OperatorTransferStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ValidatorClaimProcessed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "ValidatorDepositMade", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "ValidatorExitInitiated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "ValidatorIdentitySet", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "OPERATOR_TRANSFER_DELAY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "activateTransaction", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "cancelOperatorTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_tribunalIndex", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_commitHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_activeIndex", + "type": "uint256" + } + ], + "name": "commitTribunalAppealVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_commitHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_validatorIndex", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "commitVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "completeOperatorTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "consensus", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "consensusWithFees", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getIdentity", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "moniker", + "type": "string" + }, + { + "internalType": "string", + "name": "logoUri", + "type": "string" + }, + { + "internalType": "string", + "name": "website", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "email", + "type": "string" + }, + { + "internalType": "string", + "name": "twitter", + "type": "string" + }, + { + "internalType": "string", + "name": "telegram", + "type": "string" + }, + { + "internalType": "string", + "name": "github", + "type": "string" + }, + { + "internalType": "bytes", + "name": "extraCid", + "type": "bytes" + } + ], + "internalType": "struct IValidatorWallet.ValidatorIdentity", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOperator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOperatorPubKey", + "outputs": [ + { + "internalType": "uint256[2]", + "name": "", + "type": "uint256[2]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPendingOperator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "identity", + "outputs": [ + { + "internalType": "string", + "name": "moniker", + "type": "string" + }, + { + "internalType": "string", + "name": "logoUri", + "type": "string" + }, + { + "internalType": "string", + "name": "website", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "email", + "type": "string" + }, + { + "internalType": "string", + "name": "twitter", + "type": "string" + }, + { + "internalType": "string", + "name": "telegram", + "type": "string" + }, + { + "internalType": "string", + "name": "github", + "type": "string" + }, + { + "internalType": "bytes", + "name": "extraCid", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_factory", + "type": "address" + }, + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "_staking", + "type": "address" + }, + { + "internalType": "address", + "name": "_consensus", + "type": "address" + }, + { + "internalType": "uint256[2]", + "name": "_operatorPubKey", + "type": "uint256[2]" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "_newOperatorPubKey", + "type": "uint256[2]" + }, + { + "internalType": "bytes", + "name": "_possessionProof", + "type": "bytes" + } + ], + "name": "initiateOperatorTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "expectedAttemptId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "saltAsAValidator", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "messagesAndOtherFieldsHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "otherExecutionFieldsHash", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.VoteType", + "name": "resultValue", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "enum IMessages.MessageType", + "name": "messageType", + "type": "uint8" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "onAcceptance", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "saltNonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "feeParams", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "declaredBudget", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "allocationSubtree", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "callKey", + "type": "bytes32" + }, + { + "internalType": "bool", + "name": "useBalance", + "type": "bool" + } + ], + "internalType": "struct IMessages.SubmittedMessage[]", + "name": "messages", + "type": "tuple[]" + } + ], + "internalType": "struct IConsensusMain.LeaderRevealVoteParams", + "name": "_params", + "type": "tuple" + } + ], + "name": "leaderRevealVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "operator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "operatorPubKey", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "operatorTransferInitiated", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingOperator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "pendingOperatorPubKey", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_processingBlock", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_eqBlocksOutputs", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "proposeReceipt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_storageFeeUsed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_eqBlocksOutputs", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "proposeReceiptWithFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_txExecutionHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_processingBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_storageFeeUsed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_eqBlocksOutputs", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_vrfProof", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_reportedMessageFeesTotal", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "proposeReceiptWithMessages", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "callerConfirmation", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_tribunalIndex", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_voteHash", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.VoteType", + "name": "_voteType", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_otherExecutionFieldsHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_activeIndex", + "type": "uint256" + } + ], + "name": "revealTribunalAppealVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_voteHash", + "type": "bytes32" + }, + { + "internalType": "enum ITransactions.VoteType", + "name": "_voteType", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_otherExecutionFieldsHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_validatorIndex", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_expectedAttemptId", + "type": "bytes32" + } + ], + "name": "revealVote", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_consensus", + "type": "address" + }, + { + "internalType": "address", + "name": "_consensusWithFees", + "type": "address" + }, + { + "internalType": "address", + "name": "_staking", + "type": "address" + } + ], + "name": "setExternalAddresses", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "moniker", + "type": "string" + }, + { + "internalType": "string", + "name": "logoUri", + "type": "string" + }, + { + "internalType": "string", + "name": "website", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "email", + "type": "string" + }, + { + "internalType": "string", + "name": "twitter", + "type": "string" + }, + { + "internalType": "string", + "name": "telegram", + "type": "string" + }, + { + "internalType": "string", + "name": "github", + "type": "string" + }, + { + "internalType": "bytes", + "name": "extraCid", + "type": "bytes" + } + ], + "name": "setIdentity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "_operatorPubKey", + "type": "uint256[2]" + }, + { + "internalType": "bytes", + "name": "_possessionProof", + "type": "bytes" + } + ], + "name": "setOperatorPubKey", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "staking", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "validatorClaim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "validatorDeposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_shares", + "type": "uint256" + } + ], + "name": "validatorExit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } ] diff --git a/genlayer_py/staking/actions.py b/genlayer_py/staking/actions.py index c3a9a64..09f383e 100644 --- a/genlayer_py/staking/actions.py +++ b/genlayer_py/staking/actions.py @@ -15,6 +15,7 @@ from eth_typing import Address, ChecksumAddress from hexbytes import HexBytes +from genlayer_py.consensus.abi import ADDRESS_MANAGER_ABI from genlayer_py.exceptions import GenLayerError from genlayer_py.staking.abi import STAKING_ABI, VALIDATOR_WALLET_ABI from genlayer_py.staking.operator_registration import ( @@ -29,6 +30,14 @@ AddressLike = Union[Address, ChecksumAddress, str] +ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" + +# The joined validator registry is only readable in slices: committee capacity +# is 1,543 and an address[] that long overruns the return-size limit. 64 is the +# size the paged reads are written around, and the one genlayer-node uses for +# the same walk. +VALIDATORS_JOINED_PAGE_SIZE = 64 + def _require_staking(self: "GenLayerClient") -> ChecksumAddress: if self.chain.staking_contract is None: @@ -48,9 +57,7 @@ def _wallet(self: "GenLayerClient", validator: AddressLike): ) -def _sender( - self: "GenLayerClient", account: Optional[LocalAccount] -) -> LocalAccount: +def _sender(self: "GenLayerClient", account: Optional[LocalAccount]) -> LocalAccount: acct = account or self.local_account if acct is None: raise GenLayerError("No account provided and client has no local_account") @@ -97,11 +104,38 @@ def epoch(self: "GenLayerClient") -> int: def active_validators(self: "GenLayerClient") -> List[ChecksumAddress]: - return _staking(self).functions.activeValidators().call() + """Return validators that are currently eligible for protocol duties.""" + return _staking(self).functions.selectableValidators().call() def active_validators_count(self: "GenLayerClient") -> int: - return _staking(self).functions.activeValidatorsCount().call() + """Return the number of validators currently eligible for duties.""" + return _staking(self).functions.selectableValidatorsCount().call() + + +def joined_validators(self: "GenLayerClient") -> List[ChecksumAddress]: + """Return every validator wallet in the append-only joined registry.""" + # The joined registry can contain 1,543 entries, so read it in slices. The + # count is read first so a registry that grows underneath the walk cannot + # spin forever, and an empty page lets a shrinking walk stop safely. + staking = _staking(self) + total = staking.functions.validatorsJoinedCount().call() + + validators: List[ChecksumAddress] = [] + for start in range(0, total, VALIDATORS_JOINED_PAGE_SIZE): + page = staking.functions.getValidatorsJoined( + start, VALIDATORS_JOINED_PAGE_SIZE + ).call() + if not page: + break + validators.extend(page) + + return [v for v in validators if v != ZERO_ADDRESS] + + +def joined_validators_count(self: "GenLayerClient") -> int: + """Return the size of the append-only joined validator registry.""" + return _staking(self).functions.validatorsJoinedCount().call() def is_validator(self: "GenLayerClient", address: AddressLike) -> bool: @@ -152,25 +186,62 @@ def delegator_min_stake(self: "GenLayerClient") -> int: # ─── write methods ──────────────────────────────────────────────────── +def get_validator_join_context( + self: "GenLayerClient", + account: Optional[LocalAccount] = None, +) -> OperatorRegistrationContext: + """Return the factory-bound context required for a validator join proof.""" + sender = _sender(self, account) + address_manager_address = _staking(self).functions.addressManager().call() + address_manager = self.w3.eth.contract( + address=self.w3.to_checksum_address(address_manager_address), + abi=ADDRESS_MANAGER_ABI, + ) + factory = address_manager.functions.getAddressNonZero( + "ValidatorWalletFactory" + ).call() + return OperatorRegistrationContext( + registrar=self.w3.to_checksum_address(factory), + owner=self.w3.to_checksum_address(sender.address), + chain_id=self.w3.eth.chain_id, + ) + + def validator_join( self: "GenLayerClient", amount: int, - operator: Optional[AddressLike] = None, + registration: Optional[OperatorRegistrationProof] = None, account: Optional[LocalAccount] = None, + operator: Optional[AddressLike] = None, ) -> HexBytes: - """Joins as a validator with `amount` GEN stake. Deploys a new - ValidatorWallet. Returns the tx hash — call get_validator_info on - the resulting wallet address to discover it (or parse the receipt - for the ValidatorJoin event).""" + """Join with a proof-bound operator key and deploy a ValidatorWallet. + + Build ``registration`` with :func:`get_validator_join_context` and + ``create_operator_registration``. The legacy address-only overloads do not + exist on the train. + """ + if operator is not None or not isinstance(registration, OperatorRegistrationProof): + raise GenLayerError( + "validator_join now requires an OperatorRegistrationProof; call " + "get_validator_join_context(), create_operator_registration(), then " + "pass the resulting registration. An operator address alone is not " + "accepted by the train contract." + ) + sender = _sender(self, account) + context = get_validator_join_context(self, account) + if not verify_operator_registration(registration, context): + raise GenLayerError( + "Operator registration proof does not match the validator wallet " + "factory, owner, chain, or public key. Build a fresh proof from " + "get_validator_join_context()." + ) + contract = _staking(self) - # Staking.validatorJoin has two overloads — () and (address) — so - # web3.py needs the full signature, not the name alone. - if operator is not None: - op = self.w3.to_checksum_address(operator) - data = contract.encode_abi("validatorJoin(address)", args=[op]) - else: - data = contract.encode_abi("validatorJoin()", args=[]) + data = contract.encode_abi( + "validatorJoin", + args=[list(registration.operator_pub_key), registration.possession_proof], + ) tx = _build(self, sender, _require_staking(self), data, value=amount) return _send(self, sender, tx) @@ -186,7 +257,9 @@ def validator_deposit( sender = _sender(self, account) wallet = _wallet(self, validator) data = wallet.encode_abi("validatorDeposit", args=[]) - tx = _build(self, sender, self.w3.to_checksum_address(validator), data, value=amount) + tx = _build( + self, sender, self.w3.to_checksum_address(validator), data, value=amount + ) return _send(self, sender, tx) @@ -241,20 +314,13 @@ def set_operator( operator: AddressLike, account: Optional[LocalAccount] = None, ) -> HexBytes: - """Rotates the operator for an existing ValidatorWallet. Only the - wallet owner (the EOA that called validator_join) may do this. - - CON-715 removed this single call in favour of the two-step transfer - below; against a deployment carrying that change it reverts with no - decodable reason, because the selector no longer exists. Prefer - initiate_operator_transfer + complete_operator_transfer.""" - sender = _sender(self, account) - wallet = _wallet(self, validator) - data = wallet.encode_abi( - "setOperator", args=[self.w3.to_checksum_address(operator)] + """Explain how to migrate from the removed address-only rotation call.""" + raise GenLayerError( + "set_operator(address) was removed from the train contract and no " + "transaction was sent. Build a proof with " + "get_operator_transfer_context(), call initiate_operator_transfer(), " + "then complete_operator_transfer() after the transfer delay." ) - tx = _build(self, sender, self.w3.to_checksum_address(validator), data) - return _send(self, sender, tx) def get_operator_transfer_context( diff --git a/genlayer_py/transactions/actions.py b/genlayer_py/transactions/actions.py index 826ee91..021d9fc 100644 --- a/genlayer_py/transactions/actions.py +++ b/genlayer_py/transactions/actions.py @@ -2,21 +2,32 @@ from genlayer_py.logging import logger import json -import warnings from typing import Any, Dict, List, Literal, Optional from web3 import Web3 from web3.types import _Hash32 -from eth_typing import HexStr +from eth_typing import Address, HexStr from web3.logs import DISCARD from genlayer_py.config import transaction_config from genlayer_py.types import ( - TransactionStatus, ExecutionResult, EXECUTION_RESULT_NUMBER_TO_NAME, - TRANSACTION_STATUS_NAME_TO_NUMBER, - TRANSACTION_STATUS_NUMBER_TO_NAME, - is_decided_state, +) +from genlayer_py.types.transactions import ( + PROTOCOL_TRANSACTION_STATUS_NAME_TO_NUMBER, + PROTOCOL_TRANSACTION_STATUS_NUMBER_TO_NAME, + RESOLUTION_ACTION_NUMBER_TO_NAME, + RESOLUTION_SOURCE_NUMBER_TO_NAME, + ProtocolTransactionLifecycle, + ProtocolTransactionStatus, + transaction_lifecycle_from_protocol_status, + transaction_outcome_from_protocol_result, +) +from genlayer_py.consensus.abi import ( + ADDRESS_MANAGER_ABI, + CONSENSUS_DATA_BIG_ROUNDS_ABI, + ROUNDS_STORAGE_READ_ABI, + TRANSACTION_MANAGER_READ_ABI, ) from genlayer_py.exceptions import GenLayerError from typing import TYPE_CHECKING @@ -69,65 +80,7 @@ from genlayer_py.client import GenLayerClient -_did_warn_wait_for_transaction_receipt_status = False - - -def _warn_deprecated_receipt_status() -> None: - global _did_warn_wait_for_transaction_receipt_status - if _did_warn_wait_for_transaction_receipt_status: - return - _did_warn_wait_for_transaction_receipt_status = True - warnings.warn( - "wait_for_transaction_receipt(status=...) is deprecated; use " - "wait_until='decided' or wait_until='finalized' instead.", - DeprecationWarning, - stacklevel=3, - ) - - -def _resolve_wait_target( - status: Optional[TransactionStatus], - wait_until: Optional[Literal["decided", "finalized"]], -) -> dict: - if wait_until is not None: - if wait_until not in ("decided", "finalized"): - raise ValueError("wait_until must be 'decided' or 'finalized'.") - return {"wait_until": wait_until, "legacy_status": None, "label": wait_until} - if status is None: - return {"wait_until": "decided", "legacy_status": None, "label": "decided"} - - _warn_deprecated_receipt_status() - if status == TransactionStatus.ACCEPTED: - return {"wait_until": "decided", "legacy_status": None, "label": "decided"} - if status == TransactionStatus.FINALIZED: - return {"wait_until": "finalized", "legacy_status": None, "label": "finalized"} - return {"wait_until": None, "legacy_status": status, "label": status.value} - - -def _has_reached_wait_target(transaction_status: str, target: dict) -> bool: - if target["wait_until"] == "decided": - return is_decided_state(transaction_status) - if target["wait_until"] == "finalized": - return transaction_status == TRANSACTION_STATUS_NAME_TO_NUMBER[ - TransactionStatus.FINALIZED - ] - legacy_status = target["legacy_status"] - return ( - legacy_status is not None - and transaction_status == TRANSACTION_STATUS_NAME_TO_NUMBER[legacy_status] - ) - - -def _normalize_status_name(value: Any) -> Optional[TransactionStatus]: - if isinstance(value, TransactionStatus): - return value - if isinstance(value, str): - if value in TransactionStatus._value2member_map_: - return TransactionStatus(value) - return TRANSACTION_STATUS_NUMBER_TO_NAME.get(value) - if value is None: - return None - return TRANSACTION_STATUS_NUMBER_TO_NAME.get(str(value)) +TRANSACTION_ARRAY_PAGE_SIZE = 64 def _normalize_execution_result_name(value: Any) -> Optional[ExecutionResult]: @@ -141,9 +94,14 @@ def _normalize_execution_result_name(value: Any) -> Optional[ExecutionResult]: def is_successful(transaction: GenLayerTransaction) -> bool: - status_name = _normalize_status_name( - transaction.get("status_name", transaction.get("status")) - ) + lifecycle = transaction.get("lifecycle") + if not isinstance(lifecycle, dict): + return False + lifecycle_state = lifecycle.get("state") + successful_lifecycle = ( + lifecycle_state == "finalized" + and lifecycle.get("outcome") in (None, "accepted") + ) or (lifecycle_state == "decided" and lifecycle.get("outcome") == "accepted") execution_result_name = _normalize_execution_result_name( transaction.get( "tx_execution_result_name", @@ -164,53 +122,563 @@ def is_successful(transaction: GenLayerTransaction) -> bool: execution_result_name = ExecutionResult.FINISHED_WITH_RETURN return ( - status_name in (TransactionStatus.ACCEPTED, TransactionStatus.FINALIZED) + successful_lifecycle and execution_result_name == ExecutionResult.FINISHED_WITH_RETURN ) -def wait_for_transaction_receipt( +def _wait_for_transaction( self: GenLayerClient, transaction_hash: _Hash32, - status: Optional[TransactionStatus] = None, - wait_until: Optional[Literal["decided", "finalized"]] = None, + wait_until: Literal["decided", "finalized"], interval: int = transaction_config.wait_interval, retries: int = transaction_config.retries, full_transaction: bool = False, ) -> GenLayerTransaction: - attempts = 0 - target = _resolve_wait_target(status, wait_until) transaction = None - last_status = None + last_state = None while attempts < retries: transaction = self.get_transaction(transaction_hash=transaction_hash) if transaction is None: raise GenLayerError(f"Transaction {transaction_hash} not found") - transaction_status = str(transaction["status"]) - last_status = TRANSACTION_STATUS_NUMBER_TO_NAME.get(transaction_status) + lifecycle = transaction.get("lifecycle") + if not isinstance(lifecycle, dict) or not isinstance( + lifecycle.get("state"), str + ): + raise GenLayerError( + f"Transaction {transaction_hash} has no valid lifecycle" + ) + last_state = lifecycle["state"] + reached_target = ( + last_state in ("decided", "finalized", "canceled") + if wait_until == "decided" + else last_state == "finalized" + ) - if _has_reached_wait_target(transaction_status, target): + if wait_until == "finalized" and last_state == "canceled": + raise GenLayerError( + f"Transaction {transaction_hash} was canceled before finalization" + ) + + if reached_target: if not full_transaction: return _simplify_transaction_receipt(transaction) return transaction time.sleep(interval / 1000) attempts += 1 - last_status_label = ( - last_status.value - if isinstance(last_status, TransactionStatus) - else str(transaction["status"]) if transaction else "" - ) raise GenLayerError( - f"Transaction {transaction_hash} did not reach desired status '{target['label']}' after {retries} attempts " + f"Transaction {transaction_hash} did not reach '{wait_until}' after {retries} attempts " f"(polling every {interval}ms for a total of {retries * interval / 1000:.1f}s). " - f"Last observed status: '{last_status_label}'. " + f"Last observed lifecycle state: '{last_state or ''}'. " f"This may indicate the transaction is still processing, or the network is experiencing delays. " f"Consider increasing 'retries' or 'interval' parameters.\n" f"Transaction object simplified: {json.dumps(_simplify_transaction_receipt(transaction), indent=2, default=str)}" ) +def wait_for_decision( + self: GenLayerClient, + transaction_hash: _Hash32, + interval: int = transaction_config.wait_interval, + retries: int = transaction_config.retries, + full_transaction: bool = False, +) -> GenLayerTransaction: + """Poll until the stored transaction state is decided or terminal.""" + + return _wait_for_transaction( + self, + transaction_hash, + "decided", + interval, + retries, + full_transaction, + ) + + +def wait_for_finalization( + self: GenLayerClient, + transaction_hash: _Hash32, + interval: int = transaction_config.wait_interval, + retries: int = transaction_config.retries, + full_transaction: bool = False, +) -> GenLayerTransaction: + """Poll until the stored transaction state is finalized.""" + + return _wait_for_transaction( + self, + transaction_hash, + "finalized", + interval, + retries, + full_transaction, + ) + + +def wait_for_transaction_receipt( + self: GenLayerClient, + transaction_hash: _Hash32, + wait_until: Literal["decided", "finalized"] = "decided", + interval: int = transaction_config.wait_interval, + retries: int = transaction_config.retries, + full_transaction: bool = False, +) -> GenLayerTransaction: + """Poll for a stored decision (default) or stored finalization.""" + + if wait_until == "decided": + return wait_for_decision( + self, transaction_hash, interval, retries, full_transaction + ) + if wait_until == "finalized": + return wait_for_finalization( + self, transaction_hash, interval, retries, full_transaction + ) + raise ValueError("wait_until must be 'decided' or 'finalized'.") + + +def _call_at_block(contract_function, block_number: int): + return contract_function.call(block_identifier=block_number) + + +def _address_manager( + self: GenLayerClient, + consensus_data_contract, + block_number: int, +): + address_manager_address = _call_at_block( + consensus_data_contract.functions.addressManager(), block_number + ) + return self.w3.eth.contract( + address=address_manager_address, abi=ADDRESS_MANAGER_ABI + ) + + +def _resolve_contract( + self: GenLayerClient, + address_manager, + name: str, + abi: List[dict], + block_number: int, +): + contract_address = _call_at_block( + address_manager.functions.getAddressNonZero(name), block_number + ) + return self.w3.eth.contract(address=contract_address, abi=abi) + + +def _read_round_validators( + big_rounds_contract, + transaction_hash: _Hash32, + round_number: int, + validators_count: int, + block_number: int, +) -> List[Address]: + validators: List[Address] = [] + for offset in range(0, validators_count, TRANSACTION_ARRAY_PAGE_SIZE): + page, total = _call_at_block( + big_rounds_contract.functions.getRoundValidatorsPaged( + transaction_hash, + round_number, + offset, + TRANSACTION_ARRAY_PAGE_SIZE, + ), + block_number, + ) + if total != validators_count: + raise GenLayerError( + "Inconsistent transaction committee size at fixed block " + f"{block_number}: expected {validators_count}, got {total}" + ) + validators.extend(page) + + if len(validators) != validators_count: + raise GenLayerError( + "Incomplete transaction committee at fixed block " + f"{block_number}: expected {validators_count}, got {len(validators)}" + ) + return validators + + +def _read_consumed_validators( + big_rounds_contract, + transaction_hash: _Hash32, + validators_count: int, + block_number: int, +) -> List[Address]: + validators: List[Address] = [] + for offset in range(0, validators_count, TRANSACTION_ARRAY_PAGE_SIZE): + page, total = _call_at_block( + big_rounds_contract.functions.getConsumedValidatorsPaged( + transaction_hash, + offset, + TRANSACTION_ARRAY_PAGE_SIZE, + ), + block_number, + ) + if total != validators_count: + raise GenLayerError( + "Inconsistent consumed-validator count at fixed block " + f"{block_number}: expected {validators_count}, got {total}" + ) + validators.extend(page) + + if len(validators) != validators_count: + raise GenLayerError( + "Incomplete consumed-validator set at fixed block " + f"{block_number}: expected {validators_count}, got {len(validators)}" + ) + return validators + + +def _read_train_transaction_data( + self: GenLayerClient, + consensus_data_contract, + transaction_hash: _Hash32, + block_number: int, +): + """Compose one bounded transaction snapshot from train read surfaces.""" + address_manager = _address_manager(self, consensus_data_contract, block_number) + big_rounds = _resolve_contract( + self, + address_manager, + "ConsensusDataBigRounds", + CONSENSUS_DATA_BIG_ROUNDS_ABI, + block_number, + ) + transaction_manager = _resolve_contract( + self, + address_manager, + "TransactionManager", + TRANSACTION_MANAGER_READ_ABI, + block_number, + ) + rounds_storage = _resolve_contract( + self, + address_manager, + "RoundsStorage", + ROUNDS_STORAGE_READ_ABI, + block_number, + ) + + tx_data = _call_at_block( + big_rounds.functions.getStoredTransactionDataLight(transaction_hash), + block_number, + ) + last_round = tx_data[21] + round_number = last_round[0] + validators_count = last_round[7] + validators = _read_round_validators( + big_rounds, + transaction_hash, + round_number, + validators_count, + block_number, + ) + consumed_validators = _read_consumed_validators( + big_rounds, + transaction_hash, + tx_data[22], + block_number, + ) + validator_votes = _call_at_block( + rounds_storage.functions.getValidatorVotes(transaction_hash, round_number), + block_number, + ) + validator_votes_hash = _call_at_block( + rounds_storage.functions.getValidatorVotesHash(transaction_hash, round_number), + block_number, + ) + validator_result_hash = _call_at_block( + rounds_storage.functions.getValidatorResultHash(transaction_hash, round_number), + block_number, + ) + tx_execution_result = _call_at_block( + transaction_manager.functions.getTxExecutionResult(transaction_hash), + block_number, + ) + num_of_initial_validators = _call_at_block( + transaction_manager.functions.getNumOfInitialValidators(transaction_hash), + block_number, + ) + + for label, values in ( + ("validator votes", validator_votes), + ("validator vote hashes", validator_votes_hash), + ("validator result hashes", validator_result_hash), + ): + if len(values) != validators_count: + raise GenLayerError( + f"Incomplete {label} at fixed block {block_number}: " + f"expected {validators_count}, got {len(values)}" + ) + + return ( + tx_data, + validators, + validator_votes, + validator_votes_hash, + validator_result_hash, + consumed_validators, + tx_execution_result, + num_of_initial_validators, + ) + + +def _read_transaction_lifecycle( + consensus_data_contract, + transaction_hash: _Hash32, + block_number: int, + timestamp: int = 0, +) -> ProtocolTransactionLifecycle: + lifecycle = _call_at_block( + consensus_data_contract.functions.getTransactionLifecycle( + transaction_hash, timestamp + ), + block_number, + ) + stored_status, resolution, latest_decision, decision_active = lifecycle + projected_status = resolution[2] + resolution_action = resolution[3] + resolution_source = resolution[6] + return { + "stored_status": int(stored_status), + "stored_status_name": PROTOCOL_TRANSACTION_STATUS_NUMBER_TO_NAME[ + str(stored_status) + ], + "projected_status": int(projected_status), + "projected_status_name": PROTOCOL_TRANSACTION_STATUS_NUMBER_TO_NAME[ + str(projected_status) + ], + "resolution_action": int(resolution_action), + "resolution_action_name": RESOLUTION_ACTION_NUMBER_TO_NAME[ + str(resolution_action) + ], + "resolution_source": int(resolution_source), + "resolution_source_name": RESOLUTION_SOURCE_NUMBER_TO_NAME[ + str(resolution_source) + ], + "decision_id": str(latest_decision[1]) if decision_active else None, + "decision_active": bool(decision_active), + "evaluated_at": int(resolution[17]), + } + + +def _decode_rpc_transaction_lifecycle(result: Any) -> ProtocolTransactionLifecycle: + if not isinstance(result, dict): + raise GenLayerError("gen_getTransactionLifecycle returned no lifecycle object") + + def code(key: str) -> int: + value = result.get(key) + if isinstance(value, bool) or not isinstance(value, int): + raise GenLayerError( + f"gen_getTransactionLifecycle returned invalid {key}: {value!r}" + ) + return value + + stored_status = code("storedStatusCode") + projected_status = code("projectedStatusCode") + resolution_action = code("resolutionActionCode") + resolution_source = code("resolutionSourceCode") + try: + stored_status_name = PROTOCOL_TRANSACTION_STATUS_NUMBER_TO_NAME[ + str(stored_status) + ] + projected_status_name = PROTOCOL_TRANSACTION_STATUS_NUMBER_TO_NAME[ + str(projected_status) + ] + resolution_action_name = RESOLUTION_ACTION_NUMBER_TO_NAME[ + str(resolution_action) + ] + resolution_source_name = RESOLUTION_SOURCE_NUMBER_TO_NAME[ + str(resolution_source) + ] + except KeyError as exc: + raise GenLayerError( + f"gen_getTransactionLifecycle returned unknown protocol ordinal: {exc.args[0]}" + ) from exc + + wire_names = { + "storedStatus": stored_status_name.value, + "projectedStatus": projected_status_name.value, + "resolutionAction": resolution_action_name.value, + "resolutionSource": resolution_source_name.value, + } + for key, enum_value in wire_names.items(): + expected = enum_value + if result.get(key) != expected: + raise GenLayerError( + f"gen_getTransactionLifecycle returned inconsistent {key}: " + f"expected {expected!r}, got {result.get(key)!r}" + ) + + decision_active = result.get("decisionActive") + if not isinstance(decision_active, bool): + raise GenLayerError( + "gen_getTransactionLifecycle returned invalid decisionActive" + ) + decision_id = result.get("decisionId") + if decision_id is not None and ( + not isinstance(decision_id, str) or not decision_id.isdigit() + ): + raise GenLayerError("gen_getTransactionLifecycle returned invalid decisionId") + if decision_active != (decision_id is not None): + raise GenLayerError( + "gen_getTransactionLifecycle returned inconsistent decision identity" + ) + evaluated_at = result.get("evaluatedAt") + if isinstance(evaluated_at, bool) or not isinstance(evaluated_at, int): + raise GenLayerError("gen_getTransactionLifecycle returned invalid evaluatedAt") + + return { + "stored_status": stored_status, + "stored_status_name": stored_status_name, + "projected_status": projected_status, + "projected_status_name": projected_status_name, + "resolution_action": resolution_action, + "resolution_action_name": resolution_action_name, + "resolution_source": resolution_source, + "resolution_source_name": resolution_source_name, + "decision_id": decision_id, + "decision_active": decision_active, + "evaluated_at": evaluated_at, + } + + +_METHOD_NOT_FOUND_CODE = -32601 +_METHOD_NOT_FOUND_MESSAGES = ( + "method not found", + "does not exist", + "method not supported", +) + + +def _is_method_not_found_error(error: Any) -> bool: + """Recognize only an explicit JSON-RPC missing-method response.""" + seen = set() + current = error + while current is not None and id(current) not in seen: + seen.add(id(current)) + if isinstance(current, dict): + code = current.get("code") + message = current.get("message") + current = current.get("error") or current.get("cause") + else: + code = getattr(current, "code", None) + message = getattr(current, "message", None) or str(current) + current = getattr(current, "__cause__", None) + if code == _METHOD_NOT_FOUND_CODE: + return True + if isinstance(message, str) and any( + marker in message.lower() for marker in _METHOD_NOT_FOUND_MESSAGES + ): + return True + return False + + +def _studio_protocol_status(value: Any) -> ProtocolTransactionStatus: + """Normalize the stored status exposed by the current Studio transaction.""" + if value == "ACTIVATED": + return ProtocolTransactionStatus.PENDING + if isinstance(value, ProtocolTransactionStatus): + return value + if isinstance(value, int) or (isinstance(value, str) and value.isdigit()): + return PROTOCOL_TRANSACTION_STATUS_NUMBER_TO_NAME[str(value)] + if isinstance(value, str) and value in ProtocolTransactionStatus.__members__: + return ProtocolTransactionStatus[value] + return ProtocolTransactionStatus(value) + + +def _studio_transaction_lifecycle_fallback( + self: GenLayerClient, + transaction_hash: _Hash32, + timestamp: Optional[int], + cause: Any, +) -> ProtocolTransactionLifecycle: + """Expose only the lifecycle facts current Studio can prove.""" + try: + response = self.provider.make_request( + method="eth_getTransactionByHash", params=[transaction_hash] + ) + transaction = response.get("result") if isinstance(response, dict) else None + if not isinstance(transaction, dict): + raise ValueError("missing transaction") + status_name = _studio_protocol_status(transaction.get("status")) + status_code = int(PROTOCOL_TRANSACTION_STATUS_NAME_TO_NUMBER[status_name]) + except Exception as exc: + if isinstance(cause, BaseException): + raise cause + raise GenLayerError( + "gen_getTransactionLifecycle is unavailable and Studio did not return " + "a readable stored transaction status" + ) from exc + + return { + "stored_status": status_code, + "stored_status_name": status_name, + "projected_status": status_code, + "projected_status_name": status_name, + "resolution_action": 0, + "resolution_action_name": RESOLUTION_ACTION_NUMBER_TO_NAME["0"], + "resolution_source": 0, + "resolution_source_name": RESOLUTION_SOURCE_NUMBER_TO_NAME["0"], + "decision_id": None, + "decision_active": False, + "evaluated_at": timestamp if timestamp is not None else int(time.time()), + } + + +def get_transaction_lifecycle( + self: GenLayerClient, + transaction_hash: _Hash32, + timestamp: Optional[int] = None, +) -> ProtocolTransactionLifecycle: + """Return the raw stored/projected resolution-kernel view. + + This is an advanced protocol API. Ordinary consumers should use the + discriminated ``lifecycle`` returned by :func:`get_transaction`. + """ + + if self.chain.id == localnet.id: + tx_id = ( + Web3.to_hex(transaction_hash) + if isinstance(transaction_hash, bytes) + else transaction_hash + ) + params: Dict[str, Any] = {"txId": tx_id} + if timestamp is not None: + params["timestamp"] = timestamp + try: + response = self.provider.make_request( + method="gen_getTransactionLifecycle", params=[params] + ) + except Exception as exc: + if _is_method_not_found_error(exc): + return _studio_transaction_lifecycle_fallback( + self, tx_id, timestamp, exc + ) + raise + error = response.get("error") if isinstance(response, dict) else None + if error is not None: + if _is_method_not_found_error(error): + return _studio_transaction_lifecycle_fallback( + self, tx_id, timestamp, error + ) + raise GenLayerError(f"gen_getTransactionLifecycle failed: {error}") + return _decode_rpc_transaction_lifecycle(response.get("result")) + + consensus_data_contract = self.w3.eth.contract( + address=self.chain.consensus_data_contract["address"], + abi=self.chain.consensus_data_contract["abi"], + ) + block_number = self.w3.eth.block_number + return _read_transaction_lifecycle( + consensus_data_contract, + transaction_hash, + block_number, + timestamp if timestamp is not None else 0, + ) + + def get_transaction( self: GenLayerClient, transaction_hash: _Hash32, @@ -219,27 +687,54 @@ def get_transaction( transaction = self.provider.make_request( method="eth_getTransactionByHash", params=[transaction_hash] )["result"] - localnet_status = ( - TransactionStatus.PENDING + protocol_status = ( + ProtocolTransactionStatus.PENDING if transaction["status"] == "ACTIVATED" else transaction["status"] ) - transaction["status"] = int(TRANSACTION_STATUS_NAME_TO_NUMBER[localnet_status]) - transaction["status_name"] = localnet_status + lifecycle = transaction_lifecycle_from_protocol_status(protocol_status) + if lifecycle["state"] == "finalized": + outcome = transaction_outcome_from_protocol_result( + transaction.get("result_name", transaction.get("result", 0)) + ) + if outcome is not None: + lifecycle["outcome"] = outcome + transaction["lifecycle"] = lifecycle + transaction.pop("status", None) + transaction.pop("status_name", None) return _decode_localnet_transaction(transaction) - # Decode for testnet — call both to get messages + txExecutionResult + # Decode one fixed-block train snapshot. The light record and split array + # reads avoid the oversized aggregate getTransactionAllData response. consensus_data_contract = self.w3.eth.contract( address=self.chain.consensus_data_contract["address"], abi=self.chain.consensus_data_contract["abi"], ) - tx_data = consensus_data_contract.functions.getTransactionData( - transaction_hash, int(time.time()) - ).call() - tx_all_data, rounds_data = consensus_data_contract.functions.getTransactionAllData( - transaction_hash - ).call() - raw_transaction = GenLayerRawTransaction.from_transaction_data(tx_data) - raw_transaction.tx_execution_result = tx_all_data[1] + block_number = self.w3.eth.block_number + ( + tx_data, + validators, + validator_votes, + validator_votes_hash, + validator_result_hash, + consumed_validators, + tx_execution_result, + num_of_initial_validators, + ) = _read_train_transaction_data( + self, + consensus_data_contract, + transaction_hash, + block_number, + ) + raw_transaction = GenLayerRawTransaction.from_transaction_data_light( + tx_data, + validators, + validator_votes, + validator_votes_hash, + validator_result_hash, + consumed_validators, + tx_execution_result, + num_of_initial_validators, + ) decoded_transaction = raw_transaction.decode() decoded_transaction["triggered_transactions"] = _decode_triggered_txs( self, decoded_transaction @@ -250,20 +745,20 @@ def get_transaction( def _decode_triggered_txs( self: GenLayerClient, tx: GenLayerTransaction ) -> List[HexStr]: - status = TRANSACTION_STATUS_NUMBER_TO_NAME[tx["status"]] - if status not in [TransactionStatus.FINALIZED, TransactionStatus.ACCEPTED]: + lifecycle = tx["lifecycle"] + state = lifecycle["state"] + accepted = state == "decided" and lifecycle.get("outcome") == "accepted" + if not accepted and state != "finalized": return [] event_hashes_by_status = { - TransactionStatus.FINALIZED: self.w3.keccak( - text="TransactionFinalized(bytes32)" - ).hex(), - TransactionStatus.ACCEPTED: self.w3.keccak( - text="TransactionAccepted(bytes32)" - ).hex(), + "finalized": self.w3.keccak(text="TransactionFinalized(bytes32)").hex(), + "accepted": self.w3.keccak(text="TransactionAccepted(bytes32)").hex(), } - def process_events_for_status(event_status: TransactionStatus) -> List[HexStr]: + def process_events_for_status( + event_status: Literal["accepted", "finalized"], + ) -> List[HexStr]: """Helper function to process events for a given status.""" event_signature_hash = event_hashes_by_status[event_status] from_block = int(tx["read_state_block_range"]["proposal_block"]) @@ -295,11 +790,11 @@ def process_events_for_status(event_status: TransactionStatus) -> List[HexStr]: triggered_txs = [] # Triggered transactions can happen on ACCEPTED or FINALIZED statuses - if status in [TransactionStatus.ACCEPTED, TransactionStatus.FINALIZED]: - triggered_txs.extend(process_events_for_status(TransactionStatus.ACCEPTED)) + if accepted or state == "finalized": + triggered_txs.extend(process_events_for_status("accepted")) - if status == TransactionStatus.FINALIZED: - triggered_txs.extend(process_events_for_status(TransactionStatus.FINALIZED)) + if state == "finalized": + triggered_txs.extend(process_events_for_status("finalized")) return triggered_txs @@ -323,7 +818,16 @@ def debug_trace_transaction( ) -> Dict[str, Any]: response = self.provider.make_request( method="gen_dbg_traceTransaction", - params=[{"txID": Web3.to_hex(transaction_hash) if isinstance(transaction_hash, bytes) else transaction_hash, "round": round}], + params=[ + { + "txID": ( + Web3.to_hex(transaction_hash) + if isinstance(transaction_hash, bytes) + else transaction_hash + ), + "round": round, + } + ], ) return response.get("result", {}) @@ -333,7 +837,7 @@ def _simplify_transaction_receipt(tx: GenLayerTransaction) -> GenLayerTransactio Simplify transaction receipt by removing non-essential fields while preserving functionality. Removes: Binary data, internal timestamps, appeal fields, processing details, historical data - Preserves: Transaction IDs, status, execution results, node configs, readable data + Preserves: Transaction IDs, lifecycle, execution results, node configs, readable data """ simplified_tx = tx.copy() diff --git a/genlayer_py/transactions/fees.py b/genlayer_py/transactions/fees.py index 5780aad..59037c9 100644 --- a/genlayer_py/transactions/fees.py +++ b/genlayer_py/transactions/fees.py @@ -1081,6 +1081,7 @@ def build_estimated_fees_options_from_simulation( def build_estimated_fees_distribution( options: Optional[FeeEstimateOptions], policy: FeePolicyQuote, + default_consensus_max_rotations: BigNumberish, ) -> FeesDistribution: headroom_bps = to_uint( _get(options, "priceCapHeadroomBps", "price_cap_headroom_bps"), @@ -1119,6 +1120,17 @@ def build_estimated_fees_distribution( if emits_messages else base_execution_budget_default ) + appeal_rounds = to_uint( + _get(options, "appealRounds", "appeal_rounds"), + "appealRounds", + ) + rotations = _get(options, "rotations") + if rotations is None: + default_rotations = to_uint( + default_consensus_max_rotations, + "defaultConsensusMaxRotations", + ) + rotations = [default_rotations] * (appeal_rounds + 1) return create_fees_distribution( { @@ -1138,7 +1150,7 @@ def build_estimated_fees_distribution( if policy["enabled"] else 0, ), - "appealRounds": _get(options, "appealRounds", "appeal_rounds"), + "appealRounds": appeal_rounds, "executionBudgetPerRound": _get( options, "executionBudgetPerRound", @@ -1151,7 +1163,7 @@ def build_estimated_fees_distribution( "execution_consumed", ), "totalMessageFees": total_message_fees, - "rotations": _get(options, "rotations"), + "rotations": rotations, "maxPriceGenPerTimeUnit": _get( options, "maxPriceGenPerTimeUnit", diff --git a/genlayer_py/types/__init__.py b/genlayer_py/types/__init__.py index 04e4a64..f7021f9 100644 --- a/genlayer_py/types/__init__.py +++ b/genlayer_py/types/__init__.py @@ -2,18 +2,27 @@ from .transactions import ( GenLayerTransaction, GenLayerRawTransaction, - TransactionStatus, + TransactionLifecycle, + TransactionProcessingPhase, + TransactionDecisionOutcome, + ProcessingTransactionLifecycle, + DecidedTransactionLifecycle, + FinalizedTransactionLifecycle, + CanceledTransactionLifecycle, TransactionHashVariant, TRANSACTION_RESULT_NAME_TO_NUMBER, TRANSACTION_RESULT_NUMBER_TO_NAME, ExecutionResult, + VoteType, EXECUTION_RESULT_NUMBER_TO_NAME, - TRANSACTION_STATUS_NAME_TO_NUMBER, - TRANSACTION_STATUS_NUMBER_TO_NAME, VOTE_TYPE_NAME_TO_NUMBER, VOTE_TYPE_NUMBER_TO_NAME, - DECIDED_STATES, - is_decided_state, ) -from .chain import Chain, NativeCurrency, ContractInfo, SimpleContractInfo, GenLayerChain +from .chain import ( + Chain, + NativeCurrency, + ContractInfo, + SimpleContractInfo, + GenLayerChain, +) from .contracts import ContractSchema, SimConfig diff --git a/genlayer_py/types/transactions.py b/genlayer_py/types/transactions.py index 43623cc..01e489f 100644 --- a/genlayer_py/types/transactions.py +++ b/genlayer_py/types/transactions.py @@ -3,7 +3,17 @@ import base64 from genlayer_py.abi import calldata from enum import Enum -from typing import Dict, Optional, Any, TypedDict, List, Tuple, Literal, Union +from typing import ( + Dict, + Optional, + Any, + TypedDict, + List, + Tuple, + Literal, + Union, + NotRequired, +) from eth_typing import Address, HexStr from web3 import Web3 from dataclasses import dataclass @@ -11,76 +21,215 @@ from genlayer_py.consensus.consensus_main import decode_tx_data -class TransactionStatus(str, Enum): - """Status of a GenLayer transaction in the consensus lifecycle.""" - UNINITIALIZED = "UNINITIALIZED" - PENDING = "PENDING" - PROPOSING = "PROPOSING" - COMMITTING = "COMMITTING" - REVEALING = "REVEALING" - ACCEPTED = "ACCEPTED" - UNDETERMINED = "UNDETERMINED" - FINALIZED = "FINALIZED" - CANCELED = "CANCELED" - APPEAL_REVEALING = "APPEAL_REVEALING" - APPEAL_COMMITTING = "APPEAL_COMMITTING" - READY_TO_FINALIZE = "READY_TO_FINALIZE" - VALIDATORS_TIMEOUT = "VALIDATORS_TIMEOUT" - LEADER_TIMEOUT = "LEADER_TIMEOUT" - LEADER_REVEALING = "LEADER_REVEALING" - - -TRANSACTION_STATUS_NUMBER_TO_NAME = { - "0": TransactionStatus.UNINITIALIZED, - "1": TransactionStatus.PENDING, - "2": TransactionStatus.PROPOSING, - "3": TransactionStatus.COMMITTING, - "4": TransactionStatus.REVEALING, - "5": TransactionStatus.ACCEPTED, - "6": TransactionStatus.UNDETERMINED, - "7": TransactionStatus.FINALIZED, - "8": TransactionStatus.CANCELED, - "9": TransactionStatus.APPEAL_REVEALING, - "10": TransactionStatus.APPEAL_COMMITTING, - "11": TransactionStatus.READY_TO_FINALIZE, - "12": TransactionStatus.VALIDATORS_TIMEOUT, - "13": TransactionStatus.LEADER_TIMEOUT, - "14": TransactionStatus.LEADER_REVEALING, +class ProtocolTransactionStatus(str, Enum): + """Raw transaction status stored by the consensus contracts. + + This protocol enum is intentionally advanced API. Consumer-facing + transactions expose a small ``lifecycle`` value discriminated by ``state`` + instead. + """ + + UNINITIALIZED = "Uninitialized" + PENDING = "Pending" + PROPOSING = "Proposing" + COMMITTING = "Committing" + REVEALING = "Revealing" + ACCEPTED = "Accepted" + UNDETERMINED = "Undetermined" + FINALIZED = "Finalized" + CANCELED = "Canceled" + APPEAL_REVEALING = "AppealRevealing" + APPEAL_COMMITTING = "AppealCommitting" + VALIDATORS_TIMEOUT = "ValidatorsTimeout" + LEADER_TIMEOUT = "LeaderTimeout" + LEADER_REVEALING = "LeaderRevealing" + + +class ResolutionAction(str, Enum): + """Action projected by the transaction lifecycle resolution kernel.""" + + NO_OP = "NoOp" + CANCEL = "Cancel" + REPLACE_ACTOR = "ReplaceActor" + ROTATE_LEADER = "RotateLeader" + RESOLVE_APPEAL = "ResolveAppeal" + MATERIALIZE_DECISION = "MaterializeDecision" + FINALIZE = "Finalize" + + +class ResolutionSource(str, Enum): + """Protocol trigger that produced a transaction resolution plan.""" + + UNSPECIFIED = "Unspecified" + ACTIVATION_INSUFFICIENT_VALIDATORS = "ActivationInsufficientValidators" + PROPOSAL_HANGING = "ProposalHanging" + LEADER_RECEIPT_TIMEOUT = "LeaderReceiptTimeout" + COMMIT_HANGING = "CommitHanging" + LEADER_REVEAL_HANGING = "LeaderRevealHanging" + FULL_REVEAL = "FullReveal" + REVEAL_DEADLINE = "RevealDeadline" + APPEAL_COMMIT_HANGING = "AppealCommitHanging" + APPEAL_FULL_REVEAL = "AppealFullReveal" + APPEAL_REVEAL_DEADLINE = "AppealRevealDeadline" + SELECTION_DEPLETED = "SelectionDepleted" + + +RESOLUTION_ACTION_NUMBER_TO_NAME = { + "0": ResolutionAction.NO_OP, + "1": ResolutionAction.CANCEL, + "2": ResolutionAction.REPLACE_ACTOR, + "3": ResolutionAction.ROTATE_LEADER, + "4": ResolutionAction.RESOLVE_APPEAL, + "5": ResolutionAction.MATERIALIZE_DECISION, + "6": ResolutionAction.FINALIZE, } -TRANSACTION_STATUS_NAME_TO_NUMBER = { - TransactionStatus.UNINITIALIZED: "0", - TransactionStatus.PENDING: "1", - TransactionStatus.PROPOSING: "2", - TransactionStatus.COMMITTING: "3", - TransactionStatus.REVEALING: "4", - TransactionStatus.ACCEPTED: "5", - TransactionStatus.UNDETERMINED: "6", - TransactionStatus.FINALIZED: "7", - TransactionStatus.CANCELED: "8", - TransactionStatus.APPEAL_REVEALING: "9", - TransactionStatus.APPEAL_COMMITTING: "10", - TransactionStatus.READY_TO_FINALIZE: "11", - TransactionStatus.VALIDATORS_TIMEOUT: "12", - TransactionStatus.LEADER_TIMEOUT: "13", - TransactionStatus.LEADER_REVEALING: "14", +RESOLUTION_SOURCE_NUMBER_TO_NAME = { + str(number): source for number, source in enumerate(ResolutionSource) } -DECIDED_STATES = [ - TransactionStatus.ACCEPTED, - TransactionStatus.UNDETERMINED, - TransactionStatus.LEADER_TIMEOUT, - TransactionStatus.VALIDATORS_TIMEOUT, - TransactionStatus.CANCELED, - TransactionStatus.FINALIZED + +TransactionProcessingPhase = Literal[ + "uninitialized", + "pending", + "proposing", + "committing", + "revealing", + "appeal_revealing", + "appeal_committing", + "leader_revealing", +] +TransactionDecisionOutcome = Literal[ + "accepted", "undetermined", "validators_timeout", "leader_timeout" ] -def is_decided_state(status: str) -> bool: - return status in [TRANSACTION_STATUS_NAME_TO_NUMBER[state] for state in DECIDED_STATES] + +class ProcessingTransactionLifecycle(TypedDict): + state: Literal["processing"] + phase: TransactionProcessingPhase + + +class DecidedTransactionLifecycle(TypedDict): + state: Literal["decided"] + outcome: TransactionDecisionOutcome + + +class FinalizedTransactionLifecycle(TypedDict): + state: Literal["finalized"] + outcome: NotRequired[TransactionDecisionOutcome] + + +class CanceledTransactionLifecycle(TypedDict): + state: Literal["canceled"] + + +TransactionLifecycle = Union[ + ProcessingTransactionLifecycle, + DecidedTransactionLifecycle, + FinalizedTransactionLifecycle, + CanceledTransactionLifecycle, +] + + +class ProtocolTransactionLifecycle(TypedDict): + """Advanced resolution-kernel view for one fixed block snapshot.""" + + stored_status: int + stored_status_name: ProtocolTransactionStatus + projected_status: int + projected_status_name: ProtocolTransactionStatus + resolution_action: int + resolution_action_name: ResolutionAction + resolution_source: int + resolution_source_name: ResolutionSource + decision_id: Optional[str] + decision_active: bool + evaluated_at: int + + +# Current train protocol ordinals. Finalization readiness is a resolution +# verdict, not a stored or projected transaction status. +PROTOCOL_TRANSACTION_STATUS_NUMBER_TO_NAME = { + "0": ProtocolTransactionStatus.UNINITIALIZED, + "1": ProtocolTransactionStatus.PENDING, + "2": ProtocolTransactionStatus.PROPOSING, + "3": ProtocolTransactionStatus.COMMITTING, + "4": ProtocolTransactionStatus.REVEALING, + "5": ProtocolTransactionStatus.ACCEPTED, + "6": ProtocolTransactionStatus.UNDETERMINED, + "7": ProtocolTransactionStatus.FINALIZED, + "8": ProtocolTransactionStatus.CANCELED, + "9": ProtocolTransactionStatus.APPEAL_REVEALING, + "10": ProtocolTransactionStatus.APPEAL_COMMITTING, + "11": ProtocolTransactionStatus.VALIDATORS_TIMEOUT, + "12": ProtocolTransactionStatus.LEADER_TIMEOUT, + "13": ProtocolTransactionStatus.LEADER_REVEALING, +} + +PROTOCOL_TRANSACTION_STATUS_NAME_TO_NUMBER = { + status: str(number) for number, status in enumerate(ProtocolTransactionStatus) +} + +_PROCESSING_PHASE_BY_PROTOCOL_STATUS: Dict[ + ProtocolTransactionStatus, TransactionProcessingPhase +] = { + ProtocolTransactionStatus.UNINITIALIZED: "uninitialized", + ProtocolTransactionStatus.PENDING: "pending", + ProtocolTransactionStatus.PROPOSING: "proposing", + ProtocolTransactionStatus.COMMITTING: "committing", + ProtocolTransactionStatus.REVEALING: "revealing", + ProtocolTransactionStatus.APPEAL_REVEALING: "appeal_revealing", + ProtocolTransactionStatus.APPEAL_COMMITTING: "appeal_committing", + ProtocolTransactionStatus.LEADER_REVEALING: "leader_revealing", +} + + +def transaction_lifecycle_from_protocol_status( + status: Union[int, str, ProtocolTransactionStatus], +) -> TransactionLifecycle: + """Map every stored protocol status to the stable consumer lifecycle.""" + + if isinstance(status, ProtocolTransactionStatus): + protocol_status = status + elif isinstance(status, int) or (isinstance(status, str) and status.isdigit()): + try: + protocol_status = PROTOCOL_TRANSACTION_STATUS_NUMBER_TO_NAME[str(status)] + except KeyError as exc: + raise ValueError(f"Unknown protocol transaction status: {status}") from exc + else: + try: + status_text = str(status) + protocol_status = ( + ProtocolTransactionStatus[status_text] + if status_text in ProtocolTransactionStatus.__members__ + else ProtocolTransactionStatus(status_text) + ) + except ValueError as exc: + raise ValueError(f"Unknown protocol transaction status: {status}") from exc + + if protocol_status in _PROCESSING_PHASE_BY_PROTOCOL_STATUS: + return { + "state": "processing", + "phase": _PROCESSING_PHASE_BY_PROTOCOL_STATUS[protocol_status], + } + if protocol_status == ProtocolTransactionStatus.ACCEPTED: + return {"state": "decided", "outcome": "accepted"} + if protocol_status == ProtocolTransactionStatus.UNDETERMINED: + return {"state": "decided", "outcome": "undetermined"} + if protocol_status == ProtocolTransactionStatus.VALIDATORS_TIMEOUT: + return {"state": "decided", "outcome": "validators_timeout"} + if protocol_status == ProtocolTransactionStatus.LEADER_TIMEOUT: + return {"state": "decided", "outcome": "leader_timeout"} + if protocol_status == ProtocolTransactionStatus.FINALIZED: + return {"state": "finalized"} + if protocol_status == ProtocolTransactionStatus.CANCELED: + return {"state": "canceled"} + raise AssertionError(f"Unmapped protocol transaction status: {protocol_status}") class TransactionResult(str, Enum): """Consensus voting result across validators.""" + IDLE = "IDLE" AGREE = "AGREE" DISAGREE = "DISAGREE" @@ -112,13 +261,43 @@ class TransactionResult(str, Enum): } +def transaction_outcome_from_protocol_result( + result: Union[int, str, TransactionResult], +) -> Optional[TransactionDecisionOutcome]: + """Return an application outcome when a finalized record preserves one.""" + + if isinstance(result, TransactionResult): + result_name = result + elif isinstance(result, int) or (isinstance(result, str) and result.isdigit()): + result_name = TRANSACTION_RESULT_NUMBER_TO_NAME.get(str(result)) + else: + try: + result_name = TransactionResult(str(result)) + except ValueError: + result_name = None + + if result_name == TransactionResult.MAJORITY_AGREE: + return "accepted" + if result_name == TransactionResult.MAJORITY_TIMEOUT: + return "validators_timeout" + if result_name in ( + TransactionResult.MAJORITY_DISAGREE, + TransactionResult.DETERMINISTIC_VIOLATION, + TransactionResult.NO_MAJORITY, + ): + return "undetermined" + return None + + class ExecutionResult(str, Enum): """Result of contract execution by the GenVM.""" + NOT_VOTED = "NOT_VOTED" FINISHED_WITH_RETURN = "FINISHED_WITH_RETURN" FINISHED_WITH_ERROR = "FINISHED_WITH_ERROR" TIMEOUT = "TIMEOUT" NONDET_DISAGREE = "NONDET_DISAGREE" + DETERMINISTIC_VIOLATION = "DETERMINISTIC_VIOLATION" EXECUTION_RESULT_NUMBER_TO_NAME = { @@ -127,31 +306,37 @@ class ExecutionResult(str, Enum): "2": ExecutionResult.FINISHED_WITH_ERROR, "3": ExecutionResult.TIMEOUT, "4": ExecutionResult.NONDET_DISAGREE, + "5": ExecutionResult.DETERMINISTIC_VIOLATION, } class VoteType(str, Enum): + """Validator execution vote recorded for a consensus round.""" + NOT_VOTED = "NOT_VOTED" - AGREE = "AGREE" - DISAGREE = "DISAGREE" + FINISHED_WITH_RETURN = "FINISHED_WITH_RETURN" + FINISHED_WITH_ERROR = "FINISHED_WITH_ERROR" TIMEOUT = "TIMEOUT" + NONDET_DISAGREE = "NONDET_DISAGREE" DETERMINISTIC_VIOLATION = "DETERMINISTIC_VIOLATION" VOTE_TYPE_NUMBER_TO_NAME = { "0": VoteType.NOT_VOTED, - "1": VoteType.AGREE, - "2": VoteType.DISAGREE, + "1": VoteType.FINISHED_WITH_RETURN, + "2": VoteType.FINISHED_WITH_ERROR, "3": VoteType.TIMEOUT, - "4": VoteType.DETERMINISTIC_VIOLATION, + "4": VoteType.NONDET_DISAGREE, + "5": VoteType.DETERMINISTIC_VIOLATION, } VOTE_TYPE_NAME_TO_NUMBER = { VoteType.NOT_VOTED: "0", - VoteType.AGREE: "1", - VoteType.DISAGREE: "2", + VoteType.FINISHED_WITH_RETURN: "1", + VoteType.FINISHED_WITH_ERROR: "2", VoteType.TIMEOUT: "3", - VoteType.DETERMINISTIC_VIOLATION: "4", + VoteType.NONDET_DISAGREE: "4", + VoteType.DETERMINISTIC_VIOLATION: "5", } @@ -179,6 +364,7 @@ class DecodedCallData(TypedDict, total=False): class GenLayerTransaction(TypedDict, total=False): """Decoded transaction data returned by get_transaction and wait_for_transaction_receipt.""" + # currentTimestamp: testnet current_timestamp: Optional[str] @@ -192,6 +378,7 @@ class GenLayerTransaction(TypedDict, total=False): # numOfInitialValidators: testnet num_of_initial_validators: Optional[str] + initial_rotations: Optional[str] # txSlot: testnet tx_slot: Optional[str] @@ -218,12 +405,17 @@ class GenLayerTransaction(TypedDict, total=False): tx_data: Optional[HexStr] tx_data_decoded: Optional[Dict[str, Any]] - # txReceipt: testnet + # The train stores only the execution hash. `tx_receipt` remains present so + # callers can distinguish unavailable legacy bytes (`None`) explicitly. + tx_execution_hash: Optional[HexStr] tx_receipt: Optional[HexStr] # messages: testnet messages: Optional[List[Any]] + # consumedValidators: testnet + consumed_validators: Optional[List[Address]] + # queueType: testnet queue_type: Optional[int] @@ -236,9 +428,9 @@ class GenLayerTransaction(TypedDict, total=False): # lastLeader: testnet last_leader: Optional[Address] - # status: localnet: TransactionStatus // status: testnet: number - status: Optional[TransactionStatus] - status_name: Optional[TransactionStatus] + # Stable consumer lifecycle. Raw protocol values are returned only by + # GenLayerClient.get_transaction_lifecycle(). + lifecycle: TransactionLifecycle # hash: localnet // txId: testnet// hash: localnet // txId: testnet hash: Optional[HexStr] @@ -301,11 +493,17 @@ class LastRound: result: int round_validators: List[Address] validator_votes_hash: List[HexStr] + validator_result_hash: List[HexStr] validator_votes: List[int] @classmethod - def from_transaction_data( - cls, tx_data: Tuple + def from_light_data( + cls, + tx_data: Tuple, + round_validators: List[Address], + validator_votes: List[int], + validator_votes_hash: List[HexStr], + validator_result_hash: List[HexStr], ) -> "GenLayerRawTransaction.LastRound": return cls( round=tx_data[0], @@ -315,29 +513,13 @@ def from_transaction_data( appeal_bond=tx_data[4], rotations_left=tx_data[5], result=tx_data[6], - round_validators=tx_data[7], - validator_votes=tx_data[8], + round_validators=round_validators, + validator_votes=validator_votes, validator_votes_hash=[ - Web3.to_hex(vote_hash) for vote_hash in tx_data[9] + Web3.to_hex(vote_hash) for vote_hash in validator_votes_hash ], - ) - - @classmethod - def from_all_data_round( - cls, tx_data: Tuple - ) -> "GenLayerRawTransaction.LastRound": - return cls( - round=tx_data[0], - leader_index=tx_data[1], - votes_committed=tx_data[2], - votes_revealed=tx_data[3], - appeal_bond=tx_data[4], - rotations_left=tx_data[5], - result=tx_data[6], - round_validators=tx_data[7], - validator_votes=tx_data[8], - validator_votes_hash=[ - Web3.to_hex(vote_hash) for vote_hash in tx_data[9] + validator_result_hash=[ + Web3.to_hex(result_hash) for result_hash in validator_result_hash ], ) @@ -352,6 +534,7 @@ def decode(self) -> Dict[str, Any]: "result": str(self.result), "round_validators": self.round_validators, "validator_votes_hash": self.validator_votes_hash, + "validator_result_hash": self.validator_result_hash, "validator_votes": self.validator_votes, "validator_votes_name": [ VOTE_TYPE_NUMBER_TO_NAME[str(vote)].value @@ -363,6 +546,7 @@ def decode(self) -> Dict[str, Any]: sender: Address recipient: Address num_of_initial_validators: int + initial_rotations: int tx_slot: int created_timestamp: int last_vote_timestamp: int @@ -370,8 +554,10 @@ def decode(self) -> Dict[str, Any]: result: int tx_execution_result: int tx_data: HexStr - tx_receipt: HexStr + tx_execution_hash: HexStr + tx_receipt: Optional[HexStr] messages: List[Any] + consumed_validators: List[Address] queue_type: int queue_position: int activator: Address @@ -383,65 +569,35 @@ def decode(self) -> Dict[str, Any]: last_round: LastRound @classmethod - def from_transaction_data(cls, tx_data: Tuple) -> "GenLayerRawTransaction": - # V06/Bradbury ABI returns 23 fields (extra txExecutionHash, eqBlocksOutputs, - # consumedValidators; txData split into txCalldata). Asimov returns 21 fields. - if len(tx_data) >= 23: - return cls._from_v06(tx_data) - return cls._from_v04(tx_data) - - @classmethod - def _from_v04(cls, tx_data: Tuple) -> "GenLayerRawTransaction": - """Asimov / pre-Bradbury ABI (21 fields).""" - return cls( - current_timestamp=tx_data[0], - sender=tx_data[1], - recipient=tx_data[2], - num_of_initial_validators=tx_data[3], - tx_slot=tx_data[4], - created_timestamp=tx_data[5], - last_vote_timestamp=tx_data[6], - random_seed=Web3.to_hex(tx_data[7]), - result=tx_data[8], - tx_execution_result=0, - tx_data=Web3.to_hex(tx_data[9]), - tx_receipt=Web3.to_hex(tx_data[10]), - messages=tx_data[11], - queue_type=tx_data[12], - queue_position=tx_data[13], - activator=tx_data[14], - last_leader=tx_data[15], - status=tx_data[16], - tx_id=Web3.to_hex(tx_data[17]), - read_state_block_range=cls.ReadStateBlockRange.from_transaction_data( - tx_data[18] - ), - num_of_rounds=tx_data[19], - last_round=cls.LastRound.from_transaction_data(tx_data[20]), - ) - - @classmethod - def _from_v06(cls, tx_data: Tuple) -> "GenLayerRawTransaction": - """Bradbury / V06 ABI (23 fields). Fields differ at positions 9-12.""" - # [9] txExecutionHash (bytes32) — not in v04 - # [10] txCalldata (bytes) — equivalent to v04's txData - # [11] eqBlocksOutputs (bytes) — not in v04 - # [12+] messages and rest shifted by +1 vs v04 - # [22] consumedValidators — not in v04 + def from_transaction_data_light( + cls, + tx_data: Tuple, + round_validators: List[Address], + validator_votes: List[int], + validator_votes_hash: List[HexStr], + validator_result_hash: List[HexStr], + consumed_validators: List[Address], + tx_execution_result: int, + num_of_initial_validators: int, + ) -> "GenLayerRawTransaction": + """Parse the bounded train transaction view and separately read arrays.""" return cls( current_timestamp=tx_data[0], sender=tx_data[1], recipient=tx_data[2], - num_of_initial_validators=tx_data[3], # initialRotations in ABI + num_of_initial_validators=num_of_initial_validators, + initial_rotations=tx_data[3], tx_slot=tx_data[4], created_timestamp=tx_data[5], last_vote_timestamp=tx_data[6], random_seed=Web3.to_hex(tx_data[7]), result=tx_data[8], - tx_execution_result=0, - tx_data=Web3.to_hex(tx_data[10]), # txCalldata - tx_receipt="0x", # not present in V06; txExecutionHash is at [9] + tx_execution_result=tx_execution_result, + tx_data=Web3.to_hex(tx_data[10]), + tx_execution_hash=Web3.to_hex(tx_data[9]), + tx_receipt=None, messages=tx_data[12], + consumed_validators=consumed_validators, queue_type=tx_data[13], queue_position=tx_data[14], activator=tx_data[15], @@ -452,72 +608,50 @@ def _from_v06(cls, tx_data: Tuple) -> "GenLayerRawTransaction": tx_data[19] ), num_of_rounds=tx_data[20], - last_round=cls.LastRound.from_transaction_data(tx_data[21]), - ) - - @classmethod - def from_all_transaction_data(cls, tx_data: Tuple, rounds_data: List[Tuple]) -> "GenLayerRawTransaction": - """Parse getTransactionAllData response which returns (transaction, roundsData[]).""" - last_round_data = rounds_data[-1] if rounds_data else None - latest_block_range = tx_data[18][-1] if tx_data[18] else (0, 0, 0) - - return cls( - current_timestamp=0, - sender=tx_data[5], - recipient=tx_data[6], - num_of_initial_validators=tx_data[9], - tx_slot=tx_data[8], - created_timestamp=0, - last_vote_timestamp=0, - random_seed=Web3.to_hex(tx_data[13]), - result=tx_data[0], - tx_execution_result=tx_data[1], - tx_data=Web3.to_hex(tx_data[16]), - tx_receipt="0x", - messages=[], - queue_type=0, - queue_position=0, - activator=tx_data[7], - last_leader=tx_data[7], - status=tx_data[3], - tx_id=Web3.to_hex(tx_data[12]), - read_state_block_range=cls.ReadStateBlockRange.from_transaction_data(latest_block_range), - num_of_rounds=len(rounds_data), - last_round=cls.LastRound.from_all_data_round(last_round_data) if last_round_data else cls.LastRound( - round=0, leader_index=0, votes_committed=0, votes_revealed=0, - appeal_bond=0, rotations_left=0, result=0, round_validators=[], - validator_votes_hash=[], validator_votes=[], + last_round=cls.LastRound.from_light_data( + tx_data[21], + round_validators, + validator_votes, + validator_votes_hash, + validator_result_hash, ), ) def decode(self) -> GenLayerTransaction: + lifecycle = transaction_lifecycle_from_protocol_status(self.status) + if lifecycle["state"] == "finalized": + outcome = transaction_outcome_from_protocol_result(self.result) + if outcome is not None: + lifecycle["outcome"] = outcome return { "current_timestamp": str(self.current_timestamp), "sender": self.sender, "recipient": self.recipient, "num_of_initial_validators": str(self.num_of_initial_validators), + "initial_rotations": str(self.initial_rotations), "tx_slot": str(self.tx_slot), "created_timestamp": str(self.created_timestamp), "last_vote_timestamp": str(self.last_vote_timestamp), "random_seed": self.random_seed, "result": str(self.result), "tx_data": self.tx_data, + "tx_execution_hash": self.tx_execution_hash, "tx_receipt": self.tx_receipt, "consensus_data": { "leader_receipt": self._decode_leader_receipt(), }, "messages": self.messages, + "consumed_validators": self.consumed_validators, "queue_type": str(self.queue_type), "queue_position": str(self.queue_position), "activator": self.activator, "last_leader": self.last_leader, - "status": str(self.status), + "lifecycle": lifecycle, "tx_id": self.tx_id, "read_state_block_range": self.read_state_block_range.decode(), "num_of_rounds": str(self.num_of_rounds), "last_round": self.last_round.decode(), "tx_data_decoded": self._decode_input_data(), - "status_name": TRANSACTION_STATUS_NUMBER_TO_NAME[str(self.status)].value, "result_name": TRANSACTION_RESULT_NUMBER_TO_NAME[str(self.result)].value, "tx_execution_result": self.tx_execution_result, "tx_execution_result_name": EXECUTION_RESULT_NUMBER_TO_NAME.get( diff --git a/scripts/generate-api-docs.py b/scripts/generate-api-docs.py index 7010529..4fa0d7d 100644 --- a/scripts/generate-api-docs.py +++ b/scripts/generate-api-docs.py @@ -14,9 +14,17 @@ def clean_readme(content): lines = content.split("\n") - lines = [l for l in lines if not re.match(r'^\[!\[.*\]\(https://(img\.shields\.io|dcbadge|badge\.fury)', l)] + lines = [ + l + for l in lines + if not re.match( + r"^\[!\[.*\]\(https://(img\.shields\.io|dcbadge|badge\.fury)", l + ) + ] content = "\n".join(lines) - content = re.sub(r'(## )\S*[\U0001F000-\U0001FFFF\u2600-\u27BF\u200d]+\s*', r'\1', content) + content = re.sub( + r"(## )\S*[\U0001F000-\U0001FFFF\u2600-\u27BF\u200d]+\s*", r"\1", content + ) return content @@ -26,7 +34,12 @@ def format_type(annotation): name = getattr(annotation, "__name__", None) if name: return name - return str(annotation).replace("typing.", "").replace("ForwardRef('", "").replace("')", "") + return ( + str(annotation) + .replace("typing.", "") + .replace("ForwardRef('", "") + .replace("')", "") + ) def generate_method_doc(name, method): @@ -63,7 +76,9 @@ def generate_method_doc(name, method): req_str = "yes" if required else "no" type_display = f"`{type_str}`" if type_str else "" default_display = default.lstrip(" = ") if default else "" - lines.append(f"| {pname} | {type_display} | {req_str} | {default_display} |") + lines.append( + f"| {pname} | {type_display} | {req_str} | {default_display} |" + ) lines.append("") if ret: @@ -86,7 +101,9 @@ def generate_enum_doc(name, enum_class): def main(): from genlayer_py.client.genlayer_client import GenLayerClient from genlayer_py.types.transactions import ( - TransactionStatus, TransactionResult, ExecutionResult, VoteType, + ExecutionResult, + TransactionResult, + VoteType, ) output_dir = os.path.join(os.path.dirname(__file__), "..", "docs", "api-references") @@ -103,11 +120,29 @@ def main(): lines.append(f"{client_doc}\n") public_methods = [ - "fund_account", "get_current_nonce", "initialize_consensus_smart_contract", - "read_contract", "write_contract", "simulate_write_contract", "deploy_contract", - "get_contract_schema", "get_contract_schema_for_code", "appeal_transaction", - "wait_for_transaction_receipt", "get_transaction", - "get_triggered_transaction_ids", "debug_trace_transaction", + "fund_account", + "get_current_nonce", + "initialize_consensus_smart_contract", + "read_contract", + "write_contract", + "simulate_write_contract", + "deploy_contract", + "get_contract_schema", + "get_contract_schema_for_code", + "appeal_transaction", + "top_up_fees", + "top_up_and_submit_appeal", + "can_appeal", + "get_appeal_quote", + "get_appeal_charge", + "get_min_appeal_bond", + "wait_for_decision", + "wait_for_finalization", + "wait_for_transaction_receipt", + "get_transaction", + "get_transaction_lifecycle", + "get_triggered_transaction_ids", + "debug_trace_transaction", ] for name in public_methods: method = getattr(GenLayerClient, name, None) @@ -117,7 +152,6 @@ def main(): # Enums lines.append("## Types and Enums\n") for name, enum_class in [ - ("TransactionStatus", TransactionStatus), ("TransactionResult", TransactionResult), ("ExecutionResult", ExecutionResult), ("VoteType", VoteType), diff --git a/tests/e2e/tests/test_custom_validators.py b/tests/e2e/tests/test_custom_validators.py index 3754a37..6641c2d 100644 --- a/tests/e2e/tests/test_custom_validators.py +++ b/tests/e2e/tests/test_custom_validators.py @@ -2,7 +2,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded diff --git a/tests/e2e/tests/test_football_prediction_market.py b/tests/e2e/tests/test_football_prediction_market.py index f04f6ac..68200f5 100644 --- a/tests/e2e/tests/test_football_prediction_market.py +++ b/tests/e2e/tests/test_football_prediction_market.py @@ -4,7 +4,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded # Load environment variables from .env file @@ -67,7 +66,7 @@ def test_football_prediction_market(chain_config): # Wait for transaction with retries if specified wait_kwargs = { "transaction_hash": deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: wait_kwargs["retries"] = chain_config["retries"] @@ -92,7 +91,7 @@ def test_football_prediction_market(chain_config): # Wait for resolve transaction with retries if specified resolve_wait_kwargs = { "transaction_hash": resolve_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: resolve_wait_kwargs["retries"] = chain_config["retries"] diff --git a/tests/e2e/tests/test_intelligent_oracle_factory.py b/tests/e2e/tests/test_intelligent_oracle_factory.py index 441cbbe..0be6c6d 100644 --- a/tests/e2e/tests/test_intelligent_oracle_factory.py +++ b/tests/e2e/tests/test_intelligent_oracle_factory.py @@ -5,7 +5,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded # Load environment variables from .env file @@ -86,7 +85,7 @@ def test_intelligent_oracle_factory_pattern(chain_config): # Wait for registry deployment registry_wait_kwargs = { "transaction_hash": registry_deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", "retries": 80, } @@ -150,7 +149,7 @@ def test_intelligent_oracle_factory_pattern(chain_config): # Wait for create_new_prediction_market transaction create_wait_kwargs = { "transaction_hash": create_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", "retries": 80, } @@ -211,7 +210,7 @@ def test_intelligent_oracle_factory_pattern(chain_config): # Wait for resolve transaction resolve_wait_kwargs = { "transaction_hash": resolve_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", "retries": 80, } diff --git a/tests/e2e/tests/test_llm_erc20.py b/tests/e2e/tests/test_llm_erc20.py index 31b737d..d5598ac 100644 --- a/tests/e2e/tests/test_llm_erc20.py +++ b/tests/e2e/tests/test_llm_erc20.py @@ -4,7 +4,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded # Load environment variables from .env file @@ -79,7 +78,7 @@ def test_llm_erc20_interaction(chain_config): # Wait for transaction with retries if specified wait_kwargs = { "transaction_hash": deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: wait_kwargs["retries"] = chain_config["retries"] @@ -111,7 +110,7 @@ def test_llm_erc20_interaction(chain_config): # Wait for transfer transaction with retries if specified transfer_wait_kwargs = { "transaction_hash": transfer_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: transfer_wait_kwargs["retries"] = chain_config["retries"] diff --git a/tests/e2e/tests/test_log_indexer.py b/tests/e2e/tests/test_log_indexer.py index 8510202..9dae1ff 100644 --- a/tests/e2e/tests/test_log_indexer.py +++ b/tests/e2e/tests/test_log_indexer.py @@ -4,7 +4,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded # Load environment variables from .env file @@ -69,7 +68,7 @@ def test_log_indexer(chain_config): # Wait for transaction with retries if specified wait_kwargs = { "transaction_hash": deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: wait_kwargs["retries"] = chain_config["retries"] @@ -102,7 +101,7 @@ def test_log_indexer(chain_config): # Wait for add_log transaction add_log_0_wait_kwargs = { "transaction_hash": add_log_0_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: add_log_0_wait_kwargs["retries"] = chain_config["retries"] @@ -130,7 +129,7 @@ def test_log_indexer(chain_config): # Wait for add_log transaction add_log_1_wait_kwargs = { "transaction_hash": add_log_1_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: add_log_1_wait_kwargs["retries"] = chain_config["retries"] @@ -157,7 +156,7 @@ def test_log_indexer(chain_config): # Wait for update_log transaction update_log_0_wait_kwargs = { "transaction_hash": update_log_0_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: update_log_0_wait_kwargs["retries"] = chain_config["retries"] @@ -187,7 +186,7 @@ def test_log_indexer(chain_config): # Wait for remove_log transaction remove_log_0_wait_kwargs = { "transaction_hash": remove_log_0_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: remove_log_0_wait_kwargs["retries"] = chain_config["retries"] @@ -216,7 +215,7 @@ def test_log_indexer(chain_config): # Wait for add_log transaction add_log_2_wait_kwargs = { "transaction_hash": add_log_2_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: add_log_2_wait_kwargs["retries"] = chain_config["retries"] diff --git a/tests/e2e/tests/test_multi_file_contract.py b/tests/e2e/tests/test_multi_file_contract.py index f060703..3c34074 100644 --- a/tests/e2e/tests/test_multi_file_contract.py +++ b/tests/e2e/tests/test_multi_file_contract.py @@ -7,7 +7,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded # Load environment variables from .env file @@ -106,7 +105,7 @@ def test_multi_file_contract(chain_config): # Wait for transaction with retries if specified wait_kwargs = { "transaction_hash": deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: wait_kwargs["retries"] = chain_config["retries"] @@ -130,7 +129,7 @@ def test_multi_file_contract(chain_config): # Wait for wait transaction wait_wait_kwargs = { "transaction_hash": wait_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: wait_wait_kwargs["retries"] = chain_config["retries"] diff --git a/tests/e2e/tests/test_multi_read_erc20.py b/tests/e2e/tests/test_multi_read_erc20.py index 472a245..1a3ea2c 100644 --- a/tests/e2e/tests/test_multi_read_erc20.py +++ b/tests/e2e/tests/test_multi_read_erc20.py @@ -4,7 +4,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded # Load environment variables from .env file @@ -94,7 +93,7 @@ def test_multi_read_erc20(chain_config): # Wait for doge deployment doge_wait_kwargs = { "transaction_hash": doge_deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: doge_wait_kwargs["retries"] = chain_config["retries"] @@ -115,7 +114,7 @@ def test_multi_read_erc20(chain_config): # Wait for shiba deployment shiba_wait_kwargs = { "transaction_hash": shiba_deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: shiba_wait_kwargs["retries"] = chain_config["retries"] @@ -138,7 +137,7 @@ def test_multi_read_erc20(chain_config): # Wait for multi-read deployment multi_read_wait_kwargs = { "transaction_hash": multi_read_deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: multi_read_wait_kwargs["retries"] = chain_config["retries"] @@ -166,7 +165,7 @@ def test_multi_read_erc20(chain_config): # Wait for update_token_balances transaction update_balances_doge_wait_kwargs = { "transaction_hash": update_balances_doge_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: update_balances_doge_wait_kwargs["retries"] = chain_config["retries"] @@ -201,7 +200,7 @@ def test_multi_read_erc20(chain_config): # Wait for update_token_balances transaction update_balances_shiba_wait_kwargs = { "transaction_hash": update_balances_shiba_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: update_balances_shiba_wait_kwargs["retries"] = chain_config["retries"] diff --git a/tests/e2e/tests/test_multi_tenant_storage.py b/tests/e2e/tests/test_multi_tenant_storage.py index 0353749..18e33d8 100644 --- a/tests/e2e/tests/test_multi_tenant_storage.py +++ b/tests/e2e/tests/test_multi_tenant_storage.py @@ -4,7 +4,7 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus, GenLayerTransaction +from genlayer_py.types import GenLayerTransaction from genlayer_py.client import GenLayerClient from genlayer_py.assertions import tx_execution_succeeded @@ -24,7 +24,7 @@ def wait_for_triggered_transactions( for triggered_transaction in tx_receipt["triggered_transactions"]: client.wait_for_transaction_receipt( transaction_hash=triggered_transaction, - status=TransactionStatus.FINALIZED, + wait_until="finalized", ) @@ -110,7 +110,7 @@ def test_multi_tenant_storage(chain_config): # Wait for first storage deployment first_storage_wait_kwargs = { "transaction_hash": first_storage_deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: first_storage_wait_kwargs["retries"] = chain_config["retries"] @@ -133,7 +133,7 @@ def test_multi_tenant_storage(chain_config): # Wait for second storage deployment second_storage_wait_kwargs = { "transaction_hash": second_storage_deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: second_storage_wait_kwargs["retries"] = chain_config["retries"] @@ -158,7 +158,7 @@ def test_multi_tenant_storage(chain_config): # Wait for multi-tenant storage deployment multi_tenant_storage_wait_kwargs = { "transaction_hash": multi_tenant_storage_deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: multi_tenant_storage_wait_kwargs["retries"] = chain_config["retries"] @@ -185,7 +185,7 @@ def test_multi_tenant_storage(chain_config): # Wait for update_storage transaction update_storage_a_wait_kwargs = { "transaction_hash": update_storage_a_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: update_storage_a_wait_kwargs["retries"] = chain_config["retries"] @@ -207,7 +207,7 @@ def test_multi_tenant_storage(chain_config): # Wait for update_storage transaction update_storage_b_wait_kwargs = { "transaction_hash": update_storage_b_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: update_storage_b_wait_kwargs["retries"] = chain_config["retries"] diff --git a/tests/e2e/tests/test_read_erc20.py b/tests/e2e/tests/test_read_erc20.py index 1b6f49c..ea6172e 100644 --- a/tests/e2e/tests/test_read_erc20.py +++ b/tests/e2e/tests/test_read_erc20.py @@ -4,7 +4,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded # Load environment variables from .env file @@ -82,7 +81,7 @@ def test_read_erc20(chain_config): # Wait for LLM ERC20 deployment llm_erc20_wait_kwargs = { "transaction_hash": llm_erc20_deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: llm_erc20_wait_kwargs["retries"] = chain_config["retries"] @@ -111,7 +110,7 @@ def test_read_erc20(chain_config): # Wait for read_erc20 deployment read_erc20_wait_kwargs = { "transaction_hash": read_erc20_deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: read_erc20_wait_kwargs["retries"] = chain_config["retries"] diff --git a/tests/e2e/tests/test_storage.py b/tests/e2e/tests/test_storage.py index ad108a6..7f4f15a 100644 --- a/tests/e2e/tests/test_storage.py +++ b/tests/e2e/tests/test_storage.py @@ -4,7 +4,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded # Load environment variables from .env file @@ -67,7 +66,7 @@ def test_storage_interaction(chain_config): # Wait for transaction with retries if specified wait_kwargs = { "transaction_hash": deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: wait_kwargs["retries"] = chain_config["retries"] @@ -99,7 +98,7 @@ def test_storage_interaction(chain_config): # Wait for write transaction with retries if specified write_wait_kwargs = { "transaction_hash": write_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: write_wait_kwargs["retries"] = chain_config["retries"] diff --git a/tests/e2e/tests/test_user_storage.py b/tests/e2e/tests/test_user_storage.py index 5c12865..3999d10 100644 --- a/tests/e2e/tests/test_user_storage.py +++ b/tests/e2e/tests/test_user_storage.py @@ -4,7 +4,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded # Load environment variables from .env file @@ -79,7 +78,7 @@ def test_user_storage(chain_config): # Wait for transaction with retries if specified wait_kwargs = { "transaction_hash": deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: wait_kwargs["retries"] = chain_config["retries"] @@ -111,7 +110,7 @@ def test_user_storage(chain_config): # Wait for update_storage transaction update_storage_a_wait_kwargs = { "transaction_hash": update_storage_a_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: update_storage_a_wait_kwargs["retries"] = chain_config["retries"] @@ -146,7 +145,7 @@ def test_user_storage(chain_config): # Wait for update_storage transaction update_storage_b_wait_kwargs = { "transaction_hash": update_storage_b_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: update_storage_b_wait_kwargs["retries"] = chain_config["retries"] @@ -174,7 +173,7 @@ def test_user_storage(chain_config): # Wait for update_storage transaction update_storage_a2_wait_kwargs = { "transaction_hash": update_storage_a2_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: update_storage_a2_wait_kwargs["retries"] = chain_config["retries"] diff --git a/tests/e2e/tests/test_wizard_of_coin.py b/tests/e2e/tests/test_wizard_of_coin.py index 8de6e8a..0d61927 100644 --- a/tests/e2e/tests/test_wizard_of_coin.py +++ b/tests/e2e/tests/test_wizard_of_coin.py @@ -4,7 +4,6 @@ from genlayer_py import create_client, create_account from genlayer_py.chains import localnet, studionet, testnet_asimov -from genlayer_py.types import TransactionStatus from genlayer_py.assertions import tx_execution_succeeded # Load environment variables from .env file @@ -65,7 +64,7 @@ def test_wizard_of_coin(chain_config): # Wait for transaction with retries if specified wait_kwargs = { "transaction_hash": deploy_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: wait_kwargs["retries"] = chain_config["retries"] @@ -90,7 +89,7 @@ def test_wizard_of_coin(chain_config): # Wait for ask_for_coin transaction ask_for_coin_wait_kwargs = { "transaction_hash": ask_for_coin_tx_hash, - "status": TransactionStatus.FINALIZED, + "wait_until": "finalized", } if chain_config["retries"]: ask_for_coin_wait_kwargs["retries"] = chain_config["retries"] diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index ff06a60..1e42103 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -94,8 +94,7 @@ def sample_transaction_data(): "gaslimit": 2, "nonce": 1, "type": 2, - "status": 7, # FINALIZED - "status_name": "FINALIZED", + "lifecycle": {"state": "finalized", "outcome": "accepted"}, "result": "6", "result_name": "MAJORITY_AGREE", "created_at": "2025-07-22T19:58:39.866436+00:00", @@ -115,8 +114,7 @@ def pending_transaction_data(): """Transaction in pending state""" return { "hash": "0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - "status": 1, # PENDING - "status_name": "PENDING", + "lifecycle": {"state": "processing", "phase": "pending"}, "from_address": "0xd650f318A0C1F940a3b6dFeA695747fA9804D685", "to_address": "0xf72aa51B6350C18966923073d3609e1356a3fbBA", "value": 0, @@ -128,8 +126,7 @@ def pending_transaction_data(): def accepted_transaction_data(sample_transaction_data): """Transaction in accepted state""" data = sample_transaction_data.copy() - data["status"] = 5 # ACCEPTED - data["status_name"] = "ACCEPTED" + data["lifecycle"] = {"state": "decided", "outcome": "accepted"} return data diff --git a/tests/unit/contracts/test_contract_actions.py b/tests/unit/contracts/test_contract_actions.py index c0c6de9..b19f020 100644 --- a/tests/unit/contracts/test_contract_actions.py +++ b/tests/unit/contracts/test_contract_actions.py @@ -9,6 +9,7 @@ import genlayer_py.contracts.actions as contract_actions from genlayer_py.chains import localnet from genlayer_py.chains.testnet_asimov import testnet_asimov +from genlayer_py.consensus.abi import CONSENSUS_MAIN_ABI from genlayer_py.exceptions import GenLayerError from genlayer_py.transactions.fees import ( ADD_TRANSACTION_WITH_FEES_ARGUMENT_TYPES, @@ -32,7 +33,6 @@ requires_fee_deposit_calculation, ) - DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM = 10_000 LOCAL_EXECUTION_BUDGET_FLOOR_GAS = 306_192 @@ -124,6 +124,18 @@ } ] +# Studio refetches its ConsensusMain ABI from the simulator RPC. Its current +# native appeal entrypoint carries only the transaction id. +STUDIO_CONSENSUS_MAIN_ABI = [ + { + "type": "function", + "name": "submitAppeal", + "stateMutability": "payable", + "inputs": [{"name": "_txId", "type": "bytes32"}], + "outputs": [], + } +] + SENDER = "0x1111111111111111111111111111111111111111" RECIPIENT = "0x2222222222222222222222222222222222222222" TX_ID = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" @@ -260,7 +272,7 @@ def fake_send_consensus_call(**kwargs): def test_top_up_and_submit_appeal_sends_consensus_call_and_returns_tx_id(monkeypatch): - client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES) + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES, chain_id=testnet_asimov.id) captured = {} def fake_send_consensus_call(**kwargs): @@ -277,28 +289,58 @@ def fake_send_consensus_call(**kwargs): self=client, transaction_id=TX_ID, value=1234, + expected_decision_id=42, distribution={"appealRounds": 1, "rotations": [0, 1]}, ) selector = eth_utils.keccak( - text=f"topUpAndSubmitAppeal(bytes32,{FEES_DISTRIBUTION_ABI_TYPE})" + text=f"topUpAndSubmitAppeal(bytes32,uint256,{FEES_DISTRIBUTION_ABI_TYPE})" )[:4].hex() assert result == TX_ID assert captured["value"] == 1234 assert captured["operation_name"] == "Top up and submit appeal" assert captured["encoded_data"].startswith(f"0x{selector}") + decoded_tx_id, decision_id, _ = abi_decode( + ("bytes32", "uint256", FEES_DISTRIBUTION_ABI_TYPE), + Web3.to_bytes(hexstr=captured["encoded_data"][10:]), + ) + assert decoded_tx_id == Web3.to_bytes(hexstr=TX_ID) + assert decision_id == 42 -def test_appeal_transaction_auto_resolves_min_bond(monkeypatch): - client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES) - client.chain.fee_manager_contract = {"address": "0x4444444444444444444444444444444444444444"} - client.chain.rounds_storage_contract = {"address": "0x5555555555555555555555555555555555555555"} +def test_encode_submit_appeal_uses_exact_decision_guarded_selector(): + client = _make_client(CONSENSUS_MAIN_ABI) + + encoded = contract_actions._encode_submit_appeal_data( + self=client, + transaction_id=TX_ID, + expected_decision_id=42, + ) + + selector = eth_utils.keccak(text="submitAppeal(bytes32,uint256)")[:4].hex() + assert encoded.startswith(f"0x{selector}") + assert abi_decode(("bytes32", "uint256"), Web3.to_bytes(hexstr=encoded[10:])) == ( + Web3.to_bytes(hexstr=TX_ID), + 42, + ) + + +def test_appeal_transaction_auto_resolves_latest_quote(monkeypatch): + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES, chain_id=testnet_asimov.id) captured = {} monkeypatch.setattr( contract_actions, - "get_min_appeal_bond", - Mock(return_value=4_321), + "get_appeal_quote", + Mock( + return_value={ + "decision_id": 42, + "bond": 4_000, + "funding": 321, + "total": 4_321, + "appeal_deadline": 999, + } + ), ) monkeypatch.setattr( contract_actions, @@ -322,21 +364,32 @@ def fake_send_consensus_call(**kwargs): ) assert result == TX_ID - contract_actions.get_min_appeal_bond.assert_called_once_with(client, TX_ID) + contract_actions.get_appeal_quote.assert_called_once_with(client, TX_ID) + contract_actions._encode_submit_appeal_data.assert_called_once_with( + self=client, + transaction_id=TX_ID, + expected_decision_id=42, + ) assert captured["value"] == 4_321 assert captured["operation_name"] == "Appeal" -def test_top_up_and_submit_appeal_auto_resolves_min_bond(monkeypatch): - client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES) - client.chain.fee_manager_contract = {"address": "0x4444444444444444444444444444444444444444"} - client.chain.rounds_storage_contract = {"address": "0x5555555555555555555555555555555555555555"} +def test_top_up_and_submit_appeal_auto_resolves_latest_quote(monkeypatch): + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES, chain_id=testnet_asimov.id) captured = {} monkeypatch.setattr( contract_actions, - "get_min_appeal_bond", - Mock(return_value=9_999), + "get_appeal_quote", + Mock( + return_value={ + "decision_id": 77, + "bond": 9_000, + "funding": 999, + "total": 9_999, + "appeal_deadline": 1_234, + } + ), ) def fake_send_consensus_call(**kwargs): @@ -357,24 +410,275 @@ def fake_send_consensus_call(**kwargs): assert result == TX_ID assert captured["value"] == 9_999 + _, decision_id, _ = abi_decode( + ("bytes32", "uint256", FEES_DISTRIBUTION_ABI_TYPE), + Web3.to_bytes(hexstr=captured["encoded_data"][10:]), + ) + assert decision_id == 77 -def test_appeal_auto_bond_requires_fee_contracts(): - client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES) - client.chain.fee_manager_contract = None +def test_appeal_auto_quote_failure_is_actionable(monkeypatch): + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES, chain_id=testnet_asimov.id) + monkeypatch.setattr( + contract_actions, + "get_appeal_quote", + Mock(side_effect=RuntimeError("no active decision")), + ) - with pytest.raises(GenLayerError, match="Cannot auto-resolve appeal bond"): + with pytest.raises(GenLayerError, match="Cannot quote an active appeal decision"): contract_actions.appeal_transaction( self=client, transaction_id=TX_ID, ) +def test_get_appeal_quote_uses_consensus_data_without_full_transaction(monkeypatch): + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES, chain_id=testnet_asimov.id) + quote_call = Mock(return_value=(42, 4_000, 321, 999)) + estimate = Mock(return_value=SimpleNamespace(call=quote_call)) + contract = SimpleNamespace( + functions=SimpleNamespace(estimateLatestAppealCharge=estimate) + ) + monkeypatch.setattr( + contract_actions, + "_consensus_data_contract", + lambda self: contract, + ) + + assert contract_actions.get_appeal_quote(client, TX_ID) == { + "decision_id": 42, + "bond": 4_000, + "funding": 321, + "total": 4_321, + "appeal_deadline": 999, + } + estimate.assert_called_once_with(Web3.to_bytes(hexstr=TX_ID)) + + +def test_get_min_appeal_bond_returns_the_full_train_charge(monkeypatch): + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES, chain_id=testnet_asimov.id) + monkeypatch.setattr( + contract_actions, + "get_appeal_quote", + Mock(return_value={"total": 4_321}), + ) + + assert contract_actions.get_min_appeal_bond(client, TX_ID) == 4_321 + + +def test_get_appeal_charge_returns_the_full_train_charge(monkeypatch): + client = Mock() + monkeypatch.setattr( + contract_actions, + "get_appeal_quote", + Mock(return_value={"total": 4_321}), + ) + assert contract_actions.get_appeal_charge(client, TX_ID) == 4_321 + + +def test_can_appeal_passes_the_exact_active_decision_id(monkeypatch): + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES, chain_id=testnet_asimov.id) + client.chain.appeals_contract = { + "address": "0x4444444444444444444444444444444444444444", + "abi": [], + } + can_appeal_call = Mock(return_value=True) + can_appeal_function = Mock(return_value=SimpleNamespace(call=can_appeal_call)) + contract = SimpleNamespace(functions=SimpleNamespace(canAppeal=can_appeal_function)) + monkeypatch.setattr( + contract_actions, + "_appeals_contract", + lambda self: contract, + ) + monkeypatch.setattr( + contract_actions, + "_get_active_decision_id", + Mock(return_value=42), + ) + + assert contract_actions.can_appeal(client, TX_ID) is True + can_appeal_function.assert_called_once_with(Web3.to_bytes(hexstr=TX_ID), 42) + + +def test_can_appeal_returns_false_without_an_active_decision(monkeypatch): + client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES, chain_id=testnet_asimov.id) + client.chain.appeals_contract = { + "address": "0x4444444444444444444444444444444444444444", + "abi": [], + } + monkeypatch.setattr( + contract_actions, + "_get_active_decision_id", + Mock(return_value=None), + ) + appeals_contract = Mock() + monkeypatch.setattr( + contract_actions, + "_appeals_contract", + appeals_contract, + ) + + assert contract_actions.can_appeal(client, TX_ID) is False + appeals_contract.assert_not_called() + + +def _forbid_train_reads(monkeypatch): + train_read = Mock(side_effect=AssertionError("train read on a Studio chain")) + monkeypatch.setattr(contract_actions, "_consensus_data_contract", train_read) + monkeypatch.setattr(contract_actions, "_get_active_decision_id", train_read) + monkeypatch.setattr(contract_actions, "get_appeal_quote", train_read) + return train_read + + +def test_appeal_transaction_on_studio_keeps_the_native_call_shape(monkeypatch): + client = _make_client(STUDIO_CONSENSUS_MAIN_ABI) + train_read = _forbid_train_reads(monkeypatch) + captured = {} + + def fake_send_consensus_call(**kwargs): + captured.update(kwargs) + return "0xevmtx" + + monkeypatch.setattr( + contract_actions, + "_send_consensus_call", + fake_send_consensus_call, + ) + + result = contract_actions.appeal_transaction( + self=client, + transaction_id=TX_ID, + value=1234, + ) + + selector = eth_utils.keccak(text="submitAppeal(bytes32)")[:4].hex() + assert result == TX_ID + assert captured["value"] == 1234 + assert captured["operation_name"] == "Appeal" + assert captured["encoded_data"] == f"0x{selector}{TX_ID[2:]}" + train_read.assert_not_called() + + +def test_top_up_and_submit_appeal_on_studio_keeps_the_native_call_shape(monkeypatch): + client = _make_client(STUDIO_CONSENSUS_MAIN_ABI) + train_read = _forbid_train_reads(monkeypatch) + captured = {} + + def fake_send_consensus_call(**kwargs): + captured.update(kwargs) + return "0xevmtx" + + monkeypatch.setattr( + contract_actions, + "_send_consensus_call", + fake_send_consensus_call, + ) + + result = contract_actions.top_up_and_submit_appeal( + self=client, + transaction_id=TX_ID, + value=1234, + distribution={"appealRounds": 1, "rotations": [0, 1]}, + ) + + selector = eth_utils.keccak( + text=f"topUpAndSubmitAppeal(bytes32,{FEES_DISTRIBUTION_ABI_TYPE})" + )[:4].hex() + assert result == TX_ID + assert captured["value"] == 1234 + assert captured["operation_name"] == "Top up and submit appeal" + assert captured["encoded_data"].startswith(f"0x{selector}") + decoded_tx_id, distribution = abi_decode( + ("bytes32", FEES_DISTRIBUTION_ABI_TYPE), + Web3.to_bytes(hexstr=captured["encoded_data"][10:]), + ) + assert decoded_tx_id == Web3.to_bytes(hexstr=TX_ID) + assert distribution[2] == 1 + train_read.assert_not_called() + + +@pytest.mark.parametrize( + "action", + ( + contract_actions.get_appeal_quote, + contract_actions.get_appeal_charge, + contract_actions.get_min_appeal_bond, + ), +) +def test_appeal_quote_reads_on_studio_report_unavailable(action, monkeypatch): + client = _make_client(STUDIO_CONSENSUS_MAIN_ABI) + train_read = Mock(side_effect=AssertionError("train read on a Studio chain")) + monkeypatch.setattr(contract_actions, "_consensus_data_contract", train_read) + + with pytest.raises(GenLayerError, match="not available on current Studio"): + action(client, TX_ID) + + train_read.assert_not_called() + + +def test_studio_appeals_require_an_explicit_value(monkeypatch): + client = _make_client(STUDIO_CONSENSUS_MAIN_ABI) + train_read = _forbid_train_reads(monkeypatch) + send_consensus_call = Mock() + monkeypatch.setattr( + contract_actions, + "_send_consensus_call", + send_consensus_call, + ) + + with pytest.raises(GenLayerError, match="pass value explicitly"): + contract_actions.appeal_transaction(self=client, transaction_id=TX_ID) + with pytest.raises(GenLayerError, match="pass value explicitly"): + contract_actions.top_up_and_submit_appeal( + self=client, + transaction_id=TX_ID, + distribution={"appealRounds": 1}, + ) + + send_consensus_call.assert_not_called() + train_read.assert_not_called() + + +def test_studio_appeals_reject_the_train_decision_guard(monkeypatch): + client = _make_client(STUDIO_CONSENSUS_MAIN_ABI) + train_read = _forbid_train_reads(monkeypatch) + send_consensus_call = Mock() + monkeypatch.setattr( + contract_actions, + "_send_consensus_call", + send_consensus_call, + ) + + with pytest.raises(GenLayerError, match="expected_decision_id is not supported"): + contract_actions.appeal_transaction( + self=client, + transaction_id=TX_ID, + value=1234, + expected_decision_id=42, + ) + with pytest.raises(GenLayerError, match="expected_decision_id is not supported"): + contract_actions.top_up_and_submit_appeal( + self=client, + transaction_id=TX_ID, + distribution={"appealRounds": 1}, + value=1234, + expected_decision_id=42, + ) + + send_consensus_call.assert_not_called() + train_read.assert_not_called() + + +def test_encode_submit_appeal_still_requires_a_decision_id_on_the_train_shape(): + client = _make_client(CONSENSUS_MAIN_ABI, chain_id=testnet_asimov.id) + + with pytest.raises(ValueError, match="submitAppeal requires expected_decision_id"): + contract_actions._encode_submit_appeal_data(self=client, transaction_id=TX_ID) + + def test_send_consensus_call_returns_localnet_rpc_hash_without_waiting(monkeypatch): wait_for_transaction_receipt = Mock() - sign_transaction = Mock( - return_value=SimpleNamespace(raw_transaction=b"\x12\x34") - ) + sign_transaction = Mock(return_value=SimpleNamespace(raw_transaction=b"\x12\x34")) client = SimpleNamespace( chain=SimpleNamespace( id=localnet.id, @@ -382,12 +686,12 @@ def test_send_consensus_call_returns_localnet_rpc_hash_without_waiting(monkeypat "address": "0x3333333333333333333333333333333333333333", }, ), - provider=SimpleNamespace( - make_request=Mock(return_value={"result": TX_ID}) - ), + provider=SimpleNamespace(make_request=Mock(return_value={"result": TX_ID})), w3=SimpleNamespace( to_hex=Mock(return_value="0xsigned"), - eth=SimpleNamespace(wait_for_transaction_receipt=wait_for_transaction_receipt), + eth=SimpleNamespace( + wait_for_transaction_receipt=wait_for_transaction_receipt + ), ), ) account = SimpleNamespace(address=SENDER, sign_transaction=sign_transaction) @@ -413,14 +717,12 @@ def test_send_consensus_call_returns_localnet_rpc_hash_without_waiting(monkeypat def test_revert_selector_formatter_names_fee_errors(): error = Exception({"data": "0x632be5a1"}) - assert contract_actions._format_rpc_error(error).endswith( - "(FeeValueMustBeNonZero)" - ) + assert contract_actions._format_rpc_error(error).endswith("(FeeValueMustBeNonZero)") -def _make_client(add_transaction_abi): +def _make_client(add_transaction_abi, chain_id=localnet.id): chain = SimpleNamespace( - id=61999, + id=chain_id, consensus_main_contract={ "address": "0x3333333333333333333333333333333333333333", "abi": add_transaction_abi, @@ -432,6 +734,7 @@ def _make_client(add_transaction_abi): return SimpleNamespace( chain=chain, local_account=local_account, + provider=Mock(), w3=Web3(), ) @@ -482,7 +785,9 @@ def _make_fee_policy_client(values, gas_price=0): eth = _FakeEth(values, gas_price) return SimpleNamespace( chain=SimpleNamespace( - fee_manager_contract={"address": "0x4444444444444444444444444444444444444444"}, + fee_manager_contract={ + "address": "0x4444444444444444444444444444444444444444" + }, ), w3=SimpleNamespace( eth=eth, @@ -715,7 +1020,9 @@ def fake_send_transaction(**kwargs): assert params[9][0][6] == b"\x12\x34" -def test_write_contract_defaults_external_message_allocations_to_finalization(monkeypatch): +def test_write_contract_defaults_external_message_allocations_to_finalization( + monkeypatch, +): client = _make_client(ADD_TRANSACTION_ABI_WITH_FEES) client.initialize_consensus_smart_contract = Mock() @@ -797,6 +1104,7 @@ def test_build_estimated_fees_distribution_adds_caps_and_message_bucket(): ] }, policy, + default_consensus_max_rotations=3, ) assert distribution["leaderTimeunitsAllocation"] == 100 @@ -810,6 +1118,42 @@ def test_build_estimated_fees_distribution_adds_caps_and_message_bucket(): assert distribution["receiptFeeMaxGasPrice"] == 36 +def test_build_estimated_fees_distribution_funds_each_round_with_default_rotations(): + policy = { + "enabled": True, + "genPerTimeUnit": 10, + "storageUnitPrice": 20, + "receiptGasPrice": 30, + "executionBudgetFloor": 1_234, + } + + distribution = build_estimated_fees_distribution( + {"appealRounds": 2}, + policy, + default_consensus_max_rotations=3, + ) + + assert distribution["rotations"] == [3, 3, 3] + + +def test_build_estimated_fees_distribution_preserves_explicit_zero_rotations(): + policy = { + "enabled": True, + "genPerTimeUnit": 10, + "storageUnitPrice": 20, + "receiptGasPrice": 30, + "executionBudgetFloor": 1_234, + } + + distribution = build_estimated_fees_distribution( + {"rotations": [0]}, + policy, + default_consensus_max_rotations=3, + ) + + assert distribution["rotations"] == [0] + + def test_build_estimated_fees_distribution_preserves_explicit_execution_budget_with_messages(): policy = { "enabled": True, @@ -832,6 +1176,7 @@ def test_build_estimated_fees_distribution_preserves_explicit_execution_budget_w ], }, policy, + default_consensus_max_rotations=3, ) assert distribution["executionBudgetPerRound"] == 42 @@ -861,6 +1206,7 @@ def test_estimate_transaction_fees_uses_studio_fee_config(): chain=SimpleNamespace( fee_manager_contract=None, default_number_of_initial_validators=5, + default_consensus_max_rotations=3, ), provider=SimpleNamespace( make_request=Mock( @@ -894,7 +1240,8 @@ def test_estimate_transaction_fees_uses_studio_fee_config(): } ) assert estimate["distribution"]["executionBudgetPerRound"] == 3_000_000_000 - assert estimate["feeValue"] == 3_000_011_000 + assert estimate["distribution"]["rotations"] == [3] + assert estimate["feeValue"] == 12_000_044_000 def test_extract_studio_fee_policy_fallback_includes_message_reveal_leg(): @@ -927,6 +1274,7 @@ def test_estimate_transaction_fees_derives_message_bucket_from_allocations(): chain=SimpleNamespace( fee_manager_contract=None, default_number_of_initial_validators=5, + default_consensus_max_rotations=3, ), provider=SimpleNamespace( make_request=Mock( @@ -976,7 +1324,8 @@ def test_estimate_transaction_fees_derives_message_bucket_from_allocations(): assert estimate["distribution"]["executionBudgetPerRound"] == ( 3_000_000_000 + 30 * DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM ) - assert estimate["feeValue"] == 3_000_311_080 + assert estimate["distribution"]["rotations"] == [3] + assert estimate["feeValue"] == 12_001_244_080 assert estimate["messageAllocations"] == message_allocations assert estimate["message_allocations"] == message_allocations @@ -986,6 +1335,7 @@ def test_estimate_transaction_fees_from_simulation_builds_trusted_preset(): chain=SimpleNamespace( fee_manager_contract=None, default_number_of_initial_validators=5, + default_consensus_max_rotations=3, ), provider=SimpleNamespace( make_request=Mock( @@ -1050,7 +1400,8 @@ def test_estimate_transaction_fees_from_simulation_builds_trusted_preset(): } assert estimate["distribution"]["executionBudgetPerRound"] == 602_117 assert estimate["distribution"]["totalMessageFees"] == 6 - assert estimate["feeValue"] == 613_123 + assert estimate["distribution"]["rotations"] == [3] + assert estimate["feeValue"] == 2_452_474 def test_simulation_execution_budget_uses_floor_without_default_gas_clobber(): @@ -1058,6 +1409,7 @@ def test_simulation_execution_budget_uses_floor_without_default_gas_clobber(): chain=SimpleNamespace( fee_manager_contract=None, default_number_of_initial_validators=5, + default_consensus_max_rotations=3, ), provider=SimpleNamespace( make_request=Mock( @@ -1131,7 +1483,9 @@ def make_request(method, params): { "messageType": MessageType.Internal, "onAcceptance": True, - "parentIndex": str(MESSAGE_ALLOCATION_ROOT_PARENT_INDEX), + "parentIndex": str( + MESSAGE_ALLOCATION_ROOT_PARENT_INDEX + ), "recipient": RECIPIENT, "callKey": "0x" + "00" * 32, "budget": "110", @@ -1159,7 +1513,9 @@ def make_request(method, params): { "messageType": MessageType.Internal, "onAcceptance": True, - "parentIndex": str(MESSAGE_ALLOCATION_ROOT_PARENT_INDEX), + "parentIndex": str( + MESSAGE_ALLOCATION_ROOT_PARENT_INDEX + ), "recipient": RECIPIENT, "callKey": "0x" + "00" * 32, "budget": "110", @@ -1202,7 +1558,8 @@ def make_request(method, params): ) request_params = sim_call.kwargs["params"][0] assert request_params["value"] == hex(7) - assert request_params["fees"]["feeValue"] == 100_021_110 + assert request_params["fees"]["feeValue"] == 400_084_110 + assert request_params["fees"]["distribution"]["rotations"] == [3] assert request_params["fees"]["distribution"]["totalMessageFees"] == 110 assert request_params["fees"]["messageAllocations"][0]["budget"] == 110 assert ( @@ -1216,6 +1573,7 @@ def make_request(method, params): assert estimate["simulation"]["feeReport"]["totalEstimatedFee"] == "501664" assert estimate["distribution"]["executionBudgetPerRound"] == 100_000_000 assert estimate["distribution"]["totalMessageFees"] == 110 + assert estimate["distribution"]["rotations"] == [0] assert estimate["messageAllocations"][0]["budget"] == 110 assert estimate["feeValue"] == 100_011_110 @@ -1231,6 +1589,7 @@ def test_estimate_transaction_fees_from_simulation_preserves_mode2_allocations() chain=SimpleNamespace( fee_manager_contract=None, default_number_of_initial_validators=5, + default_consensus_max_rotations=3, ), provider=SimpleNamespace( make_request=Mock( @@ -1274,7 +1633,8 @@ def test_estimate_transaction_fees_from_simulation_preserves_mode2_allocations() assert estimate["messageAllocations"][0]["budget"] == 50 assert estimate["messageAllocations"][0]["feeParams"] == fee_params assert estimate["distribution"]["totalMessageFees"] == 50 - assert estimate["feeValue"] == 11_050 + assert estimate["distribution"]["rotations"] == [3] + assert estimate["feeValue"] == 44_050 def test_write_contract_refreshes_consensus_abi_before_add_transaction_encoding( diff --git a/tests/unit/sample_data/full_deploy_transaction_data.py b/tests/unit/sample_data/full_deploy_transaction_data.py index cb54c6a..fb3c258 100644 --- a/tests/unit/sample_data/full_deploy_transaction_data.py +++ b/tests/unit/sample_data/full_deploy_transaction_data.py @@ -485,7 +485,7 @@ "rotation_count": 0, "s": None, "sender": "0xd650f318A0C1F940a3b6dFeA695747fA9804D685", - "status": 7, + "lifecycle": {"state": "finalized", "outcome": "accepted"}, "timestamp_appeal": None, "timestamp_awaiting_finalization": 1753284244, "to_address": "0xf72aa51B6350C18966923073d3609e1356a3fbBA", @@ -498,5 +498,4 @@ "type": 1, "v": None, "value": 0, - "status_name": "FINALIZED", } diff --git a/tests/unit/sample_data/full_write_transaction_data.py b/tests/unit/sample_data/full_write_transaction_data.py index 4c1ce8e..6ac649f 100644 --- a/tests/unit/sample_data/full_write_transaction_data.py +++ b/tests/unit/sample_data/full_write_transaction_data.py @@ -654,7 +654,7 @@ "rotation_count": 0, "s": None, "sender": "0xd650f318A0C1F940a3b6dFeA695747fA9804D685", - "status": 7, + "lifecycle": {"state": "finalized", "outcome": "accepted"}, "timestamp_appeal": None, "timestamp_awaiting_finalization": 1753284298, "to_address": "0xf72aa51B6350C18966923073d3609e1356a3fbBA", @@ -667,5 +667,4 @@ "type": 2, "v": None, "value": 0, - "status_name": "FINALIZED", } diff --git a/tests/unit/sample_data/simplified_deploy_transaction_data.py b/tests/unit/sample_data/simplified_deploy_transaction_data.py index 85e5105..29d568d 100644 --- a/tests/unit/sample_data/simplified_deploy_transaction_data.py +++ b/tests/unit/sample_data/simplified_deploy_transaction_data.py @@ -178,10 +178,9 @@ "result": 6, "result_name": "MAJORITY_AGREE", "sender": "0xd650f318A0C1F940a3b6dFeA695747fA9804D685", - "status": 7, + "lifecycle": {"state": "finalized", "outcome": "accepted"}, "to_address": "0xf72aa51B6350C18966923073d3609e1356a3fbBA", "tx_id": "0x7684df399f44fb67aa51d03905310a4fce66713c1281a3ba53d63fff9bb4faa8", "type": 1, "value": 0, - "status_name": "FINALIZED", } diff --git a/tests/unit/sample_data/simplified_write_transaction_data.py b/tests/unit/sample_data/simplified_write_transaction_data.py index 71c61fa..9a118ae 100644 --- a/tests/unit/sample_data/simplified_write_transaction_data.py +++ b/tests/unit/sample_data/simplified_write_transaction_data.py @@ -208,10 +208,9 @@ "result": 6, "result_name": "MAJORITY_AGREE", "sender": "0xd650f318A0C1F940a3b6dFeA695747fA9804D685", - "status": 7, + "lifecycle": {"state": "finalized", "outcome": "accepted"}, "to_address": "0xf72aa51B6350C18966923073d3609e1356a3fbBA", "tx_id": "0x0ae9327d0d81df24f03cef4dab94571c662c50b09f69dbe29305466aa9529ff6", "type": 2, "value": 0, - "status_name": "FINALIZED", } diff --git a/tests/unit/smoke/test_bradbury_smoke.py b/tests/unit/smoke/test_bradbury_smoke.py index 778f1cb..ecac549 100644 --- a/tests/unit/smoke/test_bradbury_smoke.py +++ b/tests/unit/smoke/test_bradbury_smoke.py @@ -101,8 +101,8 @@ def test_get_pending_transaction_value_zero(self): @pytest.mark.testnet -class TestBradburyGetTransactionAllData: - """Verify getTransactionAllData returns txExecutionResult.""" +class TestBradburyTransactionRead: + """Verify the transaction read returns txExecutionResult.""" def test_get_transaction_returns_execution_result(self): """Use the actual SDK client to fetch a known finalized tx and verify tx_execution_result.""" @@ -112,11 +112,21 @@ def test_get_transaction_returns_execution_result(self): "563f046c187d711127c51213ca62e2e4fee52009a98f0989a73a0a0382d21890" ) ) - assert tx["tx_execution_result"] in [0, 1, 2] + assert tx["tx_execution_result"] in [0, 1, 2, 3, 4, 5] assert tx["tx_execution_result_name"] in [ - "NOT_VOTED", "FINISHED_WITH_RETURN", "FINISHED_WITH_ERROR" + "NOT_VOTED", + "FINISHED_WITH_RETURN", + "FINISHED_WITH_ERROR", + "TIMEOUT", + "NONDET_DISAGREE", + "DETERMINISTIC_VIOLATION", ] - assert tx["status_name"] is not None + assert tx["lifecycle"]["state"] in { + "processing", + "decided", + "finalized", + "canceled", + } assert tx["result_name"] is not None def test_get_transaction_includes_messages(self): diff --git a/tests/unit/staking/test_staking_actions.py b/tests/unit/staking/test_staking_actions.py index 3969a41..2ad50da 100644 --- a/tests/unit/staking/test_staking_actions.py +++ b/tests/unit/staking/test_staking_actions.py @@ -9,27 +9,43 @@ from types import SimpleNamespace from unittest.mock import Mock +from eth_abi import decode as abi_decode from eth_utils import keccak import pytest from web3 import Web3 import genlayer_py.staking.actions as staking_actions +from genlayer_py.exceptions import GenLayerError from genlayer_py.staking.abi import STAKING_ABI, VALIDATOR_WALLET_ABI - +from genlayer_py.staking.operator_registration import ( + OperatorRegistrationContext, + OperatorRegistrationProof, +) STAKING_ADDR = "0x1111111111111111111111111111111111111111" WALLET_ADDR = "0x2222222222222222222222222222222222222222" SENDER_ADDR = "0x3333333333333333333333333333333333333333" OTHER_ADDR = "0x4444444444444444444444444444444444444444" +ADDRESS_MANAGER_ADDR = "0x5555555555555555555555555555555555555555" +FACTORY_ADDR = "0x6666666666666666666666666666666666666666" # 4-byte selectors for the function signatures we rely on. -SEL_VALIDATOR_JOIN_NO_ARGS = keccak(text="validatorJoin()")[:4].hex() -SEL_VALIDATOR_JOIN_ADDR = keccak(text="validatorJoin(address)")[:4].hex() +SEL_VALIDATOR_JOIN = keccak(text="validatorJoin(uint256[2],bytes)")[:4].hex() SEL_WALLET_DEPOSIT = keccak(text="validatorDeposit()")[:4].hex() SEL_WALLET_EXIT = keccak(text="validatorExit(uint256)")[:4].hex() -SEL_SET_OPERATOR = keccak(text="setOperator(address)")[:4].hex() SEL_DELEGATOR_JOIN = keccak(text="delegatorJoin(address)")[:4].hex() +REGISTRATION = OperatorRegistrationProof( + operator=OTHER_ADDR, + operator_pub_key=(1, 2), + possession_proof=b"\x99" * 65, +) +JOIN_CONTEXT = OperatorRegistrationContext( + registrar=FACTORY_ADDR, + owner=SENDER_ADDR, + chain_id=61999, +) + def _make_client(): """SimpleNamespace stand-in for GenLayerClient — enough surface for @@ -59,20 +75,87 @@ def _last_tx(client): return client.local_account.sign_transaction.call_args.args[0] -def test_validator_join_no_operator_targets_staking_and_encodes_empty_args(): +def test_validator_join_targets_staking_and_encodes_proof(monkeypatch): client = _make_client() - staking_actions.validator_join(self=client, amount=10) + monkeypatch.setattr( + staking_actions, + "get_validator_join_context", + lambda self, account=None: JOIN_CONTEXT, + ) + monkeypatch.setattr( + staking_actions, + "verify_operator_registration", + lambda registration, context: registration is REGISTRATION + and context == JOIN_CONTEXT, + ) + + staking_actions.validator_join(self=client, amount=10, registration=REGISTRATION) tx = _last_tx(client) assert tx["to"].lower() == STAKING_ADDR.lower() assert tx["value"] == 10 - assert tx["data"][2:10] == SEL_VALIDATOR_JOIN_NO_ARGS + assert tx["data"][2:10] == SEL_VALIDATOR_JOIN + pub_key, proof = abi_decode( + ("uint256[2]", "bytes"), Web3.to_bytes(hexstr=tx["data"][10:]) + ) + assert pub_key == (1, 2) + assert proof == REGISTRATION.possession_proof -def test_validator_join_with_operator_encodes_address_variant(): +@pytest.mark.parametrize( + "kwargs", + ({}, {"operator": OTHER_ADDR}, {"registration": OTHER_ADDR}), +) +def test_validator_join_rejects_legacy_address_only_calls(kwargs): client = _make_client() - staking_actions.validator_join(self=client, amount=10, operator=OTHER_ADDR) - tx = _last_tx(client) - assert tx["data"][2:10] == SEL_VALIDATOR_JOIN_ADDR + with pytest.raises(GenLayerError, match="OperatorRegistrationProof"): + staking_actions.validator_join(self=client, amount=10, **kwargs) + client.local_account.sign_transaction.assert_not_called() + + +def test_validator_join_rejects_proof_for_the_wrong_context(monkeypatch): + client = _make_client() + monkeypatch.setattr( + staking_actions, + "get_validator_join_context", + lambda self, account=None: JOIN_CONTEXT, + ) + monkeypatch.setattr( + staking_actions, + "verify_operator_registration", + lambda registration, context: False, + ) + + with pytest.raises(GenLayerError, match="fresh proof"): + staking_actions.validator_join( + self=client, amount=10, registration=REGISTRATION + ) + client.local_account.sign_transaction.assert_not_called() + + +def test_validator_join_context_uses_factory_and_sender(monkeypatch): + staking = SimpleNamespace( + functions=SimpleNamespace( + addressManager=lambda: SimpleNamespace(call=lambda: ADDRESS_MANAGER_ADDR) + ) + ) + get_address = Mock(return_value=SimpleNamespace(call=lambda: FACTORY_ADDR)) + address_manager = SimpleNamespace( + functions=SimpleNamespace(getAddressNonZero=get_address) + ) + client = SimpleNamespace( + local_account=SimpleNamespace(address=SENDER_ADDR), + w3=SimpleNamespace( + to_checksum_address=Web3.to_checksum_address, + eth=SimpleNamespace( + chain_id=61999, + contract=Mock(return_value=address_manager), + ), + ), + ) + monkeypatch.setattr(staking_actions, "_staking", lambda self: staking) + + assert staking_actions.get_validator_join_context(client) == JOIN_CONTEXT + get_address.assert_called_once_with("ValidatorWalletFactory") def test_validator_deposit_targets_wallet_not_staking(): @@ -95,12 +178,13 @@ def test_validator_exit_routes_through_wallet(): assert tx["data"][2:10] == SEL_WALLET_EXIT -def test_set_operator_routes_through_wallet(): +def test_set_operator_fails_with_actionable_migration_without_sending(): client = _make_client() - staking_actions.set_operator(self=client, validator=WALLET_ADDR, operator=OTHER_ADDR) - tx = _last_tx(client) - assert tx["to"].lower() == WALLET_ADDR.lower() - assert tx["data"][2:10] == SEL_SET_OPERATOR + with pytest.raises(GenLayerError, match="initiate_operator_transfer"): + staking_actions.set_operator( + self=client, validator=WALLET_ADDR, operator=OTHER_ADDR + ) + client.local_account.sign_transaction.assert_not_called() def test_delegator_join_targets_staking_with_value(): @@ -122,11 +206,15 @@ def test_staking_not_configured_raises(): def test_abis_include_expected_functions(): """Guard against the bundled ABI JSON drifting or truncating.""" names = {e["name"] for e in STAKING_ABI if e.get("type") == "function"} - wallet_names = {e["name"] for e in VALIDATOR_WALLET_ABI if e.get("type") == "function"} + wallet_names = { + e["name"] for e in VALIDATOR_WALLET_ABI if e.get("type") == "function" + } assert { "epoch", - "activeValidators", - "activeValidatorsCount", + "selectableValidators", + "selectableValidatorsCount", + "validatorsJoinedCount", + "getValidatorsJoined", "isValidator", "validatorView", "stakeOf", @@ -137,6 +225,106 @@ def test_abis_include_expected_functions(): "delegatorExit", "delegatorClaim", }.issubset(names) - assert {"validatorDeposit", "validatorExit", "setOperator", "setIdentity"}.issubset( - wallet_names + assert { + "validatorDeposit", + "validatorExit", + "setOperatorPubKey", + "setIdentity", + }.issubset(wallet_names) + assert "setOperator" not in wallet_names + # Withdrawn from the staking contract: keeping them in the bundled ABI lets + # callers reach entrypoints that revert. + assert ( + not { + "activeValidators", + "activeValidatorsCount", + "activeWeights", + "validatorsRoot", + } + & names + ) + + +class _PagedStakingStub: + """Stands in for selectable and joined Staking reads.""" + + def __init__(self, registry, selectable=None, page_len=None): + self._registry = registry + self._selectable = registry if selectable is None else selectable + self._page_len = page_len + self.pages_requested = [] + self.functions = self + + def selectableValidators(self): + return SimpleNamespace(call=lambda: self._selectable) + + def selectableValidatorsCount(self): + return SimpleNamespace(call=lambda: len(self._selectable)) + + def validatorsJoinedCount(self): + return SimpleNamespace(call=lambda: len(self._registry)) + + def getValidatorsJoined(self, start, size): + self.pages_requested.append((start, size)) + take = self._page_len or size + return SimpleNamespace(call=lambda: self._registry[start : start + take]) + + +def _client_with_staking_stub(monkeypatch, stub): + client = _make_client() + monkeypatch.setattr(staking_actions, "_staking", lambda self: stub) + return client + + +def test_active_validators_are_strictly_selectable(monkeypatch): + joined_but_not_selectable = "0x" + "a1" * 20 + selectable = "0x" + "b2" * 20 + stub = _PagedStakingStub( + [joined_but_not_selectable, selectable], selectable=[selectable] ) + client = _client_with_staking_stub(monkeypatch, stub) + + assert staking_actions.active_validators(self=client) == [selectable] + assert staking_actions.active_validators_count(self=client) == 1 + assert stub.pages_requested == [] + + +def test_joined_validators_pages_the_registry(monkeypatch): + registry = [f"0x{str(i) * 40}" for i in range(1, 6)] + stub = _PagedStakingStub(registry, page_len=2) + client = _client_with_staking_stub(monkeypatch, stub) + + monkeypatch.setattr(staking_actions, "VALIDATORS_JOINED_PAGE_SIZE", 2) + result = staking_actions.joined_validators(self=client) + + assert result == registry + assert stub.pages_requested == [(0, 2), (2, 2), (4, 2)] + + +def test_joined_validators_stops_on_a_short_page(monkeypatch): + """An empty page means the registry shrank mid-walk: stop, do not spin.""" + stub = _PagedStakingStub([]) + stub._registry = ["0x" + "a1" * 20] + # Report a count far larger than what the pages actually yield. + stub.validatorsJoinedCount = lambda: SimpleNamespace(call=lambda: 500) + client = _client_with_staking_stub(monkeypatch, stub) + + result = staking_actions.joined_validators(self=client) + + assert result == ["0x" + "a1" * 20] + assert len(stub.pages_requested) == 2 + + +def test_joined_validators_filters_zero_address(monkeypatch): + zero = "0x" + "00" * 20 + stub = _PagedStakingStub(["0x" + "a1" * 20, zero]) + client = _client_with_staking_stub(monkeypatch, stub) + + assert staking_actions.joined_validators(self=client) == ["0x" + "a1" * 20] + + +def test_joined_validators_count_reads_the_registry(monkeypatch): + stub = _PagedStakingStub(["0x" + "a1" * 20, "0x" + "b2" * 20]) + client = _client_with_staking_stub(monkeypatch, stub) + + assert staking_actions.joined_validators_count(self=client) == 2 diff --git a/tests/unit/test_api_enum_docs.py b/tests/unit/test_api_enum_docs.py new file mode 100644 index 0000000..61f4293 --- /dev/null +++ b/tests/unit/test_api_enum_docs.py @@ -0,0 +1,59 @@ +from pathlib import Path + +import pytest + +from genlayer_py.types import ExecutionResult, VoteType + +PROJECT_ROOT = Path(__file__).resolve().parents[2] + + +@pytest.mark.parametrize( + "relative_path", + ( + "docs/api-references/api.md", + "docs/api-references/genlayer-py.md", + ), +) +@pytest.mark.parametrize( + ("enum_name", "enum_type"), + (("ExecutionResult", ExecutionResult), ("VoteType", VoteType)), +) +def test_public_enum_docs_are_generated_from_the_complete_train_enum( + relative_path, enum_name, enum_type +): + docs = (PROJECT_ROOT / relative_path).read_text() + + for member in enum_type: + assert f'{enum_name}.{member.name} = "{member.value}"' in docs + + +@pytest.mark.parametrize( + "relative_path", + ( + "docs/api-references/api.md", + "docs/api-references/genlayer-py.md", + ), +) +def test_advanced_lifecycle_enums_are_not_documented_as_primary_types(relative_path): + docs = (PROJECT_ROOT / relative_path).read_text() + + assert "### TransactionStatus" not in docs + assert "### ResolutionAction" not in docs + + +@pytest.mark.parametrize( + "relative_path", + ( + "README.md", + "docs/api-references/api.md", + "docs/api-references/genlayer-py.md", + "docs/api-references/index.md", + ), +) +def test_public_lifecycle_docs_use_state_as_the_discriminator(relative_path): + docs = (PROJECT_ROOT / relative_path).read_text() + + assert "state" in docs + assert '{"status": "processing"' not in docs + assert '{"status": "decided"' not in docs + assert 'lifecycle"]["status' not in docs diff --git a/tests/unit/test_bug_hunt_v019.py b/tests/unit/test_bug_hunt_v019.py index b0e3485..882bb5f 100644 --- a/tests/unit/test_bug_hunt_v019.py +++ b/tests/unit/test_bug_hunt_v019.py @@ -25,16 +25,12 @@ def test_create_client_endpoint_does_not_mutate_caller_chain_config(): with patch.object(GenLayerClient, "initialize_consensus_smart_contract"): client = create_client(chain=chain, endpoint="http://override.invalid:8545") - assert client.chain.rpc_urls["default"]["http"] == [ - "http://override.invalid:8545" - ] + assert client.chain.rpc_urls["default"]["http"] == ["http://override.invalid:8545"] assert chain.rpc_urls["default"]["http"] == original_endpoints def test_read_contract_uses_explicit_account_when_client_has_no_default(): - account = SimpleNamespace( - address="0x1111111111111111111111111111111111111111" - ) + account = SimpleNamespace(address="0x1111111111111111111111111111111111111111") provider = Mock() provider.make_request.return_value = {"result": ""} client = SimpleNamespace(local_account=None, provider=provider) @@ -62,7 +58,7 @@ def test_calldata_decoder_rejects_truncated_length_prefixed_bytes(): def test_is_successful_recognizes_localnet_consensus_receipt(): transaction = { - "status_name": "FINALIZED", + "lifecycle": {"state": "finalized"}, "consensus_data": { "leader_receipt": [ { diff --git a/tests/unit/test_train_abi_surfaces.py b/tests/unit/test_train_abi_surfaces.py new file mode 100644 index 0000000..43f6946 --- /dev/null +++ b/tests/unit/test_train_abi_surfaces.py @@ -0,0 +1,92 @@ +import hashlib +import json + +import genlayer_py.staking as staking +from genlayer_py.consensus.abi import ( + APPEALS_ABI, + CONSENSUS_DATA_ABI, + CONSENSUS_DATA_ABI_V06, + CONSENSUS_MAIN_ABI, + CONSENSUS_MAIN_ABI_V06, +) +from genlayer_py.staking.abi import STAKING_ABI, VALIDATOR_WALLET_ABI + +# Hashes of the recursively canonicalized function surfaces generated from the +# exact consensus train artifacts at 42ea0aaf7aed9b1426681c37ee046154e22df163. +# This covers every nested tuple component, not just top-level selectors or +# function names. +EXPECTED_TRAIN_FUNCTION_SURFACE_HASHES = { + "ConsensusData": "3a7d39f4ed6c6aaa8dc1cfd5035387191f68c737ba499cd9732cf89fc97b7d34", + "ConsensusMain": "6542ed4ac55be2cdb85d190d2bc9e7e04d13a707cea9d89bde2e53feb2586ed5", + "Appeals": "7ff07eb47a1e801645e55141bcf75aaae022d46f06c8a94f29806a9fc61a0857", + "IGenLayerStaking": "1fd78e63d480800d93d41d3f5bdfd17f572bc5f394cccc7c796098f5ce8156b4", + "ValidatorWalletBlueprint": "6960726132fd77007eec8a90f036ef4be8d8efdcb6c6b79136c8835bb0b17800", +} + + +def _canonical_parameter(parameter): + canonical = { + "name": parameter.get("name", ""), + "type": parameter["type"], + } + if parameter["type"].startswith("tuple"): + canonical["components"] = [ + _canonical_parameter(component) + for component in parameter.get("components", []) + ] + return canonical + + +def _function_surface_hash(abi): + functions = [ + { + "name": entry["name"], + "stateMutability": entry.get("stateMutability"), + "inputs": [ + _canonical_parameter(parameter) for parameter in entry.get("inputs", []) + ], + "outputs": [ + _canonical_parameter(parameter) + for parameter in entry.get("outputs", []) + ], + } + for entry in abi + if entry.get("type") == "function" + ] + functions.sort( + key=lambda entry: json.dumps(entry, sort_keys=True, separators=(",", ":")) + ) + encoded = json.dumps(functions, sort_keys=True, separators=(",", ":")).encode() + return hashlib.sha256(encoded).hexdigest() + + +def test_bundled_function_surfaces_match_the_exact_consensus_train(): + bundled = { + "ConsensusData": CONSENSUS_DATA_ABI, + "ConsensusMain": CONSENSUS_MAIN_ABI, + "Appeals": APPEALS_ABI, + "IGenLayerStaking": STAKING_ABI, + "ValidatorWalletBlueprint": VALIDATOR_WALLET_ABI, + } + + assert { + name: _function_surface_hash(abi) for name, abi in bundled.items() + } == EXPECTED_TRAIN_FUNCTION_SURFACE_HASHES + + +def test_historical_consensus_abi_imports_are_train_only_aliases(): + assert CONSENSUS_DATA_ABI_V06 is CONSENSUS_DATA_ABI + assert CONSENSUS_MAIN_ABI_V06 is CONSENSUS_MAIN_ABI + + +def test_staking_package_exports_the_proof_based_join_helpers(): + expected = { + "get_validator_join_context", + "OperatorRegistrationContext", + "OperatorRegistrationProof", + "create_operator_registration", + "verify_operator_registration", + } + + assert expected.issubset(staking.__all__) + assert all(hasattr(staking, name) for name in expected) diff --git a/tests/unit/transactions/test_transaction_data_read.py b/tests/unit/transactions/test_transaction_data_read.py new file mode 100644 index 0000000..ee15ab2 --- /dev/null +++ b/tests/unit/transactions/test_transaction_data_read.py @@ -0,0 +1,563 @@ +"""Train-only transaction reads use bounded surfaces at one block snapshot.""" + +from types import SimpleNamespace +from unittest.mock import Mock, call + +import genlayer_py.transactions.actions as transaction_actions +import pytest +from genlayer_py.consensus.abi import ( + CONSENSUS_DATA_ABI, + CONSENSUS_DATA_ABI_V06, + CONSENSUS_DATA_BIG_ROUNDS_ABI, + CONSENSUS_MAIN_ABI, + CONSENSUS_MAIN_ABI_V06, + ROUNDS_STORAGE_READ_ABI, + TRANSACTION_MANAGER_READ_ABI, +) +from genlayer_py.types import ( + EXECUTION_RESULT_NUMBER_TO_NAME, + VOTE_TYPE_NUMBER_TO_NAME, + ExecutionResult, + VoteType, +) +from genlayer_py.types.transactions import ( + ProtocolTransactionStatus, + ResolutionAction, + ResolutionSource, +) +from genlayer_py.chains import localnet + +TX_HASH = "0x" + "ab" * 32 +CONSENSUS_DATA_ADDRESS = "0x" + "11" * 20 +ADDRESS_MANAGER_ADDRESS = "0x" + "22" * 20 +BIG_ROUNDS_ADDRESS = "0x" + "33" * 20 +TRANSACTION_MANAGER_ADDRESS = "0x" + "44" * 20 +ROUNDS_STORAGE_ADDRESS = "0x" + "55" * 20 +VALIDATORS = ["0x" + f"{index:040x}" for index in range(1, 4)] +CONSUMED_VALIDATORS = ["0x" + f"{index:040x}" for index in range(4, 6)] + + +class _Call: + def __init__(self, value, calls, name, args): + self._value = value + self._calls = calls + self._name = name + self._args = args + + def call(self, block_identifier=None): + self._calls.append((self._name, self._args, block_identifier)) + return self._value() if callable(self._value) else self._value + + +class _Contract: + def __init__(self, handlers): + self._handlers = handlers + self.calls = [] + self.functions = self + + def __getattr__(self, name): + def function(*args): + value = self._handlers[name](*args) + return _Call(value, self.calls, name, args) + + return function + + +def _light_transaction(): + return ( + 1_000, + "0x" + "66" * 20, + "0x" + "77" * 20, + 3, + 4, + 900, + 950, + bytes.fromhex("88" * 32), + 1, + bytes.fromhex("99" * 32), + b"", + b"", + [], + 0, + 2, + "0x" + "aa" * 20, + VALIDATORS[0], + 5, + bytes.fromhex("ab" * 32), + (10, 11, 12), + 0, + (0, 0, 2, 2, 0, 1, 1, len(VALIDATORS)), + len(CONSUMED_VALIDATORS), + ) + + +def _client_and_contracts(): + manager = _Contract( + { + "getAddressNonZero": lambda name: { + "ConsensusDataBigRounds": BIG_ROUNDS_ADDRESS, + "TransactionManager": TRANSACTION_MANAGER_ADDRESS, + "RoundsStorage": ROUNDS_STORAGE_ADDRESS, + }[name] + } + ) + big_rounds = _Contract( + { + "getStoredTransactionDataLight": lambda tx_id: _light_transaction(), + "getRoundValidatorsPaged": lambda tx_id, round_number, offset, limit: ( + VALIDATORS[offset : offset + limit], + len(VALIDATORS), + ), + "getConsumedValidatorsPaged": lambda tx_id, offset, limit: ( + CONSUMED_VALIDATORS[offset : offset + limit], + len(CONSUMED_VALIDATORS), + ), + } + ) + transaction_manager = _Contract( + { + "getTxExecutionResult": lambda tx_id: 1, + "getNumOfInitialValidators": lambda tx_id: 5, + } + ) + rounds_storage = _Contract( + { + "getValidatorVotes": lambda tx_id, round_number: [1, 2, 3], + "getValidatorVotesHash": lambda tx_id, round_number: [ + bytes.fromhex("01" * 32), + bytes.fromhex("02" * 32), + bytes.fromhex("03" * 32), + ], + "getValidatorResultHash": lambda tx_id, round_number: [ + bytes.fromhex("11" * 32), + bytes.fromhex("12" * 32), + bytes.fromhex("13" * 32), + ], + } + ) + resolution = ( + TX_HASH, + 5, + 6, + 6, + 1, + bytes(32), + 6, + 0, + 0, + bytes(32), + bytes(32), + 0, + 0, + 0, + 0, + bytes(32), + 0, + 1_000, + ) + latest_decision = (True, 42) + consensus_data = _Contract( + { + "addressManager": lambda: ADDRESS_MANAGER_ADDRESS, + "getTransactionLifecycle": lambda tx_id, timestamp: ( + 5, + resolution, + latest_decision, + True, + ), + "canFinalize": lambda tx_id, timestamp, decision_id: (True, 1_000, 999), + } + ) + contracts = { + CONSENSUS_DATA_ADDRESS: consensus_data, + ADDRESS_MANAGER_ADDRESS: manager, + BIG_ROUNDS_ADDRESS: big_rounds, + TRANSACTION_MANAGER_ADDRESS: transaction_manager, + ROUNDS_STORAGE_ADDRESS: rounds_storage, + } + eth = SimpleNamespace( + block_number=123, + contract=lambda address, abi: contracts[address], + ) + client = SimpleNamespace( + chain=SimpleNamespace( + id=4221, + consensus_data_contract={ + "address": CONSENSUS_DATA_ADDRESS, + "abi": [], + }, + ), + w3=SimpleNamespace(eth=eth), + ) + return client, contracts + + +def test_train_transaction_read_composes_light_and_split_array_surfaces(monkeypatch): + client, contracts = _client_and_contracts() + monkeypatch.setattr(transaction_actions, "TRANSACTION_ARRAY_PAGE_SIZE", 2) + + result = transaction_actions._read_train_transaction_data( + client, + contracts[CONSENSUS_DATA_ADDRESS], + TX_HASH, + 123, + ) + + ( + tx_data, + validators, + votes, + vote_hashes, + result_hashes, + consumed_validators, + execution_result, + num_of_initial_validators, + ) = result + assert tx_data == _light_transaction() + assert validators == VALIDATORS + assert votes == [1, 2, 3] + assert vote_hashes[0] == bytes.fromhex("01" * 32) + assert result_hashes[0] == bytes.fromhex("11" * 32) + assert consumed_validators == CONSUMED_VALIDATORS + assert execution_result == 1 + assert num_of_initial_validators == 5 + assert [ + call[1][2] + for call in contracts[BIG_ROUNDS_ADDRESS].calls + if call[0] == "getRoundValidatorsPaged" + ] == [0, 2] + assert [ + call[1][1] + for call in contracts[BIG_ROUNDS_ADDRESS].calls + if call[0] == "getConsumedValidatorsPaged" + ] == [0] + assert all( + block == 123 + for contract in contracts.values() + for _, _, block in contract.calls + ) + assert not any( + name == "getTransactionAllData" + for contract in contracts.values() + for name, _, _ in contract.calls + ) + + +def test_get_transaction_exposes_only_stored_consumer_lifecycle(monkeypatch): + client, contracts = _client_and_contracts() + monkeypatch.setattr( + transaction_actions, "_decode_triggered_txs", lambda self, tx: [] + ) + + result = transaction_actions.get_transaction(client, TX_HASH) + + assert result["lifecycle"] == {"state": "decided", "outcome": "accepted"} + assert "status" not in result + assert "status_name" not in result + assert "stored_status" not in result + assert "resolution_action" not in result + assert "can_finalize" not in result + assert result["last_round"]["round_validators"] == VALIDATORS + assert result["last_round"]["validator_votes"] == [1, 2, 3] + assert result["last_round"]["validator_result_hash"] == [ + "0x" + "11" * 32, + "0x" + "12" * 32, + "0x" + "13" * 32, + ] + assert result["consumed_validators"] == CONSUMED_VALIDATORS + assert result["tx_execution_hash"] == "0x" + "99" * 32 + assert result["tx_receipt"] is None + assert result["tx_execution_result_name"] == "FINISHED_WITH_RETURN" + assert result["num_of_initial_validators"] == "5" + assert result["initial_rotations"] == "3" + + lifecycle_calls = contracts[CONSENSUS_DATA_ADDRESS].calls + assert not any(call[0] == "getTransactionLifecycle" for call in lifecycle_calls) + assert not any(call[0] == "canFinalize" for call in lifecycle_calls) + + +def test_advanced_transaction_lifecycle_keeps_projection_explicit(): + client, contracts = _client_and_contracts() + + result = transaction_actions.get_transaction_lifecycle(client, TX_HASH) + + assert result == { + "stored_status": 5, + "stored_status_name": ProtocolTransactionStatus.ACCEPTED, + "projected_status": 6, + "projected_status_name": ProtocolTransactionStatus.UNDETERMINED, + "resolution_action": 6, + "resolution_action_name": ResolutionAction.FINALIZE, + "resolution_source": 6, + "resolution_source_name": ResolutionSource.FULL_REVEAL, + "decision_id": "42", + "decision_active": True, + "evaluated_at": 1_000, + } + lifecycle_calls = contracts[CONSENSUS_DATA_ADDRESS].calls + assert ("getTransactionLifecycle", (TX_HASH, 0), 123) in lifecycle_calls + assert not any(call[0] == "canFinalize" for call in lifecycle_calls) + + +def test_local_transaction_lifecycle_decodes_the_exact_node_rpc_schema(): + provider = Mock() + provider.make_request.return_value = { + "result": { + "storedStatus": "Accepted", + "storedStatusCode": 5, + "projectedStatus": "Undetermined", + "projectedStatusCode": 6, + "resolutionAction": "Finalize", + "resolutionActionCode": 6, + "resolutionSource": "FullReveal", + "resolutionSourceCode": 6, + "decisionId": "42", + "decisionActive": True, + "evaluatedAt": 1_000, + } + } + client = SimpleNamespace( + chain=SimpleNamespace(id=localnet.id), + provider=provider, + ) + + result = transaction_actions.get_transaction_lifecycle( + client, bytes.fromhex("ab" * 32), timestamp=999 + ) + + assert result == { + "stored_status": 5, + "stored_status_name": ProtocolTransactionStatus.ACCEPTED, + "projected_status": 6, + "projected_status_name": ProtocolTransactionStatus.UNDETERMINED, + "resolution_action": 6, + "resolution_action_name": ResolutionAction.FINALIZE, + "resolution_source": 6, + "resolution_source_name": ResolutionSource.FULL_REVEAL, + "decision_id": "42", + "decision_active": True, + "evaluated_at": 1_000, + } + provider.make_request.assert_called_once_with( + method="gen_getTransactionLifecycle", + params=[{"txId": TX_HASH, "timestamp": 999}], + ) + + +def test_local_transaction_lifecycle_rejects_code_name_drift(): + provider = Mock() + provider.make_request.return_value = { + "result": { + "storedStatus": "Finalized", + "storedStatusCode": 5, + "projectedStatus": "Undetermined", + "projectedStatusCode": 6, + "resolutionAction": "Finalize", + "resolutionActionCode": 6, + "resolutionSource": "FullReveal", + "resolutionSourceCode": 6, + "decisionId": None, + "decisionActive": False, + "evaluatedAt": 1_000, + } + } + client = SimpleNamespace( + chain=SimpleNamespace(id=localnet.id), + provider=provider, + ) + + with pytest.raises(transaction_actions.GenLayerError, match="storedStatus"): + transaction_actions.get_transaction_lifecycle(client, TX_HASH) + + +def test_local_transaction_lifecycle_uses_stored_status_when_rpc_is_absent(): + provider = Mock() + provider.make_request.side_effect = [ + {"error": {"code": -32601, "message": "Method not found"}}, + {"result": {"status": "ACCEPTED"}}, + ] + client = SimpleNamespace( + chain=SimpleNamespace(id=localnet.id), + provider=provider, + ) + + result = transaction_actions.get_transaction_lifecycle( + client, bytes.fromhex("ab" * 32), timestamp=999 + ) + + assert result == { + "stored_status": 5, + "stored_status_name": ProtocolTransactionStatus.ACCEPTED, + "projected_status": 5, + "projected_status_name": ProtocolTransactionStatus.ACCEPTED, + "resolution_action": 0, + "resolution_action_name": ResolutionAction.NO_OP, + "resolution_source": 0, + "resolution_source_name": ResolutionSource.UNSPECIFIED, + "decision_id": None, + "decision_active": False, + "evaluated_at": 999, + } + assert provider.make_request.call_args_list == [ + call( + method="gen_getTransactionLifecycle", + params=[{"txId": TX_HASH, "timestamp": 999}], + ), + call(method="eth_getTransactionByHash", params=[TX_HASH]), + ] + + +def test_local_transaction_lifecycle_maps_activated_to_pending(): + provider = Mock() + provider.make_request.side_effect = [ + {"error": {"code": -32601, "message": "Method not found"}}, + {"result": {"status": "ACTIVATED"}}, + ] + client = SimpleNamespace( + chain=SimpleNamespace(id=localnet.id), + provider=provider, + ) + + result = transaction_actions.get_transaction_lifecycle(client, TX_HASH) + + assert result["stored_status_name"] == ProtocolTransactionStatus.PENDING + assert result["projected_status_name"] == ProtocolTransactionStatus.PENDING + assert result["decision_active"] is False + + +def test_local_transaction_lifecycle_does_not_hide_real_rpc_failures(): + provider = Mock() + provider.make_request.return_value = { + "error": {"code": -32000, "message": "execution reverted"} + } + client = SimpleNamespace( + chain=SimpleNamespace(id=localnet.id), + provider=provider, + ) + + with pytest.raises(transaction_actions.GenLayerError, match="execution reverted"): + transaction_actions.get_transaction_lifecycle(client, TX_HASH) + + provider.make_request.assert_called_once() + + +def test_packaged_consensus_abis_expose_only_the_train_lifecycle_signature(): + for abi in (CONSENSUS_DATA_ABI, CONSENSUS_DATA_ABI_V06): + functions = { + entry["name"]: entry for entry in abi if entry.get("type") == "function" + } + assert "getTransactionData" not in functions + assert "getStoredTransactionData" in functions + assert "getTransactionLifecycle" in functions + assert len(functions["canFinalize"]["inputs"]) == 3 + transaction_components = functions["getTransactionAllData"]["outputs"][0][ + "components" + ] + assert transaction_components[2]["name"] == "status" + assert transaction_components[-1]["name"] == "queueContext" + + big_round_functions = { + entry["name"] + for entry in CONSENSUS_DATA_BIG_ROUNDS_ABI + if entry.get("type") == "function" + } + + for abi in (CONSENSUS_MAIN_ABI, CONSENSUS_MAIN_ABI_V06): + functions = { + entry["name"]: entry for entry in abi if entry.get("type") == "function" + } + assert [item["type"] for item in functions["addTransaction"]["inputs"]] == [ + "tuple" + ] + assert [item["type"] for item in functions["deploySalted"]["inputs"]] == [ + "tuple" + ] + assert [item["type"] for item in functions["topUpFees"]["inputs"]] == [ + "bytes32", + "tuple", + ] + assert [ + item["type"] for item in functions["topUpAndSubmitAppeal"]["inputs"] + ] == ["bytes32", "uint256", "tuple"] + assert [item["type"] for item in functions["submitAppeal"]["inputs"]] == [ + "bytes32", + "uint256", + ] + assert [ + item["type"] for item in functions["finalizeTransaction"]["inputs"] + ] == ["bytes32", "uint256"] + add_params = functions["addTransaction"]["inputs"][0]["components"] + assert [item["type"] for item in add_params] == [ + "address", + "address", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + "tuple", + "bytes", + "tuple[]", + ] + assert [item["type"] for item in add_params[7]["components"]] == [ + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256[]", + "uint256", + "uint256", + "uint256", + ] + assert [item["type"] for item in add_params[9]["components"]] == [ + "uint8", + "bool", + "uint256", + "address", + "bytes32", + "uint256", + "bytes", + ] + assert big_round_functions == { + "getStoredTransactionDataLight", + "getRoundValidatorsPaged", + "getConsumedValidatorsPaged", + } + + rounds_storage_functions = { + entry["name"] + for entry in ROUNDS_STORAGE_READ_ABI + if entry.get("type") == "function" + } + assert rounds_storage_functions == { + "getValidatorVotes", + "getValidatorVotesHash", + "getValidatorResultHash", + } + + transaction_manager_functions = { + entry["name"] + for entry in TRANSACTION_MANAGER_READ_ABI + if entry.get("type") == "function" + } + assert transaction_manager_functions == { + "getTxExecutionResult", + "getNumOfInitialValidators", + } + + +def test_train_vote_and_execution_enums_cover_every_contract_ordinal(): + expected = { + "0": VoteType.NOT_VOTED, + "1": VoteType.FINISHED_WITH_RETURN, + "2": VoteType.FINISHED_WITH_ERROR, + "3": VoteType.TIMEOUT, + "4": VoteType.NONDET_DISAGREE, + "5": VoteType.DETERMINISTIC_VIOLATION, + } + assert VOTE_TYPE_NUMBER_TO_NAME == expected + assert EXECUTION_RESULT_NUMBER_TO_NAME == { + ordinal: ExecutionResult(member.value) for ordinal, member in expected.items() + } diff --git a/tests/unit/transactions/test_transaction_lifecycle.py b/tests/unit/transactions/test_transaction_lifecycle.py new file mode 100644 index 0000000..7efba8b --- /dev/null +++ b/tests/unit/transactions/test_transaction_lifecycle.py @@ -0,0 +1,124 @@ +import genlayer_py +import genlayer_py.types as public_types +import pytest +from types import SimpleNamespace + +from genlayer_py.chains import localnet +from genlayer_py.transactions.actions import get_transaction +from genlayer_py.types.transactions import ( + CanceledTransactionLifecycle, + DecidedTransactionLifecycle, + FinalizedTransactionLifecycle, + ProcessingTransactionLifecycle, + PROTOCOL_TRANSACTION_STATUS_NUMBER_TO_NAME, + ProtocolTransactionStatus, + ResolutionAction, + ResolutionSource, + transaction_lifecycle_from_protocol_status, + transaction_outcome_from_protocol_result, +) + +EXPECTED_LIFECYCLES = { + 0: {"state": "processing", "phase": "uninitialized"}, + 1: {"state": "processing", "phase": "pending"}, + 2: {"state": "processing", "phase": "proposing"}, + 3: {"state": "processing", "phase": "committing"}, + 4: {"state": "processing", "phase": "revealing"}, + 5: {"state": "decided", "outcome": "accepted"}, + 6: {"state": "decided", "outcome": "undetermined"}, + 7: {"state": "finalized"}, + 8: {"state": "canceled"}, + 9: {"state": "processing", "phase": "appeal_revealing"}, + 10: {"state": "processing", "phase": "appeal_committing"}, + 11: {"state": "decided", "outcome": "validators_timeout"}, + 12: {"state": "decided", "outcome": "leader_timeout"}, + 13: {"state": "processing", "phase": "leader_revealing"}, +} + + +@pytest.mark.parametrize("protocol_status, expected", EXPECTED_LIFECYCLES.items()) +def test_every_protocol_status_has_one_public_lifecycle(protocol_status, expected): + assert transaction_lifecycle_from_protocol_status(protocol_status) == expected + assert transaction_lifecycle_from_protocol_status(str(protocol_status)) == expected + assert ( + transaction_lifecycle_from_protocol_status( + PROTOCOL_TRANSACTION_STATUS_NUMBER_TO_NAME[str(protocol_status)] + ) + == expected + ) + + +@pytest.mark.parametrize("value", [-1, 14, "unknown"]) +def test_unknown_or_removed_protocol_status_is_rejected(value): + with pytest.raises(ValueError, match="Unknown protocol transaction status"): + transaction_lifecycle_from_protocol_status(value) + + +def test_public_lifecycle_is_a_discriminated_union(): + assert ProcessingTransactionLifecycle.__required_keys__ == {"state", "phase"} + assert DecidedTransactionLifecycle.__required_keys__ == {"state", "outcome"} + assert FinalizedTransactionLifecycle.__required_keys__ == {"state"} + assert FinalizedTransactionLifecycle.__optional_keys__ == {"outcome"} + assert CanceledTransactionLifecycle.__required_keys__ == {"state"} + + +@pytest.mark.parametrize( + "protocol_result, expected", + [ + (0, None), + (1, "accepted"), + (2, "undetermined"), + (3, "validators_timeout"), + (4, "undetermined"), + (5, "undetermined"), + ], +) +def test_finalized_outcome_is_added_only_when_the_result_proves_it( + protocol_result, expected +): + assert transaction_outcome_from_protocol_result(protocol_result) == expected + + +def test_raw_protocol_types_are_advanced_only(): + assert not hasattr(genlayer_py, "ResolutionAction") + assert not hasattr(genlayer_py, "ProtocolTransactionStatus") + assert not hasattr(public_types, "ResolutionAction") + assert not hasattr(public_types, "ResolutionSource") + assert not hasattr(public_types, "ProtocolTransactionStatus") + assert len(ProtocolTransactionStatus) == 14 + assert len(ResolutionSource) == 12 + assert ResolutionAction.FINALIZE.value == "Finalize" + + +@pytest.mark.parametrize( + "raw_transaction, expected", + [ + ( + {"status": "ACTIVATED", "data": None}, + {"state": "processing", "phase": "pending"}, + ), + ( + { + "status": "FINALIZED", + "result_name": "MAJORITY_AGREE", + "data": None, + }, + {"state": "finalized", "outcome": "accepted"}, + ), + ], +) +def test_local_transaction_runtime_exposes_only_public_lifecycle( + raw_transaction, expected +): + client = SimpleNamespace( + chain=SimpleNamespace(id=localnet.id), + provider=SimpleNamespace( + make_request=lambda method, params: {"result": dict(raw_transaction)} + ), + ) + + transaction = get_transaction(client, "0x" + "ab" * 32) + + assert transaction["lifecycle"] == expected + assert "status" not in transaction + assert "status_name" not in transaction diff --git a/tests/unit/transactions/test_wait_for_transaction_receipt.py b/tests/unit/transactions/test_wait_for_transaction_receipt.py index 6a3a7f0..48d4518 100644 --- a/tests/unit/transactions/test_wait_for_transaction_receipt.py +++ b/tests/unit/transactions/test_wait_for_transaction_receipt.py @@ -1,299 +1,159 @@ import pytest from unittest.mock import patch from genlayer_py.transactions.actions import ( + wait_for_decision, + wait_for_finalization, wait_for_transaction_receipt, _simplify_transaction_receipt, is_successful, ) -from genlayer_py.types import ( - ExecutionResult, - TransactionStatus, - DECIDED_STATES, - TRANSACTION_STATUS_NUMBER_TO_NAME, - is_decided_state, -) +from genlayer_py.types import ExecutionResult from genlayer_py.exceptions import GenLayerError +TX_HASH = "0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6" -class TestWaitForTransactionReceipt: - """Test suite for wait_for_transaction_receipt function""" - def test_wait_for_finalized_transaction_success( - self, mock_client, full_write_transaction_data - ): - """Test successful wait for finalized transaction""" - mock_client.get_transaction.return_value = full_write_transaction_data - - result = wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - status=TransactionStatus.FINALIZED, - full_transaction=True, - ) +def _transaction(lifecycle, **extra): + return {"hash": TX_HASH, "lifecycle": lifecycle, **extra} - assert result == full_write_transaction_data - mock_client.get_transaction.assert_called_once() - def test_wait_for_accepted_transaction_with_finalized_status( - self, mock_client, full_write_transaction_data +class TestTransactionWaits: + @pytest.mark.parametrize( + "lifecycle", + [ + {"state": "decided", "outcome": "accepted"}, + {"state": "decided", "outcome": "undetermined"}, + {"state": "decided", "outcome": "validators_timeout"}, + {"state": "decided", "outcome": "leader_timeout"}, + {"state": "finalized"}, + {"state": "canceled"}, + ], + ) + def test_wait_for_decision_uses_materialized_lifecycle( + self, mock_client, lifecycle ): - """Test that ACCEPTED status accepts FINALIZED transactions""" - mock_client.get_transaction.return_value = full_write_transaction_data - - result = wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - status=TransactionStatus.ACCEPTED, # Requesting ACCEPTED - full_transaction=True, + transaction = _transaction(lifecycle) + mock_client.get_transaction.return_value = transaction + + assert ( + wait_for_decision(mock_client, TX_HASH, full_transaction=True) + == transaction ) - # Should accept FINALIZED (status 7) when requesting ACCEPTED - assert result == full_write_transaction_data + def test_wait_for_finalization_ignores_nonfinal_stored_state(self, mock_client): + processing = _transaction({"state": "processing", "phase": "pending"}) + decided = _transaction({"state": "decided", "outcome": "accepted"}) + finalized = _transaction({"state": "finalized"}) + mock_client.get_transaction.side_effect = [processing, decided, finalized] - def test_wait_for_transaction_with_simplified_receipt( - self, mock_client, full_write_transaction_data - ): - """Test wait for transaction with simplified receipt (full_transaction=False)""" - mock_client.get_transaction.return_value = full_write_transaction_data - - with patch( - "genlayer_py.transactions.actions._simplify_transaction_receipt" - ) as mock_simplify: - simplified_data = {"hash": "0x123", "status": 7, "simplified": True} - mock_simplify.return_value = simplified_data - - result = wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - full_transaction=False, + with patch("time.sleep") as sleep: + result = wait_for_finalization( + mock_client, TX_HASH, interval=100, full_transaction=True ) - mock_simplify.assert_called_once_with(full_write_transaction_data) - assert result == simplified_data + assert result == finalized + assert mock_client.get_transaction.call_count == 3 + assert sleep.call_count == 2 + sleep.assert_called_with(0.1) - def test_wait_for_transaction_timeout(self, mock_client, pending_transaction_data): - """Test timeout when transaction doesn't reach desired status""" - mock_client.get_transaction.return_value = pending_transaction_data + def test_receipt_default_waits_for_decision_and_simplifies(self, mock_client): + transaction = _transaction({"state": "decided", "outcome": "accepted"}) + mock_client.get_transaction.return_value = transaction + simplified = {"hash": TX_HASH, "lifecycle": transaction["lifecycle"]} - with pytest.raises(GenLayerError, match="did not reach desired status"): - wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - retries=2, - interval=1, # 1ms for fast test - ) + with patch( + "genlayer_py.transactions.actions._simplify_transaction_receipt", + return_value=simplified, + ) as simplify: + result = wait_for_transaction_receipt(mock_client, TX_HASH) - # Should have tried 2 times - assert mock_client.get_transaction.call_count == 2 + assert result == simplified + simplify.assert_called_once_with(transaction) - def test_wait_for_nonexistent_transaction(self, mock_client): - """Test error when transaction doesn't exist""" - mock_client.get_transaction.return_value = None + def test_receipt_can_wait_for_finalization(self, mock_client): + transaction = _transaction({"state": "finalized"}) + mock_client.get_transaction.return_value = transaction - with pytest.raises(GenLayerError, match="Transaction .* not found"): + assert ( wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", + mock_client, + TX_HASH, + wait_until="finalized", + full_transaction=True, ) + == transaction + ) - @patch("time.sleep") - def test_wait_for_transaction_with_retry_logic( - self, - mock_sleep, - mock_client, - pending_transaction_data, - full_write_transaction_data, - ): - """Test retry logic with eventual success""" - # First two calls return pending, third returns finalized - mock_client.get_transaction.side_effect = [ - pending_transaction_data, - pending_transaction_data, - full_write_transaction_data, - ] - - result = wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - interval=100, # 100ms - full_transaction=True, + def test_processing_timeout_reports_public_phase(self, mock_client): + mock_client.get_transaction.return_value = _transaction( + {"state": "processing", "phase": "leader_revealing"} ) - assert result == full_write_transaction_data - assert mock_client.get_transaction.call_count == 3 - assert mock_sleep.call_count == 2 - mock_sleep.assert_called_with(0.1) # 100ms / 1000 + with pytest.raises( + GenLayerError, match="Last observed lifecycle state: 'processing'" + ): + wait_for_decision(mock_client, TX_HASH, retries=1, interval=1) - def test_wait_for_transaction_with_custom_parameters( - self, mock_client, full_write_transaction_data - ): - """Test with custom interval and retries""" - mock_client.get_transaction.return_value = full_write_transaction_data - - result = wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - status=TransactionStatus.FINALIZED, - interval=500, - retries=10, - full_transaction=True, - ) + def test_wait_for_finalization_fails_fast_when_canceled(self, mock_client): + mock_client.get_transaction.return_value = _transaction({"state": "canceled"}) - assert result == full_write_transaction_data - - def test_wait_for_accepted_with_all_decided_states(self, mock_client): - """Test that ACCEPTED status accepts all decided states""" - decided_statuses = ["5", "6", "8", "7", "12", "13"] # ACCEPTED, UNDETERMINED, CANCELED, FINALIZED, VALIDATORS_TIMEOUT, LEADER_TIMEOUT - - for status_num in decided_statuses: - mock_transaction = { - "hash": "0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - "status": status_num, - "status_name": "test_status", - "from_address": "0x123", - "to_address": "0x456", - "value": "0", - "gaslimit": "1000000", - "nonce": "1", - "created_at": "2023-01-01T00:00:00Z", - } - - mock_client.get_transaction.return_value = mock_transaction - - result = wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - status=TransactionStatus.ACCEPTED, - full_transaction=True, - ) - - assert result == mock_transaction - - def test_wait_until_decided_resolves_on_undetermined(self, mock_client): - mock_transaction = { - "hash": "0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - "status": "6", - "status_name": "UNDETERMINED", - } - mock_client.get_transaction.return_value = mock_transaction + with pytest.raises(GenLayerError, match="canceled before finalization"): + wait_for_finalization(mock_client, TX_HASH) - result = wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - wait_until="decided", - full_transaction=True, - ) + mock_client.get_transaction.assert_called_once() - assert result == mock_transaction + def test_nonexistent_transaction(self, mock_client): + mock_client.get_transaction.return_value = None - def test_status_14_does_not_crash_wait_loop(self, mock_client): - mock_transaction = { - "hash": "0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - "status": "14", - "status_name": "LEADER_REVEALING", - } - mock_client.get_transaction.return_value = mock_transaction + with pytest.raises(GenLayerError, match="Transaction .* not found"): + wait_for_decision(mock_client, TX_HASH) - with pytest.raises(GenLayerError, match="LEADER_REVEALING"): - wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - wait_until="finalized", - retries=1, - interval=1, - full_transaction=True, - ) + def test_invalid_lifecycle_is_rejected(self, mock_client): + mock_client.get_transaction.return_value = {"hash": TX_HASH} - assert TRANSACTION_STATUS_NUMBER_TO_NAME["14"] == TransactionStatus.LEADER_REVEALING - - def test_wait_for_specific_status_not_affected(self, mock_client): - """Test that waiting for specific non-ACCEPTED statuses is not affected by decided states logic""" - mock_transaction = { - "hash": "0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - "status": "7", # FINALIZED - "status_name": "FINALIZED", - "from_address": "0x123", - "to_address": "0x456", - "value": "0", - "gaslimit": "1000000", - "nonce": "1", - "created_at": "2023-01-01T00:00:00Z", - } - - mock_client.get_transaction.return_value = mock_transaction - - result = wait_for_transaction_receipt( - self=mock_client, - transaction_hash="0x4b8037744adab7ea8335b4f839979d20031d83a8ccdf706e0ae61312930335f6", - status=TransactionStatus.FINALIZED, - full_transaction=True, - ) - - assert result == mock_transaction - - -class TestDecidedStatesUtility: - """Test suite for DECIDED_STATES constant and is_decided_state function""" - - def test_decided_states_constant(self): - """Test that DECIDED_STATES contains all expected states""" - expected_states = [ - TransactionStatus.ACCEPTED, - TransactionStatus.UNDETERMINED, - TransactionStatus.LEADER_TIMEOUT, - TransactionStatus.VALIDATORS_TIMEOUT, - TransactionStatus.CANCELED, - TransactionStatus.FINALIZED - ] - - assert DECIDED_STATES == expected_states - - def test_is_decided_state_with_decided_statuses(self): - """Test is_decided_state returns True for all decided statuses""" - decided_status_numbers = ["5", "6", "8", "7", "12", "13"] # ACCEPTED, UNDETERMINED, CANCELED, FINALIZED, VALIDATORS_TIMEOUT, LEADER_TIMEOUT - - for status_num in decided_status_numbers: - assert is_decided_state(status_num) == True, f"Status {status_num} should be decided" - - def test_is_decided_state_with_non_decided_statuses(self): - """Test is_decided_state returns False for non-decided statuses""" - non_decided_status_numbers = ["0", "1", "2", "3", "4", "9", "10", "11"] # UNINITIALIZED, PENDING, PROPOSING, COMMITTING, REVEALING, APPEAL_REVEALING, APPEAL_COMMITTING, READY_TO_FINALIZE - - for status_num in non_decided_status_numbers: - assert is_decided_state(status_num) == False, f"Status {status_num} should not be decided" - - def test_is_decided_state_with_invalid_status(self): - """Test is_decided_state returns False for invalid statuses""" - invalid_statuses = ["999", "invalid", "", None] - - for status in invalid_statuses: - if status is not None: - assert is_decided_state(status) == False, f"Invalid status {status} should not be decided" + with pytest.raises(GenLayerError, match="has no valid lifecycle"): + wait_for_decision(mock_client, TX_HASH) + + def test_invalid_receipt_wait_target_is_rejected(self, mock_client): + with pytest.raises(ValueError, match="wait_until"): + wait_for_transaction_receipt(mock_client, TX_HASH, wait_until="projected") class TestIsSuccessful: def test_truth_table(self): assert is_successful( { - "status": "5", + "lifecycle": {"state": "decided", "outcome": "accepted"}, "tx_execution_result": "1", } ) assert not is_successful( { - "status": "6", + "lifecycle": {"state": "decided", "outcome": "undetermined"}, "tx_execution_result": "1", } ) assert not is_successful( { - "status": TransactionStatus.ACCEPTED, + "lifecycle": {"state": "decided", "outcome": "accepted"}, "tx_execution_result": "2", } ) assert is_successful( { - "status_name": TransactionStatus.FINALIZED, + "lifecycle": {"state": "finalized"}, + "tx_execution_result_name": ExecutionResult.FINISHED_WITH_RETURN, + } + ) + assert is_successful( + { + "lifecycle": {"state": "finalized", "outcome": "accepted"}, + "tx_execution_result_name": ExecutionResult.FINISHED_WITH_RETURN, + } + ) + assert not is_successful( + { + "lifecycle": {"state": "finalized", "outcome": "undetermined"}, "tx_execution_result_name": ExecutionResult.FINISHED_WITH_RETURN, } ) @@ -337,8 +197,7 @@ def test_simplify_preserves_essential_fields(self, full_write_transaction_data): # These fields should be preserved essential_fields = [ "hash", - "status", - "status_name", + "lifecycle", "from_address", "to_address", "value", diff --git a/uv.lock b/uv.lock index f958102..392c1a0 100644 --- a/uv.lock +++ b/uv.lock @@ -434,7 +434,7 @@ wheels = [ [[package]] name = "genlayer-py" -version = "0.18.0" +version = "0.19.0" source = { editable = "." } dependencies = [ { name = "web3" }, From b88e4929f31587cb3658174ed175f906c2e0fc05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Thu, 3 Sep 2026 00:25:22 +0100 Subject: [PATCH 15/19] fix(fees): add exact quote and appeal parity (#110) * fix(fees): mirror consensus deposit quote * fix(studio): bind appeal actions to decisions Use Studio's authoritative appeal quote for omitted payment and decision inputs, encode the v0.6 decision-bound selectors, and expose matching can-appeal behavior without attempting EVM contract reads. * fix(fees): encode internal message price caps * fix(studio): surface mined envelope reverts * fix(fees): encode schedule-free topups * fix(appeals): admit unfunded rounds safely Route the public appeal action through topUpAndSubmitAppeal, whose consensus-derived schedule extension accepts both pre-funded and unfunded rounds. Preserve direct submitAppeal encoding only as a low-level conformance surface.\n\nCover the exact selector, decision binding, compatibility tuple, quoted value, and caller-provided value on deployed Consensus and Studio. --- genlayer_py/client/genlayer_client.py | 25 +- genlayer_py/contracts/actions.py | 179 +++++----- genlayer_py/transactions/__init__.py | 3 + genlayer_py/transactions/fees.py | 155 ++++++-- tests/unit/contracts/test_contract_actions.py | 338 ++++++++++++++---- 5 files changed, 510 insertions(+), 190 deletions(-) diff --git a/genlayer_py/client/genlayer_client.py b/genlayer_py/client/genlayer_client.py index 77392ca..9622052 100644 --- a/genlayer_py/client/genlayer_client.py +++ b/genlayer_py/client/genlayer_client.py @@ -341,9 +341,8 @@ def top_up_and_submit_appeal( ) -> HexStr: """Deposits appeal funding and submits an appeal. - On deployed Consensus, omitted decision/value inputs are resolved from - the authoritative appeal quote. Current Studio requires an explicit - value and does not accept ``expected_decision_id``. + Omitted decision/value inputs are resolved from the authoritative + appeal quote on both Studio and deployed Consensus. """ return top_up_and_submit_appeal( self=self, @@ -463,9 +462,8 @@ def appeal_transaction( ): """Appeals a consensus transaction to trigger a new round of validation. Returns the original transaction_id (appeals operate on the same tx). - Deployed Consensus fills missing decision/value inputs from its - authoritative quote. Current Studio requires an explicit value and does - not accept ``expected_decision_id``. + Missing decision/value inputs are filled from the authoritative quote + on both Studio and deployed Consensus. """ return appeal_transaction( self=self, @@ -492,10 +490,7 @@ def can_appeal( transaction_id: HexStr, expected_decision_id: Optional[int] = None, ) -> bool: - """Checks whether the exact active decision can be appealed on a network. - - This decision-bound read is not available on current Studio. - """ + """Checks whether the exact active decision can be appealed.""" return can_appeal( self=self, transaction_id=transaction_id, @@ -503,17 +498,11 @@ def can_appeal( ) def get_appeal_quote(self, transaction_id: HexStr) -> Dict[str, int]: - """Returns a network's latest decision id, appeal charges, and deadline. - - Current Studio has no decision-bound quote surface. - """ + """Returns the latest decision id, appeal charges, and deadline.""" return get_appeal_quote(self=self, transaction_id=transaction_id) def get_appeal_charge(self, transaction_id: HexStr) -> int: - """Returns the full appeal payment (bond plus induced-work funding). - - Current Studio has no decision-bound quote surface. - """ + """Returns the full appeal payment (bond plus induced-work funding).""" return get_appeal_charge(self=self, transaction_id=transaction_id) def get_min_appeal_bond(self, transaction_id: HexStr) -> int: diff --git a/genlayer_py/contracts/actions.py b/genlayer_py/contracts/actions.py index a704421..23661c8 100644 --- a/genlayer_py/contracts/actions.py +++ b/genlayer_py/contracts/actions.py @@ -39,6 +39,7 @@ build_estimated_fees_options_from_simulation, calculate_local_round_fees, create_fees_distribution, + create_top_up_fees_distribution, encode_fee_aware_add_transaction_data, extract_studio_fee_policy, fees_distribution_to_abi_tuple, @@ -233,31 +234,18 @@ def appeal_transaction( Appeals emit AppealStarted/TransactionActivated events (not NewTransaction), so we send the EVM tx directly instead of going through _send_transaction. - Deployed Consensus binds the appeal to the exact active decision. Current - Studio exposes its native ``submitAppeal(bytes32)`` entrypoint instead, so - callers must provide the value explicitly and cannot supply a decision id. + Both Studio and deployed Consensus bind the appeal to the exact active + decision and can resolve omitted decision/value inputs from the + authoritative appeal quote. The schedule-extending entry point is used for + every appeal because it accepts both pre-funded and unfunded next rounds; + ``submitAppeal`` rejects an unfunded next round before collecting its quoted + funding. """ sender_account = account if account is not None else self.local_account if sender_account is None: raise GenLayerError("No account set.") if self.chain.consensus_main_contract is None: raise GenLayerError("Consensus main contract not configured.") - if _is_studio_chain(self): - resolved_value = _resolve_studio_appeal_value(value, expected_decision_id) - encoded_data = _encode_submit_appeal_data( - self=self, - transaction_id=transaction_id, - studio_appeal_shape=True, - ) - _send_consensus_call( - self=self, - encoded_data=encoded_data, - sender_account=sender_account, - value=resolved_value, - operation_name="Appeal", - ) - return transaction_id - expected_decision_id, resolved_value = _resolve_appeal_parameters( self, transaction_id, @@ -265,9 +253,13 @@ def appeal_transaction( value=value, ) - encoded_data = _encode_submit_appeal_data( + encoded_data = _encode_fee_management_data( self=self, + function_name="topUpAndSubmitAppeal", transaction_id=transaction_id, + # Consensus derives the appeal shape from live state. This normalized + # zero schedule exists only for ABI compatibility. + distribution={}, expected_decision_id=expected_decision_id, ) @@ -291,8 +283,7 @@ def top_up_fees( ) -> HexStr: """Deposits additional fee budget for an existing consensus transaction. - Returns the backend RPC hash: an EVM transaction hash on network backends, - or the target GenLayer tx id on Studio/localnet. + Returns the signed EVM envelope hash on every backend. """ sender_account = account if account is not None else self.local_account encoded_data = _encode_fee_management_data( @@ -321,28 +312,9 @@ def top_up_and_submit_appeal( """Deposits appeal fee budget and submits an appeal in one consensus call. Returns the original GenLayer transaction id, matching appeal_transaction. - Current Studio exposes the native decision-free call shape. Deployed - Consensus uses the decision-bound train shape. + Both Studio and deployed Consensus use the decision-bound train shape. """ sender_account = account if account is not None else self.local_account - if _is_studio_chain(self): - resolved_value = _resolve_studio_appeal_value(value, expected_decision_id) - encoded_data = _encode_fee_management_data( - self=self, - function_name="topUpAndSubmitAppeal", - transaction_id=transaction_id, - distribution=distribution, - studio_appeal_shape=True, - ) - _send_consensus_call( - self=self, - encoded_data=encoded_data, - sender_account=sender_account, - value=resolved_value, - operation_name="Top up and submit appeal", - ) - return transaction_id - expected_decision_id, resolved_value = _resolve_appeal_parameters( self, transaction_id, @@ -427,8 +399,28 @@ def can_appeal( When no decision id is supplied, the latest active decision is read first. The guarded on-chain call returns ``False`` if that decision changes before - it is evaluated. Current Studio does not expose this decision-bound read. + it is evaluated. Studio uses its lifecycle and appeal-quote RPCs for the + same semantics. """ + if _is_studio_chain(self): + lifecycle = self.get_transaction_lifecycle(transaction_id) + if not lifecycle["decision_active"]: + return False + active_decision_id = lifecycle["decision_id"] + if ( + expected_decision_id is not None + and expected_decision_id != active_decision_id + ): + return False + try: + return get_appeal_quote(self, transaction_id)["decision_id"] == int( + active_decision_id + ) + except Exception as exc: + if "CanNotAppeal" in str(exc): + return False + raise + if self.chain.appeals_contract is None: raise GenLayerError("appeals_contract not configured for this chain") if expected_decision_id is None: @@ -448,10 +440,35 @@ def get_appeal_quote( ``total`` is the value to submit: the appeal bond plus induced-work funding. Pass ``decision_id`` back to the decision-guarded appeal methods. - Current Studio exposes no authoritative decision-bound appeal quote. """ if _is_studio_chain(self): - raise GenLayerError(STUDIO_APPEAL_QUOTE_UNSUPPORTED) + response = self.provider.make_request( + method="gen_estimateLatestAppealCharge", + params=[{"txId": transaction_id}], + ) + if not isinstance(response, dict): + raise GenLayerError( + "gen_estimateLatestAppealCharge returned an invalid response" + ) + if response.get("error") is not None: + raise GenLayerError( + f"gen_estimateLatestAppealCharge failed: {response['error']}" + ) + quote = response.get("result") + if not isinstance(quote, dict): + raise GenLayerError( + "gen_estimateLatestAppealCharge returned an invalid result" + ) + decision_id = int(quote["decisionId"]) + bond = int(quote["bond"]) + funding = int(quote["funding"]) + return { + "decision_id": decision_id, + "bond": bond, + "funding": funding, + "total": bond + funding, + "appeal_deadline": int(quote["appealDeadline"]), + } contract = _consensus_data_contract(self) decision_id, bond, funding, appeal_deadline = ( contract.functions.estimateLatestAppealCharge( @@ -552,15 +569,6 @@ def _resolve_appeal_parameters( ) -STUDIO_APPEAL_QUOTE_UNSUPPORTED = ( - "Decision-bound appeal quotes are not available on current Studio." -) -STUDIO_APPEAL_VALUE_UNRESOLVABLE = ( - "Cannot auto-resolve the appeal payment on current Studio; pass value explicitly." -) -STUDIO_DECISION_GUARD_UNSUPPORTED = "expected_decision_id is not supported by current Studio's decision-free appeal methods." - - def _is_studio_chain(self: GenLayerClient) -> bool: """Reports whether the client targets the studio-embedded consensus. @@ -570,18 +578,6 @@ def _is_studio_chain(self: GenLayerClient) -> bool: return self.chain.id == localnet.id -def _resolve_studio_appeal_value( - value: Optional[int], - expected_decision_id: Optional[int], -) -> int: - """Require the inputs that current Studio can faithfully honor.""" - if expected_decision_id is not None: - raise GenLayerError(STUDIO_DECISION_GUARD_UNSUPPORTED) - if value is None: - raise GenLayerError(STUDIO_APPEAL_VALUE_UNRESOLVABLE) - return value - - def _to_bytes32(self: GenLayerClient, hex_str: HexStr) -> bytes: """Convert a hex string to bytes32.""" if hex_str.startswith("0x"): @@ -666,9 +662,8 @@ def _encode_submit_appeal_data( self: GenLayerClient, transaction_id: HexStr, expected_decision_id: Optional[int] = None, - studio_appeal_shape: bool = False, ): - """Encode the chain's native submitAppeal entrypoint.""" + """Encode the decision-bound submitAppeal entrypoint.""" consensus_main_contract = self.w3.eth.contract( abi=self.chain.consensus_main_contract["abi"] ) @@ -677,11 +672,9 @@ def _encode_submit_appeal_data( transaction_id = transaction_id[2:] if len(transaction_id) > 64: raise ValueError("transaction_id too long for bytes32") - arguments = [self.w3.to_bytes(hexstr=transaction_id)] - if not studio_appeal_shape: - if expected_decision_id is None: - raise ValueError("submitAppeal requires expected_decision_id") - arguments.append(expected_decision_id) + if expected_decision_id is None: + raise ValueError("submitAppeal requires expected_decision_id") + arguments = [self.w3.to_bytes(hexstr=transaction_id), expected_decision_id] params = abi_encode(contract_fn.argument_types, arguments) function_selector = eth_utils.keccak(text=contract_fn.signature)[:4].hex() encoded_data = "0x" + function_selector + params.hex() @@ -702,16 +695,19 @@ def _encode_fee_management_data( transaction_id: HexStr, distribution: FeesDistributionInput, expected_decision_id: Optional[int] = None, - studio_appeal_shape: bool = False, ): """Encode the chain's native fee-management entrypoint.""" if function_name not in ("topUpFees", "topUpAndSubmitAppeal"): raise ValueError(f"Unsupported fee management function: {function_name}") tx_bytes = _to_bytes32(self, transaction_id) - fees_distribution = create_fees_distribution(distribution) + fees_distribution = ( + create_top_up_fees_distribution(distribution) + if function_name == "topUpFees" + else create_fees_distribution(distribution) + ) fees_tuple = fees_distribution_to_abi_tuple(fees_distribution) - if function_name == "topUpAndSubmitAppeal" and not studio_appeal_shape: + if function_name == "topUpAndSubmitAppeal": if expected_decision_id is None: raise ValueError("topUpAndSubmitAppeal requires expected_decision_id") argument_types = TOP_UP_AND_SUBMIT_APPEAL_ARGUMENT_TYPES @@ -852,6 +848,9 @@ def get_current_fee_policy(self: GenLayerClient) -> FeePolicyQuote: execution_budget_floor, local_execution_budget_floor, ), + # Live networks quote through FeeManager.calculateRoundFees; this + # field is only consumed by Studio's local mirror. + "timeUnitOverlayBps": 0, } try: @@ -1218,6 +1217,23 @@ def _format_rpc_error(error: Exception) -> str: return text +def _receipt_revert_reason(self: GenLayerClient, tx_hash: HexStr) -> Optional[str]: + """Read Studio's additive receipt reason without weakening EVM semantics.""" + try: + response = self.provider.make_request( + method="eth_getTransactionReceipt", params=[tx_hash] + ) + except Exception: + return None + if not isinstance(response, dict): + return None + receipt = response.get("result") + if not isinstance(receipt, dict): + return None + reason = receipt.get("revertReason") or receipt.get("error") + return reason if isinstance(reason, str) and reason.strip() else None + + def _send_consensus_call( self: GenLayerClient, encoded_data: HexStr, @@ -1249,13 +1265,12 @@ def _send_consensus_call( raise GenLayerError( f"{operation_name} failed: {_format_rpc_error(exc)}" ) from exc - if self.chain.id == localnet.id: - return tx_hash - tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash) if tx_receipt.status != 1: - raise GenLayerError(f"{operation_name} reverted: EVM tx {tx_hash}") + reason = _receipt_revert_reason(self, tx_hash) + suffix = f". {reason}" if reason else "" + raise GenLayerError(f"{operation_name} reverted: EVM tx {tx_hash}{suffix}") return tx_hash @@ -1298,9 +1313,11 @@ def _send_transaction( tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash) if tx_receipt.status != 1: + reason = _receipt_revert_reason(self, tx_hash) + suffix = f" {reason}" if reason else "" raise GenLayerError( f"Transaction reverted: EVM tx {tx_hash} to consensus contract " - f"{self.chain.consensus_main_contract['address']} was reverted." + f"{self.chain.consensus_main_contract['address']} was reverted.{suffix}" ) consensus_main_contract = self.w3.eth.contract( diff --git a/genlayer_py/transactions/__init__.py b/genlayer_py/transactions/__init__.py index ee9b531..4f215f6 100644 --- a/genlayer_py/transactions/__init__.py +++ b/genlayer_py/transactions/__init__.py @@ -9,6 +9,7 @@ build_estimated_fees_distribution, calculate_local_round_fees, create_fees_distribution, + create_top_up_fees_distribution, derive_external_message_call_key, deploy_call_key, derive_internal_message_call_key, @@ -23,6 +24,7 @@ def is_successful(transaction): return _is_successful(transaction) + __all__ = [ "CALL_KEY_DEPLOY", "CALL_KEY_UNNAMED", @@ -34,6 +36,7 @@ def is_successful(transaction): "build_estimated_fees_distribution", "calculate_local_round_fees", "create_fees_distribution", + "create_top_up_fees_distribution", "derive_external_message_call_key", "deploy_call_key", "derive_internal_message_call_key", diff --git a/genlayer_py/transactions/fees.py b/genlayer_py/transactions/fees.py index 59037c9..1831ddb 100644 --- a/genlayer_py/transactions/fees.py +++ b/genlayer_py/transactions/fees.py @@ -1,7 +1,7 @@ from __future__ import annotations from enum import IntEnum -from typing import Any, Optional, TypedDict, Union +from typing import Any, NotRequired, Optional, TypedDict, Union from eth_abi import encode as abi_encode from eth_typing import HexStr @@ -62,6 +62,12 @@ class InternalMessageFeeParamsInput(TypedDict, total=False): executionBudgetPerRound: BigNumberish execution_budget_per_round: BigNumberish rotations: list[BigNumberish] + maxPriceGenPerTimeUnit: BigNumberish + max_price_gen_per_time_unit: BigNumberish + storageFeeMaxGasPrice: BigNumberish + storage_fee_max_gas_price: BigNumberish + receiptFeeMaxGasPrice: BigNumberish + receipt_fee_max_gas_price: BigNumberish class ExternalMessageFeeParamsInput(TypedDict, total=False): @@ -110,6 +116,7 @@ class FeePolicyQuote(TypedDict): storageUnitPrice: int receiptGasPrice: int executionBudgetFloor: int + timeUnitOverlayBps: NotRequired[int] class FeeEstimateOptions(FeesDistributionInput, total=False): @@ -356,13 +363,11 @@ def _normalize_rotations( return normalized -def create_fees_distribution( - fee_distribution: Optional[FeesDistributionInput] = None, +def _create_fees_distribution( + fee_distribution: Optional[FeesDistributionInput], + appeal_rounds: int, + rotations: list[int], ) -> FeesDistribution: - appeal_rounds = to_uint( - _get(fee_distribution, "appealRounds", "appeal_rounds"), - "fees.distribution.appealRounds", - ) return { "leaderTimeunitsAllocation": to_uint( _get( @@ -397,11 +402,7 @@ def create_fees_distribution( _get(fee_distribution, "totalMessageFees", "total_message_fees"), "fees.distribution.totalMessageFees", ), - "rotations": _normalize_rotations( - _get(fee_distribution, "rotations"), - appeal_rounds, - "fees.distribution.rotations", - ), + "rotations": rotations, "maxPriceGenPerTimeUnit": to_uint( _get( fee_distribution, @@ -429,6 +430,50 @@ def create_fees_distribution( } +def create_fees_distribution( + fee_distribution: Optional[FeesDistributionInput] = None, +) -> FeesDistribution: + appeal_rounds = to_uint( + _get(fee_distribution, "appealRounds", "appeal_rounds"), + "fees.distribution.appealRounds", + ) + rotations = _normalize_rotations( + _get(fee_distribution, "rotations"), + appeal_rounds, + "fees.distribution.rotations", + ) + return _create_fees_distribution(fee_distribution, appeal_rounds, rotations) + + +def create_top_up_fees_distribution( + fee_distribution: Optional[FeesDistributionInput] = None, +) -> FeesDistribution: + """Normalize a Consensus top-up delta without resubmitting its schedule. + + Existing fee-aware transactions use appealRounds=0/rotations=[]. An + explicit non-empty schedule remains valid for first-time fee initialization. + """ + appeal_rounds = to_uint( + _get(fee_distribution, "appealRounds", "appeal_rounds"), + "fees.distribution.appealRounds", + ) + raw_rotations = _get(fee_distribution, "rotations") + if raw_rotations is None or len(raw_rotations) == 0: + if appeal_rounds != 0: + raise ValueError( + "fees.distribution.rotations must contain appealRounds + 1 " + "entries when appealRounds is non-zero." + ) + rotations = [] + else: + rotations = _normalize_rotations( + raw_rotations, + appeal_rounds, + "fees.distribution.rotations", + ) + return _create_fees_distribution(fee_distribution, appeal_rounds, rotations) + + def encode_internal_message_fee_params( params: Optional[InternalMessageFeeParamsInput] = None, ) -> HexStr: @@ -437,7 +482,7 @@ def encode_internal_message_fee_params( "internalMessageFeeParams.appealRounds", ) encoded = abi_encode( - ("(uint256,uint256,uint256,uint256,uint256[])",), + ("(uint256,uint256,uint256,uint256,uint256[],uint256,uint256,uint256)",), ( ( to_uint( @@ -470,6 +515,30 @@ def encode_internal_message_fee_params( appeal_rounds, "internalMessageFeeParams.rotations", ), + to_uint( + _get( + params, + "maxPriceGenPerTimeUnit", + "max_price_gen_per_time_unit", + ), + "internalMessageFeeParams.maxPriceGenPerTimeUnit", + ), + to_uint( + _get( + params, + "storageFeeMaxGasPrice", + "storage_fee_max_gas_price", + ), + "internalMessageFeeParams.storageFeeMaxGasPrice", + ), + to_uint( + _get( + params, + "receiptFeeMaxGasPrice", + "receipt_fee_max_gas_price", + ), + "internalMessageFeeParams.receiptFeeMaxGasPrice", + ), ), ), ) @@ -654,6 +723,10 @@ def extract_studio_fee_policy(config: Any) -> FeePolicyQuote: policy.get("receiptGasPrice"), "policy.receiptGasPrice", ) + time_unit_overlay_bps = _int_from_unknown( + policy.get("timeUnitOverlayBps", 0), + "policy.timeUnitOverlayBps", + ) intrinsic_gas = _int_from_unknown( policy.get("intrinsicGas", DEFAULT_INTRINSIC_GAS), "policy.intrinsicGas", @@ -714,6 +787,7 @@ def extract_studio_fee_policy(config: Any) -> FeePolicyQuote: "storageUnitPrice": storage_unit_price, "receiptGasPrice": receipt_gas_price, "executionBudgetFloor": execution_budget_floor, + "timeUnitOverlayBps": time_unit_overlay_bps, } @@ -1214,6 +1288,16 @@ def _calculate_fee_for_round( ) +def _validators_per_round_safe(round_index: int) -> int: + return VALIDATORS_PER_ROUND[ + min(max(0, int(round_index)), len(VALIDATORS_PER_ROUND) - 1) + ] + + +def _successful_appeal_profit(appeal_bond: int) -> int: + return appeal_bond + appeal_bond // 2 + + def calculate_local_round_fees( distribution: FeesDistribution, num_of_initial_validators: int, @@ -1238,10 +1322,8 @@ def calculate_local_round_fees( raise ValueError("MaxPriceExceeded") start_index = _validator_index(num_of_initial_validators) - if start_index + distribution["appealRounds"] * 2 >= len(VALIDATORS_PER_ROUND): - raise ValueError("InvalidNumOfValidators") - total = _calculate_fee_for_round( + taxable_work = _calculate_fee_for_round( VALIDATORS_PER_ROUND[start_index], distribution["rotations"][0] + 1, distribution["leaderTimeunitsAllocation"], @@ -1256,22 +1338,51 @@ def calculate_local_round_fees( elif offset % 2 == 1: rotations_this_round = 1 - total += _calculate_fee_for_round( - VALIDATORS_PER_ROUND[start_index + offset], + # Consensus indexes appeal/next-normal committees by absolute round + # and saturates at the published ladder. Only round zero uses the + # caller-selected initial committee. + taxable_work += _calculate_fee_for_round( + _validators_per_round_safe(offset), rotations_this_round, distribution["leaderTimeunitsAllocation"], distribution["validatorTimeunitsAllocation"], ) - if policy["genPerTimeUnit"] > 0: - total *= policy["genPerTimeUnit"] + price_cap = distribution["maxPriceGenPerTimeUnit"] + if price_cap > 0: + taxable_work *= price_cap + + appeal_profit_reserve = 0 + for appeal_ordinal in range(distribution["appealRounds"]): + next_normal_bond = _calculate_fee_for_round( + _validators_per_round_safe((appeal_ordinal + 1) * 2), + distribution["rotations"][appeal_ordinal + 1] + 1, + distribution["leaderTimeunitsAllocation"], + distribution["validatorTimeunitsAllocation"], + ) + if price_cap > 0: + next_normal_bond *= price_cap + appeal_profit_reserve += _successful_appeal_profit(next_normal_bond) + + overlay_bps = int(policy.get("timeUnitOverlayBps", 0)) + if overlay_bps < 0 or overlay_bps >= 10_000: + raise ValueError("InvalidTimeUnitOverlayBps") + overlay = ( + taxable_work * overlay_bps // (10_000 - overlay_bps) + if overlay_bps > 0 + else 0 + ) leader_rounds = sum( rotations + 1 for rotations in distribution["rotations"] ) + distribution["appealRounds"] - total += distribution["executionBudgetPerRound"] * leader_rounds - return total + return ( + taxable_work + + appeal_profit_reserve + + overlay + + distribution["executionBudgetPerRound"] * leader_rounds + ) def build_add_transaction_params_tuple( diff --git a/tests/unit/contracts/test_contract_actions.py b/tests/unit/contracts/test_contract_actions.py index b19f020..0a6d4aa 100644 --- a/tests/unit/contracts/test_contract_actions.py +++ b/tests/unit/contracts/test_contract_actions.py @@ -24,6 +24,7 @@ build_estimated_fees_distribution, calculate_local_round_fees, create_fees_distribution, + create_top_up_fees_distribution, derive_external_message_call_key, derive_internal_message_call_key, deploy_call_key, @@ -124,16 +125,18 @@ } ] -# Studio refetches its ConsensusMain ABI from the simulator RPC. Its current -# native appeal entrypoint carries only the transaction id. +# Studio refetches its decision-bound ConsensusMain ABI from the simulator RPC. STUDIO_CONSENSUS_MAIN_ABI = [ { "type": "function", "name": "submitAppeal", "stateMutability": "payable", - "inputs": [{"name": "_txId", "type": "bytes32"}], + "inputs": [ + {"name": "_txId", "type": "bytes32"}, + {"name": "_expectedDecisionId", "type": "uint256"}, + ], "outputs": [], - } + }, ] SENDER = "0x1111111111111111111111111111111111111111" @@ -149,14 +152,33 @@ def test_encode_internal_message_fee_params_uses_consensus_tuple_shape(): "appealRounds": 1, "executionBudgetPerRound": 20, "rotations": [2, 3], + "maxPriceGenPerTimeUnit": 30, + "storageFeeMaxGasPrice": 40, + "receiptFeeMaxGasPrice": 50, + } + ) + + decoded = abi_decode( + ("(uint256,uint256,uint256,uint256,uint256[],uint256,uint256,uint256)",), + Web3.to_bytes(hexstr=encoded), + )[0] + assert decoded == (5, 10, 1, 20, (2, 3), 30, 40, 50) + + +def test_encode_internal_message_fee_params_accepts_snake_case_price_caps(): + encoded = encode_internal_message_fee_params( + { + "max_price_gen_per_time_unit": 30, + "storage_fee_max_gas_price": 40, + "receipt_fee_max_gas_price": 50, } ) decoded = abi_decode( - ("(uint256,uint256,uint256,uint256,uint256[])",), + ("(uint256,uint256,uint256,uint256,uint256[],uint256,uint256,uint256)",), Web3.to_bytes(hexstr=encoded), )[0] - assert decoded == (5, 10, 1, 20, (2, 3)) + assert decoded == (0, 0, 0, 0, (0,), 30, 40, 50) def test_derive_internal_message_call_key_for_short_method_name(): @@ -219,12 +241,8 @@ def test_encode_top_up_fees_uses_consensus_tuple_shape(): function_name="topUpFees", transaction_id=TX_ID, distribution={ - "leaderTimeunitsAllocation": 100, - "validatorTimeunitsAllocation": 200, - "appealRounds": 1, "executionBudgetPerRound": 500_000, "totalMessageFees": 30, - "rotations": [0, 2], "maxPriceGenPerTimeUnit": 12, "storageFeeMaxGasPrice": 24, "receiptFeeMaxGasPrice": 36, @@ -240,7 +258,18 @@ def test_encode_top_up_fees_uses_consensus_tuple_shape(): Web3.to_bytes(hexstr=encoded[10:]), ) assert decoded_tx_id == Web3.to_bytes(hexstr=TX_ID) - assert distribution == (100, 200, 1, 500_000, 0, 30, (0, 2), 12, 24, 36) + assert distribution == (0, 0, 0, 500_000, 0, 30, (), 12, 24, 36) + + +def test_top_up_distribution_preserves_explicit_initial_schedule(): + assert create_top_up_fees_distribution({"appealRounds": 1, "rotations": [0, 2]})[ + "rotations" + ] == [0, 2] + + +def test_top_up_distribution_requires_schedule_for_nonzero_appeal_rounds(): + with pytest.raises(ValueError, match=r"rotations must contain appealRounds \+ 1"): + create_top_up_fees_distribution({"appealRounds": 1}) def test_top_up_fees_sends_consensus_call(monkeypatch): @@ -342,12 +371,6 @@ def test_appeal_transaction_auto_resolves_latest_quote(monkeypatch): } ), ) - monkeypatch.setattr( - contract_actions, - "_encode_submit_appeal_data", - Mock(return_value="0x1234"), - ) - def fake_send_consensus_call(**kwargs): captured.update(kwargs) return "0xevmtx" @@ -365,11 +388,18 @@ def fake_send_consensus_call(**kwargs): assert result == TX_ID contract_actions.get_appeal_quote.assert_called_once_with(client, TX_ID) - contract_actions._encode_submit_appeal_data.assert_called_once_with( - self=client, - transaction_id=TX_ID, - expected_decision_id=42, + selector = eth_utils.keccak( + text=f"topUpAndSubmitAppeal(bytes32,uint256,{FEES_DISTRIBUTION_ABI_TYPE})" + )[:4].hex() + assert captured["encoded_data"].startswith(f"0x{selector}") + tx_id, decision_id, distribution = abi_decode( + ("bytes32", "uint256", FEES_DISTRIBUTION_ABI_TYPE), + Web3.to_bytes(hexstr=captured["encoded_data"][10:]), ) + assert tx_id == Web3.to_bytes(hexstr=TX_ID) + assert decision_id == 42 + assert distribution[2] == 0 + assert distribution[6] == (0,) assert captured["value"] == 4_321 assert captured["operation_name"] == "Appeal" @@ -526,12 +556,29 @@ def _forbid_train_reads(monkeypatch): train_read = Mock(side_effect=AssertionError("train read on a Studio chain")) monkeypatch.setattr(contract_actions, "_consensus_data_contract", train_read) monkeypatch.setattr(contract_actions, "_get_active_decision_id", train_read) - monkeypatch.setattr(contract_actions, "get_appeal_quote", train_read) return train_read -def test_appeal_transaction_on_studio_keeps_the_native_call_shape(monkeypatch): +def _make_studio_client(): client = _make_client(STUDIO_CONSENSUS_MAIN_ABI) + client.provider.make_request = Mock( + return_value={ + "result": { + "decisionId": "42", + "bond": "4000", + "funding": "321", + "appealDeadline": "999", + } + } + ) + client.get_transaction_lifecycle = Mock( + return_value={"decision_active": True, "decision_id": 42} + ) + return client + + +def test_appeal_transaction_on_studio_binds_the_active_decision(monkeypatch): + client = _make_studio_client() train_read = _forbid_train_reads(monkeypatch) captured = {} @@ -551,16 +598,26 @@ def fake_send_consensus_call(**kwargs): value=1234, ) - selector = eth_utils.keccak(text="submitAppeal(bytes32)")[:4].hex() + selector = eth_utils.keccak( + text=f"topUpAndSubmitAppeal(bytes32,uint256,{FEES_DISTRIBUTION_ABI_TYPE})" + )[:4].hex() assert result == TX_ID assert captured["value"] == 1234 assert captured["operation_name"] == "Appeal" - assert captured["encoded_data"] == f"0x{selector}{TX_ID[2:]}" + assert captured["encoded_data"].startswith(f"0x{selector}") + tx_id, decision_id, distribution = abi_decode( + ("bytes32", "uint256", FEES_DISTRIBUTION_ABI_TYPE), + Web3.to_bytes(hexstr=captured["encoded_data"][10:]), + ) + assert tx_id == Web3.to_bytes(hexstr=TX_ID) + assert decision_id == 42 + assert distribution[2] == 0 + assert distribution[6] == (0,) train_read.assert_not_called() -def test_top_up_and_submit_appeal_on_studio_keeps_the_native_call_shape(monkeypatch): - client = _make_client(STUDIO_CONSENSUS_MAIN_ABI) +def test_top_up_and_submit_appeal_on_studio_binds_the_active_decision(monkeypatch): + client = _make_studio_client() train_read = _forbid_train_reads(monkeypatch) captured = {} @@ -582,17 +639,18 @@ def fake_send_consensus_call(**kwargs): ) selector = eth_utils.keccak( - text=f"topUpAndSubmitAppeal(bytes32,{FEES_DISTRIBUTION_ABI_TYPE})" + text=f"topUpAndSubmitAppeal(bytes32,uint256,{FEES_DISTRIBUTION_ABI_TYPE})" )[:4].hex() assert result == TX_ID assert captured["value"] == 1234 assert captured["operation_name"] == "Top up and submit appeal" assert captured["encoded_data"].startswith(f"0x{selector}") - decoded_tx_id, distribution = abi_decode( - ("bytes32", FEES_DISTRIBUTION_ABI_TYPE), + decoded_tx_id, decision_id, distribution = abi_decode( + ("bytes32", "uint256", FEES_DISTRIBUTION_ABI_TYPE), Web3.to_bytes(hexstr=captured["encoded_data"][10:]), ) assert decoded_tx_id == Web3.to_bytes(hexstr=TX_ID) + assert decision_id == 42 assert distribution[2] == 1 train_read.assert_not_called() @@ -605,70 +663,86 @@ def fake_send_consensus_call(**kwargs): contract_actions.get_min_appeal_bond, ), ) -def test_appeal_quote_reads_on_studio_report_unavailable(action, monkeypatch): - client = _make_client(STUDIO_CONSENSUS_MAIN_ABI) +def test_appeal_quote_reads_use_studio_authoritative_rpc(action, monkeypatch): + client = _make_studio_client() train_read = Mock(side_effect=AssertionError("train read on a Studio chain")) monkeypatch.setattr(contract_actions, "_consensus_data_contract", train_read) - with pytest.raises(GenLayerError, match="not available on current Studio"): - action(client, TX_ID) + result = action(client, TX_ID) + if action is contract_actions.get_appeal_quote: + assert result == { + "decision_id": 42, + "bond": 4_000, + "funding": 321, + "total": 4_321, + "appeal_deadline": 999, + } + else: + assert result == 4_321 train_read.assert_not_called() -def test_studio_appeals_require_an_explicit_value(monkeypatch): - client = _make_client(STUDIO_CONSENSUS_MAIN_ABI) +def test_studio_appeals_auto_resolve_the_authoritative_value(monkeypatch): + client = _make_studio_client() train_read = _forbid_train_reads(monkeypatch) - send_consensus_call = Mock() + send_consensus_call = Mock(return_value="0xevmtx") monkeypatch.setattr( contract_actions, "_send_consensus_call", send_consensus_call, ) - with pytest.raises(GenLayerError, match="pass value explicitly"): - contract_actions.appeal_transaction(self=client, transaction_id=TX_ID) - with pytest.raises(GenLayerError, match="pass value explicitly"): - contract_actions.top_up_and_submit_appeal( - self=client, - transaction_id=TX_ID, - distribution={"appealRounds": 1}, - ) + contract_actions.appeal_transaction(self=client, transaction_id=TX_ID) + contract_actions.top_up_and_submit_appeal( + self=client, + transaction_id=TX_ID, + distribution={"appealRounds": 1}, + ) - send_consensus_call.assert_not_called() + assert [call.kwargs["value"] for call in send_consensus_call.call_args_list] == [ + 4_321, + 4_321, + ] train_read.assert_not_called() -def test_studio_appeals_reject_the_train_decision_guard(monkeypatch): - client = _make_client(STUDIO_CONSENSUS_MAIN_ABI) +def test_studio_appeals_accept_an_explicit_decision_guard(monkeypatch): + client = _make_studio_client() train_read = _forbid_train_reads(monkeypatch) - send_consensus_call = Mock() + send_consensus_call = Mock(return_value="0xevmtx") monkeypatch.setattr( contract_actions, "_send_consensus_call", send_consensus_call, ) - with pytest.raises(GenLayerError, match="expected_decision_id is not supported"): - contract_actions.appeal_transaction( - self=client, - transaction_id=TX_ID, - value=1234, - expected_decision_id=42, - ) - with pytest.raises(GenLayerError, match="expected_decision_id is not supported"): - contract_actions.top_up_and_submit_appeal( - self=client, - transaction_id=TX_ID, - distribution={"appealRounds": 1}, - value=1234, - expected_decision_id=42, - ) + contract_actions.appeal_transaction( + self=client, + transaction_id=TX_ID, + value=1234, + expected_decision_id=42, + ) + contract_actions.top_up_and_submit_appeal( + self=client, + transaction_id=TX_ID, + distribution={"appealRounds": 1}, + value=1234, + expected_decision_id=42, + ) - send_consensus_call.assert_not_called() + assert send_consensus_call.call_count == 2 + client.provider.make_request.assert_not_called() train_read.assert_not_called() +def test_can_appeal_on_studio_uses_the_active_decision_quote(): + client = _make_studio_client() + + assert contract_actions.can_appeal(client, TX_ID) is True + client.get_transaction_lifecycle.assert_called_once_with(TX_ID) + + def test_encode_submit_appeal_still_requires_a_decision_id_on_the_train_shape(): client = _make_client(CONSENSUS_MAIN_ABI, chain_id=testnet_asimov.id) @@ -676,8 +750,8 @@ def test_encode_submit_appeal_still_requires_a_decision_id_on_the_train_shape(): contract_actions._encode_submit_appeal_data(self=client, transaction_id=TX_ID) -def test_send_consensus_call_returns_localnet_rpc_hash_without_waiting(monkeypatch): - wait_for_transaction_receipt = Mock() +def test_send_consensus_call_returns_localnet_envelope_hash_after_receipt(monkeypatch): + wait_for_transaction_receipt = Mock(return_value=SimpleNamespace(status=1)) sign_transaction = Mock(return_value=SimpleNamespace(raw_transaction=b"\x12\x34")) client = SimpleNamespace( chain=SimpleNamespace( @@ -711,7 +785,106 @@ def test_send_consensus_call_returns_localnet_rpc_hash_without_waiting(monkeypat ) assert result == TX_ID - wait_for_transaction_receipt.assert_not_called() + wait_for_transaction_receipt.assert_called_once_with(TX_ID) + + +def test_send_consensus_call_surfaces_studio_receipt_revert_reason(monkeypatch): + wait_for_transaction_receipt = Mock(return_value=SimpleNamespace(status=0)) + sign_transaction = Mock(return_value=SimpleNamespace(raw_transaction=b"\x12\x34")) + + def make_request(*, method, params): + if method == "eth_sendRawTransaction": + return {"result": TX_ID} + if method == "eth_getTransactionReceipt": + return { + "result": { + "status": "0x0", + "revertReason": "TopUpCannotExtendSchedule", + } + } + raise AssertionError(f"unexpected method {method}") + + client = SimpleNamespace( + chain=SimpleNamespace( + id=localnet.id, + consensus_main_contract={ + "address": "0x3333333333333333333333333333333333333333", + }, + ), + provider=SimpleNamespace(make_request=Mock(side_effect=make_request)), + w3=SimpleNamespace( + to_hex=Mock(return_value="0xsigned"), + eth=SimpleNamespace( + wait_for_transaction_receipt=wait_for_transaction_receipt + ), + ), + ) + account = SimpleNamespace(address=SENDER, sign_transaction=sign_transaction) + + monkeypatch.setattr( + contract_actions, + "_prepare_transaction", + Mock(return_value={"from": SENDER}), + ) + + with pytest.raises(GenLayerError, match="TopUpCannotExtendSchedule"): + contract_actions._send_consensus_call( + self=client, + encoded_data="0x1234", + sender_account=account, + value=1, + operation_name="Top up fees", + ) + + +def test_send_transaction_surfaces_studio_receipt_revert_reason(monkeypatch): + wait_for_transaction_receipt = Mock(return_value=SimpleNamespace(status=0)) + sign_transaction = Mock(return_value=SimpleNamespace(raw_transaction=b"\x12\x34")) + + def make_request(*, method, params): + if method == "eth_sendRawTransaction": + return {"result": TX_ID} + if method == "eth_getTransactionReceipt": + return { + "result": { + "status": "0x0", + "revertReason": "InsufficientFees", + } + } + raise AssertionError(f"unexpected method {method}") + + client = SimpleNamespace( + chain=SimpleNamespace( + id=localnet.id, + name="localnet", + consensus_main_contract={ + "address": "0x3333333333333333333333333333333333333333", + "abi": [], + }, + ), + provider=SimpleNamespace(make_request=Mock(side_effect=make_request)), + w3=SimpleNamespace( + to_hex=Mock(return_value="0xsigned"), + eth=SimpleNamespace( + wait_for_transaction_receipt=wait_for_transaction_receipt + ), + ), + ) + account = SimpleNamespace(address=SENDER, sign_transaction=sign_transaction) + + monkeypatch.setattr( + contract_actions, + "_prepare_transaction", + Mock(return_value={"from": SENDER}), + ) + + with pytest.raises(GenLayerError, match="InsufficientFees"): + contract_actions._send_transaction( + self=client, + encoded_data="0x1234", + sender_account=account, + value=1, + ) def test_revert_selector_formatter_names_fee_errors(): @@ -1201,6 +1374,33 @@ def test_calculate_local_round_fees_matches_consensus_initial_round(): assert calculate_local_round_fees(distribution, 5, policy) == 11_000 +def test_calculate_local_round_fees_matches_cap_overlay_appeal_reserve_and_ladder(): + distribution = create_fees_distribution( + { + "leaderTimeunitsAllocation": 100, + "validatorTimeunitsAllocation": 200, + "appealRounds": 1, + "rotations": [0, 0], + "executionBudgetPerRound": 0, + "maxPriceGenPerTimeUnit": 12, + } + ) + policy = { + "enabled": True, + "genPerTimeUnit": 10, + "storageUnitPrice": 0, + "receiptGasPrice": 0, + "executionBudgetFloor": 0, + "timeUnitOverlayBps": 1_500, + } + + # Taxable work: (5-validator round 0 + absolute rounds 1 and 2) * cap + # = (1100 + 1500 + 2300) * 12 = 58800. + # Appeal profit reserve: 1.5 * (2300 * 12) = 41400. + # Overlay: floor(58800 * 1500 / 8500) = 10376. + assert calculate_local_round_fees(distribution, 5, policy) == 110_576 + + def test_estimate_transaction_fees_uses_studio_fee_config(): client = SimpleNamespace( chain=SimpleNamespace( @@ -1325,7 +1525,7 @@ def test_estimate_transaction_fees_derives_message_bucket_from_allocations(): 3_000_000_000 + 30 * DEFAULT_PARENT_MESSAGE_RECEIPT_HEADROOM ) assert estimate["distribution"]["rotations"] == [3] - assert estimate["feeValue"] == 12_001_244_080 + assert estimate["feeValue"] == 12_001_252_880 assert estimate["messageAllocations"] == message_allocations assert estimate["message_allocations"] == message_allocations From 34bff4676be3aaa86f8821136d3aacac58cea073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Thu, 3 Sep 2026 01:02:59 +0100 Subject: [PATCH 16/19] feat: prepare v0.19 Studio preview and RC release (#113) Add the dedicated Studio-dev network preset, preserve stable Studionet coordinates, route chain 61997 through Studio APIs, and make v0.19-dev release candidates fail closed through the tag-driven publication flow. --- .claude/skills/release/SKILL.md | 23 ++-- .github/workflows/publish.yml | 31 +++-- CONTRIBUTING.md | 4 +- README.md | 12 ++ docs/BRANCHING.md | 7 +- docs/api-references/api.md | 20 +-- docs/api-references/index.md | 12 ++ genlayer_py/accounts/actions.py | 6 +- genlayer_py/chains/__init__.py | 9 +- genlayer_py/chains/actions.py | 7 +- genlayer_py/chains/studio_devnet.py | 32 +++++ genlayer_py/chains/utils.py | 12 ++ genlayer_py/contracts/actions.py | 23 ++-- genlayer_py/transactions/actions.py | 8 +- releaserc.toml | 11 +- scripts/release.sh | 130 +++++++++++------- scripts/release_version.py | 124 +++++++++++++++++ tests/unit/chains/test_chain_actions.py | 17 +++ tests/unit/chains/test_chain_presets.py | 46 +++++++ tests/unit/contracts/test_contract_actions.py | 3 +- tests/unit/test_release_version.py | 63 +++++++++ .../test_transaction_data_read.py | 7 +- 22 files changed, 489 insertions(+), 118 deletions(-) create mode 100644 genlayer_py/chains/studio_devnet.py create mode 100644 genlayer_py/chains/utils.py create mode 100644 scripts/release_version.py create mode 100644 tests/unit/chains/test_chain_presets.py create mode 100644 tests/unit/test_release_version.py diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index 7221bc0..76bb3b0 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -1,11 +1,11 @@ --- name: release -description: Cut a release of genlayer-py. Bumps version, updates CHANGELOG, tags, pushes — CI then publishes to PyPI and creates the GitHub Release. Use when a human asks "release v0.18.x" or "ship a new version". +description: Cut a release or release candidate of genlayer-py. Bumps version, updates CHANGELOG, tags, pushes — CI then publishes to PyPI and creates the GitHub Release. --- # Release skill — genlayer-py -This repo follows a branch-per-major release model. There is no auto-bump on push. A release happens when a human (or you on their behalf) runs `scripts/release.sh` on the target stable branch. +This repo follows a branch-per-release-line model. There is no auto-bump on push. A final release is cut from its stable branch; an RC is cut from the matching `*-dev` integration branch. ## When to use this skill @@ -18,12 +18,13 @@ If they ask "publish to PyPI directly" — refuse and point at this flow. The re ## What this repo's release model expects -- Branches are named after the major they ship: `v0.18` (current stable). When `v0.19` opens, the previous `v0.18` stays read-only for back-ports. +- Branches are named after the release line they ship: `v0.18` (stable) and `v0.19-dev` (integration). When `v0.19` becomes stable, the previous `v0.18` stays available for back-ports. - Tags live within those branches: `v0.18.1`, `v0.18.2`, ... - **Semver-zero rule**: this package is still on a 0.x line, so the MINOR component is the breaking-change boundary. `0.18 → 0.19` IS a major bump. `scripts/release.sh` refuses both `minor` and `major` keywords without `--allow-major` while we're on 0.x. - A major (= minor on 0.x) bump means cutting a new branch (`v0.19`) — not tagging on top of the current one. -- `CHANGELOG.md` is updated in the release commit (python-semantic-release with explicit version). -- `publish.yml` fires on the tag push and does the PyPI publish + GitHub Release. +- `CHANGELOG.md` is updated in the release commit by python-semantic-release; an explicit requested version must match the version computed from release history and conventional commits. +- Final tags are cut from `vX.Y`; RC tags such as `v0.19.0-rc.1` are cut from `vX.Y-dev`. +- `publish.yml` verifies that the tag is the current owning branch head, publishes to PyPI, and marks RC GitHub Releases as prereleases. ## Steps @@ -31,13 +32,15 @@ If they ask "publish to PyPI directly" — refuse and point at this flow. The re - Which version? If unspecified, ask whether it's patch or explicit. - If they say "minor" or "major" while we're on 0.x, surface that this means cutting a new branch — confirm before proceeding. -2. **Switch to the target branch + sync.** +2. **Switch to the owning branch + sync.** ```bash git checkout v0.18 git pull --ff-only origin v0.18 ``` If the working tree isn't clean, stop and surface what's there. + For `v0.19.0-rc.1`, use `v0.19-dev` instead. The script rejects final versions on a dev branch and prereleases on a stable branch. + 3. **Verify the head is shippable.** - Latest CI green: ```bash @@ -50,9 +53,10 @@ If they ask "publish to PyPI directly" — refuse and point at this flow. The re 4. **Run the release script.** ```bash - scripts/release.sh # or patch + scripts/release.sh # final on vX.Y + scripts/release.sh --allow-major # first RC of a new 0.x line ``` - It bumps `pyproject.toml`, updates `CHANGELOG.md`, commits `chore(release): vX.Y.Z`, tags `vX.Y.Z`, and pushes both the branch commit and the tag. It will NOT publish to PyPI — CI handles that. + First run the same command with `--dry-run`; it exercises all read-only preflight and version-policy checks. The real command bumps `pyproject.toml`, updates `CHANGELOG.md`, commits `chore(release): X.Y.Z`, tags `vX.Y.Z`, and pushes both the branch commit and the tag. It will NOT publish to PyPI — CI handles that. 5. **Watch the publish workflow.** ```bash @@ -68,8 +72,9 @@ If they ask "publish to PyPI directly" — refuse and point at this flow. The re ## Things to refuse -- **Minor or major bump on 0.x without `--allow-major`**. Those are major bumps in semver-zero and belong on a new branch. +- **Minor or major bump on 0.x without `--allow-major`**. Those are major bumps in semver-zero and belong on a new stable/dev branch pair. - **Releasing from `main`** — `main` is retired. +- **A final tag from `*-dev`, or an RC tag from the stable branch** — the tag must belong to the exact owning branch. - **Hand-editing `pyproject.toml` to bump the version** — the script keeps pyproject, the CHANGELOG entry, the commit message, and the tag in lockstep. - **Publishing a tag where `publish.yml` failed** — fix the underlying issue, re-cut the release (delete the bad tag locally and on origin, re-run the script). diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c33afa3..5fa301f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,17 +1,19 @@ name: Publish Package to PyPI # Tag-driven publish. The release is cut by a human (or Claude via the -# release skill) running scripts/release.sh on the target stable branch +# release skill) running scripts/release.sh on the owning version branch # — that script bumps pyproject.toml, updates CHANGELOG.md, commits, # tags vX.Y.Z, and pushes both the branch commit and the tag. This # workflow fires on the tag push, runs tests, sanity-checks the tag # matches pyproject.toml, builds, and publishes to PyPI. It never # bumps or tags by itself. on: - workflow_dispatch: push: tags: - - "v*" + - "v*.*.*" + +permissions: + contents: write jobs: run-tests: @@ -33,16 +35,22 @@ jobs: - name: Install Python run: uv python install 3.12 - - name: Verify tag matches pyproject.toml version + - name: Verify tag, package version, and owning branch run: | TAG_VERSION="${GITHUB_REF_NAME#v}" PKG_VERSION="$(grep -E '^version = ' pyproject.toml | head -1 | sed -E 's/version = "([^"]+)"/\1/')" - if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then - echo "Tag ($TAG_VERSION) and pyproject.toml ($PKG_VERSION) disagree — refusing to publish." >&2 - echo "Re-cut the release via scripts/release.sh so the tag and the committed version match." >&2 + NORMALIZED_VERSION="$(python scripts/release_version.py verify-tag "$TAG_VERSION" "$PKG_VERSION")" + EXPECTED_BRANCH="$(python scripts/release_version.py branch "$TAG_VERSION")" + git fetch --no-tags origin \ + "refs/heads/$EXPECTED_BRANCH:refs/remotes/origin/$EXPECTED_BRANCH" + TAG_COMMIT="$(git rev-parse "${GITHUB_REF_NAME}^{commit}")" + BRANCH_HEAD="$(git rev-parse "origin/$EXPECTED_BRANCH")" + if [ "$TAG_COMMIT" != "$BRANCH_HEAD" ]; then + echo "Tag $GITHUB_REF_NAME points to $TAG_COMMIT, but $EXPECTED_BRANCH is at $BRANCH_HEAD." >&2 + echo "Re-cut the release from the current owning branch head via scripts/release.sh." >&2 exit 1 fi - echo "Tag $GITHUB_REF_NAME matches pyproject.toml $PKG_VERSION." + echo "Tag $GITHUB_REF_NAME matches package $NORMALIZED_VERSION and $EXPECTED_BRANCH@$BRANCH_HEAD." - name: Clean previous builds run: rm -rf -- dist build *.egg-info @@ -63,6 +71,10 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + RELEASE_FLAGS=() + if [ "$(python scripts/release_version.py is-prerelease "$GITHUB_REF_NAME")" = "true" ]; then + RELEASE_FLAGS+=(--prerelease) + fi NOTES="$(awk -v ver="$GITHUB_REF_NAME" ' $0 ~ "^## \\[?" substr(ver, 2) {capture=1; next} capture && /^## / {exit} @@ -73,4 +85,5 @@ jobs: fi gh release create "$GITHUB_REF_NAME" \ --title "$GITHUB_REF_NAME" \ - --notes "$NOTES" + --notes "$NOTES" \ + "${RELEASE_FLAGS[@]}" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 682319a..dd60ca9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,7 @@ the default/static GitHub branch. ## Releases -Releases are deliberate, not automatic. `scripts/release.sh` bumps the version, updates `CHANGELOG.md`, commits, tags, and pushes; CI takes over from the tag push and publishes to PyPI. See `.claude/skills/release/SKILL.md` for the full flow. +Releases are deliberate, not automatic. `scripts/release.sh` bumps the version, updates `CHANGELOG.md`, commits, tags, and pushes; CI takes over from the tag push and publishes to PyPI. Release candidates are cut from the active `*-dev` branch (for example, `v0.19.0-rc.1` from `v0.19-dev`), while final versions are cut from the matching stable branch. See `.claude/skills/release/SKILL.md` for the full flow. **Semver-zero rule**: this package is on a 0.x line, so the MINOR component is the breaking-change boundary. `0.18 → 0.19` is a major bump and needs a new branch — the script refuses `minor`/`major` keywords without `--allow-major`. @@ -162,7 +162,7 @@ The project uses automated semantic versioning based on commit messages: | `feat!:`, `fix!:`, or `BREAKING CHANGE:` | **Major** version bump | 1.0.0 → 2.0.0 | | `docs:`, `style:`, `refactor:`, `test:`, `chore:`, `build:`, `ci:` | **No** version bump | Version stays the same | -**Important**: Never manually edit version numbers in `pyproject.toml` or other files. Releases are cut from the stable branch using the release automation described above. +**Important**: Never manually edit version numbers in `pyproject.toml` or other files. Final releases are cut from the stable branch and release candidates from its matching `*-dev` branch using the release automation described above. ## Logging Configuration diff --git a/README.md b/README.md index 1751b16..913e3c4 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,18 @@ SDK releases follow their corresponding GenLayer protocol release. This release targets the current resolution-kernel train; use the matching older SDK release when connecting to an older deployment. +Use the dedicated preview preset for the release-candidate Studio deployment: + +```python +from genlayer_py import create_client +from genlayer_py.chains import studio_devnet + +client = create_client(chain=studio_devnet) +``` + +`studio_devnet` targets `https://studio-dev.genlayer.com/api` (chain ID 61997). +The existing `studionet` preset remains pinned to the stable hosted Studio. + Here’s how to initialize the client and connect to the GenLayer Simulator: ### Reading a Transaction diff --git a/docs/BRANCHING.md b/docs/BRANCHING.md index 0125486..0b5b652 100644 --- a/docs/BRANCHING.md +++ b/docs/BRANCHING.md @@ -41,8 +41,11 @@ When an integration train is ready, open a promotion PR from the integration branch to the matching stable branch, for example `v0.19-dev` to `v0.19`. That promotion PR is the release-readiness gate and must pass required -cross-repo `E2E Tests`. The actual package release is cut from the stable branch -using a version tag after the stable branch is ready. +cross-repo `E2E Tests`. The final package release is cut from the stable branch +using a version tag after the stable branch is ready. A release candidate may +be cut earlier from the matching integration branch; RC tags use +`vX.Y.Z-rc.N`, publish as PyPI prereleases, and never substitute for the +promotion PR's final release gate. ## `main` diff --git a/docs/api-references/api.md b/docs/api-references/api.md index 6c8844e..4c78224 100644 --- a/docs/api-references/api.md +++ b/docs/api-references/api.md @@ -185,9 +185,8 @@ client.get_contract_schema_for_code(contract_code: AnyStr) Appeals a consensus transaction to trigger a new round of validation. Returns the original transaction_id (appeals operate on the same tx). -Deployed Consensus fills missing decision/value inputs from its -authoritative quote. Current Studio requires an explicit value and does -not accept ``expected_decision_id``. +Missing decision/value inputs are filled from the authoritative quote +on both Studio and deployed Consensus. ```python client.appeal_transaction(transaction_id: HexStr, account: Optional = None, value: Optional = None, expected_decision_id: Optional = None) @@ -225,9 +224,8 @@ client.top_up_fees(transaction_id: HexStr, distribution: FeesDistributionInput, Deposits appeal funding and submits an appeal. -On deployed Consensus, omitted decision/value inputs are resolved from -the authoritative appeal quote. Current Studio requires an explicit -value and does not accept ``expected_decision_id``. +Omitted decision/value inputs are resolved from the authoritative +appeal quote on both Studio and deployed Consensus. ```python client.top_up_and_submit_appeal(transaction_id: HexStr, distribution: FeesDistributionInput, account: Optional = None, value: Optional = None, expected_decision_id: Optional = None) @@ -247,9 +245,7 @@ client.top_up_and_submit_appeal(transaction_id: HexStr, distribution: FeesDistri ### can_appeal -Checks whether the exact active decision can be appealed on a network. - -This decision-bound read is not available on current Studio. +Checks whether the exact active decision can be appealed. ```python client.can_appeal(transaction_id: HexStr, expected_decision_id: Optional = None) @@ -266,9 +262,7 @@ client.can_appeal(transaction_id: HexStr, expected_decision_id: Optional = None) ### get_appeal_quote -Returns a network's latest decision id, appeal charges, and deadline. - -Current Studio has no decision-bound quote surface. +Returns the latest decision id, appeal charges, and deadline. ```python client.get_appeal_quote(transaction_id: HexStr) @@ -286,8 +280,6 @@ client.get_appeal_quote(transaction_id: HexStr) Returns the full appeal payment (bond plus induced-work funding). -Current Studio has no decision-bound quote surface. - ```python client.get_appeal_charge(transaction_id: HexStr) ``` diff --git a/docs/api-references/index.md b/docs/api-references/index.md index 0eba21a..0e6124a 100644 --- a/docs/api-references/index.md +++ b/docs/api-references/index.md @@ -24,6 +24,18 @@ SDK releases follow their corresponding GenLayer protocol release. This release targets the current resolution-kernel train; use the matching older SDK release when connecting to an older deployment. +Use the dedicated preview preset for the release-candidate Studio deployment: + +```python +from genlayer_py import create_client +from genlayer_py.chains import studio_devnet + +client = create_client(chain=studio_devnet) +``` + +`studio_devnet` targets `https://studio-dev.genlayer.com/api` (chain ID 61997). +The existing `studionet` preset remains pinned to the stable hosted Studio. + Here’s how to initialize the client and connect to the GenLayer Simulator: ### Reading a Transaction diff --git a/genlayer_py/accounts/actions.py b/genlayer_py/accounts/actions.py index e2d8c57..56ae085 100644 --- a/genlayer_py/accounts/actions.py +++ b/genlayer_py/accounts/actions.py @@ -1,7 +1,7 @@ from __future__ import annotations from typing import TYPE_CHECKING -from genlayer_py.chains import localnet +from genlayer_py.chains.utils import is_studio_chain from hexbytes import HexBytes from web3.types import Nonce, BlockIdentifier, ENS from genlayer_py.exceptions import GenLayerError @@ -18,8 +18,8 @@ def fund_account( self: GenLayerClient, address: Union[Address, ChecksumAddress, ENS], amount: int ) -> HexBytes: - if self.chain.id != localnet.id: - raise GenLayerError("Client is not connected to the localhost") + if not is_studio_chain(self.chain): + raise GenLayerError("Account funding is only supported on Studio networks") try: response = self.provider.make_request( method="sim_fundAccount", diff --git a/genlayer_py/chains/__init__.py b/genlayer_py/chains/__init__.py index 9ff2d87..15daadc 100644 --- a/genlayer_py/chains/__init__.py +++ b/genlayer_py/chains/__init__.py @@ -2,5 +2,12 @@ from .testnet_asimov import testnet_asimov from .testnet_bradbury import testnet_bradbury from .studionet import studionet +from .studio_devnet import studio_devnet -__all__ = ["localnet", "testnet_asimov", "testnet_bradbury", "studionet"] +__all__ = [ + "localnet", + "testnet_asimov", + "testnet_bradbury", + "studionet", + "studio_devnet", +] diff --git a/genlayer_py/chains/actions.py b/genlayer_py/chains/actions.py index d387ab8..1c45cf8 100644 --- a/genlayer_py/chains/actions.py +++ b/genlayer_py/chains/actions.py @@ -1,9 +1,8 @@ from __future__ import annotations from genlayer_py.exceptions import GenLayerError -from .localnet import localnet -from .studionet import studionet from .testnet_asimov import testnet_asimov +from .utils import is_studio_chain from typing import TYPE_CHECKING @@ -23,12 +22,10 @@ def initialize_consensus_smart_contract( and bool(self.chain.consensus_main_contract.get("address")) and bool(self.chain.consensus_main_contract.get("abi")) ) - is_local_or_studio_chain = self.chain.id in (localnet.id, studionet.id) - if ( not force_reset and has_static_consensus_contract - and not is_local_or_studio_chain + and not is_studio_chain(self.chain) ): return diff --git a/genlayer_py/chains/studio_devnet.py b/genlayer_py/chains/studio_devnet.py new file mode 100644 index 0000000..cd3da4c --- /dev/null +++ b/genlayer_py/chains/studio_devnet.py @@ -0,0 +1,32 @@ +from genlayer_py.types import GenLayerChain, NativeCurrency + +from .studionet import ( + CONSENSUS_DATA_CONTRACT, + CONSENSUS_MAIN_CONTRACT, +) + + +STUDIO_DEVNET_JSON_RPC_URL = "https://studio-dev.genlayer.com/api" +STUDIO_DEVNET_EXPLORER_URL = "https://explorer-studio-dev.genlayer.com" + +studio_devnet: GenLayerChain = GenLayerChain( + id=61997, + name="GenLayer Studio Devnet", + rpc_urls={"default": {"http": [STUDIO_DEVNET_JSON_RPC_URL]}}, + native_currency=NativeCurrency(name="GEN Token", symbol="GEN", decimals=18), + block_explorers={ + "default": { + "name": "GenLayer Explorer", + "url": STUDIO_DEVNET_EXPLORER_URL, + } + }, + testnet=True, + consensus_main_contract=dict(CONSENSUS_MAIN_CONTRACT), + consensus_data_contract=dict(CONSENSUS_DATA_CONTRACT), + fee_manager_contract=None, + rounds_storage_contract=None, + appeals_contract=None, + staking_contract=None, + default_number_of_initial_validators=5, + default_consensus_max_rotations=3, +) diff --git a/genlayer_py/chains/utils.py b/genlayer_py/chains/utils.py new file mode 100644 index 0000000..be7c5ae --- /dev/null +++ b/genlayer_py/chains/utils.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from genlayer_py.types import GenLayerChain + + +STUDIO_CHAIN_IDS = frozenset({61997, 61999}) + + +def is_studio_chain(chain: GenLayerChain) -> bool: + """Return whether *chain* uses the Studio simulator RPC surface.""" + + return chain.id in STUDIO_CHAIN_IDS diff --git a/genlayer_py/contracts/actions.py b/genlayer_py/contracts/actions.py index 23661c8..96dfa64 100644 --- a/genlayer_py/contracts/actions.py +++ b/genlayer_py/contracts/actions.py @@ -14,7 +14,7 @@ from genlayer_py.exceptions import GenLayerError from genlayer_py.abi import calldata from genlayer_py.abi.transactions import serialize -from genlayer_py.chains import localnet +from genlayer_py.chains.utils import is_studio_chain from web3.constants import ADDRESS_ZERO from web3.logs import DISCARD from genlayer_py.contracts.utils import make_calldata_object @@ -57,7 +57,7 @@ def get_contract_schema( self: GenLayerClient, address: Union[Address, ChecksumAddress], ) -> ContractSchema: - if self.chain.id != localnet.id: + if not is_studio_chain(self.chain): raise GenLayerError("Contract schema is not supported on this network") response = self.provider.make_request( @@ -70,7 +70,7 @@ def get_contract_schema_for_code( self: GenLayerClient, contract_code: AnyStr, ) -> ContractSchema: - if self.chain.id != localnet.id: + if not is_studio_chain(self.chain): raise GenLayerError("Contract schema is not supported on this network") code_bytes = ( @@ -572,10 +572,9 @@ def _resolve_appeal_parameters( def _is_studio_chain(self: GenLayerClient) -> bool: """Reports whether the client targets the studio-embedded consensus. - localnet and studionet share chain id 61999, the same check - ``transactions.actions.get_transaction`` uses to take its studio path. + This includes local Studio, the stable hosted Studio, and preview Studio. """ - return self.chain.id == localnet.id + return is_studio_chain(self.chain) def _to_bytes32(self: GenLayerClient, hex_str: HexStr) -> bytes: @@ -598,8 +597,8 @@ def simulate_write_contract( sim_config: Optional[SimConfig] = None, transaction_hash_variant: TransactionHashVariant = TransactionHashVariant.LATEST_NONFINAL, ) -> dict: - if self.chain.id != localnet.id: - raise GenLayerError("Client is not connected to the localnet") + if not is_studio_chain(self.chain): + raise GenLayerError("Simulation is only supported on Studio networks") if account is None and self.local_account is None: raise GenLayerError("No account provided and no account is connected") sender_address = self.local_account.address if account is None else account.address @@ -972,8 +971,10 @@ def estimate_transaction_fees_for_write( sim_config: Optional[SimConfig] = None, transaction_hash_variant: TransactionHashVariant = TransactionHashVariant.LATEST_NONFINAL, ) -> TransactionFeeEstimate: - if self.chain.id != localnet.id: - raise GenLayerError("Target write fee estimation is only supported on localnet") + if not is_studio_chain(self.chain): + raise GenLayerError( + "Target write fee estimation is only supported on Studio networks" + ) if account is None and self.local_account is None: raise GenLayerError("No account provided and no account is connected") @@ -1148,7 +1149,7 @@ def _prepare_transaction( nonce = self.get_current_nonce(address=sender) - if self.chain.id != localnet.id: + if not is_studio_chain(self.chain): latest_block = self.w3.eth.get_block("latest") base_fee = latest_block["baseFeePerGas"] priority_fee = self.w3.to_wei(2, "gwei") diff --git a/genlayer_py/transactions/actions.py b/genlayer_py/transactions/actions.py index 021d9fc..4d872f5 100644 --- a/genlayer_py/transactions/actions.py +++ b/genlayer_py/transactions/actions.py @@ -33,7 +33,7 @@ from typing import TYPE_CHECKING from genlayer_py.types import GenLayerTransaction, GenLayerRawTransaction import time -from genlayer_py.chains import localnet +from genlayer_py.chains.utils import is_studio_chain from genlayer_py.utils.jsonifier import ( calldata_to_user_friendly_json, result_to_user_friendly_json, @@ -638,7 +638,7 @@ def get_transaction_lifecycle( discriminated ``lifecycle`` returned by :func:`get_transaction`. """ - if self.chain.id == localnet.id: + if is_studio_chain(self.chain): tx_id = ( Web3.to_hex(transaction_hash) if isinstance(transaction_hash, bytes) @@ -683,7 +683,7 @@ def get_transaction( self: GenLayerClient, transaction_hash: _Hash32, ) -> GenLayerTransaction: - if self.chain.id == localnet.id: + if is_studio_chain(self.chain): transaction = self.provider.make_request( method="eth_getTransactionByHash", params=[transaction_hash] )["result"] @@ -803,7 +803,7 @@ def get_triggered_transaction_ids( self: GenLayerClient, transaction_hash: _Hash32, ) -> List[HexStr]: - if self.chain.id == localnet.id: + if is_studio_chain(self.chain): tx = get_transaction(self, transaction_hash) return tx.get("triggered_transactions", []) diff --git a/releaserc.toml b/releaserc.toml index 612b6e5..eae5145 100644 --- a/releaserc.toml +++ b/releaserc.toml @@ -10,11 +10,16 @@ allow_zero_version = true no_git_verify = false tag_format = "v{version}" -[semantic_release.branches.main] -match = "main" +[semantic_release.branches.stable] +match = "^v[0-9]+\\.[0-9]+$" prerelease_token = "rc" prerelease = false +[semantic_release.branches.dev] +match = "^v[0-9]+\\.[0-9]+-dev$" +prerelease_token = "rc" +prerelease = true + [semantic_release.changelog] exclude_commit_patterns = [] mode = "update" @@ -61,4 +66,4 @@ insecure = false [semantic_release.publish] dist_glob_patterns = ["dist/*"] -upload_to_vcs_release = true \ No newline at end of file +upload_to_vcs_release = true diff --git a/scripts/release.sh b/scripts/release.sh index becd986..26fb4c1 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Cut a release on the current stable branch. +# Cut a release on the current owning version branch. # # Bumps pyproject.toml, updates CHANGELOG.md via python-semantic-release, # commits, tags vX.Y.Z, and pushes both the branch commit and the tag. @@ -11,11 +11,13 @@ # you want to ship a release on (e.g. v0.18 for v0.18.x). # # Usage: -# scripts/release.sh # explicit semver — recommended +# scripts/release.sh # final release from vX.Y +# scripts/release.sh # release candidate from vX.Y-dev # scripts/release.sh patch # 0.18.0 → 0.18.1 # scripts/release.sh minor # 0.18.0 → 0.19.0 — refused unless --allow-major (see below) # scripts/release.sh major # 0.18.0 → 1.0.0 — refused unless --allow-major # scripts/release.sh --allow-major +# scripts/release.sh --dry-run [--allow-major] # # Semver-zero rule: while the major is 0, the MINOR is the breaking- # change boundary (per semver). 0.18 → 0.19 IS a major bump. The script @@ -23,7 +25,7 @@ # the current major is 0. Patches stay automatic-friendly. # # Pre-flight (each check refuses to proceed on failure): -# - On a v[.] branch (refuses on main / feature branches) +# - On vX.Y for a final or vX.Y-dev for a release candidate # - Working tree clean # - Local HEAD matches origin/ # - Latest CI run on HEAD is green @@ -31,14 +33,19 @@ set -euo pipefail ALLOW_MAJOR=0 -if [ "${1:-}" = "--allow-major" ]; then - ALLOW_MAJOR=1 +DRY_RUN=0 +while [[ "${1:-}" == --* ]]; do + case "$1" in + --allow-major) ALLOW_MAJOR=1 ;; + --dry-run) DRY_RUN=1 ;; + *) echo "Unknown option: $1" >&2; exit 2 ;; + esac shift -fi +done VERSION_ARG="${1:-}" if [ -z "$VERSION_ARG" ]; then - echo "Usage: $0 [--allow-major] |patch|minor|major" >&2 + echo "Usage: $0 [--dry-run] [--allow-major] ||patch|minor|major" >&2 exit 2 fi @@ -46,12 +53,12 @@ repo_root="$(git rev-parse --show-toplevel)" cd "$repo_root" branch="$(git rev-parse --abbrev-ref HEAD)" -if ! [[ "$branch" =~ ^v[0-9]+(\.[0-9]+)?(-dev)?$ ]]; then +if ! [[ "$branch" =~ ^v[0-9]+\.[0-9]+(-dev)?$ ]]; then cat >&2 </dev/null 2>&1; then - status="$(gh run list --branch "$branch" --commit "$local_sha" --limit 1 --json conclusion --jq '.[0].conclusion' 2>/dev/null || echo "")" - case "$status" in - success) ;; - "" ) - echo "Warning: no CI run found for $local_sha on $branch. Continuing anyway." >&2 - ;; - *) - echo "Latest CI on $branch@$local_sha is '$status' (not success). Refusing to release a red commit." >&2 - exit 1 - ;; - esac +if ! command -v gh >/dev/null 2>&1; then + echo "GitHub CLI is required to verify the release head's native tests." >&2 + exit 1 fi +status="$(gh run list --workflow tests.yml --branch "$branch" --commit "$local_sha" --limit 1 --json conclusion --jq '.[0].conclusion' 2>/dev/null || echo "")" +case "$status" in + success) ;; + "" ) + echo "No Tests workflow run found for $local_sha on $branch. Refusing to release an unverified head." >&2 + exit 1 + ;; + *) + echo "Latest Tests workflow on $branch@$local_sha is '$status' (not success). Refusing to release a red commit." >&2 + exit 1 + ;; +esac -current_version="$(grep -E '^version = ' pyproject.toml | head -1 | sed -E 's/version = "([^"]+)"/\1/')" - -# Resolve to a concrete X.Y.Z so the major-bump guard can compare. +release_flags=() case "$VERSION_ARG" in major|minor|patch) - next_version="$(python3 - "$current_version" "$VERSION_ARG" <<'PY' -import sys -cur = sys.argv[1].split(".") -kind = sys.argv[2] -major, minor, patch = int(cur[0]), int(cur[1]), int(cur[2]) -if kind == "major": - print(f"{major+1}.0.0") -elif kind == "minor": - print(f"{major}.{minor+1}.0") -elif kind == "patch": - print(f"{major}.{minor}.{patch+1}") -PY -)" + release_flags+=("--$VERSION_ARG") ;; *) - next_version="$VERSION_ARG" + requested_version="$(python3 scripts/release_version.py normalize "$VERSION_ARG")" || exit 2 ;; esac -if ! [[ "$next_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then - echo "Not a valid semver: $next_version" >&2 - exit 2 +if [[ "$branch" == *-dev ]]; then + release_flags+=(--as-prerelease --prerelease-token rc) fi -cur_major="${current_version%%.*}" +computed_raw="$( + uvx --from 'python-semantic-release==10.0.2' \ + semantic-release -c releaserc.toml version --print "${release_flags[@]}" +)" +next_version="$(python3 scripts/release_version.py normalize "$computed_raw")" || exit 2 + +if [[ -n "${requested_version:-}" && "$requested_version" != "$next_version" ]]; then + cat >&2 </dev/null + +last_tag="$(git describe --tags --abbrev=0 --match 'v*.*.*' 2>/dev/null || true)" +if [[ -z "$last_tag" ]]; then + echo "No previous release tag is reachable from $branch; refusing to infer release boundaries." >&2 + exit 1 +fi +last_version="$(python3 scripts/release_version.py normalize "$last_tag")" || exit 2 +cur_major="${last_version%%.*}" next_major="${next_version%%.*}" -cur_minor="$(echo "$current_version" | cut -d. -f2)" +cur_minor="$(echo "$last_version" | cut -d. -f2)" next_minor="$(echo "$next_version" | cut -d. -f2)" # Semver-zero: while major == 0, MINOR bumps are major bumps. @@ -130,7 +147,7 @@ if [ "$cur_major" = "0" ]; then if [ "$next_major" != "0" ] || [ "$next_minor" != "$cur_minor" ]; then if [ "$ALLOW_MAJOR" -ne 1 ]; then cat >&2 <&2 </dev/null +if ! git rev-parse --verify --quiet "refs/tags/v$next_version" >/dev/null; then + echo "Release tool did not create the expected tag v$next_version; refusing to push." >&2 + exit 1 +fi + # semantic-release commits and tags locally; we push explicitly so the # behaviour matches the JS-side script and the order of operations is # obvious from this file. diff --git a/scripts/release_version.py b/scripts/release_version.py new file mode 100644 index 0000000..08b4280 --- /dev/null +++ b/scripts/release_version.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +"""Validate GenLayer Python release versions and their owning branches.""" + +from __future__ import annotations + +from dataclasses import dataclass +import re +import sys + + +_VERSION_PATTERN = re.compile( + r"^v?(?P0|[1-9][0-9]*)\." + r"(?P0|[1-9][0-9]*)\." + r"(?P0|[1-9][0-9]*)" + r"(?:(?:-?rc\.?(?P[1-9][0-9]*)))?$", + re.IGNORECASE, +) +_BRANCH_PATTERN = re.compile( + r"^v(?P0|[1-9][0-9]*)\." + r"(?P0|[1-9][0-9]*)(?P-dev)?$" +) + + +@dataclass(frozen=True) +class ReleaseVersion: + major: int + minor: int + patch: int + rc: int | None = None + + @property + def normalized(self) -> str: + suffix = "" if self.rc is None else f"-rc.{self.rc}" + return f"{self.major}.{self.minor}.{self.patch}{suffix}" + + @property + def is_prerelease(self) -> bool: + return self.rc is not None + + @property + def release_branch(self) -> str: + suffix = "-dev" if self.is_prerelease else "" + return f"v{self.major}.{self.minor}{suffix}" + + +def parse_release_version(value: str) -> ReleaseVersion: + match = _VERSION_PATTERN.fullmatch(value.strip()) + if match is None: + raise ValueError( + f"{value!r} is not a supported release version; use X.Y.Z for a " + "final release or X.Y.Z-rc.N for a release candidate" + ) + return ReleaseVersion( + major=int(match.group("major")), + minor=int(match.group("minor")), + patch=int(match.group("patch")), + rc=int(match.group("rc")) if match.group("rc") is not None else None, + ) + + +def validate_branch_version(branch: str, value: str) -> ReleaseVersion: + branch_match = _BRANCH_PATTERN.fullmatch(branch) + if branch_match is None: + raise ValueError( + f"{branch!r} is not a release branch; use vX.Y or vX.Y-dev" + ) + + version = parse_release_version(value) + branch_line = (int(branch_match.group("major")), int(branch_match.group("minor"))) + if branch_line != (version.major, version.minor): + raise ValueError( + f"{version.normalized} belongs to v{version.major}.{version.minor}, " + f"not {branch}" + ) + + branch_is_dev = branch_match.group("dev") is not None + if branch_is_dev and not version.is_prerelease: + raise ValueError(f"final release {version.normalized} must be cut from v{version.major}.{version.minor}") + if not branch_is_dev and version.is_prerelease: + raise ValueError( + f"release candidate {version.normalized} must be cut from " + f"v{version.major}.{version.minor}-dev" + ) + return version + + +def _usage() -> str: + return ( + "usage: release_version.py normalize | branch | " + "is-prerelease | validate | " + "verify-tag " + ) + + +def main(argv: list[str]) -> int: + try: + command = argv[1] + if command == "normalize" and len(argv) == 3: + print(parse_release_version(argv[2]).normalized) + elif command == "branch" and len(argv) == 3: + print(parse_release_version(argv[2]).release_branch) + elif command == "is-prerelease" and len(argv) == 3: + print("true" if parse_release_version(argv[2]).is_prerelease else "false") + elif command == "validate" and len(argv) == 4: + print(validate_branch_version(argv[2], argv[3]).normalized) + elif command == "verify-tag" and len(argv) == 4: + tag_version = parse_release_version(argv[2]) + package_version = parse_release_version(argv[3]) + if tag_version != package_version: + raise ValueError( + f"tag {tag_version.normalized} does not match package " + f"version {package_version.normalized}" + ) + print(tag_version.normalized) + else: + raise ValueError(_usage()) + except (IndexError, ValueError) as exc: + print(exc, file=sys.stderr) + return 2 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv)) diff --git a/tests/unit/chains/test_chain_actions.py b/tests/unit/chains/test_chain_actions.py index 2e9e9b8..6c07f72 100644 --- a/tests/unit/chains/test_chain_actions.py +++ b/tests/unit/chains/test_chain_actions.py @@ -5,6 +5,7 @@ from genlayer_py.chains.actions import initialize_consensus_smart_contract from genlayer_py.chains.localnet import localnet +from genlayer_py.chains.studio_devnet import studio_devnet from genlayer_py.exceptions import GenLayerError @@ -45,6 +46,22 @@ def test_initialize_consensus_refreshes_runtime_contract_for_local_chain(): assert getattr(client.chain, "__consensus_abi_fetched_from_rpc") is True +def test_initialize_consensus_refreshes_runtime_contract_for_studio_devnet(): + client = _make_client( + chain_id=studio_devnet.id, + consensus_main_contract={"address": "0x1", "abi": [{"type": "function"}]}, + ) + rpc_contract = {"address": "0x2", "abi": [{"type": "function", "name": "foo"}]} + client.provider.make_request.return_value = {"result": rpc_contract} + + initialize_consensus_smart_contract(self=client) + + client.provider.make_request.assert_called_once_with( + method="sim_getConsensusContract", params=["ConsensusMain"] + ) + assert client.chain.consensus_main_contract == rpc_contract + + def test_initialize_consensus_falls_back_to_static_contract_on_local_rpc_failure(): static_contract = {"address": "0x1", "abi": [{"type": "function"}]} client = _make_client( diff --git a/tests/unit/chains/test_chain_presets.py b/tests/unit/chains/test_chain_presets.py new file mode 100644 index 0000000..a79e95d --- /dev/null +++ b/tests/unit/chains/test_chain_presets.py @@ -0,0 +1,46 @@ +import genlayer_py + +from genlayer_py.chains import __all__ as chain_exports +from genlayer_py.chains.studio_devnet import ( + STUDIO_DEVNET_EXPLORER_URL, + STUDIO_DEVNET_JSON_RPC_URL, + studio_devnet, +) +from genlayer_py.chains.studionet import studionet +from genlayer_py.chains.testnet_asimov import testnet_asimov +from genlayer_py.chains.utils import is_studio_chain + + +def test_studio_devnet_is_exported_with_canonical_preview_coordinates(): + assert "studio_devnet" in chain_exports + assert genlayer_py.studio_devnet is studio_devnet + assert studio_devnet.id == 61997 + assert studio_devnet.name == "GenLayer Studio Devnet" + assert studio_devnet.rpc_urls == { + "default": {"http": ["https://studio-dev.genlayer.com/api"]} + } + assert STUDIO_DEVNET_JSON_RPC_URL == "https://studio-dev.genlayer.com/api" + assert STUDIO_DEVNET_EXPLORER_URL == "https://explorer-studio-dev.genlayer.com" + assert studio_devnet.block_explorers == { + "default": { + "name": "GenLayer Explorer", + "url": "https://explorer-studio-dev.genlayer.com", + } + } + assert studio_devnet.consensus_main_contract == studionet.consensus_main_contract + assert studio_devnet.consensus_data_contract == studionet.consensus_data_contract + assert studio_devnet.consensus_main_contract is not studionet.consensus_main_contract + assert studio_devnet.consensus_data_contract is not studionet.consensus_data_contract + + +def test_stable_studionet_coordinates_do_not_drift_with_preview_preset(): + assert studionet.id == 61999 + assert studionet.rpc_urls == { + "default": {"http": ["https://studio.genlayer.com/api"]} + } + + +def test_studio_chain_classification_includes_preview_but_not_public_testnet(): + assert is_studio_chain(studionet) + assert is_studio_chain(studio_devnet) + assert not is_studio_chain(testnet_asimov) diff --git a/tests/unit/contracts/test_contract_actions.py b/tests/unit/contracts/test_contract_actions.py index 0a6d4aa..03c706a 100644 --- a/tests/unit/contracts/test_contract_actions.py +++ b/tests/unit/contracts/test_contract_actions.py @@ -8,6 +8,7 @@ import genlayer_py.contracts.actions as contract_actions from genlayer_py.chains import localnet +from genlayer_py.chains.studio_devnet import studio_devnet from genlayer_py.chains.testnet_asimov import testnet_asimov from genlayer_py.consensus.abi import CONSENSUS_MAIN_ABI from genlayer_py.exceptions import GenLayerError @@ -1031,7 +1032,7 @@ def test_simulate_write_contract_passes_fee_policy_and_value_to_sim_call(): } ) client = SimpleNamespace( - chain=SimpleNamespace(id=localnet.id), + chain=SimpleNamespace(id=studio_devnet.id), local_account=SimpleNamespace(address=SENDER), provider=SimpleNamespace(make_request=make_request), ) diff --git a/tests/unit/test_release_version.py b/tests/unit/test_release_version.py new file mode 100644 index 0000000..6d3d273 --- /dev/null +++ b/tests/unit/test_release_version.py @@ -0,0 +1,63 @@ +import importlib.util +from pathlib import Path +import sys + +import pytest + + +MODULE_PATH = Path(__file__).parents[2] / "scripts" / "release_version.py" +SPEC = importlib.util.spec_from_file_location("release_version", MODULE_PATH) +assert SPEC is not None and SPEC.loader is not None +release_version = importlib.util.module_from_spec(SPEC) +sys.modules[SPEC.name] = release_version +SPEC.loader.exec_module(release_version) + + +@pytest.mark.parametrize( + ("raw", "normalized", "branch", "is_prerelease"), + [ + ("0.19.0", "0.19.0", "v0.19", False), + ("v0.19.0-rc.1", "0.19.0-rc.1", "v0.19-dev", True), + ("0.19.0rc2", "0.19.0-rc.2", "v0.19-dev", True), + ], +) +def test_release_version_normalizes_pep440_rc_spellings( + raw, normalized, branch, is_prerelease +): + version = release_version.parse_release_version(raw) + + assert version.normalized == normalized + assert version.release_branch == branch + assert version.is_prerelease is is_prerelease + + +@pytest.mark.parametrize( + ("branch", "version", "message"), + [ + ("main", "0.19.0", "not a release branch"), + ("v0.18-dev", "0.19.0-rc.1", "belongs to v0.19"), + ("v0.19", "0.19.0-rc.1", "must be cut from v0.19-dev"), + ("v0.19-dev", "0.19.0", "must be cut from v0.19"), + ], +) +def test_release_version_rejects_wrong_release_route(branch, version, message): + with pytest.raises(ValueError, match=message): + release_version.validate_branch_version(branch, version) + + +def test_release_version_accepts_rc_only_on_owning_dev_line(): + version = release_version.validate_branch_version("v0.19-dev", "0.19.0rc1") + + assert version.normalized == "0.19.0-rc.1" + + +@pytest.mark.parametrize("version", ["0.19.0-alpha.1", "0.19.0-rc.0", "00.19.0"]) +def test_release_version_rejects_non_rc_or_noncanonical_versions(version): + with pytest.raises(ValueError, match="not a supported release version"): + release_version.parse_release_version(version) + + +def test_release_tag_and_package_version_compare_after_normalization(): + assert release_version.main( + ["release_version.py", "verify-tag", "v0.19.0-rc.1", "0.19.0rc1"] + ) == 0 diff --git a/tests/unit/transactions/test_transaction_data_read.py b/tests/unit/transactions/test_transaction_data_read.py index ee15ab2..1e5b135 100644 --- a/tests/unit/transactions/test_transaction_data_read.py +++ b/tests/unit/transactions/test_transaction_data_read.py @@ -25,7 +25,7 @@ ResolutionAction, ResolutionSource, ) -from genlayer_py.chains import localnet +from genlayer_py.chains import localnet, studio_devnet TX_HASH = "0x" + "ab" * 32 CONSENSUS_DATA_ADDRESS = "0x" + "11" * 20 @@ -299,7 +299,8 @@ def test_advanced_transaction_lifecycle_keeps_projection_explicit(): assert not any(call[0] == "canFinalize" for call in lifecycle_calls) -def test_local_transaction_lifecycle_decodes_the_exact_node_rpc_schema(): +@pytest.mark.parametrize("chain_id", [localnet.id, studio_devnet.id]) +def test_studio_transaction_lifecycle_decodes_the_exact_node_rpc_schema(chain_id): provider = Mock() provider.make_request.return_value = { "result": { @@ -317,7 +318,7 @@ def test_local_transaction_lifecycle_decodes_the_exact_node_rpc_schema(): } } client = SimpleNamespace( - chain=SimpleNamespace(id=localnet.id), + chain=SimpleNamespace(id=chain_id), provider=provider, ) From 31179d8eb50d54c317118dde18a71445172e13eb Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 3 Sep 2026 01:04:35 +0100 Subject: [PATCH 17/19] chore(release): 0.19.0-rc.1 --- CHANGELOG.md | 189 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 190 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a79635..6194611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,195 @@ +## v0.19.0-rc.1 (2026-09-03) + +### Bug Fixes + +- Align Python SDK consumers with the resolution-kernel train + ([#109](https://github.com/genlayerlabs/genlayer-py/pull/109), + [`85a4782`](https://github.com/genlayerlabs/genlayer-py/commit/85a47821c2771b5d931b754d4a43f1bd0f057246)) + +- Align Python SDK with train contracts + ([#109](https://github.com/genlayerlabs/genlayer-py/pull/109), + [`85a4782`](https://github.com/genlayerlabs/genlayer-py/commit/85a47821c2771b5d931b754d4a43f1bd0f057246)) + +- Bump package version to v0.19 ([#95](https://github.com/genlayerlabs/genlayer-py/pull/95), + [`ec7cab9`](https://github.com/genlayerlabs/genlayer-py/commit/ec7cab9f33cd0b5e40003d1dd21a4be6fef2611d)) + +- Include execution budget floor in fee estimates + ([#89](https://github.com/genlayerlabs/genlayer-py/pull/89), + [`61dd2c0`](https://github.com/genlayerlabs/genlayer-py/commit/61dd2c0c3b4f0a83771aeb030a0361a141f51e69)) + +- Include pending transactions in default nonce lookup + ([#100](https://github.com/genlayerlabs/genlayer-py/pull/100), + [`3583471`](https://github.com/genlayerlabs/genlayer-py/commit/3583471d5c4a8c18da54850961dee541fee1e5e7)) + +- Keep studio appeals on the pre-train call shape + ([#109](https://github.com/genlayerlabs/genlayer-py/pull/109), + [`85a4782`](https://github.com/genlayerlabs/genlayer-py/commit/85a47821c2771b5d931b754d4a43f1bd0f057246)) + +- Read the consensus surfaces the resolution-kernel train exposes + ([#109](https://github.com/genlayerlabs/genlayer-py/pull/109), + [`85a4782`](https://github.com/genlayerlabs/genlayer-py/commit/85a47821c2771b5d931b754d4a43f1bd0f057246)) + +- Resolve v0.19 bug hunt regressions ([#99](https://github.com/genlayerlabs/genlayer-py/pull/99), + [`3106bc8`](https://github.com/genlayerlabs/genlayer-py/commit/3106bc87955d612b5bb43fa67e225b659b657c55)) + +- Resolve v0.19-dev bug-hunt findings ([#99](https://github.com/genlayerlabs/genlayer-py/pull/99), + [`3106bc8`](https://github.com/genlayerlabs/genlayer-py/commit/3106bc87955d612b5bb43fa67e225b659b657c55)) + +- Serialize fee payloads for studio estimates + ([#88](https://github.com/genlayerlabs/genlayer-py/pull/88), + [`5d8d1ce`](https://github.com/genlayerlabs/genlayer-py/commit/5d8d1ce5d74308dc772458a13c81bfff3ce56e1d)) + +- **appeals**: Admit unfunded rounds safely + ([#110](https://github.com/genlayerlabs/genlayer-py/pull/110), + [`b88e492`](https://github.com/genlayerlabs/genlayer-py/commit/b88e4929f31587cb3658174ed175f906c2e0fc05)) + +- **appeals**: Support Studio decision-bound lifecycle + ([#109](https://github.com/genlayerlabs/genlayer-py/pull/109), + [`85a4782`](https://github.com/genlayerlabs/genlayer-py/commit/85a47821c2771b5d931b754d4a43f1bd0f057246)) + +- **fees**: Add exact quote and appeal parity + ([#110](https://github.com/genlayerlabs/genlayer-py/pull/110), + [`b88e492`](https://github.com/genlayerlabs/genlayer-py/commit/b88e4929f31587cb3658174ed175f906c2e0fc05)) + +- **fees**: Encode internal message price caps + ([#110](https://github.com/genlayerlabs/genlayer-py/pull/110), + [`b88e492`](https://github.com/genlayerlabs/genlayer-py/commit/b88e4929f31587cb3658174ed175f906c2e0fc05)) + +- **fees**: Encode schedule-free topups + ([#110](https://github.com/genlayerlabs/genlayer-py/pull/110), + [`b88e492`](https://github.com/genlayerlabs/genlayer-py/commit/b88e4929f31587cb3658174ed175f906c2e0fc05)) + +- **fees**: Fund default consensus rotations + ([#109](https://github.com/genlayerlabs/genlayer-py/pull/109), + [`85a4782`](https://github.com/genlayerlabs/genlayer-py/commit/85a47821c2771b5d931b754d4a43f1bd0f057246)) + +- **fees**: Mirror consensus deposit quote + ([#110](https://github.com/genlayerlabs/genlayer-py/pull/110), + [`b88e492`](https://github.com/genlayerlabs/genlayer-py/commit/b88e4929f31587cb3658174ed175f906c2e0fc05)) + +- **fees**: Move wildcard callKey sentinel to keccak256(empty), deploy key = bytes32(0) + ([#93](https://github.com/genlayerlabs/genlayer-py/pull/93), + [`6bfa86a`](https://github.com/genlayerlabs/genlayer-py/commit/6bfa86a160a25d789630944e4ed05f0214fc164d)) + +- **genvm**: Give VecDB its explicit distance metric 🐛 + ([#105](https://github.com/genlayerlabs/genlayer-py/pull/105), + [`39d6a37`](https://github.com/genlayerlabs/genlayer-py/commit/39d6a37a4e6f2bac93ac3c30587a2c53f30d6d53)) + +- **studio**: Bind appeal actions to decisions + ([#110](https://github.com/genlayerlabs/genlayer-py/pull/110), + [`b88e492`](https://github.com/genlayerlabs/genlayer-py/commit/b88e4929f31587cb3658174ed175f906c2e0fc05)) + +- **studio**: Decouple native appeal and lifecycle reads + ([#109](https://github.com/genlayerlabs/genlayer-py/pull/109), + [`85a4782`](https://github.com/genlayerlabs/genlayer-py/commit/85a47821c2771b5d931b754d4a43f1bd0f057246)) + +- **studio**: Surface mined envelope reverts + ([#110](https://github.com/genlayerlabs/genlayer-py/pull/110), + [`b88e492`](https://github.com/genlayerlabs/genlayer-py/commit/b88e4929f31587cb3658174ed175f906c2e0fc05)) + +### Chores + +- **genvm**: Update runner hashes and align v0.3 ABI names ⬆️ + ([#105](https://github.com/genlayerlabs/genlayer-py/pull/105), + [`39d6a37`](https://github.com/genlayerlabs/genlayer-py/commit/39d6a37a4e6f2bac93ac3c30587a2c53f30d6d53)) + +- **genvm**: Update runner hashes and rename accepted to decided ⬆️ + ([#105](https://github.com/genlayerlabs/genlayer-py/pull/105), + [`39d6a37`](https://github.com/genlayerlabs/genlayer-py/commit/39d6a37a4e6f2bac93ac3c30587a2c53f30d6d53)) + +### Continuous Integration + +- Keep main forwarded to active dev branch + ([#92](https://github.com/genlayerlabs/genlayer-py/pull/92), + [`dd25ef7`](https://github.com/genlayerlabs/genlayer-py/commit/dd25ef7f43e99a14b8fe42a64e01374845ad4d2d)) + +- Run tests on pushes to dev branches ([#90](https://github.com/genlayerlabs/genlayer-py/pull/90), + [`bc04db8`](https://github.com/genlayerlabs/genlayer-py/commit/bc04db8999b86979273408511c90c9a89620ff49)) + +- Scope down release/sync-docs to dedicated GitHub Apps + ([#76](https://github.com/genlayerlabs/genlayer-py/pull/76), + [`375c1c1`](https://github.com/genlayerlabs/genlayer-py/commit/375c1c1b94929fa6536b9481cbb9e06a06970190)) + +- Skip pre-train smoke on v0.19 PRs ([#109](https://github.com/genlayerlabs/genlayer-py/pull/109), + [`85a4782`](https://github.com/genlayerlabs/genlayer-py/commit/85a47821c2771b5d931b754d4a43f1bd0f057246)) + +- **workflows**: Sync e2e-housekeeper.yml from genlayer-e2e + ([#83](https://github.com/genlayerlabs/genlayer-py/pull/83), + [`528f2a9`](https://github.com/genlayerlabs/genlayer-py/commit/528f2a99ba915bfd1633f95fc936855f59b138d1)) + +- **workflows**: Sync e2e-housekeeper.yml from genlayer-e2e + ([#81](https://github.com/genlayerlabs/genlayer-py/pull/81), + [`3d79679`](https://github.com/genlayerlabs/genlayer-py/commit/3d796797d7594669f095e173a50b2bfbf065a588)) + +- **workflows**: Sync e2e.yml from genlayer-e2e + ([#82](https://github.com/genlayerlabs/genlayer-py/pull/82), + [`9af83c6`](https://github.com/genlayerlabs/genlayer-py/commit/9af83c60ae952a7daa3569ea3b6c53e673498b6f)) + +- **workflows**: Sync e2e.yml from genlayer-e2e + ([#80](https://github.com/genlayerlabs/genlayer-py/pull/80), + [`1b3137a`](https://github.com/genlayerlabs/genlayer-py/commit/1b3137aeb3cd92c7496e977806e04ee6fdeaefb7)) + +### Documentation + +- Add branching guide ([#94](https://github.com/genlayerlabs/genlayer-py/pull/94), + [`ab386b4`](https://github.com/genlayerlabs/genlayer-py/commit/ab386b4139a7019639dcf4e6eae0b6830c0f52a8)) + +### Features + +- Add fee-aware transaction helpers + ([`8f35989`](https://github.com/genlayerlabs/genlayer-py/commit/8f359891377530827ce0d062b5499baffa9b54e7)) + +- Branch-per-major release model + ([`8314002`](https://github.com/genlayerlabs/genlayer-py/commit/8314002b7bd510927940527fe08f15987186a486)) + +- Branch-per-major release model ([#78](https://github.com/genlayerlabs/genlayer-py/pull/78), + [`88ad157`](https://github.com/genlayerlabs/genlayer-py/commit/88ad157c84b47b7df9602d62f009d5ad8cf8529c)) + +- Prepare v0.19 Studio preview and RC release + ([#113](https://github.com/genlayerlabs/genlayer-py/pull/113), + [`34bff46`](https://github.com/genlayerlabs/genlayer-py/commit/34bff4676be3aaa86f8821136d3aacac58cea073)) + +- **calldata**: Migrate method-call key "method" -> "" (v0.6 genvm ABI) + ([#96](https://github.com/genlayerlabs/genlayer-py/pull/96), + [`ef36623`](https://github.com/genlayerlabs/genlayer-py/commit/ef36623ac7e36fb36701449f66bb61f6518d9d2e)) + +- **fees**: Estimation correctness, wait-for-decided semantics, v0.6 parity with genlayer-js + ([#90](https://github.com/genlayerlabs/genlayer-py/pull/90), + [`bc04db8`](https://github.com/genlayerlabs/genlayer-py/commit/bc04db8999b86979273408511c90c9a89620ff49)) + +- **staking**: Support the two-step operator rotation + ([#104](https://github.com/genlayerlabs/genlayer-py/pull/104), + [`2a689c0`](https://github.com/genlayerlabs/genlayer-py/commit/2a689c0465e8267a5e79641dd754850175d55c6e)) + +- **transactions**: Layer consumer and protocol lifecycles + ([#109](https://github.com/genlayerlabs/genlayer-py/pull/109), + [`85a4782`](https://github.com/genlayerlabs/genlayer-py/commit/85a47821c2771b5d931b754d4a43f1bd0f057246)) + +- **vesting**: Add vesting staking actions + ([#97](https://github.com/genlayerlabs/genlayer-py/pull/97), + [`30c262c`](https://github.com/genlayerlabs/genlayer-py/commit/30c262c48ec5f089223c584b381101074381e397)) + +- **vesting**: Validator-leg actions (join/deposit/exit/claim, operator transfer, identity, wallet + reads) ([#97](https://github.com/genlayerlabs/genlayer-py/pull/97), + [`30c262c`](https://github.com/genlayerlabs/genlayer-py/commit/30c262c48ec5f089223c584b381101074381e397)) + +- **vesting**: Vesting staking actions + ABI + ([#97](https://github.com/genlayerlabs/genlayer-py/pull/97), + [`30c262c`](https://github.com/genlayerlabs/genlayer-py/commit/30c262c48ec5f089223c584b381101074381e397)) + +### Testing + +- Add v0.19 bug hunt regressions ([#99](https://github.com/genlayerlabs/genlayer-py/pull/99), + [`3106bc8`](https://github.com/genlayerlabs/genlayer-py/commit/3106bc87955d612b5bb43fa67e225b659b657c55)) + +- **e2e**: Migrate contract fixtures to genvm v0.3 SDK API + ([#96](https://github.com/genlayerlabs/genlayer-py/pull/96), + [`ef36623`](https://github.com/genlayerlabs/genlayer-py/commit/ef36623ac7e36fb36701449f66bb61f6518d9d2e)) + + ## v0.18.0 (2026-04-22) ### Bug Fixes diff --git a/pyproject.toml b/pyproject.toml index 8b401c2..353645f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "genlayer-py" -version = "0.19.0" +version = "0.19.0-rc.1" description = "GenLayer Python SDK" authors = [ { name = "GenLayer" } From 4556920d58108642131089f5ecc0a678d592a400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgars=20Nem=C5=A1e?= Date: Thu, 3 Sep 2026 15:13:57 +0100 Subject: [PATCH 18/19] fix: align localnet chain id with Studio (#115) * fix: align localnet chain id with Studio * build: keep release lock metadata in sync --- .github/workflows/tests.yml | 3 ++ genlayer_py/chains/localnet.py | 2 +- genlayer_py/chains/utils.py | 2 +- releaserc.toml | 3 +- tests/unit/chains/test_chain_presets.py | 13 ++++++++ tests/unit/contracts/test_contract_actions.py | 30 ++++++++++++++----- uv.lock | 2 +- 7 files changed, 43 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 216cc65..8873a61 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -26,6 +26,9 @@ jobs: - name: Setup | Install Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} + - name: Check | Verify locked package metadata + run: uv lock --check + - name: Setup | Install Dependencies run: uv sync --group dev --python ${{ matrix.python-version }} diff --git a/genlayer_py/chains/localnet.py b/genlayer_py/chains/localnet.py index e23c9b1..10dbf13 100644 --- a/genlayer_py/chains/localnet.py +++ b/genlayer_py/chains/localnet.py @@ -16,7 +16,7 @@ } localnet: GenLayerChain = GenLayerChain( - id=61999, + id=61127, name="GenLayer Localnet", rpc_urls={ "default": { diff --git a/genlayer_py/chains/utils.py b/genlayer_py/chains/utils.py index be7c5ae..c339e3a 100644 --- a/genlayer_py/chains/utils.py +++ b/genlayer_py/chains/utils.py @@ -3,7 +3,7 @@ from genlayer_py.types import GenLayerChain -STUDIO_CHAIN_IDS = frozenset({61997, 61999}) +STUDIO_CHAIN_IDS = frozenset({61127, 61997, 61999}) def is_studio_chain(chain: GenLayerChain) -> bool: diff --git a/releaserc.toml b/releaserc.toml index eae5145..dac3fbb 100644 --- a/releaserc.toml +++ b/releaserc.toml @@ -1,6 +1,7 @@ [semantic_release] version_toml = ["pyproject.toml:project.version"] -assets = [] +assets = ["uv.lock"] +build_command = "uv lock" build_command_env = [] commit_message = "chore(release): {version}" commit_parser = "conventional" diff --git a/tests/unit/chains/test_chain_presets.py b/tests/unit/chains/test_chain_presets.py index a79e95d..f500829 100644 --- a/tests/unit/chains/test_chain_presets.py +++ b/tests/unit/chains/test_chain_presets.py @@ -1,6 +1,7 @@ import genlayer_py from genlayer_py.chains import __all__ as chain_exports +from genlayer_py.chains.localnet import localnet from genlayer_py.chains.studio_devnet import ( STUDIO_DEVNET_EXPLORER_URL, STUDIO_DEVNET_JSON_RPC_URL, @@ -11,6 +12,13 @@ from genlayer_py.chains.utils import is_studio_chain +def test_localnet_uses_the_canonical_local_studio_chain_id(): + assert localnet.id == 61127 + assert localnet.rpc_urls == { + "default": {"http": ["http://127.0.0.1:4000/api"]} + } + + def test_studio_devnet_is_exported_with_canonical_preview_coordinates(): assert "studio_devnet" in chain_exports assert genlayer_py.studio_devnet is studio_devnet @@ -41,6 +49,11 @@ def test_stable_studionet_coordinates_do_not_drift_with_preview_preset(): def test_studio_chain_classification_includes_preview_but_not_public_testnet(): + assert is_studio_chain(localnet) assert is_studio_chain(studionet) assert is_studio_chain(studio_devnet) assert not is_studio_chain(testnet_asimov) + + +def test_studio_presets_have_distinct_chain_ids(): + assert len({localnet.id, studio_devnet.id, studionet.id}) == 3 diff --git a/tests/unit/contracts/test_contract_actions.py b/tests/unit/contracts/test_contract_actions.py index 03c706a..bc8b36a 100644 --- a/tests/unit/contracts/test_contract_actions.py +++ b/tests/unit/contracts/test_contract_actions.py @@ -751,9 +751,17 @@ def test_encode_submit_appeal_still_requires_a_decision_id_on_the_train_shape(): contract_actions._encode_submit_appeal_data(self=client, transaction_id=TX_ID) -def test_send_consensus_call_returns_localnet_envelope_hash_after_receipt(monkeypatch): +def test_send_consensus_call_signs_for_canonical_local_studio_chain(): wait_for_transaction_receipt = Mock(return_value=SimpleNamespace(status=1)) sign_transaction = Mock(return_value=SimpleNamespace(raw_transaction=b"\x12\x34")) + + def make_request(method, params): + if method == "eth_estimateGas": + return {"result": "0x5208"} + if method == "eth_sendRawTransaction": + return {"result": TX_ID} + raise AssertionError(f"unexpected method {method}") + client = SimpleNamespace( chain=SimpleNamespace( id=localnet.id, @@ -761,7 +769,8 @@ def test_send_consensus_call_returns_localnet_envelope_hash_after_receipt(monkey "address": "0x3333333333333333333333333333333333333333", }, ), - provider=SimpleNamespace(make_request=Mock(return_value={"result": TX_ID})), + get_current_nonce=Mock(return_value=7), + provider=SimpleNamespace(make_request=Mock(side_effect=make_request)), w3=SimpleNamespace( to_hex=Mock(return_value="0xsigned"), eth=SimpleNamespace( @@ -771,12 +780,6 @@ def test_send_consensus_call_returns_localnet_envelope_hash_after_receipt(monkey ) account = SimpleNamespace(address=SENDER, sign_transaction=sign_transaction) - monkeypatch.setattr( - contract_actions, - "_prepare_transaction", - Mock(return_value={"from": SENDER}), - ) - result = contract_actions._send_consensus_call( self=client, encoded_data="0x1234", @@ -786,6 +789,17 @@ def test_send_consensus_call_returns_localnet_envelope_hash_after_receipt(monkey ) assert result == TX_ID + transaction = sign_transaction.call_args.args[0] + assert transaction == { + "from": SENDER, + "nonce": "0x7", + "data": "0x1234", + "to": "0x3333333333333333333333333333333333333333", + "value": "0x1", + "gasPrice": 0, + "chainId": 61127, + "gas": "0x5208", + } wait_for_transaction_receipt.assert_called_once_with(TX_ID) diff --git a/uv.lock b/uv.lock index 392c1a0..ce37887 100644 --- a/uv.lock +++ b/uv.lock @@ -434,7 +434,7 @@ wheels = [ [[package]] name = "genlayer-py" -version = "0.19.0" +version = "0.19.0rc1" source = { editable = "." } dependencies = [ { name = "web3" }, From 4ea0055f19c17e42d6837de7276b77a319c5e8b1 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 3 Sep 2026 15:22:07 +0100 Subject: [PATCH 19/19] chore(release): 0.19.0-rc.2 --- CHANGELOG.md | 13 +++++++++++++ pyproject.toml | 2 +- uv.lock | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6194611..5000bc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ +## v0.19.0-rc.2 (2026-09-03) + +### Bug Fixes + +- Align localnet chain id with Studio ([#115](https://github.com/genlayerlabs/genlayer-py/pull/115), + [`4556920`](https://github.com/genlayerlabs/genlayer-py/commit/4556920d58108642131089f5ecc0a678d592a400)) + +### Build System + +- Keep release lock metadata in sync ([#115](https://github.com/genlayerlabs/genlayer-py/pull/115), + [`4556920`](https://github.com/genlayerlabs/genlayer-py/commit/4556920d58108642131089f5ecc0a678d592a400)) + + ## v0.19.0-rc.1 (2026-09-03) ### Bug Fixes diff --git a/pyproject.toml b/pyproject.toml index 353645f..6343772 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "genlayer-py" -version = "0.19.0-rc.1" +version = "0.19.0-rc.2" description = "GenLayer Python SDK" authors = [ { name = "GenLayer" } diff --git a/uv.lock b/uv.lock index ce37887..fe9d491 100644 --- a/uv.lock +++ b/uv.lock @@ -434,7 +434,7 @@ wheels = [ [[package]] name = "genlayer-py" -version = "0.19.0rc1" +version = "0.19.0rc2" source = { editable = "." } dependencies = [ { name = "web3" },