From 98ee4417332fe08a95db0cfa1c079aec90c8ccd3 Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Sun, 29 Jun 2025 11:34:10 +0530 Subject: [PATCH 1/2] add init multisig ix --- .../src/instructions/initialize_multisig.rs | 85 +++++++++++++++++++ .../src/instructions/initialize_multisig_2.rs | 83 ++++++++++++++++++ programs/token/src/instructions/mod.rs | 4 + 3 files changed, 172 insertions(+) create mode 100644 programs/token/src/instructions/initialize_multisig.rs create mode 100644 programs/token/src/instructions/initialize_multisig_2.rs diff --git a/programs/token/src/instructions/initialize_multisig.rs b/programs/token/src/instructions/initialize_multisig.rs new file mode 100644 index 00000000..2cada8e3 --- /dev/null +++ b/programs/token/src/instructions/initialize_multisig.rs @@ -0,0 +1,85 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + program_error::ProgramError, + ProgramResult, +}; + +extern crate alloc; + +use alloc::vec::Vec; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Initialize a new Multisig. +/// +/// ### Accounts: +/// 0. `[writable]` The multisig account to initialize. +/// 1. `[]` Rent sysvar +/// 2. ..`2+N`. `[]` The signer accounts, must equal to N where `1 <= N <= +/// 11`. +pub struct InitializeMultisig<'a> { + /// Multisig Account. + pub multisig: &'a AccountInfo, + /// Rent sysvar Account. + pub rent_sysvar: &'a AccountInfo, + /// Signer Accounts + pub multisig_signers: Vec<&'a AccountInfo>, + /// The number of signers (M) required to validate this multisignature + /// account. + pub m: u8, +} + +impl InitializeMultisig<'_> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed::(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + if ACCOUNTS != self.multisig_signers.len() + 2 { + return Err(ProgramError::InvalidArgument); + } + + // Account metadata + let mut account_metas = Vec::with_capacity(1 + self.multisig_signers.len()); + account_metas.push(AccountMeta::writable(self.multisig.key())); + + account_metas.extend( + self.multisig_signers + .iter() + .map(|a| AccountMeta::readonly(a.key())), + ); + + // Instruction data layout: + // - [0]: instruction discriminator (1 byte, u8) + // - [1]: m (1 byte, u8) + let mut instruction_data = [UNINIT_BYTE; 2]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[2]); + // Set number of signers (m) at offset 1 + write_bytes(&mut instruction_data[1..2], &[self.m]); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: account_metas.as_slice(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 2) }, + }; + + let mut account_infos = Vec::with_capacity(2 + self.multisig_signers.len()); + + account_infos.push(self.multisig); + + account_infos.extend_from_slice(self.multisig_signers.as_slice()); + + let account_infos: [&AccountInfo; ACCOUNTS] = account_infos + .try_into() + .map_err(|_| ProgramError::InvalidArgument)?; + + invoke_signed(&instruction, &account_infos, signers) + } +} diff --git a/programs/token/src/instructions/initialize_multisig_2.rs b/programs/token/src/instructions/initialize_multisig_2.rs new file mode 100644 index 00000000..838ee68b --- /dev/null +++ b/programs/token/src/instructions/initialize_multisig_2.rs @@ -0,0 +1,83 @@ +use core::slice::from_raw_parts; + +use pinocchio::{ + account_info::AccountInfo, + instruction::{AccountMeta, Instruction, Signer}, + program::invoke_signed, + program_error::ProgramError, + ProgramResult, +}; + +extern crate alloc; + +use alloc::vec::Vec; + +use crate::{write_bytes, UNINIT_BYTE}; + +/// Initialize a new Multisig. +/// +/// ### Accounts: +/// 0. `[writable]` The multisig account to initialize. +/// 1. `[]` Rent sysvar +/// 2. ..`2+N`. `[]` The signer accounts, must equal to N where `1 <= N <= +/// 11`. +pub struct InitializeMultisig2<'a> { + /// Multisig Account. + pub multisig: &'a AccountInfo, + /// Signer Accounts + pub multisig_signers: Vec<&'a AccountInfo>, + /// The number of signers (M) required to validate this multisignature + /// account. + pub m: u8, +} + +impl InitializeMultisig2<'_> { + #[inline(always)] + pub fn invoke(&self) -> ProgramResult { + self.invoke_signed::(&[]) + } + + pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { + if ACCOUNTS != self.multisig_signers.len() + 1 { + return Err(ProgramError::InvalidArgument); + } + + // Account metadata + let mut account_metas = Vec::with_capacity(1 + self.multisig_signers.len()); + account_metas.push(AccountMeta::writable(self.multisig.key())); + + account_metas.extend( + self.multisig_signers + .iter() + .map(|a| AccountMeta::readonly(a.key())), + ); + + // Instruction data layout: + // - [0]: instruction discriminator (1 byte, u8) + // - [1]: m (1 byte, u8) + let mut instruction_data = [UNINIT_BYTE; 2]; + + // Set discriminator as u8 at offset [0] + write_bytes(&mut instruction_data, &[2]); + // Set number of signers (m) at offset 1 + write_bytes(&mut instruction_data[1..2], &[self.m]); + + let instruction = Instruction { + program_id: &crate::ID, + accounts: account_metas.as_slice(), + data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 2) }, + }; + + let mut account_infos = Vec::with_capacity(1 + self.multisig_signers.len()); + + account_infos.push(self.multisig); + + account_infos.extend_from_slice(self.multisig_signers.as_slice()); + + let account_infos: [&AccountInfo; ACCOUNTS] = account_infos + .try_into() + .map_err(|_| ProgramError::InvalidArgument)?; + + invoke_signed(&instruction, &account_infos, signers) + } +} diff --git a/programs/token/src/instructions/mod.rs b/programs/token/src/instructions/mod.rs index 9287f8f9..dd2e0d1f 100644 --- a/programs/token/src/instructions/mod.rs +++ b/programs/token/src/instructions/mod.rs @@ -9,6 +9,8 @@ mod initialize_account_2; mod initialize_account_3; mod initialize_mint; mod initialize_mint_2; +mod initialize_multisig; +mod initialize_multisig_2; mod mint_to; mod mint_to_checked; mod revoke; @@ -29,6 +31,8 @@ pub use initialize_account_2::*; pub use initialize_account_3::*; pub use initialize_mint::*; pub use initialize_mint_2::*; +pub use initialize_multisig::*; +pub use initialize_multisig_2::*; pub use mint_to::*; pub use mint_to_checked::*; pub use revoke::*; From 15542d4f95389123296dadbd31657eebc0439b8c Mon Sep 17 00:00:00 2001 From: NagaprasadVr Date: Tue, 15 Jul 2025 08:58:55 +0530 Subject: [PATCH 2/2] chore : resolve comments and refactor --- .../src/instructions/initialize_multisig.rs | 102 ++++++++++-------- .../src/instructions/initialize_multisig_2.rs | 97 +++++++++-------- 2 files changed, 114 insertions(+), 85 deletions(-) diff --git a/programs/token/src/instructions/initialize_multisig.rs b/programs/token/src/instructions/initialize_multisig.rs index 2cada8e3..4eb62b82 100644 --- a/programs/token/src/instructions/initialize_multisig.rs +++ b/programs/token/src/instructions/initialize_multisig.rs @@ -1,85 +1,103 @@ -use core::slice::from_raw_parts; +use core::{mem::MaybeUninit, slice}; use pinocchio::{ account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, + cpi::invoke_with_bounds, + instruction::{AccountMeta, Instruction}, program_error::ProgramError, ProgramResult, }; -extern crate alloc; - -use alloc::vec::Vec; - -use crate::{write_bytes, UNINIT_BYTE}; +/// Maximum number of multisignature signers. +pub const MAX_MULTISIG_SIGNERS: usize = 11; /// Initialize a new Multisig. /// /// ### Accounts: /// 0. `[writable]` The multisig account to initialize. /// 1. `[]` Rent sysvar -/// 2. ..`2+N`. `[]` The signer accounts, must equal to N where `1 <= N <= -/// 11`. -pub struct InitializeMultisig<'a> { +/// 2. ..`2+N`. `[]` The N signer accounts, where N is between 1 and 11. +pub struct InitializeMultisig<'a, 'b> +where + 'a: 'b, +{ /// Multisig Account. pub multisig: &'a AccountInfo, /// Rent sysvar Account. pub rent_sysvar: &'a AccountInfo, /// Signer Accounts - pub multisig_signers: Vec<&'a AccountInfo>, + pub signers: &'b [&'a AccountInfo], /// The number of signers (M) required to validate this multisignature /// account. pub m: u8, } -impl InitializeMultisig<'_> { +impl InitializeMultisig<'_, '_> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed::(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - if ACCOUNTS != self.multisig_signers.len() + 2 { + pub fn invoke(&self) -> ProgramResult { + let &Self { + multisig, + rent_sysvar, + signers, + m, + } = self; + + if signers.len() > MAX_MULTISIG_SIGNERS { return Err(ProgramError::InvalidArgument); } + let num_accounts = 2 + signers.len(); + // Account metadata - let mut account_metas = Vec::with_capacity(1 + self.multisig_signers.len()); - account_metas.push(AccountMeta::writable(self.multisig.key())); + const UNINIT_META: MaybeUninit = MaybeUninit::::uninit(); + let mut acc_metas = [UNINIT_META; 2 + MAX_MULTISIG_SIGNERS]; + + unsafe { + // SAFETY: + // - `account_metas` is sized to 2 + MAX_MULTISIG_SIGNERS + // - Index 0 and 1 are always present + acc_metas + .get_unchecked_mut(0) + .write(AccountMeta::writable(multisig.key())); + acc_metas + .get_unchecked_mut(1) + .write(AccountMeta::readonly(rent_sysvar.key())); + } - account_metas.extend( - self.multisig_signers - .iter() - .map(|a| AccountMeta::readonly(a.key())), - ); + for (account_meta, signer) in acc_metas[2..].iter_mut().zip(signers.iter()) { + account_meta.write(AccountMeta::readonly(signer.key())); + } // Instruction data layout: // - [0]: instruction discriminator (1 byte, u8) // - [1]: m (1 byte, u8) - let mut instruction_data = [UNINIT_BYTE; 2]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[2]); - // Set number of signers (m) at offset 1 - write_bytes(&mut instruction_data[1..2], &[self.m]); + let data = &[2, m]; let instruction = Instruction { program_id: &crate::ID, - accounts: account_metas.as_slice(), - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 2) }, + accounts: unsafe { slice::from_raw_parts(acc_metas.as_ptr() as _, num_accounts) }, + data, }; - let mut account_infos = Vec::with_capacity(2 + self.multisig_signers.len()); - - account_infos.push(self.multisig); + // Account info array + const UNINIT_INFO: MaybeUninit<&AccountInfo> = MaybeUninit::uninit(); + let mut acc_infos = [UNINIT_INFO; 2 + MAX_MULTISIG_SIGNERS]; - account_infos.extend_from_slice(self.multisig_signers.as_slice()); + unsafe { + // SAFETY: + // - `account_infos` is sized to 2 + MAX_MULTISIG_SIGNERS + // - Index 0 and 1 are always present + acc_infos.get_unchecked_mut(0).write(multisig); + acc_infos.get_unchecked_mut(1).write(rent_sysvar); + } - let account_infos: [&AccountInfo; ACCOUNTS] = account_infos - .try_into() - .map_err(|_| ProgramError::InvalidArgument)?; + // Fill signer accounts + for (account_info, signer) in acc_infos[2..].iter_mut().zip(signers.iter()) { + account_info.write(signer); + } - invoke_signed(&instruction, &account_infos, signers) + invoke_with_bounds::<{ 2 + MAX_MULTISIG_SIGNERS }>(&instruction, unsafe { + slice::from_raw_parts(acc_infos.as_ptr() as _, num_accounts) + }) } } diff --git a/programs/token/src/instructions/initialize_multisig_2.rs b/programs/token/src/instructions/initialize_multisig_2.rs index 838ee68b..e56de3ac 100644 --- a/programs/token/src/instructions/initialize_multisig_2.rs +++ b/programs/token/src/instructions/initialize_multisig_2.rs @@ -1,83 +1,94 @@ -use core::slice::from_raw_parts; +use core::{mem::MaybeUninit, slice}; use pinocchio::{ account_info::AccountInfo, - instruction::{AccountMeta, Instruction, Signer}, - program::invoke_signed, + cpi::invoke_with_bounds, + instruction::{AccountMeta, Instruction}, program_error::ProgramError, ProgramResult, }; -extern crate alloc; - -use alloc::vec::Vec; - -use crate::{write_bytes, UNINIT_BYTE}; +use crate::instructions::MAX_MULTISIG_SIGNERS; /// Initialize a new Multisig. /// /// ### Accounts: /// 0. `[writable]` The multisig account to initialize. -/// 1. `[]` Rent sysvar -/// 2. ..`2+N`. `[]` The signer accounts, must equal to N where `1 <= N <= -/// 11`. -pub struct InitializeMultisig2<'a> { +/// 1. ..`1+N`. `[]` The N signer accounts, where N is between 1 and 11. +pub struct InitializeMultisig2<'a, 'b> +where + 'a: 'b, +{ /// Multisig Account. pub multisig: &'a AccountInfo, /// Signer Accounts - pub multisig_signers: Vec<&'a AccountInfo>, + pub signers: &'b [&'a AccountInfo], /// The number of signers (M) required to validate this multisignature /// account. pub m: u8, } -impl InitializeMultisig2<'_> { +impl InitializeMultisig2<'_, '_> { #[inline(always)] - pub fn invoke(&self) -> ProgramResult { - self.invoke_signed::(&[]) - } - - pub fn invoke_signed(&self, signers: &[Signer]) -> ProgramResult { - if ACCOUNTS != self.multisig_signers.len() + 1 { + pub fn invoke(&self) -> ProgramResult { + let &Self { + multisig, + signers, + m, + } = self; + + if signers.len() > MAX_MULTISIG_SIGNERS { return Err(ProgramError::InvalidArgument); } + let num_accounts = 1 + signers.len(); + // Account metadata - let mut account_metas = Vec::with_capacity(1 + self.multisig_signers.len()); - account_metas.push(AccountMeta::writable(self.multisig.key())); + const UNINIT_META: MaybeUninit = MaybeUninit::::uninit(); + let mut acc_metas = [UNINIT_META; 1 + MAX_MULTISIG_SIGNERS]; + + unsafe { + // SAFETY: + // - `account_metas` is sized to 1 + MAX_MULTISIG_SIGNERS + // - Index 0 is always present + acc_metas + .get_unchecked_mut(0) + .write(AccountMeta::writable(multisig.key())); + } - account_metas.extend( - self.multisig_signers - .iter() - .map(|a| AccountMeta::readonly(a.key())), - ); + for (account_meta, signer) in acc_metas[1..].iter_mut().zip(signers.iter()) { + account_meta.write(AccountMeta::readonly(signer.key())); + } // Instruction data layout: // - [0]: instruction discriminator (1 byte, u8) // - [1]: m (1 byte, u8) - let mut instruction_data = [UNINIT_BYTE; 2]; - - // Set discriminator as u8 at offset [0] - write_bytes(&mut instruction_data, &[2]); - // Set number of signers (m) at offset 1 - write_bytes(&mut instruction_data[1..2], &[self.m]); + let data = &[19, m]; let instruction = Instruction { program_id: &crate::ID, - accounts: account_metas.as_slice(), - data: unsafe { from_raw_parts(instruction_data.as_ptr() as _, 2) }, + accounts: unsafe { slice::from_raw_parts(acc_metas.as_ptr() as _, num_accounts) }, + data, }; - let mut account_infos = Vec::with_capacity(1 + self.multisig_signers.len()); - - account_infos.push(self.multisig); + // Account info array + const UNINIT_INFO: MaybeUninit<&AccountInfo> = MaybeUninit::uninit(); + let mut acc_infos = [UNINIT_INFO; 1 + MAX_MULTISIG_SIGNERS]; - account_infos.extend_from_slice(self.multisig_signers.as_slice()); + unsafe { + // SAFETY: + // - `account_infos` is sized to 1 + MAX_MULTISIG_SIGNERS + // - Index 0 is always present + acc_infos.get_unchecked_mut(0).write(multisig); + } - let account_infos: [&AccountInfo; ACCOUNTS] = account_infos - .try_into() - .map_err(|_| ProgramError::InvalidArgument)?; + // Fill signer accounts + for (account_info, signer) in acc_infos[1..].iter_mut().zip(signers.iter()) { + account_info.write(signer); + } - invoke_signed(&instruction, &account_infos, signers) + invoke_with_bounds::<{ 1 + MAX_MULTISIG_SIGNERS }>(&instruction, unsafe { + slice::from_raw_parts(acc_infos.as_ptr() as _, num_accounts) + }) } }