use light_compressed_account::instruction_data::compressed_proof::ValidityProof;
use light_compressed_token_sdk::compressed_token::create_compressed_mint::derive_mint_compressed_address;
use light_sdk::instruction::PackedAddressTreeInfo;
use light_token_interface::MINT_ADDRESS_TREE;
use solana_instruction::AccountMeta;
use solana_pubkey::Pubkey;
use thiserror::Error;
use super::pack::{pack_proof, pack_proof_for_mints, PackError};
use crate::{
indexer::{AddressWithTree, Indexer, IndexerError, ValidityProofWithContext},
rpc::{Rpc, RpcError},
};
#[derive(Debug, Error)]
pub enum CreateAccountsProofError {
#[error("Inputs cannot be empty")]
EmptyInputs,
#[error("Indexer error: {0}")]
Indexer(#[from] IndexerError),
#[error("RPC error: {0}")]
Rpc(RpcError),
#[error("Pack error: {0}")]
Pack(#[from] PackError),
}
#[derive(Clone, Debug)]
pub enum CreateAccountsProofInput {
Pda(Pubkey),
PdaWithOwner { pda: Pubkey, owner: Pubkey },
Mint(Pubkey),
}
impl CreateAccountsProofInput {
pub fn pda(pda: Pubkey) -> Self {
Self::Pda(pda)
}
pub fn pda_with_owner(pda: Pubkey, owner: Pubkey) -> Self {
Self::PdaWithOwner { pda, owner }
}
pub fn mint(mint_signer: Pubkey) -> Self {
Self::Mint(mint_signer)
}
fn derive_address(&self, address_tree: &Pubkey, program_id: &Pubkey) -> [u8; 32] {
match self {
Self::Pda(pda) => light_compressed_account::address::derive_address(
&pda.to_bytes(),
&address_tree.to_bytes(),
&program_id.to_bytes(),
),
Self::PdaWithOwner { pda, owner } => light_compressed_account::address::derive_address(
&pda.to_bytes(),
&address_tree.to_bytes(),
&owner.to_bytes(),
),
Self::Mint(signer) => {
derive_mint_compressed_address(signer, &Pubkey::new_from_array(MINT_ADDRESS_TREE))
}
}
}
fn address_tree(&self, default_tree: &Pubkey) -> Pubkey {
match self {
Self::Pda(_) | Self::PdaWithOwner { .. } => *default_tree,
Self::Mint(_) => Pubkey::new_from_array(MINT_ADDRESS_TREE),
}
}
}
pub use light_sdk_types::interface::CreateAccountsProof;
pub struct CreateAccountsProofResult {
pub create_accounts_proof: CreateAccountsProof,
pub remaining_accounts: Vec<AccountMeta>,
}
pub async fn get_create_accounts_proof<R: Rpc + Indexer>(
rpc: &R,
program_id: &Pubkey,
inputs: Vec<CreateAccountsProofInput>,
) -> Result<CreateAccountsProofResult, CreateAccountsProofError> {
if inputs.is_empty() {
let state_tree_info = rpc
.get_random_state_tree_info()
.map_err(CreateAccountsProofError::Rpc)?;
let packed = pack_proof(
program_id,
ValidityProofWithContext::default(),
&state_tree_info,
None, )?;
return Ok(CreateAccountsProofResult {
create_accounts_proof: CreateAccountsProof {
proof: ValidityProof::default(),
address_tree_info: PackedAddressTreeInfo::default(),
output_state_tree_index: packed.output_tree_index,
state_tree_index: None,
system_accounts_offset: packed.system_accounts_offset,
},
remaining_accounts: packed.remaining_accounts,
});
}
let address_tree = rpc.get_address_tree_v2();
let address_tree_pubkey = address_tree.tree;
let derived_addresses: Vec<[u8; 32]> = inputs
.iter()
.map(|input| input.derive_address(&address_tree_pubkey, program_id))
.collect();
let addresses_with_trees: Vec<AddressWithTree> = inputs
.iter()
.zip(derived_addresses.iter())
.map(|(input, &address)| AddressWithTree {
address,
tree: input.address_tree(&address_tree_pubkey),
})
.collect();
let validity_proof = rpc
.get_validity_proof(vec![], addresses_with_trees, None)
.await?
.value;
let state_tree_info = rpc
.get_random_state_tree_info()
.map_err(CreateAccountsProofError::Rpc)?;
let has_mints = inputs
.iter()
.any(|i| matches!(i, CreateAccountsProofInput::Mint(_)));
let cpi_context = if has_mints {
state_tree_info.cpi_context
} else {
None
};
let packed = if has_mints {
pack_proof_for_mints(
program_id,
validity_proof.clone(),
&state_tree_info,
cpi_context,
)?
} else {
pack_proof(
program_id,
validity_proof.clone(),
&state_tree_info,
cpi_context,
)?
};
let address_tree_info = packed
.packed_tree_infos
.address_trees
.first()
.copied()
.ok_or(CreateAccountsProofError::EmptyInputs)?;
Ok(CreateAccountsProofResult {
create_accounts_proof: CreateAccountsProof {
proof: validity_proof.proof,
address_tree_info,
output_state_tree_index: packed.output_tree_index,
state_tree_index: packed.state_tree_index,
system_accounts_offset: packed.system_accounts_offset,
},
remaining_accounts: packed.remaining_accounts,
})
}