Skip to content

Commit 4f47ad2

Browse files
feborustopianNagaprasadvr
authored
token-2022: Add initialize multisig helpers (#220)
* Add initialize multisig helpers * Update programs/token-2022/src/instructions/initialize_multisig_2.rs Co-authored-by: Peter Keay <[email protected]> * `feat`: add multisig state (#221) feat: add multisig state * Revert "`feat`: add multisig state" (#222) Revert "`feat`: add multisig state (#221)" This reverts commit ef45bf3. --------- Co-authored-by: Peter Keay <[email protected]> Co-authored-by: Nagaprasad V R <[email protected]>
1 parent 0f3cc2f commit 4f47ad2

File tree

3 files changed

+211
-0
lines changed

3 files changed

+211
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use core::{mem::MaybeUninit, slice};
2+
3+
use pinocchio::{
4+
account_info::AccountInfo,
5+
cpi::invoke_with_bounds,
6+
instruction::{AccountMeta, Instruction},
7+
program_error::ProgramError,
8+
pubkey::Pubkey,
9+
ProgramResult,
10+
};
11+
12+
/// Maximum number of multisignature signers.
13+
pub const MAX_MULTISIG_SIGNERS: usize = 11;
14+
15+
/// Initialize a new Multisig.
16+
///
17+
/// ### Accounts:
18+
/// 0. `[writable]` The multisig account to initialize.
19+
/// 1. `[]` Rent sysvar
20+
/// 2. `..+N` `[]` The `N` signer accounts, where `N` is `1 <=
21+
/// N <= 11`.
22+
pub struct InitializeMultisig<'a, 'b, 'c>
23+
where
24+
'a: 'b,
25+
{
26+
/// Multisig Account.
27+
pub multisig: &'a AccountInfo,
28+
/// Rent sysvar Account.
29+
pub rent_sysvar: &'a AccountInfo,
30+
/// Signer Accounts
31+
pub signers: &'b [&'a AccountInfo],
32+
/// The number of signers (M) required to validate this multisignature
33+
/// account.
34+
pub m: u8,
35+
/// Token Program.
36+
pub token_program: &'c Pubkey,
37+
}
38+
39+
impl InitializeMultisig<'_, '_, '_> {
40+
#[inline(always)]
41+
pub fn invoke(&self) -> ProgramResult {
42+
let &Self {
43+
multisig,
44+
rent_sysvar,
45+
signers,
46+
m,
47+
token_program,
48+
} = self;
49+
50+
if signers.len() > MAX_MULTISIG_SIGNERS {
51+
return Err(ProgramError::InvalidArgument);
52+
}
53+
54+
let num_accounts = 2 + signers.len();
55+
56+
// Account metadata
57+
const UNINIT_META: MaybeUninit<AccountMeta> = MaybeUninit::<AccountMeta>::uninit();
58+
let mut acc_metas = [UNINIT_META; 2 + MAX_MULTISIG_SIGNERS];
59+
60+
unsafe {
61+
// SAFETY:
62+
// - `account_metas` is sized to 2 + MAX_MULTISIG_SIGNERS
63+
// - Index 0 and 1 are always present
64+
acc_metas
65+
.get_unchecked_mut(0)
66+
.write(AccountMeta::writable(multisig.key()));
67+
acc_metas
68+
.get_unchecked_mut(1)
69+
.write(AccountMeta::readonly(rent_sysvar.key()));
70+
}
71+
72+
for (account_meta, signer) in acc_metas[2..].iter_mut().zip(signers.iter()) {
73+
account_meta.write(AccountMeta::readonly(signer.key()));
74+
}
75+
76+
// Instruction data layout:
77+
// - [0]: instruction discriminator (1 byte, u8)
78+
// - [1]: m (1 byte, u8)
79+
let data = &[2, m];
80+
81+
let instruction = Instruction {
82+
program_id: token_program,
83+
accounts: unsafe { slice::from_raw_parts(acc_metas.as_ptr() as _, num_accounts) },
84+
data,
85+
};
86+
87+
// Account info array
88+
const UNINIT_INFO: MaybeUninit<&AccountInfo> = MaybeUninit::uninit();
89+
let mut acc_infos = [UNINIT_INFO; 2 + MAX_MULTISIG_SIGNERS];
90+
91+
unsafe {
92+
// SAFETY:
93+
// - `account_infos` is sized to 2 + MAX_MULTISIG_SIGNERS
94+
// - Index 0 and 1 are always present
95+
acc_infos.get_unchecked_mut(0).write(multisig);
96+
acc_infos.get_unchecked_mut(1).write(rent_sysvar);
97+
}
98+
99+
// Fill signer accounts
100+
for (account_info, signer) in acc_infos[2..].iter_mut().zip(signers.iter()) {
101+
account_info.write(signer);
102+
}
103+
104+
invoke_with_bounds::<{ 2 + MAX_MULTISIG_SIGNERS }>(&instruction, unsafe {
105+
slice::from_raw_parts(acc_infos.as_ptr() as _, num_accounts)
106+
})
107+
}
108+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use core::{mem::MaybeUninit, slice};
2+
3+
use pinocchio::{
4+
account_info::AccountInfo,
5+
cpi::invoke_with_bounds,
6+
instruction::{AccountMeta, Instruction},
7+
program_error::ProgramError,
8+
pubkey::Pubkey,
9+
ProgramResult,
10+
};
11+
12+
use crate::instructions::MAX_MULTISIG_SIGNERS;
13+
14+
/// Initialize a new Multisig.
15+
///
16+
/// ### Accounts:
17+
/// 0. `[writable]` The multisig account to initialize.
18+
/// 1. `..+N` `[]` The `N` signer accounts, where `N` is `1 <=
19+
/// N <= 11`.
20+
pub struct InitializeMultisig2<'a, 'b, 'c>
21+
where
22+
'a: 'b,
23+
{
24+
/// Multisig Account.
25+
pub multisig: &'a AccountInfo,
26+
/// Signer Accounts
27+
pub signers: &'b [&'a AccountInfo],
28+
/// The number of signers (M) required to validate this multisignature
29+
/// account.
30+
pub m: u8,
31+
/// Token Program.
32+
pub token_program: &'c Pubkey,
33+
}
34+
35+
impl InitializeMultisig2<'_, '_, '_> {
36+
#[inline(always)]
37+
pub fn invoke(&self) -> ProgramResult {
38+
let &Self {
39+
multisig,
40+
signers,
41+
m,
42+
token_program,
43+
} = self;
44+
45+
if signers.len() > MAX_MULTISIG_SIGNERS {
46+
return Err(ProgramError::InvalidArgument);
47+
}
48+
49+
let num_accounts = 1 + signers.len();
50+
51+
// Account metadata
52+
const UNINIT_META: MaybeUninit<AccountMeta> = MaybeUninit::<AccountMeta>::uninit();
53+
let mut acc_metas = [UNINIT_META; 1 + MAX_MULTISIG_SIGNERS];
54+
55+
unsafe {
56+
// SAFETY:
57+
// - `account_metas` is sized to 1 + MAX_MULTISIG_SIGNERS
58+
// - Index 0 is always present
59+
acc_metas
60+
.get_unchecked_mut(0)
61+
.write(AccountMeta::writable(multisig.key()));
62+
}
63+
64+
for (account_meta, signer) in acc_metas[1..].iter_mut().zip(signers.iter()) {
65+
account_meta.write(AccountMeta::readonly(signer.key()));
66+
}
67+
68+
// Instruction data layout:
69+
// - [0]: instruction discriminator (1 byte, u8)
70+
// - [1]: m (1 byte, u8)
71+
let data = &[19, m];
72+
73+
let instruction = Instruction {
74+
program_id: token_program,
75+
accounts: unsafe { slice::from_raw_parts(acc_metas.as_ptr() as _, num_accounts) },
76+
data,
77+
};
78+
79+
// Account info array
80+
const UNINIT_INFO: MaybeUninit<&AccountInfo> = MaybeUninit::uninit();
81+
let mut acc_infos = [UNINIT_INFO; 1 + MAX_MULTISIG_SIGNERS];
82+
83+
unsafe {
84+
// SAFETY:
85+
// - `account_infos` is sized to 1 + MAX_MULTISIG_SIGNERS
86+
// - Index 0 is always present
87+
acc_infos.get_unchecked_mut(0).write(multisig);
88+
}
89+
90+
// Fill signer accounts
91+
for (account_info, signer) in acc_infos[1..].iter_mut().zip(signers.iter()) {
92+
account_info.write(signer);
93+
}
94+
95+
invoke_with_bounds::<{ 1 + MAX_MULTISIG_SIGNERS }>(&instruction, unsafe {
96+
slice::from_raw_parts(acc_infos.as_ptr() as _, num_accounts)
97+
})
98+
}
99+
}

programs/token-2022/src/instructions/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ mod initialize_account_2;
99
mod initialize_account_3;
1010
mod initialize_mint;
1111
mod initialize_mint_2;
12+
mod initialize_multisig;
13+
mod initialize_multisig_2;
1214
mod mint_to;
1315
mod mint_to_checked;
1416
mod revoke;
@@ -29,6 +31,8 @@ pub use initialize_account_2::*;
2931
pub use initialize_account_3::*;
3032
pub use initialize_mint::*;
3133
pub use initialize_mint_2::*;
34+
pub use initialize_multisig::*;
35+
pub use initialize_multisig_2::*;
3236
pub use mint_to::*;
3337
pub use mint_to_checked::*;
3438
pub use revoke::*;

0 commit comments

Comments
 (0)