use {
crate::{error::TwammError, math},
ahash::AHasher,
anchor_lang::prelude::*,
std::hash::Hasher,
};
#[repr(packed)]
#[account(zero_copy)]
#[derive(Default)]
pub struct Multisig {
pub num_signers: u8,
pub num_signed: u8,
pub min_signatures: u8,
pub instruction_accounts_len: u8,
pub instruction_data_len: u16,
pub instruction_hash: u64,
pub signers: [Pubkey; 6], pub signed: [bool; 6], pub bump: u8,
}
pub enum AdminInstruction {
InitTokenPair,
SetPermissions,
SetLimits,
SetFees,
SetAdminSigners,
SetCrankAuthority,
SetOracleConfig,
SetTimeInForce,
WithdrawFees,
SetTestOraclePrice,
SetTestTime,
DeleteTestPool,
DeleteTestPair,
}
impl Multisig {
pub const MAX_SIGNERS: usize = 6;
pub const LEN: usize = 8 + std::mem::size_of::<Multisig>();
pub fn get_instruction_hash(
instruction_accounts: &[AccountInfo],
instruction_data: &[u8],
) -> u64 {
let mut hasher = AHasher::new_with_keys(697533735114380, 537268678243635);
for account in instruction_accounts {
hasher.write(account.key.as_ref());
}
if !instruction_data.is_empty() {
hasher.write(instruction_data);
}
hasher.finish()
}
pub fn get_account_infos<'info, T: ToAccountInfos<'info>>(
ctx: &Context<'_, '_, '_, 'info, T>,
) -> Vec<AccountInfo<'info>> {
let mut infos = ctx.accounts.to_account_infos();
infos.extend_from_slice(ctx.remaining_accounts);
infos
}
pub fn get_instruction_data<T: AnchorSerialize>(
instruction_type: AdminInstruction,
params: &T,
) -> Result<Vec<u8>> {
let mut res = vec![];
AnchorSerialize::serialize(¶ms, &mut res)?;
res.push(instruction_type as u8);
Ok(res)
}
pub fn set_signers(&mut self, admin_signers: &[AccountInfo], min_signatures: u8) -> Result<()> {
if admin_signers.is_empty() || min_signatures == 0 {
msg!("Error: At least one signer is required");
return Err(ProgramError::MissingRequiredSignature.into());
}
if (min_signatures as usize) > admin_signers.len() {
msg!(
"Error: Number of min signatures ({}) exceeded number of signers ({})",
min_signatures,
admin_signers.len(),
);
return Err(ProgramError::InvalidArgument.into());
}
if admin_signers.len() > Multisig::MAX_SIGNERS {
msg!(
"Error: Number of signers ({}) exceeded max ({})",
admin_signers.len(),
Multisig::MAX_SIGNERS
);
return Err(ProgramError::InvalidArgument.into());
}
let mut signers: [Pubkey; Multisig::MAX_SIGNERS] = Default::default();
let mut signed: [bool; Multisig::MAX_SIGNERS] = Default::default();
for idx in 0..admin_signers.len() {
if signers.contains(admin_signers[idx].key) {
msg!("Error: Duplicate signer {}", admin_signers[idx].key);
return Err(ProgramError::InvalidArgument.into());
}
signers[idx] = *admin_signers[idx].key;
signed[idx] = false;
}
*self = Multisig {
num_signers: admin_signers.len() as u8,
num_signed: 0,
min_signatures,
instruction_accounts_len: 0,
instruction_data_len: 0,
instruction_hash: 0,
signers,
signed,
bump: self.bump,
};
Ok(())
}
pub fn sign_multisig(
&mut self,
signer_account: &AccountInfo,
instruction_accounts: &[AccountInfo],
instruction_data: &[u8],
) -> Result<u8> {
if !signer_account.is_signer {
return Err(ProgramError::MissingRequiredSignature.into());
}
let signer_idx = if let Ok(idx) = self.get_signer_index(signer_account.key) {
idx
} else {
return err!(TwammError::MultisigAccountNotAuthorized);
};
if self.num_signers <= 1 {
return Ok(0);
}
let instruction_hash =
Multisig::get_instruction_hash(instruction_accounts, instruction_data);
if instruction_hash != self.instruction_hash
|| instruction_accounts.len() != self.instruction_accounts_len as usize
|| instruction_data.len() != self.instruction_data_len as usize
{
self.num_signed = 1;
self.instruction_accounts_len = instruction_accounts.len() as u8;
self.instruction_data_len = instruction_data.len() as u16;
self.instruction_hash = instruction_hash;
self.signed.fill(false);
self.signed[signer_idx] = true;
math::checked_sub(self.min_signatures, 1)
} else if self.signed[signer_idx] {
err!(TwammError::MultisigAlreadySigned)
} else if self.num_signed < self.min_signatures {
self.num_signed += 1;
self.signed[signer_idx] = true;
if self.num_signed == self.min_signatures {
Ok(0)
} else {
math::checked_sub(self.min_signatures, self.num_signed)
}
} else {
err!(TwammError::MultisigAlreadyExecuted)
}
}
pub fn unsign_multisig(&mut self, signer_account: &AccountInfo) -> Result<()> {
if !signer_account.is_signer {
return Err(ProgramError::MissingRequiredSignature.into());
}
if self.num_signers <= 1 || self.num_signed == 0 {
return Ok(());
}
let signer_idx = if let Ok(idx) = self.get_signer_index(signer_account.key) {
idx
} else {
return err!(TwammError::MultisigAccountNotAuthorized);
};
if !self.signed[signer_idx] {
return Ok(());
}
self.num_signed -= 1;
self.signed[signer_idx] = false;
Ok(())
}
pub fn get_signer_index(&self, signer: &Pubkey) -> Result<usize> {
for i in 0..self.num_signers as usize {
if &self.signers[i] == signer {
return Ok(i);
}
}
err!(TwammError::MultisigAccountNotAuthorized)
}
pub fn is_signer(&self, key: &Pubkey) -> Result<bool> {
Ok(self.get_signer_index(key).is_ok())
}
}