Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/actions/setup-and-build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ runs:
target/deploy/create_address_test_program.so
target/deploy/sdk_anchor_test.so
target/deploy/sdk-compressible-test.so
target/deploy/csdk_anchor_derived_test.so
target/deploy/csdk_anchor_full_derived_test.so
key: ${{ runner.os }}-program-tests-${{ hashFiles('program-tests/**/Cargo.toml', 'program-tests/**/Cargo.lock', 'program-tests/**/*.rs', 'test-programs/**/Cargo.toml', 'test-programs/**/*.rs', 'sdk-tests/**/Cargo.toml', 'sdk-tests/**/*.rs') }}
restore-keys: |
${{ runner.os }}-program-tests-
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
- program: native
sub-tests: '["cargo-test-sbf -p sdk-native-test", "cargo-test-sbf -p sdk-v1-native-test", "cargo-test-sbf -p client-test"]'
- program: anchor & pinocchio
sub-tests: '["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -p sdk-compressible-test", "cargo-test-sbf -p sdk-pinocchio-v1-test", "cargo-test-sbf -p sdk-pinocchio-v2-test", "cargo-test-sbf -p pinocchio-nostd-test"]'
sub-tests: '["cargo-test-sbf -p sdk-anchor-test", "cargo-test-sbf -p sdk-compressible-test", "cargo-test-sbf -p csdk-anchor-derived-test", "cargo-test-sbf -p csdk-anchor-full-derived-test", "cargo-test-sbf -p sdk-pinocchio-v1-test", "cargo-test-sbf -p sdk-pinocchio-v2-test", "cargo-test-sbf -p pinocchio-nostd-test"]'
- program: token test
sub-tests: '["cargo-test-sbf -p sdk-token-test"]'
- program: sdk-libs
Expand Down
77 changes: 76 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ members = [
"sdk-tests/sdk-v1-native-test",
"sdk-tests/sdk-token-test",
"sdk-tests/sdk-compressible-test",
"sdk-tests/csdk-anchor-derived-test",
"sdk-tests/csdk-anchor-full-derived-test",
"forester-utils",
"forester",
"sparse-merkle-tree",
Expand Down
6 changes: 5 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion program-tests/registry-test/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ async fn test_initialize_protocol_config() {
config: ProgramTestConfig::default(),
transaction_counter: 0,
pre_context: None,
auto_compress_programs: Vec::new(),
auto_mine_cold_state_programs: Vec::new(),
};

let payer = rpc.get_payer().insecure_clone();
Expand Down
4 changes: 2 additions & 2 deletions programs/system/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ reinit = []
default = ["reinit"]
test-sbf = []
readonly = []
profile-program = ["light-program-profiler/profile-program"]
profile-heap = ["light-program-profiler/profile-heap", "dep:light-heap"]
profile-program = []
profile-heap = ["dep:light-heap"]
custom-heap = []

[dependencies]
Expand Down
160 changes: 160 additions & 0 deletions sdk-libs/compressed-token-sdk/src/decompress_runtime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
//! Runtime helpers for token decompression.
use light_ctoken_types::instructions::transfer2::MultiInputTokenDataWithContext;
use light_sdk::{cpi::v2::CpiAccounts, instruction::ValidityProof};
use light_sdk_types::instruction::account_meta::CompressedAccountMetaNoLamportsNoAddress;
use solana_account_info::AccountInfo;
use solana_msg::msg;
use solana_program_error::ProgramError;
use solana_pubkey::Pubkey;

use crate::compat::PackedCTokenData;

/// Trait for getting token account seeds.
pub trait CTokenSeedProvider: Copy {
/// Type of accounts struct needed for seed derivation.
type Accounts<'info>;

/// Get seeds for the token account PDA (used for decompression).
fn get_seeds<'a, 'info>(
&self,
accounts: &'a Self::Accounts<'info>,
remaining_accounts: &'a [AccountInfo<'info>],
) -> Result<(Vec<Vec<u8>>, Pubkey), ProgramError>;

/// Get authority seeds for signing during compression.
fn get_authority_seeds<'a, 'info>(
&self,
accounts: &'a Self::Accounts<'info>,
remaining_accounts: &'a [AccountInfo<'info>],
) -> Result<(Vec<Vec<u8>>, Pubkey), ProgramError>;
}

/// Token decompression processor.
#[inline(never)]
#[allow(clippy::too_many_arguments)]
pub fn process_decompress_tokens_runtime<'info, 'a, 'b, V, A>(
accounts_for_seeds: &A,
remaining_accounts: &[AccountInfo<'info>],
fee_payer: &AccountInfo<'info>,
ctoken_program: &AccountInfo<'info>,
ctoken_rent_sponsor: &AccountInfo<'info>,
ctoken_cpi_authority: &AccountInfo<'info>,
ctoken_config: &AccountInfo<'info>,
config: &AccountInfo<'info>,
ctoken_accounts: Vec<(
PackedCTokenData<V>,
CompressedAccountMetaNoLamportsNoAddress,
)>,
proof: ValidityProof,
cpi_accounts: &CpiAccounts<'b, 'info>,
post_system_accounts: &[AccountInfo<'info>],
has_pdas: bool,
program_id: &Pubkey,
) -> Result<(), ProgramError>
where
V: CTokenSeedProvider<Accounts<'info> = A>,
A: 'info,
{
let mut token_decompress_indices: Box<Vec<crate::instructions::DecompressFullIndices>> =
Box::new(Vec::with_capacity(ctoken_accounts.len()));
let mut token_signers_seed_groups: Vec<Vec<Vec<u8>>> =
Vec::with_capacity(ctoken_accounts.len());
let packed_accounts = post_system_accounts;

let authority = cpi_accounts
.authority()
.map_err(|_| ProgramError::MissingRequiredSignature)?;
let cpi_context = cpi_accounts
.cpi_context()
.map_err(|_| ProgramError::MissingRequiredSignature)?;

for (token_data, meta) in ctoken_accounts.into_iter() {
let owner_index: u8 = token_data.token_data.owner;
let mint_index: u8 = token_data.token_data.mint;
let mint_info = &packed_accounts[mint_index as usize];
let owner_info = &packed_accounts[owner_index as usize];

// Use trait method to get seeds (program-specific)
let (ctoken_signer_seeds, derived_token_account_address) = token_data
.variant
.get_seeds(accounts_for_seeds, remaining_accounts)?;

if derived_token_account_address != *owner_info.key {
msg!(
"derived_token_account_address: {:?}",
derived_token_account_address
);
msg!("owner_info.key: {:?}", owner_info.key);
return Err(ProgramError::InvalidAccountData);
}

let seed_refs: Vec<&[u8]> = ctoken_signer_seeds.iter().map(|s| s.as_slice()).collect();
let seeds_slice: &[&[u8]] = &seed_refs;

crate::instructions::create_token_account::create_ctoken_account_signed(
*program_id,
fee_payer.clone(),
owner_info.clone(),
mint_info.clone(),
*authority.key,
seeds_slice,
ctoken_rent_sponsor.clone(),
ctoken_config.clone(),
Some(2),
None,
)?;

let source = MultiInputTokenDataWithContext {
owner: token_data.token_data.owner,
amount: token_data.token_data.amount,
has_delegate: token_data.token_data.has_delegate,
delegate: token_data.token_data.delegate,
mint: token_data.token_data.mint,
version: token_data.token_data.version,
merkle_context: meta.tree_info.into(),
root_index: meta.tree_info.root_index,
};
let decompress_index = crate::instructions::DecompressFullIndices {
source,
destination_index: owner_index,
};
token_decompress_indices.push(decompress_index);
token_signers_seed_groups.push(ctoken_signer_seeds);
}

let ctoken_ix = crate::instructions::decompress_full_ctoken_accounts_with_indices(
*fee_payer.key,
proof,
if has_pdas {
Some(*cpi_context.key)
} else {
None
},
&token_decompress_indices,
packed_accounts,
)
.map_err(ProgramError::from)?;

let mut all_account_infos: Vec<AccountInfo<'info>> =
Vec::with_capacity(1 + post_system_accounts.len() + 3);
all_account_infos.push(fee_payer.clone());
all_account_infos.push(ctoken_cpi_authority.clone());
all_account_infos.push(ctoken_program.clone());
all_account_infos.push(ctoken_rent_sponsor.clone());
all_account_infos.push(config.clone());
all_account_infos.extend_from_slice(post_system_accounts);

let signer_seed_refs: Vec<Vec<&[u8]>> = token_signers_seed_groups
.iter()
.map(|group| group.iter().map(|s| s.as_slice()).collect())
.collect();
let signer_seed_slices: Vec<&[&[u8]]> = signer_seed_refs.iter().map(|g| g.as_slice()).collect();

solana_cpi::invoke_signed(
&ctoken_ix,
all_account_infos.as_slice(),
signer_seed_slices.as_slice(),
)?;

Ok(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,9 @@ pub fn compress_and_close_ctoken_accounts<'info>(
};

// Determine rent recipient from extension or use default
let actual_rent_sponsor = if rent_sponsor_pubkey.is_none() {
let actual_rent_sponsor = if let Some(sponsor) = rent_sponsor_pubkey {
sponsor
} else {
// Check if there's a rent recipient in the compressible extension
if let Some(extensions) = &compressed_token.extensions {
for extension in extensions {
Expand All @@ -364,8 +366,6 @@ pub fn compress_and_close_ctoken_accounts<'info>(
}
}
rent_sponsor_pubkey.ok_or(TokenSdkError::InvalidAccountData)?
} else {
rent_sponsor_pubkey.unwrap()
};

let destination_pubkey = if with_compression_authority {
Expand Down Expand Up @@ -408,6 +408,7 @@ pub fn compress_and_close_ctoken_accounts<'info>(
/// subset of `remaining_accounts`.
#[allow(clippy::too_many_arguments)]
#[profile]
#[allow(clippy::extra_unused_lifetimes)]
pub fn compress_and_close_ctoken_accounts_signed<'b, 'info>(
token_accounts_to_compress: &[AccountInfoToCompress<'info>],
fee_payer: AccountInfo<'info>,
Expand Down
3 changes: 2 additions & 1 deletion sdk-libs/compressed-token-sdk/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod account;
pub mod account2;
pub mod ctoken;
pub mod decompress_runtime;
pub mod error;
pub mod instructions;
pub mod pack;
Expand All @@ -13,7 +14,7 @@ pub mod utils;
use anchor_lang::{AnchorDeserialize, AnchorSerialize};
#[cfg(not(feature = "anchor"))]
use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize};
// Re-export
pub use decompress_runtime::{process_decompress_tokens_runtime, CTokenSeedProvider};
pub use light_compressed_token_types::*;
pub use pack::{compat, Pack, Unpack};
pub use utils::{
Expand Down
1 change: 1 addition & 0 deletions sdk-libs/compressible-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ solana-account = { workspace = true }

light-client = { workspace = true, features = ["v2"] }
light-sdk = { workspace = true, features = ["v2", "cpi-context"] }
light-compressed-account = { workspace = true, features = ["std"] }

anchor-lang = { workspace = true, features = ["idl-build"], optional = true }
borsh = { workspace = true }
Expand Down
Loading