multisig 0.1.0

Created with Anchor
Documentation
use anchor_lang::prelude::*;

use crate::{
    state::{
        group::Group,
        member::{GroupMember, Permissions},
    },
    utils::fractional_threshold::FractionalThreshold,
};

#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct CreateGroupInstructionArgs {
    pub group_seed: Pubkey,
    pub rent_collector: Pubkey,
    pub add_threshold: FractionalThreshold,
    pub not_add_threshold: FractionalThreshold,
    pub remove_threshold: FractionalThreshold,
    pub not_remove_threshold: FractionalThreshold,
    pub change_config_threshold: FractionalThreshold,
    pub not_change_config_threshold: FractionalThreshold,
    pub minimum_member_count: u32,
    pub minimum_vote_count: u32,
    pub max_member_weight: u32,
    /// Minimum timelock (seconds) that every proposal for this group must honour.
    pub minimum_timelock: u32,
    pub member_weights: [u32; 5],
    pub member_permissions: [Permissions; 5],
}

#[derive(Accounts)]
#[instruction(args: CreateGroupInstructionArgs)]
pub struct CreateGroupInstructionAccounts<'info> {
    #[account(
        init,
        payer = payer,
        space = 8 + Group::INIT_SPACE,
        seeds = [b"group", args.group_seed.as_ref()],
        bump
    )]
    pub group: Account<'info, Group>,

    /// 5 initial members
    pub member_1: SystemAccount<'info>,
    pub member_2: SystemAccount<'info>,
    pub member_3: SystemAccount<'info>,
    pub member_4: SystemAccount<'info>,
    pub member_5: SystemAccount<'info>,

    /// Each member has a PDA account storing their membership state
    #[account(
        init,
        payer = payer,
        space = 8 + GroupMember::INIT_SPACE,
        seeds = [b"member", group.key().as_ref(), member_1.key().as_ref()],
        bump
    )]
    pub member_account_1: Account<'info, GroupMember>,

    #[account(
        init,
        payer = payer,
        space = 8 + GroupMember::INIT_SPACE,
        seeds = [b"member", group.key().as_ref(), member_2.key().as_ref()],
        bump
    )]
    pub member_account_2: Account<'info, GroupMember>,

    #[account(
        init,
        payer = payer,
        space = 8 + GroupMember::INIT_SPACE,
        seeds = [b"member", group.key().as_ref(), member_3.key().as_ref()],
        bump
    )]
    pub member_account_3: Account<'info, GroupMember>,

    #[account(
        init,
        payer = payer,
        space = 8 + GroupMember::INIT_SPACE,
        seeds = [b"member", group.key().as_ref(), member_4.key().as_ref()],
        bump
    )]
    pub member_account_4: Account<'info, GroupMember>,

    #[account(
        init,
        payer = payer,
        space = 8 + GroupMember::INIT_SPACE,
        seeds = [b"member", group.key().as_ref(), member_5.key().as_ref()],
        bump
    )]
    pub member_account_5: Account<'info, GroupMember>,

    #[account(mut)]
    pub payer: Signer<'info>,

    pub system_program: Program<'info, System>,
}
/// Initializes a new governance group account with its initial configuration, seeds,
/// and proposal index tracking as well as other state for maintaining the multisig.
/// This instruction can be called by anyone.
pub fn create_group_handler(
    ctx: Context<CreateGroupInstructionAccounts>,
    args: CreateGroupInstructionArgs,
) -> Result<()> {
    let CreateGroupInstructionArgs {
        group_seed,
        rent_collector,
        add_threshold,
        not_add_threshold,
        remove_threshold,
        not_remove_threshold,
        change_config_threshold,
        not_change_config_threshold,
        minimum_member_count,
        minimum_vote_count,
        max_member_weight,
        minimum_timelock,
        member_weights,
        member_permissions,
    } = args;

    let group = &mut ctx.accounts.group;

    let new_group = Group::new(
        group_seed,
        rent_collector,
        add_threshold,
        not_add_threshold,
        remove_threshold,
        not_remove_threshold,
        change_config_threshold,
        not_change_config_threshold,
        minimum_member_count,
        minimum_vote_count,
        max_member_weight,
        minimum_timelock,
        5, // initial member count
        ctx.bumps.group,
    )?;
    group.set_inner(new_group);

    let member_accounts = [
        &mut ctx.accounts.member_account_1,
        &mut ctx.accounts.member_account_2,
        &mut ctx.accounts.member_account_3,
        &mut ctx.accounts.member_account_4,
        &mut ctx.accounts.member_account_5,
    ];

    let member_account_bumps = [
        ctx.bumps.member_account_1,
        ctx.bumps.member_account_2,
        ctx.bumps.member_account_3,
        ctx.bumps.member_account_4,
        ctx.bumps.member_account_5,
    ];

    let members = [
        &ctx.accounts.member_1,
        &ctx.accounts.member_2,
        &ctx.accounts.member_3,
        &ctx.accounts.member_4,
        &ctx.accounts.member_5,
    ];

    for ((((account, member), weight), permissions), bump) in member_accounts
        .into_iter()
        .zip(members.into_iter())
        .zip(member_weights.into_iter())
        .zip(member_permissions.into_iter())
        .zip(member_account_bumps.into_iter())
    {
        account.set_inner(GroupMember::new(
            member.key(),
            group.key(),
            permissions,
            weight,
            bump,
            max_member_weight,
        )?);
    }

    Ok(())
}