前言Sui使用DPoS权益证明作为共识的核心。用户可以把手上的SUI委托质押给验证者节点,在每一个epoch周期内,交易的gas费、存储费用、质押补贴等收入都会分给验证节点和他们的质押用户。每个验证者节点都维护着自己的质押池子,并根据exchangerates算法来计算每个用户的质押奖励核心
Sui使用DPoS权益证明作为共识的核心。用户可以把手上的SUI委托质押给验证者节点,在每一个epoch周期内,交易的gas费、存储费用、质押补贴等收入都会分给验证节点和他们的质押用户。
每个验证者节点都维护着自己的质押池子,并根据exchange rates算法来计算每个用户的质押奖励
核心的逻辑都是通过MOVE在链上完成的,staking_pool.move,本文将对该合约进行详细的分析
每个validator都会维护一个StakingPool, 在SUI的POS共识中,都是按照纪元epoch周期来结算的, 当前质押SUI到validator后,要等到下一个epoch才会生效, 同样的withdraw取回质押后对应的兑换率生效也是在下一个epoch中
public struct StakingPool has key, store {
id: UID,
activation_epoch: Option<u64>, // 池子生效的epoch
deactivation_epoch: Option<u64>, // 池子失效的epoch
sui_balance: u64, // 池子里sui的总量,包括质押的sui和验证者奖励的sui
rewards_pool: Balance<SUI>, // 在每一个epoch结束收到的验证者奖励
pool_token_balance: u64, // 池子发行的token总量
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将要生效取出的token数量
extra_fields: Bag,
}
兑换率其实就是SUI/TOKEN之间数量的比值
public struct PoolTokenExchangeRate has store, copy, drop {
sui_amount: u64,
pool_token_amount: u64,
}
StakedSui是质押池的核心数据结构,这里的store能力是sip6新增的,方便第三方流动性质押协议(vSUI、haSUI、afSUI)通过StakedSui来管理质押的实际情况
public struct StakedSui has key, store {
id: UID,
pool_id: ID,
stake_activation_epoch: u64, //质押生效的epoch
principal: Balance<SUI>, //质押的SUI数量
}
request_add_stake 质押SUI操作,质押的数量添加到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> {
// 如果质押还未生效,就直接解构StakedSui,取回里面的SUI balance
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
};
// 根据StakedSui质押生效的兑换率得到需要取回的token数量
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()
);
// 总共需要取回的SUI数量,包括质押的SUI和质押奖励的SUI
let total_sui_withdraw_amount = principal_withdraw_amount + rewards_withdraw.value();
// 加入到pending中
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;
// 如果池子已经inactive状态了,则直接取回,不用等下一个epoch了
if (is_inactive(pool)) process_pending_stake_withdraw(pool);
principal_withdraw.join(rewards_withdraw);
principal_withdraw
}
public(package) fun withdraw_from_principal(
pool: &StakingPool,
staked_sui: StakedSui,
) : (u64, Balance<SUI>) {
// Check that the stake information matches the pool.
assert!(staked_sui.pool_id == object::id(pool), EWrongPool);
// 根据StakedSui质押时候的兑换率
let exchange_rate_at_staking_epoch = pool_token_exchange_rate_at_epoch(pool, staked_sui.stake_activation_epoch);
let principal_withdraw = unwrap_staked_sui(staked_sui);
let pool_token_withdraw_amount = get_token_amount(
&exchange_rate_at_staking_epoch,
principal_withdraw.value()
);
(
pool_token_withdraw_amount,
principal_withdraw,
)
}
计算质押的SUI收益,从rewards_pool中取出sui balance
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)
}
每当新的epoch来临的时候,都会处理一下pending,同时把最新的兑换率添加到exchange_rates中去
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质押池的Move合约,该合约实现了对单个validator进行质押SUI,然后从validator的rewards_pool中获取质押收益
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!