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#[derive(AnchorSerialize, AnchorDeserialize)]
10pub struct MultisigCreateArgs {
11    /// The authority that can configure the multisig: add/remove members, change the threshold, etc.
12    /// Should be set to `None` for autonomous multisigs.
13    pub config_authority: Option<Pubkey>,
14    /// The number of signatures required to execute a transaction.
15    pub threshold: u16,
16    /// The members of the multisig.
17    pub members: Vec<Member>,
18    /// How many seconds must pass between transaction voting, settlement, and execution.
19    pub time_lock: u32,
20    /// Memo is used for indexing only.
21    pub memo: Option<String>,
22}
23
24#[deprecated(
25    since = "0.4.0",
26    note = "This instruction is deprecated and will be removed soon. Please use `multisig_create_v2` to ensure future compatibility."
27)]
28#[derive(Accounts)]
29#[instruction(args: MultisigCreateArgs)]
30pub struct MultisigCreate<'info> {
31    #[account(
32        init,
33        payer = creator,
34        space = Multisig::size(args.members.len()),
35        seeds = [SEED_PREFIX, SEED_MULTISIG, create_key.key().as_ref()],
36        bump
37    )]
38    pub multisig: Account<'info, Multisig>,
39
40    /// An ephemeral signer that is used as a seed for the Multisig PDA.
41    /// Must be a signer to prevent front-running attack by someone else but the original creator.
42    pub create_key: Signer<'info>,
43
44    /// The creator of the multisig.
45    #[account(mut)]
46    pub creator: Signer<'info>,
47
48    pub system_program: Program<'info, System>,
49}
50
51#[allow(deprecated)]
52impl MultisigCreate<'_> {
53    fn validate(&self) -> Result<()> {
54        Ok(())
55    }
56
57    /// Creates a multisig.
58    #[allow(deprecated)]
59    #[access_control(ctx.accounts.validate())]
60    pub fn multisig_create(ctx: Context<Self>, args: MultisigCreateArgs) -> Result<()> {
61        msg!("WARNING: This instruction is deprecated and will be removed soon. Please use `multisig_create_v2` to ensure future compatibility.");
62
63        // Sort the members by pubkey.
64        let mut members = args.members;
65        members.sort_by_key(|m| m.key);
66
67        // Initialize the multisig.
68        let multisig = &mut ctx.accounts.multisig;
69        multisig.config_authority = args.config_authority.unwrap_or_default();
70        multisig.threshold = args.threshold;
71        multisig.time_lock = args.time_lock;
72        multisig.transaction_index = 0;
73        multisig.stale_transaction_index = 0;
74        multisig.create_key = ctx.accounts.create_key.key();
75        multisig.bump = ctx.bumps.multisig;
76        multisig.members = members;
77        multisig.rent_collector = None;
78
79        multisig.invariant()?;
80
81        Ok(())
82    }
83}
84
85#[derive(AnchorSerialize, AnchorDeserialize)]
86pub struct MultisigCreateArgsV2 {
87    /// The authority that can configure the multisig: add/remove members, change the threshold, etc.
88    /// Should be set to `None` for autonomous multisigs.
89    pub config_authority: Option<Pubkey>,
90    /// The number of signatures required to execute a transaction.
91    pub threshold: u16,
92    /// The members of the multisig.
93    pub members: Vec<Member>,
94    /// How many seconds must pass between transaction voting, settlement, and execution.
95    pub time_lock: u32,
96    /// The address where the rent for the accounts related to executed, rejected, or cancelled
97    /// transactions can be reclaimed. If set to `None`, the rent reclamation feature is turned off.
98    pub rent_collector: Option<Pubkey>,
99    /// Memo is used for indexing only.
100    pub memo: Option<String>,
101}
102
103#[derive(Accounts)]
104#[instruction(args: MultisigCreateArgsV2)]
105pub struct MultisigCreateV2<'info> {
106    /// Global program config account.
107    #[account(seeds = [SEED_PREFIX, SEED_PROGRAM_CONFIG], bump)]
108    pub program_config: Account<'info, ProgramConfig>,
109
110    /// The treasury where the creation fee is transferred to.
111    /// CHECK: validation is performed in the `MultisigCreate::validate()` method.
112    #[account(mut)]
113    pub treasury: AccountInfo<'info>,
114
115    #[account(
116        init,
117        payer = creator,
118        space = Multisig::size(args.members.len()),
119        seeds = [SEED_PREFIX, SEED_MULTISIG, create_key.key().as_ref()],
120        bump
121    )]
122    pub multisig: Account<'info, Multisig>,
123
124    /// An ephemeral signer that is used as a seed for the Multisig PDA.
125    /// Must be a signer to prevent front-running attack by someone else but the original creator.
126    pub create_key: Signer<'info>,
127
128    /// The creator of the multisig.
129    #[account(mut)]
130    pub creator: Signer<'info>,
131
132    pub system_program: Program<'info, System>,
133}
134
135impl MultisigCreateV2<'_> {
136    fn validate(&self) -> Result<()> {
137        //region treasury
138        require_keys_eq!(
139            self.treasury.key(),
140            self.program_config.treasury,
141            MultisigError::InvalidAccount
142        );
143        //endregion
144
145        Ok(())
146    }
147
148    /// Creates a multisig.
149    #[access_control(ctx.accounts.validate())]
150    pub fn multisig_create(ctx: Context<Self>, args: MultisigCreateArgsV2) -> Result<()> {
151        // Sort the members by pubkey.
152        let mut members = args.members;
153        members.sort_by_key(|m| m.key);
154
155        // Initialize the multisig.
156        let multisig = &mut ctx.accounts.multisig;
157        multisig.config_authority = args.config_authority.unwrap_or_default();
158        multisig.threshold = args.threshold;
159        multisig.time_lock = args.time_lock;
160        multisig.transaction_index = 0;
161        multisig.stale_transaction_index = 0;
162        multisig.create_key = ctx.accounts.create_key.key();
163        multisig.bump = ctx.bumps.multisig;
164        multisig.members = members;
165        multisig.rent_collector = args.rent_collector;
166
167        multisig.invariant()?;
168
169        let creation_fee = ctx.accounts.program_config.multisig_creation_fee;
170
171        if creation_fee > 0 {
172            system_program::transfer(
173                CpiContext::new(
174                    ctx.accounts.system_program.to_account_info(),
175                    system_program::Transfer {
176                        from: ctx.accounts.creator.to_account_info(),
177                        to: ctx.accounts.treasury.to_account_info(),
178                    },
179                ),
180                creation_fee,
181            )?;
182            msg!("Creation fee: {}", creation_fee / LAMPORTS_PER_SOL);
183        }
184
185        Ok(())
186    }
187}