Skip to content

Commit 3a77432

Browse files
committed
stash cpi accounts unit tested
1 parent 106ad7c commit 3a77432

File tree

19 files changed

+901
-47
lines changed

19 files changed

+901
-47
lines changed

Cargo.lock

Lines changed: 11 additions & 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/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,16 @@ default = []
2121
[dependencies]
2222
light-compressed-token-sdk = { workspace = true }
2323
light-sdk = { workspace = true }
24+
solana-program = { workspace = true }
25+
light-macros = { workspace = true }
26+
anchor-lang = { workspace = true }
2427

2528
[dev-dependencies]
2629
light-program-test = { workspace = true, features = ["devenv"] }
2730
light-test-utils = { workspace = true }
2831
tokio = { workspace = true }
2932
serial_test = { workspace = true }
3033
solana-sdk = { workspace = true }
31-
anchor-lang = { workspace = true }
3234
anchor-spl = { workspace = true }
3335

3436
[lints.rust.unexpected_cfgs]
Lines changed: 92 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,92 @@
1-
// use light_macros::pubkey;
2-
// use light_sdk::{cpi::CpiSigner, derive_light_cpi_signer};
3-
// use solana_program::{
4-
// account_info::AccountInfo, entrypoint, program_error::ProgramError, pubkey::Pubkey,
5-
// };
6-
7-
// pub const ID: Pubkey = pubkey!("5p1t1GAaKtK1FKCh5Hd2Gu8JCu3eREhJm4Q2qYfTEPYK");
8-
// pub const LIGHT_CPI_SIGNER: CpiSigner
9-
// entrypoint!(process_instruction);
10-
11-
// pub fn process_instruction(
12-
// _program_id: &Pubkey,
13-
// _accounts: &[AccountInfo],
14-
// _instruction_data: &[u8],
15-
// ) -> Result<(), ProgramError> {
16-
// // Empty program for now
17-
// Ok(())
18-
// }
1+
#![allow(unexpected_cfgs)]
2+
3+
use anchor_lang::{prelude::*, solana_program::program::invoke, Discriminator};
4+
use light_compressed_token_sdk::cpi::{
5+
create_compressed_token_instruction, CpiAccounts, CpiInputs,
6+
};
7+
8+
declare_id!("5p1t1GAaKtK1FKCh5Hd2Gu8JCu3eREhJm4Q2qYfTEPYK");
9+
10+
#[program]
11+
pub mod sdk_token_test {
12+
13+
use super::*;
14+
15+
pub fn compress<'info>(
16+
ctx: Context<'_, '_, '_, 'info, Generic<'info>>,
17+
output_tree_index: u8,
18+
recipient: Pubkey,
19+
mint: Pubkey, // TODO: deserialize from token account.
20+
amount: u64,
21+
) -> Result<()> {
22+
let light_cpi_accounts =
23+
CpiAccounts::new(ctx.accounts.signer.as_ref(), ctx.remaining_accounts);
24+
25+
let mut token_account = light_compressed_token_sdk::account::CTokenAccount::new_empty(
26+
mint,
27+
recipient,
28+
output_tree_index,
29+
);
30+
token_account.compress(amount).unwrap();
31+
32+
let cpi_inputs = CpiInputs::new_compress(vec![token_account]);
33+
34+
// TODO: add to program error conversion
35+
let instruction =
36+
create_compressed_token_instruction(cpi_inputs, &light_cpi_accounts).unwrap();
37+
let account_infos = light_cpi_accounts.to_account_infos();
38+
invoke(&instruction, account_infos.as_slice())?;
39+
40+
Ok(())
41+
}
42+
43+
// pub fn transfer<'info>(
44+
// ctx: Context<'_, '_, '_, 'info, UpdateNestedData<'info>>,
45+
// proof: ValidityProof,
46+
// my_compressed_account: MyCompressedAccount,
47+
// account_meta: CompressedAccountMeta,
48+
// nested_data: NestedData,
49+
// ) -> Result<()> {
50+
// let mut my_compressed_account = LightAccount::<'_, MyCompressedAccount>::new_mut(
51+
// &crate::ID,
52+
// &account_meta,
53+
// my_compressed_account,
54+
// )
55+
// .map_err(ProgramError::from)?;
56+
57+
// my_compressed_account.nested = nested_data;
58+
59+
// let light_cpi_accounts = CpiAccounts::new(
60+
// ctx.accounts.signer.as_ref(),
61+
// ctx.remaining_accounts,
62+
// crate::LIGHT_CPI_SIGNER,
63+
// );
64+
65+
// let cpi_inputs = CpiInputs::new(
66+
// proof,
67+
// vec![my_compressed_account
68+
// .to_account_info()
69+
// .map_err(ProgramError::from)?],
70+
// );
71+
72+
// cpi_inputs
73+
// .invoke_light_system_program(light_cpi_accounts)
74+
// .map_err(ProgramError::from)?;
75+
76+
// Ok(())
77+
// }
78+
79+
// pub fn decompress<'info>(
80+
// ctx: Context<'_, '_, '_, 'info, WithoutCompressedAccount<'info>>,
81+
// name: String,
82+
// ) -> Result<()> {
83+
// ctx.accounts.my_regular_account.name = name;
84+
// Ok(())
85+
// }
86+
}
87+
88+
#[derive(Accounts)]
89+
pub struct Generic<'info> {
90+
#[account(mut)]
91+
pub signer: Signer<'info>,
92+
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ use solana_sdk::{signature::Keypair, signer::Signer};
1010
#[tokio::test]
1111
async fn test_create_token_account_and_mint() {
1212
// Initialize the test environment
13-
let mut rpc = LightProgramTest::new(ProgramTestConfig::new(false, None))
14-
.await
15-
.unwrap();
13+
let mut rpc = LightProgramTest::new(ProgramTestConfig::new(
14+
false,
15+
Some(vec![("sdk_token_test", sdk_token_test::ID)]),
16+
))
17+
.await
18+
.unwrap();
1619

1720
let payer = rpc.get_payer().insecure_clone();
1821

sdk-libs/compressed-token-sdk/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ anchor = ["anchor-lang", "light-compressed-token-types/anchor"]
1010
[dependencies]
1111
# Light Protocol dependencies
1212
light-compressed-token-types = { workspace = true }
13+
light-compressed-account = { workspace = true }
14+
light-sdk = { workspace = true }
1315
light-macros = { workspace = true }
1416
thiserror = { workspace = true }
1517
# Serialization
@@ -18,6 +20,13 @@ borsh = { workspace = true }
1820
# Solana dependencies
1921
solana-pubkey = { workspace = true }
2022
solana-instruction = { workspace = true }
23+
solana-account-info = { workspace = true }
24+
solana-cpi = { workspace = true }
2125

2226
# Optional Anchor dependency
2327
anchor-lang = { workspace = true, optional = true }
28+
29+
[dev-dependencies]
30+
light-account-checks = { workspace = true, features = ["test-only", "solana"] }
31+
light-compressed-token = { workspace = true }
32+
anchor-lang = { workspace = true }

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

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
use std::ops::Deref;
22

3-
use crate::error::CTokenSdkError;
3+
use crate::error::TokenSdkError;
44
use light_compressed_token_types::{InputTokenDataWithContext, PackedTokenTransferOutputData};
55
use solana_pubkey::Pubkey;
66

77
/// Compress, decompress, new
88
/// Questions:
99
/// 1. do we need to implement compress?
10-
#[derive(Debug, PartialEq)]
10+
#[derive(Debug, PartialEq, Clone)]
1111
pub struct CTokenAccount {
1212
inputs: Vec<InputTokenDataWithContext>,
1313
output: PackedTokenTransferOutputData,
1414
compression_amount: Option<u64>,
1515
is_compress: bool,
1616
is_decompress: bool,
17+
mint: Pubkey,
1718
}
1819

1920
impl CTokenAccount {
2021
pub fn new(
22+
mint: Pubkey,
2123
owner: Pubkey,
2224
token_data: Vec<InputTokenDataWithContext>,
2325
output_merkle_tree_index: u8,
@@ -37,10 +39,11 @@ impl CTokenAccount {
3739
compression_amount: None,
3840
is_compress: false,
3941
is_decompress: false,
42+
mint,
4043
}
4144
}
4245

43-
pub fn new_empty(owner: Pubkey, output_merkle_tree_index: u8) -> Self {
46+
pub fn new_empty(mint: Pubkey, owner: Pubkey, output_merkle_tree_index: u8) -> Self {
4447
Self {
4548
inputs: vec![],
4649
output: PackedTokenTransferOutputData {
@@ -53,6 +56,7 @@ impl CTokenAccount {
5356
compression_amount: None,
5457
is_compress: false,
5558
is_decompress: false,
59+
mint,
5660
}
5761
}
5862

@@ -61,9 +65,9 @@ impl CTokenAccount {
6165
recipient: &Pubkey,
6266
amount: u64,
6367
output_merkle_tree_index: Option<u8>,
64-
) -> Result<Self, CTokenSdkError> {
68+
) -> Result<Self, TokenSdkError> {
6569
if amount > self.output.amount {
66-
return Err(CTokenSdkError::InsufficientBalance);
70+
return Err(TokenSdkError::InsufficientBalance);
6771
}
6872
// TODO: skip outputs with zero amount when creating the instruction data.
6973
self.output.amount -= amount;
@@ -81,12 +85,16 @@ impl CTokenAccount {
8185
tlv: None,
8286
merkle_tree_index,
8387
},
88+
mint: self.mint,
8489
})
8590
}
8691

87-
pub fn compress(&mut self, amount: u64) -> Result<(), CTokenSdkError> {
92+
pub fn compress(&mut self, amount: u64) -> Result<(), TokenSdkError> {
8893
self.output.amount += amount;
8994
self.is_compress = true;
95+
if self.is_decompress {
96+
return Err(TokenSdkError::CannotCompressAndDecompress);
97+
}
9098

9199
match self.compression_amount.as_mut() {
92100
Some(amount_ref) => *amount_ref += amount,
@@ -95,17 +103,36 @@ impl CTokenAccount {
95103
Ok(())
96104
}
97105

98-
pub fn decompress(&mut self, amount: u64) -> Result<(), CTokenSdkError> {
106+
pub fn decompress(&mut self, amount: u64) -> Result<(), TokenSdkError> {
107+
if self.is_compress {
108+
return Err(TokenSdkError::CannotCompressAndDecompress);
109+
}
110+
if self.output.amount < amount {
111+
return Err(TokenSdkError::InsufficientBalance);
112+
}
99113
self.output.amount -= amount;
114+
100115
self.is_decompress = true;
101116

102117
match self.compression_amount.as_mut() {
103-
Some(amount_ref) => *amount_ref -= amount,
118+
Some(amount_ref) => *amount_ref += amount,
104119
None => self.compression_amount = Some(amount),
105120
}
106121
Ok(())
107122
}
108123

124+
pub fn is_compress(&self) -> bool {
125+
self.is_compress
126+
}
127+
128+
pub fn is_decompress(&self) -> bool {
129+
self.is_decompress
130+
}
131+
132+
pub fn mint(&self) -> &Pubkey {
133+
&self.mint
134+
}
135+
109136
/// Consumes token account for instruction creation.
110137
pub fn into_inputs_and_outputs(
111138
self,

0 commit comments

Comments
 (0)