use anchor_lang::prelude::*;
use crate::errors::*;
use crate::state::*;
use crate::TransactionMessage;
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct BatchAddTransactionArgs {
pub ephemeral_signers: u8,
pub transaction_message: Vec<u8>,
}
#[derive(Accounts)]
#[instruction(args: BatchAddTransactionArgs)]
pub struct BatchAddTransaction<'info> {
#[account(
seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()],
bump = multisig.bump,
)]
pub multisig: Account<'info, Multisig>,
#[account(
seeds = [
SEED_PREFIX,
multisig.key().as_ref(),
SEED_TRANSACTION,
&batch.index.to_le_bytes(),
SEED_PROPOSAL,
],
bump = proposal.bump,
)]
pub proposal: Account<'info, Proposal>,
#[account(
mut,
seeds = [
SEED_PREFIX,
multisig.key().as_ref(),
SEED_TRANSACTION,
&batch.index.to_le_bytes(),
],
bump = batch.bump,
)]
pub batch: Account<'info, Batch>,
#[account(
init,
payer = rent_payer,
space = VaultBatchTransaction::size(args.ephemeral_signers, &args.transaction_message)?,
seeds = [
SEED_PREFIX,
multisig.key().as_ref(),
SEED_TRANSACTION,
&batch.index.to_le_bytes(),
SEED_BATCH_TRANSACTION,
&batch.size.checked_add(1).unwrap().to_le_bytes(),
],
bump
)]
pub transaction: Account<'info, VaultBatchTransaction>,
pub member: Signer<'info>,
#[account(mut)]
pub rent_payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
impl BatchAddTransaction<'_> {
fn validate(&self) -> Result<()> {
let Self {
multisig,
member,
proposal,
..
} = self;
require!(
multisig.is_member(member.key()).is_some(),
MultisigError::NotAMember
);
require!(
multisig.member_has_permission(member.key(), Permission::Initiate),
MultisigError::Unauthorized
);
require!(
matches!(proposal.status, ProposalStatus::Draft { .. }),
MultisigError::InvalidProposalStatus
);
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn batch_add_transaction(ctx: Context<Self>, args: BatchAddTransactionArgs) -> Result<()> {
let batch = &mut ctx.accounts.batch;
let transaction = &mut ctx.accounts.transaction;
let batch_key = batch.key();
let transaction_message =
TransactionMessage::deserialize(&mut args.transaction_message.as_slice())?;
let ephemeral_signer_bumps: Vec<u8> = (0..args.ephemeral_signers)
.into_iter()
.map(|ephemeral_signer_index| {
let ephemeral_signer_seeds = &[
SEED_PREFIX,
batch_key.as_ref(),
SEED_EPHEMERAL_SIGNER,
&ephemeral_signer_index.to_le_bytes(),
];
let (_, bump) =
Pubkey::find_program_address(ephemeral_signer_seeds, ctx.program_id);
bump
})
.collect();
transaction.bump = *ctx.bumps.get("transaction").unwrap();
transaction.ephemeral_signer_bumps = ephemeral_signer_bumps;
transaction.message = transaction_message.try_into()?;
batch.size = batch.size.checked_add(1).expect("overflow");
msg!("batch index: {}", batch.index);
msg!("batch size: {}", batch.size);
Ok(())
}
}