Skip to main content

spl_single_pool/
state.rs

1//! State transition types
2
3use {
4    crate::{error::SinglePoolError, find_pool_address},
5    borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
6    solana_account_info::AccountInfo,
7    solana_borsh::v1::try_from_slice_unchecked,
8    solana_program_error::ProgramError,
9    solana_pubkey::Pubkey,
10};
11
12/// Single-Validator Stake Pool account type
13#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
14pub enum SinglePoolAccountType {
15    /// Uninitialized account
16    #[default]
17    Uninitialized,
18    /// Main pool account
19    Pool,
20}
21
22/// Single-Validator Stake Pool account, used to derive all PDAs
23#[derive(Clone, Debug, Default, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
24pub struct SinglePool {
25    /// Pool account type, reserved for future compatibility
26    pub account_type: SinglePoolAccountType,
27    /// The vote account this pool is mapped to
28    pub vote_account_address: Pubkey,
29}
30impl SinglePool {
31    /// Onchain serialized size of `SinglePool`
32    pub const fn size_of() -> usize {
33        33
34    }
35
36    /// Create a `SinglePool` struct from its account info
37    pub fn from_account_info(
38        account_info: &AccountInfo,
39        program_id: &Pubkey,
40    ) -> Result<Self, ProgramError> {
41        // pool is allocated and owned by this program
42        if account_info.data_len() == 0 || account_info.owner != program_id {
43            return Err(SinglePoolError::InvalidPoolAccount.into());
44        }
45
46        let pool = try_from_slice_unchecked::<SinglePool>(&account_info.data.borrow())?;
47
48        // pool is well-typed
49        if pool.account_type != SinglePoolAccountType::Pool {
50            return Err(SinglePoolError::InvalidPoolAccount.into());
51        }
52
53        // pool vote account address is properly configured. in practice this is
54        // irrefutable because the pool is initialized from the address that
55        // derives it, and never modified
56        if *account_info.key != find_pool_address(program_id, &pool.vote_account_address) {
57            return Err(SinglePoolError::InvalidPoolAccount.into());
58        }
59
60        Ok(pool)
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use {super::*, solana_borsh::v1::get_packed_len};
67
68    #[test]
69    fn single_pool_size_of() {
70        assert_eq!(SinglePool::size_of(), get_packed_len::<SinglePool>());
71    }
72}