概述
Sui 区块链采用 DPoS(委托权益证明)作为其共识机制的核心。用户可将持有的 SUI 代币委托质押给验证者节点,在每个 epoch(纪元)周期结束时,系统会将交易手续费、存储费用及质押补贴等收入分配给验证者及其质押用户。每个验证者节点均维护独立的质押池,并基于 exchange rates(兑换率)算法计算每位用户的质押奖励。本文将深入解析 Sui 质押池的 Move 合约实现,帮助读者理解其核心逻辑与运作细节。
质押池的核心数据结构
在 Sui 的 POS 共识机制中,所有质押操作均按 epoch 周期进行结算。当前 epoch 内发起的质押或取回操作,需等到下一个 epoch 方能生效。
StakingPool 结构
每个验证者均拥有一个 StakingPool 结构,用于管理其质押池状态:
public struct StakingPool has key, store {
id: UID,
activation_epoch: Option<u64>, // 池子生效的 epoch
deactivation_epoch: Option<u64>, // 池子失效的 epoch
sui_balance: u64, // 池内 SUI 总量(含质押与奖励)
rewards_pool: Balance<SUI>, // 每 epoch 结束时收到的验证者奖励
pool_token_balance: u64, // 池子发行的代币总量
exchange_rates: Table<u64, PoolTokenExchangeRate>, // 各 epoch 的 SUI/TOKEN 兑换率
pending_stake: u64, // 下 epoch 将生效的质押 SUI 数量
pending_total_sui_withdraw: u64, // 下 epoch 将生效的取回 SUI 总量
pending_pool_token_withdraw: u64, // 下 epoch 将生效的取回代币数量
extra_fields: Bag,
}
PoolTokenExchangeRate 结构
PoolTokenExchangeRate 定义了 SUI 与池代币之间的数量兑换比率:
public struct PoolTokenExchangeRate has store, copy, drop {
sui_amount: u64,
pool_token_amount: u64,
}
StakedSui 结构
StakedSui 是代表用户质押的核心资产,其 store
能力由 SIP-6 引入,便于第三方流动性质押协议(如 vSUI、haSUI、afSUI)基于此结构管理质押资产:
public struct StakedSui has key, store {
id: UID,
pool_id: ID,
stake_activation_epoch: u64, // 质押生效的 epoch
principal: Balance<SUI>, // 质押的 SUI 数量
}
核心操作方法与流程
质押:request_add_stake
用户执行质押操作时,质押数量将暂存于 pending_stake
中,待下一 epoch 生效,并返回代表质押权益的 StakedSui 资产:
public(package) fun request_add_stake(
pool: &mut StakingPool,
stake: Balance<SUI>,
stake_activation_epoch: u64,
ctx: &mut TxContext
): StakedSui {
let sui_amount = stake.value();
assert!(!is_inactive(pool), EDelegationToInactivePool);
assert!(sui_amount > 0, EDelegationOfZeroSui);
let staked_sui = StakedSui {
id: object::new(ctx),
pool_id: object::id(pool),
stake_activation_epoch,
principal: stake,
};
pool.pending_stake = pool.pending_stake + sui_amount;
staked_sui
}
取回质押:request_withdraw_stake
用户可根据持有的 StakedSui 取回质押的 SUI,此操作涉及本金与收益的计算:
public(package) fun request_withdraw_stake(
pool: &mut StakingPool,
staked_sui: StakedSui,
ctx: &TxContext
): Balance<SUI> {
// 若质押尚未生效,直接取回本金
if (staked_sui.stake_activation_epoch > ctx.epoch()) {
let principal = unwrap_staked_sui(staked_sui);
pool.pending_stake = pool.pending_stake - principal.value();
return principal
};
// 根据质押时的兑换率计算应取回的代币数量
let (pool_token_withdraw_amount, mut principal_withdraw) =
withdraw_from_principal(pool, staked_sui);
let principal_withdraw_amount = principal_withdraw.value();
// 计算当前 epoch 可获得的 SUI 奖励
let rewards_withdraw = withdraw_rewards(
pool, principal_withdraw_amount, pool_token_withdraw_amount, ctx.epoch()
);
// 总取回数量 = 本金 + 奖励
let total_sui_withdraw_amount = principal_withdraw_amount + rewards_withdraw.value();
// 登记待处理取回
pool.pending_total_sui_withdraw = pool.pending_total_sui_withdraw + total_sui_withdraw_amount;
pool.pending_pool_token_withdraw = pool.pending_pool_token_withdraw + pool_token_withdraw_amount;
// 若池处于非活跃状态,立即处理取回
if (is_inactive(pool)) process_pending_stake_withdraw(pool);
principal_withdraw.join(rewards_withdraw);
principal_withdraw
}
收益计算过程中,系统会从 rewards_pool
中提取对应的 SUI 余额:
fun withdraw_rewards(
pool: &mut StakingPool,
principal_withdraw_amount: u64,
pool_token_withdraw_amount: u64,
epoch: u64,
): Balance<SUI> {
let exchange_rate = pool_token_exchange_rate_at_epoch(pool, epoch);
let total_sui_withdraw_amount = get_sui_amount(&exchange_rate, pool_token_withdraw_amount);
let mut reward_withdraw_amount =
if (total_sui_withdraw_amount >= principal_withdraw_amount)
total_sui_withdraw_amount - principal_withdraw_amount
else 0;
reward_withdraw_amount = reward_withdraw_amount.min(pool.rewards_pool.value());
pool.rewards_pool.split(reward_withdraw_amount)
}
纪元更新:process_pending_stakes_and_withdraws
每个新 epoch 开始时,系统会处理所有待处理的质押与取回操作,并更新兑换率记录:
public(package) fun process_pending_stakes_and_withdraws(pool: &mut StakingPool, ctx: &TxContext) {
let new_epoch = ctx.epoch() + 1;
process_pending_stake_withdraw(pool);
process_pending_stake(pool);
pool.exchange_rates.add(
new_epoch,
PoolTokenExchangeRate { sui_amount: pool.sui_balance, pool_token_amount: pool.pool_token_balance },
);
check_balance_invariants(pool, new_epoch);
}
待处理取回操作会实时更新池余额:
fun process_pending_stake_withdraw(pool: &mut StakingPool) {
pool.sui_balance = pool.sui_balance - pool.pending_total_sui_withdraw;
pool.pool_token_balance = pool.pool_token_balance - pool.pending_pool_token_withdraw;
pool.pending_total_sui_withdraw = 0;
pool.pending_pool_token_withdraw = 0;
}
待处理质押操作则基于最新兑换率计算新增的代币发行量:
public(package) fun process_pending_stake(pool: &mut StakingPool) {
let latest_exchange_rate =
PoolTokenExchangeRate { sui_amount: pool.sui_balance, pool_token_amount: pool.pool_token_balance };
pool.sui_balance = pool.sui_balance + pool.pending_stake;
pool.pool_token_balance = get_token_amount(&latest_exchange_rate, pool.sui_balance);
pool.pending_stake = 0;
}
常见问题
什么是 Sui 的质押池?
Sui 质押池是验证者节点用于管理用户质押 SUI 及分配奖励的智能合约结构。每个验证者拥有独立池子,按 epoch 周期结算收益,并通过兑换率算法确保奖励分配公平。
质押后何时开始产生收益?
质押操作在当前 epoch 发起后,需待下一 epoch 方正式生效并开始计算收益。同样,取回操作也需等到下一 epoch 才能完成处理。
如何计算质押奖励?
奖励基于兑换率机制计算。系统会记录每个 epoch 的 SUI 与池代币兑换比率,用户取回时根据其质押时的兑换率与当前兑换率差异核算应得奖励。
StakedSui 资产有何作用?
StakedSui 代表用户质押资产的所有权,其具备 store
能力,可被集成至各类 DeFi 协议中用于流动性质押,👉 探索更多质押策略,提升资金利用效率。
验证者池失效如何处理?
若验证者池被标记为失效(inactive),用户发起的取回操作将立即处理,无需等待下一 epoch,保障资产可随时退出。
总结
Sui 的质押池通过 Move 合约实现了高效且透明的委托权益管理机制。用户通过质押 SUI 参与网络安全维护并获得收益,验证者则借助灵活的兑换率算法确保奖励分配准确无误。本文深入剖析了质押池的核心数据结构与关键操作流程,为开发者与用户理解 Sui 质押经济模型提供了详细技术参考。