Skip to main content

squads_multisig_program/instructions/
vault_transaction_create.rs

1use anchor_lang::prelude::*;
2use anchor_lang::system_program;
3
4use crate::errors::*;
5use crate::state::*;
6use crate::utils::*;
7
8#[derive(AnchorSerialize, AnchorDeserialize)]
9pub struct VaultTransactionCreateArgs {
10    /// Index of the vault this transaction belongs to.
11    pub vault_index: u8,
12    /// Number of ephemeral signing PDAs required by the transaction.
13    pub ephemeral_signers: u8,
14    pub transaction_message: Vec<u8>,
15    pub memo: Option<String>,
16}
17
18#[derive(Accounts)]
19#[instruction(args: VaultTransactionCreateArgs)]
20pub struct VaultTransactionCreate<'info> {
21    #[account(
22        mut,
23        seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()],
24        bump = multisig.bump,
25    )]
26    pub multisig: Account<'info, Multisig>,
27
28    #[account(
29        init,
30        payer = rent_payer,
31        space = VaultTransaction::size(args.ephemeral_signers, &args.transaction_message)?,
32        seeds = [
33            SEED_PREFIX,
34            multisig.key().as_ref(),
35            SEED_TRANSACTION,
36            &multisig.transaction_index.checked_add(1).unwrap().to_le_bytes(),
37        ],
38        bump
39    )]
40    pub transaction: Account<'info, VaultTransaction>,
41
42    /// The member of the multisig that is creating the transaction.
43    pub creator: Signer<'info>,
44
45    /// The payer for the transaction account rent.
46    #[account(mut)]
47    pub rent_payer: Signer<'info>,
48
49    pub system_program: Program<'info, System>,
50}
51
52impl<'info> VaultTransactionCreate<'info> {
53    pub fn validate(&self) -> Result<()> {
54        let Self {
55            multisig, creator, ..
56        } = self;
57
58        // creator
59        require!(
60            multisig.is_member(creator.key()).is_some(),
61            MultisigError::NotAMember
62        );
63        require!(
64            multisig.member_has_permission(creator.key(), Permission::Initiate),
65            MultisigError::Unauthorized
66        );
67
68        Ok(())
69    }
70
71    /// Create a new vault transaction.
72    #[access_control(ctx.accounts.validate())]
73    pub fn vault_transaction_create(
74        ctx: Context<Self>,
75        args: VaultTransactionCreateArgs,
76    ) -> Result<()> {
77        let multisig = &mut ctx.accounts.multisig;
78        let transaction = &mut ctx.accounts.transaction;
79        let creator = &mut ctx.accounts.creator;
80
81        let transaction_message =
82            TransactionMessage::deserialize(&mut args.transaction_message.as_slice())?;
83
84        let multisig_key = multisig.key();
85        let transaction_key = transaction.key();
86
87        let vault_seeds = &[
88            SEED_PREFIX,
89            multisig_key.as_ref(),
90            SEED_VAULT,
91            &args.vault_index.to_le_bytes(),
92        ];
93        let (_, vault_bump) = Pubkey::find_program_address(vault_seeds, ctx.program_id);
94
95        let ephemeral_signer_bumps: Vec<u8> = (0..args.ephemeral_signers)
96            .map(|ephemeral_signer_index| {
97                let ephemeral_signer_seeds = &[
98                    SEED_PREFIX,
99                    transaction_key.as_ref(),
100                    SEED_EPHEMERAL_SIGNER,
101                    &ephemeral_signer_index.to_le_bytes(),
102                ];
103
104                let (_, bump) =
105                    Pubkey::find_program_address(ephemeral_signer_seeds, ctx.program_id);
106                bump
107            })
108            .collect();
109
110        // Increment the transaction index.
111        let transaction_index = multisig.transaction_index.checked_add(1).unwrap();
112
113        // Initialize the transaction fields.
114        transaction.multisig = multisig_key;
115        transaction.creator = creator.key();
116        transaction.index = transaction_index;
117        transaction.bump = ctx.bumps.transaction;
118        transaction.vault_index = args.vault_index;
119        transaction.vault_bump = vault_bump;
120        transaction.ephemeral_signer_bumps = ephemeral_signer_bumps;
121        transaction.message = transaction_message.try_into()?;
122
123        // Updated last transaction index in the multisig account.
124        multisig.transaction_index = transaction_index;
125
126        multisig.invariant()?;
127
128        // Logs for indexing.
129        msg!("transaction index: {}", transaction_index);
130
131        Ok(())
132    }
133}
134
135/// Unvalidated instruction data, must be treated as untrusted.
136#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
137pub struct TransactionMessage {
138    /// The number of signer pubkeys in the account_keys vec.
139    pub num_signers: u8,
140    /// The number of writable signer pubkeys in the account_keys vec.
141    pub num_writable_signers: u8,
142    /// The number of writable non-signer pubkeys in the account_keys vec.
143    pub num_writable_non_signers: u8,
144    /// The list of unique account public keys (including program IDs) that will be used in the provided instructions.
145    pub account_keys: SmallVec<u8, Pubkey>,
146    /// The list of instructions to execute.
147    pub instructions: SmallVec<u8, CompiledInstruction>,
148    /// List of address table lookups used to load additional accounts
149    /// for this transaction.
150    pub address_table_lookups: SmallVec<u8, MessageAddressTableLookup>,
151}
152
153// Concise serialization schema for instructions that make up transaction.
154#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
155pub struct CompiledInstruction {
156    pub program_id_index: u8,
157    /// Indices into the tx's `account_keys` list indicating which accounts to pass to the instruction.
158    pub account_indexes: SmallVec<u8, u8>,
159    /// Instruction data.
160    pub data: SmallVec<u16, u8>,
161}
162
163/// Address table lookups describe an on-chain address lookup table to use
164/// for loading more readonly and writable accounts in a single tx.
165#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
166pub struct MessageAddressTableLookup {
167    /// Address lookup table account key
168    pub account_key: Pubkey,
169    /// List of indexes used to load writable account addresses
170    pub writable_indexes: SmallVec<u8, u8>,
171    /// List of indexes used to load readonly account addresses
172    pub readonly_indexes: SmallVec<u8, u8>,
173}