Skip to main content

squads_multisig_program/instructions/
multisig_create.rs

1#![allow(deprecated)]
2use anchor_lang::prelude::*;
3use anchor_lang::system_program;
4use solana_program::native_token::LAMPORTS_PER_SOL;
5
6use crate::errors::MultisigError;
7use crate::state::*;
8
9// Dummy Account context for multisigCreate, since Anchor doesn't allow empty instructions.
10#[derive(Accounts)]
11pub struct Deprecated<'info> {
12    ///CHECK: Dummy Account
13    pub null: AccountInfo<'info>,
14}
15
16#[derive(AnchorSerialize, AnchorDeserialize)]
17pub struct MultisigCreateArgsV2 {
18    /// The authority that can configure the multisig: add/remove members, change the threshold, etc.
19    /// Should be set to `None` for autonomous multisigs.
20    pub config_authority: Option<Pubkey>,
21    /// The number of signatures required to execute a transaction.
22    pub threshold: u16,
23    /// The members of the multisig.
24    pub members: Vec<Member>,
25    /// How many seconds must pass between transaction voting, settlement, and execution.
26    pub time_lock: u32,
27    /// The address where the rent for the accounts related to executed, rejected, or cancelled
28    /// transactions can be reclaimed. If set to `None`, the rent reclamation feature is turned off.
29    pub rent_collector: Option<Pubkey>,
30    /// Memo is used for indexing only.
31    pub memo: Option<String>,
32}
33
34#[derive(Accounts)]
35#[instruction(args: MultisigCreateArgsV2)]
36pub struct MultisigCreateV2<'info> {
37    /// Global program config account.
38    #[account(seeds = [SEED_PREFIX, SEED_PROGRAM_CONFIG], bump)]
39    pub program_config: Account<'info, ProgramConfig>,
40
41    /// The treasury where the creation fee is transferred to.
42    /// CHECK: validation is performed in the `MultisigCreate::validate()` method.
43    #[account(mut)]
44    pub treasury: AccountInfo<'info>,
45
46    #[account(
47        init,
48        payer = creator,
49        space = Multisig::size(args.members.len()),
50        seeds = [SEED_PREFIX, SEED_MULTISIG, create_key.key().as_ref()],
51        bump
52    )]
53    pub multisig: Account<'info, Multisig>,
54
55    /// An ephemeral signer that is used as a seed for the Multisig PDA.
56    /// Must be a signer to prevent front-running attack by someone else but the original creator.
57    pub create_key: Signer<'info>,
58
59    /// The creator of the multisig.
60    #[account(mut)]
61    pub creator: Signer<'info>,
62
63    pub system_program: Program<'info, System>,
64}
65
66impl MultisigCreateV2<'_> {
67    fn validate(&self) -> Result<()> {
68        //region treasury
69        require_keys_eq!(
70            self.treasury.key(),
71            self.program_config.treasury,
72            MultisigError::InvalidAccount
73        );
74        //endregion
75
76        Ok(())
77    }
78
79    /// Creates a multisig.
80    #[access_control(ctx.accounts.validate())]
81    pub fn multisig_create(ctx: Context<Self>, args: MultisigCreateArgsV2) -> Result<()> {
82        // Sort the members by pubkey.
83        let mut members = args.members;
84        members.sort_by_key(|m| m.key);
85
86        // Initialize the multisig.
87        let multisig = &mut ctx.accounts.multisig;
88        multisig.config_authority = args.config_authority.unwrap_or_default();
89        multisig.threshold = args.threshold;
90        multisig.time_lock = args.time_lock;
91        multisig.transaction_index = 0;
92        multisig.stale_transaction_index = 0;
93        multisig.create_key = ctx.accounts.create_key.key();
94        multisig.bump = ctx.bumps.multisig;
95        multisig.members = members;
96        multisig.rent_collector = args.rent_collector;
97
98        multisig.invariant()?;
99
100        let creation_fee = ctx.accounts.program_config.multisig_creation_fee;
101
102        if creation_fee > 0 {
103            system_program::transfer(
104                CpiContext::new(
105                    ctx.accounts.system_program.to_account_info(),
106                    system_program::Transfer {
107                        from: ctx.accounts.creator.to_account_info(),
108                        to: ctx.accounts.treasury.to_account_info(),
109                    },
110                ),
111                creation_fee,
112            )?;
113            msg!("Creation fee: {}", creation_fee / LAMPORTS_PER_SOL);
114        }
115
116        Ok(())
117    }
118}