Address
| Contract | Address |
|---|---|
| Staking Precompile |
Functions and selectors
| State-modifying function | Selector |
|---|---|
addValidator(bytes,bytes,bytes) | 0xf145204c |
delegate(uint64) | 0x84994fec |
undelegate(uint64,uint256,uint8) | 0x5cf41514 |
withdraw(uint64,uint8) | 0xaed2ee73 |
compound(uint64) | 0xb34fea67 |
claimRewards(uint64) | 0xa76e2ca5 |
changeCommission(uint64,uint256) | 0x9bdcc3c8 |
externalReward(uint64) | 0xe4b3303b |
| View function | Selector |
|---|---|
getValidator(uint64) | 0x2b6d639a |
getDelegator(uint64,address) | 0x573c1ce0 |
getWithdrawalRequest(uint64,address,uint8) | 0x56fa2045 |
getConsensusValidatorSet(uint32) | 0xfb29b729 |
getSnapshotValidatorSet(uint32) | 0xde66a368 |
getExecutionValidatorSet(uint32) | 0x7cb074df |
getDelegations(address,uint64) | 0x4fd66050 |
getDelegators(uint64,address) | 0xa0843a26 |
getEpoch() | 0x757991a8 |
getProposerValId() | 0xfbacb0be |
| Syscall function | Selector |
|---|---|
syscallOnEpochChange(uint64) | 0x1d4e9f02 |
syscallReward(address) | 0x791bdcf3 |
syscallSnapshot() | 0x157eeb21 |
Constants
| Constant | Meaning | Value |
|---|---|---|
BOUNDARY_BLOCK_PERIOD | Blocks from boundary block to boundary block | 50,000 blocks |
EPOCH_DELAY_ROUNDS | Rounds between the boundary block and the start of each epoch | 5,000 rounds |
WITHDRAWAL_DELAY | Number of epochs before unstaked tokens can be withdrawn | 1 epoch |
MIN_AUTH_ADDRESS_STAKE | Min MON self-delegated by a validator for active set eligibility | 100,000 MON |
ACTIVE_VALIDATOR_STAKE | Min total MON staked with a validator for active set eligibility | 10,000,000 MON |
ACTIVE_VALSET_SIZE | Number of validators in the active set | 200 |
REWARD | MON reward per block | 25 MON |
DUST_THRESHOLD | Minimum amount per individual delegation | 1 gwei (1e9 wei) |
MAX_COMMISSION | Maximum commission rate | 100% (1e18) |
ACCUMULATOR_DENOMINATOR | Accumulator unit multiplier for accuracy | 1e36 |
PAGINATED_RESULTS_SIZE | Max results returned per paginated call | 100 |
State-modifying functions
addValidator
Creates a validator with an associated delegator account and returns the resultantvalidatorId.
Function selector
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
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
delegate
Creates a delegator account if it does not exist and increases the delegator’s balance. Function selectorvalidatorId- id of the validator that delegator would like to delegate tomsg.value- the amount to delegate
- 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
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 function.
Function selector
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
- 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
Completes an undelegation action (which started with a call to theundelegate function), sending the amount to msg.sender, provided that sufficient epochs have passed.
Function selector
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.
- 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
Converts the delegator’s accumulated rewards into additional stake. Function selectorvalidatorId- id of the validator to which sender previously delegated, for which we are compounding rewards
- 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
Allows a delegator to claim any rewards instead of compounding them. Function selectorvalidatorId- id of the validator to which sender previously delegated, for which we are claiming rewards
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
Allows theauthAddress for a validator to modify the commission for the validator.
Function selector
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)
- 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
Allows anyone to send extra MON to the stakers of a particular validator, typically called by the validator themselves to share extra tips to their delegators. Function selectorvalidatorId- id of the validatormsg.value- the MON to add to unclaimed rewards
- 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
View functions
Because only
CALLs are allowed to the staking precompile, all view functions are given the default nonpayable state mutability.getValidator
Returns a complete view of the validator’s state across execution, consensus, and snapshot contexts.ValExecution(execution view)- Stake and commission (consensus view)
- Stake and commission (snapshot view)
validatorId- id of the validator
getDelegator
Returns the delegator’sDelInfo for the specified validator, providing a view of the delegator’s stake, accumulated rewards, and pending changes in stake.
Function selector
validatorId- id of the validatordelegator- address of the delegator about whose stake we are inquiring
getWithdrawalRequest
Returns the pendingWithdrawalRequest for the (validatorId, delegator, withdrawId) tuple.
Function selector
get*ValidatorSet
Returns the consensus, snapshot, and execution validator IDs, respectively. Function selectorsstartIndex- 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.
PAGINATED_RESULTS_SIZE = 100).
Behavior
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
Returns a paginated list of validator IDs that a given delegator address has delegated to. Function selectordelegator- the address whose delegations we want to look upstartValId
PAGINATED_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, make the first function call using startValId = 0.
getDelegators
Returns a paginated list of delegator addresses for a given validator. Function selectorvalidatorId- the id of the validator for which we want to know the delegatorsstartDelegator
PAGINATED_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
Returns the current epoch and timing within the epoch (before or after the boundary block). Function selectorinEpochDelayPeriod 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
Returns the validator ID of the current block proposer, corresponding to the SECP value of the block author. Function selectorSyscalls
There are currently three syscalls. Users cannot invoke these directly. They are only triggered through special system transactions.syscallOnEpochChange
Triggered at the end of the epoch delay rounds to finalize accumulator values and update epoch state. Function selectorepoch- the new consensus epoch being entered
- 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
Rewards the block-producing validator and their delegators with the configured block reward for every block. Function selectorblockAuthor— the address of the validator that produced the block.
- 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
Sorts the current execution-layer validator set, selects the topN staked validators as the upcoming consensus validator set, stores the updated set in state, and clears the previous consensus set.
Function selector
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
Emitted when block reward is allocated viasyscallReward.
ValidatorCreated
Emitted when a validator is added viaaddValidator.
ValidatorStatusChanged
Emitted duringaddValidator, delegate, undelegate, or compound. if the validator’s flags change.
Delegate
Emitted when delegation amount is increased, i.e. duringaddValidator, delegate, or compound.
Undelegate
Emitted when a delegator callsundelegate.
Withdraw
Emitted when a delegator executeswithdraw successfully.
ClaimRewards
Emitted when a delegator claims rewards viaclaimRewards.
CommissionChanged
Emitted when a validator changes commission viachangeCommission.
EpochChanged
Emitted when epoch changes viasyscallOnEpochChange.
Precompile internals
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.Which functions give visibility into the list of validator ids?
Which functions give visibility into the list of validator ids?
See the valset getters.
Which functions give visibility into a validator's state?
Which functions give visibility into a validator's state?
See
getValidator.Which functions give visibility into a delegator's delegation to one particular validator?
Which functions 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.

