Skip to main content

pinocchio_token/instructions/
initialize_account.rs

1use {
2    crate::{
3        instructions::{account_borrow_failed_error, invalid_argument_error, CpiWriter},
4        UNINIT_BYTE, UNINIT_CPI_ACCOUNT, UNINIT_INSTRUCTION_ACCOUNT,
5    },
6    core::{mem::MaybeUninit, slice::from_raw_parts},
7    solana_account_view::AccountView,
8    solana_instruction_view::{
9        cpi::{invoke_unchecked, CpiAccount},
10        InstructionAccount, InstructionView,
11    },
12    solana_program_error::{ProgramError, ProgramResult},
13};
14
15/// Initializes a new account to hold tokens.  If this account is associated
16/// with the native mint then the token balance of the initialized account
17/// will be equal to the amount of SOL in the account. If this account is
18/// associated with another mint, that mint must be initialized before this
19/// command can succeed.
20///
21/// The [`super::InitializeAccount`] instruction requires no
22/// signers and MUST be included within the same Transaction as the
23/// system program's `CreateAccount` instruction that creates the
24/// account being initialized. Otherwise another party can acquire
25/// ownership of the uninitialized account.
26///
27/// Accounts expected by this instruction:
28///
29///   0. `[writable]`  The account to initialize.
30///   1. `[]` The mint this account will be associated with.
31///   2. `[]` The new account's owner/multisignature.
32///   3. `[]` Rent sysvar.
33pub struct InitializeAccount<'account> {
34    /// The account to initialize.
35    pub account: &'account AccountView,
36
37    /// The mint this account will be associated with.
38    pub mint: &'account AccountView,
39
40    /// The new account's owner/multisignature.
41    pub owner: &'account AccountView,
42
43    /// Rent sysvar.
44    pub rent_sysvar: &'account AccountView,
45}
46
47impl<'account> InitializeAccount<'account> {
48    pub const DISCRIMINATOR: u8 = 1;
49
50    /// Expected number of accounts.
51    pub const ACCOUNTS_LEN: usize = 4;
52
53    /// Instruction data length:
54    ///   - discriminator (1 byte)
55    pub const DATA_LEN: usize = 1;
56
57    #[inline(always)]
58    pub fn new(
59        account: &'account AccountView,
60        mint: &'account AccountView,
61        owner: &'account AccountView,
62        rent_sysvar: &'account AccountView,
63    ) -> Self {
64        Self {
65            account,
66            mint,
67            owner,
68            rent_sysvar,
69        }
70    }
71
72    #[inline(always)]
73    pub fn invoke(&self) -> ProgramResult {
74        let mut instruction_accounts =
75            [UNINIT_INSTRUCTION_ACCOUNT; InitializeAccount::ACCOUNTS_LEN];
76        let written_instruction_accounts =
77            self.write_instruction_accounts(&mut instruction_accounts)?;
78
79        let mut accounts = [UNINIT_CPI_ACCOUNT; Self::ACCOUNTS_LEN];
80        let written_accounts = self.write_accounts(&mut accounts)?;
81
82        let mut instruction_data = [UNINIT_BYTE; Self::DATA_LEN];
83        let written_instruction_data = self.write_instruction_data(&mut instruction_data)?;
84
85        unsafe {
86            invoke_unchecked(
87                &InstructionView {
88                    program_id: &crate::ID,
89                    accounts: from_raw_parts(
90                        instruction_accounts.as_ptr() as _,
91                        written_instruction_accounts,
92                    ),
93                    data: from_raw_parts(instruction_data.as_ptr() as _, written_instruction_data),
94                },
95                from_raw_parts(accounts.as_ptr() as _, written_accounts),
96            );
97        }
98
99        Ok(())
100    }
101}
102
103impl CpiWriter for InitializeAccount<'_> {
104    #[inline(always)]
105    fn write_accounts<'cpi>(
106        &self,
107        accounts: &mut [MaybeUninit<CpiAccount<'cpi>>],
108    ) -> Result<usize, ProgramError>
109    where
110        Self: 'cpi,
111    {
112        write_accounts(
113            self.account,
114            self.mint,
115            self.owner,
116            self.rent_sysvar,
117            accounts,
118        )
119    }
120
121    #[inline(always)]
122    fn write_instruction_accounts<'cpi>(
123        &self,
124        accounts: &mut [MaybeUninit<InstructionAccount<'cpi>>],
125    ) -> Result<usize, ProgramError>
126    where
127        Self: 'cpi,
128    {
129        write_instruction_accounts(
130            self.account,
131            self.mint,
132            self.owner,
133            self.rent_sysvar,
134            accounts,
135        )
136    }
137
138    #[inline(always)]
139    fn write_instruction_data(&self, data: &mut [MaybeUninit<u8>]) -> Result<usize, ProgramError> {
140        write_instruction_data(data)
141    }
142}
143
144impl super::IntoBatch for InitializeAccount<'_> {
145    #[inline(always)]
146    fn into_batch<'account, 'state>(
147        self,
148        batch: &mut super::Batch<'account, 'state>,
149    ) -> ProgramResult
150    where
151        Self: 'account + 'state,
152    {
153        batch.push(
154            |accounts| {
155                write_accounts(
156                    self.account,
157                    self.mint,
158                    self.owner,
159                    self.rent_sysvar,
160                    accounts,
161                )
162            },
163            |accounts| {
164                write_instruction_accounts(
165                    self.account,
166                    self.mint,
167                    self.owner,
168                    self.rent_sysvar,
169                    accounts,
170                )
171            },
172            write_instruction_data,
173        )
174    }
175}
176
177#[inline(always)]
178fn write_accounts<'account, 'out>(
179    account: &'account AccountView,
180    mint: &'account AccountView,
181    owner: &'account AccountView,
182    rent_sysvar: &'account AccountView,
183    accounts: &mut [MaybeUninit<CpiAccount<'out>>],
184) -> Result<usize, ProgramError>
185where
186    'account: 'out,
187{
188    if accounts.len() < InitializeAccount::ACCOUNTS_LEN {
189        return Err(invalid_argument_error());
190    }
191
192    if account.is_borrowed() {
193        return Err(account_borrow_failed_error());
194    }
195
196    CpiAccount::init_from_account_view(account, &mut accounts[0]);
197
198    CpiAccount::init_from_account_view(mint, &mut accounts[1]);
199
200    CpiAccount::init_from_account_view(owner, &mut accounts[2]);
201
202    CpiAccount::init_from_account_view(rent_sysvar, &mut accounts[3]);
203
204    Ok(InitializeAccount::ACCOUNTS_LEN)
205}
206
207#[inline(always)]
208fn write_instruction_accounts<'account, 'out>(
209    account: &'account AccountView,
210    mint: &'account AccountView,
211    owner: &'account AccountView,
212    rent_sysvar: &'account AccountView,
213    accounts: &mut [MaybeUninit<InstructionAccount<'out>>],
214) -> Result<usize, ProgramError>
215where
216    'account: 'out,
217{
218    if accounts.len() < InitializeAccount::ACCOUNTS_LEN {
219        return Err(invalid_argument_error());
220    }
221
222    accounts[0].write(InstructionAccount::writable(account.address()));
223
224    accounts[1].write(InstructionAccount::readonly(mint.address()));
225
226    accounts[2].write(InstructionAccount::readonly(owner.address()));
227
228    accounts[3].write(InstructionAccount::readonly(rent_sysvar.address()));
229
230    Ok(InitializeAccount::ACCOUNTS_LEN)
231}
232
233#[inline(always)]
234fn write_instruction_data(data: &mut [MaybeUninit<u8>]) -> Result<usize, ProgramError> {
235    if data.len() < InitializeAccount::DATA_LEN {
236        return Err(invalid_argument_error());
237    }
238
239    data[0].write(InitializeAccount::DISCRIMINATOR);
240
241    Ok(InitializeAccount::DATA_LEN)
242}