From a4a6c80bd8bb6dd6d50447e7ccb87d12678b15f6 Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sat, 24 Jan 2026 20:45:44 +0000 Subject: [PATCH 01/10] add caller program rent sponsor --- .../src/interface/light_program_interface.rs | 10 ++++++++++ .../src/lib.rs | 8 ++++++++ .../tests/amm_test.rs | 17 +++++++++++------ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/sdk-libs/client/src/interface/light_program_interface.rs b/sdk-libs/client/src/interface/light_program_interface.rs index 7815037bc2..f145309702 100644 --- a/sdk-libs/client/src/interface/light_program_interface.rs +++ b/sdk-libs/client/src/interface/light_program_interface.rs @@ -229,6 +229,16 @@ pub trait LightProgramInterface: Sized { #[must_use] fn program_id(&self) -> Pubkey; + /// Returns the program's LightConfig PDA. + /// Used for PDA decompression instructions. + #[must_use] + fn light_config_pda(&self) -> Pubkey; + + /// Returns the program's rent sponsor PDA for cPDAs. + /// Derived via `derive_light_rent_sponsor_pda!` macro. + #[must_use] + fn light_rent_sponsor_pda(&self) -> Pubkey; + /// Construct SDK from root account(s). fn from_keyed_accounts(accounts: &[AccountInterface]) -> Result; diff --git a/sdk-tests/csdk-anchor-full-derived-test-sdk/src/lib.rs b/sdk-tests/csdk-anchor-full-derived-test-sdk/src/lib.rs index 6966efb783..c8d9bc1df2 100644 --- a/sdk-tests/csdk-anchor-full-derived-test-sdk/src/lib.rs +++ b/sdk-tests/csdk-anchor-full-derived-test-sdk/src/lib.rs @@ -318,6 +318,14 @@ impl LightProgramInterface for AmmSdk { PROGRAM_ID } + fn light_config_pda(&self) -> Pubkey { + light_sdk::LightConfig::derive_default_pda(&PROGRAM_ID).0 + } + + fn light_rent_sponsor_pda(&self) -> Pubkey { + csdk_anchor_full_derived_test::program_rent_sponsor() + } + fn from_keyed_accounts(accounts: &[AccountInterface]) -> Result { let mut sdk = Self::new(); diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs index 1ae4913fb1..8f4c8ab7ee 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs @@ -21,7 +21,6 @@ use light_client::interface::{ CreateAccountsProofInput, InitializeRentFreeConfig, LightProgramInterface, }; use light_compressible::rent::SLOTS_PER_EPOCH; -use light_macros::pubkey; use light_program_test::{ program_test::{setup_mock_program_data, LightProgramTest, TestRpc}, Indexer, ProgramTestConfig, Rpc, @@ -36,8 +35,6 @@ use solana_keypair::Keypair; use solana_pubkey::Pubkey; use solana_signer::Signer; -const RENT_SPONSOR: Pubkey = pubkey!("CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG"); - async fn assert_onchain_exists(rpc: &mut LightProgramTest, pda: &Pubkey) { assert!( rpc.get_account(*pda).await.unwrap().is_some(), @@ -141,11 +138,19 @@ async fn setup() -> AmmTestContext { // Setup mock program data and compression config let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Get program's rent sponsor PDA (derived via derive_light_rent_sponsor_pda! macro) + let program_rent_sponsor = csdk_anchor_full_derived_test::program_rent_sponsor(); + + // Fund the rent sponsor PDA before config init + light_test_utils::airdrop_lamports(&mut rpc, &program_rent_sponsor, 10_000_000_000) + .await + .unwrap(); + let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + program_rent_sponsor, payer.pubkey(), ) .build(); @@ -590,8 +595,8 @@ async fn test_amm_full_lifecycle() { let decompress_ixs = create_load_instructions( &all_specs, ctx.payer.pubkey(), - ctx.config_pda, - ctx.payer.pubkey(), + sdk.light_config_pda(), + sdk.light_rent_sponsor_pda(), &ctx.rpc, ) .await From 660c3c1ef52bd13d6cc64784d48aead190493694 Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sat, 24 Jan 2026 21:35:42 +0000 Subject: [PATCH 02/10] update rent sponsor use --- Cargo.lock | 2 +- forester/tests/test_compressible_pda.rs | 34 ++-- .../client/src/interface/initialize_config.rs | 32 ++- .../src/interface/light_program_interface.rs | 4 +- sdk-libs/macros/src/lib.rs | 34 ++-- sdk-libs/macros/src/rent_sponsor.rs | 189 ++++-------------- sdk-libs/sdk-types/src/lib.rs | 32 +++ sdk-libs/sdk/src/lib.rs | 5 +- sdk-libs/sdk/src/utils.rs | 29 ++- sdk-libs/token-sdk/src/anchor.rs | 3 +- .../csdk-anchor-full-derived-test/src/lib.rs | 57 +++++- .../tests/amm_test.rs | 16 +- .../tests/basic_test.rs | 20 +- .../tests/d10_token_accounts_test.rs | 5 +- .../tests/integration_tests.rs | 5 +- .../tests/mint/metadata_test.rs | 5 +- sdk-tests/single-ata-test/tests/test.rs | 5 +- sdk-tests/single-mint-test/tests/test.rs | 5 +- sdk-tests/single-pda-test/tests/test.rs | 5 +- sdk-tests/single-token-test/tests/test.rs | 5 +- 20 files changed, 254 insertions(+), 238 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 246f03d9b0..62f2882e46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "Inflector" diff --git a/forester/tests/test_compressible_pda.rs b/forester/tests/test_compressible_pda.rs index 5e32ab4e67..eea3c8aa9a 100644 --- a/forester/tests/test_compressible_pda.rs +++ b/forester/tests/test_compressible_pda.rs @@ -284,13 +284,8 @@ async fn test_compressible_pda_bootstrap() { .await .expect("Failed to airdrop to authority"); - // Fund rent sponsor - rpc.airdrop_lamports(&RENT_SPONSOR, 10_000_000_000) - .await - .expect("Failed to fund rent sponsor"); - - // Initialize compression config - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + // Initialize compression config (includes rent sponsor funding) + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &authority.pubkey(), &Pubkey::find_program_address( @@ -300,10 +295,11 @@ async fn test_compressible_pda_bootstrap() { .0, RENT_SPONSOR, authority.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &authority.pubkey(), &[&authority]) + rpc.create_and_send_transaction(&init_config_ixs, &authority.pubkey(), &[&authority]) .await .expect("Initialize config should succeed"); @@ -474,13 +470,8 @@ async fn test_compressible_pda_compression() { .await .expect("Failed to airdrop to authority"); - // Fund rent sponsor - rpc.airdrop_lamports(&RENT_SPONSOR, 10_000_000_000) - .await - .expect("Failed to fund rent sponsor"); - - // Initialize compression config - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + // Initialize compression config (includes rent sponsor funding) + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &authority.pubkey(), &Pubkey::find_program_address( @@ -490,10 +481,11 @@ async fn test_compressible_pda_compression() { .0, RENT_SPONSOR, authority.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &authority.pubkey(), &[&authority]) + rpc.create_and_send_transaction(&init_config_ixs, &authority.pubkey(), &[&authority]) .await .expect("Initialize config should succeed"); @@ -706,17 +698,14 @@ async fn test_compressible_pda_subscription() { rpc.airdrop_lamports(&authority.pubkey(), 10_000_000_000) .await .expect("Failed to airdrop to authority"); - rpc.airdrop_lamports(&RENT_SPONSOR, 10_000_000_000) - .await - .expect("Failed to fund rent sponsor"); // Wait for indexer wait_for_indexer(&rpc) .await .expect("Failed to wait for indexer"); - // Initialize compression config - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + // Initialize compression config (includes rent sponsor funding) + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &authority.pubkey(), &Pubkey::find_program_address( @@ -726,10 +715,11 @@ async fn test_compressible_pda_subscription() { .0, RENT_SPONSOR, authority.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &authority.pubkey(), &[&authority]) + rpc.create_and_send_transaction(&init_config_ixs, &authority.pubkey(), &[&authority]) .await .expect("Initialize config should succeed"); diff --git a/sdk-libs/client/src/interface/initialize_config.rs b/sdk-libs/client/src/interface/initialize_config.rs index a9145bf828..4d8731481f 100644 --- a/sdk-libs/client/src/interface/initialize_config.rs +++ b/sdk-libs/client/src/interface/initialize_config.rs @@ -7,6 +7,7 @@ use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSeria use light_sdk::interface::config::LightConfig; use solana_instruction::{AccountMeta, Instruction}; use solana_pubkey::Pubkey; +use solana_system_interface::instruction as system_instruction; /// Default address tree v2 pubkey. pub const ADDRESS_TREE_V2: Pubkey = @@ -26,12 +27,15 @@ pub struct InitializeCompressionConfigAnchorData { } /// Builder for `initialize_compression_config` instruction with sensible defaults. +/// +/// Automatically includes a transfer instruction to fund the rent sponsor PDA. pub struct InitializeRentFreeConfig { program_id: Pubkey, fee_payer: Pubkey, program_data_pda: Pubkey, authority: Option, rent_sponsor: Pubkey, + rent_sponsor_funding: u64, compression_authority: Pubkey, rent_config: light_compressible::rent::RentConfig, write_top_up: u32, @@ -40,12 +44,18 @@ pub struct InitializeRentFreeConfig { } impl InitializeRentFreeConfig { + /// Creates a new builder for initializing rent-free config. + /// + /// # Arguments + /// * `rent_sponsor_funding` - Lamports to transfer to the rent sponsor PDA. + /// This funds the PDA that will pay rent for compressed accounts. pub fn new( program_id: &Pubkey, fee_payer: &Pubkey, program_data_pda: &Pubkey, rent_sponsor: Pubkey, compression_authority: Pubkey, + rent_sponsor_funding: u64, ) -> Self { Self { program_id: *program_id, @@ -53,6 +63,7 @@ impl InitializeRentFreeConfig { program_data_pda: *program_data_pda, authority: None, rent_sponsor, + rent_sponsor_funding, compression_authority, rent_config: light_compressible::rent::RentConfig::default(), write_top_up: DEFAULT_INIT_WRITE_TOP_UP, @@ -86,10 +97,25 @@ impl InitializeRentFreeConfig { self } - pub fn build(self) -> (Instruction, Pubkey) { + /// Builds the instructions to initialize rent-free config. + /// + /// Returns a vector containing: + /// 1. Transfer instruction to fund the rent sponsor PDA + /// 2. Initialize compression config instruction + /// + /// Both instructions should be sent in a single atomic transaction. + pub fn build(self) -> (Vec, Pubkey) { let authority = self.authority.unwrap_or(self.fee_payer); let (config_pda, _) = LightConfig::derive_pda(&self.program_id, self.config_bump); + // 1. Transfer to fund rent sponsor PDA + let transfer_ix = system_instruction::transfer( + &self.fee_payer, + &self.rent_sponsor, + self.rent_sponsor_funding, + ); + + // 2. Initialize compression config let accounts = vec![ AccountMeta::new(self.fee_payer, true), // payer AccountMeta::new(config_pda, false), // config @@ -121,12 +147,12 @@ impl InitializeRentFreeConfig { data.extend_from_slice(&DISCRIMINATOR); data.extend_from_slice(&serialized_data); - let instruction = Instruction { + let init_config_ix = Instruction { program_id: self.program_id, accounts, data, }; - (instruction, config_pda) + (vec![transfer_ix, init_config_ix], config_pda) } } diff --git a/sdk-libs/client/src/interface/light_program_interface.rs b/sdk-libs/client/src/interface/light_program_interface.rs index f145309702..76dc0c9149 100644 --- a/sdk-libs/client/src/interface/light_program_interface.rs +++ b/sdk-libs/client/src/interface/light_program_interface.rs @@ -234,8 +234,8 @@ pub trait LightProgramInterface: Sized { #[must_use] fn light_config_pda(&self) -> Pubkey; - /// Returns the program's rent sponsor PDA for cPDAs. - /// Derived via `derive_light_rent_sponsor_pda!` macro. + /// Returns the program's rent sponsor PDA for cPDAs (version 1). + /// Derived via `derive_light_rent_sponsors!` macro. #[must_use] fn light_rent_sponsor_pda(&self) -> Pubkey; diff --git a/sdk-libs/macros/src/lib.rs b/sdk-libs/macros/src/lib.rs index 992ff3b382..5f0b6936f4 100644 --- a/sdk-libs/macros/src/lib.rs +++ b/sdk-libs/macros/src/lib.rs @@ -339,38 +339,28 @@ pub fn light_account_derive(input: TokenStream) -> TokenStream { into_token_stream(light_pdas::account::light_compressible::derive_light_account(input)) } -/// Derives a Rent Sponsor PDA for a program at compile time. +/// Derives 4 Rent Sponsor PDAs (versions 1-4) at compile time. /// -/// Seeds: ["rent_sponsor", ] +/// Returns a `RentSponsors` struct containing an array of 4 `RentSponsor` entries. +/// Version 1 is always the default, accessed via `.default()` or index `[0]`. /// /// ## Example /// /// ```ignore -/// use light_sdk_macros::derive_light_rent_sponsor_pda; +/// use light_sdk_macros::derive_light_rent_sponsors; /// -/// pub const RENT_SPONSOR_DATA: ([u8; 32], u8) = -/// derive_light_rent_sponsor_pda!("8Ld9pGkCNfU6A7KdKe1YrTNYJWKMCFqVHqmUvjNmER7B", 1); -/// ``` -#[proc_macro] -pub fn derive_light_rent_sponsor_pda(input: TokenStream) -> TokenStream { - rent_sponsor::derive_light_rent_sponsor_pda(input) -} - -/// Derives a complete Rent Sponsor configuration for a program at compile time. +/// pub const RENT_SPONSORS: ::light_sdk::sdk_types::RentSponsors = +/// derive_light_rent_sponsors!("8Ld9pGkCNfU6A7KdKe1YrTNYJWKMCFqVHqmUvjNmER7B"); /// -/// Returns ::light_sdk_types::RentSponsor { program_id, rent_sponsor, bump, version }. -/// -/// ## Example -/// -/// ```ignore -/// use light_sdk_macros::derive_light_rent_sponsor; +/// // Get default (version 1) +/// let default_sponsor = RENT_SPONSORS.default(); /// -/// pub const RENT_SPONSOR: ::light_sdk_types::RentSponsor = -/// derive_light_rent_sponsor!("8Ld9pGkCNfU6A7KdKe1YrTNYJWKMCFqVHqmUvjNmER7B", 1); +/// // Get specific version +/// let v2_sponsor = RENT_SPONSORS.get(2).unwrap(); /// ``` #[proc_macro] -pub fn derive_light_rent_sponsor(input: TokenStream) -> TokenStream { - rent_sponsor::derive_light_rent_sponsor(input) +pub fn derive_light_rent_sponsors(input: TokenStream) -> TokenStream { + rent_sponsor::derive_light_rent_sponsors(input) } /// Generates `LightFinalize` trait implementation for Light Protocol accounts. diff --git a/sdk-libs/macros/src/rent_sponsor.rs b/sdk-libs/macros/src/rent_sponsor.rs index d851ca171a..1929d976f4 100644 --- a/sdk-libs/macros/src/rent_sponsor.rs +++ b/sdk-libs/macros/src/rent_sponsor.rs @@ -1,151 +1,41 @@ use light_sdk_types::constants::RENT_SPONSOR_SEED; use proc_macro::TokenStream; use quote::quote; -use syn::{parse::Parse, parse_macro_input, punctuated::Punctuated, Expr, LitInt, LitStr, Token}; +use syn::{parse::Parse, parse_macro_input, LitStr}; -struct Args { +struct ProgramIdArg { program_id: LitStr, - version: Option, } -impl Parse for Args { + +impl Parse for ProgramIdArg { fn parse(input: syn::parse::ParseStream) -> syn::Result { - let elems = Punctuated::::parse_terminated(input)?; - if elems.is_empty() { - return Err(syn::Error::new( - input.span(), - "Expected at least a program id string literal", - )); - } - if elems.len() > 2 { - return Err(syn::Error::new_spanned( - &elems[2], - "Too many arguments: expected at most 2 (program_id, version)", - )); - } - // First argument must be a string literal - let program_id = match &elems[0] { - Expr::Lit(expr_lit) => { - if let syn::Lit::Str(ls) = &expr_lit.lit { - ls.clone() - } else { - return Err(syn::Error::new_spanned( - &elems[0], - "First argument must be a string literal program id", - )); - } - } - _ => { - return Err(syn::Error::new_spanned( - &elems[0], - "First argument must be a string literal program id", - )) - } - }; - // Optional second argument: version as integer literal (u16) - let version = if elems.len() > 1 { - match &elems[1] { - Expr::Lit(expr_lit) => { - if let syn::Lit::Int(li) = &expr_lit.lit { - Some(li.clone()) - } else { - return Err(syn::Error::new_spanned( - &elems[1], - "Second argument must be an integer literal (u16 version)", - )); - } - } - _ => { - return Err(syn::Error::new_spanned( - &elems[1], - "Second argument must be an integer literal (u16 version)", - )) - } - } - } else { - None - }; - Ok(Args { - program_id, - version, - }) + let program_id: LitStr = input.parse()?; + Ok(ProgramIdArg { program_id }) } } -/// Derives a Rent Sponsor PDA for a program at compile time. +/// Derives 4 Rent Sponsor PDAs (versions 1-4) at compile time. /// -/// Seeds: ["rent_sponsor", ] +/// Returns a `RentSponsors` struct containing an array of 4 `RentSponsor` entries. +/// Version 1 is always the default, accessed via `.default()` or index `[0]`. /// /// Usage: -/// - With default version=1: -/// const DATA: ([u8; 32], u8) = derive_light_rent_sponsor_pda!("Program1111111111111111111111111111111111"); -/// - With explicit version: -/// const DATA: ([u8; 32], u8) = derive_light_rent_sponsor_pda!("Program1111111111111111111111111111111111", 2); -pub fn derive_light_rent_sponsor_pda(input: TokenStream) -> TokenStream { - let args = parse_macro_input!(input as Args); - let program_id_str = args.program_id.value(); - let version_u16: u16 = match args.version.as_ref() { - Some(lit) => match lit.base10_parse::() { - Ok(v) => v, - Err(e) => { - return syn::Error::new_spanned(lit, format!("Invalid version number: {}", e)) - .to_compile_error() - .into(); - } - }, - None => 1u16, - }; - - // Parse program ID at compile time - use std::str::FromStr; - let program_id = match solana_pubkey::Pubkey::from_str(&program_id_str) { - Ok(id) => id, - Err(_) => { - return syn::Error::new( - proc_macro2::Span::call_site(), - "Invalid program ID format. Expected a base58 encoded public key", - ) - .to_compile_error() - .into(); - } - }; - - let seeds = &[RENT_SPONSOR_SEED, &version_u16.to_le_bytes()[..]]; - let (pda, bump) = solana_pubkey::Pubkey::find_program_address(seeds, &program_id); - - let pda_bytes = pda.to_bytes(); - let bytes = pda_bytes - .iter() - .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)); - - let output = quote! { - ([#(#bytes),*], #bump) - }; - output.into() -} - -/// Derives a Rent Sponsor configuration struct at compile time. +/// ```ignore +/// use light_sdk_macros::derive_light_rent_sponsors; /// -/// Returns `::light_sdk::sdk_types::RentSponsor { program_id, rent_sponsor, bump, version }`. +/// pub const RENT_SPONSORS: ::light_sdk::sdk_types::RentSponsors = +/// derive_light_rent_sponsors!("Program1111111111111111111111111111111111"); /// -/// Usage: -/// const RENT_SPONSOR: ::light_sdk::sdk_types::RentSponsor = -/// derive_light_rent_sponsor!("Program1111111111111111111111111111111111", 1); -pub fn derive_light_rent_sponsor(input: TokenStream) -> TokenStream { - let args = parse_macro_input!(input as Args); +/// // Get default (version 1) +/// let default_sponsor = RENT_SPONSORS.default(); +/// +/// // Get specific version (1-indexed) +/// let v2_sponsor = RENT_SPONSORS.get(2).unwrap(); +/// ``` +pub fn derive_light_rent_sponsors(input: TokenStream) -> TokenStream { + let args = parse_macro_input!(input as ProgramIdArg); let program_id_str = args.program_id.value(); - let version_u16: u16 = match args.version.as_ref() { - Some(lit) => match lit.base10_parse::() { - Ok(v) => v, - Err(e) => { - return syn::Error::new_spanned(lit, format!("Invalid version number: {}", e)) - .to_compile_error() - .into(); - } - }, - None => 1u16, - }; - // Parse program ID at compile time use std::str::FromStr; let program_id = match solana_pubkey::Pubkey::from_str(&program_id_str) { Ok(id) => id, @@ -159,28 +49,37 @@ pub fn derive_light_rent_sponsor(input: TokenStream) -> TokenStream { } }; - let seeds = &[RENT_SPONSOR_SEED, &version_u16.to_le_bytes()[..]]; - let (pda, bump) = solana_pubkey::Pubkey::find_program_address(seeds, &program_id); - let program_id_bytes = program_id.to_bytes(); - let pda_bytes = pda.to_bytes(); - - let program_id_literals = program_id_bytes - .iter() - .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)); - let pda_literals = pda_bytes + let program_id_literals: Vec<_> = program_id_bytes .iter() - .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)); + .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)) + .collect(); - let version_lit = proc_macro2::Literal::u16_unsuffixed(version_u16); - let output = quote! { - { + let mut sponsor_entries = Vec::with_capacity(4); + for version in 1u16..=4u16 { + let seeds = &[RENT_SPONSOR_SEED, &version.to_le_bytes()[..]]; + let (pda, bump) = solana_pubkey::Pubkey::find_program_address(seeds, &program_id); + let pda_bytes = pda.to_bytes(); + let pda_literals: Vec<_> = pda_bytes + .iter() + .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)) + .collect(); + let version_lit = proc_macro2::Literal::u16_unsuffixed(version); + let prog_lits = &program_id_literals; + + sponsor_entries.push(quote! { ::light_sdk::sdk_types::RentSponsor { - program_id: [#(#program_id_literals),*], + program_id: [#(#prog_lits),*], rent_sponsor: [#(#pda_literals),*], bump: #bump, version: #version_lit, } + }); + } + + let output = quote! { + ::light_sdk::sdk_types::RentSponsors { + sponsors: [#(#sponsor_entries),*], } }; output.into() diff --git a/sdk-libs/sdk-types/src/lib.rs b/sdk-libs/sdk-types/src/lib.rs index b63eebc639..1a136ba581 100644 --- a/sdk-libs/sdk-types/src/lib.rs +++ b/sdk-libs/sdk-types/src/lib.rs @@ -26,3 +26,35 @@ pub struct RentSponsor { pub bump: u8, pub version: u16, } + +/// Pre-computed rent sponsor PDAs for versions 1-4. +/// Version 1 is always the default. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct RentSponsors { + pub sponsors: [RentSponsor; 4], +} + +impl RentSponsors { + /// Returns the default rent sponsor (version 1). + #[inline] + pub const fn default(&self) -> &RentSponsor { + &self.sponsors[0] + } + + /// Returns the rent sponsor for the given version (1-4). + /// Returns None if version is 0 or > 4. + #[inline] + pub const fn get(&self, version: u16) -> Option<&RentSponsor> { + if version == 0 || version > 4 { + None + } else { + Some(&self.sponsors[(version - 1) as usize]) + } + } + + /// Returns all 4 rent sponsors. + #[inline] + pub const fn all(&self) -> &[RentSponsor; 4] { + &self.sponsors + } +} diff --git a/sdk-libs/sdk/src/lib.rs b/sdk-libs/sdk/src/lib.rs index fce8b42a5d..4075a76e6c 100644 --- a/sdk-libs/sdk/src/lib.rs +++ b/sdk-libs/sdk/src/lib.rs @@ -185,7 +185,7 @@ pub mod sdk_types { pub use light_sdk_types::{ cpi_accounts::CpiAccountsConfig, instruction::{PackedAddressTreeInfo, PackedAddressTreeInfoExt}, - RentSponsor, + RentSponsor, RentSponsors, }; } @@ -206,8 +206,7 @@ pub extern crate light_hasher; use light_hasher::DataHasher; pub use light_macros::{derive_light_cpi_signer, derive_light_cpi_signer_pda}; pub use light_sdk_macros::{ - derive_light_rent_sponsor, derive_light_rent_sponsor_pda, LightDiscriminator, LightHasher, - LightHasherSha, + derive_light_rent_sponsors, LightDiscriminator, LightHasher, LightHasherSha, }; pub use light_sdk_types::{constants, instruction::PackedAddressTreeInfoExt, CpiSigner}; use solana_account_info::AccountInfo; diff --git a/sdk-libs/sdk/src/utils.rs b/sdk-libs/sdk/src/utils.rs index 14dbceb08a..6da4d0c6c5 100644 --- a/sdk-libs/sdk/src/utils.rs +++ b/sdk-libs/sdk/src/utils.rs @@ -1,3 +1,4 @@ +use light_sdk_types::{RentSponsor, RentSponsors}; use solana_pubkey::Pubkey; #[allow(unused_imports)] @@ -20,11 +21,37 @@ pub fn get_light_cpi_signer_seeds(program_id: &Pubkey) -> ([Vec; 2], Pubkey) (signer_seeds, pda) } -/// Derives the rent sponsor PDA for a given program and version. +/// Derives a single rent sponsor PDA for a given program and version. /// /// Seeds: ["rent_sponsor", ] +#[inline] pub fn derive_rent_sponsor_pda(program_id: &Pubkey, version: u16) -> (Pubkey, u8) { let version_bytes = version.to_le_bytes(); let seeds = &[RENT_SPONSOR_SEED, &version_bytes[..]]; Pubkey::find_program_address(seeds, program_id) } + +/// Derives all 4 rent sponsor PDAs (versions 1-4) for a given program at runtime. +/// +/// Seeds: ["rent_sponsor", ] +pub fn derive_rent_sponsors(program_id: &Pubkey) -> RentSponsors { + let program_id_bytes = program_id.to_bytes(); + let mut sponsors = [RentSponsor { + program_id: program_id_bytes, + rent_sponsor: [0u8; 32], + bump: 0, + version: 0, + }; 4]; + + for (i, version) in (1u16..=4u16).enumerate() { + let (pda, bump) = derive_rent_sponsor_pda(program_id, version); + sponsors[i] = RentSponsor { + program_id: program_id_bytes, + rent_sponsor: pda.to_bytes(), + bump, + version, + }; + } + + RentSponsors { sponsors } +} diff --git a/sdk-libs/token-sdk/src/anchor.rs b/sdk-libs/token-sdk/src/anchor.rs index d067a5a80c..1303bf579f 100644 --- a/sdk-libs/token-sdk/src/anchor.rs +++ b/sdk-libs/token-sdk/src/anchor.rs @@ -20,8 +20,7 @@ pub use light_sdk::{ // Re-export Light SDK macros pub use light_sdk_macros::{ // Proc macros - derive_light_rent_sponsor, - derive_light_rent_sponsor_pda, + derive_light_rent_sponsors, // Attribute macros light_program, // Derive macros diff --git a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs index 826aefb20c..0669aa9190 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs @@ -1,11 +1,10 @@ -#![allow(deprecated)] #![allow(clippy::useless_asref)] // Testing macro handling of .as_ref() patterns use anchor_lang::prelude::*; use light_instruction_decoder_derive::instruction_decoder; -use light_sdk::{derive_light_cpi_signer, derive_light_rent_sponsor_pda}; +use light_sdk::{derive_light_cpi_signer, derive_light_rent_sponsors}; use light_sdk_macros::light_program; -use light_sdk_types::CpiSigner; +use light_sdk_types::{CpiSigner, RentSponsors}; pub mod amm_test; pub mod d5_markers; @@ -74,12 +73,12 @@ declare_id!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah"); pub const LIGHT_CPI_SIGNER: CpiSigner = derive_light_cpi_signer!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah"); -pub const PROGRAM_RENT_SPONSOR_DATA: ([u8; 32], u8) = - derive_light_rent_sponsor_pda!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah", 1); +pub const RENT_SPONSORS: RentSponsors = + derive_light_rent_sponsors!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah"); #[inline] pub fn program_rent_sponsor() -> Pubkey { - Pubkey::from(PROGRAM_RENT_SPONSOR_DATA.0) + Pubkey::from(RENT_SPONSORS.default().rent_sponsor) } pub const GAME_SESSION_SEED: &str = "game_session"; @@ -1463,3 +1462,49 @@ pub enum CsdkTestInstruction { )] CreateMintWithMetadata, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_derive_light_rent_sponsors_macro_matches_runtime() { + let runtime_sponsors = light_sdk::utils::derive_rent_sponsors(&crate::ID); + + for version in 1u16..=4u16 { + let macro_sponsor = RENT_SPONSORS.get(version).unwrap(); + let runtime_sponsor = runtime_sponsors.get(version).unwrap(); + + assert_eq!( + macro_sponsor, runtime_sponsor, + "Macro-derived sponsor for version {} should match runtime (all)", + version + ); + + // Also verify single-version function matches + let (single_pda, single_bump) = + light_sdk::utils::derive_rent_sponsor_pda(&crate::ID, version); + assert_eq!( + macro_sponsor.rent_sponsor, + single_pda.to_bytes(), + "Macro-derived PDA for version {} should match runtime (single)", + version + ); + assert_eq!(macro_sponsor.bump, single_bump); + } + } + + #[test] + fn test_rent_sponsors_default_is_version_1() { + let default = RENT_SPONSORS.default(); + let v1 = RENT_SPONSORS.get(1).unwrap(); + assert_eq!(default, v1, "Default should be version 1"); + assert_eq!(default.version, 1); + } + + #[test] + fn test_rent_sponsors_invalid_versions() { + assert!(RENT_SPONSORS.get(0).is_none(), "Version 0 should be None"); + assert!(RENT_SPONSORS.get(5).is_none(), "Version 5 should be None"); + } +} diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs index 8f4c8ab7ee..4c16e1352b 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs @@ -138,24 +138,22 @@ async fn setup() -> AmmTestContext { // Setup mock program data and compression config let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - // Get program's rent sponsor PDA (derived via derive_light_rent_sponsor_pda! macro) - let program_rent_sponsor = csdk_anchor_full_derived_test::program_rent_sponsor(); + // Construct SDK and use trait method for rent sponsor PDA + let sdk = AmmSdk::new(); + let program_rent_sponsor = sdk.light_rent_sponsor_pda(); - // Fund the rent sponsor PDA before config init - light_test_utils::airdrop_lamports(&mut rpc, &program_rent_sponsor, 10_000_000_000) - .await - .unwrap(); - - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + // Initialize config with rent sponsor funding (transfer + init in one tx) + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, program_rent_sponsor, payer.pubkey(), + 10_000_000_000, // rent sponsor funding ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs index 8fa22eb893..1590979086 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs @@ -81,16 +81,17 @@ async fn test_create_pdas_and_mint_auto() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); @@ -537,16 +538,17 @@ async fn test_create_two_mints() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); @@ -733,16 +735,17 @@ async fn test_create_multi_mints() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); @@ -887,16 +890,17 @@ async fn setup_d9_test_context() -> (LightProgramTest, Keypair, Pubkey, Pubkey) let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs index a5cd62a7c3..bf28b3bf17 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs @@ -47,16 +47,17 @@ impl D10TestContext { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR_PUBKEY, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs index 7272f4d4a0..71732bd154 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs @@ -50,16 +50,17 @@ impl TestContext { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs index c614beb673..66989f93db 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs @@ -41,16 +41,17 @@ async fn test_create_mint_with_metadata() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); diff --git a/sdk-tests/single-ata-test/tests/test.rs b/sdk-tests/single-ata-test/tests/test.rs index 7de9f960e5..6e4898a4aa 100644 --- a/sdk-tests/single-ata-test/tests/test.rs +++ b/sdk-tests/single-ata-test/tests/test.rs @@ -92,16 +92,17 @@ async fn test_create_single_ata() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, _config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, _config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); diff --git a/sdk-tests/single-mint-test/tests/test.rs b/sdk-tests/single-mint-test/tests/test.rs index fb289a6e15..92b46db079 100644 --- a/sdk-tests/single-mint-test/tests/test.rs +++ b/sdk-tests/single-mint-test/tests/test.rs @@ -30,16 +30,17 @@ async fn test_create_single_mint() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); diff --git a/sdk-tests/single-pda-test/tests/test.rs b/sdk-tests/single-pda-test/tests/test.rs index b0bf6c32bc..1d4465200a 100644 --- a/sdk-tests/single-pda-test/tests/test.rs +++ b/sdk-tests/single-pda-test/tests/test.rs @@ -29,16 +29,17 @@ async fn test_create_single_pda() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); diff --git a/sdk-tests/single-token-test/tests/test.rs b/sdk-tests/single-token-test/tests/test.rs index a2b7e87708..df208dbdd5 100644 --- a/sdk-tests/single-token-test/tests/test.rs +++ b/sdk-tests/single-token-test/tests/test.rs @@ -92,16 +92,17 @@ async fn test_create_single_token_vault() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, _config_pda) = InitializeRentFreeConfig::new( + let (init_config_ixs, _config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, RENT_SPONSOR, payer.pubkey(), + 10_000_000_000, ) .build(); - rpc.create_and_send_transaction(&[init_config_ix], &payer.pubkey(), &[&payer]) + rpc.create_and_send_transaction(&init_config_ixs, &payer.pubkey(), &[&payer]) .await .expect("Initialize config should succeed"); From 6f721a88d9981a1a3d9ac5380c3b028ea7e4955c Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sat, 24 Jan 2026 22:08:11 +0000 Subject: [PATCH 03/10] fix: simplify rent sponsor to single version, fix client dependency - Remove RentSponsors (4 versions) -> single RentSponsor (version 1) - Rename derive_light_rent_sponsors! -> derive_light_rent_sponsor! - Add rent_sponsor_bump to LightConfig for signing during decompression - Add solana-system-interface dependency to light-client - Pass rent_sponsor_seeds to invoke_signed for PDA creation --- Cargo.lock | 3 +- sdk-libs/client/Cargo.toml | 1 + .../client/src/interface/initialize_config.rs | 13 ++++ sdk-libs/macros/src/lib.rs | 24 ++++---- .../light_pdas/account/decompress_context.rs | 2 + .../src/light_pdas/program/instructions.rs | 2 + sdk-libs/macros/src/rent_sponsor.rs | 58 +++++++----------- sdk-libs/sdk-types/src/lib.rs | 34 +---------- sdk-libs/sdk/src/interface/config.rs | 23 ++++++- .../src/interface/decompress_idempotent.rs | 12 ++-- .../sdk/src/interface/decompress_runtime.rs | 8 +++ sdk-libs/sdk/src/lib.rs | 4 +- sdk-libs/sdk/src/utils.rs | 41 ++++--------- sdk-libs/token-sdk/src/anchor.rs | 2 +- .../csdk-anchor-full-derived-test/src/lib.rs | 61 ++++++------------- 15 files changed, 126 insertions(+), 162 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62f2882e46..a229d1330c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -3589,6 +3589,7 @@ dependencies = [ "solana-rpc-client", "solana-rpc-client-api", "solana-signature", + "solana-system-interface 1.0.0", "solana-transaction", "solana-transaction-error", "solana-transaction-status-client-types", diff --git a/sdk-libs/client/Cargo.toml b/sdk-libs/client/Cargo.toml index fa614d97fe..526cbc7ad1 100644 --- a/sdk-libs/client/Cargo.toml +++ b/sdk-libs/client/Cargo.toml @@ -37,6 +37,7 @@ solana-address-lookup-table-interface = { version = "2.2.1", features = [ "bincode", ] } solana-message = { workspace = true } +solana-system-interface = { workspace = true } spl-token-2022-interface = { workspace = true } spl-pod = { workspace = true } diff --git a/sdk-libs/client/src/interface/initialize_config.rs b/sdk-libs/client/src/interface/initialize_config.rs index 4d8731481f..8514f514e9 100644 --- a/sdk-libs/client/src/interface/initialize_config.rs +++ b/sdk-libs/client/src/interface/initialize_config.rs @@ -21,6 +21,7 @@ pub const DEFAULT_INIT_WRITE_TOP_UP: u32 = 5_000; pub struct InitializeCompressionConfigAnchorData { pub write_top_up: u32, pub rent_sponsor: Pubkey, + pub rent_sponsor_bump: u8, pub compression_authority: Pubkey, pub rent_config: light_compressible::rent::RentConfig, pub address_space: Vec, @@ -108,6 +109,17 @@ impl InitializeRentFreeConfig { let authority = self.authority.unwrap_or(self.fee_payer); let (config_pda, _) = LightConfig::derive_pda(&self.program_id, self.config_bump); + // Derive rent sponsor bump (version 1, hardcoded) + let (derived_rent_sponsor, rent_sponsor_bump) = Pubkey::find_program_address( + &[b"rent_sponsor", &1u16.to_le_bytes()], + &self.program_id, + ); + assert_eq!( + derived_rent_sponsor, self.rent_sponsor, + "Rent sponsor PDA mismatch: derived {:?} != provided {:?}", + derived_rent_sponsor, self.rent_sponsor + ); + // 1. Transfer to fund rent sponsor PDA let transfer_ix = system_instruction::transfer( &self.fee_payer, @@ -130,6 +142,7 @@ impl InitializeRentFreeConfig { let instruction_data = InitializeCompressionConfigAnchorData { write_top_up: self.write_top_up, rent_sponsor: self.rent_sponsor, + rent_sponsor_bump, compression_authority: self.compression_authority, rent_config: self.rent_config, address_space: self.address_space, diff --git a/sdk-libs/macros/src/lib.rs b/sdk-libs/macros/src/lib.rs index 5f0b6936f4..f145d8c199 100644 --- a/sdk-libs/macros/src/lib.rs +++ b/sdk-libs/macros/src/lib.rs @@ -339,28 +339,26 @@ pub fn light_account_derive(input: TokenStream) -> TokenStream { into_token_stream(light_pdas::account::light_compressible::derive_light_account(input)) } -/// Derives 4 Rent Sponsor PDAs (versions 1-4) at compile time. +/// Derives the Rent Sponsor PDA at compile time (version 1, hardcoded). /// -/// Returns a `RentSponsors` struct containing an array of 4 `RentSponsor` entries. -/// Version 1 is always the default, accessed via `.default()` or index `[0]`. +/// Returns a `RentSponsor` struct with the PDA address and bump. /// /// ## Example /// /// ```ignore -/// use light_sdk_macros::derive_light_rent_sponsors; +/// use light_sdk_macros::derive_light_rent_sponsor; /// -/// pub const RENT_SPONSORS: ::light_sdk::sdk_types::RentSponsors = -/// derive_light_rent_sponsors!("8Ld9pGkCNfU6A7KdKe1YrTNYJWKMCFqVHqmUvjNmER7B"); +/// pub const RENT_SPONSOR: ::light_sdk::sdk_types::RentSponsor = +/// derive_light_rent_sponsor!("8Ld9pGkCNfU6A7KdKe1YrTNYJWKMCFqVHqmUvjNmER7B"); /// -/// // Get default (version 1) -/// let default_sponsor = RENT_SPONSORS.default(); -/// -/// // Get specific version -/// let v2_sponsor = RENT_SPONSORS.get(2).unwrap(); +/// // Access the pubkey +/// let pubkey = Pubkey::from(RENT_SPONSOR.rent_sponsor); +/// // Access the bump for signing +/// let bump = RENT_SPONSOR.bump; /// ``` #[proc_macro] -pub fn derive_light_rent_sponsors(input: TokenStream) -> TokenStream { - rent_sponsor::derive_light_rent_sponsors(input) +pub fn derive_light_rent_sponsor(input: TokenStream) -> TokenStream { + rent_sponsor::derive_light_rent_sponsor(input) } /// Generates `LightFinalize` trait implementation for Light Protocol accounts. diff --git a/sdk-libs/macros/src/light_pdas/account/decompress_context.rs b/sdk-libs/macros/src/light_pdas/account/decompress_context.rs index 269221f96f..effc04bf77 100644 --- a/sdk-libs/macros/src/light_pdas/account/decompress_context.rs +++ b/sdk-libs/macros/src/light_pdas/account/decompress_context.rs @@ -81,6 +81,7 @@ pub fn generate_decompress_context_trait_impl( #seed_params_update light_sdk::interface::handle_packed_pda_variant::<#inner_type, #packed_inner_type, _, _>( &*self.rent_sponsor, + rent_sponsor_seeds, cpi_accounts, address_space, &solana_accounts[i], @@ -157,6 +158,7 @@ pub fn generate_decompress_context_trait_impl( compressed_accounts: Vec, solana_accounts: &[solana_account_info::AccountInfo<#lifetime>], seed_params: std::option::Option<&Self::SeedParams>, + rent_sponsor_seeds: &[&[u8]], ) -> std::result::Result<( Vec<::light_sdk::compressed_account::CompressedAccountInfo>, Vec<(Self::PackedTokenData, Self::CompressedMeta)>, diff --git a/sdk-libs/macros/src/light_pdas/program/instructions.rs b/sdk-libs/macros/src/light_pdas/program/instructions.rs index 79f2682c7e..0630dccda7 100644 --- a/sdk-libs/macros/src/light_pdas/program/instructions.rs +++ b/sdk-libs/macros/src/light_pdas/program/instructions.rs @@ -433,6 +433,7 @@ fn codegen( ctx: Context<'_, '_, '_, 'info, InitializeCompressionConfig<'info>>, write_top_up: u32, rent_sponsor: Pubkey, + rent_sponsor_bump: u8, compression_authority: Pubkey, rent_config: ::light_sdk::interface::rent::RentConfig, address_space: Vec, @@ -442,6 +443,7 @@ fn codegen( &ctx.accounts.authority.to_account_info(), &ctx.accounts.program_data.to_account_info(), &rent_sponsor, + rent_sponsor_bump, &compression_authority, rent_config, write_top_up, diff --git a/sdk-libs/macros/src/rent_sponsor.rs b/sdk-libs/macros/src/rent_sponsor.rs index 1929d976f4..d5c2559a27 100644 --- a/sdk-libs/macros/src/rent_sponsor.rs +++ b/sdk-libs/macros/src/rent_sponsor.rs @@ -14,25 +14,23 @@ impl Parse for ProgramIdArg { } } -/// Derives 4 Rent Sponsor PDAs (versions 1-4) at compile time. +/// Derives the Rent Sponsor PDA at compile time (version 1, hardcoded). /// -/// Returns a `RentSponsors` struct containing an array of 4 `RentSponsor` entries. -/// Version 1 is always the default, accessed via `.default()` or index `[0]`. +/// Returns a `RentSponsor` struct with the PDA address and bump. /// /// Usage: /// ```ignore -/// use light_sdk_macros::derive_light_rent_sponsors; +/// use light_sdk_macros::derive_light_rent_sponsor; /// -/// pub const RENT_SPONSORS: ::light_sdk::sdk_types::RentSponsors = -/// derive_light_rent_sponsors!("Program1111111111111111111111111111111111"); +/// pub const RENT_SPONSOR: ::light_sdk::sdk_types::RentSponsor = +/// derive_light_rent_sponsor!("Program1111111111111111111111111111111111"); /// -/// // Get default (version 1) -/// let default_sponsor = RENT_SPONSORS.default(); -/// -/// // Get specific version (1-indexed) -/// let v2_sponsor = RENT_SPONSORS.get(2).unwrap(); +/// // Access the pubkey +/// let pubkey = Pubkey::from(RENT_SPONSOR.rent_sponsor); +/// // Access the bump for signing +/// let bump = RENT_SPONSOR.bump; /// ``` -pub fn derive_light_rent_sponsors(input: TokenStream) -> TokenStream { +pub fn derive_light_rent_sponsor(input: TokenStream) -> TokenStream { let args = parse_macro_input!(input as ProgramIdArg); let program_id_str = args.program_id.value(); @@ -55,31 +53,21 @@ pub fn derive_light_rent_sponsors(input: TokenStream) -> TokenStream { .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)) .collect(); - let mut sponsor_entries = Vec::with_capacity(4); - for version in 1u16..=4u16 { - let seeds = &[RENT_SPONSOR_SEED, &version.to_le_bytes()[..]]; - let (pda, bump) = solana_pubkey::Pubkey::find_program_address(seeds, &program_id); - let pda_bytes = pda.to_bytes(); - let pda_literals: Vec<_> = pda_bytes - .iter() - .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)) - .collect(); - let version_lit = proc_macro2::Literal::u16_unsuffixed(version); - let prog_lits = &program_id_literals; - - sponsor_entries.push(quote! { - ::light_sdk::sdk_types::RentSponsor { - program_id: [#(#prog_lits),*], - rent_sponsor: [#(#pda_literals),*], - bump: #bump, - version: #version_lit, - } - }); - } + // Always version 1 + const VERSION: u16 = 1; + let seeds = &[RENT_SPONSOR_SEED, &VERSION.to_le_bytes()[..]]; + let (pda, bump) = solana_pubkey::Pubkey::find_program_address(seeds, &program_id); + let pda_bytes = pda.to_bytes(); + let pda_literals: Vec<_> = pda_bytes + .iter() + .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)) + .collect(); let output = quote! { - ::light_sdk::sdk_types::RentSponsors { - sponsors: [#(#sponsor_entries),*], + ::light_sdk::sdk_types::RentSponsor { + program_id: [#(#program_id_literals),*], + rent_sponsor: [#(#pda_literals),*], + bump: #bump, } }; output.into() diff --git a/sdk-libs/sdk-types/src/lib.rs b/sdk-libs/sdk-types/src/lib.rs index 1a136ba581..dc634b2bc6 100644 --- a/sdk-libs/sdk-types/src/lib.rs +++ b/sdk-libs/sdk-types/src/lib.rs @@ -19,42 +19,10 @@ use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSeria pub use constants::*; pub use light_compressed_account::CpiSigner; +/// Pre-computed rent sponsor PDA (version 1, hardcoded). #[derive(Clone, Copy, Debug, PartialEq, Eq, AnchorSerialize, AnchorDeserialize)] pub struct RentSponsor { pub program_id: [u8; 32], pub rent_sponsor: [u8; 32], pub bump: u8, - pub version: u16, -} - -/// Pre-computed rent sponsor PDAs for versions 1-4. -/// Version 1 is always the default. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct RentSponsors { - pub sponsors: [RentSponsor; 4], -} - -impl RentSponsors { - /// Returns the default rent sponsor (version 1). - #[inline] - pub const fn default(&self) -> &RentSponsor { - &self.sponsors[0] - } - - /// Returns the rent sponsor for the given version (1-4). - /// Returns None if version is 0 or > 4. - #[inline] - pub const fn get(&self, version: u16) -> Option<&RentSponsor> { - if version == 0 || version > 4 { - None - } else { - Some(&self.sponsors[(version - 1) as usize]) - } - } - - /// Returns all 4 rent sponsors. - #[inline] - pub const fn all(&self) -> &[RentSponsor; 4] { - &self.sponsors - } } diff --git a/sdk-libs/sdk/src/interface/config.rs b/sdk-libs/sdk/src/interface/config.rs index d9a2843b35..a4ba1e91c6 100644 --- a/sdk-libs/sdk/src/interface/config.rs +++ b/sdk-libs/sdk/src/interface/config.rs @@ -36,6 +36,8 @@ pub struct LightConfig { pub config_bump: u8, /// PDA bump seed pub bump: u8, + /// Rent sponsor PDA bump seed (for signing during decompression) + pub rent_sponsor_bump: u8, /// Address space for compressed accounts (currently 1 address_tree allowed) pub address_space: Vec, } @@ -49,6 +51,7 @@ impl LightConfig { + core::mem::size_of::() + 1 + 1 + + 1 // rent_sponsor_bump + 4 + (32 * MAX_ADDRESS_TREES_PER_SPACE); @@ -62,6 +65,7 @@ impl LightConfig { + core::mem::size_of::() + 1 + 1 + + 1 // rent_sponsor_bump + 4 + (32 * num_address_trees) } @@ -81,6 +85,19 @@ impl LightConfig { Self::derive_pda(program_id, 0) } + /// Returns the rent sponsor signer seeds for invoke_signed. + /// The returned Vec contains the seed bytes that can be used with invoke_signed. + /// Returns the rent sponsor signer seeds for invoke_signed. + /// Version is hardcoded to 1. + pub fn rent_sponsor_signer_seeds(&self) -> ([u8; 12], [u8; 2], [u8; 1]) { + let mut seed_buf = [0u8; 12]; + seed_buf.copy_from_slice(crate::constants::RENT_SPONSOR_SEED); + const VERSION: u16 = 1; + let version_bytes = VERSION.to_le_bytes(); + let bump_bytes = [self.rent_sponsor_bump]; + (seed_buf, version_bytes, bump_bytes) + } + /// Checks the config account pub fn validate(&self) -> Result<(), crate::ProgramError> { if self.version != 1 { @@ -180,6 +197,7 @@ pub fn process_initialize_light_config<'info>( config_account: &AccountInfo<'info>, update_authority: &AccountInfo<'info>, rent_sponsor: &Pubkey, + rent_sponsor_bump: u8, compression_authority: &Pubkey, rent_config: RentConfig, write_top_up: u32, @@ -264,8 +282,9 @@ pub fn process_initialize_light_config<'info>( compression_authority: *compression_authority, rent_config, config_bump, - address_space, bump, + rent_sponsor_bump, + address_space, }; let mut data = config_account @@ -465,6 +484,7 @@ pub fn process_initialize_light_config_checked<'info>( update_authority: &AccountInfo<'info>, program_data_account: &AccountInfo<'info>, rent_sponsor: &Pubkey, + rent_sponsor_bump: u8, compression_authority: &Pubkey, rent_config: RentConfig, write_top_up: u32, @@ -490,6 +510,7 @@ pub fn process_initialize_light_config_checked<'info>( config_account, update_authority, rent_sponsor, + rent_sponsor_bump, compression_authority, rent_config, write_top_up, diff --git a/sdk-libs/sdk/src/interface/decompress_idempotent.rs b/sdk-libs/sdk/src/interface/decompress_idempotent.rs index aa41ec0c98..0fe504be06 100644 --- a/sdk-libs/sdk/src/interface/decompress_idempotent.rs +++ b/sdk-libs/sdk/src/interface/decompress_idempotent.rs @@ -63,14 +63,16 @@ pub fn into_compressed_meta_with_address<'info>( // TODO: consider folding into main fn. /// Helper to invoke create_account on heap. +/// Both the rent_sponsor (funder) and the new account (PDA) need to sign. #[inline(never)] fn invoke_create_account_with_heap<'info>( rent_sponsor: &AccountInfo<'info>, + rent_sponsor_seeds: &[&[u8]], solana_account: &AccountInfo<'info>, rent_minimum_balance: u64, space: u64, program_id: &Pubkey, - seeds: &[&[u8]], + pda_seeds: &[&[u8]], system_program: &AccountInfo<'info>, ) -> Result<(), LightSdkError> { let create_account_ix = system_instruction::create_account( @@ -88,7 +90,7 @@ fn invoke_create_account_with_heap<'info>( solana_account.clone(), system_program.clone(), ], - &[seeds], + &[rent_sponsor_seeds, pda_seeds], ) .map_err(|e| LightSdkError::ProgramError(e)) } @@ -103,8 +105,9 @@ pub fn prepare_account_for_decompression_idempotent<'a, 'info, T>( compressed_meta: CompressedAccountMeta, solana_account: &AccountInfo<'info>, rent_sponsor: &AccountInfo<'info>, + rent_sponsor_seeds: &[&[u8]], cpi_accounts: &CpiAccounts<'a, 'info>, - signer_seeds: &[&[u8]], + pda_signer_seeds: &[&[u8]], ) -> Result< Option, LightSdkError, @@ -138,11 +141,12 @@ where invoke_create_account_with_heap( rent_sponsor, + rent_sponsor_seeds, solana_account, rent_minimum_balance, space as u64, &cpi_accounts.self_program_id(), - signer_seeds, + pda_signer_seeds, cpi_accounts.system_program()?, )?; diff --git a/sdk-libs/sdk/src/interface/decompress_runtime.rs b/sdk-libs/sdk/src/interface/decompress_runtime.rs index 3e4a3859da..900ab0f4b9 100644 --- a/sdk-libs/sdk/src/interface/decompress_runtime.rs +++ b/sdk-libs/sdk/src/interface/decompress_runtime.rs @@ -71,6 +71,7 @@ pub trait DecompressContext<'info> { compressed_accounts: Vec, solana_accounts: &[AccountInfo<'info>], seed_params: Option<&Self::SeedParams>, + rent_sponsor_seeds: &[&[u8]], ) -> Result<( Vec, Vec<(Self::PackedTokenData, Self::CompressedMeta)> @@ -128,6 +129,7 @@ pub fn check_account_types(compressed_accounts: &[T]) -> (bo #[allow(clippy::too_many_arguments)] pub fn handle_packed_pda_variant<'a, 'b, 'info, T, P, A, S>( accounts_rent_sponsor: &AccountInfo<'info>, + rent_sponsor_seeds: &[&[u8]], cpi_accounts: &CpiAccounts<'b, 'info>, address_space: Pubkey, solana_account: &AccountInfo<'info>, @@ -193,6 +195,7 @@ where ), solana_account, accounts_rent_sponsor, + rent_sponsor_seeds, cpi_accounts, &seed_refs[..len], )? @@ -224,6 +227,10 @@ where let compression_config = crate::interface::LightConfig::load_checked(ctx.config(), program_id)?; let address_space = compression_config.address_space[0]; + // Get rent sponsor signer seeds from config + let (seed_buf, version_bytes, bump_bytes) = compression_config.rent_sponsor_signer_seeds(); + let rent_sponsor_seeds: &[&[u8]] = &[&seed_buf, &version_bytes, &bump_bytes]; + let (has_tokens, has_pdas) = check_account_types(&compressed_accounts); if !has_tokens && !has_pdas { @@ -266,6 +273,7 @@ where compressed_accounts, solana_accounts, seed_params, + rent_sponsor_seeds, )?; let has_pdas = !compressed_pda_infos.is_empty(); diff --git a/sdk-libs/sdk/src/lib.rs b/sdk-libs/sdk/src/lib.rs index 4075a76e6c..a6c4ea06ba 100644 --- a/sdk-libs/sdk/src/lib.rs +++ b/sdk-libs/sdk/src/lib.rs @@ -185,7 +185,7 @@ pub mod sdk_types { pub use light_sdk_types::{ cpi_accounts::CpiAccountsConfig, instruction::{PackedAddressTreeInfo, PackedAddressTreeInfoExt}, - RentSponsor, RentSponsors, + RentSponsor, }; } @@ -206,7 +206,7 @@ pub extern crate light_hasher; use light_hasher::DataHasher; pub use light_macros::{derive_light_cpi_signer, derive_light_cpi_signer_pda}; pub use light_sdk_macros::{ - derive_light_rent_sponsors, LightDiscriminator, LightHasher, LightHasherSha, + derive_light_rent_sponsor, LightDiscriminator, LightHasher, LightHasherSha, }; pub use light_sdk_types::{constants, instruction::PackedAddressTreeInfoExt, CpiSigner}; use solana_account_info::AccountInfo; diff --git a/sdk-libs/sdk/src/utils.rs b/sdk-libs/sdk/src/utils.rs index 6da4d0c6c5..890797e824 100644 --- a/sdk-libs/sdk/src/utils.rs +++ b/sdk-libs/sdk/src/utils.rs @@ -1,4 +1,4 @@ -use light_sdk_types::{RentSponsor, RentSponsors}; +use light_sdk_types::RentSponsor; use solana_pubkey::Pubkey; #[allow(unused_imports)] @@ -21,37 +21,18 @@ pub fn get_light_cpi_signer_seeds(program_id: &Pubkey) -> ([Vec; 2], Pubkey) (signer_seeds, pda) } -/// Derives a single rent sponsor PDA for a given program and version. +/// Derives the rent sponsor PDA for a given program (version 1, hardcoded). /// -/// Seeds: ["rent_sponsor", ] +/// Seeds: ["rent_sponsor", <1u16 little-endian>] #[inline] -pub fn derive_rent_sponsor_pda(program_id: &Pubkey, version: u16) -> (Pubkey, u8) { - let version_bytes = version.to_le_bytes(); +pub fn derive_rent_sponsor(program_id: &Pubkey) -> RentSponsor { + const VERSION: u16 = 1; + let version_bytes = VERSION.to_le_bytes(); let seeds = &[RENT_SPONSOR_SEED, &version_bytes[..]]; - Pubkey::find_program_address(seeds, program_id) -} - -/// Derives all 4 rent sponsor PDAs (versions 1-4) for a given program at runtime. -/// -/// Seeds: ["rent_sponsor", ] -pub fn derive_rent_sponsors(program_id: &Pubkey) -> RentSponsors { - let program_id_bytes = program_id.to_bytes(); - let mut sponsors = [RentSponsor { - program_id: program_id_bytes, - rent_sponsor: [0u8; 32], - bump: 0, - version: 0, - }; 4]; - - for (i, version) in (1u16..=4u16).enumerate() { - let (pda, bump) = derive_rent_sponsor_pda(program_id, version); - sponsors[i] = RentSponsor { - program_id: program_id_bytes, - rent_sponsor: pda.to_bytes(), - bump, - version, - }; + let (pda, bump) = Pubkey::find_program_address(seeds, program_id); + RentSponsor { + program_id: program_id.to_bytes(), + rent_sponsor: pda.to_bytes(), + bump, } - - RentSponsors { sponsors } } diff --git a/sdk-libs/token-sdk/src/anchor.rs b/sdk-libs/token-sdk/src/anchor.rs index 1303bf579f..367b723c05 100644 --- a/sdk-libs/token-sdk/src/anchor.rs +++ b/sdk-libs/token-sdk/src/anchor.rs @@ -20,7 +20,7 @@ pub use light_sdk::{ // Re-export Light SDK macros pub use light_sdk_macros::{ // Proc macros - derive_light_rent_sponsors, + derive_light_rent_sponsor, // Attribute macros light_program, // Derive macros diff --git a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs index 0669aa9190..98bb5ac540 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs @@ -2,9 +2,9 @@ use anchor_lang::prelude::*; use light_instruction_decoder_derive::instruction_decoder; -use light_sdk::{derive_light_cpi_signer, derive_light_rent_sponsors}; +use light_sdk::{derive_light_cpi_signer, derive_light_rent_sponsor}; use light_sdk_macros::light_program; -use light_sdk_types::{CpiSigner, RentSponsors}; +use light_sdk_types::{CpiSigner, RentSponsor}; pub mod amm_test; pub mod d5_markers; @@ -73,12 +73,12 @@ declare_id!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah"); pub const LIGHT_CPI_SIGNER: CpiSigner = derive_light_cpi_signer!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah"); -pub const RENT_SPONSORS: RentSponsors = - derive_light_rent_sponsors!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah"); +pub const RENT_SPONSOR: RentSponsor = + derive_light_rent_sponsor!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah"); #[inline] pub fn program_rent_sponsor() -> Pubkey { - Pubkey::from(RENT_SPONSORS.default().rent_sponsor) + Pubkey::from(RENT_SPONSOR.rent_sponsor) } pub const GAME_SESSION_SEED: &str = "game_session"; @@ -1468,43 +1468,20 @@ mod tests { use super::*; #[test] - fn test_derive_light_rent_sponsors_macro_matches_runtime() { - let runtime_sponsors = light_sdk::utils::derive_rent_sponsors(&crate::ID); - - for version in 1u16..=4u16 { - let macro_sponsor = RENT_SPONSORS.get(version).unwrap(); - let runtime_sponsor = runtime_sponsors.get(version).unwrap(); - - assert_eq!( - macro_sponsor, runtime_sponsor, - "Macro-derived sponsor for version {} should match runtime (all)", - version - ); - - // Also verify single-version function matches - let (single_pda, single_bump) = - light_sdk::utils::derive_rent_sponsor_pda(&crate::ID, version); - assert_eq!( - macro_sponsor.rent_sponsor, - single_pda.to_bytes(), - "Macro-derived PDA for version {} should match runtime (single)", - version - ); - assert_eq!(macro_sponsor.bump, single_bump); - } - } - - #[test] - fn test_rent_sponsors_default_is_version_1() { - let default = RENT_SPONSORS.default(); - let v1 = RENT_SPONSORS.get(1).unwrap(); - assert_eq!(default, v1, "Default should be version 1"); - assert_eq!(default.version, 1); - } + fn test_derive_light_rent_sponsor_macro_matches_runtime() { + let runtime_sponsor = light_sdk::utils::derive_rent_sponsor(&crate::ID); - #[test] - fn test_rent_sponsors_invalid_versions() { - assert!(RENT_SPONSORS.get(0).is_none(), "Version 0 should be None"); - assert!(RENT_SPONSORS.get(5).is_none(), "Version 5 should be None"); + assert_eq!( + RENT_SPONSOR.rent_sponsor, runtime_sponsor.rent_sponsor, + "Macro-derived PDA should match runtime" + ); + assert_eq!( + RENT_SPONSOR.bump, runtime_sponsor.bump, + "Macro-derived bump should match runtime" + ); + assert_eq!( + RENT_SPONSOR.program_id, runtime_sponsor.program_id, + "Program ID should match" + ); } } From bf4c0b59fe712bf8c1b587edba28a4611c787744 Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sat, 24 Jan 2026 22:35:18 +0000 Subject: [PATCH 04/10] fix --- .../src/light_pdas/program/instructions.rs | 18 +++++++- .../src/lib.rs | 2 +- .../csdk-anchor-full-derived-test/src/lib.rs | 6 +-- .../tests/basic_test.rs | 43 ++++++++++++------- .../tests/d10_token_accounts_test.rs | 19 ++++---- .../tests/integration_tests.rs | 8 ++-- .../tests/mint/metadata_test.rs | 9 ++-- sdk-tests/single-ata-test/tests/test.rs | 13 ++++-- sdk-tests/single-mint-test/tests/test.rs | 15 +++++-- sdk-tests/single-pda-test/tests/test.rs | 10 ++++- sdk-tests/single-token-test/tests/test.rs | 13 ++++-- 11 files changed, 106 insertions(+), 50 deletions(-) diff --git a/sdk-libs/macros/src/light_pdas/program/instructions.rs b/sdk-libs/macros/src/light_pdas/program/instructions.rs index 0630dccda7..1c943098ee 100644 --- a/sdk-libs/macros/src/light_pdas/program/instructions.rs +++ b/sdk-libs/macros/src/light_pdas/program/instructions.rs @@ -548,7 +548,23 @@ fn codegen( content.1.push(item); } - Ok(quote! { #module }) + // Generate light_rent_sponsor() helper function at crate level (outside the module) + // This uses crate::ID which is set by declare_id!() + Ok(quote! { + /// Returns the program's rent sponsor PDA (version 1). + /// + /// Derives the rent sponsor PDA from this program's ID. + /// This is automatically generated by `#[light_program]`. + pub fn light_rent_sponsor() -> ::solana_pubkey::Pubkey { + let (pda, _) = ::solana_pubkey::Pubkey::find_program_address( + &[b"rent_sponsor", &1u16.to_le_bytes()], + &crate::ID, + ); + pda + } + + #module + }) } // ============================================================================= diff --git a/sdk-tests/csdk-anchor-full-derived-test-sdk/src/lib.rs b/sdk-tests/csdk-anchor-full-derived-test-sdk/src/lib.rs index c8d9bc1df2..3d86b49e62 100644 --- a/sdk-tests/csdk-anchor-full-derived-test-sdk/src/lib.rs +++ b/sdk-tests/csdk-anchor-full-derived-test-sdk/src/lib.rs @@ -323,7 +323,7 @@ impl LightProgramInterface for AmmSdk { } fn light_rent_sponsor_pda(&self) -> Pubkey { - csdk_anchor_full_derived_test::program_rent_sponsor() + csdk_anchor_full_derived_test::light_rent_sponsor() } fn from_keyed_accounts(accounts: &[AccountInterface]) -> Result { diff --git a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs index 98bb5ac540..ffac4ae671 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs @@ -73,13 +73,11 @@ declare_id!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah"); pub const LIGHT_CPI_SIGNER: CpiSigner = derive_light_cpi_signer!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah"); +// RENT_SPONSOR constant for compile-time access to bump (kept for efficiency in on-chain code) pub const RENT_SPONSOR: RentSponsor = derive_light_rent_sponsor!("FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah"); -#[inline] -pub fn program_rent_sponsor() -> Pubkey { - Pubkey::from(RENT_SPONSOR.rent_sponsor) -} +// Note: light_rent_sponsor() is auto-generated by #[light_program] macro below pub const GAME_SESSION_SEED: &str = "game_session"; diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs index 1590979086..51143410a6 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs @@ -22,10 +22,11 @@ use solana_signer::Signer; async fn test_create_pdas_and_mint_auto() { use csdk_anchor_full_derived_test::{ instruction_accounts::{LP_MINT_SIGNER_SEED, VAULT_SEED}, - FullAutoWithMintParams, GameSession, + light_rent_sponsor, FullAutoWithMintParams, GameSession, }; use light_token::instruction::{ - get_associated_token_address_and_bump, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR, + get_associated_token_address_and_bump, COMPRESSIBLE_CONFIG_V1, + RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, }; use light_token_interface::state::Token; @@ -81,11 +82,12 @@ async fn test_create_pdas_and_mint_auto() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + light_rent_sponsor(), payer.pubkey(), 10_000_000_000, ) @@ -181,7 +183,7 @@ async fn test_create_pdas_and_mint_auto() { user_ata: user_ata_pda, compression_config: config_pda, light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: RENT_SPONSOR, + rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, @@ -515,11 +517,13 @@ async fn test_create_pdas_and_mint_auto() { /// Verifies multi-mint support in the RentFree macro. #[tokio::test] async fn test_create_two_mints() { - use csdk_anchor_full_derived_test::instruction_accounts::{ - CreateTwoMintsParams, MINT_SIGNER_A_SEED, MINT_SIGNER_B_SEED, + use csdk_anchor_full_derived_test::{ + instruction_accounts::{CreateTwoMintsParams, MINT_SIGNER_A_SEED, MINT_SIGNER_B_SEED}, + light_rent_sponsor, }; use light_token::instruction::{ - find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR, + find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, + RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, }; let program_id = csdk_anchor_full_derived_test::ID; @@ -538,11 +542,12 @@ async fn test_create_two_mints() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + light_rent_sponsor(), payer.pubkey(), 10_000_000_000, ) @@ -599,7 +604,7 @@ async fn test_create_two_mints() { cmint_b: cmint_b_pda, compression_config: config_pda, light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: RENT_SPONSOR, + rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, @@ -716,11 +721,15 @@ async fn test_create_two_mints() { /// Verifies multi-mint support in the RentFree macro scales beyond 2. #[tokio::test] async fn test_create_multi_mints() { - use csdk_anchor_full_derived_test::instruction_accounts::{ - CreateThreeMintsParams, MINT_SIGNER_A_SEED, MINT_SIGNER_B_SEED, MINT_SIGNER_C_SEED, + use csdk_anchor_full_derived_test::{ + instruction_accounts::{ + CreateThreeMintsParams, MINT_SIGNER_A_SEED, MINT_SIGNER_B_SEED, MINT_SIGNER_C_SEED, + }, + light_rent_sponsor, }; use light_token::instruction::{ - find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR, + find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, + RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, }; let program_id = csdk_anchor_full_derived_test::ID; @@ -735,11 +744,12 @@ async fn test_create_multi_mints() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + light_rent_sponsor(), payer.pubkey(), 10_000_000_000, ) @@ -794,7 +804,7 @@ async fn test_create_multi_mints() { cmint_c: cmint_c_pda, compression_config: config_pda, light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: RENT_SPONSOR, + rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, @@ -876,7 +886,7 @@ async fn test_create_multi_mints() { /// Helper function to set up test context for D9 instruction data tests. /// Returns (rpc, payer, program_id, config_pda). async fn setup_d9_test_context() -> (LightProgramTest, Keypair, Pubkey, Pubkey) { - use light_token::instruction::RENT_SPONSOR; + use csdk_anchor_full_derived_test::light_rent_sponsor; let program_id = csdk_anchor_full_derived_test::ID; let mut config = ProgramTestConfig::new_v2( @@ -890,11 +900,12 @@ async fn setup_d9_test_context() -> (LightProgramTest, Keypair, Pubkey, Pubkey) let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + light_rent_sponsor(), payer.pubkey(), 10_000_000_000, ) diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs index bf28b3bf17..6117d9f102 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs @@ -6,24 +6,24 @@ mod shared; use anchor_lang::{InstructionData, ToAccountMetas}; -use csdk_anchor_full_derived_test::d10_token_accounts::{ - D10SingleAtaParams, D10SingleVaultParams, D10_SINGLE_VAULT_AUTH_SEED, D10_SINGLE_VAULT_SEED, +use csdk_anchor_full_derived_test::{ + d10_token_accounts::{ + D10SingleAtaParams, D10SingleVaultParams, D10_SINGLE_VAULT_AUTH_SEED, D10_SINGLE_VAULT_SEED, + }, + light_rent_sponsor, }; use light_client::interface::{get_create_accounts_proof, InitializeRentFreeConfig}; -use light_macros::pubkey; use light_program_test::{ program_test::{setup_mock_program_data, LightProgramTest}, ProgramTestConfig, Rpc, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; -use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR}; +use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR}; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; use solana_signer::Signer; -const RENT_SPONSOR_PUBKEY: Pubkey = pubkey!("CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG"); - /// Test context for D10 token account tests struct D10TestContext { rpc: LightProgramTest, @@ -47,11 +47,12 @@ impl D10TestContext { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR_PUBKEY, + light_rent_sponsor(), payer.pubkey(), 10_000_000_000, ) @@ -118,7 +119,7 @@ async fn test_d10_single_vault() { d10_vault_authority, d10_single_vault, light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: RENT_SPONSOR, + light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), system_program: solana_sdk::system_program::ID, @@ -177,7 +178,7 @@ async fn test_d10_single_ata() { d10_ata_owner: ata_owner, d10_single_ata, light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: RENT_SPONSOR, + light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), system_program: solana_sdk::system_program::ID, }; diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs index 71732bd154..ddac33f7df 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs @@ -8,13 +8,12 @@ mod shared; use anchor_lang::{InstructionData, ToAccountMetas}; -use csdk_anchor_full_derived_test::csdk_anchor_full_derived_test::LightAccountVariant; +use csdk_anchor_full_derived_test::{csdk_anchor_full_derived_test::LightAccountVariant, light_rent_sponsor}; use light_client::interface::{ create_load_instructions, get_create_accounts_proof, AccountInterfaceExt, AccountSpec, CreateAccountsProofInput, InitializeRentFreeConfig, PdaSpec, }; use light_compressible::rent::SLOTS_PER_EPOCH; -use light_macros::pubkey; use light_program_test::{ program_test::{setup_mock_program_data, LightProgramTest, TestRpc}, Indexer, ProgramTestConfig, Rpc, @@ -25,8 +24,6 @@ use solana_keypair::Keypair; use solana_pubkey::Pubkey; use solana_signer::Signer; -const RENT_SPONSOR: Pubkey = pubkey!("CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG"); - /// Test context shared across instruction tests #[allow(dead_code)] struct TestContext { @@ -50,11 +47,12 @@ impl TestContext { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + light_rent_sponsor(), payer.pubkey(), 10_000_000_000, ) diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs index 66989f93db..e1462e2fc7 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs @@ -1,6 +1,7 @@ //! Integration tests for mint with metadata support in #[light_account(init)] macro. use anchor_lang::{InstructionData, ToAccountMetas}; +use csdk_anchor_full_derived_test::light_rent_sponsor; use light_client::interface::{ decompress_mint::decompress_mint, get_create_accounts_proof, AccountInterfaceExt, CreateAccountsProofInput, InitializeRentFreeConfig, @@ -26,7 +27,8 @@ async fn test_create_mint_with_metadata() { CreateMintWithMetadataParams, METADATA_MINT_SIGNER_SEED, }; use light_token::instruction::{ - find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR, + find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, + RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, }; let program_id = csdk_anchor_full_derived_test::ID; @@ -41,11 +43,12 @@ async fn test_create_mint_with_metadata() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + light_rent_sponsor(), payer.pubkey(), 10_000_000_000, ) @@ -97,7 +100,7 @@ async fn test_create_mint_with_metadata() { cmint: cmint_pda, compression_config: config_pda, light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: RENT_SPONSOR, + rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, diff --git a/sdk-tests/single-ata-test/tests/test.rs b/sdk-tests/single-ata-test/tests/test.rs index 6e4898a4aa..e15628db07 100644 --- a/sdk-tests/single-ata-test/tests/test.rs +++ b/sdk-tests/single-ata-test/tests/test.rs @@ -7,12 +7,18 @@ use light_program_test::{ Indexer, ProgramTestConfig, Rpc, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; -use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR}; +use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR}; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; use solana_signer::Signer; +/// Derive the program's rent sponsor PDA (version 1). +fn program_rent_sponsor(program_id: &Pubkey) -> Pubkey { + let (pda, _) = Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); + pda +} + /// Setup helper: Creates a compressed mint directly using the ctoken SDK. /// Returns (mint_pda, mint_seed_keypair) async fn setup_create_mint( @@ -92,11 +98,12 @@ async fn test_create_single_ata() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, _config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + program_rent_sponsor(&program_id), payer.pubkey(), 10_000_000_000, ) @@ -133,7 +140,7 @@ async fn test_create_single_ata() { ata_owner, ata, light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: RENT_SPONSOR, + light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), system_program: solana_sdk::system_program::ID, }; diff --git a/sdk-tests/single-mint-test/tests/test.rs b/sdk-tests/single-mint-test/tests/test.rs index 92b46db079..b4c664b0d6 100644 --- a/sdk-tests/single-mint-test/tests/test.rs +++ b/sdk-tests/single-mint-test/tests/test.rs @@ -9,12 +9,20 @@ use light_program_test::{ ProgramTestConfig, Rpc, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; -use light_token::instruction::{find_mint_address, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR}; +use light_token::instruction::{ + find_mint_address, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, +}; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; use solana_signer::Signer; +/// Derive the program's rent sponsor PDA (version 1). +fn program_rent_sponsor(program_id: &Pubkey) -> Pubkey { + let (pda, _) = Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); + pda +} + /// Test creating a single mint using the macro. /// Validates that #[light_account(init, mint, ...)] works in isolation. #[tokio::test] @@ -30,11 +38,12 @@ async fn test_create_single_mint() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + program_rent_sponsor(&program_id), payer.pubkey(), 10_000_000_000, ) @@ -71,7 +80,7 @@ async fn test_create_single_mint() { mint: mint_pda, compression_config: config_pda, light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: RENT_SPONSOR, + rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, diff --git a/sdk-tests/single-pda-test/tests/test.rs b/sdk-tests/single-pda-test/tests/test.rs index 1d4465200a..2865a3edc6 100644 --- a/sdk-tests/single-pda-test/tests/test.rs +++ b/sdk-tests/single-pda-test/tests/test.rs @@ -8,12 +8,17 @@ use light_program_test::{ program_test::{setup_mock_program_data, LightProgramTest}, ProgramTestConfig, Rpc, }; -use light_token::instruction::RENT_SPONSOR; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; use solana_signer::Signer; +/// Derive the program's rent sponsor PDA (version 1). +fn program_rent_sponsor(program_id: &Pubkey) -> Pubkey { + let (pda, _) = Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); + pda +} + /// Test creating a single compressible PDA using the macro. /// Validates that #[light_account(init)] works in isolation for PDAs. #[tokio::test] @@ -29,11 +34,12 @@ async fn test_create_single_pda() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + program_rent_sponsor(&program_id), payer.pubkey(), 10_000_000_000, ) diff --git a/sdk-tests/single-token-test/tests/test.rs b/sdk-tests/single-token-test/tests/test.rs index df208dbdd5..6847c38806 100644 --- a/sdk-tests/single-token-test/tests/test.rs +++ b/sdk-tests/single-token-test/tests/test.rs @@ -7,12 +7,18 @@ use light_program_test::{ Indexer, ProgramTestConfig, Rpc, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; -use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR}; +use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR}; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; use solana_signer::Signer; +/// Derive the program's rent sponsor PDA (version 1). +fn program_rent_sponsor(program_id: &Pubkey) -> Pubkey { + let (pda, _) = Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); + pda +} + /// Setup helper: Creates a compressed mint directly using the ctoken SDK. /// Returns (mint_pda, mint_seed_keypair) async fn setup_create_mint( @@ -92,11 +98,12 @@ async fn test_create_single_token_vault() { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); + // Use program's own rent sponsor for LightConfig initialization let (init_config_ixs, _config_pda) = InitializeRentFreeConfig::new( &program_id, &payer.pubkey(), &program_data_pda, - RENT_SPONSOR, + program_rent_sponsor(&program_id), payer.pubkey(), 10_000_000_000, ) @@ -133,7 +140,7 @@ async fn test_create_single_token_vault() { vault_authority, vault, light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: RENT_SPONSOR, + light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), system_program: solana_sdk::system_program::ID, From efaa1b3eac504b5eba519d32e63ae7344febb92f Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sun, 25 Jan 2026 01:13:15 +0000 Subject: [PATCH 05/10] fmt, lint, cleanup --- .../client/src/interface/initialize_config.rs | 6 ++-- .../src/interface/light_program_interface.rs | 29 ++++++++++++++- .../v2/create_compressed_mint/instruction.rs | 4 +-- .../v2/mint_to_compressed/instruction.rs | 9 ++--- .../src/light_pdas/program/expr_traversal.rs | 14 +++----- sdk-libs/token-sdk/src/instruction/mod.rs | 12 +++++++ .../csdk-anchor-full-derived-test/src/lib.rs | 3 +- .../tests/amm_test.rs | 15 +++++--- .../tests/basic_test.rs | 36 +++++++++---------- .../tests/d10_token_accounts_test.rs | 14 +++++--- .../tests/integration_tests.rs | 35 ++++++++++-------- .../tests/mint/metadata_test.rs | 12 +++---- sdk-tests/single-ata-test/tests/test.rs | 3 +- sdk-tests/single-mint-test/tests/test.rs | 3 +- sdk-tests/single-pda-test/tests/test.rs | 3 +- sdk-tests/single-token-test/tests/test.rs | 3 +- 16 files changed, 123 insertions(+), 78 deletions(-) diff --git a/sdk-libs/client/src/interface/initialize_config.rs b/sdk-libs/client/src/interface/initialize_config.rs index 8514f514e9..b161edc410 100644 --- a/sdk-libs/client/src/interface/initialize_config.rs +++ b/sdk-libs/client/src/interface/initialize_config.rs @@ -110,10 +110,8 @@ impl InitializeRentFreeConfig { let (config_pda, _) = LightConfig::derive_pda(&self.program_id, self.config_bump); // Derive rent sponsor bump (version 1, hardcoded) - let (derived_rent_sponsor, rent_sponsor_bump) = Pubkey::find_program_address( - &[b"rent_sponsor", &1u16.to_le_bytes()], - &self.program_id, - ); + let (derived_rent_sponsor, rent_sponsor_bump) = + Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], &self.program_id); assert_eq!( derived_rent_sponsor, self.rent_sponsor, "Rent sponsor PDA mismatch: derived {:?} != provided {:?}", diff --git a/sdk-libs/client/src/interface/light_program_interface.rs b/sdk-libs/client/src/interface/light_program_interface.rs index 76dc0c9149..096756dfb4 100644 --- a/sdk-libs/client/src/interface/light_program_interface.rs +++ b/sdk-libs/client/src/interface/light_program_interface.rs @@ -9,7 +9,10 @@ use std::fmt::Debug; use light_sdk::interface::Pack; -use light_token::instruction::derive_token_ata; +use light_token::{ + constants::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR_V1}, + instruction::derive_token_ata, +}; use solana_pubkey::Pubkey; use super::{AccountInterface, TokenAccountInterface}; @@ -239,6 +242,30 @@ pub trait LightProgramInterface: Sized { #[must_use] fn light_rent_sponsor_pda(&self) -> Pubkey; + /// Returns the ctoken program's rent sponsor PDA for token operations. + /// + /// This is the rent sponsor used by the Light Compressed Token program + /// (`cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m`) for funding token + /// account decompression and receiving rent on token compression. + /// + /// Override this if using a custom compressible config with a different + /// rent sponsor. + #[must_use] + fn light_token_rent_sponsor_pda(&self) -> Pubkey { + RENT_SPONSOR_V1 + } + + /// Returns the ctoken program's compressible config PDA. + /// + /// This is the global config account owned by the Light Registry program + /// that stores rent parameters for compressible token accounts. + /// + /// Override this if using a custom compressible config. + #[must_use] + fn light_token_config_pda(&self) -> Pubkey { + COMPRESSIBLE_CONFIG_V1 + } + /// Construct SDK from root account(s). fn from_keyed_accounts(accounts: &[AccountInterface]) -> Result; diff --git a/sdk-libs/compressed-token-sdk/src/compressed_token/v2/create_compressed_mint/instruction.rs b/sdk-libs/compressed-token-sdk/src/compressed_token/v2/create_compressed_mint/instruction.rs index 5e8e254928..2341523578 100644 --- a/sdk-libs/compressed-token-sdk/src/compressed_token/v2/create_compressed_mint/instruction.rs +++ b/sdk-libs/compressed-token-sdk/src/compressed_token/v2/create_compressed_mint/instruction.rs @@ -70,12 +70,12 @@ pub fn create_compressed_mint_cpi( instruction_data = instruction_data.with_cpi_context(ctx); } - let meta_config = if cpi_context_pubkey.is_some() { + let meta_config = if let Some(ctx_pubkey) = cpi_context_pubkey { MintActionMetaConfig::new_cpi_context( &instruction_data, input.payer, input.mint_authority, - cpi_context_pubkey.unwrap(), + ctx_pubkey, )? } else { MintActionMetaConfig::new_create_mint( diff --git a/sdk-libs/compressed-token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs b/sdk-libs/compressed-token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs index 5468dd6c24..161d0d5ad7 100644 --- a/sdk-libs/compressed-token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs +++ b/sdk-libs/compressed-token-sdk/src/compressed_token/v2/mint_to_compressed/instruction.rs @@ -70,13 +70,8 @@ pub fn create_mint_to_compressed_instruction( instruction_data = instruction_data.with_cpi_context(ctx); } - let meta_config = if cpi_context_pubkey.is_some() { - MintActionMetaConfig::new_cpi_context( - &instruction_data, - payer, - mint_authority, - cpi_context_pubkey.unwrap(), - )? + let meta_config = if let Some(ctx_pubkey) = cpi_context_pubkey { + MintActionMetaConfig::new_cpi_context(&instruction_data, payer, mint_authority, ctx_pubkey)? } else { MintActionMetaConfig::new( payer, diff --git a/sdk-libs/macros/src/light_pdas/program/expr_traversal.rs b/sdk-libs/macros/src/light_pdas/program/expr_traversal.rs index 8f74097d2e..ca949d6531 100644 --- a/sdk-libs/macros/src/light_pdas/program/expr_traversal.rs +++ b/sdk-libs/macros/src/light_pdas/program/expr_traversal.rs @@ -73,11 +73,8 @@ fn transform_expr_internal( } Expr::MethodCall(method_call) => { let mut new_call = method_call.clone(); - new_call.receiver = Box::new(transform_expr_internal( - &method_call.receiver, - ctx_field_names, - state_field_names, - )); + *new_call.receiver = + transform_expr_internal(&method_call.receiver, ctx_field_names, state_field_names); new_call.args = method_call .args .iter() @@ -96,11 +93,8 @@ fn transform_expr_internal( } Expr::Reference(ref_expr) => { let mut new_ref = ref_expr.clone(); - new_ref.expr = Box::new(transform_expr_internal( - &ref_expr.expr, - ctx_field_names, - state_field_names, - )); + *new_ref.expr = + transform_expr_internal(&ref_expr.expr, ctx_field_names, state_field_names); Expr::Reference(new_ref) } _ => expr.clone(), diff --git a/sdk-libs/token-sdk/src/instruction/mod.rs b/sdk-libs/token-sdk/src/instruction/mod.rs index d5be2ca2f1..6662358107 100644 --- a/sdk-libs/token-sdk/src/instruction/mod.rs +++ b/sdk-libs/token-sdk/src/instruction/mod.rs @@ -222,3 +222,15 @@ pub use crate::{ spl_interface::get_spl_interface_pda_and_bump, utils::{get_associated_token_address, get_associated_token_address_and_bump}, }; + +/// Returns the default compressible config PDA for Light Token. +#[inline] +pub fn light_token_config_pda() -> solana_pubkey::Pubkey { + COMPRESSIBLE_CONFIG_V1 +} + +/// Returns the default rent sponsor PDA for Light Token. +#[inline] +pub fn light_token_rent_sponsor_pda() -> solana_pubkey::Pubkey { + RENT_SPONSOR +} diff --git a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs index ffac4ae671..e86a5d8e56 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs @@ -1,4 +1,5 @@ -#![allow(clippy::useless_asref)] // Testing macro handling of .as_ref() patterns +// anchor's #[program] uses deprecated AccountInfo::realloc internally +#![allow(clippy::useless_asref, deprecated)] use anchor_lang::prelude::*; use light_instruction_decoder_derive::instruction_decoder; diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs index 4c16e1352b..6fcdb1d2d8 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs @@ -26,8 +26,8 @@ use light_program_test::{ Indexer, ProgramTestConfig, Rpc, }; use light_token::instruction::{ - find_mint_address, get_associated_token_address_and_bump, COMPRESSIBLE_CONFIG_V1, - LIGHT_TOKEN_CPI_AUTHORITY, LIGHT_TOKEN_PROGRAM_ID, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, + find_mint_address, get_associated_token_address_and_bump, LIGHT_TOKEN_CPI_AUTHORITY, + LIGHT_TOKEN_PROGRAM_ID, }; use light_token_interface::state::Token; use solana_instruction::Instruction; @@ -117,6 +117,10 @@ struct AmmTestContext { program_id: Pubkey, token_0_mint: Pubkey, token_1_mint: Pubkey, + /// CToken program's compressible config PDA (from SDK trait) + light_token_config_pda: Pubkey, + /// CToken program's rent sponsor PDA (from SDK trait) + light_token_rent_sponsor_pda: Pubkey, creator: Keypair, creator_token_0: Pubkey, creator_token_1: Pubkey, @@ -203,6 +207,9 @@ async fn setup() -> AmmTestContext { program_id, token_0_mint, token_1_mint, + // Use SDK trait methods for token config/rent sponsor + light_token_config_pda: sdk.light_token_config_pda(), + light_token_rent_sponsor_pda: sdk.light_token_rent_sponsor_pda(), creator, creator_token_0, creator_token_1, @@ -350,8 +357,8 @@ async fn test_amm_full_lifecycle() { system_program: solana_sdk::system_program::ID, rent: solana_sdk::sysvar::rent::ID, compression_config: ctx.config_pda, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, + light_token_compressible_config: ctx.light_token_config_pda, + rent_sponsor: ctx.light_token_rent_sponsor_pda, light_token_program: LIGHT_TOKEN_PROGRAM_ID, light_token_cpi_authority: LIGHT_TOKEN_CPI_AUTHORITY, }; diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs index 51143410a6..7b9d94ab02 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/basic_test.rs @@ -9,7 +9,9 @@ use light_program_test::{ Indexer, ProgramTestConfig, Rpc, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; -use light_token::instruction::find_mint_address as find_cmint_address; +use light_token::instruction::{ + find_mint_address as find_cmint_address, light_token_config_pda, light_token_rent_sponsor_pda, +}; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; @@ -24,12 +26,12 @@ async fn test_create_pdas_and_mint_auto() { instruction_accounts::{LP_MINT_SIGNER_SEED, VAULT_SEED}, light_rent_sponsor, FullAutoWithMintParams, GameSession, }; - use light_token::instruction::{ - get_associated_token_address_and_bump, COMPRESSIBLE_CONFIG_V1, - RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, - }; + use light_token::instruction::get_associated_token_address_and_bump; use light_token_interface::state::Token; + let light_token_config = light_token_config_pda(); + let light_token_rent_sponsor = light_token_rent_sponsor_pda(); + // Helpers async fn assert_onchain_exists(rpc: &mut LightProgramTest, pda: &Pubkey) { assert!(rpc.get_account(*pda).await.unwrap().is_some()); @@ -182,8 +184,8 @@ async fn test_create_pdas_and_mint_auto() { vault_authority: vault_authority_pda, user_ata: user_ata_pda, compression_config: config_pda, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, + light_token_compressible_config: light_token_config, + rent_sponsor: light_token_rent_sponsor, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, @@ -521,11 +523,9 @@ async fn test_create_two_mints() { instruction_accounts::{CreateTwoMintsParams, MINT_SIGNER_A_SEED, MINT_SIGNER_B_SEED}, light_rent_sponsor, }; - use light_token::instruction::{ - find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, - RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, - }; + let light_token_config = light_token_config_pda(); + let light_token_rent_sponsor = light_token_rent_sponsor_pda(); let program_id = csdk_anchor_full_derived_test::ID; let config = ProgramTestConfig::new_v2( true, @@ -603,8 +603,8 @@ async fn test_create_two_mints() { cmint_a: cmint_a_pda, cmint_b: cmint_b_pda, compression_config: config_pda, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, + light_token_compressible_config: light_token_config, + rent_sponsor: light_token_rent_sponsor, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, @@ -727,11 +727,9 @@ async fn test_create_multi_mints() { }, light_rent_sponsor, }; - use light_token::instruction::{ - find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, - RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, - }; + let light_token_config = light_token_config_pda(); + let light_token_rent_sponsor = light_token_rent_sponsor_pda(); let program_id = csdk_anchor_full_derived_test::ID; let mut config = ProgramTestConfig::new_v2( true, @@ -803,8 +801,8 @@ async fn test_create_multi_mints() { cmint_b: cmint_b_pda, cmint_c: cmint_c_pda, compression_config: config_pda, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, + light_token_compressible_config: light_token_config, + rent_sponsor: light_token_rent_sponsor, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs index 6117d9f102..f69412be1a 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/d10_token_accounts_test.rs @@ -18,7 +18,7 @@ use light_program_test::{ ProgramTestConfig, Rpc, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; -use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR}; +use light_token::instruction::{light_token_config_pda, light_token_rent_sponsor_pda}; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; @@ -96,6 +96,8 @@ impl D10TestContext { /// The macro should generate CreateTokenAccountCpi in LightFinalize. #[tokio::test] async fn test_d10_single_vault() { + let light_token_config = light_token_config_pda(); + let light_token_rent_sponsor = light_token_rent_sponsor_pda(); let mut ctx = D10TestContext::new().await; // Setup mint @@ -118,8 +120,8 @@ async fn test_d10_single_vault() { d10_mint: mint, d10_vault_authority, d10_single_vault, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, + light_token_compressible_config: light_token_config, + light_token_rent_sponsor, light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), system_program: solana_sdk::system_program::ID, @@ -155,6 +157,8 @@ async fn test_d10_single_vault() { /// The macro should generate create_associated_token_account_idempotent in LightFinalize. #[tokio::test] async fn test_d10_single_ata() { + let light_token_config = light_token_config_pda(); + let light_token_rent_sponsor = light_token_rent_sponsor_pda(); let mut ctx = D10TestContext::new().await; // Setup mint @@ -177,8 +181,8 @@ async fn test_d10_single_ata() { d10_ata_mint: mint, d10_ata_owner: ata_owner, d10_single_ata, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, + light_token_compressible_config: light_token_config, + light_token_rent_sponsor, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), system_program: solana_sdk::system_program::ID, }; diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs index ddac33f7df..465bb60df2 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs @@ -8,7 +8,9 @@ mod shared; use anchor_lang::{InstructionData, ToAccountMetas}; -use csdk_anchor_full_derived_test::{csdk_anchor_full_derived_test::LightAccountVariant, light_rent_sponsor}; +use csdk_anchor_full_derived_test::{ + csdk_anchor_full_derived_test::LightAccountVariant, light_rent_sponsor, +}; use light_client::interface::{ create_load_instructions, get_create_accounts_proof, AccountInterfaceExt, AccountSpec, CreateAccountsProofInput, InitializeRentFreeConfig, PdaSpec, @@ -19,6 +21,7 @@ use light_program_test::{ Indexer, ProgramTestConfig, Rpc, }; use light_sdk::interface::IntoVariant; +use light_token::instruction::{light_token_config_pda, light_token_rent_sponsor_pda}; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; @@ -1462,8 +1465,9 @@ async fn test_d5_light_token() { D5LightTokenParams, D5_VAULT_AUTH_SEED, D5_VAULT_SEED, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; - use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR}; + let light_token_config = light_token_config_pda(); + let light_token_rent_sponsor = light_token_rent_sponsor_pda(); let mut ctx = TestContext::new().await; // Setup mint @@ -1485,8 +1489,8 @@ async fn test_d5_light_token() { mint, vault_authority, d5_token_vault: vault, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: RENT_SPONSOR, + light_token_compressible_config: light_token_config, + light_token_rent_sponsor, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, @@ -1527,8 +1531,9 @@ async fn test_d5_all_markers() { D5AllMarkersParams, D5_ALL_AUTH_SEED, D5_ALL_VAULT_SEED, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; - use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR}; + let light_token_config = light_token_config_pda(); + let light_token_rent_sponsor = light_token_rent_sponsor_pda(); let mut ctx = TestContext::new().await; let owner = Keypair::new().pubkey(); @@ -1559,8 +1564,8 @@ async fn test_d5_all_markers() { d5_all_authority, d5_all_record, d5_all_vault, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: RENT_SPONSOR, + light_token_compressible_config: light_token_config, + light_token_rent_sponsor, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, @@ -1611,10 +1616,9 @@ async fn test_d7_light_token_config() { D7LightTokenConfigParams, D7_LIGHT_TOKEN_AUTH_SEED, D7_LIGHT_TOKEN_VAULT_SEED, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; - use light_token::instruction::{ - COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, - }; + let light_token_config = light_token_config_pda(); + let light_token_rent_sponsor = light_token_rent_sponsor_pda(); let mut ctx = TestContext::new().await; // Setup mint @@ -1637,8 +1641,8 @@ async fn test_d7_light_token_config() { mint, d7_light_token_authority, d7_light_token_vault, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - light_token_rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, + light_token_compressible_config: light_token_config, + light_token_rent_sponsor, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, @@ -1678,8 +1682,9 @@ async fn test_d7_all_names() { D7AllNamesParams, D7_ALL_AUTH_SEED, D7_ALL_VAULT_SEED, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; - use light_token::instruction::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR}; + let light_token_config = light_token_config_pda(); + let light_token_rent_sponsor = light_token_rent_sponsor_pda(); let mut ctx = TestContext::new().await; let owner = Keypair::new().pubkey(); @@ -1710,8 +1715,8 @@ async fn test_d7_all_names() { d7_all_authority, d7_all_record, d7_all_vault, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: RENT_SPONSOR, + light_token_compressible_config: light_token_config, + rent_sponsor: light_token_rent_sponsor, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs index e1462e2fc7..b886b9fd35 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/mint/metadata_test.rs @@ -12,6 +12,7 @@ use light_program_test::{ Indexer, ProgramTestConfig, Rpc, }; use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID; +use light_token::instruction::{light_token_config_pda, light_token_rent_sponsor_pda}; use solana_instruction::Instruction; use solana_keypair::Keypair; use solana_pubkey::Pubkey; @@ -26,11 +27,10 @@ async fn test_create_mint_with_metadata() { use csdk_anchor_full_derived_test::instruction_accounts::{ CreateMintWithMetadataParams, METADATA_MINT_SIGNER_SEED, }; - use light_token::instruction::{ - find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, - RENT_SPONSOR as LIGHT_TOKEN_RENT_SPONSOR, - }; + use light_token::instruction::find_mint_address as find_cmint_address; + let light_token_config = light_token_config_pda(); + let light_token_rent_sponsor = light_token_rent_sponsor_pda(); let program_id = csdk_anchor_full_derived_test::ID; let mut config = ProgramTestConfig::new_v2( true, @@ -99,8 +99,8 @@ async fn test_create_mint_with_metadata() { mint_signer: mint_signer_pda, cmint: cmint_pda, compression_config: config_pda, - light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, + light_token_compressible_config: light_token_config, + rent_sponsor: light_token_rent_sponsor, light_token_program: LIGHT_TOKEN_PROGRAM_ID.into(), light_token_cpi_authority: light_token_types::CPI_AUTHORITY_PDA.into(), system_program: solana_sdk::system_program::ID, diff --git a/sdk-tests/single-ata-test/tests/test.rs b/sdk-tests/single-ata-test/tests/test.rs index e15628db07..0475a09976 100644 --- a/sdk-tests/single-ata-test/tests/test.rs +++ b/sdk-tests/single-ata-test/tests/test.rs @@ -15,7 +15,8 @@ use solana_signer::Signer; /// Derive the program's rent sponsor PDA (version 1). fn program_rent_sponsor(program_id: &Pubkey) -> Pubkey { - let (pda, _) = Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); + let (pda, _) = + Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); pda } diff --git a/sdk-tests/single-mint-test/tests/test.rs b/sdk-tests/single-mint-test/tests/test.rs index b4c664b0d6..ccc9dd5a6c 100644 --- a/sdk-tests/single-mint-test/tests/test.rs +++ b/sdk-tests/single-mint-test/tests/test.rs @@ -19,7 +19,8 @@ use solana_signer::Signer; /// Derive the program's rent sponsor PDA (version 1). fn program_rent_sponsor(program_id: &Pubkey) -> Pubkey { - let (pda, _) = Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); + let (pda, _) = + Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); pda } diff --git a/sdk-tests/single-pda-test/tests/test.rs b/sdk-tests/single-pda-test/tests/test.rs index 2865a3edc6..57cd289286 100644 --- a/sdk-tests/single-pda-test/tests/test.rs +++ b/sdk-tests/single-pda-test/tests/test.rs @@ -15,7 +15,8 @@ use solana_signer::Signer; /// Derive the program's rent sponsor PDA (version 1). fn program_rent_sponsor(program_id: &Pubkey) -> Pubkey { - let (pda, _) = Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); + let (pda, _) = + Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); pda } diff --git a/sdk-tests/single-token-test/tests/test.rs b/sdk-tests/single-token-test/tests/test.rs index 6847c38806..1ef97fae8a 100644 --- a/sdk-tests/single-token-test/tests/test.rs +++ b/sdk-tests/single-token-test/tests/test.rs @@ -15,7 +15,8 @@ use solana_signer::Signer; /// Derive the program's rent sponsor PDA (version 1). fn program_rent_sponsor(program_id: &Pubkey) -> Pubkey { - let (pda, _) = Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); + let (pda, _) = + Pubkey::find_program_address(&[b"rent_sponsor", &1u16.to_le_bytes()], program_id); pda } From e09fe5f341aba34264477f4418f6ff602135494f Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sun, 25 Jan 2026 01:14:50 +0000 Subject: [PATCH 06/10] cleanup --- .../src/interface/light_program_interface.rs | 29 +------------------ .../tests/amm_test.rs | 20 +++++-------- 2 files changed, 9 insertions(+), 40 deletions(-) diff --git a/sdk-libs/client/src/interface/light_program_interface.rs b/sdk-libs/client/src/interface/light_program_interface.rs index 096756dfb4..76dc0c9149 100644 --- a/sdk-libs/client/src/interface/light_program_interface.rs +++ b/sdk-libs/client/src/interface/light_program_interface.rs @@ -9,10 +9,7 @@ use std::fmt::Debug; use light_sdk::interface::Pack; -use light_token::{ - constants::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR_V1}, - instruction::derive_token_ata, -}; +use light_token::instruction::derive_token_ata; use solana_pubkey::Pubkey; use super::{AccountInterface, TokenAccountInterface}; @@ -242,30 +239,6 @@ pub trait LightProgramInterface: Sized { #[must_use] fn light_rent_sponsor_pda(&self) -> Pubkey; - /// Returns the ctoken program's rent sponsor PDA for token operations. - /// - /// This is the rent sponsor used by the Light Compressed Token program - /// (`cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m`) for funding token - /// account decompression and receiving rent on token compression. - /// - /// Override this if using a custom compressible config with a different - /// rent sponsor. - #[must_use] - fn light_token_rent_sponsor_pda(&self) -> Pubkey { - RENT_SPONSOR_V1 - } - - /// Returns the ctoken program's compressible config PDA. - /// - /// This is the global config account owned by the Light Registry program - /// that stores rent parameters for compressible token accounts. - /// - /// Override this if using a custom compressible config. - #[must_use] - fn light_token_config_pda(&self) -> Pubkey { - COMPRESSIBLE_CONFIG_V1 - } - /// Construct SDK from root account(s). fn from_keyed_accounts(accounts: &[AccountInterface]) -> Result; diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs index 6fcdb1d2d8..144dc31fda 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/amm_test.rs @@ -25,9 +25,12 @@ use light_program_test::{ program_test::{setup_mock_program_data, LightProgramTest, TestRpc}, Indexer, ProgramTestConfig, Rpc, }; -use light_token::instruction::{ - find_mint_address, get_associated_token_address_and_bump, LIGHT_TOKEN_CPI_AUTHORITY, - LIGHT_TOKEN_PROGRAM_ID, +use light_token::{ + constants::{COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR_V1}, + instruction::{ + find_mint_address, get_associated_token_address_and_bump, LIGHT_TOKEN_CPI_AUTHORITY, + LIGHT_TOKEN_PROGRAM_ID, + }, }; use light_token_interface::state::Token; use solana_instruction::Instruction; @@ -117,10 +120,6 @@ struct AmmTestContext { program_id: Pubkey, token_0_mint: Pubkey, token_1_mint: Pubkey, - /// CToken program's compressible config PDA (from SDK trait) - light_token_config_pda: Pubkey, - /// CToken program's rent sponsor PDA (from SDK trait) - light_token_rent_sponsor_pda: Pubkey, creator: Keypair, creator_token_0: Pubkey, creator_token_1: Pubkey, @@ -207,9 +206,6 @@ async fn setup() -> AmmTestContext { program_id, token_0_mint, token_1_mint, - // Use SDK trait methods for token config/rent sponsor - light_token_config_pda: sdk.light_token_config_pda(), - light_token_rent_sponsor_pda: sdk.light_token_rent_sponsor_pda(), creator, creator_token_0, creator_token_1, @@ -357,8 +353,8 @@ async fn test_amm_full_lifecycle() { system_program: solana_sdk::system_program::ID, rent: solana_sdk::sysvar::rent::ID, compression_config: ctx.config_pda, - light_token_compressible_config: ctx.light_token_config_pda, - rent_sponsor: ctx.light_token_rent_sponsor_pda, + light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, + rent_sponsor: RENT_SPONSOR_V1, light_token_program: LIGHT_TOKEN_PROGRAM_ID, light_token_cpi_authority: LIGHT_TOKEN_CPI_AUTHORITY, }; From c66d34cf6fe52e86acd4466a72b6e9248f515a03 Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sun, 25 Jan 2026 02:09:33 +0000 Subject: [PATCH 07/10] update forester test compressible pda --- forester/tests/test_compressible_pda.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/forester/tests/test_compressible_pda.rs b/forester/tests/test_compressible_pda.rs index eea3c8aa9a..56bbd1deec 100644 --- a/forester/tests/test_compressible_pda.rs +++ b/forester/tests/test_compressible_pda.rs @@ -48,8 +48,10 @@ const CSDK_TEST_PROGRAM_ID: &str = "FAMipfVEhN4hjCLpKCvjDXXfzLsoVTqQccXzePz1L1ah // This needs to match the discriminator from csdk_anchor_full_derived_test::state::d1_field_types::single_pubkey::SinglePubkeyRecord const SINGLE_PUBKEY_RECORD_DISCRIMINATOR: [u8; 8] = csdk_anchor_full_derived_test::state::d1_field_types::single_pubkey::SinglePubkeyRecord::LIGHT_DISCRIMINATOR; -// Rent sponsor pubkey used in tests -const RENT_SPONSOR: Pubkey = solana_sdk::pubkey!("CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG"); +/// Get the program's derived rent sponsor PDA +fn program_rent_sponsor() -> Pubkey { + csdk_anchor_full_derived_test::light_rent_sponsor() +} /// Context returned from forester registration struct ForesterContext { @@ -285,6 +287,7 @@ async fn test_compressible_pda_bootstrap() { .expect("Failed to airdrop to authority"); // Initialize compression config (includes rent sponsor funding) + let rent_sponsor = program_rent_sponsor(); let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &authority.pubkey(), @@ -293,7 +296,7 @@ async fn test_compressible_pda_bootstrap() { &solana_sdk::pubkey!("BPFLoaderUpgradeab1e11111111111111111111111"), ) .0, - RENT_SPONSOR, + rent_sponsor, authority.pubkey(), 10_000_000_000, ) @@ -471,6 +474,7 @@ async fn test_compressible_pda_compression() { .expect("Failed to airdrop to authority"); // Initialize compression config (includes rent sponsor funding) + let rent_sponsor = program_rent_sponsor(); let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &authority.pubkey(), @@ -479,7 +483,7 @@ async fn test_compressible_pda_compression() { &solana_sdk::pubkey!("BPFLoaderUpgradeab1e11111111111111111111111"), ) .0, - RENT_SPONSOR, + rent_sponsor, authority.pubkey(), 10_000_000_000, ) @@ -705,6 +709,7 @@ async fn test_compressible_pda_subscription() { .expect("Failed to wait for indexer"); // Initialize compression config (includes rent sponsor funding) + let rent_sponsor = program_rent_sponsor(); let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &authority.pubkey(), @@ -713,7 +718,7 @@ async fn test_compressible_pda_subscription() { &solana_sdk::pubkey!("BPFLoaderUpgradeab1e11111111111111111111111"), ) .0, - RENT_SPONSOR, + rent_sponsor, authority.pubkey(), 10_000_000_000, ) From b2e8ff51ea085c0ef483bd236da27f67fafbf692 Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sun, 25 Jan 2026 02:23:26 +0000 Subject: [PATCH 08/10] fix airdrop amount --- forester/tests/test_compressible_pda.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/forester/tests/test_compressible_pda.rs b/forester/tests/test_compressible_pda.rs index 56bbd1deec..5e319dfffa 100644 --- a/forester/tests/test_compressible_pda.rs +++ b/forester/tests/test_compressible_pda.rs @@ -281,8 +281,8 @@ async fn test_compressible_pda_bootstrap() { // Use PAYER_KEYPAIR as it's the upgrade authority for the program let authority = Keypair::try_from(PAYER_KEYPAIR.as_ref()).expect("Invalid PAYER_KEYPAIR"); - // Fund the authority account - rpc.airdrop_lamports(&authority.pubkey(), 10_000_000_000) + // Fund the authority account (extra for tx fees + rent sponsor funding) + rpc.airdrop_lamports(&authority.pubkey(), 20_000_000_000) .await .expect("Failed to airdrop to authority"); @@ -468,8 +468,8 @@ async fn test_compressible_pda_compression() { // Use PAYER_KEYPAIR as it's the upgrade authority for the program let authority = Keypair::try_from(PAYER_KEYPAIR.as_ref()).expect("Invalid PAYER_KEYPAIR"); - // Fund the authority account - rpc.airdrop_lamports(&authority.pubkey(), 10_000_000_000) + // Fund the authority account (extra for tx fees + rent sponsor funding) + rpc.airdrop_lamports(&authority.pubkey(), 20_000_000_000) .await .expect("Failed to airdrop to authority"); @@ -698,8 +698,8 @@ async fn test_compressible_pda_subscription() { let authority = Keypair::try_from(&PAYER_KEYPAIR[..]).unwrap(); - // Fund accounts - rpc.airdrop_lamports(&authority.pubkey(), 10_000_000_000) + // Fund accounts (extra for tx fees + rent sponsor funding) + rpc.airdrop_lamports(&authority.pubkey(), 20_000_000_000) .await .expect("Failed to airdrop to authority"); From 46c505e618c668ff32452d518be9436ae06767c4 Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sun, 25 Jan 2026 03:12:46 +0000 Subject: [PATCH 09/10] add rate limt --- forester/tests/test_compressible_pda.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/forester/tests/test_compressible_pda.rs b/forester/tests/test_compressible_pda.rs index 5e319dfffa..b8a800b91c 100644 --- a/forester/tests/test_compressible_pda.rs +++ b/forester/tests/test_compressible_pda.rs @@ -99,8 +99,8 @@ async fn register_forester( .config; // Use airdrop for forester - println!("Funding forester {} with 10 SOL", forester_pubkey); - rpc.airdrop_lamports(&forester_pubkey, 10_000_000_000) + println!("Funding forester {} with 2 SOL", forester_pubkey); + rpc.airdrop_lamports(&forester_pubkey, 2_000_000_000) .await?; sleep(Duration::from_millis(500)).await; @@ -281,11 +281,14 @@ async fn test_compressible_pda_bootstrap() { // Use PAYER_KEYPAIR as it's the upgrade authority for the program let authority = Keypair::try_from(PAYER_KEYPAIR.as_ref()).expect("Invalid PAYER_KEYPAIR"); - // Fund the authority account (extra for tx fees + rent sponsor funding) - rpc.airdrop_lamports(&authority.pubkey(), 20_000_000_000) + // Fund the authority account + rpc.airdrop_lamports(&authority.pubkey(), 2_000_000_000) .await .expect("Failed to airdrop to authority"); + // for rate limit airdrop + tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; + // Initialize compression config (includes rent sponsor funding) let rent_sponsor = program_rent_sponsor(); let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( @@ -298,7 +301,7 @@ async fn test_compressible_pda_bootstrap() { .0, rent_sponsor, authority.pubkey(), - 10_000_000_000, + 500_000_000, // 0.5 SOL for rent sponsor ) .build(); @@ -468,8 +471,8 @@ async fn test_compressible_pda_compression() { // Use PAYER_KEYPAIR as it's the upgrade authority for the program let authority = Keypair::try_from(PAYER_KEYPAIR.as_ref()).expect("Invalid PAYER_KEYPAIR"); - // Fund the authority account (extra for tx fees + rent sponsor funding) - rpc.airdrop_lamports(&authority.pubkey(), 20_000_000_000) + // Fund the authority account + rpc.airdrop_lamports(&authority.pubkey(), 2_000_000_000) .await .expect("Failed to airdrop to authority"); @@ -485,7 +488,7 @@ async fn test_compressible_pda_compression() { .0, rent_sponsor, authority.pubkey(), - 10_000_000_000, + 500_000_000, // 0.5 SOL for rent sponsor ) .build(); @@ -698,8 +701,8 @@ async fn test_compressible_pda_subscription() { let authority = Keypair::try_from(&PAYER_KEYPAIR[..]).unwrap(); - // Fund accounts (extra for tx fees + rent sponsor funding) - rpc.airdrop_lamports(&authority.pubkey(), 20_000_000_000) + // Fund accounts + rpc.airdrop_lamports(&authority.pubkey(), 2_000_000_000) .await .expect("Failed to airdrop to authority"); @@ -720,7 +723,7 @@ async fn test_compressible_pda_subscription() { .0, rent_sponsor, authority.pubkey(), - 10_000_000_000, + 500_000_000, // 0.5 SOL for rent sponsor ) .build(); From 8fd616cc379b99ccf2047b4f0d270fc0d8c4f1a5 Mon Sep 17 00:00:00 2001 From: Swenschaeferjohann Date: Sun, 25 Jan 2026 04:32:31 +0000 Subject: [PATCH 10/10] rm sleep --- forester/tests/test_compressible_pda.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/forester/tests/test_compressible_pda.rs b/forester/tests/test_compressible_pda.rs index b8a800b91c..92146f5239 100644 --- a/forester/tests/test_compressible_pda.rs +++ b/forester/tests/test_compressible_pda.rs @@ -286,9 +286,6 @@ async fn test_compressible_pda_bootstrap() { .await .expect("Failed to airdrop to authority"); - // for rate limit airdrop - tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; - // Initialize compression config (includes rent sponsor funding) let rent_sponsor = program_rent_sponsor(); let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new(