stake-pool: Add last epoch values for APR calculation (#2491)

* stake-pool: Add last epoch values for APR calculation

* Bump versions for release
This commit is contained in:
Jon Cinque 2021-10-07 00:01:15 -04:00 committed by GitHub
parent 06563bf879
commit 13375afff2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 114 additions and 83 deletions

4
Cargo.lock generated
View File

@ -3779,7 +3779,7 @@ dependencies = [
[[package]] [[package]]
name = "spl-stake-pool" name = "spl-stake-pool"
version = "0.6.0" version = "0.6.1"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"bincode", "bincode",
@ -3801,7 +3801,7 @@ dependencies = [
[[package]] [[package]]
name = "spl-stake-pool-cli" name = "spl-stake-pool-cli"
version = "0.6.0" version = "0.6.1"
dependencies = [ dependencies = [
"bincode", "bincode",
"borsh", "borsh",

View File

@ -6,7 +6,7 @@ homepage = "https://spl.solana.com/stake-pool"
license = "Apache-2.0" license = "Apache-2.0"
name = "spl-stake-pool-cli" name = "spl-stake-pool-cli"
repository = "https://github.com/solana-labs/solana-program-library" repository = "https://github.com/solana-labs/solana-program-library"
version = "0.6.0" version = "0.6.1"
[dependencies] [dependencies]
borsh = "0.9" borsh = "0.9"

View File

@ -967,7 +967,7 @@ fn command_list(config: &Config, stake_pool_address: &Pubkey) -> CommandResult {
} }
println!( println!(
"Total Pool Stake: {}{}", "Total Pool Stake: {}{}",
Sol(stake_pool.total_stake_lamports), Sol(stake_pool.total_lamports),
if stake_pool.last_update_epoch != epoch_info.epoch { if stake_pool.last_update_epoch != epoch_info.epoch {
" [UPDATE REQUIRED]" " [UPDATE REQUIRED]"
} else { } else {
@ -1542,7 +1542,7 @@ fn command_list_all_pools(config: &Config) -> CommandResult {
"Address: {}\tManager: {}\tLamports: {}\tPool tokens: {}\tValidators: {}", "Address: {}\tManager: {}\tLamports: {}\tPool tokens: {}\tValidators: {}",
address, address,
stake_pool.manager, stake_pool.manager,
stake_pool.total_stake_lamports, stake_pool.total_lamports,
stake_pool.pool_token_supply, stake_pool.pool_token_supply,
validator_list.validators.len() validator_list.validators.len()
); );

View File

@ -1,6 +1,6 @@
[package] [package]
name = "spl-stake-pool" name = "spl-stake-pool"
version = "0.6.0" version = "0.6.1"
description = "Solana Program Library Stake Pool" description = "Solana Program Library Stake Pool"
authors = ["Solana Maintainers <maintainers@solana.foundation>"] authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana-program-library" repository = "https://github.com/solana-labs/solana-program-library"

View File

@ -607,8 +607,7 @@ impl Processor {
let stake_state = try_from_slice_unchecked::<stake_program::StakeState>( let stake_state = try_from_slice_unchecked::<stake_program::StakeState>(
&reserve_stake_info.data.borrow(), &reserve_stake_info.data.borrow(),
)?; )?;
let total_stake_lamports = if let stake_program::StakeState::Initialized(meta) = stake_state let total_lamports = if let stake_program::StakeState::Initialized(meta) = stake_state {
{
if meta.lockup != stake_program::Lockup::default() { if meta.lockup != stake_program::Lockup::default() {
msg!("Reserve stake account has some lockup"); msg!("Reserve stake account has some lockup");
return Err(StakePoolError::WrongStakeState.into()); return Err(StakePoolError::WrongStakeState.into());
@ -653,7 +652,7 @@ impl Processor {
stake_pool.manager_fee_account = *manager_fee_info.key; stake_pool.manager_fee_account = *manager_fee_info.key;
stake_pool.token_program_id = *token_program_info.key; stake_pool.token_program_id = *token_program_info.key;
stake_pool.last_update_epoch = Clock::get()?.epoch; stake_pool.last_update_epoch = Clock::get()?.epoch;
stake_pool.total_stake_lamports = total_stake_lamports; stake_pool.total_lamports = total_lamports;
stake_pool.epoch_fee = epoch_fee; stake_pool.epoch_fee = epoch_fee;
stake_pool.next_epoch_fee = None; stake_pool.next_epoch_fee = None;
stake_pool.preferred_deposit_validator_vote_address = None; stake_pool.preferred_deposit_validator_vote_address = None;
@ -1614,12 +1613,13 @@ impl Processor {
return Err(StakePoolError::InvalidState.into()); return Err(StakePoolError::InvalidState.into());
} }
let previous_lamports = stake_pool.total_stake_lamports; let previous_lamports = stake_pool.total_lamports;
let previous_pool_token_supply = stake_pool.pool_token_supply;
let reserve_stake = try_from_slice_unchecked::<stake_program::StakeState>( let reserve_stake = try_from_slice_unchecked::<stake_program::StakeState>(
&reserve_stake_info.data.borrow(), &reserve_stake_info.data.borrow(),
)?; )?;
let mut total_stake_lamports = let mut total_lamports = if let stake_program::StakeState::Initialized(meta) = reserve_stake
if let stake_program::StakeState::Initialized(meta) = reserve_stake { {
reserve_stake_info reserve_stake_info
.lamports() .lamports()
.checked_sub(minimum_reserve_lamports(&meta)) .checked_sub(minimum_reserve_lamports(&meta))
@ -1632,12 +1632,12 @@ impl Processor {
if validator_stake_record.last_update_epoch < clock.epoch { if validator_stake_record.last_update_epoch < clock.epoch {
return Err(StakePoolError::StakeListOutOfDate.into()); return Err(StakePoolError::StakeListOutOfDate.into());
} }
total_stake_lamports = total_stake_lamports total_lamports = total_lamports
.checked_add(validator_stake_record.stake_lamports()) .checked_add(validator_stake_record.stake_lamports())
.ok_or(StakePoolError::CalculationFailure)?; .ok_or(StakePoolError::CalculationFailure)?;
} }
let reward_lamports = total_stake_lamports.saturating_sub(previous_lamports); let reward_lamports = total_lamports.saturating_sub(previous_lamports);
// If the manager fee info is invalid, they don't deserve to receive the fee. // If the manager fee info is invalid, they don't deserve to receive the fee.
let fee = if stake_pool.check_manager_fee_info(manager_fee_info).is_ok() { let fee = if stake_pool.check_manager_fee_info(manager_fee_info).is_ok() {
@ -1659,11 +1659,6 @@ impl Processor {
stake_pool.stake_withdraw_bump_seed, stake_pool.stake_withdraw_bump_seed,
fee, fee,
)?; )?;
stake_pool.pool_token_supply = stake_pool
.pool_token_supply
.checked_add(fee)
.ok_or(StakePoolError::CalculationFailure)?;
} }
if stake_pool.last_update_epoch < clock.epoch { if stake_pool.last_update_epoch < clock.epoch {
@ -1680,8 +1675,10 @@ impl Processor {
stake_pool.next_sol_withdrawal_fee = None; stake_pool.next_sol_withdrawal_fee = None;
} }
stake_pool.last_update_epoch = clock.epoch; stake_pool.last_update_epoch = clock.epoch;
stake_pool.last_epoch_total_lamports = previous_lamports;
stake_pool.last_epoch_pool_token_supply = previous_pool_token_supply;
} }
stake_pool.total_stake_lamports = total_stake_lamports; stake_pool.total_lamports = total_lamports;
let pool_mint = Mint::unpack_from_slice(&pool_mint_info.data.borrow())?; let pool_mint = Mint::unpack_from_slice(&pool_mint_info.data.borrow())?;
stake_pool.pool_token_supply = pool_mint.supply; stake_pool.pool_token_supply = pool_mint.supply;
@ -1974,8 +1971,8 @@ impl Processor {
.ok_or(StakePoolError::CalculationFailure)?; .ok_or(StakePoolError::CalculationFailure)?;
// We treat the extra lamports as though they were // We treat the extra lamports as though they were
// transferred directly to the reserve stake account. // transferred directly to the reserve stake account.
stake_pool.total_stake_lamports = stake_pool stake_pool.total_lamports = stake_pool
.total_stake_lamports .total_lamports
.checked_add(all_deposit_lamports) .checked_add(all_deposit_lamports)
.ok_or(StakePoolError::CalculationFailure)?; .ok_or(StakePoolError::CalculationFailure)?;
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?; stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
@ -2121,8 +2118,8 @@ impl Processor {
.pool_token_supply .pool_token_supply
.checked_add(new_pool_tokens) .checked_add(new_pool_tokens)
.ok_or(StakePoolError::CalculationFailure)?; .ok_or(StakePoolError::CalculationFailure)?;
stake_pool.total_stake_lamports = stake_pool stake_pool.total_lamports = stake_pool
.total_stake_lamports .total_lamports
.checked_add(deposit_lamports) .checked_add(deposit_lamports)
.ok_or(StakePoolError::CalculationFailure)?; .ok_or(StakePoolError::CalculationFailure)?;
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?; stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
@ -2345,8 +2342,8 @@ impl Processor {
.pool_token_supply .pool_token_supply
.checked_sub(pool_tokens_burnt) .checked_sub(pool_tokens_burnt)
.ok_or(StakePoolError::CalculationFailure)?; .ok_or(StakePoolError::CalculationFailure)?;
stake_pool.total_stake_lamports = stake_pool stake_pool.total_lamports = stake_pool
.total_stake_lamports .total_lamports
.checked_sub(withdraw_lamports) .checked_sub(withdraw_lamports)
.ok_or(StakePoolError::CalculationFailure)?; .ok_or(StakePoolError::CalculationFailure)?;
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?; stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;
@ -2500,8 +2497,8 @@ impl Processor {
.pool_token_supply .pool_token_supply
.checked_sub(pool_tokens_burnt) .checked_sub(pool_tokens_burnt)
.ok_or(StakePoolError::CalculationFailure)?; .ok_or(StakePoolError::CalculationFailure)?;
stake_pool.total_stake_lamports = stake_pool stake_pool.total_lamports = stake_pool
.total_stake_lamports .total_lamports
.checked_sub(withdraw_lamports) .checked_sub(withdraw_lamports)
.ok_or(StakePoolError::CalculationFailure)?; .ok_or(StakePoolError::CalculationFailure)?;
stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?; stake_pool.serialize(&mut *stake_pool_info.data.borrow_mut())?;

View File

@ -86,12 +86,12 @@ pub struct StakePool {
/// Total stake under management. /// Total stake under management.
/// Note that if `last_update_epoch` does not match the current epoch then /// Note that if `last_update_epoch` does not match the current epoch then
/// this field may not be accurate /// this field may not be accurate
pub total_stake_lamports: u64, pub total_lamports: u64,
/// Total supply of pool tokens (should always match the supply in the Pool Mint) /// Total supply of pool tokens (should always match the supply in the Pool Mint)
pub pool_token_supply: u64, pub pool_token_supply: u64,
/// Last epoch the `total_stake_lamports` field was updated /// Last epoch the `total_lamports` field was updated
pub last_update_epoch: u64, pub last_update_epoch: u64,
/// Lockup that all stakes in the pool must have /// Lockup that all stakes in the pool must have
@ -146,18 +146,24 @@ pub struct StakePool {
/// Future SOL withdrawal fee, to be set for the following epoch /// Future SOL withdrawal fee, to be set for the following epoch
pub next_sol_withdrawal_fee: Option<Fee>, pub next_sol_withdrawal_fee: Option<Fee>,
/// Last epoch's total pool tokens, used only for APR estimation
pub last_epoch_pool_token_supply: u64,
/// Last epoch's total lamports, used only for APR estimation
pub last_epoch_total_lamports: u64,
} }
impl StakePool { impl StakePool {
/// calculate the pool tokens that should be minted for a deposit of `stake_lamports` /// calculate the pool tokens that should be minted for a deposit of `stake_lamports`
#[inline] #[inline]
pub fn calc_pool_tokens_for_deposit(&self, stake_lamports: u64) -> Option<u64> { pub fn calc_pool_tokens_for_deposit(&self, stake_lamports: u64) -> Option<u64> {
if self.total_stake_lamports == 0 || self.pool_token_supply == 0 { if self.total_lamports == 0 || self.pool_token_supply == 0 {
return Some(stake_lamports); return Some(stake_lamports);
} }
u64::try_from( u64::try_from(
(stake_lamports as u128) (stake_lamports as u128)
.checked_mul(self.pool_token_supply as u128)? .checked_mul(self.pool_token_supply as u128)?
.checked_div(self.total_stake_lamports as u128)?, .checked_div(self.total_lamports as u128)?,
) )
.ok() .ok()
} }
@ -168,7 +174,7 @@ impl StakePool {
// `checked_ceil_div` returns `None` for a 0 quotient result, but in this // `checked_ceil_div` returns `None` for a 0 quotient result, but in this
// case, a return of 0 is valid for small amounts of pool tokens. So // case, a return of 0 is valid for small amounts of pool tokens. So
// we check for that separately // we check for that separately
let numerator = (pool_tokens as u128).checked_mul(self.total_stake_lamports as u128)?; let numerator = (pool_tokens as u128).checked_mul(self.total_lamports as u128)?;
let denominator = self.pool_token_supply as u128; let denominator = self.pool_token_supply as u128;
if numerator < denominator || denominator == 0 { if numerator < denominator || denominator == 0 {
Some(0) Some(0)
@ -227,22 +233,21 @@ impl StakePool {
/// Calculate the fee in pool tokens that goes to the manager /// Calculate the fee in pool tokens that goes to the manager
/// ///
/// This function assumes that `reward_lamports` has not already been added /// This function assumes that `reward_lamports` has not already been added
/// to the stake pool's `total_stake_lamports` /// to the stake pool's `total_lamports`
#[inline] #[inline]
pub fn calc_epoch_fee_amount(&self, reward_lamports: u64) -> Option<u64> { pub fn calc_epoch_fee_amount(&self, reward_lamports: u64) -> Option<u64> {
if reward_lamports == 0 { if reward_lamports == 0 {
return Some(0); return Some(0);
} }
let total_stake_lamports = let total_lamports = (self.total_lamports as u128).checked_add(reward_lamports as u128)?;
(self.total_stake_lamports as u128).checked_add(reward_lamports as u128)?;
let fee_lamports = self.epoch_fee.apply(reward_lamports)?; let fee_lamports = self.epoch_fee.apply(reward_lamports)?;
if total_stake_lamports == fee_lamports || self.pool_token_supply == 0 { if total_lamports == fee_lamports || self.pool_token_supply == 0 {
Some(reward_lamports) Some(reward_lamports)
} else { } else {
u64::try_from( u64::try_from(
(self.pool_token_supply as u128) (self.pool_token_supply as u128)
.checked_mul(fee_lamports)? .checked_mul(fee_lamports)?
.checked_div(total_stake_lamports.checked_sub(fee_lamports)?)?, .checked_div(total_lamports.checked_sub(fee_lamports)?)?,
) )
.ok() .ok()
} }
@ -816,7 +821,10 @@ mod test {
solana_program::borsh::{ solana_program::borsh::{
get_instance_packed_len, get_packed_len, try_from_slice_unchecked, get_instance_packed_len, get_packed_len, try_from_slice_unchecked,
}, },
solana_program::native_token::LAMPORTS_PER_SOL, solana_program::{
clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_S_PER_SLOT, SECONDS_PER_DAY},
native_token::LAMPORTS_PER_SOL,
},
}; };
fn uninitialized_validator_list() -> ValidatorList { fn uninitialized_validator_list() -> ValidatorList {
@ -992,11 +1000,11 @@ mod test {
} }
prop_compose! { prop_compose! {
fn total_stake_and_rewards()(total_stake_lamports in 1..u64::MAX)( fn total_stake_and_rewards()(total_lamports in 1..u64::MAX)(
total_stake_lamports in Just(total_stake_lamports), total_lamports in Just(total_lamports),
rewards in 0..=total_stake_lamports, rewards in 0..=total_lamports,
) -> (u64, u64) { ) -> (u64, u64) {
(total_stake_lamports - rewards, rewards) (total_lamports - rewards, rewards)
} }
} }
@ -1008,7 +1016,7 @@ mod test {
denominator: 10, denominator: 10,
}; };
let mut stake_pool = StakePool { let mut stake_pool = StakePool {
total_stake_lamports: 100 * LAMPORTS_PER_SOL, total_lamports: 100 * LAMPORTS_PER_SOL,
pool_token_supply: 100 * LAMPORTS_PER_SOL, pool_token_supply: 100 * LAMPORTS_PER_SOL,
epoch_fee, epoch_fee,
..StakePool::default() ..StakePool::default()
@ -1016,7 +1024,7 @@ mod test {
let reward_lamports = 10 * LAMPORTS_PER_SOL; let reward_lamports = 10 * LAMPORTS_PER_SOL;
let pool_token_fee = stake_pool.calc_epoch_fee_amount(reward_lamports).unwrap(); let pool_token_fee = stake_pool.calc_epoch_fee_amount(reward_lamports).unwrap();
stake_pool.total_stake_lamports += reward_lamports; stake_pool.total_lamports += reward_lamports;
stake_pool.pool_token_supply += pool_token_fee; stake_pool.pool_token_supply += pool_token_fee;
let fee_lamports = stake_pool let fee_lamports = stake_pool
@ -1042,7 +1050,7 @@ mod test {
#[test] #[test]
fn divide_by_zero_fee() { fn divide_by_zero_fee() {
let stake_pool = StakePool { let stake_pool = StakePool {
total_stake_lamports: 0, total_lamports: 0,
epoch_fee: Fee { epoch_fee: Fee {
numerator: 1, numerator: 1,
denominator: 10, denominator: 10,
@ -1054,22 +1062,44 @@ mod test {
assert_eq!(fee, rewards); assert_eq!(fee, rewards);
} }
#[test]
fn approximate_apr_calculation() {
// 8% / year means roughly .044% / epoch
let stake_pool = StakePool {
last_epoch_total_lamports: 100_000,
last_epoch_pool_token_supply: 100_000,
total_lamports: 100_044,
pool_token_supply: 100_000,
..StakePool::default()
};
let pool_token_value =
stake_pool.total_lamports as f64 / stake_pool.pool_token_supply as f64;
let last_epoch_pool_token_value = stake_pool.last_epoch_total_lamports as f64
/ stake_pool.last_epoch_pool_token_supply as f64;
let epoch_rate = pool_token_value / last_epoch_pool_token_value - 1.0;
const SECONDS_PER_EPOCH: f64 = DEFAULT_SLOTS_PER_EPOCH as f64 * DEFAULT_S_PER_SLOT;
const EPOCHS_PER_YEAR: f64 = SECONDS_PER_DAY as f64 * 365.25 / SECONDS_PER_EPOCH;
const EPSILON: f64 = 0.00001;
let yearly_rate = epoch_rate * EPOCHS_PER_YEAR;
assert!((yearly_rate - 0.080355).abs() < EPSILON);
}
proptest! { proptest! {
#[test] #[test]
fn fee_calculation( fn fee_calculation(
(numerator, denominator) in fee(), (numerator, denominator) in fee(),
(total_stake_lamports, reward_lamports) in total_stake_and_rewards(), (total_lamports, reward_lamports) in total_stake_and_rewards(),
) { ) {
let epoch_fee = Fee { denominator, numerator }; let epoch_fee = Fee { denominator, numerator };
let mut stake_pool = StakePool { let mut stake_pool = StakePool {
total_stake_lamports, total_lamports,
pool_token_supply: total_stake_lamports, pool_token_supply: total_lamports,
epoch_fee, epoch_fee,
..StakePool::default() ..StakePool::default()
}; };
let pool_token_fee = stake_pool.calc_epoch_fee_amount(reward_lamports).unwrap(); let pool_token_fee = stake_pool.calc_epoch_fee_amount(reward_lamports).unwrap();
stake_pool.total_stake_lamports += reward_lamports; stake_pool.total_lamports += reward_lamports;
stake_pool.pool_token_supply += pool_token_fee; stake_pool.pool_token_supply += pool_token_fee;
let fee_lamports = stake_pool.calc_lamports_withdraw_amount(pool_token_fee).unwrap(); let fee_lamports = stake_pool.calc_lamports_withdraw_amount(pool_token_fee).unwrap();
@ -1082,7 +1112,7 @@ mod test {
// since we do two "flooring" conversions, the max epsilon should be // since we do two "flooring" conversions, the max epsilon should be
// correct up to 2 lamports (one for each floor division), plus a // correct up to 2 lamports (one for each floor division), plus a
// correction for huge discrepancies between rewards and total stake // correction for huge discrepancies between rewards and total stake
let epsilon = 2 + reward_lamports / total_stake_lamports; let epsilon = 2 + reward_lamports / total_lamports;
assert!(max_fee_lamports - fee_lamports <= epsilon, assert!(max_fee_lamports - fee_lamports <= epsilon,
"Max expected fee in lamports {}, actually receive {}, epsilon {}", "Max expected fee in lamports {}, actually receive {}, epsilon {}",
max_fee_lamports, fee_lamports, epsilon); max_fee_lamports, fee_lamports, epsilon);
@ -1102,16 +1132,16 @@ mod test {
proptest! { proptest! {
#[test] #[test]
fn deposit_and_withdraw( fn deposit_and_withdraw(
(total_stake_lamports, pool_token_supply, deposit_stake) in total_tokens_and_deposit() (total_lamports, pool_token_supply, deposit_stake) in total_tokens_and_deposit()
) { ) {
let mut stake_pool = StakePool { let mut stake_pool = StakePool {
total_stake_lamports, total_lamports,
pool_token_supply, pool_token_supply,
..StakePool::default() ..StakePool::default()
}; };
let deposit_result = stake_pool.calc_pool_tokens_for_deposit(deposit_stake).unwrap(); let deposit_result = stake_pool.calc_pool_tokens_for_deposit(deposit_stake).unwrap();
prop_assume!(deposit_result > 0); prop_assume!(deposit_result > 0);
stake_pool.total_stake_lamports += deposit_stake; stake_pool.total_lamports += deposit_stake;
stake_pool.pool_token_supply += deposit_result; stake_pool.pool_token_supply += deposit_result;
let withdraw_result = stake_pool.calc_lamports_withdraw_amount(deposit_result).unwrap(); let withdraw_result = stake_pool.calc_lamports_withdraw_amount(deposit_result).unwrap();
assert!(withdraw_result <= deposit_stake); assert!(withdraw_result <= deposit_stake);

View File

@ -203,8 +203,8 @@ async fn success() {
let post_stake_pool = let post_stake_pool =
try_from_slice_unchecked::<state::StakePool>(post_stake_pool.data.as_slice()).unwrap(); try_from_slice_unchecked::<state::StakePool>(post_stake_pool.data.as_slice()).unwrap();
assert_eq!( assert_eq!(
post_stake_pool.total_stake_lamports, post_stake_pool.total_lamports,
pre_stake_pool.total_stake_lamports + stake_lamports pre_stake_pool.total_lamports + stake_lamports
); );
assert_eq!( assert_eq!(
post_stake_pool.pool_token_supply, post_stake_pool.pool_token_supply,
@ -374,8 +374,8 @@ async fn success_with_extra_stake_lamports() {
let post_stake_pool = let post_stake_pool =
try_from_slice_unchecked::<state::StakePool>(post_stake_pool.data.as_slice()).unwrap(); try_from_slice_unchecked::<state::StakePool>(post_stake_pool.data.as_slice()).unwrap();
assert_eq!( assert_eq!(
post_stake_pool.total_stake_lamports, post_stake_pool.total_lamports,
pre_stake_pool.total_stake_lamports + extra_lamports + stake_lamports pre_stake_pool.total_lamports + extra_lamports + stake_lamports
); );
assert_eq!( assert_eq!(
post_stake_pool.pool_token_supply, post_stake_pool.pool_token_supply,

View File

@ -103,8 +103,8 @@ async fn success() {
let post_stake_pool = let post_stake_pool =
try_from_slice_unchecked::<state::StakePool>(post_stake_pool.data.as_slice()).unwrap(); try_from_slice_unchecked::<state::StakePool>(post_stake_pool.data.as_slice()).unwrap();
assert_eq!( assert_eq!(
post_stake_pool.total_stake_lamports, post_stake_pool.total_lamports,
pre_stake_pool.total_stake_lamports + TEST_STAKE_AMOUNT pre_stake_pool.total_lamports + TEST_STAKE_AMOUNT
); );
assert_eq!( assert_eq!(
post_stake_pool.pool_token_supply, post_stake_pool.pool_token_supply,

View File

@ -69,7 +69,7 @@ async fn setup(
pool_mint: stake_pool_accounts.pool_mint.pubkey(), pool_mint: stake_pool_accounts.pool_mint.pubkey(),
manager_fee_account: stake_pool_accounts.pool_fee_account.pubkey(), manager_fee_account: stake_pool_accounts.pool_fee_account.pubkey(),
token_program_id: spl_token::id(), token_program_id: spl_token::id(),
total_stake_lamports: 0, total_lamports: 0,
pool_token_supply: 0, pool_token_supply: 0,
last_update_epoch: 0, last_update_epoch: 0,
lockup: stake_program::Lockup::default(), lockup: stake_program::Lockup::default(),
@ -87,6 +87,8 @@ async fn setup(
sol_withdraw_authority: None, sol_withdraw_authority: None,
sol_withdrawal_fee: Fee::default(), sol_withdrawal_fee: Fee::default(),
next_sol_withdrawal_fee: None, next_sol_withdrawal_fee: None,
last_epoch_pool_token_supply: 0,
last_epoch_total_lamports: 0,
}; };
let mut validator_list = ValidatorList::new(max_validators); let mut validator_list = ValidatorList::new(max_validators);
@ -168,7 +170,7 @@ async fn setup(
transient_seed_suffix_end: 0, transient_seed_suffix_end: 0,
}); });
stake_pool.total_stake_lamports += active_stake_lamports; stake_pool.total_lamports += active_stake_lamports;
stake_pool.pool_token_supply += active_stake_lamports; stake_pool.pool_token_supply += active_stake_lamports;
} }

View File

@ -83,7 +83,7 @@ async fn success() {
) )
.await; .await;
let stake_pool = try_from_slice_unchecked::<StakePool>(stake_pool.data.as_slice()).unwrap(); let stake_pool = try_from_slice_unchecked::<StakePool>(stake_pool.data.as_slice()).unwrap();
assert_eq!(pre_balance, stake_pool.total_stake_lamports); assert_eq!(pre_balance, stake_pool.total_lamports);
let pre_token_supply = get_token_supply( let pre_token_supply = get_token_supply(
&mut context.banks_client, &mut context.banks_client,
@ -138,7 +138,7 @@ async fn success() {
) )
.await; .await;
let stake_pool = try_from_slice_unchecked::<StakePool>(stake_pool.data.as_slice()).unwrap(); let stake_pool = try_from_slice_unchecked::<StakePool>(stake_pool.data.as_slice()).unwrap();
assert_eq!(post_balance, stake_pool.total_stake_lamports); assert_eq!(post_balance, stake_pool.total_lamports);
let post_fee = get_token_balance( let post_fee = get_token_balance(
&mut context.banks_client, &mut context.banks_client,
@ -162,6 +162,8 @@ async fn success() {
assert_eq!(expected_fee, actual_fee); assert_eq!(expected_fee, actual_fee);
assert_eq!(pool_token_supply, stake_pool.pool_token_supply); assert_eq!(pool_token_supply, stake_pool.pool_token_supply);
assert_eq!(pre_token_supply, stake_pool.last_epoch_pool_token_supply);
assert_eq!(pre_balance, stake_pool.last_epoch_total_lamports);
} }
#[tokio::test] #[tokio::test]
@ -180,7 +182,7 @@ async fn success_ignoring_extra_lamports() {
) )
.await; .await;
let stake_pool = try_from_slice_unchecked::<StakePool>(stake_pool.data.as_slice()).unwrap(); let stake_pool = try_from_slice_unchecked::<StakePool>(stake_pool.data.as_slice()).unwrap();
assert_eq!(pre_balance, stake_pool.total_stake_lamports); assert_eq!(pre_balance, stake_pool.total_lamports);
let pre_token_supply = get_token_supply( let pre_token_supply = get_token_supply(
&mut context.banks_client, &mut context.banks_client,

View File

@ -214,7 +214,7 @@ async fn success() {
) )
.await; .await;
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap(); let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
assert_eq!(new_lamports, stake_pool.total_stake_lamports); assert_eq!(new_lamports, stake_pool.total_lamports);
} }
#[tokio::test] #[tokio::test]
@ -282,7 +282,7 @@ async fn merge_into_reserve() {
) )
.await; .await;
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap(); let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
assert_eq!(expected_lamports, stake_pool.total_stake_lamports); assert_eq!(expected_lamports, stake_pool.total_lamports);
println!("Warp one more epoch so the stakes deactivate"); println!("Warp one more epoch so the stakes deactivate");
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
@ -325,7 +325,7 @@ async fn merge_into_reserve() {
) )
.await; .await;
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap(); let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
assert_eq!(expected_lamports, stake_pool.total_stake_lamports); assert_eq!(expected_lamports, stake_pool.total_lamports);
} }
#[tokio::test] #[tokio::test]
@ -389,7 +389,7 @@ async fn merge_into_validator_stake() {
) )
.await; .await;
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap(); let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
assert_eq!(expected_lamports, stake_pool.total_stake_lamports); assert_eq!(expected_lamports, stake_pool.total_lamports);
// Warp one more epoch so the stakes activate, ready to merge // Warp one more epoch so the stakes activate, ready to merge
let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch; let slots_per_epoch = context.genesis_config().epoch_schedule.slots_per_epoch;
@ -422,7 +422,7 @@ async fn merge_into_validator_stake() {
) )
.await; .await;
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap(); let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
assert_eq!(current_lamports, stake_pool.total_stake_lamports); assert_eq!(current_lamports, stake_pool.total_lamports);
// Check that transient accounts are gone // Check that transient accounts are gone
for stake_account in &stake_accounts { for stake_account in &stake_accounts {
@ -803,7 +803,7 @@ async fn success_ignoring_hijacked_transient_stake() {
) )
.await; .await;
let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap(); let stake_pool = try_from_slice_unchecked::<StakePool>(&stake_pool_info.data).unwrap();
assert_eq!(pre_lamports, stake_pool.total_stake_lamports); assert_eq!(pre_lamports, stake_pool.total_lamports);
} }
#[tokio::test] #[tokio::test]

View File

@ -245,8 +245,8 @@ async fn _success(test_type: SuccessTestType) {
}; };
let tokens_burnt = tokens_to_withdraw - tokens_withdrawal_fee; let tokens_burnt = tokens_to_withdraw - tokens_withdrawal_fee;
assert_eq!( assert_eq!(
stake_pool.total_stake_lamports, stake_pool.total_lamports,
stake_pool_before.total_stake_lamports - tokens_burnt stake_pool_before.total_lamports - tokens_burnt
); );
assert_eq!( assert_eq!(
stake_pool.pool_token_supply, stake_pool.pool_token_supply,

View File

@ -119,8 +119,8 @@ async fn success() {
let amount_withdrawn_minus_fee = let amount_withdrawn_minus_fee =
pool_tokens - stake_pool_accounts.calculate_withdrawal_fee(pool_tokens); pool_tokens - stake_pool_accounts.calculate_withdrawal_fee(pool_tokens);
assert_eq!( assert_eq!(
post_stake_pool.total_stake_lamports, post_stake_pool.total_lamports,
pre_stake_pool.total_stake_lamports - amount_withdrawn_minus_fee pre_stake_pool.total_lamports - amount_withdrawn_minus_fee
); );
assert_eq!( assert_eq!(
post_stake_pool.pool_token_supply, post_stake_pool.pool_token_supply,