> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monad.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Staking API Reference

> Reference for Monad's staking precompile

export const CopyToClipboard = ({value, children}) => {
  const [copied, setCopied] = useState(false);
  const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(value);
      setCopied(true);
      setTimeout(() => setCopied(false), 1000);
    } catch {
      const textarea = document.createElement("textarea");
      textarea.value = value;
      textarea.style.position = "fixed";
      textarea.style.opacity = "0";
      document.body.appendChild(textarea);
      textarea.select();
      document.execCommand("copy");
      document.body.removeChild(textarea);
      setCopied(true);
      setTimeout(() => setCopied(false), 1000);
    }
  };
  return <span style={{
    display: "inline",
    whiteSpace: "nowrap"
  }}>
      {children}
      <button onClick={handleCopy} title={copied ? "Copied!" : "Copy to clipboard"} style={{
    background: "none",
    border: "none",
    cursor: "pointer",
    padding: "2px",
    display: "inline-flex",
    alignItems: "center",
    verticalAlign: "middle",
    marginLeft: "4px",
    opacity: copied ? 1 : 0.4,
    transition: "opacity 0.15s"
  }} onMouseEnter={e => {
    if (!copied) e.currentTarget.style.opacity = "0.8";
  }} onMouseLeave={e => {
    if (!copied) e.currentTarget.style.opacity = "0.4";
  }}>
        {copied ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
            <polyline points="20 6 9 17 4 12" />
          </svg> : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
            <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
          </svg>}
      </button>
    </span>;
};

This page is a technical reference for the staking precompile interface. For a developer-oriented guide to common workflows, visit the [overview](/reference/staking/overview).

## Address

| Contract           | Address                                                                                                                            |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| Staking Precompile | <CopyToClipboard value="0x0000000000000000000000000000000000001000">`0x0000000000000000000000000000000000001000`</CopyToClipboard> |

## Functions and selectors

| State-modifying function                                | Selector     |
| ------------------------------------------------------- | ------------ |
| [`addValidator(bytes,bytes,bytes)`](#addvalidator)      | `0xf145204c` |
| [`delegate(uint64)`](#delegate)                         | `0x84994fec` |
| [`undelegate(uint64,uint256,uint8)`](#undelegate)       | `0x5cf41514` |
| [`withdraw(uint64,uint8)`](#withdraw)                   | `0xaed2ee73` |
| [`compound(uint64)`](#compound)                         | `0xb34fea67` |
| [`claimRewards(uint64)`](#claimrewards)                 | `0xa76e2ca5` |
| [`changeCommission(uint64,uint256)`](#changecommission) | `0x9bdcc3c8` |
| [`externalReward(uint64)`](#externalreward)             | `0xe4b3303b` |

| View function                                                         | Selector     |
| --------------------------------------------------------------------- | ------------ |
| [`getValidator(uint64)`](#getvalidator)                               | `0x2b6d639a` |
| [`getDelegator(uint64,address)`](#getdelegator)                       | `0x573c1ce0` |
| [`getWithdrawalRequest(uint64,address,uint8)`](#getwithdrawalrequest) | `0x56fa2045` |
| [`getConsensusValidatorSet(uint32)`](#getvalidatorset)                | `0xfb29b729` |
| [`getSnapshotValidatorSet(uint32)`](#getvalidatorset)                 | `0xde66a368` |
| [`getExecutionValidatorSet(uint32)`](#getvalidatorset)                | `0x7cb074df` |
| [`getDelegations(address,uint64)`](#getdelegations)                   | `0x4fd66050` |
| [`getDelegators(uint64,address)`](#getdelegators)                     | `0xa0843a26` |
| [`getEpoch()`](#getepoch)                                             | `0x757991a8` |
| [`getProposerValId()`](#getproposervalid)                             | `0xfbacb0be` |

| Syscall function                                        | Selector     |
| ------------------------------------------------------- | ------------ |
| [`syscallOnEpochChange(uint64)`](#syscallonepochchange) | `0x1d4e9f02` |
| [`syscallReward(address)`](#syscallreward)              | `0x791bdcf3` |
| [`syscallSnapshot()`](#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 resultant `validatorId`.

**Function selector**

```
addValidator(bytes,bytes,bytes) : 0xf145204c
```

**Function signature**

```solidity theme={null}
function addValidator(
    bytes calldata payload,
    bytes calldata signedSecpMessage,
    bytes calldata signedBlsMessage
) external payable returns (uint64 validatorId);
```

**Parameters**

1. `payload` - consists of the following fields, packed together in big endian
   (equivalent to `abi.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 equal `msg.value`)
   * `uint256 commission` (commission charged to delegators multiplied by 1e18, e.g. `10% = 1e17`)
2. `signedSecpMessage` - SECP signature over payload
3. `signedBlsMessage` - BLS signature over payload

**Gas cost**

505,125

**Behavior**

The function 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 (`signedSecpMessage` and `signedBlsMessage`) must be valid and must sign
  over the `payload`.
* Multiple validators may share the same `authAddress`.
* `msg.value` must be equal or greater than `MIN_AUTH_ADDRESS_STAKE` or the call will revert.
* If the `msg.value` is also equal or greater than `ACTIVE_VALIDATOR_STAKE` then the validator
  will become active in the future:
  * If `addValidator` was called before the boundary block, then in epoch `n+1`;
  * Otherwise it will become active in epoch `n+2`.

<Accordion title="Pseudocode">
  ```solidity theme={null}
  secp_pubkey, bls_pubkey, auth_address, amount, commission = payload

  assert amount == msg.value

  // increment validator id
  last_val_id = last_val_id + 1;

  // set uniqueness of keys
  secp_to_val_id[secp_eth_address] = last_val_id;
  bls_to_val_id[bls_eth_address] = last_val_id;

  // set validator info
  val_execution[last_val_id] = ValExecution{
      uint256 stake = msg.value;
      uint256 commission = commission;
      bytes secp_pubkey = secp_pubkey;
      bytes bls_pubkey = bls_pubkey;
      uint256 address_flags = set_flags();
  }

  // set authority delegator info
  delegator[last_val_id][input.auth_address] = DelInfo{
      uint256 delta_stake = set_stake()[0];
      uint256 next_delta_stake = set_stake()[1];
      uint64 delta_epoch = set_stake()[2];
      uint64 next_delta_epoch = set_stake()[3];
  }

  // set delegator accumulator
  epoch_acc[last_val_id][getEpoch()] = Accumulator{
      uint256 ref_count += 1;
  }

  // set flags
  set_flags();

  // push validator id
  if (val_execution[last_val_id].stake() >= ACTIVE_VALIDATOR_STAKE
          and last_val_id not in execution_valset):
      execution_valset.push(last_val_id);

  return last_val_id;

  def set_flags():
      if msg.value + val_execution[last_val_id].stake() >= ACTIVE_VALIDATOR_STAKE:
          return ValidatorFlagsOk;
      if msg.value + val_execution[last_val_id].stake() >= MIN_AUTH_ADDRESS_STAKE
          return ValidatorFlagsStakeTooLow;

  def set_stake():
      if in_epoch_delay_rounds:
          delta_stake = 0;
          next_delta_stake = msg.value;
          delta_epoch = 0;
          next_delta_epoch = current_epoch + 2;
      else:
          delta_stake = msg.value;
          next_delta_stake = 0;
          delta_epoch = current_epoch + 1;
          next_delta_epoch = 0;
      return [delta_stake, next_delta_stake, delta_epoch, next_delta_epoch];
  ```
</Accordion>

**Usage**

Here is an example of assembling the payload and signing:

```
def generate_add_validator_call_data_and_sign(
    secp_pubkey: bytes,
    bls_pubkey: bytes,
    auth_address: bytes,
    amount: int,
    commission: int
    secp_privkey: bytes
    bls_privkey: bytes
) -> bytes:
    # 1) Encode
    payload_parts = [
        secp_pubkey,
        bls_pubkey,
        auth_address,
        toBigEndian32(amount),
        toBigEndian32(commission),
    ]
    payload = b"".join(payload_parts)

    # 2) Sign with both keys
    secp_sig = SECP256K1_SIGN(blake3(payload), secp_privkey)
    bls_sig  = BLS_SIGN(hash_to_curve(payload), bls_privkey)

    # 3) Solidity encode the payload and two signatures
    return eth_abi.encode(['bytes', 'bytes', 'bytes'], [payload, secp_sig, bls_sig])
```

### delegate

Creates a delegator account if it does not exist and increases the delegator's balance.

**Function selector**

```
delegate(uint64) : 0x84994fec
```

**Function signature**

```solidity theme={null}
function delegate(
    uint64 validatorId
) external payable returns (bool success);
```

**Parameters**

1. `validatorId` - id of the validator that delegator would like to delegate to
2. `msg.value` - the amount to delegate

**Gas cost**

260,850

**Behavior**

* The delegator account is determined by `msg.sender`.
* `validatorId` must correspond to a valid validator.
* `msg.value` must be >= `DUST_THRESHOLD`.
* If this delegation causes the validator's total stake to exceed `ACTIVE_VALIDATOR_STAKE`,
  then the validator will be added to `execution_valset` if not already present.
* The delegator stake becomes active
  * in epoch `n+1` if the request is before the boundary block
  * in epoch `n+2` otherwise

<Accordion title="Pseudocode">
  ```solidity theme={null}
  validator_id = msg.input.val_id;

  // set validator information
  val_execution[validator_id] =  ValExecution{
      uint256 stake += msg.value();
  }

  // set delegator information
  DelInfo current_delegator = delegator[validator_id][msg.sender];

  // apply get_current_stake() first. This updates the delegator stake
  // to be inline with the current stake activated in consensus.
  get_current_stake();

  // apply add_stake() second.
  uint256[4] add_stake_info = add_stake(msg.value());

  current_delegator = DelInfo{
      uint256 delta_stake = add_stake_info[0];
      uint256 next_delta_stake = add_stake_info[1];
      uint64 delta_epoch = add_stake_info[2];
      uint64 next_delta_epoch = add_stake_info[3];
  }

  // set epoch accumulator
  epoch_acc[validator_id][getEpoch()].ref_count += 1;

  // set flags
  set_flags();

  // push validator id
  if val_execution[validator_id].stake() >= ACTIVE_VALIDATOR_STAKE
          and validator_id not in execution_valset:
      execution_valset.push(validator_id);

  def add_stake(uint256 amount):
      uint256 _delta_stake;
      uint256 _next_delta_stake;
      uint64 _delta_epoch;
      uint64 _next_delta_epoch;

      if not in_epoch_delay_rounds:
          _delta_stake = current_delegator.delta_stake() + amount;
          _next_delta_stake = 0;
          _delta_epoch = current_epoch + 1;
          _next_delta_epoch = 0;
      else:
          _delta_stake = 0;
          _next_delta_stake = current_delegator.next_delta_stake() + amount;
          _delta_epoch = 0;
          _next_delta_epoch = current_epoch + 2;
      return [_delta_stake, _next_delta_stake, _delta_epoch, _next_delta_epoch];


  def maybe_process_next_epoch_state():
      """
      Helper function to process and update rewards
      based on the current epoch state.
      """

      if (
          epoch_acc[validator_id][current_delegator.delta_epoch()] != 0
          and current_epoch > current_delegator.delta_epoch()
          and current_delegator.delta_epoch() > 0
      ):
          // Compute rewards from the last checked epoch.
          _rewards += current_delegator.stake() * (
              epoch_acc[validator_id][current_delegator.delta_epoch()].val()
              - current_delegator.acc()
          )

          // Promote stake to active in delegator view.
          current_delegator.stake() += current_delegator.delta_stake()
          current_delegator.acc() = (
              epoch_acc[validator_id][current_delegator.delta_epoch()].val()
          )
          current_delegator.delta_epoch() = current_delegator.next_delta_epoch()
          current_delegator.delta_stake() = current_delegator.next_delta_stake()
          current_delegator.next_delta_epoch() = 0
          current_delegator.next_delta_stake() = 0

          epoch_acc[validator_id][current_delegator.delta_epoch].ref_count -= 1


  def get_current_stake():
      uint256 _rewards = 0;

      // Process next epoch rewards and increment stake
      maybe_process_next_epoch_state()
      // Perform again to capture max two additional epochs
      maybe_process_next_epoch_state()

      current_delegator.rewards() += _rewards;
      return _rewards;
  ```
</Accordion>

### undelegate

Deducts `amount` 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`](#withdraw) function.

**Function selector**

```
undelegate(uint64,uint256,uint8) : 0x5cf41514
```

**Function signature**

```solidity theme={null}
function undelegate(
    uint64 validatorId,
    uint256 amount,
    uint8 withdrawId
) external returns (bool success);
```

**Parameters**

1. `validatorId` - id of the validator to which sender previously delegated, from which we are
   removing delegation
2. `amount` - amount to undelegate, in Monad wei
3. `withdrawId` - 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,750

**Behavior**

* The delegator account is determined by `msg.sender`.
* `validatorId` must 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 below `ACTIVE_VALIDATOR_STAKE`, then the
  validator is scheduled to be removed from the valset.
* If the `authAddress` on a validator undelegates enough of their own stake to drop below
  `MIN_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`.
  `withdrawId`s can be reused after calling [`withdraw`](#withdraw).
* 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+1` if the request is before the boundary block
  * in epoch `n+2` otherwise
* The delegator stake becomes withdrawable, and thus no longer subject to slashing
  * in epoch `n + 1 + WITHDRAWAL_DELAY` if the request is before the boundary block
  * in epoch `n + 2 + WITHDRAWAL_DELAY` otherwise

<img src="https://mintcdn.com/monadfoundation-40611fb6/c3ZcPFY7YVeS_v57/static/img/developer-essentials/staking/undelegate-timeline.png?fit=max&auto=format&n=c3ZcPFY7YVeS_v57&q=85&s=8511c75f512336d15c497ca89b1ea653" alt="timeline of undelegation and withdrawal" width="400" style={{marginLeft: "auto", marginRight: "auto"}} data-path="static/img/developer-essentials/staking/undelegate-timeline.png" />

<p style={{textAlign: "center", fontSize: "0.875rem", opacity: 0.65,marginTop: "0.5rem", fontStyle: "italic"}}>Timeline of withdrawability of stake relative to <code>undelegate</code> command</p>

<Accordion title="Pseudocode">
  ```solidity theme={null}
  uint64 validator_id = msg.input.val_id;
  uint256 amount = msg.input.amount;
  uint8 withdraw_id = msg.input.withdraw_id;

  ValExecution current_validator = val_execution[validator_id];

  // set validator information
  current_validator =  ValExecution{
      uint256 stake -= amount;
  }

  // apply get_current_stake() first.
  get_current_stake();

  DelInfo current_delegator = delegator[validator_id][msg.sender];
  // set delegator information
  current_delegator = DelInfo{
      uint256 stake -= amount;
  }

  // set withdraw request
  withdrawal[validator_id][msg.sender][withdraw_id] = WithdrawalRequest{
      uint256 amount = amount;
      uint256 acc = current_validator.acc();
      uint64 epoch = getEpoch();
  };

  // set epoch accumulator
  epoch_acc[validator_id][getEpoch()].ref_count += 1;

  // schedule validator to leave set
  if current_validator.stake
   < ACTIVE_VALIDATOR_STAKE and validator_id in execution_valset:
      current_validator.set_flag(INSUFFICIENT_STAKE);

  if (current_delegator.stake <= MIN_AUTH_ADDRESS_STAKE and validator_id in execution_valset) and msg.sender == current_validator.auth_address:
      current_validator.set_flag(INSUFFICIENT_VALIDATOR_STAKE);
  ```
</Accordion>

### withdraw

Completes an undelegation action (which started with a call to the `undelegate` function), sending the amount to `msg.sender`, provided that sufficient epochs have passed.

**Function selector**

```
withdraw(uint64,uint8) : 0xaed2ee73
```

**Function signature**

```solidity theme={null}
function withdraw(
    uint64 validatorId,
    uint8 withdrawId
) external returns (bool success);
```

**Parameters**

1. `validatorId` - id of the validator to which sender previously delegated, from which we previously
   issued an `undelegate` command
2. `withdrawId` - identifier for a delegator's previously created withdrawal; the same id
   previously supplied to [`undelegate`](#undelegate). For each (validator, delegator) tuple,
   there can be a maximum of 256 in-flight withdrawal requests.

**Gas cost**

68,675

**Behavior**

* The delegator is `msg.sender`. The withdrawal is identified by `msg.sender`, `validatorId`,
  and `withdrawId`
* The withdraw action can take place once the undelegation is complete, and the withdraw delay has passed:
  * in epoch `n + 1 + WITHDRAWAL_DELAY` if the undelegate request is before the boundary block
  * in epoch `n + 2 + WITHDRAWAL_DELAY` otherwise

<Accordion title="Pseudocode">
  ```solidity theme={null}
  uint64 validator_id = msg.input.val_id;
  uint8 withdraw_id = msg.input.withdraw_id;

  WithdrawalRequest current_withdraw = withdrawal[validator_id][msg.sender][withdraw_id];

  // Compute any additional rewards and transfer funds to delegator
  transfer(msg.sender, current_withdraw.amount + get_withdraw_rewards());

  // unset withdraw request
  withdrawal[validator_id][msg.sender][withdraw_id] = WithdrawalRequest{
      uint256 amount = 0,
      uint256 acc = 0,
      uint64 epoch = 0
  };

  def get_withdraw_rewards():
      epoch_acc[validator_id][current_withdraw.epoch].ref_count -= 1;
      return current_withdraw.amount() * (epoch_acc[validator_id][current_withdraw.epoch()].val() - current_withdraw.acc());
  ```
</Accordion>

### compound

Converts the delegator's accumulated rewards into additional stake.

**Function selector**

```
compound(uint64) : 0xb34fea67
```

**Function signature**

```solidity theme={null}
function compound(
    uint64 validatorId
) external returns (bool success);
```

**Parameters**

1. `validatorId` - id of the validator to which sender previously delegated, for which we are
   compounding rewards

**Gas cost**

289,325

**Behavior**

* The account compounded is determined by `msg.sender`. If a delegator account does not exist, then the call reverts
* `validatorId` must correspond to a valid validator to which the sender previously delegated
* The delegator rewards become active in the valset
  * in epoch `n+1` if the request is before the boundary block
  * in epoch `n+2` otherwise.

<Accordion title="Pseudocode">
  ```solidity theme={null}
  validator_id = msg.input.val_id;

  // set delegator information
  DelInfo current_delegator = delegator[validator_id][msg.sender];

  // apply get_current_stake() first. This updates the delegator stake
  // to be inline with the current stake activated in consensus.
  rewards_compounded = get_current_stake();

  // apply add_stake() second.
  uint256[4] add_stake_info = add_stake(rewards_compounded);

  // set delegator information
  current_delegator = DelInfo{
      uint256 delta_stake = add_stake_info[0];
      uint256 next_delta_stake = add_stake_info[1];
      uint64 delta_epoch = add_stake_info[2];
      uint64 next_delta_epoch = add_stake_info[3];
      uint256 rewards = 0;
  }

  // set validator information
  val_execution[validator_id] = ValExecution{
      uint256 stake += rewards_compounded;
  }

  // set accumulator
  epoch_acc[validator_id][getEpoch()] = Accumulator{
      uint256 ref_count += 1;
  }

  // set flags
  set_flags();

  // push validator id
  if val_execution[validator_id].stake() >= ACTIVE_VALIDATOR_STAKE and validator_id not in execution_valset:
      execution_valset.push(validator_id);

  ```
</Accordion>

### claimRewards

Allows a delegator to claim any rewards instead of [compounding](#compound) them.

**Function selector**

```
claimRewards(uint64) : 0xa76e2ca5
```

**Function signature**

```solidity theme={null}
function claimRewards(
    uint64 validatorId
) external returns (bool success);
```

**Parameters**

1. `validatorId` - id of the validator to which sender previously delegated, for which we are
   claiming rewards

**Gas cost**

155,375

**Behavior**

* `validatorId` must 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

<Accordion title="Pseudocode">
  ```solidity theme={null}
  // set delegator information
  DelInfo current_delegator = delegator[validator_id][msg.sender];

  // apply get_current_stake() first.
  uint256 current_rewards = get_current_stake();

  // set delegator information
  current_delegator = DelInfo{
      uint256 rewards = 0;
  )

  // send rewards to delegator
  transfer(msg.sender, current_rewards);
  ```
</Accordion>

### changeCommission

Allows the `authAddress` for a validator to modify the commission for the validator.

**Function selector**

```
changeCommission(uint64,uint256) : 0x9bdcc3c8
```

**Function signature**

```solidity theme={null}
function changeCommission(
    uint64 validatorId,
    uint256 commission
) external returns (bool success);
```

**Parameters**

1. `validatorId` - id of the validator, who would like to change their commission rate
2. `commission` - commission rate taken from block rewards, expressed in 1e18 units
   (e.g., 10% = 1e17)

**Gas cost**

39,475

**Behavior**

* The `msg.sender` must be the `authAddress` for 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+1` if request is not in the epoch delay rounds.
  * in epoch `n+2` if request is in the epoch delay rounds.

<Accordion title="Pseudocode">
  ```solidity theme={null}
  validator_id = msg.input.val_id;


  val_execution[validator_id] = ValExecution{
      uint256 commission = msg.input.commission;
  }


  ```
</Accordion>

### 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 selector**

```
externalReward(uint64) : 0xe4b3303b
```

**Function signature**

```solidity theme={null}
function externalReward(
    uint64 validatorId
) external payable returns (bool success);
```

**Parameters**

1. `validatorId` - id of the validator
2. `msg.value` - the MON to add to unclaimed rewards

**Gas cost**

66,575

**Behavior**

* This can only be called for a validator currently in the consensus validator set; otherwise
  the transaction reverts.
* `msg.value` must 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.

<Accordion title="Pseudocode">
  ```solidity theme={null}
  validator_id = msg.input.val_id;

  require(msg.value >= 1e18 && msg.value <= 1e24, "Reward out of bounds");
  require(val_consensus[validator_id] > 0 , "Validator not active");

  val_execution[validator_id].unclaimed_reward += msg.value;
  val_execution[val_id].acc += msg.value / val_consensus[val_id].stake();

  ```
</Accordion>

## View functions

<Note>
  Because only `CALL`s are allowed to the staking precompile, all view functions are given the default `nonpayable` state mutability.
</Note>

### getValidator

Returns a complete view of the validator's state across execution, consensus, and snapshot contexts.

* [`ValExecution`](#validator-structs) (execution view)
* Stake and commission (consensus view)
* Stake and commission (snapshot view)

**Function selector**

```
getValidator(uint64) : 0x2b6d639a
```

**Function signature**

```solidity theme={null}
function getValidator(
    uint64 validatorId
) external returns (
    address authAddress,
    uint64 flags,
    uint256 stake,
    uint256 accRewardPerToken,
    uint256 commission,
    uint256 unclaimedRewards,
    uint256 consensusStake,
    uint256 consensusCommission,
    uint256 snapshotStake,
    uint256 snapshotCommission,
    bytes memory secpPubkey,
    bytes memory blsPubkey
);
```

**Parameters**

1. `validatorId` - id of the validator

**Gas cost**

97,200

### getDelegator

Returns the delegator's [`DelInfo`](#delegator-structs) for the specified validator, providing a view of the delegator's stake, accumulated rewards, and pending changes in stake.

**Function selector**

```
getDelegator(uint64,address) : 0x573c1ce0
```

**Function signature**

```solidity theme={null}
function getDelegator(
    uint64 validatorId,
    address delegator
) external returns (
    uint256 stake,
    uint256 accRewardPerToken,
    uint256 unclaimedRewards,
    uint256 deltaStake,
    uint256 nextDeltaStake,
    uint64 deltaEpoch,
    uint64 nextDeltaEpoch
);
```

**Parameters**

1. `validatorId` - id of the validator
2. `delegator` - address of the delegator about whose stake we are inquiring

**Gas cost**

184,900

### getWithdrawalRequest

Returns the pending [`WithdrawalRequest`](#delegator-structs) for the `(validatorId, delegator, withdrawId)` tuple.

**Function selector**

```
getWithdrawalRequest(uint64,address,uint8) : 0x56fa2045
```

**Function signature**

```solidity theme={null}
function getWithdrawalRequest(
    uint64 validatorId,
    address delegator,
    uint8 withdrawId
) external returns (
    uint256 withdrawalAmount,
    uint256 accRewardPerToken,
    uint64 withdrawEpoch
);
```

**Gas cost**

24,300

### get\*ValidatorSet

Returns the consensus, snapshot, and execution validator IDs, respectively.

**Function selectors**

```
getConsensusValidatorSet(uint32) : 0xfb29b729
getSnapshotValidatorSet(uint32) : 0xde66a368
getExecutionValidatorSet(uint32) : 0x7cb074df
```

**Function signatures**

```solidity theme={null}
function getConsensusValidatorSet(
    uint32 startIndex
) external returns (bool isDone, uint32 nextIndex, uint64[] memory valIds);

function getSnapshotValidatorSet(
    uint32 startIndex
) external returns (bool isDone, uint32 nextIndex, uint64[] memory valIds);

function getExecutionValidatorSet(
    uint32 startIndex
) external returns (bool isDone, uint32 nextIndex, uint64[] memory valIds);
```

**Parameters**

1. `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. Pass `startIndex` to
   indicate where in the list to start.

**Gas cost**

814,000 gas (assuming `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 selector**

```
getDelegations(address,uint64) : 0x4fd66050
```

**Function signature**

```solidity theme={null}
function getDelegations(
    address delegator,
    uint64 startValId
) external returns (bool isDone, uint64 nextValId, uint64[] memory valIds);
```

**Parameters**

1. `delegator` - the address whose delegations we want to look up
2. `startValId`

**Gas cost**

814,000

**Behavior**

Each call retrieves up to `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 selector**

```
getDelegators(uint64,address) : 0xa0843a26
```

**Function signature**

```solidity theme={null}
function getDelegators(
    uint64 validatorId,
    address startDelegator
) external returns (bool isDone, address nextDelegator, address[] memory delegators);
```

**Parameters**

1. `validatorId` - the id of the validator for which we want to know the delegators
2. `startDelegator`

**Gas cost**

814,000

**Behavior**

Each call retrieves up to `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`.

<Note>
  The number of delegators to a given validator can be very large, so it is recommended to
  maintain an updated list via the
  [events](/monad-arch/realtime-data/data-sources#real-time-data-sources) framework, rather
  than periodically calling this expensive lookup.
</Note>

### getEpoch

Returns the current epoch and timing within the epoch (before or after the boundary block).

**Function selector**

```
getEpoch() : 0x757991a8
```

**Function signature**

```solidity theme={null}
function getEpoch() external returns (uint64 epoch, bool inEpochDelayPeriod);
```

**Gas cost**

200

**Behavior**

If `inEpochDelayPeriod` 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 selector**

```
getProposerValId() : 0xfbacb0be
```

**Function signature**

```solidity theme={null}
function getProposerValId() external returns (uint64 val_id);
```

**Gas cost**

100

## Syscalls

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 selector**

```
syscallOnEpochChange(uint64) : 0x1d4e9f02
```

**Function signature**

```solidity theme={null}
function syscallOnEpochChange(uint64 epoch) external;
```

**Parameters**

1. `epoch` - the new consensus epoch being entered

**Behavior**

1. 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
2. If any validator was active in the previous epoch but becomes inactive in the current epoch,
   it also saves their current accumulator value
3. Sets the current epoch in state

<Accordion title="Pseudocode">
  ```solidity theme={null}
  uint64 current_epoch = msg.input.epoch;

  for i in snapshot_valset:
      if epoch_acc[i][current_epoch] is not empty:
          epoch_acc[i][current_epoch].val() = execution_valset[i].acc()
      if epoch_acc[i][current_epoch + 1] is not empty:
          epoch_acc[i][current_epoch].val() = execution_valset[i].acc()

  in_epoch_delay_rounds = false;
  epoch = current_epoch;
  ```
</Accordion>

### syscallReward

Rewards the block-producing validator and their delegators with the configured block reward for every block.

**Function selector**

```
syscallReward(address) : 0x791bdcf3
```

**Function signature**

```solidity theme={null}
function syscallReward(address blockAuthor) external;
```

**Parameters**

1. `blockAuthor` — the address of the validator that produced the block.

**Behavior**

1. If the validator has a nonzero commission, a portion of the reward is allocated to the
   validator's `authAddress`.
2. The remaining reward is claimable to the validator's delegators.

Note that the commission is calculated as a percentage of the total block reward.

<Info title="Example">
  * 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.

  Then the validator receives 10% of the total block reward as their commission. The remaining 90%
  of the reward is distributed to the stake pool. Since the validator owns 20% of the pool, they
  also receive 20% of that remaining amount.
</Info>

<Accordion title="Pseudocode">
  ```solidity theme={null}
  uint64 val_id = secp_to_val_id[block_author];
  DelInfo auth_del = delegator[val_id][val_execution[val_id].auth_address()];
  uint256 _commission = REWARD * val_execution[val_id].commission / 1e18;
  uint256 _unclaimed_rewards = REWARD - _commission;

  // state update
  auth_del.rewards() += _commission;
  val_execution[val_id].unclaimed_rewards += _unclaimed_rewards;
  val_execution[val_id].acc += _unclaimed_rewards / val_consensus[val_id].stake();

  mint(STAKING_CONTRACT_ADDRESS, REWARD);
  ```
</Accordion>

### syscallSnapshot

Sorts the current execution-layer validator set, selects the top `N` staked validators as the upcoming consensus validator set, stores the updated set in state, and clears the previous consensus set.

**Function selector**

```
syscallSnapshot() : 0x157eeb21
```

**Function signature**

```solidity theme={null}
function syscallSnapshot() external;
```

**Behavior**

<Accordion title="Pseudocode">
  ```solidity theme={null}

  uint64[] filter_top_n_validators = sort(execution_valset);

  for i in snapshot_valset:
      val_snapshot[i].stake = 0;
      val_snapshot[i].commission = 0;

  snapshot_valset = consensus_valset;
  consensus_valset = filter_top_n_validators;

  for i in filter_top_n_validators:
      val_consensus[i].stake = val_execution[i].stake;
      val_consensus[i].commission = val_execution[i].commission;
  ```
</Accordion>

## 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 via [`syscallReward`](#syscallreward).

```solidity theme={null}
event ValidatorRewarded(
        uint64 indexed validatorId,
        address indexed from,
        uint256 amount,
        uint64 epoch
);
```

### ValidatorCreated

Emitted when a validator is added via [`addValidator`](#addvalidator).

```solidity theme={null}
event ValidatorCreated(
    uint64  indexed validatorId,
    address indexed authAddress,
    uint256 commission
);
```

### ValidatorStatusChanged

Emitted during [`addValidator`](#addvalidator), [`delegate`](#delegate), [`undelegate`](#undelegate), or [`compound`](#compound). if the validator's flags change.

```solidity theme={null}
event ValidatorStatusChanged(
    uint64  indexed validatorId,
    uint64  flags
);
```

### Delegate

Emitted when delegation amount is increased, i.e. during [`addValidator`](#addvalidator), [`delegate`](#delegate), or [`compound`](#compound).

```solidity theme={null}
event Delegate(
    uint64  indexed validatorId,
    address indexed delegator,
    uint256 amount,
    uint64  activationEpoch
);
```

### Undelegate

Emitted when a delegator calls [`undelegate`](#undelegate).

```solidity theme={null}
event Undelegate(
    uint64  indexed validatorId,
    address indexed delegator,
    uint8   withdrawId,
    uint256 amount,
    uint64  activationEpoch
);
```

### Withdraw

Emitted when a delegator executes [`withdraw`](#withdraw) successfully.

```solidity theme={null}
event Withdraw(
    uint64 indexed validatorId,
    address indexed delegator,
    uint8   withdrawId,
    uint256 amount,
    uint64  withdrawEpoch
);
```

### ClaimRewards

Emitted when a delegator claims rewards via [`claimRewards`](#claimrewards).

```solidity theme={null}
event ClaimRewards(
    uint64 indexed validatorId,
    address indexed delegator,
    uint256 amount,
    uint64 epoch
);
```

### CommissionChanged

Emitted when a validator changes commission via [`changeCommission`](#changecommission).

```solidity theme={null}
event CommissionChanged(
    uint64 indexed validatorId,
    uint256 oldCommission,
    uint256 newCommission
);
```

### EpochChanged

Emitted when epoch changes via [`syscallOnEpochChange`](#syscallonepochchange).

```solidity theme={null}
    event EpochChanged(
        uint64 oldEpoch,
        uint64 newEpoch
    );
```

## Precompile internals

* [Constants](#constants)
* [Validator structs](#validator-structs)
* [Delegator structs](#delegator-structs)
* [State variables](#state-variables)
* [Mappings](#mappings)

### Validator structs

```solidity theme={null}
struct ValExecution             // Realtime execution state for one validator
{
    uint256 stake;              // Upcoming stake pool balance
    uint256 acc;                // Current accumulator value for validator
    uint256 commission;         // Proportion of block reward charged as commission, times 1e18; 10% = 1e17
    bytes   secp_pubkey;        // Secp256k1 public key used by consensus
    bytes   bls_pubkey;         // Bls public key used by consensus
    uint256 address_flags;      // Flags to represent validators' current state
    uint256 unclaimed_rewards;  // Unclaimed rewards
    address auth_address;       // Delegator address with authority over validator stake
}

struct ValConsensus             // A subset of validator state for the consensus system
{
    uint256 stake;              // Current active stake
    uint256 commission;         // Commission rate for current epoch
    bytes   secp_pubkey;        // Secp256k1 public key used by consensus
    bytes   bls_pubkey;         // Bls public key used by consensus
}
```

### Delegator structs

```solidity theme={null}
struct DelInfo
{
    uint256 stake;               // Current active stake
    uint256 acc;                 // Last checked accumulator
    uint256 rewards;             // Last checked rewards
    uint256 delta_stake;         // Stake to be activated next epoch
    uint256 next_delta_stake;    // Stake to be activated in 2 epochs
    uint64 delta_epoch;          // Epoch when delta_stake becomes active
    uint64 next_delta_epoch;     // Epoch when next_delta_stake becomes active
}

struct WithdrawalRequest
{
    uint256 amount;              // Amount to undelegate from validator
    uint256 acc;                 // Validator accumulator when undelegate was called
    uint64 epoch;                // Epoch when undelegate stake deactivates
};

struct Accumulator
{
    uint256 val;               // Current accumulator value
    uint256 refcount;            // Reference count for this accumulator value
};
```

### State variables

```solidity theme={null}
// Current consensus epoch
uint64 epoch;

// Flag indicating if currently in epoch delay rounds
bool in_epoch_delay_rounds;

// Counter for validator ids
uint64 last_val_id;

// Current execution view of validator set
StorageArray<uint64> execution_valset;

// Previous consensus view of validator set
StorageArray<uint64> snapshot_valset;

// Current consensus view of validator set
StorageArray<uint64> consensus_valset;
```

### Mappings

```solidity theme={null}
//These mappings only exist to ensure the SECP/BLS Keys are unique
mapping (secp_eth_address => uint64) secp_to_val_id;
mapping (bls_eth_address => uint64) bls_to_val_id;

// Keys(val_id, epoch) => Value(acc)
// making note of the validator accumulator at start of epoch.
mapping(uint64 => mapping(uint64 => Accumulator)) epoch_acc;

// Key(val_id)
// Contains the validator info for the execution view. Changes to stake
// or commission are reflected immediately.
mapping(uint64 => ValExecution) val_execution;

// Key(val_id)
// Contains a subset of the validator info relevant to consensus. Changes to
// stake or commission are reflected in the following epoch. This is referenced
// by the reward system call *before* the epoch delay rounds.
mapping(uint64 => ValConsensus) val_consensus;

// Key(val_id)
// Contains a subset of the validator info relevant to consensus. Changes to
// stake or commission are reflected in the following epoch. This is referenced
// by the reward system call *during* the epoch delay rounds.
mapping(uint64 => ValConsensus) val_snapshot;

// Keys(val_id,msg.sender) => DelInfo
mapping(uint64 => mapping(address => DelInfo)) delegator;

// Keys(val_id,msg.sender,withdrawal_id) => WithdrawalRequest
mapping(uint64 => mapping(address => mapping (uint8 => WithdrawalRequest))) withdrawal;
```

## Solidity Staking Interface

*To copy to clipboard, click the button in the top right of the code block.*

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

interface IMonadStaking {
    function addValidator(
        bytes calldata payload,
        bytes calldata signedSecpMessage,
        bytes calldata signedBlsMessage
    ) external payable returns (uint64 validatorId);

    function delegate(
        uint64 validatorId
    ) external payable returns (bool success);

    function undelegate(
        uint64 validatorId,
        uint256 amount,
        uint8 withdrawId
    ) external returns (bool success);

    function compound(
        uint64 validatorId
    ) external returns (bool success);

    function withdraw(
        uint64 validatorId,
        uint8 withdrawId
    ) external returns (bool success);

    function claimRewards(
        uint64 validatorId
    ) external returns (bool success);

    function changeCommission(
        uint64 validatorId,
        uint256 commission
    ) external returns (bool success);

    function externalReward(
        uint64 validatorId
    ) external payable returns (bool success);


    function getValidator(
        uint64 validatorId
    ) external returns (
        address authAddress,
        uint64 flags,
        uint256 stake,
        uint256 accRewardPerToken,
        uint256 commission,
        uint256 unclaimedRewards,
        uint256 consensusStake,
        uint256 consensusCommission,
        uint256 snapshotStake,
        uint256 snapshotCommission,
        bytes memory secpPubkey,
        bytes memory blsPubkey
    );

    function getDelegator(
        uint64 validatorId,
        address delegator
    ) external returns (
        uint256 stake,
        uint256 accRewardPerToken,
        uint256 unclaimedRewards,
        uint256 deltaStake,
        uint256 nextDeltaStake,
        uint64 deltaEpoch,
        uint64 nextDeltaEpoch
    );

    function getWithdrawalRequest(
        uint64 validatorId,
        address delegator,
        uint8 withdrawId
    ) external returns (
        uint256 withdrawalAmount,
        uint256 accRewardPerToken,
        uint64 withdrawEpoch
    );

    function getConsensusValidatorSet(
        uint32 startIndex
    ) external returns (bool isDone, uint32 nextIndex, uint64[] memory valIds);

    function getSnapshotValidatorSet(
        uint32 startIndex
    ) external returns (bool isDone, uint32 nextIndex, uint64[] memory valIds);

    function getExecutionValidatorSet(
        uint32 startIndex
    ) external returns (bool isDone, uint32 nextIndex, uint64[] memory valIds);

    function getDelegations(
        address delegator,
        uint64 startValId
    ) external returns (bool isDone, uint64 nextValId, uint64[] memory valIds);

    function getDelegators(
        uint64 validatorId,
        address startDelegator
    ) external returns (bool isDone, address nextDelegator, address[] memory delegators);

    function getEpoch() external returns (uint64 epoch, bool inEpochDelayPeriod);

    function getProposerValId() external returns (uint64 val_id);

    function syscallOnEpochChange(uint64 epoch) external;

    function syscallReward(address blockAuthor) external;

    function syscallSnapshot() external;

     event ValidatorRewarded(
        uint64 indexed validatorId,
        address indexed from,
        uint256 amount,
        uint64 epoch
    );
    event ValidatorCreated(
        uint64  indexed validatorId,
        address indexed authAddress,
        uint256 commission

    );
    event ValidatorStatusChanged(
        uint64  indexed validatorId,
        uint64  flags
    );
    event Delegate(
        uint64  indexed validatorId,
        address indexed delegator,
        uint256 amount,
        uint64  activationEpoch
    );
    event Undelegate(
        uint64  indexed validatorId,
        address indexed delegator,
        uint8   withdrawId,
        uint256 amount,
        uint64  activationEpoch
    );
    event Withdraw(
        uint64 indexed validatorId,
        address indexed delegator,
        uint8   withdrawId,
        uint256 amount,
        uint64  withdrawEpoch
    );
    event ClaimRewards(
        uint64 indexed validatorId,
        address indexed delegator,
        uint256 amount,
        uint64  epoch
    );
    event CommissionChanged(
        uint64 indexed validatorId,
        uint256 oldCommission,
        uint256 newCommission
    );
    event EpochChanged(
        uint64 oldEpoch,
        uint64 newEpoch
    );
}
```

## Staking ABI JSON

*To copy to clipboard, click the button in the top right of the code block.*

```json theme={null}
[
  {"type":"function","name":"addValidator","inputs":[{"name":"payload","type":"bytes","internalType":"bytes"},{"name":"signedSecpMessage","type":"bytes","internalType":"bytes"},{"name":"signedBlsMessage","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"}],"stateMutability":"payable"},
  {"type":"function","name":"changeCommission","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"},{"name":"commission","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"success","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"claimRewards","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"success","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"compound","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"success","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"delegate","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"success","type":"bool","internalType":"bool"}],"stateMutability":"payable"},
  {"type":"function","name":"externalReward","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"success","type":"bool","internalType":"bool"}],"stateMutability":"payable"},
  {"type":"function","name":"getConsensusValidatorSet","inputs":[{"name":"startIndex","type":"uint32","internalType":"uint32"}],"outputs":[{"name":"isDone","type":"bool","internalType":"bool"},{"name":"nextIndex","type":"uint32","internalType":"uint32"},{"name":"valIds","type":"uint64[]","internalType":"uint64[]"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"getDelegations","inputs":[{"name":"delegator","type":"address","internalType":"address"},{"name":"startValId","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"isDone","type":"bool","internalType":"bool"},{"name":"nextValId","type":"uint64","internalType":"uint64"},{"name":"valIds","type":"uint64[]","internalType":"uint64[]"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"getDelegator","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"},{"name":"delegator","type":"address","internalType":"address"}],"outputs":[{"name":"stake","type":"uint256","internalType":"uint256"},{"name":"accRewardPerToken","type":"uint256","internalType":"uint256"},{"name":"unclaimedRewards","type":"uint256","internalType":"uint256"},{"name":"deltaStake","type":"uint256","internalType":"uint256"},{"name":"nextDeltaStake","type":"uint256","internalType":"uint256"},{"name":"deltaEpoch","type":"uint64","internalType":"uint64"},{"name":"nextDeltaEpoch","type":"uint64","internalType":"uint64"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"getDelegators","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"},{"name":"startDelegator","type":"address","internalType":"address"}],"outputs":[{"name":"isDone","type":"bool","internalType":"bool"},{"name":"nextDelegator","type":"address","internalType":"address"},{"name":"delegators","type":"address[]","internalType":"address[]"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"getEpoch","inputs":[],"outputs":[{"name":"epoch","type":"uint64","internalType":"uint64"},{"name":"inEpochDelayPeriod","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"getProposerValId","inputs":[],"outputs":[{"name":"val_id","type":"uint64","internalType": "uint64"}],
  "stateMutability":"nonpayable"},
  {"type":"function","name":"getExecutionValidatorSet","inputs":[{"name":"startIndex","type":"uint32","internalType":"uint32"}],"outputs":[{"name":"isDone","type":"bool","internalType":"bool"},{"name":"nextIndex","type":"uint32","internalType":"uint32"},{"name":"valIds","type":"uint64[]","internalType":"uint64[]"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"getSnapshotValidatorSet","inputs":[{"name":"startIndex","type":"uint32","internalType":"uint32"}],"outputs":[{"name":"isDone","type":"bool","internalType":"bool"},{"name":"nextIndex","type":"uint32","internalType":"uint32"},{"name":"valIds","type":"uint64[]","internalType":"uint64[]"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"getValidator","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"authAddress","type":"address","internalType":"address"},{"name":"flags","type":"uint64","internalType":"uint64"},{"name":"stake","type":"uint256","internalType":"uint256"},{"name":"accRewardPerToken","type":"uint256","internalType":"uint256"},{"name":"commission","type":"uint256","internalType":"uint256"},{"name":"unclaimedRewards","type":"uint256","internalType":"uint256"},{"name":"consensusStake","type":"uint256","internalType":"uint256"},{"name":"consensusCommission","type":"uint256","internalType":"uint256"},{"name":"snapshotStake","type":"uint256","internalType":"uint256"},{"name":"snapshotCommission","type":"uint256","internalType":"uint256"},{"name":"secpPubkey","type":"bytes","internalType":"bytes"},{"name":"blsPubkey","type":"bytes","internalType":"bytes"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"getWithdrawalRequest","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"},{"name":"delegator","type":"address","internalType":"address"},{"name":"withdrawId","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"withdrawalAmount","type":"uint256","internalType":"uint256"},{"name":"accRewardPerToken","type":"uint256","internalType":"uint256"},{"name":"withdrawEpoch","type":"uint64","internalType":"uint64"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"syscallOnEpochChange","inputs":[{"name":"epoch","type":"uint64","internalType":"uint64"}],"outputs":[],"stateMutability":"nonpayable"},
  {"type":"function","name":"syscallReward","inputs":[{"name":"blockAuthor","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},
  {"type":"function","name":"syscallSnapshot","inputs":[],"outputs":[],"stateMutability":"nonpayable"},
  {"type":"function","name":"undelegate","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"withdrawId","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"success","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},
  {"type":"function","name":"withdraw","inputs":[{"name":"validatorId","type":"uint64","internalType":"uint64"},{"name":"withdrawId","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"success","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},
  {"type":"event","name":"ClaimRewards","inputs":[{"name":"validatorId","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"delegator","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"epoch","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},
  {"type":"event","name":"CommissionChanged","inputs":[{"name":"validatorId","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"oldCommission","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"newCommission","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},
  {"type":"event","name":"Delegate","inputs":[{"name":"validatorId","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"delegator","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"activationEpoch","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},
  {"type":"event","name":"EpochChanged","inputs":[{"name":"oldEpoch","type":"uint64","indexed":false,"internalType":"uint64"},{"name":"newEpoch","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},
  {"type":"event","name":"Undelegate","inputs":[{"name":"validatorId","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"delegator","type":"address","indexed":true,"internalType":"address"},{"name":"withdrawId","type":"uint8","indexed":false,"internalType":"uint8"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"activationEpoch","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},
  {"type":"event","name":"ValidatorCreated","inputs":[{"name":"validatorId","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"authAddress","type":"address","indexed":true,"internalType":"address"},{"name":"commission","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},
  {"type":"event","name":"ValidatorRewarded","inputs":[{"name":"validatorId","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"from","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"epoch","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},
  {"type":"event","name":"ValidatorStatusChanged","inputs":[{"name":"validatorId","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"flags","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},
  {"type":"event","name":"Withdraw","inputs":[{"name":"validatorId","type":"uint64","indexed":true,"internalType":"uint64"},{"name":"delegator","type":"address","indexed":true,"internalType":"address"},{"name":"withdrawId","type":"uint8","indexed":false,"internalType":"uint8"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"withdrawEpoch","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false}
]
```

## FAQ

<Accordion title="Is there a `removeValidator` function?">
  There is no direct `removeValidator` function. Instead, if a validator's `auth_account` removes
  enough stake through [`undelegate`](#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.
</Accordion>

<Accordion title="How does a validator change their `commission`?">
  A validator can change their `commission` by calling [`changeCommission`](#changecommission).
</Accordion>

<Accordion title="Which functions give visibility into the list of validator ids?">
  See the [valset getters](#getvalidatorset).
</Accordion>

<Accordion title="Which functions give visibility into a validator's state?">
  See [`getValidator`](#getvalidator).
</Accordion>

<Accordion title="Which functions give visibility into a delegator's delegation to one particular validator?">
  See [`getDelegator`](#getdelegator).

  For pending withdrawals by that delegator from that validator, see [`getWithdrawalRequest`](#getwithdrawalrequest).
</Accordion>

<Accordion title="Is it more like a contract or a precompile?">
  Despite using Solidity selectors and ABI, it is a precompile. Accessing code at <CopyToClipboard value="0x0000000000000000000000000000000000001000">`0x0000000000000000000000000000000000001000`</CopyToClipboard>
  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](https://eips.ethereum.org/EIPS/eip-7702), all calls to it will revert.
</Accordion>
