Skip to content

Commit

Permalink
Merge pull request #3279 from autonomys/refactor-staking
Browse files Browse the repository at this point in the history
Minor fix and refactoring to staking
  • Loading branch information
NingLin-P authored Dec 4, 2024
2 parents bdf31b8 + a64d921 commit d4f51d4
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 75 deletions.
50 changes: 13 additions & 37 deletions crates/pallet-domains/src/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,6 @@ pub struct Operator<Balance, Share, DomainBlockNumber> {
pub nomination_tax: Percent,
/// Total active stake of combined nominators under this operator.
pub current_total_stake: Balance,
/// Total rewards this operator received this current epoch.
pub current_epoch_rewards: Balance,
/// Total shares of all the nominators under this operator.
pub current_total_shares: Share,
/// The status of the operator, it may be stale due to the `OperatorStatus::PendingSlash` is
Expand Down Expand Up @@ -227,7 +225,6 @@ impl<Balance: Zero, Share: Zero, DomainBlockNumber> Operator<Balance, Share, Dom
minimum_nominator_stake,
nomination_tax: Default::default(),
current_total_stake: Zero::zero(),
current_epoch_rewards: Zero::zero(),
current_total_shares: Zero::zero(),
partial_status: OperatorStatus::Registered,
deposits_in_epoch: Zero::zero(),
Expand Down Expand Up @@ -373,7 +370,6 @@ pub fn do_register_operator<T: Config>(
minimum_nominator_stake,
nomination_tax,
current_total_stake: Zero::zero(),
current_epoch_rewards: Zero::zero(),
current_total_shares: Zero::zero(),
partial_status: OperatorStatus::Registered,
// sum total deposits added during this epoch.
Expand Down Expand Up @@ -1106,16 +1102,7 @@ pub(crate) fn do_unlock_nominator<T: Config>(
);

let mut total_shares = operator.current_total_shares;
// take any operator current epoch rewards to include in total stake and set to zero.
let operator_current_epoch_rewards = operator.current_epoch_rewards;
operator.current_epoch_rewards = Zero::zero();

// calculate total stake of operator.
let mut total_stake = operator
.current_total_stake
.checked_add(&operator_current_epoch_rewards)
.ok_or(Error::BalanceOverflow)?;

let mut total_stake = operator.current_total_stake;
let share_price = SharePrice::new::<T>(total_shares, total_stake);

let mut total_storage_fee_deposit = operator.total_storage_fee_deposit;
Expand Down Expand Up @@ -1298,6 +1285,9 @@ pub(crate) fn do_reward_operators<T: Config>(
operators: IntoIter<OperatorId>,
rewards: BalanceOf<T>,
) -> Result<(), Error> {
if rewards.is_zero() {
return Ok(());
}
DomainStakingSummary::<T>::mutate(domain_id, |maybe_stake_summary| {
let stake_summary = maybe_stake_summary
.as_mut()
Expand All @@ -1308,38 +1298,25 @@ pub(crate) fn do_reward_operators<T: Config>(
let operator_weights = operators.into_iter().fold(
BTreeMap::<OperatorId, u64>::new(),
|mut acc, operator_id| {
let total_weight = match acc.get(&operator_id) {
None => 1,
Some(weight) => weight + 1,
};
acc.insert(operator_id, total_weight);
acc.entry(operator_id)
.and_modify(|weight| *weight += 1)
.or_insert(1);
acc
},
);

let mut allocated_rewards = BalanceOf::<T>::zero();
let mut weight_balance_cache = BTreeMap::<u64, BalanceOf<T>>::new();
for (operator_id, weight) in operator_weights {
let operator_reward = match weight_balance_cache.get(&weight) {
None => {
let distribution = Perquintill::from_rational(weight, total_count);
let operator_reward = distribution.mul_floor(rewards);
weight_balance_cache.insert(weight, operator_reward);
operator_reward
}
Some(operator_reward) => *operator_reward,
};

let total_reward = match stake_summary.current_epoch_rewards.get(&operator_id) {
None => operator_reward,
Some(rewards) => rewards
.checked_add(&operator_reward)
.ok_or(Error::BalanceOverflow)?,
let operator_reward = {
let distribution = Perquintill::from_rational(weight, total_count);
distribution.mul_floor(rewards)
};

stake_summary
.current_epoch_rewards
.insert(operator_id, total_reward);
.entry(operator_id)
.and_modify(|rewards| *rewards = rewards.saturating_add(operator_reward))
.or_insert(operator_reward);

Pallet::<T>::deposit_event(Event::OperatorRewarded {
source: source.clone(),
Expand Down Expand Up @@ -1626,7 +1603,6 @@ pub(crate) mod tests {
minimum_nominator_stake: SSC,
nomination_tax: Default::default(),
current_total_stake: operator_stake,
current_epoch_rewards: 0,
current_total_shares: operator_stake,
partial_status: OperatorStatus::Registered,
deposits_in_epoch: 0,
Expand Down
71 changes: 33 additions & 38 deletions crates/pallet-domains/src/staking_epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,25 @@ pub(crate) fn operator_take_reward_tax_and_stake<T: Config>(
) -> Result<u32, Error> {
let mut rewarded_operator_count = 0;
DomainStakingSummary::<T>::try_mutate(domain_id, |maybe_domain_stake_summary| {
let mut to_treasury = BalanceOf::<T>::zero();
let stake_summary = maybe_domain_stake_summary
.as_mut()
.ok_or(TransitionError::DomainNotInitialized)?;

while let Some((operator_id, reward)) = stake_summary.current_epoch_rewards.pop_first() {
Operators::<T>::try_mutate(operator_id, |maybe_operator| {
let operator = match maybe_operator.as_mut() {
// it is possible that operator may have de registered by the time they got rewards
// if not available, skip the operator
None => return Ok(()),
// It is possible that operator may have de registered and unlocked by the time they
// got rewards, in this case, move the reward to the treasury
None => {
to_treasury += reward;
return Ok(())
}
// Move the reward of slashed and pening slash operator to the treasury
Some(operator) if matches!(*operator.status::<T>(operator_id), OperatorStatus::Slashed | OperatorStatus::PendingSlash) => {
to_treasury += reward;
return Ok(())
}
Some(operator) => operator,
};

Expand Down Expand Up @@ -125,13 +134,14 @@ pub(crate) fn operator_take_reward_tax_and_stake<T: Config>(
});
}

// add remaining rewards to nominators to be distributed during the epoch transition
// Add the remaining rewards to the operator's `current_total_stake` which increases the
// share price of the staking pool and as a way to distribute the reward to the nominator
let rewards = reward
.checked_sub(&operator_tax_amount)
.ok_or(TransitionError::BalanceUnderflow)?;

operator.current_epoch_rewards = operator
.current_epoch_rewards
operator.current_total_stake = operator
.current_total_stake
.checked_add(&rewards)
.ok_or(TransitionError::BalanceOverflow)?;

Expand All @@ -141,6 +151,10 @@ pub(crate) fn operator_take_reward_tax_and_stake<T: Config>(
})?;
}

if !to_treasury.is_zero() {
mint_into_treasury::<T>(to_treasury).ok_or(TransitionError::MintBalance)?;
}

Ok(())
})
.map_err(Error::OperatorRewardStaking)?;
Expand Down Expand Up @@ -228,51 +242,38 @@ pub(crate) fn do_finalize_operator_epoch_staking<T: Config>(

// if there are no deposits, withdrawls, and epoch rewards for this operator
// then short-circuit and return early.
if operator.deposits_in_epoch.is_zero()
&& operator.withdrawals_in_epoch.is_zero()
&& operator.current_epoch_rewards.is_zero()
{
if operator.deposits_in_epoch.is_zero() && operator.withdrawals_in_epoch.is_zero() {
return Ok((operator.current_total_stake, false));
}

let total_stake = operator
.current_total_stake
.checked_add(&operator.current_epoch_rewards)
.ok_or(TransitionError::BalanceOverflow)?;

let total_shares = operator.current_total_shares;

let mut total_stake = operator.current_total_stake;
let mut total_shares = operator.current_total_shares;
let share_price = SharePrice::new::<T>(total_shares, total_stake);

// calculate and subtract total withdrew shares from previous epoch
let (total_stake, total_shares) = if !operator.withdrawals_in_epoch.is_zero() {
if !operator.withdrawals_in_epoch.is_zero() {
let withdraw_stake = share_price.shares_to_stake::<T>(operator.withdrawals_in_epoch);
let total_stake = total_stake
total_stake = total_stake
.checked_sub(&withdraw_stake)
.ok_or(TransitionError::BalanceUnderflow)?;
let total_shares = total_shares
total_shares = total_shares
.checked_sub(&operator.withdrawals_in_epoch)
.ok_or(TransitionError::ShareUnderflow)?;

operator.withdrawals_in_epoch = Zero::zero();
(total_stake, total_shares)
} else {
(total_stake, total_shares)
};

// calculate and add total deposits from the previous epoch
let (total_stake, total_shares) = if !operator.deposits_in_epoch.is_zero() {
if !operator.deposits_in_epoch.is_zero() {
let deposited_shares = share_price.stake_to_shares::<T>(operator.deposits_in_epoch);
let total_stake = total_stake
total_stake = total_stake
.checked_add(&operator.deposits_in_epoch)
.ok_or(TransitionError::BalanceOverflow)?;
let total_shares = total_shares
total_shares = total_shares
.checked_add(&deposited_shares)
.ok_or(TransitionError::ShareOverflow)?;

operator.deposits_in_epoch = Zero::zero();
(total_stake, total_shares)
} else {
(total_stake, total_shares)
};

// update operator pool epoch share price
Expand All @@ -288,7 +289,6 @@ pub(crate) fn do_finalize_operator_epoch_staking<T: Config>(
// update operator state
operator.current_total_shares = total_shares;
operator.current_total_stake = total_stake;
operator.current_epoch_rewards = Zero::zero();
Operators::<T>::set(operator_id, Some(operator));

Ok((total_stake, true))
Expand Down Expand Up @@ -354,12 +354,7 @@ pub(crate) fn do_slash_operator<T: Config>(

let staked_hold_id = T::HoldIdentifier::staking_staked();

let mut total_stake = operator
.current_total_stake
.checked_add(&operator.current_epoch_rewards)
.ok_or(TransitionError::BalanceOverflow)?;

operator.current_epoch_rewards = Zero::zero();
let mut total_stake = operator.current_total_stake;
let mut total_shares = operator.current_total_shares;
let share_price = SharePrice::new::<T>(total_shares, total_stake);

Expand Down Expand Up @@ -757,7 +752,6 @@ mod tests {
operator.current_total_stake + operator.total_storage_fee_deposit,
total_updated_stake
);
assert_eq!(operator.current_epoch_rewards, Zero::zero());

let domain_stake_summary = DomainStakingSummary::<Test>::get(domain_id).unwrap();
assert_eq!(
Expand Down Expand Up @@ -817,6 +811,7 @@ mod tests {
// 10% tax
let nomination_tax = Percent::from_parts(10);
let mut operator = Operators::<Test>::get(operator_id).unwrap();
let pre_total_stake = operator.current_total_stake;
let pre_storage_fund_deposit = operator.total_storage_fee_deposit;
operator.nomination_tax = nomination_tax;
Operators::<Test>::insert(operator_id, operator);
Expand All @@ -835,7 +830,7 @@ mod tests {
let new_storage_fund_deposit =
operator.total_storage_fee_deposit - pre_storage_fund_deposit;
assert_eq!(
operator.current_epoch_rewards,
operator.current_total_stake - pre_total_stake,
(10 * SSC - expected_operator_tax)
);

Expand Down

0 comments on commit d4f51d4

Please sign in to comment.