This page is intended for developers looking to build smart contracts or interfaces
that interact with the staking system.
- join the validator set (
addValidator) - delegate their stake to a validator (
delegate) - undelegate their stake from a validator (a multi-step process:
undelegate, waitWITHDRAWAL_DELAYepochs, thenwithdraw) - compound the rewards they earned as a delegator (i.e. delegate the rewards)
(
compound) - claim the rewards they earned as a delegator (
claimRewards)
Only standard
CALLs are allowed to the staking precompile. In particular,
STATICCALL, DELEGATECALL, and CALLCODE are not allowed.Because the staking system is a precompile and not a smart contract,
you cannot test against it in a forked environment.Precompile Address and Selectors
The contract address is . The external functions are identified by the following 4-byte selectors. External state-modifying methods:addValidator(bytes,bytes,bytes)-0xf145204cdelegate(uint64)-0x84994fecundelegate(uint64,uint256,uint8)-0x5cf41514withdraw(uint64,uint8)-0xaed2ee73compound(uint64)-0xb34fea67claimRewards(uint64)-0xa76e2ca5changeCommission(uint64,uint256)-0x9bdcc3c8externalReward(uint64)-0xe4b3303b
getValidator(uint64)-0x2b6d639agetDelegator(uint64,address)-0x573c1ce0getWithdrawalRequest(uint64,address,uint8)-0x56fa2045getConsensusValidatorSet(uint32)-0xfb29b729getSnapshotValidatorSet(uint32)-0xde66a368getExecutionValidatorSet(uint32)-0x7cb074dfgetDelegations(address,uint64)-0x4fd66050getDelegators(uint64,address)-0xa0843a26getEpoch()-0x757991a8getProposerValId()-0xfbacb0be
syscallOnEpochChange(uint64)-0x1d4e9f02syscallReward(address)-0x791bdcf3syscallSnapshot()-0x157eeb21
External State-Modifying Methods
addValidator
Function selector
Function signature
Parameters
payload- consists of the following fields, packed together in big endian (equivalent toabi.encodePacked()in Solidity):bytes secpPubkey(unique SECP public key used for consensus)bytes blsPubkey(unique BLS public key used for consensus)address authAddress(address used for the validator’s delegator account. This address has withdrawal authority for the validator’s staked amount)uint256 amount(amount the validator is self-staking. Must equalmsg.value)uint256 commission(commission charged to delegators multiplied by 1e18, e.g.10% = 1e17)
signedSecpMessage- SECP signature over payloadsignedBlsMessage- BLS signature over payload
Gas cost
505,125Behavior
This creates a validator with an associated delegator account and returns the resultantvalidatorId.
The method starts by unpacking the payload to retrieve the secpPubkey, blsPubkey,
authAddress, amount, and commission, then verifying that the signedSecpMessage
and signedBlsMessage correspond to the payload signed by the corresponding SECP and BLS
private keys.
- The validator must provide both a unique BLS key and a unique SECP key. Submissions with any repeated public keys will revert.
- Both signatures (
signedSecpMessageandsignedBlsMessage) must be valid and must sign over thepayload. - Multiple validators may share the same
authAddress. msg.valuemust be equal or greater thanMIN_AUTH_ADDRESS_STAKEor the call will revert.- If the
msg.valueis also equal or greater thanACTIVE_VALIDATOR_STAKEthen the validator will become active in the future:- If
addValidatorwas called before the boundary block, then in epochn+1; - Otherwise it will become active in epoch
n+2.
- If
Pseudocode
Pseudocode
Usage
Here is an example of assembling the payload and signing:delegate
Function selector
Function signature
Parameters
validatorId- id of the validator that delegator would like to delegate tomsg.value- the amount to delegate
Gas cost
260,850Behavior
This creates a delegator account if it does not exist and increases the delegator’s balance.- The delegator account is determined by
msg.sender. validatorIdmust correspond to a valid validator.msg.valuemust be >=DUST_THRESHOLD.- If this delegation causes the validator’s total stake to exceed
ACTIVE_VALIDATOR_STAKE, then the validator will be added toexecution_valsetif not already present. - The delegator stake becomes active
- in epoch
n+1if the request is before the boundary block - in epoch
n+2otherwise
- in epoch
Pseudocode
Pseudocode
undelegate
Function selector
Function signature
Parameters
validatorId- id of the validator to which sender previously delegated, from which we are removing delegationamount- amount to undelegate, in Monad weiwithdrawId- integer between 0 and 255, inclusive, which serves as the identifier for a delegator’s withdrawal. For each (validator, delegator) tuple, there can be a maximum of 256 in-flight withdrawal requests
Gas cost
147,750Behavior
This deductsamount from the delegator account and moves it to a withdrawal request object,
where it remains in a pending state for WITHDRAWAL_DELAY
epochs before the funds are claimable via the withdraw method.
- The delegator account is determined by
msg.sender. validatorIdmust correspond to a valid validator to which the sender previously delegated- The delegator must have stake >= amount.
- If the withdrawal causes
Val(validatorId).stake()to drop belowACTIVE_VALIDATOR_STAKE, then the validator is scheduled to be removed from the valset. - If the
authAddresson a validator undelegates enough of their own stake to drop belowMIN_AUTH_ADDRESS_STAKE, then the validator is scheduled to be removed from the valset. - The function will revert if there is a pending withdrawal with the same
withdrawId.withdrawIds can be reused after callingwithdraw. - A delegator can only remove a stake after it has been activated. This is the stake field in the delegator struct. Pending delegations cannot be removed until they are active.
- The delegator stake becomes inactive in the valset
- in epoch
n+1if the request is before the boundary block - in epoch
n+2otherwise
- in epoch
- The delegator stake becomes withdrawable, and thus no longer subject to slashing
- in epoch
n + 1 + WITHDRAWAL_DELAYif the request is before the boundary block - in epoch
n + 2 + WITHDRAWAL_DELAYotherwise
- in epoch

Timeline of withdrawability of stake relative to undelegate command
Pseudocode
Pseudocode
withdraw
Function selector
Function signature
Parameters
validatorId- id of the validator to which sender previously delegated, from which we previously issued anundelegatecommandwithdrawId- identifier for a delegator’s previously created withdrawal; the same id previously supplied toundelegate. For each (validator, delegator) tuple, there can be a maximum of 256 in-flight withdrawal requests.
Gas cost
68,675Behavior
This completes an undelegation action (which started with a call to theundelegate function),
sending the amount to msg.sender, provided that sufficient epochs have passed.
- The delegator is
msg.sender. The withdrawal is identified bymsg.sender,validatorId, andwithdrawId - The withdraw action can take place once the undelegation is complete, and the withdraw delay has passed:
- in epoch
n + 1 + WITHDRAWAL_DELAYif the undelegate request is before the boundary block - in epoch
n + 2 + WITHDRAWAL_DELAYotherwise
- in epoch
Pseudocode
Pseudocode
compound
Function selector
Function signature
Parameters
validatorId- id of the validator to which sender previously delegated, for which we are compounding rewards
Gas cost
289,325Behavior
This precompile converts the delegator’s accumulated rewards into additional stake.- The account compounded is determined by
msg.sender. If a delegator account does not exist, then the call reverts validatorIdmust correspond to a valid validator to which the sender previously delegated- The delegator rewards become active in the valset
- in epoch
n+1if the request is before the boundary block - in epoch
n+2otherwise.
- in epoch
Pseudocode
Pseudocode
claimRewards
Function selector
Function signature
Parameters
validatorId- id of the validator to which sender previously delegated, for which we are claiming rewards
Gas cost
155,375Behavior
This precompile allows a delegator to claim any rewards instead of compounding them.validatorIdmust correspond to a valid validator to which the sender previously delegated- If delegator account does not exist for this
(validatorId, msg.sender)tuple, then the call reverts - The delegator’s accumulated rewards are transferred to their delegation
Pseudocode
Pseudocode
changeCommission
Function selector
Function signature
Parameters
validatorId- id of the validator, who would like to change their commission ratecommission- commission rate taken from block rewards, expressed in 1e18 units (e.g., 10% = 1e17)
Gas cost
39,475Behavior
This allows theauthAddress for a validator to modify the commission for the validator.
The commission cannot be set larger than MAX_COMMISSION (currently 100%).
- The
msg.sendermust be theauthAddressfor the respective validator Id. - The commission cannot be set larger than
MAX_COMMISSION(currently 100%). - The change in commission occurs in the following epochs:
- in epoch
n+1if request is not in the epoch delay rounds. - in epoch
n+2if request is in the epoch delay rounds.
- in epoch
Pseudocode
Pseudocode
externalReward
Function selector
Function signature
Parameters
validatorId- id of the validatormsg.value- the MON to add to unclaimed rewards
Gas cost
66,575Behavior
This function allows anyone to send extra MON to the stakers of a particular validator. This typically will be called by the validator themselves to share extra tips to their delegators. Notes:- This can only be called for a validator currently in the consensus validator set; otherwise the transaction reverts.
msg.valuemust be between 1 MON and 1,000,000 MON; otherwise the transaction reverts.- Commission is not deducted from this and diverted to the validator’s
auth_address. If you wish for a portion to be deducted, it should be deducted before sending.
Pseudocode
Pseudocode
External View Methods
Because only
CALLs are allowed to the staking precompile, all view methods
are given the default nonpayable state mutability.getValidator
Function selector
Function signature
Parameters
validatorId- id of the validator
Gas cost
97,200Behavior
This is the primary method to obtain information about a validator state. It provides a complete view of the validator’s state across execution, consensus, and snapshot contexts. It returns:ValExecution(execution view)- Stake and commission (consensus view)
- Stake and commission (snapshot view)
getDelegator
Function selector
Function signature
Parameters
validatorId- id of the validatordelegator- address of the delegator about whose stake we are inquiring
Gas cost
184,900Behavior
This returns the delegator’sDelInfo for the specified validator,
providing a view of the delegator’s stake, accumulated rewards and pending changes in stake.
getWithdrawalRequest
Function selector
Function signature
Gas cost
24,300Behavior
This returns the pendingWithdrawalRequest for the (validatorId, delegator, withdrawId) tuple.
get*ValidatorSet
Function selectors
Function signatures
Parameters
startIndex- since the list being looked up is potentially very long, each of these functions is paginated, returning a fixed-length subset of the desired list. PassstartIndexto indicate where in the list to start.
Gas cost
814,000 gas (assumingPAGINATED_RESULTS_SIZE = 100).
Behavior
These functions return the consensus, snapshot, and execution validator ids, respectively.getExecutionValidatorSet() returns the entire active validator set of all validators that meet the staking criteria to be considered for inclusion. getSnapshotValidatorSet() returns the subset of validators that have been chosen to be leaders in the next epoch. getConsensusValidatorSet() returns the subset of validators that are leaders in the current epoch.
Each call retrieves up to PAGINATED_RESULTS_SIZE validator ids starting from startIndex and
returns a tuple (bool done, uint32 nextIndex, uint256[] valids).
The bool isDone indicates whether the end of the list was reached. The uint32 nextIndex
is the last slot in the array.
getDelegations
Function selector
Function signature
Parameters
delegator- the address whose delegations we want to look upstartValId
Gas cost
814,000Behavior
Each call retrieves up toPAGINATED_RESULTS_SIZE validator ids starting from startValId
and returns a tuple (bool isDone, uint64 nextValId, uint64[] valIds) with delegation
from the input delegator address.
The bool isdone indicates whether the end of the list was reached.
The uint64 nextValId is the id after the last element in valIds. Use it as the
startValId for the next call.
If delegator has delegated to over PAGINATED_RESULTS_SIZE validator ids,
multiple calls are required (while isDone is false).
To capture the full set, the make the first function call using startValId = 0.
getDelegators
Function selector
Function signature
Parameters
validatorId- the id of the validator for which we want to know the delegatorsstartDelegator
Gas cost
814,000Behavior
Each call retrieves up toPAGINATED_RESULTS_SIZE delegator addresses starting from
startDelegator and returns a tuple (bool isDone, address nextDelegator, address[] delegators)
with delegation to the input validatorId.
The bool isDone indicates the end of the list was reached.
The nextDelegator is the address immediately after the last element in delegators.
Use it as startDelegator for the next call.
To capture the full set, the function should be called with startDelegator = 0.
The number of delegators to a given validator can be very large, so it is recommended to
maintain an updated list via the
events framework, rather
than periodically calling this expensive lookup.
getEpoch
Function selector
Function signature
Gas cost
200Behavior
This function is a handy utility to determine the current epoch and timing within the epoch (before or after the boundary block). IfinEpochDelayPeriod is false, the boundary block has not been reached yet
and write operations at that time should be effective for epoch + 1.
If inEpochDelayPeriod is true, the network is past the boundary block and
and write operations at that time should be effective for epoch + 2
getProposerValId
Function selector
Function signature
Gas cost
100Behavior
This function returns the validator ID of the current block proposer. Specifically, this validator ID corresponds to the sec_p value of the block author.Syscalls
There are currently three syscalls. Users cannot invoke these directly. They are only triggered through special system transactions.syscallOnEpochChange
Function selector
Function signature
Parameters
epoch- the new consensus epoch being entered
Behavior
This syscall is triggered at the end of the epoch delay rounds. It performs the following actions:- If the validator received a request to change stake in the previous epoch and participated in the previous epoch’s consensus validator set then it saves the corresponding accumulator value
- If any validator was active in the previous epoch but becomes inactive in the current epoch, it also saves their current accumulator value
- Sets the current epoch in state
Pseudocode
Pseudocode
syscallReward
Function selector
Function signature
Parameters
blockAuthor— the address of the validator that produced the block.
Behavior
This syscall is invoked for every block. It rewards the block-producing validator (and their delegators) with the configured block reward:- If the validator has a nonzero commission, a portion of the reward is allocated to the
validator’s
authAddress. - The remaining reward is claimable to the validator’s delegators.
- Suppose that a validator’s personal stake comprises 20% of the total delegation to their validator.
- The commission is set at 10% of total rewards.
Pseudocode
Pseudocode
syscallSnapshot
Function selector
Function signature
Parameters
(none)Behavior
This syscall sorts the current execution-layer validator set. It selects the topN staked
validators as the upcoming consensus validator set. The updated set is stored in state. The
previous consensus set is cleared.
Pseudocode
Pseudocode
Events
The staking precompiles emit standard events that appear in transaction receipts. These events provide indexed information about validator and delegator actions.ValidatorRewarded
syscallReward.
ValidatorCreated
addValidator.
ValidatorStatusChanged
addValidator, delegate,
undelegate, or compound. if the validator’s flags change.
Delegate
addValidator,
delegate, or compound.
Undelegate
undelegate.
Withdraw
withdraw successfully.
ClaimRewards
claimRewards.
CommissionChanged
changeCommission.
EpochChanged
syscallOnEpochChange.
Precompile Internals
Constants
Validator structs
Delegator structs
State variables
Mappings
Solidity Staking Interface
To copy to clipboard, click the button in the top right of the code block.Staking ABI JSON
To copy to clipboard, click the button in the top right of the code block.FAQ
Is there a `removeValidator` function?
Is there a `removeValidator` function?
There is no direct
removeValidator function. Instead, if a validator’s auth_account removes
enough stake through undelegate, the validator is removed from the consensus set
in a future epoch.This occurs in either epoch n+1 or epoch n+2, depending on whether the undelegate
occurred within the epoch delay rounds.Even when not active, a validator’s information is always retained. Validator ids are permanent since
other delegators may still be delegating and need to reference that val_id to undelegate/withdraw.How does a validator change their `commission`?
How does a validator change their `commission`?
A validator can change their
commission by calling changeCommission.What methods give visibility into the list of validator ids?
What methods give visibility into the list of validator ids?
See the valset getters.
What methods give visibility into a validator's state?
What methods give visibility into a validator's state?
See
getValidator.What methods give visibility into a delegator's delegation to one particular validator?
What methods give visibility into a delegator's delegation to one particular validator?
See
getDelegator.For pending withdrawals by that delegator from that validator, see getWithdrawalRequest.Is it more like a contract or a precompile?
Is it more like a contract or a precompile?
Despite using Solidity selectors and ABI, it is a precompile. Accessing code at
returns empty code. Its account is always accessed warm, and calls with invalid arguments consume all gas.Exception: if an account attempts to delegate to the staking precompile using
EIP-7702, all calls to it will revert.

