使用系统模块
Stable 通过固定地址的预编译合约公开协议级结算逻辑。预编译合约允许 EVM 代码调用 Stable SDK 模块(质押、奖励分配、STABLE 代币操作),而无需重新实现它们。由于它们在协议级别运行,因此比同等的 Solidity 实现效率更高,消耗的 Gas 更少。
本指南介绍了如何从 Solidity 和 ethers.js 调用预编译合约,以及何时使用预编译合约而不是常规合约。
开放的功能
| 模块 | 预编译合约地址 | 用途 |
|---|---|---|
| Bank (银行) | 0x0000000000000000000000000000000000001003 | STABLE 代币转账和余额操作 |
| Distribution (分配) | 0x0000000000000000000000000000000000000801 | 领取质押奖励、奖励查询、佣金管理 |
| Staking (质押) | 0x0000000000000000000000000000000000000800 | 委托、解除委托、重新委托、验证者查询 |
| Gov (治理) | 0x0000000000000000000000000000000000000805 | 提案、计票结果和链上投票记录 |
| Slashing (罚没) | 0x0000000000000000000000000000000000000806 | 验证者签名信息和正常运行时间 |
| StableSystem | 0x0000000000000000000000000000000000009999 | 系统交易的 EVM 事件 emission(解除绑定完成) |
所有这些都可以从任何 EVM 合约或链下客户端调用。这些地址是稳定的,在主网和测试网上都相同。
何时调用预编译合约与常规合约
- 在以下情况下使用预编译合约:当操作映射到 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: 预编译合约地址在 Stable 上固定且永不改变。从 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.tsDelegation shares: 1000000000000000000000
Delegation balance: 1000.0 STABLE订阅系统交易事件
一些 Stable SDK 操作(例如解除绑定完成)不会自然地发出 EVM 事件。Stable 通过系统交易弥补了这一空白:验证者生成的交易调用 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.tsListening for UnbondingCompleted events...
Unbonding completed for: 0xabcd...
Amount: 100.0 STABLE
Tx: 0x12ab...有关完整的系统交易机制以及按用户筛选/历史查询模式,请参阅跟踪解除绑定完成情况。
各模块参考
每个预编译合约的完整方法列表、事件和授权规则都可以在其参考页面中找到。
下一步建议
- 跟踪解除绑定完成情况:订阅通过 StableSystem 预编译合约发出的
UnbondingCompleted事件。 - 系统模块参考:跳转到各模块的 ABI、方法签名和事件模式。
- 系统模块概念:了解 Stable 为什么通过预编译合约公开 SDK 模块。

