> ## 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.

# 质押 API 参考

> Monad 质押预编译合约参考

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>;
};

本页是质押预编译合约接口的技术参考。要获取面向开发者的常见工作流指南,请访问 [概述](/zh/reference/staking/overview)。

## 地址

| 合约      | 地址                                                                                                                                 |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| 质押预编译合约 | <CopyToClipboard value="0x0000000000000000000000000000000000001000">`0x0000000000000000000000000000000000001000`</CopyToClipboard> |

## 函数与选择器

| 状态修改函数                                                  | 选择器          |
| ------------------------------------------------------- | ------------ |
| [`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` |

| 视图函数                                                                  | 选择器          |
| --------------------------------------------------------------------- | ------------ |
| [`getValidator(uint64)`](#getvalidator)                               | `0x2b6d639a` |
| [`getDelegator(uint64,address)`](#getdelegator)                       | `0x573c1ce0` |
| [`getWithdrawalRequest(uint64,address,uint8)`](#getwithdrawalrequest) | `0x56fa2045` |
| [`getConsensusValidatorSet(uint32)`](#get-validatorset)               | `0xfb29b729` |
| [`getSnapshotValidatorSet(uint32)`](#get-validatorset)                | `0xde66a368` |
| [`getExecutionValidatorSet(uint32)`](#get-validatorset)               | `0x7cb074df` |
| [`getDelegations(address,uint64)`](#getdelegations)                   | `0x4fd66050` |
| [`getDelegators(uint64,address)`](#getdelegators)                     | `0xa0843a26` |
| [`getEpoch()`](#getepoch)                                             | `0x757991a8` |
| [`getProposerValId()`](#getproposervalid)                             | `0xfbacb0be` |

| Syscall 函数                                              | 选择器          |
| ------------------------------------------------------- | ------------ |
| [`syscallOnEpochChange(uint64)`](#syscallonepochchange) | `0x1d4e9f02` |
| [`syscallReward(address)`](#syscallreward)              | `0x791bdcf3` |
| [`syscallSnapshot()`](#syscallsnapshot)                 | `0x157eeb21` |

## 常量

| 常量                        | 含义                          | 值                |
| ------------------------- | --------------------------- | ---------------- |
| `BOUNDARY_BLOCK_PERIOD`   | 从边界区块到下一个边界区块之间的区块数         | 50,000 个区块       |
| `EPOCH_DELAY_ROUNDS`      | 边界区块与每个纪元开始之间的轮数            | 5,000 轮          |
| `WITHDRAWAL_DELAY`        | 解除质押的代币可提现前需要等待的纪元数         | 1 个纪元            |
| `MIN_AUTH_ADDRESS_STAKE`  | 验证者自委托、以获得进入活跃集资格的最小 MON 数量 | 100,000 MON      |
| `ACTIVE_VALIDATOR_STAKE`  | 验证者获得活跃集资格所需的最小总质押 MON 数量   | 10,000,000 MON   |
| `ACTIVE_VALSET_SIZE`      | 活跃集中的验证者数量                  | 200              |
| `REWARD`                  | 每个区块的 MON 奖励                | 18 MON           |
| `DUST_THRESHOLD`          | 每笔单独委托的最小金额                 | 1 gwei (1e9 wei) |
| `MAX_COMMISSION`          | 最大佣金率                       | 100% (1e18)      |
| `ACCUMULATOR_DENOMINATOR` | 用于精度的累加器单位乘数                | 1e36             |
| `PAGINATED_RESULTS_SIZE`  | 每次分页调用返回的最大结果数              | 100              |

## 状态修改函数

### addValidator

创建一个关联委托者账户的验证者,并返回结果 `validatorId`。

**函数选择器**

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

**函数签名**

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

**参数**

1. `payload` - 由以下字段组成,以大端字节序打包在一起(等价于 Solidity 中的 `abi.encodePacked()`):
   * `bytes secpPubkey`(用于共识的唯一 SECP 公钥)
   * `bytes blsPubkey`(用于共识的唯一 BLS 公钥)
   * `address authAddress`(用于验证者委托者账户的地址。该地址对验证者的质押金额拥有提现权限)
   * `uint256 amount`(验证者自我质押的金额。必须等于 `msg.value`)
   * `uint256 commission`(向委托者收取的佣金,乘以 1e18,例如 `10% = 1e17`)
2. `signedSecpMessage` - 对 payload 的 SECP 签名
3. `signedBlsMessage` - 对 payload 的 BLS 签名

**Gas 成本**

505,125

**行为**

该函数首先解包 payload 以获取 `secpPubkey`、`blsPubkey`、`authAddress`、`amount` 和 `commission`,然后验证 `signedSecpMessage` 和 `signedBlsMessage` 对应于由相应 SECP 和 BLS 私钥签名的 payload。

* 验证者必须同时提供唯一的 BLS 密钥和唯一的 SECP 密钥。任何包含重复公钥的提交都会回滚。
* 两个签名(`signedSecpMessage` 和 `signedBlsMessage`)都必须有效,且都必须是对 `payload` 的签名。
* 多个验证者可以共享同一个 `authAddress`。
* `msg.value` 必须大于或等于 `MIN_AUTH_ADDRESS_STAKE`,否则调用将回滚。
* 如果 `msg.value` 还大于或等于 `ACTIVE_VALIDATOR_STAKE`,则该验证者将在未来激活:
  * 如果 `addValidator` 在边界区块之前调用,则在纪元 `n+1`;
  * 否则将在纪元 `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>

**用法**

以下是组装 payload 并签名的示例:

```
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

如果委托者账户不存在则创建,并增加委托者的余额。

**函数选择器**

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

**函数签名**

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

**参数**

1. `validatorId` - 委托者希望委托的验证者的 ID
2. `msg.value` - 要委托的金额

**Gas 成本**

260,850

**行为**

* 委托者账户由 `msg.sender` 决定。
* `validatorId` 必须对应一个有效的验证者。
* `msg.value` 必须 >= `DUST_THRESHOLD`。
* 如果此次委托使验证者的总质押超过 `ACTIVE_VALIDATOR_STAKE`,并且该验证者尚不在 `execution_valset` 中,则会被添加进去。
* 委托者的质押变为活跃
  * 若请求在边界区块之前,则在纪元 `n+1` 生效
  * 否则在纪元 `n+2` 生效

<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

从委托者账户扣除 `amount` 并将其移入一个提现请求对象,在 `WITHDRAWAL_DELAY` 个纪元的挂起状态之后,资金即可通过 [`withdraw`](#withdraw) 函数领取。

**函数选择器**

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

**函数签名**

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

**参数**

1. `validatorId` - 发送者此前已委托的验证者的 ID,我们要从该验证者移除委托
2. `amount` - 要解除委托的金额,以 Monad wei 计
3. `withdrawId` - 0 到 255(含)之间的整数,作为委托者提现的标识符。对于每个 (validator, delegator) 元组,最多可有 256 个未完成的提现请求

**Gas 成本**

147,750

**行为**

* 委托者账户由 `msg.sender` 决定。
* `validatorId` 必须对应一个发送者此前已委托过的有效验证者
* 委托者必须拥有 >= amount 的质押。
* 如果提现导致 `Val(validatorId).stake()` 降至 `ACTIVE_VALIDATOR_STAKE` 以下,则该验证者将被安排从 valset 中移除。
* 如果验证者的 `authAddress` 解除足够多的自身质押,使其降至 `MIN_AUTH_ADDRESS_STAKE` 以下,则该验证者将被安排从 valset 中移除。
* 如果存在具有相同 `withdrawId` 的待处理提现,该函数会回滚。调用 [`withdraw`](#withdraw) 后 `withdrawId` 可复用。
* 委托者只能在质押已激活后才能移除该质押。这是委托者结构体中的 stake 字段。待生效的委托在激活前不能被移除。
* 委托者的质押在 valset 中变为非活跃
  * 若请求在边界区块之前,则在纪元 `n+1` 生效
  * 否则在纪元 `n+2` 生效
* 委托者的质押变为可提现,因此不再受罚没约束
  * 若请求在边界区块之前,则在纪元 `n + 1 + WITHDRAWAL_DELAY`
  * 否则在纪元 `n + 2 + WITHDRAWAL_DELAY`

<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"}}>质押可提现性相对于 <code>undelegate</code> 命令的时间线</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

完成一次解除委托操作(以调用 `undelegate` 函数开始),将金额发送给 `msg.sender`,前提是已经过足够的纪元。

**函数选择器**

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

**函数签名**

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

**参数**

1. `validatorId` - 发送者此前已委托的验证者的 ID,我们之前对该验证者发出了 `undelegate` 命令
2. `withdrawId` - 委托者此前创建的提现的标识符;即之前提供给 [`undelegate`](#undelegate) 的相同 id。对于每个 (validator, delegator) 元组,最多可有 256 个未完成的提现请求。

**Gas 成本**

68,675

**行为**

* 委托者为 `msg.sender`。提现由 `msg.sender`、`validatorId` 和 `withdrawId` 标识
* 一旦解除委托完成且提现延迟已过,即可执行提现操作:
  * 若解除委托请求在边界区块之前,则在纪元 `n + 1 + WITHDRAWAL_DELAY`
  * 否则在纪元 `n + 2 + WITHDRAWAL_DELAY`

<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

将委托者已累积的奖励转换为额外的质押。

**函数选择器**

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

**函数签名**

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

**参数**

1. `validatorId` - 发送者此前已委托的验证者的 ID,我们要为其复投奖励

**Gas 成本**

289,325

**行为**

* 用于复投的账户由 `msg.sender` 决定。如果委托者账户不存在,则调用回滚
* `validatorId` 必须对应一个发送者此前已委托过的有效验证者
* 委托者的奖励在 valset 中变为活跃
  * 若请求在边界区块之前,则在纪元 `n+1` 生效
  * 否则在纪元 `n+2` 生效.

<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

允许委托者领取奖励而非 [复投](#compound) 它们。

**函数选择器**

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

**函数签名**

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

**参数**

1. `validatorId` - 发送者此前已委托的验证者的 ID,我们要为其领取奖励

**Gas 成本**

155,375

**行为**

* `validatorId` 必须对应一个发送者此前已委托过的有效验证者
* 如果该 `(validatorId, msg.sender)` 元组对应的委托者账户不存在,则调用回滚
* 委托者累积的奖励会转入其委托账户

<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

允许验证者的 `authAddress` 修改该验证者的佣金。

**函数选择器**

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

**函数签名**

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

**参数**

1. `validatorId` - 希望更改佣金率的验证者的 ID
2. `commission` - 从区块奖励中收取的佣金率,以 1e18 为单位表示(例如 10% = 1e17)

**Gas 成本**

39,475

**行为**

* `msg.sender` 必须是相应验证者 Id 的 `authAddress`。
* 佣金不能设置为大于 `MAX_COMMISSION`(当前为 100%)。
* 佣金变更在以下纪元生效:
  * 如果请求不在纪元延迟轮次内,则在纪元 `n+1`。
  * 如果请求在纪元延迟轮次内,则在纪元 `n+2`。

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


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


  ```
</Accordion>

### externalReward

允许任何人向特定验证者的质押者发送额外的 MON,通常由验证者自己调用以将额外的小费分享给其委托者。

**函数选择器**

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

**函数签名**

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

**参数**

1. `validatorId` - 验证者的 ID
2. `msg.value` - 要添加到未领取奖励中的 MON

**Gas 成本**

66,575

**行为**

* 只能对当前在共识验证者集中的验证者调用;否则交易将回滚。
* `msg.value` 必须介于 1 MON 与 1,000,000 MON 之间;否则交易将回滚。
* 此处不会从中扣除佣金并转入验证者的 `auth_address`。如果您希望扣除一部分,应在发送前先扣除。

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

## 视图函数

<Note>
  由于质押预编译合约只允许 `CALL`,所有视图函数都被赋予默认的 `nonpayable` 状态可变性。
</Note>

### getValidator

返回验证者跨执行、共识和快照上下文的完整状态视图。

* [`ValExecution`](#validator-structs)(执行视图)
* 质押和佣金(共识视图)
* 质押和佣金(快照视图)

**函数选择器**

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

**函数签名**

```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
);
```

**参数**

1. `validatorId` - 验证者的 ID

**Gas 成本**

97,200

### getDelegator

返回指定验证者对应的委托者 [`DelInfo`](#delegator-structs),提供委托者的质押、累积奖励和待生效质押变更的视图。

**函数选择器**

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

**函数签名**

```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
);
```

**参数**

1. `validatorId` - 验证者的 ID
2. `delegator` - address of the delegator about whose stake we are inquiring

**Gas 成本**

184,900

### getWithdrawalRequest

返回 `(validatorId, delegator, withdrawId)` 元组对应的待处理 [`WithdrawalRequest`](#delegator-structs)。

**函数选择器**

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

**函数签名**

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

**Gas 成本**

24,300

### get\*ValidatorSet

分别返回共识、快照和执行验证者 ID。

**函数选择器**

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

**函数签名**

```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);
```

**参数**

1. `startIndex` - 由于所查找的列表可能非常长,因此这些函数都是分页的,返回所需列表的一个固定长度子集。传入 `startIndex` 指示从列表的哪个位置开始。

**Gas 成本**

814,000 gas (assuming `PAGINATED_RESULTS_SIZE = 100`).

**行为**

`getExecutionValidatorSet()` 返回所有符合入选质押标准的完整活跃验证者集合。`getSnapshotValidatorSet()` 返回被选定为下一纪元领导者的验证者子集。`getConsensusValidatorSet()` 返回当前纪元中作为领导者的验证者子集。

每次调用从 `startIndex` 开始最多获取 `PAGINATED_RESULTS_SIZE` 个验证者 ID,并返回元组 `(bool done, uint32 nextIndex, uint256[] valids)`。

`bool isDone` 表示是否已到达列表末尾。`uint32 nextIndex` 是数组中的最后一个槽位。

### getDelegations

返回给定委托者地址已委托的验证者 ID 的分页列表。

**函数选择器**

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

**函数签名**

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

**参数**

1. `delegator` - 我们要查询其委托的地址
2. `startValId`

**Gas 成本**

814,000

**行为**

每次调用从 `startValId` 开始最多获取 `PAGINATED_RESULTS_SIZE` 个验证者 ID,并返回元组 `(bool isDone, uint64 nextValId, uint64[] valIds)`,包含来自输入 `delegator` 地址的委托。

`bool isdone` 表示是否已到达列表末尾。`uint64 nextValId` 是 `valIds` 中最后一个元素之后的 ID。将其用作下一次调用的 `startValId`。

如果 `delegator` 委托的验证者 ID 数超过 `PAGINATED_RESULTS_SIZE`,则需要多次调用(在 `isDone` 为 false 时继续)。

要获取完整集合,首次调用请使用 `startValId = 0`。

### getDelegators

返回给定验证者的委托者地址分页列表。

**函数选择器**

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

**函数签名**

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

**参数**

1. `validatorId` - 我们要了解其委托者的验证者的 ID
2. `startDelegator`

**Gas 成本**

814,000

**行为**

每次调用从 `startDelegator` 开始最多获取 `PAGINATED_RESULTS_SIZE` 个委托者地址,并返回元组 `(bool isDone, address nextDelegator, address[] delegators)`,包含对输入 `validatorId` 的委托。

`bool isDone` 表示已到达列表末尾。`nextDelegator` 是 `delegators` 中最后一个元素之后的地址。将其用作下一次调用的 `startDelegator`。

要获取完整集合,应使用 `startDelegator = 0` 调用该函数。

<Note>
  某个验证者的委托者数量可能非常庞大,因此建议通过 [events](/zh/monad-arch/realtime-data/data-sources#how-do-i-receive-real-time-data) 框架维护一份更新的列表,而不是周期性地调用这个昂贵的查找。
</Note>

### getEpoch

返回当前纪元以及在纪元中的时序(边界区块之前或之后)。

**函数选择器**

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

**函数签名**

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

**Gas 成本**

200

**行为**

如果 `inEpochDelayPeriod` 为 false,则尚未到达边界区块,此时的写操作应在 `epoch + 1` 生效。如果 `inEpochDelayPeriod` 为 true,则网络已过边界区块,此时的写操作应在 `epoch + 2` 生效

### getProposerValId

返回当前区块提议者的验证者 ID,对应于区块作者的 SECP 值。

**函数选择器**

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

**函数签名**

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

**Gas 成本**

100

## Syscalls

目前有三个 syscall。用户不能直接调用它们。它们只能通过特殊的系统交易触发。

### syscallOnEpochChange

在纪元延迟轮次结束时触发,以敲定累加器值并更新纪元状态。

**函数选择器**

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

**函数签名**

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

**参数**

1. `epoch` - 正要进入的新共识纪元

**行为**

1. 如果验证者在上一纪元收到了更改质押的请求,并参与了上一纪元的共识验证者集,则保存相应的累加器值
2. 如果任何验证者在上一纪元处于活跃状态但在当前纪元变为非活跃,也保存其当前的累加器值
3. 在状态中设置当前纪元

<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

为每个区块奖励出块的验证者及其委托者已配置的区块奖励。

**函数选择器**

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

**函数签名**

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

**参数**

1. `blockAuthor` —— 出块验证者的地址。

**行为**

1. 如果验证者的佣金不为零,则会将一部分奖励分配给验证者的 `authAddress`。
2. 剩余的奖励可供验证者的委托者领取。

请注意,佣金按总区块奖励的百分比计算。

<Info title="Example">
  * 假设某验证者的个人质押占其验证者总委托的 20%。
  * 佣金设置为总奖励的 10%。

  那么该验证者将获得总区块奖励的 10% 作为佣金。剩余的 90% 分配给质押池。由于该验证者拥有质押池的 20%,他们也将获得剩余部分的 20%。
</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

对当前执行层验证者集进行排序,选出质押最多的前 `N` 名验证者作为即将到来的共识验证者集,将更新后的集合存入状态,并清除之前的共识集。

**函数选择器**

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

**函数签名**

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

**行为**

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

## 事件

质押预编译合约会发出出现在交易回执中的标准事件。这些事件为验证者和委托者的操作提供带索引的信息。

### ValidatorRewarded

当通过 [`syscallReward`](#syscallreward) 分配区块奖励,或通过 [`externalReward`](#externalreward) 向验证者的质押者发送额外 MON 时发出。

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

### ValidatorCreated

当通过 [`addValidator`](#addvalidator) 添加验证者时发出。

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

### ValidatorStatusChanged

在 [`addValidator`](#addvalidator)、[`delegate`](#delegate)、[`undelegate`](#undelegate) 或 [`compound`](#compound) 期间,当验证者的标志发生变化时发出。

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

### Delegate

当委托金额增加时发出,即在 [`addValidator`](#addvalidator)、[`delegate`](#delegate) 或 [`compound`](#compound) 期间。

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

### Undelegate

当委托者调用 [`undelegate`](#undelegate) 时发出。

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

### Withdraw

当委托者成功执行 [`withdraw`](#withdraw) 时发出。

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

### ClaimRewards

当委托者通过 [`claimRewards`](#claimrewards) 领取奖励时发出。

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

### CommissionChanged

当验证者通过 [`changeCommission`](#changecommission) 更改佣金时发出。

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

### EpochChanged

当纪元通过 [`syscallOnEpochChange`](#syscallonepochchange) 改变时发出。

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

## 预编译合约内部

* [常量](#constants)
* [验证者结构体](#validator-structs)
* [委托者结构体](#delegator-structs)
* [状态变量](#state-variables)
* [映射](#mappings)

### 验证者结构体

```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
}
```

### 委托者结构体

```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
};
```

### 状态变量

```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;
```

### 映射

```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 质押接口

*点击代码块右上角的按钮可复制到剪贴板。*

```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
    );
}
```

## 质押 ABI JSON

*点击代码块右上角的按钮可复制到剪贴板。*

```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}
]
```

## 常见问题

<Accordion title="是否存在 `removeValidator` 函数?">
  没有直接的 `removeValidator` 函数。相反,如果某验证者的 `auth_account` 通过 [`undelegate`](#undelegate) 移除了足够多的质押,该验证者会在未来某个纪元从共识集合中被移除。

  这将发生在纪元 `n+1` 或纪元 `n+2`,取决于 `undelegate` 是否发生在纪元延迟轮次内。

  即使未处于活跃状态,验证者的信息也会始终保留。验证者 ID 是永久的,因为其他委托者可能仍在委托,并需要引用该 `val_id` 进行解除委托/提现。
</Accordion>

<Accordion title="验证者如何更改自己的 `commission`?">
  验证者可通过调用 [`changeCommission`](#changecommission) 更改其 `commission`。
</Accordion>

<Accordion title="哪些函数可以查看验证者 ID 列表?">
  参见 [valset getters](#get-validatorset)。
</Accordion>

<Accordion title="哪些函数可以查看验证者的状态?">
  参见 [`getValidator`](#getvalidator)。
</Accordion>

<Accordion title="哪些函数可以查看委托者对某个特定验证者的委托情况?">
  参见 [`getDelegator`](#getdelegator)。

  对于该委托者对该验证者的待处理提现,请参见 [`getWithdrawalRequest`](#getwithdrawalrequest)。
</Accordion>

<Accordion title="它更像合约还是预编译?">
  尽管使用了 Solidity 选择器和 ABI,它是一个预编译合约。访问 <CopyToClipboard value="0x0000000000000000000000000000000000001000">`0x0000000000000000000000000000000000001000`</CopyToClipboard> 处的代码会返回空代码。它的账户总是以 warm 方式访问,使用无效参数的调用会消耗全部 gas。

  例外:如果某账户尝试使用 [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) 将委托设置为质押预编译合约,则对其所有的调用都会回滚。
</Accordion>
