use anchor_lang::prelude::*;
use crate::errors::*;
use crate::state::*;
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct MultisigAddMemberArgs {
pub new_member: Member,
pub memo: Option<String>,
}
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct MultisigRemoveMemberArgs {
pub old_member: Pubkey,
pub memo: Option<String>,
}
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct MultisigChangeThresholdArgs {
new_threshold: u16,
pub memo: Option<String>,
}
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct MultisigSetTimeLockArgs {
time_lock: u32,
pub memo: Option<String>,
}
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct MultisigSetConfigAuthorityArgs {
config_authority: Pubkey,
pub memo: Option<String>,
}
#[derive(Accounts)]
pub struct MultisigConfig<'info> {
#[account(
mut,
seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()],
bump = multisig.bump,
)]
multisig: Account<'info, Multisig>,
pub config_authority: Signer<'info>,
#[account(mut)]
pub rent_payer: Option<Signer<'info>>,
pub system_program: Option<Program<'info, System>>,
}
impl MultisigConfig<'_> {
fn validate(&self) -> Result<()> {
require_keys_eq!(
self.config_authority.key(),
self.multisig.config_authority,
MultisigError::Unauthorized
);
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn multisig_add_member(ctx: Context<Self>, args: MultisigAddMemberArgs) -> Result<()> {
let MultisigAddMemberArgs { new_member, .. } = args;
let system_program = &ctx
.accounts
.system_program
.as_ref()
.ok_or(MultisigError::MissingAccount)?;
let rent_payer = &ctx
.accounts
.rent_payer
.as_ref()
.ok_or(MultisigError::MissingAccount)?;
let multisig = &mut ctx.accounts.multisig;
let reallocated = Multisig::realloc_if_needed(
multisig.to_account_info(),
multisig.members.len() + 1,
rent_payer.to_account_info(),
system_program.to_account_info(),
)?;
if reallocated {
multisig.reload()?;
}
multisig.add_member(new_member);
multisig.invariant()?;
multisig.invalidate_prior_transactions();
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn multisig_remove_member(
ctx: Context<Self>,
args: MultisigRemoveMemberArgs,
) -> Result<()> {
let multisig = &mut ctx.accounts.multisig;
require!(multisig.members.len() > 1, MultisigError::RemoveLastMember);
multisig.remove_member(args.old_member)?;
if usize::from(multisig.threshold) > multisig.members.len() {
multisig.threshold = multisig
.members
.len()
.try_into()
.expect("didn't expect more that `u16::MAX` members");
};
multisig.invariant()?;
multisig.invalidate_prior_transactions();
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn multisig_change_threshold(
ctx: Context<Self>,
args: MultisigChangeThresholdArgs,
) -> Result<()> {
let MultisigChangeThresholdArgs { new_threshold, .. } = args;
let multisig = &mut ctx.accounts.multisig;
multisig.threshold = new_threshold;
multisig.invariant()?;
multisig.invalidate_prior_transactions();
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn multisig_set_time_lock(ctx: Context<Self>, args: MultisigSetTimeLockArgs) -> Result<()> {
let multisig = &mut ctx.accounts.multisig;
multisig.time_lock = args.time_lock;
multisig.invariant()?;
multisig.invalidate_prior_transactions();
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn multisig_set_config_authority(
ctx: Context<Self>,
args: MultisigSetConfigAuthorityArgs,
) -> Result<()> {
let multisig = &mut ctx.accounts.multisig;
multisig.config_authority = args.config_authority;
multisig.invariant()?;
multisig.invalidate_prior_transactions();
Ok(())
}
}