Skip to content

Commit 0e3e5f4

Browse files
add test-sdk-derived program
1 parent cfd0202 commit 0e3e5f4

16 files changed

+1273
-33
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ members = [
5050
"sparse-merkle-tree",
5151
"program-tests/anchor-compressible-user",
5252
"program-tests/anchor-compressible-user-derived",
53+
"program-tests/sdk-test-derived",
5354
]
5455

5556
resolver = "2"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
[package]
2+
name = "sdk-test-derived"
3+
version = "1.0.0"
4+
description = "Test program using generalized account compression"
5+
repository = "https://github.com/Lightprotocol/light-protocol"
6+
license = "Apache-2.0"
7+
edition = "2021"
8+
9+
[lib]
10+
crate-type = ["cdylib", "lib"]
11+
name = "sdk_test_derived"
12+
13+
[features]
14+
no-entrypoint = []
15+
no-idl = []
16+
no-log-ix-name = []
17+
cpi = ["no-entrypoint"]
18+
test-sbf = []
19+
default = []
20+
21+
[dependencies]
22+
light-sdk = { workspace = true }
23+
light-sdk-types = { workspace = true }
24+
light-hasher = { workspace = true, features = ["solana"] }
25+
solana-program = { workspace = true }
26+
light-macros = { workspace = true, features = ["solana"] }
27+
borsh = { workspace = true }
28+
light-compressed-account = { workspace = true, features = ["solana"] }
29+
solana-clock = { workspace = true }
30+
solana-sysvar = { workspace = true }
31+
arrayvec = { workspace = true }
32+
33+
[dev-dependencies]
34+
light-program-test = { workspace = true, features = ["devenv"] }
35+
tokio = { workspace = true }
36+
solana-sdk = { workspace = true }
37+
38+
[lints.rust.unexpected_cfgs]
39+
level = "allow"
40+
check-cfg = [
41+
'cfg(target_os, values("solana"))',
42+
'cfg(feature, values("frozen-abi", "no-entrypoint"))',
43+
]
44+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[target.bpfel-unknown-unknown.dependencies.std]
2+
features = []
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use borsh::{BorshDeserialize, BorshSerialize};
2+
use light_sdk::{
3+
compressible::{compress_pda, CompressibleConfig},
4+
cpi::CpiAccounts,
5+
error::LightSdkError,
6+
instruction::{account_meta::CompressedAccountMeta, ValidityProof},
7+
};
8+
use light_sdk_types::CpiAccountsConfig;
9+
use solana_program::account_info::AccountInfo;
10+
11+
use crate::decompress_dynamic_pda::MyPdaAccount;
12+
13+
/// Compresses a PDA back into a compressed account
14+
/// Anyone can call this after the timeout period has elapsed
15+
pub fn compress_dynamic_pda(
16+
accounts: &[AccountInfo],
17+
instruction_data: &[u8],
18+
) -> Result<(), LightSdkError> {
19+
let mut instruction_data = instruction_data;
20+
let instruction_data = CompressFromPdaInstructionData::deserialize(&mut instruction_data)
21+
.map_err(|_| LightSdkError::Borsh)?;
22+
23+
let pda_account = &accounts[1];
24+
let rent_recipient = &accounts[2];
25+
let config_account = &accounts[3];
26+
27+
// Load config
28+
let config = CompressibleConfig::load_checked(config_account, &crate::ID)?;
29+
30+
// CHECK: rent recipient from config
31+
if rent_recipient.key != &config.rent_recipient {
32+
return Err(LightSdkError::ConstraintViolation);
33+
}
34+
35+
// Cpi accounts
36+
let cpi_config = CpiAccountsConfig::new(crate::LIGHT_CPI_SIGNER);
37+
let cpi_accounts = CpiAccounts::new_with_config(
38+
&accounts[0],
39+
&accounts[instruction_data.system_accounts_offset as usize..],
40+
cpi_config,
41+
);
42+
43+
compress_pda::<MyPdaAccount>(
44+
pda_account,
45+
&instruction_data.compressed_account_meta,
46+
instruction_data.proof,
47+
cpi_accounts,
48+
&crate::ID,
49+
rent_recipient,
50+
&config.compression_delay,
51+
)?;
52+
53+
// any other program logic here...
54+
55+
Ok(())
56+
}
57+
58+
#[derive(Clone, Debug, Default, BorshDeserialize, BorshSerialize)]
59+
pub struct CompressFromPdaInstructionData {
60+
pub proof: ValidityProof,
61+
pub compressed_account_meta: CompressedAccountMeta,
62+
pub system_accounts_offset: u8,
63+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use borsh::{BorshDeserialize, BorshSerialize};
2+
use light_sdk::{compressible::create_compression_config_checked, error::LightSdkError};
3+
use solana_program::account_info::AccountInfo;
4+
use solana_program::pubkey::Pubkey;
5+
6+
/// Creates a new compressible config PDA
7+
pub fn process_create_compression_config_checked(
8+
accounts: &[AccountInfo],
9+
instruction_data: &[u8],
10+
) -> Result<(), LightSdkError> {
11+
let mut instruction_data = instruction_data;
12+
let instruction_data = CreateConfigInstructionData::deserialize(&mut instruction_data)
13+
.map_err(|_| LightSdkError::Borsh)?;
14+
15+
// Get accounts
16+
let payer = &accounts[0];
17+
let config_account = &accounts[1];
18+
let update_authority = &accounts[2];
19+
let system_program = &accounts[3];
20+
let program_data_account = &accounts[4];
21+
22+
create_compression_config_checked(
23+
config_account,
24+
update_authority,
25+
program_data_account,
26+
&instruction_data.rent_recipient,
27+
instruction_data.address_space,
28+
instruction_data.compression_delay,
29+
payer,
30+
system_program,
31+
&crate::ID,
32+
)?;
33+
34+
Ok(())
35+
}
36+
37+
#[derive(Clone, Debug, BorshDeserialize, BorshSerialize)]
38+
pub struct CreateConfigInstructionData {
39+
pub rent_recipient: Pubkey,
40+
/// Address spaces (1-4 allowed, first is primary for writing)
41+
pub address_space: Vec<Pubkey>,
42+
pub compression_delay: u32,
43+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use borsh::{BorshDeserialize, BorshSerialize};
2+
use light_compressed_account::instruction_data::data::ReadOnlyAddress;
3+
use light_sdk::{
4+
compressible::{compress_pda_new, CompressibleConfig, CompressionInfo},
5+
cpi::CpiAccounts,
6+
error::LightSdkError,
7+
instruction::{PackedAddressTreeInfo, ValidityProof},
8+
};
9+
use solana_program::account_info::AccountInfo;
10+
11+
use crate::decompress_dynamic_pda::MyPdaAccount;
12+
13+
/// INITS a PDA and compresses it into a new compressed account.
14+
pub fn create_dynamic_pda(
15+
accounts: &[AccountInfo],
16+
instruction_data: &[u8],
17+
) -> Result<(), LightSdkError> {
18+
let mut instruction_data = instruction_data;
19+
let instruction_data = CreateDynamicPdaInstructionData::deserialize(&mut instruction_data)
20+
.map_err(|_| LightSdkError::Borsh)?;
21+
22+
let fee_payer = &accounts[0];
23+
// UNCHECKED: ...caller program checks this.
24+
let pda_account = &accounts[1];
25+
let rent_recipient = &accounts[2];
26+
let config_account = &accounts[3];
27+
28+
// Load config
29+
let config = CompressibleConfig::load_checked(config_account, &crate::ID)?;
30+
31+
// CHECK: rent recipient from config
32+
if rent_recipient.key != &config.rent_recipient {
33+
return Err(LightSdkError::ConstraintViolation);
34+
}
35+
36+
// Cpi accounts
37+
let cpi_accounts_struct = CpiAccounts::new(fee_payer, &accounts[4..], crate::LIGHT_CPI_SIGNER);
38+
39+
// the onchain PDA is the seed for the cPDA. this way devs don't have to
40+
// change their onchain PDA checks.
41+
let new_address_params = instruction_data
42+
.address_tree_info
43+
.into_new_address_params_packed(pda_account.key.to_bytes());
44+
45+
// We do not have to serialize into the PDA account, it's closed at the end
46+
// of this invocation.
47+
let mut pda_account_data = MyPdaAccount::try_from_slice(&pda_account.data.borrow())
48+
.map_err(|_| LightSdkError::Borsh)?;
49+
50+
// Initialize compression info with current slot and decompressed state
51+
pda_account_data.compression_info = CompressionInfo::new()?;
52+
53+
compress_pda_new::<MyPdaAccount>(
54+
pda_account,
55+
instruction_data.compressed_address,
56+
new_address_params,
57+
instruction_data.output_state_tree_index,
58+
instruction_data.proof,
59+
cpi_accounts_struct,
60+
&crate::ID,
61+
rent_recipient,
62+
&config.address_space,
63+
instruction_data.read_only_addresses,
64+
)?;
65+
66+
Ok(())
67+
}
68+
69+
#[derive(Clone, Debug, Default, BorshDeserialize, BorshSerialize)]
70+
pub struct CreateDynamicPdaInstructionData {
71+
pub proof: ValidityProof,
72+
pub compressed_address: [u8; 32],
73+
pub address_tree_info: PackedAddressTreeInfo,
74+
/// Optional read-only addresses for exclusion proofs (same address, different trees)
75+
pub read_only_addresses: Option<Vec<ReadOnlyAddress>>,
76+
pub output_state_tree_index: u8,
77+
}

0 commit comments

Comments
 (0)