Skip to main content

pinocchio_token/instructions/
initialize_multisig.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/// Maximum number of multisignature signers.
16pub const MAX_MULTISIG_SIGNERS: usize = 11;
17
18/// Initializes a multisignature account with N provided signers.
19///
20/// Multisignature accounts can used in place of any single owner/delegate
21/// accounts in any token instruction that require an owner/delegate to be
22/// present.  The variant field represents the number of signers (M)
23/// required to validate this multisignature account.
24///
25/// The [`super::InitializeMultisig`] instruction requires no
26/// signers and MUST be included within the same Transaction as the
27/// system program's `CreateAccount` instruction that creates the
28/// account being initialized. Otherwise another party can acquire
29/// ownership of the uninitialized account.
30///
31/// Accounts expected by this instruction:
32///
33///   0. `[writable]` The multisignature account to initialize.
34///   1. `[]` Rent sysvar.
35///   2. `..+N` `[signer]` The signer accounts, must equal to N where `1 <= N <=
36///      11`.
37pub struct InitializeMultisig<'account, 'multisig, MultisigSigner: AsRef<AccountView>>
38where
39    'account: 'multisig,
40{
41    /// The multisignature account to initialize.
42    pub multisig: &'account AccountView,
43
44    /// Rent sysvar.
45    pub rent_sysvar: &'account AccountView,
46
47    /// The signer accounts.
48    pub multisig_signers: &'multisig [MultisigSigner],
49
50    /// The number of signers (M) required to validate this multisignature
51    /// account.
52    pub m: u8,
53}
54
55impl<'account, 'multisig, MultisigSigner: AsRef<AccountView>>
56    InitializeMultisig<'account, 'multisig, MultisigSigner>
57where
58    'account: 'multisig,
59{
60    pub const DISCRIMINATOR: u8 = 2;
61
62    /// Maximum number of accounts expected by this instruction.
63    ///
64    /// The required number of accounts will depend whether the
65    /// source account has a single owner or a multisignature
66    /// owner.
67    pub const MAX_ACCOUNTS_LEN: usize = 2 + MAX_MULTISIG_SIGNERS;
68
69    /// Instruction data length:
70    ///   - discriminator (1 byte)
71    ///   - number of signers (1 byte)
72    pub const DATA_LEN: usize = 2;
73
74    #[inline(always)]
75    pub fn new(
76        multisig: &'account AccountView,
77        rent_sysvar: &'account AccountView,
78        multisig_signers: &'multisig [MultisigSigner],
79        m: u8,
80    ) -> Self {
81        Self {
82            multisig,
83            rent_sysvar,
84            multisig_signers,
85            m,
86        }
87    }
88
89    #[inline(always)]
90    pub fn invoke(&self) -> ProgramResult {
91        if self.multisig_signers.len() > MAX_MULTISIG_SIGNERS {
92            return Err(ProgramError::InvalidArgument);
93        }
94
95        let mut instruction_accounts =
96            [UNINIT_INSTRUCTION_ACCOUNT; InitializeMultisig::<&AccountView>::MAX_ACCOUNTS_LEN];
97        let written_instruction_accounts =
98            self.write_instruction_accounts(&mut instruction_accounts)?;
99
100        let mut accounts =
101            [UNINIT_CPI_ACCOUNT; InitializeMultisig::<&AccountView>::MAX_ACCOUNTS_LEN];
102        let written_accounts = self.write_accounts(&mut accounts)?;
103
104        let mut instruction_data = [UNINIT_BYTE; InitializeMultisig::<&AccountView>::DATA_LEN];
105        let written_instruction_data = self.write_instruction_data(&mut instruction_data)?;
106
107        unsafe {
108            invoke_unchecked(
109                &InstructionView {
110                    program_id: &crate::ID,
111                    accounts: from_raw_parts(
112                        instruction_accounts.as_ptr() as _,
113                        written_instruction_accounts,
114                    ),
115                    data: from_raw_parts(instruction_data.as_ptr() as _, written_instruction_data),
116                },
117                from_raw_parts(accounts.as_ptr() as _, written_accounts),
118            );
119        }
120
121        Ok(())
122    }
123}
124
125impl<MultisigSigner: AsRef<AccountView>> CpiWriter for InitializeMultisig<'_, '_, MultisigSigner> {
126    #[inline(always)]
127    fn write_accounts<'cpi>(
128        &self,
129        accounts: &mut [MaybeUninit<CpiAccount<'cpi>>],
130    ) -> Result<usize, ProgramError>
131    where
132        Self: 'cpi,
133    {
134        write_accounts(
135            self.multisig,
136            self.rent_sysvar,
137            self.multisig_signers,
138            accounts,
139        )
140    }
141
142    #[inline(always)]
143    fn write_instruction_accounts<'cpi>(
144        &self,
145        accounts: &mut [MaybeUninit<InstructionAccount<'cpi>>],
146    ) -> Result<usize, ProgramError>
147    where
148        Self: 'cpi,
149    {
150        write_instruction_accounts(
151            self.multisig,
152            self.rent_sysvar,
153            self.multisig_signers,
154            accounts,
155        )
156    }
157
158    #[inline(always)]
159    fn write_instruction_data(&self, data: &mut [MaybeUninit<u8>]) -> Result<usize, ProgramError> {
160        write_instruction_data(self.m, data)
161    }
162}
163
164impl<MultisigSigner: AsRef<AccountView>> super::IntoBatch
165    for InitializeMultisig<'_, '_, MultisigSigner>
166{
167    #[inline(always)]
168    fn into_batch<'account, 'state>(
169        self,
170        batch: &mut super::Batch<'account, 'state>,
171    ) -> ProgramResult
172    where
173        Self: 'account + 'state,
174    {
175        batch.push(
176            |accounts| {
177                write_accounts(
178                    self.multisig,
179                    self.rent_sysvar,
180                    self.multisig_signers,
181                    accounts,
182                )
183            },
184            |accounts| {
185                write_instruction_accounts(
186                    self.multisig,
187                    self.rent_sysvar,
188                    self.multisig_signers,
189                    accounts,
190                )
191            },
192            |data| write_instruction_data(self.m, data),
193        )
194    }
195}
196
197#[inline(always)]
198fn write_accounts<'account, 'multisig, 'out, MultisigSigner: AsRef<AccountView>>(
199    multisig: &'account AccountView,
200    rent_sysvar: &'account AccountView,
201    multisig_signers: &'multisig [MultisigSigner],
202    accounts: &mut [MaybeUninit<CpiAccount<'out>>],
203) -> Result<usize, ProgramError>
204where
205    'account: 'out,
206    'multisig: 'out,
207{
208    let expected_accounts = 2 + multisig_signers.len();
209
210    if expected_accounts > accounts.len() {
211        return Err(invalid_argument_error());
212    }
213
214    if multisig.is_borrowed() {
215        return Err(account_borrow_failed_error());
216    }
217
218    CpiAccount::init_from_account_view(multisig, &mut accounts[0]);
219
220    CpiAccount::init_from_account_view(rent_sysvar, &mut accounts[1]);
221
222    for (account, signer) in accounts[2..expected_accounts]
223        .iter_mut()
224        .zip(multisig_signers.iter())
225    {
226        CpiAccount::init_from_account_view(signer.as_ref(), account);
227    }
228
229    Ok(expected_accounts)
230}
231
232#[inline(always)]
233fn write_instruction_accounts<'account, 'multisig, 'out, MultisigSigner: AsRef<AccountView>>(
234    multisig: &'account AccountView,
235    rent_sysvar: &'account AccountView,
236    multisig_signers: &'multisig [MultisigSigner],
237    accounts: &mut [MaybeUninit<InstructionAccount<'out>>],
238) -> Result<usize, ProgramError>
239where
240    'account: 'out,
241    'multisig: 'out,
242{
243    let expected_accounts = 2 + multisig_signers.len();
244
245    if expected_accounts > accounts.len() {
246        return Err(invalid_argument_error());
247    }
248
249    accounts[0].write(InstructionAccount::writable(multisig.address()));
250
251    accounts[1].write(InstructionAccount::readonly(rent_sysvar.address()));
252
253    for (account, signer) in accounts[2..expected_accounts]
254        .iter_mut()
255        .zip(multisig_signers.iter())
256    {
257        account.write(InstructionAccount::readonly(signer.as_ref().address()));
258    }
259
260    Ok(expected_accounts)
261}
262
263#[inline(always)]
264fn write_instruction_data(m: u8, data: &mut [MaybeUninit<u8>]) -> Result<usize, ProgramError> {
265    if data.len() < InitializeMultisig::<&AccountView>::DATA_LEN {
266        return Err(invalid_argument_error());
267    }
268
269    data[0].write(InitializeMultisig::<&AccountView>::DISCRIMINATOR);
270
271    data[1].write(m);
272
273    Ok(InitializeMultisig::<&AccountView>::DATA_LEN)
274}