1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Create stake account
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint::ProgramResult,
    program_error::ProgramError,
    pubkey::Pubkey,
    system_program,
};

use crate::state::{StakeAccount, StakePool, Tag};
use crate::{cpi::Cpi, error::AccessError};

use bonfida_utils::{BorshSize, InstructionsAccount};
use crate::instruction::ProgramInstruction::CreateStakeAccount;

use crate::utils::{check_account_key, check_account_owner};
use crate::state:: CentralStateV2;
#[derive(BorshDeserialize, BorshSerialize, BorshSize)]
/// The required parameters for the `create_stake_account` instruction
pub struct Params {
    // The PDA nonce
    pub nonce: u8,
    // Owner of the stake account
    pub owner: Pubkey,
}

#[derive(InstructionsAccount)]
/// The required parameters for the `create_stake_account` instruction
pub struct Accounts<'a, T> {
    /// The stake account
    #[cons(writable)]
    pub stake_account: &'a T,

    /// The system program account
    pub system_program: &'a T,

    /// The stake pool account
    pub stake_pool: &'a T,

    /// The fee payer account
    #[cons(writable, signer)]
    pub fee_payer: &'a T,

    /// The central state account
    pub central_state: &'a T,
}

impl<'a, 'b: 'a> Accounts<'a, AccountInfo<'b>> {
    pub fn parse(
        accounts: &'a [AccountInfo<'b>],
        program_id: &Pubkey,
    ) -> Result<Self, ProgramError> {
        let accounts_iter = &mut accounts.iter();
        let accounts = Accounts {
            stake_account: next_account_info(accounts_iter)?,
            system_program: next_account_info(accounts_iter)?,
            stake_pool: next_account_info(accounts_iter)?,
            fee_payer: next_account_info(accounts_iter)?,
            central_state: next_account_info(accounts_iter)?,
        };

        // Check keys
        check_account_key(
            accounts.system_program,
            &system_program::ID,
            AccessError::WrongSystemProgram,
        )?;

        // Check ownership
        check_account_owner(accounts.central_state, program_id, AccessError::WrongOwner)?;
        check_account_owner(
            accounts.stake_account,
            &system_program::ID,
            AccessError::WrongOwner,
        )?;
        check_account_owner(accounts.stake_pool, program_id, AccessError::WrongOwner)?;

        Ok(accounts)
    }
}

pub fn process_create_stake_account(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    params: Params,
) -> ProgramResult {
    let accounts = Accounts::parse(accounts, program_id)?;

    let central_state = CentralStateV2::from_account_info(accounts.central_state)?;
    central_state.assert_instruction_allowed(&CreateStakeAccount)?;
    let stake_pool = StakePool::get_checked(accounts.stake_pool, vec![Tag::StakePool])?;

    let derived_stake_key = StakeAccount::create_key(
        &params.nonce,
        &params.owner,
        accounts.stake_pool.key,
        program_id,
    )?;

    check_account_key(
        accounts.stake_account,
        &derived_stake_key,
        AccessError::AccountNotDeterministic,
    )?;

    let stake_account = StakeAccount::new(
        params.owner,
        *accounts.stake_pool.key,
        stake_pool.header.minimum_stake_amount,
    );

    Cpi::create_account(
        program_id,
        accounts.system_program,
        accounts.fee_payer,
        accounts.stake_account,
        &[
            StakeAccount::SEED,
            &params.owner.to_bytes(),
            &accounts.stake_pool.key.to_bytes(),
            &[params.nonce],
        ],
        stake_account.borsh_len(),
    )?;

    stake_account.save(&mut accounts.stake_account.data.borrow_mut())?;

    Ok(())
}