light_client/interface/
pack.rs

1//! Helper for packing validity proofs into remaining accounts.
2
3use light_sdk::instruction::{PackedAccounts, SystemAccountMetaConfig};
4pub use light_sdk::instruction::{PackedAddressTreeInfo, PackedStateTreeInfo};
5use solana_instruction::AccountMeta;
6use solana_pubkey::Pubkey;
7use thiserror::Error;
8
9use crate::indexer::{TreeInfo, ValidityProofWithContext};
10
11#[derive(Debug, Error)]
12pub enum PackError {
13    #[error("Failed to add system accounts: {0}")]
14    SystemAccounts(#[from] light_sdk::error::LightSdkError),
15}
16
17/// Packed state tree infos from validity proof.
18#[derive(Clone, Default, Debug)]
19pub struct PackedStateTreeInfos {
20    pub packed_tree_infos: Vec<PackedStateTreeInfo>,
21    pub output_tree_index: u8,
22}
23
24/// Packed tree infos from validity proof.
25#[derive(Clone, Default, Debug)]
26pub struct PackedTreeInfos {
27    pub state_trees: Option<PackedStateTreeInfos>,
28    pub address_trees: Vec<PackedAddressTreeInfo>,
29}
30
31/// Result of packing a validity proof into remaining accounts.
32pub struct PackedProofResult {
33    /// Remaining accounts to append to your instruction's accounts.
34    pub remaining_accounts: Vec<AccountMeta>,
35    /// Packed tree infos from the proof. Use `.address_trees` or `.state_trees` as needed.
36    pub packed_tree_infos: PackedTreeInfos,
37    /// Index of output tree in remaining accounts. Pass to instruction data.
38    pub output_tree_index: u8,
39    /// Index of state merkle tree in remaining accounts (when included for mint creation).
40    pub state_tree_index: Option<u8>,
41    /// Offset where system accounts start. Pass to instruction data if needed.
42    pub system_accounts_offset: u8,
43}
44
45/// Packs a validity proof into remaining accounts for instruction building.
46pub fn pack_proof(
47    program_id: &Pubkey,
48    proof: ValidityProofWithContext,
49    output_tree: &TreeInfo,
50    cpi_context: Option<Pubkey>,
51) -> Result<PackedProofResult, PackError> {
52    pack_proof_internal(program_id, proof, output_tree, cpi_context, false)
53}
54
55/// Same as `pack_proof` but also includes state merkle tree for mint creation.
56pub fn pack_proof_for_mints(
57    program_id: &Pubkey,
58    proof: ValidityProofWithContext,
59    output_tree: &TreeInfo,
60    cpi_context: Option<Pubkey>,
61) -> Result<PackedProofResult, PackError> {
62    pack_proof_internal(program_id, proof, output_tree, cpi_context, true)
63}
64
65fn pack_proof_internal(
66    program_id: &Pubkey,
67    proof: ValidityProofWithContext,
68    output_tree: &TreeInfo,
69    cpi_context: Option<Pubkey>,
70    include_state_tree: bool,
71) -> Result<PackedProofResult, PackError> {
72    let mut packed = PackedAccounts::default();
73
74    let system_config = match cpi_context {
75        Some(ctx) => SystemAccountMetaConfig::new_with_cpi_context(*program_id, ctx),
76        None => SystemAccountMetaConfig::new(*program_id),
77    };
78    packed.add_system_accounts_v2(system_config)?;
79
80    let output_queue = output_tree
81        .next_tree_info
82        .as_ref()
83        .map(|n| n.queue)
84        .unwrap_or(output_tree.queue);
85    let output_tree_index = packed.insert_or_get(output_queue);
86
87    // For mint creation: pack address tree first (index 1), then state tree.
88    let (client_packed_tree_infos, state_tree_index) = if include_state_tree {
89        // Pack tree infos first to ensure address tree is at index 1
90        let tree_infos = proof.pack_tree_infos(&mut packed);
91
92        // Then add state tree (will be after address tree)
93        let state_tree = output_tree
94            .next_tree_info
95            .as_ref()
96            .map(|n| n.tree)
97            .unwrap_or(output_tree.tree);
98        let state_idx = packed.insert_or_get(state_tree);
99
100        (tree_infos, Some(state_idx))
101    } else {
102        let tree_infos = proof.pack_tree_infos(&mut packed);
103        (tree_infos, None)
104    };
105    let (remaining_accounts, system_offset, _) = packed.to_account_metas();
106
107    // Convert from light_client's types to our local types
108    let packed_tree_infos = PackedTreeInfos {
109        state_trees: client_packed_tree_infos
110            .state_trees
111            .map(|st| PackedStateTreeInfos {
112                packed_tree_infos: st.packed_tree_infos,
113                output_tree_index: st.output_tree_index,
114            }),
115        address_trees: client_packed_tree_infos.address_trees,
116    };
117
118    Ok(PackedProofResult {
119        remaining_accounts,
120        packed_tree_infos,
121        output_tree_index,
122        state_tree_index,
123        system_accounts_offset: system_offset as u8,
124    })
125}