squads_multisig_program/instructions/
vault_transaction_execute.rs1use anchor_lang::prelude::*;
2
3use crate::errors::*;
4use crate::state::*;
5use crate::utils::*;
6
7#[derive(Accounts)]
8pub struct VaultTransactionExecute<'info> {
9 #[account(
10 seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()],
11 bump = multisig.bump,
12 )]
13 pub multisig: Box<Account<'info, Multisig>>,
14
15 #[account(
17 mut,
18 seeds = [
19 SEED_PREFIX,
20 multisig.key().as_ref(),
21 SEED_TRANSACTION,
22 &transaction.index.to_le_bytes(),
23 SEED_PROPOSAL,
24 ],
25 bump = proposal.bump,
26 )]
27 pub proposal: Account<'info, Proposal>,
28
29 #[account(
31 seeds = [
32 SEED_PREFIX,
33 multisig.key().as_ref(),
34 SEED_TRANSACTION,
35 &transaction.index.to_le_bytes(),
36 ],
37 bump = transaction.bump,
38 )]
39 pub transaction: Account<'info, VaultTransaction>,
40
41 pub member: Signer<'info>,
42 }
47
48impl VaultTransactionExecute<'_> {
49 fn validate(&self) -> Result<()> {
50 let Self {
51 multisig,
52 proposal,
53 member,
54 ..
55 } = self;
56
57 require!(
59 multisig.is_member(member.key()).is_some(),
60 MultisigError::NotAMember
61 );
62 require!(
63 multisig.member_has_permission(member.key(), Permission::Execute),
64 MultisigError::Unauthorized
65 );
66
67 match proposal.status {
69 ProposalStatus::Approved { timestamp } => {
70 require!(
71 Clock::get()?.unix_timestamp - timestamp >= i64::from(multisig.time_lock),
72 MultisigError::TimeLockNotReleased
73 );
74 }
75 _ => return err!(MultisigError::InvalidProposalStatus),
76 }
77 Ok(())
83 }
84
85 #[access_control(ctx.accounts.validate())]
88 pub fn vault_transaction_execute(ctx: Context<Self>) -> Result<()> {
89 let multisig = &mut ctx.accounts.multisig;
90 let proposal = &mut ctx.accounts.proposal;
91
92 let transaction = ctx.accounts.transaction.take();
97
98 let multisig_key = multisig.key();
99 let transaction_key = ctx.accounts.transaction.key();
100
101 let vault_seeds = &[
102 SEED_PREFIX,
103 multisig_key.as_ref(),
104 SEED_VAULT,
105 &transaction.vault_index.to_le_bytes(),
106 &[transaction.vault_bump],
107 ];
108
109 let transaction_message = transaction.message;
110 let num_lookups = transaction_message.address_table_lookups.len();
111
112 let message_account_infos = ctx
113 .remaining_accounts
114 .get(num_lookups..)
115 .ok_or(MultisigError::InvalidNumberOfAccounts)?;
116 let address_lookup_table_account_infos = ctx
117 .remaining_accounts
118 .get(..num_lookups)
119 .ok_or(MultisigError::InvalidNumberOfAccounts)?;
120
121 let vault_pubkey = Pubkey::create_program_address(vault_seeds, ctx.program_id).unwrap();
122
123 let (ephemeral_signer_keys, ephemeral_signer_seeds) =
124 derive_ephemeral_signers(transaction_key, &transaction.ephemeral_signer_bumps);
125
126 let executable_message = ExecutableTransactionMessage::new_validated(
127 transaction_message,
128 message_account_infos,
129 address_lookup_table_account_infos,
130 &vault_pubkey,
131 &ephemeral_signer_keys,
132 )?;
133
134 let protected_accounts = &[proposal.key()];
135
136 executable_message.execute_message(
143 vault_seeds,
144 &ephemeral_signer_seeds,
145 protected_accounts,
146 )?;
147
148 proposal.status = ProposalStatus::Executed {
150 timestamp: Clock::get()?.unix_timestamp,
151 };
152
153 Ok(())
154 }
155}