use anchor_lang::{prelude::*, Bumps};
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{instruction::Instruction, program::invoke_signed};
use crate::{
address::NewAddressParamsPacked,
compressed_account::{
OutputCompressedAccountWithPackedContext, PackedCompressedAccountWithMerkleContext,
},
error::LightSdkError,
proof::CompressedProof,
traits::{
InvokeAccounts, InvokeCpiAccounts, InvokeCpiContextAccount, LightSystemAccount,
SignerAccounts,
},
CPI_AUTHORITY_PDA_SEED, PROGRAM_ID_LIGHT_SYSTEM,
};
pub fn find_cpi_signer(program_id: &Pubkey) -> Pubkey {
Pubkey::find_program_address([CPI_AUTHORITY_PDA_SEED].as_slice(), program_id).0
}
#[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct CompressedCpiContext {
pub set_context: bool,
pub first_set_context: bool,
pub cpi_context_account_index: u8,
}
#[derive(Debug, PartialEq, Default, Clone, BorshDeserialize, BorshSerialize)]
pub struct InstructionDataInvokeCpi {
pub proof: Option<CompressedProof>,
pub new_address_params: Vec<NewAddressParamsPacked>,
pub input_compressed_accounts_with_merkle_context:
Vec<PackedCompressedAccountWithMerkleContext>,
pub output_compressed_accounts: Vec<OutputCompressedAccountWithPackedContext>,
pub relay_fee: Option<u64>,
pub compress_or_decompress_lamports: Option<u64>,
pub is_compress: bool,
pub cpi_context: Option<CompressedCpiContext>,
}
#[inline(always)]
pub fn setup_cpi_accounts<'info>(
ctx: &Context<
'_,
'_,
'_,
'info,
impl InvokeAccounts<'info>
+ LightSystemAccount<'info>
+ InvokeCpiAccounts<'info>
+ SignerAccounts<'info>
+ InvokeCpiContextAccount<'info>
+ Bumps,
>,
) -> (Vec<AccountInfo<'info>>, Vec<AccountMeta>) {
let none_account_info = ctx.accounts.get_light_system_program().to_account_info();
let (cpi_context_account_info, cpi_context_account_meta) =
match ctx.accounts.get_cpi_context_account() {
Some(acc) => (
acc.to_account_info(),
AccountMeta {
pubkey: acc.key(),
is_signer: false,
is_writable: true,
},
),
None => (
none_account_info.clone(),
AccountMeta {
pubkey: ctx.accounts.get_light_system_program().key(),
is_signer: false,
is_writable: false,
},
),
};
let mut account_infos = vec![
ctx.accounts.get_fee_payer().to_account_info(),
ctx.accounts.get_authority().to_account_info(),
ctx.accounts.get_registered_program_pda().to_account_info(),
ctx.accounts.get_noop_program().to_account_info(),
ctx.accounts
.get_account_compression_authority()
.to_account_info(),
ctx.accounts
.get_account_compression_program()
.to_account_info(),
ctx.accounts.get_invoking_program().to_account_info(),
none_account_info.clone(),
none_account_info,
ctx.accounts.get_system_program().to_account_info(),
cpi_context_account_info,
];
for remaining_account in ctx.remaining_accounts {
account_infos.push(remaining_account.to_owned());
}
let mut account_metas = vec![
AccountMeta {
pubkey: account_infos[0].key(),
is_signer: true,
is_writable: true,
},
AccountMeta {
pubkey: account_infos[1].key(),
is_signer: true,
is_writable: false,
},
AccountMeta {
pubkey: account_infos[2].key(),
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: account_infos[3].key(),
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: account_infos[4].key(),
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: account_infos[5].key(),
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: account_infos[6].key(),
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: account_infos[7].key(),
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: account_infos[8].key(),
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: account_infos[9].key(),
is_signer: false,
is_writable: false,
},
cpi_context_account_meta,
];
for remaining_account in ctx.remaining_accounts {
account_metas.extend(remaining_account.to_account_metas(None));
}
(account_infos, account_metas)
}
#[derive(BorshDeserialize, BorshSerialize)]
pub struct InvokeCpi {
pub inputs: Vec<u8>,
}
#[inline(always)]
pub fn invoke_cpi(
account_infos: &[AccountInfo],
accounts_metas: Vec<AccountMeta>,
inputs: Vec<u8>,
signer_seeds: &[&[&[u8]]],
) -> Result<()> {
let instruction_data = InvokeCpi { inputs };
let mut data = [49, 212, 191, 129, 39, 194, 43, 196].to_vec();
data.extend(instruction_data.try_to_vec()?);
let instruction = Instruction {
program_id: PROGRAM_ID_LIGHT_SYSTEM,
accounts: accounts_metas,
data,
};
invoke_signed(&instruction, account_infos, signer_seeds)?;
Ok(())
}
pub fn verify<'info, 'a, 'b, 'c, T>(
ctx: &Context<
'_,
'_,
'_,
'info,
impl InvokeAccounts<'info>
+ LightSystemAccount<'info>
+ InvokeCpiAccounts<'info>
+ SignerAccounts<'info>
+ InvokeCpiContextAccount<'info>
+ Bumps,
>,
inputs: &T,
signer_seeds: &'a [&'b [&'c [u8]]],
) -> Result<()>
where
T: BorshSerialize,
{
if ctx.accounts.get_light_system_program().key() != PROGRAM_ID_LIGHT_SYSTEM {
return err!(LightSdkError::InvalidLightSystemProgram);
}
let inputs = inputs.try_to_vec()?;
let (account_infos, account_metas) = setup_cpi_accounts(ctx);
invoke_cpi(&account_infos, account_metas, inputs, signer_seeds)?;
Ok(())
}