light_client/interface/
create_accounts_proof.rs1use light_compressed_account::instruction_data::compressed_proof::ValidityProof;
5use light_sdk::instruction::PackedAddressTreeInfo;
6use light_token::compressed_token::create_compressed_mint::derive_mint_compressed_address;
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_compressible::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 },
132 remaining_accounts: packed.remaining_accounts,
133 });
134 }
135
136 let address_tree = rpc.get_address_tree_v2();
138 let address_tree_pubkey = address_tree.tree;
139
140 let derived_addresses: Vec<[u8; 32]> = inputs
142 .iter()
143 .map(|input| input.derive_address(&address_tree_pubkey, program_id))
144 .collect();
145
146 let addresses_with_trees: Vec<AddressWithTree> = inputs
148 .iter()
149 .zip(derived_addresses.iter())
150 .map(|(input, &address)| AddressWithTree {
151 address,
152 tree: input.address_tree(&address_tree_pubkey),
153 })
154 .collect();
155
156 let validity_proof = rpc
158 .get_validity_proof(vec![], addresses_with_trees, None)
159 .await?
160 .value;
161
162 let state_tree_info = rpc
164 .get_random_state_tree_info()
165 .map_err(CreateAccountsProofError::Rpc)?;
166
167 let has_mints = inputs
169 .iter()
170 .any(|i| matches!(i, CreateAccountsProofInput::Mint(_)));
171 let cpi_context = if has_mints {
172 state_tree_info.cpi_context
173 } else {
174 None
175 };
176
177 let packed = if has_mints {
179 pack_proof_for_mints(
180 program_id,
181 validity_proof.clone(),
182 &state_tree_info,
183 cpi_context,
184 )?
185 } else {
186 pack_proof(
187 program_id,
188 validity_proof.clone(),
189 &state_tree_info,
190 cpi_context,
191 )?
192 };
193
194 let address_tree_info = packed
196 .packed_tree_infos
197 .address_trees
198 .first()
199 .copied()
200 .ok_or(CreateAccountsProofError::EmptyInputs)?;
201
202 Ok(CreateAccountsProofResult {
203 create_accounts_proof: CreateAccountsProof {
204 proof: validity_proof.proof,
205 address_tree_info,
206 output_state_tree_index: packed.output_tree_index,
207 state_tree_index: packed.state_tree_index,
208 },
209 remaining_accounts: packed.remaining_accounts,
210 })
211}