squads_multisig_program/instructions/
vault_transaction_create.rs1use anchor_lang::prelude::*;
2
3use crate::errors::*;
4use crate::state::*;
5use crate::utils::*;
6
7#[derive(AnchorSerialize, AnchorDeserialize)]
8pub struct VaultTransactionCreateArgs {
9 pub vault_index: u8,
11 pub ephemeral_signers: u8,
13 pub transaction_message: Vec<u8>,
14 pub memo: Option<String>,
15}
16
17#[derive(Accounts)]
18#[instruction(args: VaultTransactionCreateArgs)]
19pub struct VaultTransactionCreate<'info> {
20 #[account(
21 mut,
22 seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()],
23 bump = multisig.bump,
24 )]
25 pub multisig: Account<'info, Multisig>,
26
27 #[account(
28 init,
29 payer = rent_payer,
30 space = VaultTransaction::size(args.ephemeral_signers, &args.transaction_message)?,
31 seeds = [
32 SEED_PREFIX,
33 multisig.key().as_ref(),
34 SEED_TRANSACTION,
35 &multisig.transaction_index.checked_add(1).unwrap().to_le_bytes(),
36 ],
37 bump
38 )]
39 pub transaction: Account<'info, VaultTransaction>,
40
41 pub creator: Signer<'info>,
43
44 #[account(mut)]
46 pub rent_payer: Signer<'info>,
47
48 pub system_program: Program<'info, System>,
49}
50
51impl VaultTransactionCreate<'_> {
52 fn validate(&self) -> Result<()> {
53 let Self {
54 multisig, creator, ..
55 } = self;
56
57 require!(
59 multisig.is_member(creator.key()).is_some(),
60 MultisigError::NotAMember
61 );
62 require!(
63 multisig.member_has_permission(creator.key(), Permission::Initiate),
64 MultisigError::Unauthorized
65 );
66
67 Ok(())
68 }
69
70 #[access_control(ctx.accounts.validate())]
72 pub fn vault_transaction_create(
73 ctx: Context<Self>,
74 args: VaultTransactionCreateArgs,
75 ) -> Result<()> {
76 let multisig = &mut ctx.accounts.multisig;
77 let transaction = &mut ctx.accounts.transaction;
78 let creator = &mut ctx.accounts.creator;
79
80 let transaction_message =
81 TransactionMessage::deserialize(&mut args.transaction_message.as_slice())?;
82
83 let multisig_key = multisig.key();
84 let transaction_key = transaction.key();
85
86 let vault_seeds = &[
87 SEED_PREFIX,
88 multisig_key.as_ref(),
89 SEED_VAULT,
90 &args.vault_index.to_le_bytes(),
91 ];
92 let (_, vault_bump) = Pubkey::find_program_address(vault_seeds, ctx.program_id);
93
94 let ephemeral_signer_bumps: Vec<u8> = (0..args.ephemeral_signers)
95 .map(|ephemeral_signer_index| {
96 let ephemeral_signer_seeds = &[
97 SEED_PREFIX,
98 transaction_key.as_ref(),
99 SEED_EPHEMERAL_SIGNER,
100 &ephemeral_signer_index.to_le_bytes(),
101 ];
102
103 let (_, bump) =
104 Pubkey::find_program_address(ephemeral_signer_seeds, ctx.program_id);
105 bump
106 })
107 .collect();
108
109 let transaction_index = multisig.transaction_index.checked_add(1).unwrap();
111
112 transaction.multisig = multisig_key;
114 transaction.creator = creator.key();
115 transaction.index = transaction_index;
116 transaction.bump = ctx.bumps.transaction;
117 transaction.vault_index = args.vault_index;
118 transaction.vault_bump = vault_bump;
119 transaction.ephemeral_signer_bumps = ephemeral_signer_bumps;
120 transaction.message = transaction_message.try_into()?;
121
122 multisig.transaction_index = transaction_index;
124
125 multisig.invariant()?;
126
127 msg!("transaction index: {}", transaction_index);
129
130 Ok(())
131 }
132}
133
134#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
136pub struct TransactionMessage {
137 pub num_signers: u8,
139 pub num_writable_signers: u8,
141 pub num_writable_non_signers: u8,
143 pub account_keys: SmallVec<u8, Pubkey>,
145 pub instructions: SmallVec<u8, CompiledInstruction>,
147 pub address_table_lookups: SmallVec<u8, MessageAddressTableLookup>,
150}
151
152#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
154pub struct CompiledInstruction {
155 pub program_id_index: u8,
156 pub account_indexes: SmallVec<u8, u8>,
158 pub data: SmallVec<u16, u8>,
160}
161
162#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
165pub struct MessageAddressTableLookup {
166 pub account_key: Pubkey,
168 pub writable_indexes: SmallVec<u8, u8>,
170 pub readonly_indexes: SmallVec<u8, u8>,
172}