Skip to main content

squads_multisig_program/instructions/
vault_transaction_create_from_buffer.rs

1use crate::errors::*;
2use crate::instructions::*;
3use crate::state::*;
4use anchor_lang::{prelude::*, system_program};
5
6#[derive(Accounts)]
7pub struct VaultTransactionCreateFromBuffer<'info> {
8    // The context needed for the VaultTransactionCreate instruction
9    pub vault_transaction_create: VaultTransactionCreate<'info>,
10
11    #[account(
12        mut,
13        close = creator,
14        // Only the creator can turn the buffer into a transaction and reclaim
15        // the rent
16        constraint = transaction_buffer.creator == creator.key() @ MultisigError::Unauthorized,
17        seeds = [
18            SEED_PREFIX,
19            vault_transaction_create.multisig.key().as_ref(),
20            SEED_TRANSACTION_BUFFER,
21            creator.key().as_ref(),
22            &transaction_buffer.buffer_index.to_le_bytes(),
23        ],
24        bump
25    )]
26    pub transaction_buffer: Box<Account<'info, TransactionBuffer>>,
27
28    // Anchor doesn't allow us to use the creator inside of
29    // vault_transaction_create, so we just re-pass it here with the same constraint
30    #[account(
31        mut,
32        address = vault_transaction_create.creator.key(),
33    )]
34    pub creator: Signer<'info>,
35}
36
37impl<'info> VaultTransactionCreateFromBuffer<'info> {
38    pub fn validate(&self, args: &VaultTransactionCreateArgs) -> Result<()> {
39        let transaction_buffer_account = &self.transaction_buffer;
40
41        // Check that the transaction message is "empty"
42        require!(
43            args.transaction_message == vec![0, 0, 0, 0, 0, 0],
44            MultisigError::InvalidInstructionArgs
45        );
46
47        // Validate that the final hash matches the buffer
48        transaction_buffer_account.validate_hash()?;
49
50        // Validate that the final size is correct
51        transaction_buffer_account.validate_size()?;
52        Ok(())
53    }
54    /// Create a new vault transaction from a completed transaction buffer account.
55    #[access_control(ctx.accounts.validate(&args))]
56    pub fn vault_transaction_create_from_buffer(
57        ctx: Context<'_, '_, 'info, 'info, Self>,
58        args: VaultTransactionCreateArgs,
59    ) -> Result<()> {
60        // Account infos necessary for reallocation
61        let vault_transaction_account_info = &ctx
62            .accounts
63            .vault_transaction_create
64            .transaction
65            .to_account_info();
66        let rent_payer_account_info = &ctx
67            .accounts
68            .vault_transaction_create
69            .rent_payer
70            .to_account_info();
71
72        let system_program = &ctx.accounts.vault_transaction_create.system_program.to_account_info();
73
74        // Read-only accounts
75        let transaction_buffer = &ctx.accounts.transaction_buffer;
76
77        // Calculate the new required length of the vault transaction account,
78        // since it was initialized with an empty transaction message
79        let new_len =
80            VaultTransaction::size(args.ephemeral_signers, transaction_buffer.buffer.as_slice())?;
81
82        // Calculate the rent exemption for new length
83        let rent_exempt_lamports = Rent::get().unwrap().minimum_balance(new_len).max(1);
84
85        // Check the difference between the rent exemption and the current lamports
86        let top_up_lamports =
87            rent_exempt_lamports.saturating_sub(vault_transaction_account_info.lamports());
88
89        // System Transfer the remaining difference to the vault transaction account
90        let transfer_context = CpiContext::new(
91            system_program.to_account_info(),
92            system_program::Transfer {
93                from: rent_payer_account_info.clone(),
94                to: vault_transaction_account_info.clone(),
95            },
96        );
97        system_program::transfer(transfer_context, top_up_lamports)?;
98
99        // Reallocate the vault transaction account to the new length of the
100        // actual transaction message
101        AccountInfo::realloc(&vault_transaction_account_info, new_len, true)?;
102
103        // Create the args for the vault transaction create instruction
104        let create_args = VaultTransactionCreateArgs {
105            vault_index: args.vault_index,
106            ephemeral_signers: args.ephemeral_signers,
107            transaction_message: transaction_buffer.buffer.clone(),
108            memo: args.memo,
109        };
110        // Create the context for the vault transaction create instruction
111        let context = Context::new(
112            ctx.program_id,
113            &mut ctx.accounts.vault_transaction_create,
114            ctx.remaining_accounts,
115            ctx.bumps.vault_transaction_create,
116        );
117
118        // Call the vault transaction create instruction
119        VaultTransactionCreate::vault_transaction_create(context, create_args)?;
120
121        Ok(())
122    }
123}