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: 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/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, ) 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" },