Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

使用系统模块

Stable 通过位于固定地址的**预编译合约(precompiled contracts)**暴露协议层的结算逻辑。这些预编译合约让 EVM 代码可以调用 Stable SDK 模块(质押、奖励分发、STABLE 代币操作),而无需重新实现它们。由于它们运行在协议层,因此比等价的 Solidity 实现显著更省 gas。

本指南展示了如何同时从 Solidity 和 ethers.js 调用预编译合约,以及何时应该使用它而非普通合约。

暴露了哪些内容

模块预编译地址用途
Bank0x0000000000000000000000000000000000001003STABLE 代币转账和余额操作
Distribution0x0000000000000000000000000000000000000801领取质押奖励、奖励查询、佣金管理
Staking0x0000000000000000000000000000000000000800委托、解除委托、重新委托、验证人查询
Gov0x0000000000000000000000000000000000000805提案、计票结果和链上投票记录
Slashing0x0000000000000000000000000000000000000806验证人签名信息和在线时长
StableSystem0x0000000000000000000000000000000000009999为系统交易(解绑完成)发出 EVM 事件

以上所有内容都可以从任何 EVM 合约或链下客户端调用。这些地址是固定的,在主网和测试网上完全相同。

何时调用预编译合约 vs 普通合约

  • 使用预编译合约:当操作对应于某个 Stable SDK 模块时,例如质押、奖励分发、STABLE 代币操作。调用预编译合约既更便宜,也是触发协议层行为的唯一方式。
  • 使用普通合约:当操作属于应用逻辑时,例如托管、定价、访问控制。如果你需要自定义授权或验证,可将预编译合约的调用封装在你自己的合约中。

预编译合约并不是应用合约的替代品。它们是进入底层协议的稳定接口。

从 Solidity 调用

为你需要的方法声明一个接口,然后像调用已部署合约一样调用预编译合约。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
 
interface IStaking {
    function delegate(
        address delegatorAddress,
        string calldata validatorAddress,
        uint256 amount
    ) external returns (bool success);
 
    function delegation(
        address delegatorAddress,
        string calldata validatorAddress
    ) external view returns (uint256 shares, uint256 balance);
}
 
contract StakingHelper {
    address constant STAKING_PRECOMPILE =
        0x0000000000000000000000000000000000000800;
 
    /// @notice Delegate STABLE tokens to a validator from this contract.
    function delegateToValidator(
        string calldata validatorAddress,
        uint256 amount
    ) external returns (bool) {
        return IStaking(STAKING_PRECOMPILE).delegate(
            address(this),
            validatorAddress,
            amount
        );
    }
 
    /// @notice Read the current delegation of this contract to a validator.
    function myDelegation(string calldata validatorAddress)
        external
        view
        returns (uint256 shares, uint256 balance)
    {
        return IStaking(STAKING_PRECOMPILE).delegation(
            address(this),
            validatorAddress
        );
    }
}

使用 Foundry 或 Hardhat 编译并部署。预编译地址被固化到合约的常量槽中,因此部署后无需额外配置。

// SAFE: precompile address is fixed on Stable and never changes.

从 ethers.js 调用

对于链下客户端,将同样的接口声明为最小化 ABI,并实例化一个指向预编译地址的合约。

// queryDelegation.ts
import { ethers } from "ethers";
import "dotenv/config";
 
const provider = new ethers.JsonRpcProvider("https://rpc.testnet.stable.xyz");
 
const STAKING_PRECOMPILE = "0x0000000000000000000000000000000000000800";
 
const staking = new ethers.Contract(
  STAKING_PRECOMPILE,
  [
    "function delegation(address delegator, string validator) view returns (uint256 shares, uint256 balance)",
  ],
  provider
);
 
const delegator = "0xDelegatorAddress";
const validator = "stablevaloper1..."; // bech32 validator operator address
 
const [shares, balance] = await staking.delegation(delegator, validator);
console.log("Delegation shares: ", shares.toString());
console.log("Delegation balance:", ethers.formatUnits(balance, 18), "STABLE");
npx tsx queryDelegation.ts
Delegation shares:  1000000000000000000000
Delegation balance: 1000.0 STABLE

订阅系统交易事件

某些 Stable SDK 操作(例如解绑完成)本身不会自然地发出 EVM 事件。Stable 通过**系统交易(system transactions)**来填补这一空白:由验证人生成的交易会调用 StableSystem 预编译合约,在下一个区块中发出标准的 EVM 事件。

要监听 UnbondingCompleted,请像监听任何 ERC-20 Transfer 一样,在该预编译地址上进行订阅。

// watchUnbonding.ts
import { ethers } from "ethers";
 
const provider = new ethers.JsonRpcProvider("https://rpc.testnet.stable.xyz");
 
const STABLE_SYSTEM = "0x0000000000000000000000000000000000009999";
 
const stableSystem = new ethers.Contract(
  STABLE_SYSTEM,
  [
    "event UnbondingCompleted(address indexed delegator, address indexed validator, uint256 amount)",
  ],
  provider
);
 
stableSystem.on("UnbondingCompleted", (delegator, validator, amount, event) => {
  console.log("Unbonding completed for:", delegator);
  console.log("Amount:", ethers.formatEther(amount), "STABLE");
  console.log("Tx:", event.log.transactionHash);
});
 
console.log("Listening for UnbondingCompleted events...");
npx tsx watchUnbonding.ts
Listening for UnbondingCompleted events...
Unbonding completed for: 0xabcd...
Amount: 100.0 STABLE
Tx: 0x12ab...

关于完整的系统交易机制以及按用户筛选 / 历史查询的模式,请参阅追踪解绑完成

各模块参考

每个预编译合约的完整方法列表、事件和授权规则都在其对应的参考页面中。

推荐的后续阅读

  • 追踪解绑完成 — 订阅通过 StableSystem 预编译合约发出的 UnbondingCompleted 事件。
  • 系统模块参考 — 直接查看各模块的 ABI、方法签名和事件结构。
  • 系统模块概念 — 理解 Stable 为何通过预编译合约暴露 SDK 模块。