Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit d336b8b

Browse files
authored
stake-pool: Use checked_ceil_div for withdraw calc (#1482)
* stake-pool: Use checked_ceil_div for withdraw calc When a stake account is totally removed from a stake pool by the manager, there's a chance that the operation would not take enough of the manager's pool tokens by 1 due to truncation. Do a ceiling division instead, and refactor ceiling division into the math library. * Use new function name on CLI * Cargo fmt
1 parent 04fc247 commit d336b8b

File tree

10 files changed

+32
-26
lines changed

10 files changed

+32
-26
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

token-swap/program/src/curve/math.rs renamed to libraries/math/src/checked_ceil_div.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
//! Defines useful math utils
1+
//! Defines performing checked ceiling division for different types
22
3-
use spl_math::uint::U256;
3+
use crate::uint::U256;
44

55
/// Perform a division that does not truncate value from either side, returning
66
/// the (quotient, divisor) as a tuple

libraries/math/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![forbid(unsafe_code)]
55

66
pub mod approximations;
7+
pub mod checked_ceil_div;
78
mod entrypoint;
89
pub mod error;
910
pub mod instruction;

stake-pool/cli/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ fn pick_withdraw_accounts(
777777
continue;
778778
}
779779
let available_for_withdrawal = stake_pool
780-
.calc_lamports_amount(account.lamports - *MIN_STAKE_BALANCE)
780+
.calc_lamports_withdraw_amount(account.lamports - *MIN_STAKE_BALANCE)
781781
.unwrap();
782782
let withdraw_amount = u64::min(available_for_withdrawal, remaining_amount);
783783

@@ -878,7 +878,7 @@ fn command_withdraw(
878878
for withdraw_stake in withdraw_accounts {
879879
// Convert pool tokens amount to lamports
880880
let sol_withdraw_amount = pool_data
881-
.calc_lamports_amount(withdraw_stake.pool_amount)
881+
.calc_lamports_withdraw_amount(withdraw_stake.pool_amount)
882882
.unwrap();
883883

884884
println!(

stake-pool/program/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ num_enum = "0.5.1"
1919
serde = "1.0.121"
2020
serde_derive = "1.0.103"
2121
solana-program = "1.6.1"
22+
spl-math = { path = "../../libraries/math", features = [ "no-entrypoint" ] }
2223
spl-token = { path = "../../token/program", features = [ "no-entrypoint" ] }
2324
thiserror = "1.0"
2425
bincode = "1.3.1"

stake-pool/program/src/processor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,7 @@ impl Processor {
10901090
.ok_or(StakePoolError::ValidatorNotFound)?;
10911091

10921092
let stake_amount = stake_pool
1093-
.calc_lamports_amount(pool_amount)
1093+
.calc_lamports_withdraw_amount(pool_amount)
10941094
.ok_or(StakePoolError::CalculationFailure)?;
10951095

10961096
Self::stake_split(

stake-pool/program/src/state.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use {
66
account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError,
77
pubkey::Pubkey,
88
},
9+
spl_math::checked_ceil_div::CheckedCeilDiv,
910
std::convert::{TryFrom, TryInto},
1011
std::mem::size_of,
1112
};
@@ -50,19 +51,22 @@ impl StakePool {
5051
if self.stake_total == 0 {
5152
return Some(stake_lamports);
5253
}
53-
self.calc_pool_withdraw_amount(stake_lamports)
54-
}
55-
/// calculate the pool tokens that should be withdrawn
56-
pub fn calc_pool_withdraw_amount(&self, stake_lamports: u64) -> Option<u64> {
5754
u64::try_from(
5855
(stake_lamports as u128)
5956
.checked_mul(self.pool_total as u128)?
6057
.checked_div(self.stake_total as u128)?,
6158
)
6259
.ok()
6360
}
64-
/// calculate lamports amount
65-
pub fn calc_lamports_amount(&self, pool_tokens: u64) -> Option<u64> {
61+
/// calculate the pool tokens that should be withdrawn
62+
pub fn calc_pool_withdraw_amount(&self, stake_lamports: u64) -> Option<u64> {
63+
let (quotient, _) = (stake_lamports as u128)
64+
.checked_mul(self.pool_total as u128)?
65+
.checked_ceil_div(self.stake_total as u128)?;
66+
u64::try_from(quotient).ok()
67+
}
68+
/// calculate lamports amount on withdrawal
69+
pub fn calc_lamports_withdraw_amount(&self, pool_tokens: u64) -> Option<u64> {
6670
u64::try_from(
6771
(pool_tokens as u128)
6872
.checked_mul(self.stake_total as u128)?

token-swap/program/src/curve/constant_price.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
//! Simple constant price swap curve, set at init
22
3-
use crate::{
4-
curve::calculator::{
5-
map_zero_to_none, CurveCalculator, DynPack, RoundDirection, SwapWithoutFeesResult,
6-
TradeDirection, TradingTokenResult,
3+
use {
4+
crate::{
5+
curve::calculator::{
6+
map_zero_to_none, CurveCalculator, DynPack, RoundDirection, SwapWithoutFeesResult,
7+
TradeDirection, TradingTokenResult,
8+
},
9+
error::SwapError,
710
},
8-
curve::math::CheckedCeilDiv,
9-
error::SwapError,
10-
};
11-
use arrayref::{array_mut_ref, array_ref};
12-
use solana_program::{
13-
program_error::ProgramError,
14-
program_pack::{IsInitialized, Pack, Sealed},
11+
arrayref::{array_mut_ref, array_ref},
12+
solana_program::{
13+
program_error::ProgramError,
14+
program_pack::{IsInitialized, Pack, Sealed},
15+
},
16+
spl_math::{checked_ceil_div::CheckedCeilDiv, precise_number::PreciseNumber, uint::U256},
1517
};
16-
use spl_math::{precise_number::PreciseNumber, uint::U256};
1718

1819
/// ConstantPriceCurve struct implementing CurveCalculator
1920
#[derive(Clone, Debug, Default, PartialEq)]

token-swap/program/src/curve/constant_product.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ use {
66
map_zero_to_none, CurveCalculator, DynPack, RoundDirection, SwapWithoutFeesResult,
77
TradeDirection, TradingTokenResult,
88
},
9-
curve::math::CheckedCeilDiv,
109
error::SwapError,
1110
},
1211
solana_program::{
1312
program_error::ProgramError,
1413
program_pack::{IsInitialized, Pack, Sealed},
1514
},
16-
spl_math::precise_number::PreciseNumber,
15+
spl_math::{checked_ceil_div::CheckedCeilDiv, precise_number::PreciseNumber},
1716
};
1817

1918
/// ConstantProductCurve struct implementing CurveCalculator

token-swap/program/src/curve/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,5 @@ pub mod calculator;
55
pub mod constant_price;
66
pub mod constant_product;
77
pub mod fees;
8-
pub mod math;
98
pub mod offset;
109
pub mod stable;

0 commit comments

Comments
 (0)