light_client/interface/
create_accounts_proof.rs1use light_compressed_account::instruction_data::compressed_proof::ValidityProof;
5use light_compressed_token_sdk::compressed_token::create_compressed_mint::derive_mint_compressed_address;
6use light_sdk::instruction::PackedAddressTreeInfo;
7use light_token_interface::MINT_ADDRESS_TREE;
8use solana_instruction::AccountMeta;
9use solana_pubkey::Pubkey;
10use thiserror::Error;
11
12use super::pack::{pack_proof, pack_proof_for_mints, PackError};
13use crate::{
14 indexer::{AddressWithTree, Indexer, IndexerError, ValidityProofWithContext},
15 rpc::{Rpc, RpcError},
16};
17
18#[derive(Debug, Error)]
20pub enum CreateAccountsProofError {
21 #[error("Inputs cannot be empty")]
22 EmptyInputs,
23
24 #[error("Indexer error: {0}")]
25 Indexer(#[from] IndexerError),
26
27 #[error("RPC error: {0}")]
28 Rpc(RpcError),
29
30 #[error("Pack error: {0}")]
31 Pack(#[from] PackError),
32}
33
34#[derive(Clone, Debug)]
37pub enum CreateAccountsProofInput {
38 Pda(Pubkey),
40 PdaWithOwner { pda: Pubkey, owner: Pubkey },
42 Mint(Pubkey),
44}
45
46impl CreateAccountsProofInput {
47 pub fn pda(pda: Pubkey) -> Self {
50 Self::Pda(pda)
51 }
52
53 pub fn pda_with_owner(pda: Pubkey, owner: Pubkey) -> Self {
56 Self::PdaWithOwner { pda, owner }
57 }
58
59 pub fn mint(mint_signer: Pubkey) -> Self {
62 Self::Mint(mint_signer)
63 }
64
65 fn derive_address(&self, address_tree: &Pubkey, program_id: &Pubkey) -> [u8; 32] {
67 match self {
68 Self::Pda(pda) => light_compressed_account::address::derive_address(
69 &pda.to_bytes(),
70 &address_tree.to_bytes(),
71 &program_id.to_bytes(),
72 ),
73 Self::PdaWithOwner { pda, owner } => light_compressed_account::address::derive_address(
74 &pda.to_bytes(),
75 &address_tree.to_bytes(),
76 &owner.to_bytes(),
77 ),
78 Self::Mint(signer) => {
80 derive_mint_compressed_address(signer, &Pubkey::new_from_array(MINT_ADDRESS_TREE))
81 }
82 }
83 }
84
85 fn address_tree(&self, default_tree: &Pubkey) -> Pubkey {
87 match self {
88 Self::Pda(_) | Self::PdaWithOwner { .. } => *default_tree,
89 Self::Mint(_) => Pubkey::new_from_array(MINT_ADDRESS_TREE),
91 }
92 }
93}
94
95pub use light_sdk_types::interface::CreateAccountsProof;
96
97pub struct CreateAccountsProofResult {
99 pub create_accounts_proof: CreateAccountsProof,
101 pub remaining_accounts: Vec<AccountMeta>,
103}
104
105pub async fn get_create_accounts_proof<R: Rpc + Indexer>(
107 rpc: &R,
108 program_id: &Pubkey,
109 inputs: Vec<CreateAccountsProofInput>,
110) -> Result<CreateAccountsProofResult, CreateAccountsProofError> {
111 if inputs.is_empty() {
112 let state_tree_info = rpc
114 .get_random_state_tree_info()
115 .map_err(CreateAccountsProofError::Rpc)?;
116
117 let packed = pack_proof(
119 program_id,
120 ValidityProofWithContext::default(),
121 &state_tree_info,
122 None, )?;
124
125 return Ok(CreateAccountsProofResult {
126 create_accounts_proof: CreateAccountsProof {
127 proof: ValidityProof::default(),
128 address_tree_info: PackedAddressTreeInfo::default(),
129 output_state_tree_index: packed.output_tree_index,
130 state_tree_index: None,
131 system_accounts_offset: packed.system_accounts_offset,
132 },
133 remaining_accounts: packed.remaining_accounts,
134 });
135 }
136
137 let address_tree = rpc.get_address_tree_v2();
139 let address_tree_pubkey = address_tree.tree;
140
141 let derived_addresses: Vec<[u8; 32]> = inputs
143 .iter()
144 .map(|input| input.derive_address(&address_tree_pubkey, program_id))
145 .collect();
146
147 let addresses_with_trees: Vec<AddressWithTree> = inputs
149 .iter()
150 .zip(derived_addresses.iter())
151 .map(|(input, &address)| AddressWithTree {
152 address,
153 tree: input.address_tree(&address_tree_pubkey),
154 })
155 .collect();
156
157 let validity_proof = rpc
159 .get_validity_proof(vec![], addresses_with_trees, None)
160 .await?
161 .value;
162
163 let state_tree_info = rpc
165 .get_random_state_tree_info()
166 .map_err(CreateAccountsProofError::Rpc)?;
167
168 let has_mints = inputs
170 .iter()
171 .any(|i| matches!(i, CreateAccountsProofInput::Mint(_)));
172 let cpi_context = if has_mints {
173 state_tree_info.cpi_context
174 } else {
175 None
176 };
177
178 let packed = if has_mints {
180 pack_proof_for_mints(
181 program_id,
182 validity_proof.clone(),
183 &state_tree_info,
184 cpi_context,
185 )?
186 } else {
187 pack_proof(
188 program_id,
189 validity_proof.clone(),
190 &state_tree_info,
191 cpi_context,
192 )?
193 };
194
195 let address_tree_info = packed
197 .packed_tree_infos
198 .address_trees
199 .first()
200 .copied()
201 .ok_or(CreateAccountsProofError::EmptyInputs)?;
202
203 Ok(CreateAccountsProofResult {
204 create_accounts_proof: CreateAccountsProof {
205 proof: validity_proof.proof,
206 address_tree_info,
207 output_state_tree_index: packed.output_tree_index,
208 state_tree_index: packed.state_tree_index,
209 system_accounts_offset: packed.system_accounts_offset,
210 },
211 remaining_accounts: packed.remaining_accounts,
212 })
213}