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 let transaction = &mut ctx.accounts.transaction;
92
93 let multisig_key = multisig.key();
94 let transaction_key = transaction.key();
95
96 let vault_seeds = &[
97 SEED_PREFIX,
98 multisig_key.as_ref(),
99 SEED_VAULT,
100 &transaction.vault_index.to_le_bytes(),
101 &[transaction.vault_bump],
102 ];
103
104 let transaction_message = &transaction.message;
105 let num_lookups = transaction_message.address_table_lookups.len();
106
107 let message_account_infos = ctx
108 .remaining_accounts
109 .get(num_lookups..)
110 .ok_or(MultisigError::InvalidNumberOfAccounts)?;
111 let address_lookup_table_account_infos = ctx
112 .remaining_accounts
113 .get(..num_lookups)
114 .ok_or(MultisigError::InvalidNumberOfAccounts)?;
115
116 let vault_pubkey = Pubkey::create_program_address(vault_seeds, ctx.program_id).unwrap();
117
118 let (ephemeral_signer_keys, ephemeral_signer_seeds) =
119 derive_ephemeral_signers(transaction_key, &transaction.ephemeral_signer_bumps);
120
121 let executable_message = ExecutableTransactionMessage::new_validated(
122 transaction_message,
123 message_account_infos,
124 address_lookup_table_account_infos,
125 &vault_pubkey,
126 &ephemeral_signer_keys,
127 )?;
128
129 let protected_accounts = &[proposal.key()];
130
131 executable_message.execute_message(
133 &vault_seeds
134 .iter()
135 .map(|seed| seed.to_vec())
136 .collect::<Vec<Vec<u8>>>(),
137 &ephemeral_signer_seeds,
138 protected_accounts,
139 )?;
140
141 proposal.status = ProposalStatus::Executed {
143 timestamp: Clock::get()?.unix_timestamp,
144 };
145
146 Ok(())
147 }
148}