Skip to content

Commit a8fbbc6

Browse files
committed
decompress test works
1 parent bed4749 commit a8fbbc6

File tree

8 files changed

+200
-72
lines changed

8 files changed

+200
-72
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

program-tests/sdk-token-test/src/lib.rs

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,12 @@ pub mod sdk_token_test {
3434
ctx.accounts.signer.as_ref(),
3535
ctx.remaining_accounts,
3636
);
37-
msg!(
38-
"light_cpi_accounts config {:?}",
39-
light_cpi_accounts.config()
40-
);
41-
msg!(
42-
"light_cpi_accounts config is_compress_or_decompress {:?}",
43-
light_cpi_accounts.config().is_compress_or_decompress()
44-
);
45-
msg!(
46-
"ctx.remaining_accounts len {:?}",
47-
ctx.remaining_accounts.len()
48-
);
37+
4938
// TODO: add to program error conversion
5039
let instruction =
5140
create_compressed_token_instruction(cpi_inputs, &light_cpi_accounts).unwrap();
5241
let account_infos = light_cpi_accounts.to_account_infos();
53-
msg!("account_infos {:?}", account_infos);
54-
msg!("instruction {:?}", instruction);
42+
5543
invoke(&instruction, account_infos.as_slice())?;
5644

5745
Ok(())
@@ -85,7 +73,7 @@ pub mod sdk_token_test {
8573
// TODO: add to program error conversion
8674
let instruction =
8775
create_compressed_token_instruction(cpi_inputs, &light_cpi_accounts).unwrap();
88-
msg!("instruction created {:?}", instruction);
76+
8977
let account_infos = light_cpi_accounts.to_account_infos();
9078

9179
// TODO: make invoke_signed
@@ -120,8 +108,7 @@ pub mod sdk_token_test {
120108
let instruction =
121109
create_compressed_token_instruction(cpi_inputs, &light_cpi_accounts).unwrap();
122110
let account_infos = light_cpi_accounts.to_account_infos();
123-
msg!("account_infos {:?}", account_infos);
124-
msg!("instruction {:?}", instruction);
111+
125112
// TODO: make invoke_signed
126113
invoke(&instruction, account_infos.as_slice())?;
127114

program-tests/sdk-token-test/tests/test.rs

Lines changed: 164 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,99 @@ async fn test() {
180180
assert_eq!(recipient_amount, transfer_amount);
181181
println!("Verified recipient balance: {}", recipient_amount);
182182

183-
println!("Compression and transfer test completed successfully!");
183+
// Now decompress some tokens from the recipient back to SPL token account
184+
let decompress_token_account_keypair = Keypair::new();
185+
let decompress_amount = 10; // Decompress a small amount
186+
rpc.airdrop_lamports(&transfer_recipient.pubkey(), 10_000_000_000)
187+
.await
188+
.unwrap();
189+
// Create a new SPL token account for decompression
190+
create_token_account(
191+
&mut rpc,
192+
&mint_pubkey,
193+
&decompress_token_account_keypair,
194+
&transfer_recipient,
195+
)
196+
.await
197+
.unwrap();
198+
199+
println!(
200+
"Created decompress token account: {}",
201+
decompress_token_account_keypair.pubkey()
202+
);
203+
204+
// Get the recipient's compressed token account after transfer
205+
let recipient_compressed_accounts = rpc
206+
.indexer()
207+
.unwrap()
208+
.get_compressed_token_accounts_by_owner(&transfer_recipient.pubkey(), None, None)
209+
.await
210+
.unwrap()
211+
.value
212+
.items;
213+
214+
let recipient_compressed_account = &recipient_compressed_accounts[0];
215+
216+
// Decompress tokens from recipient's compressed account to SPL token account
217+
decompress_compressed_tokens(
218+
&mut rpc,
219+
&transfer_recipient,
220+
recipient_compressed_account,
221+
decompress_token_account_keypair.pubkey(),
222+
decompress_amount,
223+
)
224+
.await
225+
.unwrap();
226+
227+
println!(
228+
"Decompressed {} tokens from recipient successfully",
229+
decompress_amount
230+
);
231+
232+
// Verify the decompression worked
233+
let decompress_token_account_data = rpc
234+
.get_account(decompress_token_account_keypair.pubkey())
235+
.await
236+
.unwrap()
237+
.unwrap();
238+
239+
let decompress_token_account =
240+
TokenAccount::try_deserialize(&mut decompress_token_account_data.data.as_slice()).unwrap();
241+
242+
// Assert the SPL token account has the decompressed amount
243+
assert_eq!(decompress_token_account.amount, decompress_amount);
244+
assert_eq!(decompress_token_account.mint, mint_pubkey);
245+
assert_eq!(decompress_token_account.owner, transfer_recipient.pubkey());
246+
247+
println!(
248+
"Verified SPL token account after decompression: amount={}",
249+
decompress_token_account.amount
250+
);
251+
252+
// Verify the compressed account balance was reduced
253+
let updated_recipient_accounts = rpc
254+
.indexer()
255+
.unwrap()
256+
.get_compressed_token_accounts_by_owner(&transfer_recipient.pubkey(), None, None)
257+
.await
258+
.unwrap()
259+
.value
260+
.items;
261+
262+
if !updated_recipient_accounts.is_empty() {
263+
let updated_recipient_account = &updated_recipient_accounts[0];
264+
let remaining_compressed_amount = updated_recipient_account.token.amount;
265+
assert_eq!(
266+
remaining_compressed_amount,
267+
transfer_amount - decompress_amount
268+
);
269+
println!(
270+
"Verified remaining compressed balance: {}",
271+
remaining_compressed_amount
272+
);
273+
}
274+
275+
println!("Compression, transfer, and decompress test completed successfully!");
184276
}
185277

186278
async fn compress_spl_tokens(
@@ -193,13 +285,7 @@ async fn compress_spl_tokens(
193285
) -> Result<Signature, RpcError> {
194286
let mut remaining_accounts = PackedAccounts::default();
195287
let token_pool_pda = get_token_pool_pda(&mint);
196-
let config = TokenAccountsMetaConfig::compress(
197-
payer.pubkey(),
198-
payer.pubkey(),
199-
token_pool_pda,
200-
token_account,
201-
false,
202-
);
288+
let config = TokenAccountsMetaConfig::compress(token_pool_pda, token_account, false);
203289
remaining_accounts.add_pre_accounts_signer_mut(payer.pubkey());
204290
let metas = get_transfer_instruction_account_metas(config);
205291
println!("metas {:?}", metas.to_vec());
@@ -238,7 +324,7 @@ async fn transfer_compressed_tokens(
238324
compressed_account: &CompressedTokenAccount,
239325
) -> Result<Signature, RpcError> {
240326
let mut remaining_accounts = PackedAccounts::default();
241-
let config = TokenAccountsMetaConfig::new(payer.pubkey(), payer.pubkey());
327+
let config = TokenAccountsMetaConfig::new();
242328
remaining_accounts.add_pre_accounts_signer_mut(payer.pubkey());
243329
let metas = get_transfer_instruction_account_metas(config);
244330
remaining_accounts.add_pre_accounts_metas(metas.as_slice());
@@ -297,3 +383,72 @@ async fn transfer_compressed_tokens(
297383
rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer])
298384
.await
299385
}
386+
387+
async fn decompress_compressed_tokens(
388+
rpc: &mut LightProgramTest,
389+
payer: &Keypair,
390+
compressed_account: &CompressedTokenAccount,
391+
decompress_token_account: Pubkey,
392+
decompress_amount: u64,
393+
) -> Result<Signature, RpcError> {
394+
let mut remaining_accounts = PackedAccounts::default();
395+
let token_pool_pda = get_token_pool_pda(&compressed_account.token.mint);
396+
let config =
397+
TokenAccountsMetaConfig::decompress(token_pool_pda, decompress_token_account, false);
398+
remaining_accounts.add_pre_accounts_signer_mut(payer.pubkey());
399+
let metas = get_transfer_instruction_account_metas(config);
400+
remaining_accounts.add_pre_accounts_metas(metas.as_slice());
401+
402+
// Get validity proof from RPC
403+
let rpc_result = rpc
404+
.get_validity_proof(vec![compressed_account.account.hash], vec![], None)
405+
.await?
406+
.value;
407+
408+
let packed_tree_info = rpc_result.pack_tree_infos(&mut remaining_accounts);
409+
let output_tree_index = packed_tree_info
410+
.state_trees
411+
.as_ref()
412+
.unwrap()
413+
.output_tree_index;
414+
415+
// Use the tree info from the validity proof result
416+
let tree_info = packed_tree_info
417+
.state_trees
418+
.as_ref()
419+
.unwrap()
420+
.packed_tree_infos[0];
421+
422+
// Create input token data
423+
let token_data = vec![InputTokenDataWithContext {
424+
amount: compressed_account.token.amount,
425+
delegate_index: None,
426+
merkle_context: PackedMerkleContext {
427+
merkle_tree_pubkey_index: tree_info.merkle_tree_pubkey_index,
428+
nullifier_queue_pubkey_index: tree_info.queue_pubkey_index,
429+
leaf_index: tree_info.leaf_index,
430+
proof_by_index: tree_info.prove_by_index,
431+
},
432+
root_index: tree_info.root_index,
433+
lamports: None,
434+
tlv: None,
435+
}];
436+
437+
let (remaining_accounts, _, _) = remaining_accounts.to_account_metas();
438+
println!(" remaining_accounts: {:?}", remaining_accounts);
439+
440+
let instruction = Instruction {
441+
program_id: sdk_token_test::ID,
442+
accounts: [remaining_accounts].concat(),
443+
data: sdk_token_test::instruction::Decompress {
444+
validity_proof: rpc_result.proof,
445+
token_data,
446+
output_tree_index,
447+
mint: compressed_account.token.mint,
448+
}
449+
.data(),
450+
};
451+
452+
rpc.create_and_send_transaction(&[instruction], &payer.pubkey(), &[payer])
453+
.await
454+
}

sdk-libs/compressed-token-sdk/src/account.rs

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ use crate::error::TokenSdkError;
44
use light_compressed_token_types::{InputTokenDataWithContext, PackedTokenTransferOutputData};
55
use solana_pubkey::Pubkey;
66

7-
/// Compress, decompress, new
8-
/// Questions:
9-
/// 1. do we need to implement compress?
107
#[derive(Debug, PartialEq, Clone)]
118
pub struct CTokenAccount {
129
inputs: Vec<InputTokenDataWithContext>,
@@ -137,6 +134,10 @@ impl CTokenAccount {
137134
self.compression_amount
138135
}
139136

137+
pub fn owner(&self) -> Pubkey {
138+
Pubkey::new_from_array(self.owner)
139+
}
140+
140141
/// Consumes token account for instruction creation.
141142
pub fn into_inputs_and_outputs(
142143
self,
@@ -146,23 +147,6 @@ impl CTokenAccount {
146147
) {
147148
(self.inputs, self.output)
148149
}
149-
150-
// /// 1. Serializes the account data and sets the output data hash.
151-
// /// 2. Returns CompressedAccountInfo.
152-
// ///
153-
// /// Note this is an expensive operation
154-
// /// that should only be called once per instruction.
155-
// pub fn to_account_info(mut self) -> Result<CompressedAccountInfo, LightSdkError> {
156-
// if let Some(output) = self.account_info.output.as_mut() {
157-
// output.data_hash = self.account.hash::<Poseidon>()?;
158-
// output.data = self
159-
// .account
160-
// .try_to_vec()
161-
// .map_err(|_| LightSdkError::Borsh)?;
162-
// }
163-
// Ok(self.account_info)
164-
// }
165-
// }
166150
}
167151

168152
impl Deref for CTokenAccount {

sdk-libs/compressed-token-sdk/src/cpi/invoke.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use light_compressed_token_types::{
77
};
88
use solana_account_info::AccountInfo;
99
use solana_instruction::Instruction;
10-
use solana_msg::msg;
1110
use solana_pubkey::Pubkey;
1211

1312
use crate::{
@@ -18,12 +17,25 @@ use crate::{
1817
};
1918

2019
/// CPI inputs for compressed token operations
21-
#[derive(Debug, Clone, Default)]
20+
#[derive(Debug, Clone)]
2221
pub struct CpiInputs {
2322
pub token_accounts: Vec<CTokenAccount>,
2423
pub validity_proof: ValidityProof,
2524
pub cpi_context: Option<CompressedCpiContext>,
2625
pub with_transaction_hash: bool,
26+
pub filter_zero_amount_outputs: bool,
27+
}
28+
29+
impl Default for CpiInputs {
30+
fn default() -> Self {
31+
Self {
32+
token_accounts: Vec::new(),
33+
validity_proof: ValidityProof::default(),
34+
cpi_context: None,
35+
with_transaction_hash: false,
36+
filter_zero_amount_outputs: true,
37+
}
38+
}
2739
}
2840

2941
impl CpiInputs {
@@ -59,6 +71,7 @@ pub fn create_compressed_token_instruction(
5971
.token_accounts
6072
.iter()
6173
.any(|acc| acc.is_decompress());
74+
6275
let mint = *cpi_inputs.token_accounts[0].mint();
6376
let mut compress_or_decompress_amount: Option<u64> = None;
6477
for acc in cpi_inputs.token_accounts.iter() {
@@ -96,14 +109,12 @@ pub fn create_compressed_token_instruction(
96109
for token_account in cpi_inputs.token_accounts {
97110
let (inputs, output) = token_account.into_inputs_and_outputs();
98111
input_token_data_with_context.extend(inputs);
99-
output_compressed_accounts.push(output);
112+
if output.amount == 0 && cpi_inputs.filter_zero_amount_outputs {
113+
} else {
114+
output_compressed_accounts.push(output);
115+
}
100116
}
101-
msg!("inputs {:?}", input_token_data_with_context);
102-
msg!("outputs {:?}", output_compressed_accounts);
103-
msg!(
104-
"compress_or_decompress_amount {:?}",
105-
compress_or_decompress_amount
106-
);
117+
107118
// Create instruction data
108119
let instruction_data = CompressedTokenInstructionDataTransfer {
109120
proof: cpi_inputs.validity_proof.into(),
@@ -117,7 +128,7 @@ pub fn create_compressed_token_instruction(
117128
delegated_transfer: None, // TODO: support in separate pr
118129
lamports_change_account_merkle_tree_index: None, // TODO: support in separate pr
119130
};
120-
msg!("instruction_data {:?}", instruction_data);
131+
121132
// TODO: calculate exact len.
122133
let serialized = instruction_data
123134
.try_to_vec()

0 commit comments

Comments
 (0)