Skip to main content

spl_stake_pool/
lib.rs

1#![deny(missing_docs)]
2
3//! A program for creating and managing pools of stake
4
5pub mod big_vec;
6pub mod error;
7pub mod inline_mpl_token_metadata;
8pub mod instruction;
9pub mod processor;
10pub mod state;
11
12#[cfg(not(feature = "no-entrypoint"))]
13pub mod entrypoint;
14
15// Export current sdk types for downstream users building with a different sdk
16// version
17pub use solana_program;
18use {
19    crate::state::Fee,
20    solana_program::{pubkey::Pubkey, stake::state::Meta},
21    std::num::NonZeroU32,
22};
23
24/// Seed for deposit authority seed
25const AUTHORITY_DEPOSIT: &[u8] = b"deposit";
26
27/// Seed for withdraw authority seed
28const AUTHORITY_WITHDRAW: &[u8] = b"withdraw";
29
30/// Seed for transient stake account
31const TRANSIENT_STAKE_SEED_PREFIX: &[u8] = b"transient";
32
33/// Seed for ephemeral stake account
34const EPHEMERAL_STAKE_SEED_PREFIX: &[u8] = b"ephemeral";
35
36/// Seed for user stake account created during session withdrawal
37pub const USER_STAKE_SEED_PREFIX: &[u8] = b"user_stake";
38
39/// Minimum amount of staked lamports required in a validator stake account to
40/// allow for merges without a mismatch on credits observed
41pub const MINIMUM_ACTIVE_STAKE: u64 = 1_000_000;
42
43/// Minimum amount of lamports in the reserve
44pub const MINIMUM_RESERVE_LAMPORTS: u64 = 0;
45
46/// Maximum amount of validator stake accounts to update per
47/// `UpdateValidatorListBalance` instruction, based on compute limits
48pub const MAX_VALIDATORS_TO_UPDATE: usize = 4;
49
50/// Maximum factor by which a withdrawal fee can be increased per epoch
51/// protecting stakers from malicious users.
52/// If current fee is 0, `WITHDRAWAL_BASELINE_FEE` is used as the baseline
53pub const MAX_WITHDRAWAL_FEE_INCREASE: Fee = Fee {
54    numerator: 3,
55    denominator: 2,
56};
57/// Drop-in baseline fee when evaluating withdrawal fee increases when fee is 0
58pub const WITHDRAWAL_BASELINE_FEE: Fee = Fee {
59    numerator: 1,
60    denominator: 1000,
61};
62
63/// The maximum number of transient stake accounts respecting
64/// transaction account limits.
65pub const MAX_TRANSIENT_STAKE_ACCOUNTS: usize = 10;
66
67/// The maximum number of validators that can be supported in a pool in order
68/// for stake withdrawals to still work
69pub const MAX_VALIDATORS_IN_POOL: u32 = 20_000;
70
71/// Get the stake amount under consideration when calculating pool token
72/// conversions
73#[inline]
74pub fn minimum_stake_lamports(meta: &Meta, stake_program_minimum_delegation: u64) -> u64 {
75    meta.rent_exempt_reserve
76        .saturating_add(minimum_delegation(stake_program_minimum_delegation))
77}
78
79/// Get the minimum delegation required by a stake account in a stake pool
80#[inline]
81pub fn minimum_delegation(stake_program_minimum_delegation: u64) -> u64 {
82    std::cmp::max(stake_program_minimum_delegation, MINIMUM_ACTIVE_STAKE)
83}
84
85/// Get the stake amount under consideration when calculating pool token
86/// conversions
87#[inline]
88pub fn minimum_reserve_lamports(meta: &Meta) -> u64 {
89    meta.rent_exempt_reserve
90        .saturating_add(MINIMUM_RESERVE_LAMPORTS)
91}
92
93/// Generates the deposit authority program address for the stake pool
94pub fn find_deposit_authority_program_address(
95    program_id: &Pubkey,
96    stake_pool_address: &Pubkey,
97) -> (Pubkey, u8) {
98    Pubkey::find_program_address(
99        &[stake_pool_address.as_ref(), AUTHORITY_DEPOSIT],
100        program_id,
101    )
102}
103
104/// Generates the withdraw authority program address for the stake pool
105pub fn find_withdraw_authority_program_address(
106    program_id: &Pubkey,
107    stake_pool_address: &Pubkey,
108) -> (Pubkey, u8) {
109    Pubkey::find_program_address(
110        &[stake_pool_address.as_ref(), AUTHORITY_WITHDRAW],
111        program_id,
112    )
113}
114
115/// Generates the stake program address for a validator's vote account
116pub fn find_stake_program_address(
117    program_id: &Pubkey,
118    vote_account_address: &Pubkey,
119    stake_pool_address: &Pubkey,
120    seed: Option<NonZeroU32>,
121) -> (Pubkey, u8) {
122    let seed = seed.map(|s| s.get().to_le_bytes());
123    Pubkey::find_program_address(
124        &[
125            vote_account_address.as_ref(),
126            stake_pool_address.as_ref(),
127            seed.as_ref().map(|s| s.as_slice()).unwrap_or(&[]),
128        ],
129        program_id,
130    )
131}
132
133/// Generates the stake program address for a validator's vote account
134pub fn find_transient_stake_program_address(
135    program_id: &Pubkey,
136    vote_account_address: &Pubkey,
137    stake_pool_address: &Pubkey,
138    seed: u64,
139) -> (Pubkey, u8) {
140    Pubkey::find_program_address(
141        &[
142            TRANSIENT_STAKE_SEED_PREFIX,
143            vote_account_address.as_ref(),
144            stake_pool_address.as_ref(),
145            &seed.to_le_bytes(),
146        ],
147        program_id,
148    )
149}
150
151/// Generates the ephemeral program address for stake pool redelegation
152pub fn find_ephemeral_stake_program_address(
153    program_id: &Pubkey,
154    stake_pool_address: &Pubkey,
155    seed: u64,
156) -> (Pubkey, u8) {
157    Pubkey::find_program_address(
158        &[
159            EPHEMERAL_STAKE_SEED_PREFIX,
160            stake_pool_address.as_ref(),
161            &seed.to_le_bytes(),
162        ],
163        program_id,
164    )
165}
166
167/// Generates the user stake account PDA for session-based withdrawals.
168/// The PDA is derived from the user's wallet and a unique seed.
169pub fn find_user_stake_program_address(
170    program_id: &Pubkey,
171    user_wallet: &Pubkey,
172    seed: u64,
173) -> (Pubkey, u8) {
174    Pubkey::find_program_address(
175        &[
176            USER_STAKE_SEED_PREFIX,
177            user_wallet.as_ref(),
178            &seed.to_le_bytes(),
179        ],
180        program_id,
181    )
182}
183
184solana_program::declare_id!("SP1s4uFeTAX9jsXXmwyDs1gxYYf7cdDZ8qHUHVxE1yr");
185/// Program id for devnet
186pub mod devnet {
187    solana_program::declare_id!("DPoo15wWDqpPJJtS2MUZ49aRxqz5ZaaJCJP4z8bLuib");
188}
189
190#[cfg(test)]
191mod test {
192    use super::*;
193
194    #[test]
195    fn validator_stake_account_derivation() {
196        let vote = Pubkey::new_unique();
197        let stake_pool = Pubkey::new_unique();
198        let function_derived = find_stake_program_address(&id(), &vote, &stake_pool, None);
199        let hand_derived =
200            Pubkey::find_program_address(&[vote.as_ref(), stake_pool.as_ref()], &id());
201        assert_eq!(function_derived, hand_derived);
202    }
203}