light_program_test/forester/
compress_and_close_forester.rs1use light_client::{
2 indexer::Indexer,
3 rpc::{Rpc, RpcError},
4};
5use light_compressible::config::CompressibleConfig;
6use light_sdk::instruction::PackedAccounts;
7use light_token::compressed_token::CompressAndCloseAccounts as CTokenCompressAndCloseAccounts;
8use solana_sdk::{
9 pubkey::Pubkey,
10 signature::{Keypair, Signature},
11 signer::Signer,
12};
13
14use crate::registry_sdk::{
15 build_compress_and_close_instruction, get_forester_epoch_pda_from_authority,
16 CompressAndCloseIndices, REGISTRY_PROGRAM_ID,
17};
18
19pub async fn compress_and_close_forester<R: Rpc + Indexer>(
34 rpc: &mut R,
35 solana_ctoken_accounts: &[Pubkey],
36 authority: &Keypair,
37 payer: &Keypair,
38 destination: Option<Pubkey>,
39) -> Result<Signature, RpcError> {
40 let compressed_token_program_id =
42 Pubkey::from_str_const("cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m");
43
44 let current_epoch = 0;
45
46 let (registered_forester_pda, _) =
48 get_forester_epoch_pda_from_authority(&authority.pubkey(), current_epoch);
49
50 let config = CompressibleConfig::light_token_v1(Pubkey::default(), Pubkey::default());
51
52 let compressible_config = CompressibleConfig::derive_v1_config_pda(®ISTRY_PROGRAM_ID).0;
53
54 let compression_authority = config.compression_authority;
56 println!("config compression_authority {:?}", compression_authority);
57
58 if solana_ctoken_accounts.is_empty() {
60 return Err(RpcError::CustomError(
61 "No token accounts provided".to_string(),
62 ));
63 }
64
65 let output_tree_info = rpc
67 .get_random_state_tree_info()
68 .map_err(|e| RpcError::CustomError(format!("Failed to get state tree info: {}", e)))?;
69 let output_queue = output_tree_info
70 .get_output_pubkey()
71 .map_err(|e| RpcError::CustomError(format!("Failed to get output queue: {}", e)))?;
72
73 let mut packed_accounts = PackedAccounts::default();
75
76 packed_accounts.insert_or_get(output_queue);
78
79 use light_token_interface::state::Token;
81 use light_zero_copy::traits::ZeroCopyAt;
82
83 let mut indices_vec = Vec::with_capacity(solana_ctoken_accounts.len());
84
85 let mut compression_authority_pubkey: Option<Pubkey> = None;
86
87 for solana_ctoken_account_pubkey in solana_ctoken_accounts {
88 let ctoken_solana_account = rpc
90 .get_account(*solana_ctoken_account_pubkey)
91 .await
92 .map_err(|e| {
93 RpcError::CustomError(format!(
94 "Failed to get ctoken account {}: {}",
95 solana_ctoken_account_pubkey, e
96 ))
97 })?
98 .ok_or_else(|| {
99 RpcError::CustomError(format!(
100 "Light Token account {} not found",
101 solana_ctoken_account_pubkey
102 ))
103 })?;
104
105 let (ctoken_account, _) = Token::zero_copy_at(ctoken_solana_account.data.as_slice())
106 .map_err(|e| {
107 RpcError::CustomError(format!(
108 "Failed to parse ctoken account {}: {:?}",
109 solana_ctoken_account_pubkey, e
110 ))
111 })?;
112
113 let source_index = packed_accounts.insert_or_get(*solana_ctoken_account_pubkey);
115 let mint_index =
116 packed_accounts.insert_or_get(Pubkey::from(ctoken_account.mint.to_bytes()));
117
118 let compressible_ext = ctoken_account.get_compressible_extension().ok_or_else(|| {
120 RpcError::CustomError(
121 "Missing Compressible extension on Light Token account".to_string(),
122 )
123 })?;
124 let current_authority = Pubkey::from(compressible_ext.info.compression_authority);
125 let rent_sponsor_pubkey = Pubkey::from(compressible_ext.info.rent_sponsor);
126
127 if compression_authority_pubkey.is_none() {
128 compression_authority_pubkey = Some(current_authority);
129 }
130
131 let compressed_token_owner = if compressible_ext.info.compress_to_pubkey == 1 {
132 *solana_ctoken_account_pubkey
133 } else {
134 Pubkey::from(ctoken_account.owner.to_bytes())
135 };
136
137 let owner_index = packed_accounts.insert_or_get(compressed_token_owner);
138 let rent_sponsor_index = packed_accounts.insert_or_get(rent_sponsor_pubkey);
139
140 let delegate_index = if ctoken_account.delegate != [0u8; 32] {
142 let delegate_pubkey = Pubkey::from(ctoken_account.delegate);
143 packed_accounts.insert_or_get(delegate_pubkey)
144 } else {
145 0 };
147
148 let indices = CompressAndCloseIndices {
149 source_index,
150 mint_index,
151 owner_index,
152 rent_sponsor_index,
153 delegate_index,
154 };
155
156 indices_vec.push(indices);
157 }
158
159 let destination_pubkey = destination.unwrap_or_else(|| payer.pubkey());
160 let destination_index = packed_accounts.insert_or_get_config(destination_pubkey, false, true);
161
162 let compression_authority_pubkey = compression_authority_pubkey.ok_or_else(|| {
163 RpcError::CustomError("No compression authority found in accounts".to_string())
164 })?;
165
166 let authority_index =
167 packed_accounts.insert_or_get_config(compression_authority_pubkey, false, true);
168
169 let ctoken_config = CTokenCompressAndCloseAccounts {
170 compressed_token_program: compressed_token_program_id,
171 cpi_authority_pda: Pubkey::find_program_address(
172 &[b"cpi_authority"],
173 &compressed_token_program_id,
174 )
175 .0,
176 cpi_context: None,
177 self_program: None, };
179 packed_accounts
180 .add_custom_system_accounts(ctoken_config)
181 .map_err(|e| RpcError::CustomError(format!("Failed to add system accounts: {:?}", e)))?;
182
183 let (remaining_account_metas, _, _) = packed_accounts.to_account_metas();
185
186 let compress_and_close_ix = build_compress_and_close_instruction(
188 authority.pubkey(),
189 registered_forester_pda,
190 compression_authority,
191 compressible_config,
192 authority_index,
193 destination_index,
194 indices_vec,
195 remaining_account_metas,
196 );
197
198 let mut signers = vec![payer];
200 if authority.pubkey() != payer.pubkey() {
201 signers.push(authority);
202 }
203
204 rpc.create_and_send_transaction(&[compress_and_close_ix], &payer.pubkey(), &signers)
206 .await
207}