From 5ad3463c95ceded0733fba013c6734710cee0869 Mon Sep 17 00:00:00 2001 From: Edgars Date: Wed, 22 Apr 2026 14:46:13 +0100 Subject: [PATCH 1/3] feat(staking): add StakingActions to GenLayerClient MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors genlayer-js StakingActions so Python consumers don't have to drop to raw web3.py to stake, delegate, rotate operators, etc. Bundles slimmed IGenLayerStaking and ValidatorWallet ABIs as JSON resources. Writes are routed according to on-chain sender checks: - validatorJoin/Claim/Prime and all delegator methods go to Staking with the caller EOA as msg.sender. - validatorDeposit/Exit are routed through the ValidatorWallet's own forwarders because Staking rejects those two with a wallet-only sender assertion. Same constraint is being fixed in genlayer-js. Reads: epoch, active_validators, active_validators_count, is_validator, get_validator_info, get_stake_info (stakeOf), banned_validators, validator_min_stake, delegator_min_stake. Writes: validator_join (payable, operator optional), validator_deposit, validator_exit, validator_claim, validator_prime, set_operator, set_identity, delegator_join, delegator_exit, delegator_claim. GenLayerChain gains an optional staking_contract (SimpleContractInfo); all existing presets pass None because staking is only configured when a caller wires it in. Add the address/ABI manually or via a future chain preset once the staking contract is published per network. Unit tests assert that each write encodes the right selector and targets the right contract — critical for the wallet-vs-staking routing fix. Lifecycle coverage lives in the ci-core-e2e-runner tooling suite (needs a live node). --- genlayer_py/chains/localnet.py | 1 + genlayer_py/chains/studionet.py | 1 + genlayer_py/chains/testnet_asimov.py | 1 + genlayer_py/chains/testnet_bradbury.py | 1 + genlayer_py/client/genlayer_client.py | 129 + genlayer_py/staking/__init__.py | 43 + genlayer_py/staking/abi/__init__.py | 16 + genlayer_py/staking/abi/staking_abi.json | 4033 +++++++++++++++++ .../staking/abi/validator_wallet_abi.json | 1002 ++++ genlayer_py/staking/actions.py | 308 ++ genlayer_py/types/chain.py | 1 + pyproject.toml | 2 +- tests/unit/staking/__init__.py | 0 tests/unit/staking/test_staking_actions.py | 142 + uv.lock | 2 +- 15 files changed, 5680 insertions(+), 2 deletions(-) create mode 100644 genlayer_py/staking/__init__.py create mode 100644 genlayer_py/staking/abi/__init__.py create mode 100644 genlayer_py/staking/abi/staking_abi.json create mode 100644 genlayer_py/staking/abi/validator_wallet_abi.json create mode 100644 genlayer_py/staking/actions.py create mode 100644 tests/unit/staking/__init__.py create mode 100644 tests/unit/staking/test_staking_actions.py diff --git a/genlayer_py/chains/localnet.py b/genlayer_py/chains/localnet.py index 39db76b..e23c9b1 100644 --- a/genlayer_py/chains/localnet.py +++ b/genlayer_py/chains/localnet.py @@ -33,6 +33,7 @@ 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/studionet.py b/genlayer_py/chains/studionet.py index a6ab986..7e96912 100644 --- a/genlayer_py/chains/studionet.py +++ b/genlayer_py/chains/studionet.py @@ -34,6 +34,7 @@ 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/testnet_asimov.py b/genlayer_py/chains/testnet_asimov.py index b740305..794f294 100644 --- a/genlayer_py/chains/testnet_asimov.py +++ b/genlayer_py/chains/testnet_asimov.py @@ -37,6 +37,7 @@ 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/testnet_bradbury.py b/genlayer_py/chains/testnet_bradbury.py index 3b062b9..352bf3c 100644 --- a/genlayer_py/chains/testnet_bradbury.py +++ b/genlayer_py/chains/testnet_bradbury.py @@ -134,6 +134,7 @@ 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 5bbac68..2c2e052 100644 --- a/genlayer_py/client/genlayer_client.py +++ b/genlayer_py/client/genlayer_client.py @@ -38,6 +38,27 @@ get_triggered_transaction_ids, debug_trace_transaction, ) +from genlayer_py.staking.actions import ( + validator_join, + validator_deposit, + validator_exit, + validator_claim, + validator_prime, + set_operator, + set_identity, + delegator_join, + delegator_exit, + delegator_claim, + epoch as staking_epoch, + active_validators, + active_validators_count, + is_validator, + get_validator_info, + get_stake_info, + banned_validators, + validator_min_stake, + delegator_min_stake, +) from genlayer_py.config import transaction_config @@ -272,3 +293,111 @@ def can_appeal(self, transaction_id: HexStr) -> bool: def get_min_appeal_bond(self, transaction_id: HexStr) -> int: """Calculates the minimum bond required to appeal a transaction.""" return get_min_appeal_bond(self=self, transaction_id=transaction_id) + + # ── Staking actions (EVM, not consensus-layer) ──────────────────── + # Mirrors genlayer-js StakingActions. Requires chain.staking_contract + # to be set — see examples/staking.py or the bradbury chain preset. + + def staking_epoch(self) -> int: + """Returns the current staking epoch.""" + return staking_epoch(self=self) + + def active_validators(self) -> List: + """Returns ValidatorWallet addresses active in the current epoch.""" + return active_validators(self=self) + + def active_validators_count(self) -> int: + return active_validators_count(self=self) + + def is_validator(self, address) -> bool: + return is_validator(self=self, address=address) + + def get_validator_info(self, validator) -> dict: + """Returns the raw validatorView struct for a validator wallet.""" + return get_validator_info(self=self, validator=validator) + + def get_stake_info(self, delegator, validator) -> dict: + """Returns a delegator's stake position on a validator.""" + return get_stake_info(self=self, delegator=delegator, validator=validator) + + def banned_validators(self, start_index: int = 0, size: int = 100) -> List: + return banned_validators(self=self, start_index=start_index, size=size) + + def validator_min_stake(self) -> int: + return validator_min_stake(self=self) + + def delegator_min_stake(self) -> int: + return delegator_min_stake(self=self) + + def validator_join( + self, + amount: int, + operator=None, + account: Optional[LocalAccount] = None, + ) -> HexBytes: + """Joins as a validator. Deploys a ValidatorWallet with msg.sender + as owner and `operator` (defaults to owner) as operator.""" + return validator_join( + self=self, amount=amount, operator=operator, account=account + ) + + def validator_deposit( + self, validator, amount: int, account: Optional[LocalAccount] = None + ) -> HexBytes: + """Adds stake to an active validator. Routed via the wallet so + Staking sees msg.sender == wallet (required by the contract).""" + return validator_deposit( + self=self, validator=validator, amount=amount, account=account + ) + + def validator_exit( + self, validator, shares: int, account: Optional[LocalAccount] = None + ) -> HexBytes: + """Burns validator shares. Routed via the wallet.""" + return validator_exit( + self=self, validator=validator, shares=shares, account=account + ) + + def validator_claim( + self, validator, account: Optional[LocalAccount] = None + ) -> HexBytes: + return validator_claim(self=self, validator=validator, account=account) + + def validator_prime( + self, validator, account: Optional[LocalAccount] = None + ) -> HexBytes: + return validator_prime(self=self, validator=validator, account=account) + + def set_operator( + self, validator, operator, account: Optional[LocalAccount] = None + ) -> HexBytes: + """Rotates the operator for an existing ValidatorWallet.""" + return set_operator( + self=self, validator=validator, operator=operator, account=account + ) + + def set_identity( + self, validator, moniker: str, account: Optional[LocalAccount] = None + ) -> HexBytes: + return set_identity( + self=self, validator=validator, moniker=moniker, account=account + ) + + def delegator_join( + self, validator, amount: int, account: Optional[LocalAccount] = None + ) -> HexBytes: + return delegator_join( + self=self, validator=validator, amount=amount, account=account + ) + + def delegator_exit( + self, validator, shares: int, account: Optional[LocalAccount] = None + ) -> HexBytes: + return delegator_exit( + self=self, validator=validator, shares=shares, account=account + ) + + def delegator_claim( + self, validator, account: Optional[LocalAccount] = None + ) -> HexBytes: + return delegator_claim(self=self, validator=validator, account=account) diff --git a/genlayer_py/staking/__init__.py b/genlayer_py/staking/__init__.py new file mode 100644 index 0000000..8c8fa92 --- /dev/null +++ b/genlayer_py/staking/__init__.py @@ -0,0 +1,43 @@ +from genlayer_py.staking.actions import ( + validator_join, + validator_deposit, + validator_exit, + validator_claim, + validator_prime, + set_operator, + set_identity, + delegator_join, + delegator_exit, + delegator_claim, + epoch, + active_validators, + active_validators_count, + is_validator, + get_validator_info, + get_stake_info, + banned_validators, + validator_min_stake, + delegator_min_stake, +) + +__all__ = [ + "validator_join", + "validator_deposit", + "validator_exit", + "validator_claim", + "validator_prime", + "set_operator", + "set_identity", + "delegator_join", + "delegator_exit", + "delegator_claim", + "epoch", + "active_validators", + "active_validators_count", + "is_validator", + "get_validator_info", + "get_stake_info", + "banned_validators", + "validator_min_stake", + "delegator_min_stake", +] diff --git a/genlayer_py/staking/abi/__init__.py b/genlayer_py/staking/abi/__init__.py new file mode 100644 index 0000000..5956002 --- /dev/null +++ b/genlayer_py/staking/abi/__init__.py @@ -0,0 +1,16 @@ +import json +import importlib.resources + +with importlib.resources.as_file( + importlib.resources.files("genlayer_py.staking.abi").joinpath("staking_abi.json") +) as path, open(path, "r", encoding="utf-8") as f: + STAKING_ABI = json.load(f) + +with importlib.resources.as_file( + importlib.resources.files("genlayer_py.staking.abi").joinpath( + "validator_wallet_abi.json" + ) +) as path, open(path, "r", encoding="utf-8") as f: + VALIDATOR_WALLET_ABI = json.load(f) + +__all__ = ["STAKING_ABI", "VALIDATOR_WALLET_ABI"] diff --git a/genlayer_py/staking/abi/staking_abi.json b/genlayer_py/staking/abi/staking_abi.json new file mode 100644 index 0000000..fba7fd3 --- /dev/null +++ b/genlayer_py/staking/abi/staking_abi.json @@ -0,0 +1,4033 @@ +[ + { + "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" + } +] diff --git a/genlayer_py/staking/abi/validator_wallet_abi.json b/genlayer_py/staking/abi/validator_wallet_abi.json new file mode 100644 index 0000000..cfd125a --- /dev/null +++ b/genlayer_py/staking/abi/validator_wallet_abi.json @@ -0,0 +1,1002 @@ +[ + { + "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" + } +] diff --git a/genlayer_py/staking/actions.py b/genlayer_py/staking/actions.py new file mode 100644 index 0000000..065b465 --- /dev/null +++ b/genlayer_py/staking/actions.py @@ -0,0 +1,308 @@ +"""Staking actions for GenLayerClient. + +Mirrors the genlayer-js StakingActions module. All methods operate on +the Staking contract at `chain.staking_contract["address"]` except the +validator-wallet-only writes (validatorDeposit, validatorExit), which +route through the ValidatorWallet so msg.sender on Staking is the +wallet contract — not the operator EOA — per the on-chain sender check. +""" + +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.staking.abi import STAKING_ABI, VALIDATOR_WALLET_ABI + +if TYPE_CHECKING: + from genlayer_py.client import GenLayerClient + + +AddressLike = Union[Address, ChecksumAddress, str] + + +def _require_staking(self: "GenLayerClient") -> ChecksumAddress: + if self.chain.staking_contract is None: + raise GenLayerError( + "staking_contract not configured for this chain — set chain.staking_contract" + ) + return self.w3.to_checksum_address(self.chain.staking_contract["address"]) + + +def _staking(self: "GenLayerClient"): + return self.w3.eth.contract(address=_require_staking(self), abi=STAKING_ABI) + + +def _wallet(self: "GenLayerClient", validator: AddressLike): + return self.w3.eth.contract( + address=self.w3.to_checksum_address(validator), abi=VALIDATOR_WALLET_ABI + ) + + +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 epoch(self: "GenLayerClient") -> int: + return _staking(self).functions.epoch().call() + + +def active_validators(self: "GenLayerClient") -> List[ChecksumAddress]: + return _staking(self).functions.activeValidators().call() + + +def active_validators_count(self: "GenLayerClient") -> int: + return _staking(self).functions.activeValidatorsCount().call() + + +def is_validator(self: "GenLayerClient", address: AddressLike) -> bool: + return ( + _staking(self) + .functions.isValidator(self.w3.to_checksum_address(address)) + .call() + ) + + +def get_validator_info(self: "GenLayerClient", validator: AddressLike) -> dict: + """Returns the raw validatorView struct for a validator wallet.""" + return ( + _staking(self) + .functions.validatorView(self.w3.to_checksum_address(validator)) + .call() + ) + + +def get_stake_info( + self: "GenLayerClient", delegator: AddressLike, validator: AddressLike +) -> tuple: + """Returns (shares, stake) for a delegator on a specific validator.""" + return ( + _staking(self) + .functions.stakeOf( + self.w3.to_checksum_address(delegator), + self.w3.to_checksum_address(validator), + ) + .call() + ) + + +def banned_validators( + self: "GenLayerClient", start_index: int = 0, size: int = 100 +) -> List[ChecksumAddress]: + return _staking(self).functions.banned(start_index, size).call() + + +def validator_min_stake(self: "GenLayerClient") -> int: + return _staking(self).functions.validatorMinStake().call() + + +def delegator_min_stake(self: "GenLayerClient") -> int: + return _staking(self).functions.delegatorMinStake().call() + + +# ─── write methods ──────────────────────────────────────────────────── + + +def validator_join( + self: "GenLayerClient", + amount: int, + operator: Optional[AddressLike] = None, + account: Optional[LocalAccount] = 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).""" + sender = _sender(self, account) + 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=[]) + tx = _build(self, sender, _require_staking(self), data, value=amount) + return _send(self, sender, tx) + + +def validator_deposit( + self: "GenLayerClient", + validator: AddressLike, + amount: int, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Adds self-stake to an active validator position. Sent through + the ValidatorWallet so that Staking sees msg.sender == wallet.""" + 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) + return _send(self, sender, tx) + + +def validator_exit( + self: "GenLayerClient", + validator: AddressLike, + shares: int, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Burns `shares` of a validator position. Routed through wallet.""" + sender = _sender(self, account) + wallet = _wallet(self, validator) + data = wallet.encode_abi("validatorExit", args=[shares]) + tx = _build(self, sender, self.w3.to_checksum_address(validator), data) + return _send(self, sender, tx) + + +def validator_claim( + self: "GenLayerClient", + validator: AddressLike, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Claims pending withdrawals on a validator. Staking accepts this + from any caller (it just pays out to the validator wallet owner).""" + sender = _sender(self, account) + contract = _staking(self) + data = contract.encode_abi( + "validatorClaim", args=[self.w3.to_checksum_address(validator)] + ) + tx = _build(self, sender, _require_staking(self), data) + return _send(self, sender, tx) + + +def validator_prime( + self: "GenLayerClient", + validator: AddressLike, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Primes a validator for the next epoch.""" + sender = _sender(self, account) + contract = _staking(self) + data = contract.encode_abi( + "validatorPrime", args=[self.w3.to_checksum_address(validator)] + ) + tx = _build(self, sender, _require_staking(self), data) + return _send(self, sender, tx) + + +def set_operator( + self: "GenLayerClient", + validator: AddressLike, + 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.""" + sender = _sender(self, account) + wallet = _wallet(self, validator) + data = wallet.encode_abi( + "setOperator", args=[self.w3.to_checksum_address(operator)] + ) + tx = _build(self, sender, self.w3.to_checksum_address(validator), data) + return _send(self, sender, tx) + + +def set_identity( + self: "GenLayerClient", + validator: AddressLike, + moniker: str, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Sets the public moniker for a validator wallet.""" + sender = _sender(self, account) + wallet = _wallet(self, validator) + data = wallet.encode_abi("setIdentity", args=[moniker]) + tx = _build(self, sender, self.w3.to_checksum_address(validator), data) + return _send(self, sender, tx) + + +def delegator_join( + self: "GenLayerClient", + validator: AddressLike, + amount: int, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Delegates `amount` GEN to a validator.""" + sender = _sender(self, account) + contract = _staking(self) + data = contract.encode_abi( + "delegatorJoin", args=[self.w3.to_checksum_address(validator)] + ) + tx = _build(self, sender, _require_staking(self), data, value=amount) + return _send(self, sender, tx) + + +def delegator_exit( + self: "GenLayerClient", + validator: AddressLike, + shares: int, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Burns `shares` of a delegator position on a specific validator.""" + sender = _sender(self, account) + contract = _staking(self) + data = contract.encode_abi( + "delegatorExit", args=[self.w3.to_checksum_address(validator), shares] + ) + tx = _build(self, sender, _require_staking(self), data) + return _send(self, sender, tx) + + +def delegator_claim( + self: "GenLayerClient", + validator: AddressLike, + account: Optional[LocalAccount] = None, +) -> HexBytes: + """Claims pending delegator withdrawals from a validator.""" + sender = _sender(self, account) + contract = _staking(self) + data = contract.encode_abi( + "delegatorClaim", args=[self.w3.to_checksum_address(validator)] + ) + tx = _build(self, sender, _require_staking(self), data) + return _send(self, sender, tx) diff --git a/genlayer_py/types/chain.py b/genlayer_py/types/chain.py index fad13d7..788d360 100644 --- a/genlayer_py/types/chain.py +++ b/genlayer_py/types/chain.py @@ -37,5 +37,6 @@ class GenLayerChain(Chain): fee_manager_contract: Optional[SimpleContractInfo] rounds_storage_contract: Optional[SimpleContractInfo] appeals_contract: Optional[SimpleContractInfo] + staking_contract: Optional[SimpleContractInfo] default_number_of_initial_validators: int default_consensus_max_rotations: int diff --git a/pyproject.toml b/pyproject.toml index c455147..aad35df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ where = ["."] exclude = ["tests*", "scripts*"] [tool.setuptools.package-data] -"genlayer_py" = ["consensus/abi/*.json"] +"genlayer_py" = ["consensus/abi/*.json", "staking/abi/*.json"] [dependency-groups] dev = [ diff --git a/tests/unit/staking/__init__.py b/tests/unit/staking/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/staking/test_staking_actions.py b/tests/unit/staking/test_staking_actions.py new file mode 100644 index 0000000..3969a41 --- /dev/null +++ b/tests/unit/staking/test_staking_actions.py @@ -0,0 +1,142 @@ +"""Unit tests for the staking action module. + +The tests here are structural — they check that methods encode the +expected ABI signature and target the right contract (Staking vs. +ValidatorWallet). End-to-end stake lifecycle is covered in the +ci-core-e2e-runner tooling suite instead, since it needs 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.staking.actions as staking_actions +from genlayer_py.staking.abi import STAKING_ABI, VALIDATOR_WALLET_ABI + + +STAKING_ADDR = "0x1111111111111111111111111111111111111111" +WALLET_ADDR = "0x2222222222222222222222222222222222222222" +SENDER_ADDR = "0x3333333333333333333333333333333333333333" +OTHER_ADDR = "0x4444444444444444444444444444444444444444" + +# 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_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() + + +def _make_client(): + """SimpleNamespace stand-in for GenLayerClient — enough surface for + the action helpers. 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) + # gas_price is a property on Eth — stub the attr path used by _build. + 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, + staking_contract={"address": STAKING_ADDR, "abi": STAKING_ABI}, + ) + 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 test_validator_join_no_operator_targets_staking_and_encodes_empty_args(): + client = _make_client() + staking_actions.validator_join(self=client, amount=10) + 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 + + +def test_validator_join_with_operator_encodes_address_variant(): + 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 + + +def test_validator_deposit_targets_wallet_not_staking(): + """The sender check on Staking.validatorDeposit requires msg.sender + to be the ValidatorWallet — the SDK must route through the wallet.""" + client = _make_client() + staking_actions.validator_deposit(self=client, validator=WALLET_ADDR, amount=5) + tx = _last_tx(client) + assert tx["to"].lower() == WALLET_ADDR.lower() + assert tx["to"].lower() != STAKING_ADDR.lower() + assert tx["value"] == 5 + assert tx["data"][2:10] == SEL_WALLET_DEPOSIT + + +def test_validator_exit_routes_through_wallet(): + client = _make_client() + staking_actions.validator_exit(self=client, validator=WALLET_ADDR, shares=42) + tx = _last_tx(client) + assert tx["to"].lower() == WALLET_ADDR.lower() + assert tx["data"][2:10] == SEL_WALLET_EXIT + + +def test_set_operator_routes_through_wallet(): + 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 + + +def test_delegator_join_targets_staking_with_value(): + client = _make_client() + staking_actions.delegator_join(self=client, validator=WALLET_ADDR, amount=7) + tx = _last_tx(client) + assert tx["to"].lower() == STAKING_ADDR.lower() + assert tx["value"] == 7 + assert tx["data"][2:10] == SEL_DELEGATOR_JOIN + + +def test_staking_not_configured_raises(): + client = _make_client() + client.chain.staking_contract = None + with pytest.raises(Exception, match="staking_contract"): + staking_actions.epoch(self=client) + + +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"} + assert { + "epoch", + "activeValidators", + "activeValidatorsCount", + "isValidator", + "validatorView", + "stakeOf", + "validatorJoin", + "validatorClaim", + "validatorPrime", + "delegatorJoin", + "delegatorExit", + "delegatorClaim", + }.issubset(names) + assert {"validatorDeposit", "validatorExit", "setOperator", "setIdentity"}.issubset( + wallet_names + ) diff --git a/uv.lock b/uv.lock index 3550a56..9c5651b 100644 --- a/uv.lock +++ b/uv.lock @@ -434,7 +434,7 @@ wheels = [ [[package]] name = "genlayer-py" -version = "0.10.1" +version = "0.16.3" source = { editable = "." } dependencies = [ { name = "web3" }, From d12cbbeebea63e2247b484e8fa76efa3af302abb Mon Sep 17 00:00:00 2001 From: Edgars Date: Wed, 22 Apr 2026 15:09:11 +0100 Subject: [PATCH 2/3] fix(smoke): route connectivity probes through GenLayerProvider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The smoke tests were constructing a stock Web3.HTTPProvider directly. web3.py's HTTPProvider starts its request counter at 0, so every fresh Web3() sends id=0 on its first RPC call. The GenLayer RPC servers reject id=0 with "Invalid Request: Key: 'Request.ID' Error:Field validation for 'ID' failed on the 'required' tag" (go-playground validator's `required` treats int zero as unset), so every fresh HTTPProvider's first call fails. GenLayerProvider (the SDK's own provider) ids requests by int(time.time() * 1000), which is always nonzero, and is the path real consumers already use. Switch the smoke tests to construct via create_client() so we're testing the SDK path, not a bypass. test_rpc_connects no longer calls Web3.is_connected — GenLayerProvider deliberately doesn't implement BaseProvider.is_connected, and any successful RPC call is a stronger liveness signal anyway (chain_id matching 4221 confirms we're reaching the right network too). --- tests/unit/smoke/test_bradbury_smoke.py | 30 +++++++++++++++++-------- tests/unit/smoke/test_testnet_smoke.py | 28 +++++++++++++++-------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/tests/unit/smoke/test_bradbury_smoke.py b/tests/unit/smoke/test_bradbury_smoke.py index 9bd0424..778f1cb 100644 --- a/tests/unit/smoke/test_bradbury_smoke.py +++ b/tests/unit/smoke/test_bradbury_smoke.py @@ -39,19 +39,30 @@ def test_consensus_data_address(self): @pytest.mark.testnet class TestBradburyConnectivity: - """Verify RPC connectivity to Bradbury testnet.""" + """Verify RPC connectivity to Bradbury testnet. + + Uses create_client() rather than a stock Web3.HTTPProvider: the + Bradbury RPC rejects JSON-RPC requests with id=0 as an "Invalid + Request" (server-side go-playground/validator `required` tag treats + int zero as unset). Stock web3.py's HTTPProvider starts its request + counter at 0, so the first probe on any fresh Web3() fails. The SDK + provider (GenLayerProvider) ids requests by timestamp and is the + path real consumers take.""" def test_rpc_connects(self): - w3 = Web3(Web3.HTTPProvider(TESTNET_JSON_RPC_URL)) - assert w3.is_connected() + # Success of any RPC call is what we actually care about; + # GenLayerProvider doesn't implement the BaseProvider.is_connected + # liveness check web3.py calls from Web3().is_connected. + client = create_client(chain=testnet_bradbury) + assert client.chain_id == 4221 def test_chain_id_matches(self): - w3 = Web3(Web3.HTTPProvider(TESTNET_JSON_RPC_URL)) - assert w3.eth.chain_id == 4221 + client = create_client(chain=testnet_bradbury) + assert client.chain_id == 4221 def test_block_number_positive(self): - w3 = Web3(Web3.HTTPProvider(TESTNET_JSON_RPC_URL)) - assert w3.eth.block_number > 0 + client = create_client(chain=testnet_bradbury) + assert client.block_number > 0 @pytest.mark.testnet @@ -62,8 +73,9 @@ class TestBradburyConsensusContract: def setup(self): from genlayer_py.consensus.abi import CONSENSUS_MAIN_ABI_V06 - self.w3 = Web3(Web3.HTTPProvider(TESTNET_JSON_RPC_URL)) - self.contract = self.w3.eth.contract( + client = create_client(chain=testnet_bradbury) + self.w3 = client + self.contract = client.contract( address=Web3.to_checksum_address(CONSENSUS_MAIN_CONTRACT["address"]), abi=CONSENSUS_MAIN_ABI_V06, ) diff --git a/tests/unit/smoke/test_testnet_smoke.py b/tests/unit/smoke/test_testnet_smoke.py index 9890f87..09dce89 100644 --- a/tests/unit/smoke/test_testnet_smoke.py +++ b/tests/unit/smoke/test_testnet_smoke.py @@ -11,6 +11,7 @@ CONSENSUS_MAIN_CONTRACT, CONSENSUS_DATA_CONTRACT, ) +from genlayer_py.client import create_client class TestTestnetConfig: @@ -37,19 +38,27 @@ def test_consensus_data_address(self): @pytest.mark.testnet class TestTestnetConnectivity: - """Verify RPC connectivity to testnet. Run with: pytest -m testnet""" + """Verify RPC connectivity to testnet. Run with: pytest -m testnet + + Uses create_client() so requests go through GenLayerProvider. + Stock Web3.HTTPProvider starts its request counter at 0, and the + GenLayer RPC servers reject id=0 as an "Invalid Request" (Go-side + validator treats int zero as unset).""" def test_rpc_connects(self): - w3 = Web3(Web3.HTTPProvider(TESTNET_JSON_RPC_URL)) - assert w3.is_connected() + # Success of any RPC call is what we actually care about; + # GenLayerProvider doesn't implement the BaseProvider.is_connected + # liveness check web3.py calls from Web3().is_connected. + client = create_client(chain=testnet_asimov) + assert client.chain_id == 4221 def test_chain_id_matches(self): - w3 = Web3(Web3.HTTPProvider(TESTNET_JSON_RPC_URL)) - assert w3.eth.chain_id == 4221 + client = create_client(chain=testnet_asimov) + assert client.chain_id == 4221 def test_block_number_positive(self): - w3 = Web3(Web3.HTTPProvider(TESTNET_JSON_RPC_URL)) - assert w3.eth.block_number > 0 + client = create_client(chain=testnet_asimov) + assert client.block_number > 0 @pytest.mark.testnet @@ -60,8 +69,9 @@ class TestConsensusContractReadOnly: def setup(self): from genlayer_py.consensus.abi import CONSENSUS_MAIN_ABI - self.w3 = Web3(Web3.HTTPProvider(TESTNET_JSON_RPC_URL)) - self.contract = self.w3.eth.contract( + client = create_client(chain=testnet_asimov) + self.w3 = client + self.contract = client.contract( address=Web3.to_checksum_address(CONSENSUS_MAIN_CONTRACT["address"]), abi=CONSENSUS_MAIN_ABI, ) From 1d4ff7518238e7611b0e886498db1b4cb91d6043 Mon Sep 17 00:00:00 2001 From: Edgars Date: Wed, 22 Apr 2026 15:20:11 +0100 Subject: [PATCH 3/3] =?UTF-8?q?ci(smoke):=20bump=20timeout=205m=20?= =?UTF-8?q?=E2=86=92=2015m=20for=20slow=20testnet=20reads?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/smoke.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/smoke.yml b/.github/workflows/smoke.yml index 458e671..39b3e25 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/smoke.yml @@ -10,7 +10,11 @@ jobs: smoke: name: Testnet Smoke Tests runs-on: ubuntu-latest - timeout-minutes: 5 + # Bradbury/Asimov RPC reads occasionally stall for 2-3 minutes on + # single calls (testnet capacity); with 17 testnet-flagged tests + # even one hiccup blew past the old 5-minute ceiling. 15m gives + # enough slack for two slow calls without indefinite hangs. + timeout-minutes: 15 continue-on-error: true strategy: matrix: