marinade_sdk/
stake_system.rs1use borsh::{BorshSerialize, BorshDeserialize};
2use solana_program::{pubkey::Pubkey, msg, account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError};
3use crate::{ID, checks::check_address, list::List, located::Located, state::State};
4
5
6
7#[derive(Clone, Copy, Debug, Default, PartialEq, BorshSerialize, BorshDeserialize)]
8pub struct StakeRecord {
9 pub stake_account: Pubkey,
10 pub last_update_delegated_lamports: u64,
11 pub last_update_epoch: u64,
12 pub is_emergency_unstaking: u8, }
14
15impl StakeRecord {
16 pub const DISCRIMINATOR: &'static [u8; 8] = b"staker__";
17}
18
19#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)]
20pub struct StakeSystem {
21 pub stake_list: List,
22 pub delayed_unstake_cooling_down: u64,
25 pub stake_deposit_bump_seed: u8,
26 pub stake_withdraw_bump_seed: u8,
27
28 pub slots_for_stake_delta: u64,
30 pub last_stake_delta_epoch: u64,
34 pub min_stake: u64, pub extra_stake_delta_runs: u32,
38}
39
40impl StakeSystem {
41 pub const STAKE_WITHDRAW_SEED: &'static [u8] = b"withdraw";
42 pub const STAKE_DEPOSIT_SEED: &'static [u8] = b"deposit";
43
44 pub fn bytes_for_list(count: u32, additional_record_space: u32) -> u32 {
45 List::bytes_for(
46 StakeRecord::default().try_to_vec().unwrap().len() as u32 + additional_record_space,
47 count,
48 )
49 }
50
51 pub fn find_stake_withdraw_authority(state: &Pubkey) -> (Pubkey, u8) {
52 Pubkey::find_program_address(&[&state.to_bytes()[..32], Self::STAKE_WITHDRAW_SEED], &ID)
53 }
54
55 pub fn find_stake_deposit_authority(state: &Pubkey) -> (Pubkey, u8) {
56 Pubkey::find_program_address(&[&state.to_bytes()[..32], Self::STAKE_DEPOSIT_SEED], &ID)
57 }
58
59 pub fn stake_list_address(&self) -> &Pubkey {
60 &self.stake_list.account
61 }
62
63 pub fn stake_count(&self) -> u32 {
64 self.stake_list.len()
65 }
66
67 pub fn stake_list_capacity(&self, stake_list_len: usize) -> Result<u32, ProgramError> {
68 self.stake_list.capacity(stake_list_len)
69 }
70
71 pub fn stake_record_size(&self) -> u32 {
72 self.stake_list.item_size()
73 }
74
75 pub fn get(&self, stake_list_data: &[u8], index: u32) -> Result<StakeRecord, ProgramError> {
76 self.stake_list.get(stake_list_data, index, "stake_list")
77 }
78
79 pub fn check_stake_list<'info>(&self, stake_list: &AccountInfo<'info>) -> ProgramResult {
80 check_address(stake_list.key, self.stake_list_address(), "stake_list")?;
81 if &stake_list.data.borrow().as_ref()[0..8] != StakeRecord::DISCRIMINATOR {
82 msg!("Wrong stake list account discriminator");
83 return Err(ProgramError::InvalidAccountData);
84 }
85 Ok(())
86 }
87}
88
89pub trait StakeSystemHelpers {
90 fn stake_withdraw_authority(&self) -> Pubkey;
91 fn with_stake_withdraw_authority_seeds<R, F: FnOnce(&[&[u8]]) -> R>(&self, f: F) -> R;
92 fn check_stake_withdraw_authority(&self, stake_withdraw_authority: &Pubkey) -> ProgramResult;
93
94 fn stake_deposit_authority(&self) -> Pubkey;
95 fn with_stake_deposit_authority_seeds<R, F: FnOnce(&[&[u8]]) -> R>(&self, f: F) -> R;
96 fn check_stake_deposit_authority(&self, stake_deposit_authority: &Pubkey) -> ProgramResult;
97}
98
99impl<T> StakeSystemHelpers for T
100where
101 T: Located<State>,
102{
103 fn stake_withdraw_authority(&self) -> Pubkey {
104 self.with_stake_withdraw_authority_seeds(|seeds| {
105 Pubkey::create_program_address(seeds, &ID).unwrap()
106 })
107 }
108
109 fn with_stake_withdraw_authority_seeds<R, F: FnOnce(&[&[u8]]) -> R>(&self, f: F) -> R {
110 f(&[
111 &self.key().to_bytes()[..32],
112 StakeSystem::STAKE_WITHDRAW_SEED,
113 &[self.as_ref().stake_system.stake_withdraw_bump_seed],
114 ])
115 }
116
117 fn check_stake_withdraw_authority(&self, stake_withdraw_authority: &Pubkey) -> ProgramResult {
118 check_address(
119 stake_withdraw_authority,
120 &self.stake_withdraw_authority(),
121 "stake_withdraw_authority",
122 )
123 }
124
125 fn stake_deposit_authority(&self) -> Pubkey {
126 self.with_stake_deposit_authority_seeds(|seeds| {
127 Pubkey::create_program_address(seeds, &ID).unwrap()
128 })
129 }
130
131 fn with_stake_deposit_authority_seeds<R, F: FnOnce(&[&[u8]]) -> R>(&self, f: F) -> R {
132 f(&[
133 &self.key().to_bytes()[..32],
134 StakeSystem::STAKE_DEPOSIT_SEED,
135 &[self.as_ref().stake_system.stake_deposit_bump_seed],
136 ])
137 }
138
139 fn check_stake_deposit_authority(&self, stake_deposit_authority: &Pubkey) -> ProgramResult {
140 check_address(
141 stake_deposit_authority,
142 &self.stake_deposit_authority(),
143 "stake_deposit_authority",
144 )
145 }
146}