use light_compressed_account::compressed_account::PackedMerkleContext;
use light_program_profiler::profile;
use light_sdk::{
error::LightSdkError,
instruction::{AccountMetasVec, PackedAccounts, PackedStateTreeInfo, SystemAccountMetaConfig},
};
use light_token_interface::instructions::{
extensions::ExtensionInstructionData,
transfer2::{CompressedCpiContext, MultiInputTokenDataWithContext},
};
use solana_account_info::AccountInfo;
use solana_instruction::{AccountMeta, Instruction};
use solana_pubkey::Pubkey;
use super::{
account2::CTokenAccount2,
transfer2::{
account_metas::Transfer2AccountsMetaConfig, create_transfer2_instruction, Transfer2Config,
Transfer2Inputs,
},
};
use crate::{
compat::TokenData, error::TokenSdkError, utils::TokenDefaultAccounts, AnchorDeserialize,
AnchorSerialize, ValidityProof,
};
#[derive(Debug, Clone, AnchorDeserialize, AnchorSerialize)]
pub struct DecompressFullIndices {
pub source: MultiInputTokenDataWithContext, pub destination_index: u8, pub tlv: Option<Vec<ExtensionInstructionData>>,
pub is_ata: bool,
}
#[profile]
pub fn decompress_full_token_accounts_with_indices<'info>(
fee_payer: Pubkey,
validity_proof: ValidityProof,
cpi_context_pubkey: Option<Pubkey>,
indices: &[DecompressFullIndices],
packed_accounts: &[AccountInfo<'info>],
) -> Result<Instruction, TokenSdkError> {
if indices.is_empty() {
return Err(TokenSdkError::InvalidAccountData);
}
let mut token_accounts = Vec::with_capacity(indices.len());
let mut in_tlv_data: Vec<Vec<ExtensionInstructionData>> = Vec::with_capacity(indices.len());
let mut has_any_tlv = false;
let mut signer_flags = vec![false; packed_accounts.len()];
for idx in indices.iter() {
let mut token_account = CTokenAccount2::new(vec![idx.source])?;
token_account.decompress(idx.source.amount, idx.destination_index)?;
token_accounts.push(token_account);
if let Some(tlv) = &idx.tlv {
has_any_tlv = true;
in_tlv_data.push(tlv.clone());
} else {
in_tlv_data.push(Vec::new());
}
let owner_idx = idx.source.owner as usize;
if owner_idx >= signer_flags.len() {
return Err(TokenSdkError::InvalidAccountData);
}
if !idx.is_ata {
signer_flags[owner_idx] = true;
}
}
let mut packed_account_metas = Vec::with_capacity(packed_accounts.len());
for (i, info) in packed_accounts.iter().enumerate() {
packed_account_metas.push(AccountMeta {
pubkey: *info.key,
is_signer: info.is_signer || signer_flags[i],
is_writable: info.is_writable,
});
}
let (meta_config, transfer_config) = if let Some(cpi_context) = cpi_context_pubkey {
let cpi_context_config = CompressedCpiContext {
set_context: false,
first_set_context: false,
};
(
Transfer2AccountsMetaConfig {
fee_payer: Some(fee_payer),
cpi_context: Some(cpi_context),
decompressed_accounts_only: false,
sol_pool_pda: None,
sol_decompression_recipient: None,
with_sol_pool: false,
packed_accounts: Some(packed_account_metas),
},
Transfer2Config::default()
.filter_zero_amount_outputs()
.with_cpi_context(cpi_context_config),
)
} else {
(
Transfer2AccountsMetaConfig::new(fee_payer, packed_account_metas),
Transfer2Config::default().filter_zero_amount_outputs(),
)
};
let inputs = Transfer2Inputs {
meta_config,
token_accounts,
transfer_config,
validity_proof,
in_tlv: if has_any_tlv { Some(in_tlv_data) } else { None },
..Default::default()
};
create_transfer2_instruction(inputs)
}
#[profile]
pub fn pack_for_decompress_full(
token: &TokenData,
tree_info: &PackedStateTreeInfo,
destination: Pubkey,
packed_accounts: &mut PackedAccounts,
tlv: Option<Vec<ExtensionInstructionData>>,
version: u8,
) -> DecompressFullIndices {
let source = MultiInputTokenDataWithContext {
owner: packed_accounts.insert_or_get_config(token.owner, true, false),
amount: token.amount,
has_delegate: token.delegate.is_some(),
delegate: token
.delegate
.map(|d| packed_accounts.insert_or_get(d))
.unwrap_or(0),
mint: packed_accounts.insert_or_get(token.mint),
version,
merkle_context: PackedMerkleContext {
merkle_tree_pubkey_index: tree_info.merkle_tree_pubkey_index,
queue_pubkey_index: tree_info.queue_pubkey_index,
prove_by_index: tree_info.prove_by_index,
leaf_index: tree_info.leaf_index,
},
root_index: tree_info.root_index,
};
DecompressFullIndices {
source,
destination_index: packed_accounts.insert_or_get(destination),
tlv,
is_ata: false, }
}
#[profile]
pub fn pack_for_decompress_full_with_ata(
token: &TokenData,
tree_info: &PackedStateTreeInfo,
destination: Pubkey,
packed_accounts: &mut PackedAccounts,
tlv: Option<Vec<ExtensionInstructionData>>,
version: u8,
is_ata: bool,
) -> DecompressFullIndices {
let owner_is_signer = !is_ata;
let source = MultiInputTokenDataWithContext {
owner: packed_accounts.insert_or_get_config(token.owner, owner_is_signer, false),
amount: token.amount,
has_delegate: token.delegate.is_some(),
delegate: token
.delegate
.map(|d| packed_accounts.insert_or_get(d))
.unwrap_or(0),
mint: packed_accounts.insert_or_get(token.mint),
version,
merkle_context: PackedMerkleContext {
merkle_tree_pubkey_index: tree_info.merkle_tree_pubkey_index,
queue_pubkey_index: tree_info.queue_pubkey_index,
prove_by_index: tree_info.prove_by_index,
leaf_index: tree_info.leaf_index,
},
root_index: tree_info.root_index,
};
DecompressFullIndices {
source,
destination_index: packed_accounts.insert_or_get(destination),
tlv,
is_ata,
}
}
pub struct DecompressFullAccounts {
pub compressed_token_program: Pubkey,
pub cpi_authority_pda: Pubkey,
pub cpi_context: Option<Pubkey>,
pub self_program: Option<Pubkey>,
}
impl DecompressFullAccounts {
pub fn new(cpi_context: Option<Pubkey>) -> Self {
Self {
compressed_token_program: TokenDefaultAccounts::default().compressed_token_program,
cpi_authority_pda: TokenDefaultAccounts::default().cpi_authority_pda,
cpi_context,
self_program: None,
}
}
pub fn new_with_cpi_context(cpi_context: Option<Pubkey>, self_program: Option<Pubkey>) -> Self {
Self {
compressed_token_program: TokenDefaultAccounts::default().compressed_token_program,
cpi_authority_pda: TokenDefaultAccounts::default().cpi_authority_pda,
cpi_context,
self_program,
}
}
}
impl AccountMetasVec for DecompressFullAccounts {
fn get_account_metas_vec(&self, accounts: &mut PackedAccounts) -> Result<(), LightSdkError> {
if !accounts.system_accounts_set() {
#[cfg(feature = "cpi-context")]
let config = {
let mut config = SystemAccountMetaConfig::default();
config.self_program = self.self_program;
config.cpi_context = self.cpi_context;
config
};
#[cfg(not(feature = "cpi-context"))]
let config = {
let mut config = SystemAccountMetaConfig::default();
config.self_program = self.self_program;
config
};
accounts.add_system_accounts_v2(config)?;
}
accounts.pre_accounts.extend_from_slice(&[
AccountMeta {
pubkey: self.compressed_token_program,
is_signer: false,
is_writable: false,
},
AccountMeta {
pubkey: self.cpi_authority_pda,
is_signer: false,
is_writable: false,
},
]);
Ok(())
}
}