diff --git a/Cargo.lock b/Cargo.lock index 246f03d9b0..a229d1330c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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/forester/tests/test_compressible_pda.rs b/forester/tests/test_compressible_pda.rs index 5e32ab4e67..92146f5239 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 { @@ -97,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; @@ -280,17 +282,13 @@ async fn test_compressible_pda_bootstrap() { 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) + rpc.airdrop_lamports(&authority.pubkey(), 2_000_000_000) .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 rent_sponsor = program_rent_sponsor(); + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &authority.pubkey(), &Pubkey::find_program_address( @@ -298,12 +296,13 @@ async fn test_compressible_pda_bootstrap() { &solana_sdk::pubkey!("BPFLoaderUpgradeab1e11111111111111111111111"), ) .0, - RENT_SPONSOR, + rent_sponsor, authority.pubkey(), + 500_000_000, // 0.5 SOL for rent sponsor ) .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"); @@ -470,17 +469,13 @@ async fn test_compressible_pda_compression() { 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) + rpc.airdrop_lamports(&authority.pubkey(), 2_000_000_000) .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 rent_sponsor = program_rent_sponsor(); + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &authority.pubkey(), &Pubkey::find_program_address( @@ -488,12 +483,13 @@ async fn test_compressible_pda_compression() { &solana_sdk::pubkey!("BPFLoaderUpgradeab1e11111111111111111111111"), ) .0, - RENT_SPONSOR, + rent_sponsor, authority.pubkey(), + 500_000_000, // 0.5 SOL for rent sponsor ) .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"); @@ -703,20 +699,18 @@ 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) + rpc.airdrop_lamports(&authority.pubkey(), 2_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 rent_sponsor = program_rent_sponsor(); + let (init_config_ixs, config_pda) = InitializeRentFreeConfig::new( &program_id, &authority.pubkey(), &Pubkey::find_program_address( @@ -724,12 +718,13 @@ async fn test_compressible_pda_subscription() { &solana_sdk::pubkey!("BPFLoaderUpgradeab1e11111111111111111111111"), ) .0, - RENT_SPONSOR, + rent_sponsor, authority.pubkey(), + 500_000_000, // 0.5 SOL for rent sponsor ) .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/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 a9145bf828..b161edc410 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 = @@ -20,18 +21,22 @@ 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, } /// 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 +45,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 +64,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 +98,34 @@ 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); + // 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, + &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 @@ -104,6 +140,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, @@ -121,12 +158,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 7815037bc2..76dc0c9149 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 (version 1). + /// Derived via `derive_light_rent_sponsors!` 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-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/lib.rs b/sdk-libs/macros/src/lib.rs index 992ff3b382..f145d8c199 100644 --- a/sdk-libs/macros/src/lib.rs +++ b/sdk-libs/macros/src/lib.rs @@ -339,34 +339,22 @@ 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 the Rent Sponsor PDA at compile time (version 1, hardcoded). /// -/// Seeds: ["rent_sponsor", ] -/// -/// ## Example -/// -/// ```ignore -/// use light_sdk_macros::derive_light_rent_sponsor_pda; -/// -/// 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. -/// -/// Returns ::light_sdk_types::RentSponsor { program_id, rent_sponsor, bump, version }. +/// Returns a `RentSponsor` struct with the PDA address and bump. /// /// ## Example /// /// ```ignore /// use light_sdk_macros::derive_light_rent_sponsor; /// -/// pub const RENT_SPONSOR: ::light_sdk_types::RentSponsor = -/// derive_light_rent_sponsor!("8Ld9pGkCNfU6A7KdKe1YrTNYJWKMCFqVHqmUvjNmER7B", 1); +/// pub const RENT_SPONSOR: ::light_sdk::sdk_types::RentSponsor = +/// derive_light_rent_sponsor!("8Ld9pGkCNfU6A7KdKe1YrTNYJWKMCFqVHqmUvjNmER7B"); +/// +/// // 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_sponsor(input: TokenStream) -> TokenStream { 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/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/macros/src/light_pdas/program/instructions.rs b/sdk-libs/macros/src/light_pdas/program/instructions.rs index 79f2682c7e..1c943098ee 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, @@ -546,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-libs/macros/src/rent_sponsor.rs b/sdk-libs/macros/src/rent_sponsor.rs index d851ca171a..d5c2559a27 100644 --- a/sdk-libs/macros/src/rent_sponsor.rs +++ b/sdk-libs/macros/src/rent_sponsor.rs @@ -1,151 +1,39 @@ 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 the Rent Sponsor PDA at compile time (version 1, hardcoded). /// -/// Seeds: ["rent_sponsor", ] +/// Returns a `RentSponsor` struct with the PDA address and bump. /// /// 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_sponsor; /// -/// Returns `::light_sdk::sdk_types::RentSponsor { program_id, rent_sponsor, bump, version }`. +/// pub const RENT_SPONSOR: ::light_sdk::sdk_types::RentSponsor = +/// derive_light_rent_sponsor!("Program1111111111111111111111111111111111"); /// -/// Usage: -/// const RENT_SPONSOR: ::light_sdk::sdk_types::RentSponsor = -/// derive_light_rent_sponsor!("Program1111111111111111111111111111111111", 1); +/// // 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_sponsor(input: TokenStream) -> TokenStream { - let args = parse_macro_input!(input as Args); + 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 +47,27 @@ 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 + let program_id_literals: Vec<_> = program_id_bytes .iter() - .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)); - let pda_literals = pda_bytes + .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)) + .collect(); + + // 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)); + .map(|b| proc_macro2::Literal::u8_unsuffixed(*b)) + .collect(); - let version_lit = proc_macro2::Literal::u16_unsuffixed(version_u16); let output = quote! { - { - ::light_sdk::sdk_types::RentSponsor { - program_id: [#(#program_id_literals),*], - rent_sponsor: [#(#pda_literals),*], - bump: #bump, - version: #version_lit, - } + ::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 b63eebc639..dc634b2bc6 100644 --- a/sdk-libs/sdk-types/src/lib.rs +++ b/sdk-libs/sdk-types/src/lib.rs @@ -19,10 +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, } 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 fce8b42a5d..a6c4ea06ba 100644 --- a/sdk-libs/sdk/src/lib.rs +++ b/sdk-libs/sdk/src/lib.rs @@ -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_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 14dbceb08a..890797e824 100644 --- a/sdk-libs/sdk/src/utils.rs +++ b/sdk-libs/sdk/src/utils.rs @@ -1,3 +1,4 @@ +use light_sdk_types::RentSponsor; use solana_pubkey::Pubkey; #[allow(unused_imports)] @@ -20,11 +21,18 @@ 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 the rent sponsor PDA for a given program (version 1, hardcoded). /// -/// Seeds: ["rent_sponsor", ] -pub fn derive_rent_sponsor_pda(program_id: &Pubkey, version: u16) -> (Pubkey, u8) { - let version_bytes = version.to_le_bytes(); +/// Seeds: ["rent_sponsor", <1u16 little-endian>] +#[inline] +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) + let (pda, bump) = Pubkey::find_program_address(seeds, program_id); + RentSponsor { + program_id: program_id.to_bytes(), + rent_sponsor: pda.to_bytes(), + bump, + } } diff --git a/sdk-libs/token-sdk/src/anchor.rs b/sdk-libs/token-sdk/src/anchor.rs index d067a5a80c..367b723c05 100644 --- a/sdk-libs/token-sdk/src/anchor.rs +++ b/sdk-libs/token-sdk/src/anchor.rs @@ -21,7 +21,6 @@ pub use light_sdk::{ pub use light_sdk_macros::{ // Proc macros derive_light_rent_sponsor, - derive_light_rent_sponsor_pda, // Attribute macros light_program, // Derive macros 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-sdk/src/lib.rs b/sdk-tests/csdk-anchor-full-derived-test-sdk/src/lib.rs index 6966efb783..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 @@ -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::light_rent_sponsor() + } + fn from_keyed_accounts(accounts: &[AccountInterface]) -> Result { let mut sdk = Self::new(); 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..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,11 +1,11 @@ -#![allow(deprecated)] -#![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; -use light_sdk::{derive_light_cpi_signer, derive_light_rent_sponsor_pda}; +use light_sdk::{derive_light_cpi_signer, derive_light_rent_sponsor}; use light_sdk_macros::light_program; -use light_sdk_types::CpiSigner; +use light_sdk_types::{CpiSigner, RentSponsor}; pub mod amm_test; pub mod d5_markers; @@ -74,13 +74,11 @@ 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); +// 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(PROGRAM_RENT_SPONSOR_DATA.0) -} +// Note: light_rent_sponsor() is auto-generated by #[light_program] macro below pub const GAME_SESSION_SEED: &str = "game_session"; @@ -1463,3 +1461,26 @@ pub enum CsdkTestInstruction { )] CreateMintWithMetadata, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_derive_light_rent_sponsor_macro_matches_runtime() { + let runtime_sponsor = light_sdk::utils::derive_rent_sponsor(&crate::ID); + + 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" + ); + } +} 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..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 @@ -21,14 +21,16 @@ 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, }; -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, +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; @@ -36,8 +38,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,16 +141,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); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + // Construct SDK and use trait method for rent sponsor PDA + let sdk = AmmSdk::new(); + let program_rent_sponsor = sdk.light_rent_sponsor_pda(); + + // 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, - RENT_SPONSOR, + 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"); @@ -348,7 +354,7 @@ async fn test_amm_full_lifecycle() { rent: solana_sdk::sysvar::rent::ID, compression_config: ctx.config_pda, light_token_compressible_config: COMPRESSIBLE_CONFIG_V1, - rent_sponsor: LIGHT_TOKEN_RENT_SPONSOR, + rent_sponsor: RENT_SPONSOR_V1, light_token_program: LIGHT_TOKEN_PROGRAM_ID, light_token_cpi_authority: LIGHT_TOKEN_CPI_AUTHORITY, }; @@ -590,8 +596,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 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..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; @@ -22,13 +24,14 @@ 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, - }; - use light_token::instruction::{ - get_associated_token_address_and_bump, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR, + light_rent_sponsor, FullAutoWithMintParams, GameSession, }; + 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()); @@ -81,16 +84,18 @@ 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( + // 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, ) .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"); @@ -179,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: 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, @@ -514,13 +519,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 light_token::instruction::{ - find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR, + use csdk_anchor_full_derived_test::{ + instruction_accounts::{CreateTwoMintsParams, MINT_SIGNER_A_SEED, MINT_SIGNER_B_SEED}, + light_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, @@ -537,16 +542,18 @@ 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( + // 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, ) .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"); @@ -596,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: 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, @@ -714,13 +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 light_token::instruction::{ - find_mint_address as find_cmint_address, COMPRESSIBLE_CONFIG_V1, RENT_SPONSOR, + use csdk_anchor_full_derived_test::{ + instruction_accounts::{ + CreateThreeMintsParams, MINT_SIGNER_A_SEED, MINT_SIGNER_B_SEED, MINT_SIGNER_C_SEED, + }, + light_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, @@ -733,16 +742,18 @@ 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( + // 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, ) .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"); @@ -790,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: 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, @@ -873,7 +884,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( @@ -887,16 +898,18 @@ 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( + // 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, ) .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..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 @@ -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::{light_token_config_pda, light_token_rent_sponsor_pda}; 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,16 +47,18 @@ impl D10TestContext { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + // 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, ) .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"); @@ -94,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 @@ -116,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: 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, @@ -153,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 @@ -175,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: 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 7272f4d4a0..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,25 +8,25 @@ 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, }; 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; use solana_signer::Signer; -const RENT_SPONSOR: Pubkey = pubkey!("CLEuMG7pzJX9xAuKCFzBP154uiG1GaNo4Fq7x6KAcAfG"); - /// Test context shared across instruction tests #[allow(dead_code)] struct TestContext { @@ -50,16 +50,18 @@ impl TestContext { let program_data_pda = setup_mock_program_data(&mut rpc, &payer, &program_id); - let (init_config_ix, config_pda) = InitializeRentFreeConfig::new( + // 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, ) .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"); @@ -1463,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 @@ -1486,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, @@ -1528,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(); @@ -1560,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, @@ -1612,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 @@ -1638,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, @@ -1679,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(); @@ -1711,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 c614beb673..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 @@ -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, @@ -11,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; @@ -25,10 +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, - }; + 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, @@ -41,16 +43,18 @@ 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( + // 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, ) .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"); @@ -95,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: 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 7de9f960e5..0475a09976 100644 --- a/sdk-tests/single-ata-test/tests/test.rs +++ b/sdk-tests/single-ata-test/tests/test.rs @@ -7,12 +7,19 @@ 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,16 +99,18 @@ 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( + // 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, ) .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"); @@ -132,7 +141,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 fb289a6e15..ccc9dd5a6c 100644 --- a/sdk-tests/single-mint-test/tests/test.rs +++ b/sdk-tests/single-mint-test/tests/test.rs @@ -9,12 +9,21 @@ 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,16 +39,18 @@ 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( + // 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, ) .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"); @@ -70,7 +81,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 b0bf6c32bc..57cd289286 100644 --- a/sdk-tests/single-pda-test/tests/test.rs +++ b/sdk-tests/single-pda-test/tests/test.rs @@ -8,12 +8,18 @@ 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,16 +35,18 @@ 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( + // 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, ) .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..1ef97fae8a 100644 --- a/sdk-tests/single-token-test/tests/test.rs +++ b/sdk-tests/single-token-test/tests/test.rs @@ -7,12 +7,19 @@ 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,16 +99,18 @@ 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( + // 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, ) .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"); @@ -132,7 +141,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,