Skip to main content

pinocchio_token/instructions/
initialize_account_2.rs

1use {
2    crate::{
3        instructions::{account_borrow_failed_error, invalid_argument_error, CpiWriter},
4        write_bytes, UNINIT_BYTE, UNINIT_CPI_ACCOUNT, UNINIT_INSTRUCTION_ACCOUNT,
5    },
6    core::{mem::MaybeUninit, slice::from_raw_parts},
7    solana_account_view::AccountView,
8    solana_address::Address,
9    solana_instruction_view::{
10        cpi::{invoke_unchecked, CpiAccount},
11        InstructionAccount, InstructionView,
12    },
13    solana_program_error::{ProgramError, ProgramResult},
14};
15
16/// Like [`super::InitializeAccount`], but the owner pubkey is
17/// passed via instruction data rather than the accounts list. This
18/// variant may be preferable when using Cross Program Invocation from
19/// an instruction that does not need the owner's `AccountInfo`
20/// otherwise.
21///
22/// Accounts expected by this instruction:
23///
24///   0. `[writable]`  The account to initialize.
25///   1. `[]` The mint this account will be associated with.
26///   2. `[]` Rent sysvar.
27pub struct InitializeAccount2<'account> {
28    /// The account to initialize.
29    pub account: &'account AccountView,
30
31    /// The mint this account will be associated with.
32    pub mint: &'account AccountView,
33
34    /// Rent sysvar.
35    pub rent_sysvar: &'account AccountView,
36
37    /// The new account's owner/multisignature.
38    pub owner: &'account Address,
39}
40
41impl<'account> InitializeAccount2<'account> {
42    pub const DISCRIMINATOR: u8 = 16;
43
44    /// Expected number of accounts.
45    pub const ACCOUNTS_LEN: usize = 3;
46
47    /// Instruction data length:
48    ///   - discriminator (1 byte)
49    ///   - owner pubkey (32 bytes)
50    pub const DATA_LEN: usize = 33;
51
52    #[inline(always)]
53    pub fn new(
54        account: &'account AccountView,
55        mint: &'account AccountView,
56        rent_sysvar: &'account AccountView,
57        owner: &'account Address,
58    ) -> Self {
59        Self {
60            account,
61            mint,
62            rent_sysvar,
63            owner,
64        }
65    }
66
67    #[inline(always)]
68    pub fn invoke(&self) -> ProgramResult {
69        let mut instruction_accounts =
70            [UNINIT_INSTRUCTION_ACCOUNT; InitializeAccount2::ACCOUNTS_LEN];
71        let written_instruction_accounts =
72            self.write_instruction_accounts(&mut instruction_accounts)?;
73
74        let mut accounts = [UNINIT_CPI_ACCOUNT; Self::ACCOUNTS_LEN];
75        let written_accounts = self.write_accounts(&mut accounts)?;
76
77        let mut instruction_data = [UNINIT_BYTE; Self::DATA_LEN];
78        let written_instruction_data = self.write_instruction_data(&mut instruction_data)?;
79
80        unsafe {
81            invoke_unchecked(
82                &InstructionView {
83                    program_id: &crate::ID,
84                    accounts: from_raw_parts(
85                        instruction_accounts.as_ptr() as _,
86                        written_instruction_accounts,
87                    ),
88                    data: from_raw_parts(instruction_data.as_ptr() as _, written_instruction_data),
89                },
90                from_raw_parts(accounts.as_ptr() as *const CpiAccount, written_accounts),
91            );
92        }
93
94        Ok(())
95    }
96}
97
98impl CpiWriter for InitializeAccount2<'_> {
99    #[inline(always)]
100    fn write_accounts<'cpi>(
101        &self,
102        accounts: &mut [MaybeUninit<CpiAccount<'cpi>>],
103    ) -> Result<usize, ProgramError>
104    where
105        Self: 'cpi,
106    {
107        write_accounts(self.account, self.mint, self.rent_sysvar, accounts)
108    }
109
110    #[inline(always)]
111    fn write_instruction_accounts<'cpi>(
112        &self,
113        accounts: &mut [MaybeUninit<InstructionAccount<'cpi>>],
114    ) -> Result<usize, ProgramError>
115    where
116        Self: 'cpi,
117    {
118        write_instruction_accounts(self.account, self.mint, self.rent_sysvar, accounts)
119    }
120
121    #[inline(always)]
122    fn write_instruction_data(&self, data: &mut [MaybeUninit<u8>]) -> Result<usize, ProgramError> {
123        write_instruction_data(self.owner, data)
124    }
125}
126
127impl super::IntoBatch for InitializeAccount2<'_> {
128    #[inline(always)]
129    fn into_batch<'account, 'state>(
130        self,
131        batch: &mut super::Batch<'account, 'state>,
132    ) -> ProgramResult
133    where
134        Self: 'account + 'state,
135    {
136        batch.push(
137            |accounts| write_accounts(self.account, self.mint, self.rent_sysvar, accounts),
138            |accounts| {
139                write_instruction_accounts(self.account, self.mint, self.rent_sysvar, accounts)
140            },
141            |data| write_instruction_data(self.owner, data),
142        )
143    }
144}
145
146#[inline(always)]
147fn write_accounts<'account, 'out>(
148    account: &'account AccountView,
149    mint: &'account AccountView,
150    rent_sysvar: &'account AccountView,
151    accounts: &mut [MaybeUninit<CpiAccount<'out>>],
152) -> Result<usize, ProgramError>
153where
154    'account: 'out,
155{
156    if accounts.len() < InitializeAccount2::ACCOUNTS_LEN {
157        return Err(invalid_argument_error());
158    }
159
160    if account.is_borrowed() {
161        return Err(account_borrow_failed_error());
162    }
163
164    CpiAccount::init_from_account_view(account, &mut accounts[0]);
165
166    CpiAccount::init_from_account_view(mint, &mut accounts[1]);
167
168    CpiAccount::init_from_account_view(rent_sysvar, &mut accounts[2]);
169
170    Ok(InitializeAccount2::ACCOUNTS_LEN)
171}
172
173#[inline(always)]
174fn write_instruction_accounts<'account, 'out>(
175    account: &'account AccountView,
176    mint: &'account AccountView,
177    rent_sysvar: &'account AccountView,
178    accounts: &mut [MaybeUninit<InstructionAccount<'out>>],
179) -> Result<usize, ProgramError>
180where
181    'account: 'out,
182{
183    if accounts.len() < InitializeAccount2::ACCOUNTS_LEN {
184        return Err(invalid_argument_error());
185    }
186
187    accounts[0].write(InstructionAccount::writable(account.address()));
188
189    accounts[1].write(InstructionAccount::readonly(mint.address()));
190
191    accounts[2].write(InstructionAccount::readonly(rent_sysvar.address()));
192
193    Ok(InitializeAccount2::ACCOUNTS_LEN)
194}
195
196#[inline(always)]
197fn write_instruction_data(
198    owner: &Address,
199    data: &mut [MaybeUninit<u8>],
200) -> Result<usize, ProgramError> {
201    if data.len() < InitializeAccount2::DATA_LEN {
202        return Err(invalid_argument_error());
203    }
204
205    data[0].write(InitializeAccount2::DISCRIMINATOR);
206
207    write_bytes(&mut data[1..InitializeAccount2::DATA_LEN], owner.as_array());
208
209    Ok(InitializeAccount2::DATA_LEN)
210}