diff --git a/program-tests/compressed-token-test/tests/light_token/burn.rs b/program-tests/compressed-token-test/tests/light_token/burn.rs index fb8efce92e..e0bf011c6b 100644 --- a/program-tests/compressed-token-test/tests/light_token/burn.rs +++ b/program-tests/compressed-token-test/tests/light_token/burn.rs @@ -48,6 +48,7 @@ async fn test_burn_success_cases() { amount: burn_amount, authority: ctx.owner_keypair.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -79,6 +80,7 @@ async fn test_burn_success_cases() { amount: burn_amount, authority: ctx.owner_keypair.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -129,6 +131,7 @@ async fn test_burn_fails() { amount: 50, authority: ctx.owner_keypair.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -159,6 +162,7 @@ async fn test_burn_fails() { amount: 50, authority: ctx.owner_keypair.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -205,6 +209,7 @@ async fn test_burn_fails() { amount: 50, authority: ctx.owner_keypair.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -235,6 +240,7 @@ async fn test_burn_fails() { amount: 200, // More than 100 balance authority: ctx.owner_keypair.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -270,6 +276,7 @@ async fn test_burn_fails() { amount: 50, authority: wrong_authority.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -371,6 +378,7 @@ async fn setup_burn_test() -> BurnTestContext { amount: 100, authority: mint_authority.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -416,6 +424,7 @@ async fn test_burn_checked_success() { decimals: 8, // Correct decimals authority: ctx.owner_keypair.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -448,6 +457,7 @@ async fn test_burn_checked_wrong_decimals() { decimals: 7, // Wrong decimals authority: ctx.owner_keypair.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); diff --git a/program-tests/compressed-token-test/tests/light_token/extensions_failing.rs b/program-tests/compressed-token-test/tests/light_token/extensions_failing.rs index a8b4cb1fb8..bb3be01280 100644 --- a/program-tests/compressed-token-test/tests/light_token/extensions_failing.rs +++ b/program-tests/compressed-token-test/tests/light_token/extensions_failing.rs @@ -196,6 +196,7 @@ async fn test_ctoken_transfer_fails_when_mint_paused() { decimals: 9, authority: owner.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -243,6 +244,7 @@ async fn test_ctoken_transfer_fails_with_non_zero_transfer_fee() { decimals: 9, authority: owner.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -291,6 +293,7 @@ async fn test_ctoken_transfer_fails_with_non_nil_transfer_hook() { decimals: 9, authority: owner.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); diff --git a/program-tests/compressed-token-test/tests/light_token/transfer.rs b/program-tests/compressed-token-test/tests/light_token/transfer.rs index 47727c17ba..d2c1499414 100644 --- a/program-tests/compressed-token-test/tests/light_token/transfer.rs +++ b/program-tests/compressed-token-test/tests/light_token/transfer.rs @@ -728,6 +728,7 @@ async fn transfer_checked_and_assert( decimals, authority: authority.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -769,6 +770,7 @@ async fn transfer_checked_and_assert_fails( decimals, authority: authority.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -941,6 +943,7 @@ async fn test_ctoken_transfer_checked_max_top_up_exceeded() { decimals: 9, authority: owner_keypair.pubkey(), max_top_up: Some(1), + fee_payer: None, } .instruction() .unwrap(); diff --git a/program-tests/compressed-token-test/tests/light_token/transfer_checked.rs b/program-tests/compressed-token-test/tests/light_token/transfer_checked.rs index 84e60bc24c..99d4386024 100644 --- a/program-tests/compressed-token-test/tests/light_token/transfer_checked.rs +++ b/program-tests/compressed-token-test/tests/light_token/transfer_checked.rs @@ -165,6 +165,7 @@ async fn test_transfer_requires_checked_for_restricted_extensions() { amount: transfer_amount, authority: owner.pubkey(), max_top_up: Some(0), // 0 = no limit, but includes system program for compressible + fee_payer: None, } .instruction() .unwrap(); @@ -188,6 +189,7 @@ async fn test_transfer_requires_checked_for_restricted_extensions() { decimals: 9, authority: owner.pubkey(), max_top_up: Some(0), // 0 = no limit, but includes system program for compressible + fee_payer: None, } .instruction() .unwrap(); diff --git a/program-tests/compressed-token-test/tests/mint/burn.rs b/program-tests/compressed-token-test/tests/mint/burn.rs index f3dba6224f..f4d7525150 100644 --- a/program-tests/compressed-token-test/tests/mint/burn.rs +++ b/program-tests/compressed-token-test/tests/mint/burn.rs @@ -100,6 +100,7 @@ async fn test_ctoken_burn() { amount: 500, authority: ctx.owner_keypair.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -122,6 +123,7 @@ async fn test_ctoken_burn() { amount: 500, authority: ctx.owner_keypair.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); diff --git a/program-tests/compressed-token-test/tests/mint/mint_to.rs b/program-tests/compressed-token-test/tests/mint/mint_to.rs index 9af8c5296a..c4f90404bc 100644 --- a/program-tests/compressed-token-test/tests/mint/mint_to.rs +++ b/program-tests/compressed-token-test/tests/mint/mint_to.rs @@ -95,6 +95,7 @@ async fn test_ctoken_mint_to() { amount: 500, authority: ctx.mint_authority.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -117,6 +118,7 @@ async fn test_ctoken_mint_to() { amount: 500, authority: ctx.mint_authority.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -168,6 +170,7 @@ async fn test_ctoken_mint_to_checked_success() { decimals: 8, // Correct decimals authority: ctx.mint_authority.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -210,6 +213,7 @@ async fn test_ctoken_mint_to_checked_wrong_decimals() { decimals: 7, // Wrong decimals authority: ctx.mint_authority.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); diff --git a/program-tests/registry-test/tests/compressible.rs b/program-tests/registry-test/tests/compressible.rs index 7bb35f33ae..7104bad345 100644 --- a/program-tests/registry-test/tests/compressible.rs +++ b/program-tests/registry-test/tests/compressible.rs @@ -1247,6 +1247,7 @@ async fn mint_to_token( amount, authority: mint_authority.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .map_err(|e| RpcError::CustomError(format!("Failed to create MintTo instruction: {:?}", e)))?; diff --git a/programs/compressed-token/program/docs/ctoken/BURN.md b/programs/compressed-token/program/docs/ctoken/BURN.md index 57565bf692..1e3c0846c9 100644 --- a/programs/compressed-token/program/docs/ctoken/BURN.md +++ b/programs/compressed-token/program/docs/ctoken/BURN.md @@ -35,7 +35,14 @@ Format 2 (10 bytes): - (signer) - Owner of the source CToken account - Must sign the transaction - - Also serves as payer for rent top-ups if needed + - If no fee_payer provided: also serves as payer for top-ups (must be writable) + - If fee_payer provided: readonly (only needs to sign) + +4. fee_payer (optional) + - (signer, writable) + - Optional separate account to pay for rent top-ups + - If not provided, authority account pays for top-ups + - Must have sufficient lamports to cover the top-up amount **Instruction Logic and Checks:** diff --git a/programs/compressed-token/program/docs/ctoken/BURN_CHECKED.md b/programs/compressed-token/program/docs/ctoken/BURN_CHECKED.md index 7b1fda2e22..b1919c10c9 100644 --- a/programs/compressed-token/program/docs/ctoken/BURN_CHECKED.md +++ b/programs/compressed-token/program/docs/ctoken/BURN_CHECKED.md @@ -38,7 +38,14 @@ Format 2 (11 bytes): - (signer) - Owner of the source CToken account - Must sign the transaction - - Also serves as payer for rent top-ups if needed + - If no fee_payer provided: also serves as payer for top-ups (must be writable) + - If fee_payer provided: readonly (only needs to sign) + +4. fee_payer (optional) + - (signer, writable) + - Optional separate account to pay for rent top-ups + - If not provided, authority account pays for top-ups + - Must have sufficient lamports to cover the top-up amount **Instruction Logic and Checks:** diff --git a/programs/compressed-token/program/docs/ctoken/MINT_TO.md b/programs/compressed-token/program/docs/ctoken/MINT_TO.md index eb55eca502..2fa45c1a69 100644 --- a/programs/compressed-token/program/docs/ctoken/MINT_TO.md +++ b/programs/compressed-token/program/docs/ctoken/MINT_TO.md @@ -39,10 +39,17 @@ Format variants: - May receive rent top-up if compressible 3. authority - - (signer, writable when top-ups needed) + - (signer) - Mint authority of the CMint account - Validated: must sign the transaction - - Also serves as payer for rent top-ups if needed + - If no fee_payer provided: also serves as payer for top-ups (must be writable) + - If fee_payer provided: readonly (only needs to sign) + +4. fee_payer (optional) + - (signer, writable) + - Optional separate account to pay for rent top-ups + - If not provided, authority account pays for top-ups + - Must have sufficient lamports to cover the top-up amount **Instruction Logic and Checks:** diff --git a/programs/compressed-token/program/docs/ctoken/MINT_TO_CHECKED.md b/programs/compressed-token/program/docs/ctoken/MINT_TO_CHECKED.md index 5f78c7eaca..60c8c7d32a 100644 --- a/programs/compressed-token/program/docs/ctoken/MINT_TO_CHECKED.md +++ b/programs/compressed-token/program/docs/ctoken/MINT_TO_CHECKED.md @@ -42,10 +42,17 @@ Format variants: - May receive rent top-up if compressible 3. authority - - (signer, writable when top-ups needed) + - (signer) - Mint authority of the CMint account - Validated: must sign the transaction - - Also serves as payer for rent top-ups if needed + - If no fee_payer provided: also serves as payer for top-ups (must be writable) + - If fee_payer provided: readonly (only needs to sign) + +4. fee_payer (optional) + - (signer, writable) + - Optional separate account to pay for rent top-ups + - If not provided, authority account pays for top-ups + - Must have sufficient lamports to cover the top-up amount **Instruction Logic and Checks:** diff --git a/programs/compressed-token/program/docs/ctoken/TRANSFER.md b/programs/compressed-token/program/docs/ctoken/TRANSFER.md index d65ef76763..55a29f01f4 100644 --- a/programs/compressed-token/program/docs/ctoken/TRANSFER.md +++ b/programs/compressed-token/program/docs/ctoken/TRANSFER.md @@ -51,8 +51,14 @@ After discriminator byte, the following formats are supported: - (signer) - Owner of the source account or delegate with sufficient allowance - Must sign the transaction - -Note: The authority account (index 2) also serves as the payer for top-ups when accounts have compressible extension. + - If no fee_payer provided: also serves as payer for top-ups (must be writable) + - If fee_payer provided: readonly (only needs to sign) + +4. fee_payer (optional) + - (signer, writable) + - Optional separate account to pay for rent top-ups + - If not provided, authority account pays for top-ups + - Must have sufficient lamports to cover the top-up amount **Instruction Logic and Checks:** diff --git a/programs/compressed-token/program/docs/ctoken/TRANSFER_CHECKED.md b/programs/compressed-token/program/docs/ctoken/TRANSFER_CHECKED.md index 2f1d110c4e..a890cbfdf0 100644 --- a/programs/compressed-token/program/docs/ctoken/TRANSFER_CHECKED.md +++ b/programs/compressed-token/program/docs/ctoken/TRANSFER_CHECKED.md @@ -53,7 +53,14 @@ Transfers tokens between decompressed ctoken solana accounts with mint decimals - Owner of the source account or delegate with sufficient allowance - Must sign the transaction - If is permanent delegate, validated as signer and pinocchio validation is skipped - - Also serves as payer for top-ups when accounts have compressible extension + - If no fee_payer provided: also serves as payer for top-ups (must be writable) + - If fee_payer provided: readonly (only needs to sign) + +5. fee_payer (optional) + - (signer, writable) + - Optional separate account to pay for rent top-ups + - If not provided, authority account pays for top-ups + - Must have sufficient lamports to cover the top-up amount **Instruction Logic and Checks:** diff --git a/programs/compressed-token/program/src/ctoken/burn.rs b/programs/compressed-token/program/src/ctoken/burn.rs index 40a30dccdb..acd5f4358c 100644 --- a/programs/compressed-token/program/src/ctoken/burn.rs +++ b/programs/compressed-token/program/src/ctoken/burn.rs @@ -13,9 +13,13 @@ pub(crate) type ProcessorFn = fn(&[AccountInfo], &[u8]) -> Result<(), PinocchioP pub(crate) const BASE_LEN_UNCHECKED: usize = 8; pub(crate) const BASE_LEN_CHECKED: usize = 9; -/// Burn account indices: [ctoken=0, cmint=1, authority=2] +/// Burn account indices: [ctoken=0, cmint=1, authority=2, system_program=3, fee_payer=4 (optional)] const BURN_CMINT_IDX: usize = 1; const BURN_CTOKEN_IDX: usize = 0; +const PAYER_IDX: usize = 2; +#[allow(dead_code)] +const SYSTEM_PROGRAM_IDX: usize = 3; +const FEE_PAYER_IDX: usize = 4; /// Process ctoken burn instruction /// @@ -26,7 +30,9 @@ const BURN_CTOKEN_IDX: usize = 0; /// Account layout: /// 0: source CToken account (writable) /// 1: CMint account (writable) -/// 2: authority (signer, also payer for top-ups) +/// 2: authority (signer, readonly if fee_payer provided, writable otherwise) +/// 3: system_program (readonly) - required for rent top-up CPIs +/// 4: fee_payer (optional, signer, writable) - pays for top-ups instead of authority #[profile] #[inline(always)] pub fn process_ctoken_burn( @@ -49,7 +55,9 @@ pub fn process_ctoken_burn( /// Account layout (same as burn): /// 0: source CToken account (writable) /// 1: CMint account (writable) -/// 2: authority (signer, also payer for top-ups) +/// 2: authority (signer, readonly if fee_payer provided, writable otherwise) +/// 3: system_program (readonly) - required for rent top-up CPIs +/// 4: fee_payer (optional, signer, writable) - pays for top-ups instead of authority #[profile] #[inline(always)] pub fn process_ctoken_burn_checked( @@ -112,7 +120,10 @@ pub(crate) fn process_ctoken_supply_change_inner< // SAFETY: accounts.len() >= 3 validated at function entry let cmint = &accounts[CMINT_IDX]; let ctoken = &accounts[CTOKEN_IDX]; - let payer = accounts.get(2); + // Use fee_payer if provided, otherwise fall back to authority + let authority_payer = accounts.get(PAYER_IDX); + let fee_payer = accounts.get(FEE_PAYER_IDX); + let effective_payer = fee_payer.or(authority_payer); - calculate_and_execute_compressible_top_ups(cmint, ctoken, payer, max_top_up) + calculate_and_execute_compressible_top_ups(cmint, ctoken, effective_payer, max_top_up) } diff --git a/programs/compressed-token/program/src/ctoken/mint_to.rs b/programs/compressed-token/program/src/ctoken/mint_to.rs index 2a552e5483..0b98e194a4 100644 --- a/programs/compressed-token/program/src/ctoken/mint_to.rs +++ b/programs/compressed-token/program/src/ctoken/mint_to.rs @@ -20,7 +20,9 @@ pub(crate) const MINT_CTOKEN_IDX: usize = 1; /// Account layout: /// 0: CMint account (writable) /// 1: destination CToken account (writable) -/// 2: authority (signer, also payer for top-ups) +/// 2: authority (signer, readonly if fee_payer provided, writable otherwise) +/// 3: system_program (readonly) - required for rent top-up CPIs +/// 4: fee_payer (optional, signer, writable) - pays for top-ups instead of authority #[profile] #[inline(always)] pub fn process_ctoken_mint_to( @@ -43,7 +45,9 @@ pub fn process_ctoken_mint_to( /// Account layout (same as mint_to): /// 0: CMint account (writable) /// 1: destination CToken account (writable) -/// 2: authority (signer, also payer for top-ups) +/// 2: authority (signer, readonly if fee_payer provided, writable otherwise) +/// 3: system_program (readonly) - required for rent top-up CPIs +/// 4: fee_payer (optional, signer, writable) - pays for top-ups instead of authority #[profile] #[inline(always)] pub fn process_ctoken_mint_to_checked( diff --git a/programs/compressed-token/program/src/ctoken/transfer/checked.rs b/programs/compressed-token/program/src/ctoken/transfer/checked.rs index 3db6d122cd..eed8ccacc7 100644 --- a/programs/compressed-token/program/src/ctoken/transfer/checked.rs +++ b/programs/compressed-token/program/src/ctoken/transfer/checked.rs @@ -16,6 +16,9 @@ const ACCOUNT_SOURCE: usize = 0; const ACCOUNT_MINT: usize = 1; const ACCOUNT_DESTINATION: usize = 2; const ACCOUNT_AUTHORITY: usize = 3; +#[allow(dead_code)] +const ACCOUNT_SYSTEM_PROGRAM: usize = 4; +const ACCOUNT_FEE_PAYER: usize = 5; /// Process ctoken transfer_checked instruction /// @@ -47,7 +50,8 @@ pub fn process_ctoken_transfer_checked( // Hot path: 165-byte accounts have no extensions, skip all extension processing if source.data_len() == 165 && destination.data_len() == 165 { - return process_transfer_checked(accounts, &instruction_data[..9], false) + // Slice to exactly 4 accounts: [source, mint, destination, authority] + return process_transfer_checked(&accounts[..4], &instruction_data[..9], false) .map_err(convert_pinocchio_token_error); } @@ -66,12 +70,14 @@ pub fn process_ctoken_transfer_checked( _ => return Err(ProgramError::InvalidInstructionData), }; + let fee_payer = accounts.get(ACCOUNT_FEE_PAYER); let (signer_is_validated, extension_decimals) = process_transfer_extensions_transfer_checked( TransferAccounts { source, destination, authority, mint: Some(mint), + fee_payer, }, max_top_up, )?; @@ -97,7 +103,8 @@ pub fn process_ctoken_transfer_checked( .map_err(convert_pinocchio_token_error) } else { check_token_program_owner(mint)?; - process_transfer(accounts, amount, Some(decimals), signer_is_validated) + // Slice to exactly 4 accounts: [source, mint, destination, authority] + process_transfer(&accounts[..4], amount, Some(decimals), signer_is_validated) .map_err(convert_pinocchio_token_error) } } diff --git a/programs/compressed-token/program/src/ctoken/transfer/default.rs b/programs/compressed-token/program/src/ctoken/transfer/default.rs index 09bb0a8954..488f0ed8e6 100644 --- a/programs/compressed-token/program/src/ctoken/transfer/default.rs +++ b/programs/compressed-token/program/src/ctoken/transfer/default.rs @@ -10,6 +10,9 @@ use crate::shared::convert_pinocchio_token_error; const ACCOUNT_SOURCE: usize = 0; const ACCOUNT_DESTINATION: usize = 1; const ACCOUNT_AUTHORITY: usize = 2; +#[allow(dead_code)] +const ACCOUNT_SYSTEM_PROGRAM: usize = 3; +const ACCOUNT_FEE_PAYER: usize = 4; /// Process ctoken transfer instruction /// @@ -40,7 +43,8 @@ pub fn process_ctoken_transfer( let source = &accounts[ACCOUNT_SOURCE]; let destination = &accounts[ACCOUNT_DESTINATION]; if source.data_len() == 165 && destination.data_len() == 165 { - return process_transfer(accounts, &instruction_data[..8], false) + // Slice to exactly 3 accounts: [source, destination, authority] + return process_transfer(&accounts[..3], &instruction_data[..8], false) .map_err(convert_pinocchio_token_error); } @@ -59,7 +63,8 @@ pub fn process_ctoken_transfer( let signer_is_validated = process_extensions(accounts, max_top_up)?; // Only pass the first 8 bytes (amount) to the SPL transfer processor - process_transfer(accounts, &instruction_data[..8], signer_is_validated) + // Slice to exactly 3 accounts: [source, destination, authority] + process_transfer(&accounts[..3], &instruction_data[..8], signer_is_validated) .map_err(convert_pinocchio_token_error) } @@ -68,6 +73,7 @@ fn process_extensions(accounts: &[AccountInfo], max_top_up: u16) -> Result Result { pub destination: &'a AccountInfo, pub authority: &'a AccountInfo, pub mint: Option<&'a AccountInfo>, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option<&'a AccountInfo>, } /// Process transfer extensions for CTokenTransfer instruction. @@ -142,8 +144,11 @@ fn transfer_top_up( amount: recipient_top_up, }, ]; - multi_transfer_lamports(transfer_accounts.authority, &transfers) - .map_err(convert_program_error) + // Use fee_payer if provided, otherwise fall back to authority + let payer = transfer_accounts + .fee_payer + .unwrap_or(transfer_accounts.authority); + multi_transfer_lamports(payer, &transfers).map_err(convert_program_error) } else { Ok(()) } diff --git a/sdk-libs/token-sdk/src/instruction/burn.rs b/sdk-libs/token-sdk/src/instruction/burn.rs index 1c99355ad1..cabcc98644 100644 --- a/sdk-libs/token-sdk/src/instruction/burn.rs +++ b/sdk-libs/token-sdk/src/instruction/burn.rs @@ -18,6 +18,7 @@ use solana_pubkey::Pubkey; /// amount: 100, /// authority, /// max_top_up: None, +/// fee_payer: None, /// }.instruction()?; /// # Ok::<(), solana_program_error::ProgramError>(()) /// ``` @@ -33,6 +34,8 @@ pub struct Burn { /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) /// When set to a non-zero value, includes max_top_up in instruction data pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option, } /// # Burn ctoken via CPI: @@ -42,12 +45,15 @@ pub struct Burn { /// # let source: AccountInfo = todo!(); /// # let mint: AccountInfo = todo!(); /// # let authority: AccountInfo = todo!(); +/// # let system_program: AccountInfo = todo!(); /// BurnCpi { /// source, /// mint, /// amount: 100, /// authority, +/// system_program, /// max_top_up: None, +/// fee_payer: None, /// } /// .invoke()?; /// # Ok::<(), solana_program_error::ProgramError>(()) @@ -57,8 +63,11 @@ pub struct BurnCpi<'info> { pub mint: AccountInfo<'info>, pub amount: u64, pub authority: AccountInfo<'info>, + pub system_program: AccountInfo<'info>, /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option>, } impl<'info> BurnCpi<'info> { @@ -68,14 +77,36 @@ impl<'info> BurnCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = Burn::from(&self).instruction()?; - let account_infos = [self.source, self.mint, self.authority]; - invoke(&instruction, &account_infos) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.source, + self.mint, + self.authority, + self.system_program, + fee_payer, + ]; + invoke(&instruction, &account_infos) + } else { + let account_infos = [self.source, self.mint, self.authority, self.system_program]; + invoke(&instruction, &account_infos) + } } pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = Burn::from(&self).instruction()?; - let account_infos = [self.source, self.mint, self.authority]; - invoke_signed(&instruction, &account_infos, signer_seeds) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.source, + self.mint, + self.authority, + self.system_program, + fee_payer, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } else { + let account_infos = [self.source, self.mint, self.authority, self.system_program]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } } } @@ -87,19 +118,37 @@ impl<'info> From<&BurnCpi<'info>> for Burn { amount: cpi.amount, authority: *cpi.authority.key, max_top_up: cpi.max_top_up, + fee_payer: cpi.fee_payer.as_ref().map(|a| *a.key), } } } impl Burn { pub fn instruction(self) -> Result { + // Authority is writable only when max_top_up is set AND no fee_payer + // (authority pays for top-ups only if no separate fee_payer) + let authority_meta = if self.max_top_up.is_some() && self.fee_payer.is_none() { + AccountMeta::new(self.authority, true) + } else { + AccountMeta::new_readonly(self.authority, true) + }; + + let mut accounts = vec![ + AccountMeta::new(self.source, false), + AccountMeta::new(self.mint, false), + authority_meta, + // System program required for rent top-up CPIs + AccountMeta::new_readonly(Pubkey::default(), false), + ]; + + // Add fee_payer if provided (must be signer and writable) + if let Some(fee_payer) = self.fee_payer { + accounts.push(AccountMeta::new(fee_payer, true)); + } + Ok(Instruction { program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID), - accounts: vec![ - AccountMeta::new(self.source, false), - AccountMeta::new(self.mint, false), - AccountMeta::new_readonly(self.authority, true), - ], + accounts, data: { let mut data = vec![8u8]; // CTokenBurn discriminator data.extend_from_slice(&self.amount.to_le_bytes()); diff --git a/sdk-libs/token-sdk/src/instruction/burn_checked.rs b/sdk-libs/token-sdk/src/instruction/burn_checked.rs index 1c6ca045ba..ef21ab0a72 100644 --- a/sdk-libs/token-sdk/src/instruction/burn_checked.rs +++ b/sdk-libs/token-sdk/src/instruction/burn_checked.rs @@ -19,6 +19,7 @@ use solana_pubkey::Pubkey; /// decimals: 8, /// authority, /// max_top_up: None, +/// fee_payer: None, /// }.instruction()?; /// # Ok::<(), solana_program_error::ProgramError>(()) /// ``` @@ -36,6 +37,8 @@ pub struct BurnChecked { /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) /// When set to a non-zero value, includes max_top_up in instruction data pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option, } /// # Burn ctoken via CPI with decimals validation: @@ -45,13 +48,16 @@ pub struct BurnChecked { /// # let source: AccountInfo = todo!(); /// # let mint: AccountInfo = todo!(); /// # let authority: AccountInfo = todo!(); +/// # let system_program: AccountInfo = todo!(); /// BurnCheckedCpi { /// source, /// mint, /// amount: 100, /// decimals: 8, /// authority, +/// system_program, /// max_top_up: None, +/// fee_payer: None, /// } /// .invoke()?; /// # Ok::<(), solana_program_error::ProgramError>(()) @@ -62,8 +68,11 @@ pub struct BurnCheckedCpi<'info> { pub amount: u64, pub decimals: u8, pub authority: AccountInfo<'info>, + pub system_program: AccountInfo<'info>, /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option>, } impl<'info> BurnCheckedCpi<'info> { @@ -73,14 +82,36 @@ impl<'info> BurnCheckedCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = BurnChecked::from(&self).instruction()?; - let account_infos = [self.source, self.mint, self.authority]; - invoke(&instruction, &account_infos) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.source, + self.mint, + self.authority, + self.system_program, + fee_payer, + ]; + invoke(&instruction, &account_infos) + } else { + let account_infos = [self.source, self.mint, self.authority, self.system_program]; + invoke(&instruction, &account_infos) + } } pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = BurnChecked::from(&self).instruction()?; - let account_infos = [self.source, self.mint, self.authority]; - invoke_signed(&instruction, &account_infos, signer_seeds) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.source, + self.mint, + self.authority, + self.system_program, + fee_payer, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } else { + let account_infos = [self.source, self.mint, self.authority, self.system_program]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } } } @@ -93,19 +124,37 @@ impl<'info> From<&BurnCheckedCpi<'info>> for BurnChecked { decimals: cpi.decimals, authority: *cpi.authority.key, max_top_up: cpi.max_top_up, + fee_payer: cpi.fee_payer.as_ref().map(|a| *a.key), } } } impl BurnChecked { pub fn instruction(self) -> Result { + // Authority is writable only when max_top_up is set AND no fee_payer + // (authority pays for top-ups only if no separate fee_payer) + let authority_meta = if self.max_top_up.is_some() && self.fee_payer.is_none() { + AccountMeta::new(self.authority, true) + } else { + AccountMeta::new_readonly(self.authority, true) + }; + + let mut accounts = vec![ + AccountMeta::new(self.source, false), + AccountMeta::new(self.mint, false), + authority_meta, + // System program required for rent top-up CPIs + AccountMeta::new_readonly(Pubkey::default(), false), + ]; + + // Add fee_payer if provided (must be signer and writable) + if let Some(fee_payer) = self.fee_payer { + accounts.push(AccountMeta::new(fee_payer, true)); + } + Ok(Instruction { program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID), - accounts: vec![ - AccountMeta::new(self.source, false), - AccountMeta::new(self.mint, false), - AccountMeta::new_readonly(self.authority, true), - ], + accounts, data: { let mut data = vec![15u8]; // CTokenBurnChecked discriminator data.extend_from_slice(&self.amount.to_le_bytes()); diff --git a/sdk-libs/token-sdk/src/instruction/mint_to.rs b/sdk-libs/token-sdk/src/instruction/mint_to.rs index 4f5c4f02a3..a882be6751 100644 --- a/sdk-libs/token-sdk/src/instruction/mint_to.rs +++ b/sdk-libs/token-sdk/src/instruction/mint_to.rs @@ -18,6 +18,7 @@ use solana_pubkey::Pubkey; /// amount: 100, /// authority, /// max_top_up: None, +/// fee_payer: None, /// }.instruction()?; /// # Ok::<(), solana_program_error::ProgramError>(()) /// ``` @@ -33,6 +34,8 @@ pub struct MintTo { /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) /// When set to a non-zero value, includes max_top_up in instruction data pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option, } /// # Mint to ctoken via CPI: @@ -50,6 +53,7 @@ pub struct MintTo { /// authority, /// system_program, /// max_top_up: None, +/// fee_payer: None, /// } /// .invoke()?; /// # Ok::<(), solana_program_error::ProgramError>(()) @@ -62,6 +66,8 @@ pub struct MintToCpi<'info> { pub system_program: AccountInfo<'info>, /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option>, } impl<'info> MintToCpi<'info> { @@ -71,24 +77,46 @@ impl<'info> MintToCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = MintTo::from(&self).instruction()?; - let account_infos = [ - self.mint, - self.destination, - self.authority, - self.system_program, - ]; - invoke(&instruction, &account_infos) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.mint, + self.destination, + self.authority, + self.system_program, + fee_payer, + ]; + invoke(&instruction, &account_infos) + } else { + let account_infos = [ + self.mint, + self.destination, + self.authority, + self.system_program, + ]; + invoke(&instruction, &account_infos) + } } pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = MintTo::from(&self).instruction()?; - let account_infos = [ - self.mint, - self.destination, - self.authority, - self.system_program, - ]; - invoke_signed(&instruction, &account_infos, signer_seeds) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.mint, + self.destination, + self.authority, + self.system_program, + fee_payer, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } else { + let account_infos = [ + self.mint, + self.destination, + self.authority, + self.system_program, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } } } @@ -100,20 +128,37 @@ impl<'info> From<&MintToCpi<'info>> for MintTo { amount: cpi.amount, authority: *cpi.authority.key, max_top_up: cpi.max_top_up, + fee_payer: cpi.fee_payer.as_ref().map(|a| *a.key), } } } impl MintTo { pub fn instruction(self) -> Result { + // Authority is writable only when max_top_up is set AND no fee_payer + // (authority pays for top-ups only if no separate fee_payer) + let authority_meta = if self.max_top_up.is_some() && self.fee_payer.is_none() { + AccountMeta::new(self.authority, true) + } else { + AccountMeta::new_readonly(self.authority, true) + }; + + let mut accounts = vec![ + AccountMeta::new(self.mint, false), + AccountMeta::new(self.destination, false), + authority_meta, + // System program required for rent top-up CPIs + AccountMeta::new_readonly(Pubkey::default(), false), + ]; + + // Add fee_payer if provided (must be signer and writable) + if let Some(fee_payer) = self.fee_payer { + accounts.push(AccountMeta::new(fee_payer, true)); + } + Ok(Instruction { program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID), - accounts: vec![ - AccountMeta::new(self.mint, false), - AccountMeta::new(self.destination, false), - AccountMeta::new(self.authority, true), - AccountMeta::new_readonly(Pubkey::default(), false), // System program for lamport transfers - ], + accounts, data: { let mut data = vec![7u8]; // MintTo discriminator data.extend_from_slice(&self.amount.to_le_bytes()); diff --git a/sdk-libs/token-sdk/src/instruction/mint_to_checked.rs b/sdk-libs/token-sdk/src/instruction/mint_to_checked.rs index 71730b3cf6..021cfb55da 100644 --- a/sdk-libs/token-sdk/src/instruction/mint_to_checked.rs +++ b/sdk-libs/token-sdk/src/instruction/mint_to_checked.rs @@ -19,6 +19,7 @@ use solana_pubkey::Pubkey; /// decimals: 8, /// authority, /// max_top_up: None, +/// fee_payer: None, /// }.instruction()?; /// # Ok::<(), solana_program_error::ProgramError>(()) /// ``` @@ -36,6 +37,8 @@ pub struct MintToChecked { /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) /// When set to a non-zero value, includes max_top_up in instruction data pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option, } /// # Mint to ctoken via CPI with decimals validation: @@ -45,13 +48,16 @@ pub struct MintToChecked { /// # let mint: AccountInfo = todo!(); /// # let destination: AccountInfo = todo!(); /// # let authority: AccountInfo = todo!(); +/// # let system_program: AccountInfo = todo!(); /// MintToCheckedCpi { /// mint, /// destination, /// amount: 100, /// decimals: 8, /// authority, +/// system_program, /// max_top_up: None, +/// fee_payer: None, /// } /// .invoke()?; /// # Ok::<(), solana_program_error::ProgramError>(()) @@ -62,8 +68,11 @@ pub struct MintToCheckedCpi<'info> { pub amount: u64, pub decimals: u8, pub authority: AccountInfo<'info>, + pub system_program: AccountInfo<'info>, /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option>, } impl<'info> MintToCheckedCpi<'info> { @@ -73,14 +82,46 @@ impl<'info> MintToCheckedCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = MintToChecked::from(&self).instruction()?; - let account_infos = [self.mint, self.destination, self.authority]; - invoke(&instruction, &account_infos) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.mint, + self.destination, + self.authority, + self.system_program, + fee_payer, + ]; + invoke(&instruction, &account_infos) + } else { + let account_infos = [ + self.mint, + self.destination, + self.authority, + self.system_program, + ]; + invoke(&instruction, &account_infos) + } } pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = MintToChecked::from(&self).instruction()?; - let account_infos = [self.mint, self.destination, self.authority]; - invoke_signed(&instruction, &account_infos, signer_seeds) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.mint, + self.destination, + self.authority, + self.system_program, + fee_payer, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } else { + let account_infos = [ + self.mint, + self.destination, + self.authority, + self.system_program, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } } } @@ -93,19 +134,37 @@ impl<'info> From<&MintToCheckedCpi<'info>> for MintToChecked { decimals: cpi.decimals, authority: *cpi.authority.key, max_top_up: cpi.max_top_up, + fee_payer: cpi.fee_payer.as_ref().map(|a| *a.key), } } } impl MintToChecked { pub fn instruction(self) -> Result { + // Authority is writable only when max_top_up is set AND no fee_payer + // (authority pays for top-ups only if no separate fee_payer) + let authority_meta = if self.max_top_up.is_some() && self.fee_payer.is_none() { + AccountMeta::new(self.authority, true) + } else { + AccountMeta::new_readonly(self.authority, true) + }; + + let mut accounts = vec![ + AccountMeta::new(self.mint, false), + AccountMeta::new(self.destination, false), + authority_meta, + // System program required for rent top-up CPIs + AccountMeta::new_readonly(Pubkey::default(), false), + ]; + + // Add fee_payer if provided (must be signer and writable) + if let Some(fee_payer) = self.fee_payer { + accounts.push(AccountMeta::new(fee_payer, true)); + } + Ok(Instruction { program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID), - accounts: vec![ - AccountMeta::new(self.mint, false), - AccountMeta::new(self.destination, false), - AccountMeta::new_readonly(self.authority, true), - ], + accounts, data: { let mut data = vec![14u8]; // TokenMintToChecked discriminator data.extend_from_slice(&self.amount.to_le_bytes()); diff --git a/sdk-libs/token-sdk/src/instruction/transfer.rs b/sdk-libs/token-sdk/src/instruction/transfer.rs index 551c1ea3e7..4edf532f56 100644 --- a/sdk-libs/token-sdk/src/instruction/transfer.rs +++ b/sdk-libs/token-sdk/src/instruction/transfer.rs @@ -18,6 +18,7 @@ use solana_pubkey::Pubkey; /// amount: 100, /// authority, /// max_top_up: None, +/// fee_payer: None, /// }.instruction()?; /// # Ok::<(), solana_program_error::ProgramError>(()) /// ``` @@ -29,6 +30,9 @@ pub struct Transfer { /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) /// When set, includes max_top_up in instruction data and adds system program account for compressible top-up pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + /// When set, fee_payer pays for top-ups instead of authority. + pub fee_payer: Option, } /// # Transfer ctoken via CPI: @@ -38,12 +42,15 @@ pub struct Transfer { /// # let source: AccountInfo = todo!(); /// # let destination: AccountInfo = todo!(); /// # let authority: AccountInfo = todo!(); +/// # let system_program: AccountInfo = todo!(); /// TransferCpi { /// source, /// destination, /// amount: 100, /// authority, +/// system_program, /// max_top_up: None, +/// fee_payer: None, /// } /// .invoke()?; /// # Ok::<(), solana_program_error::ProgramError>(()) @@ -53,8 +60,11 @@ pub struct TransferCpi<'info> { pub destination: AccountInfo<'info>, pub amount: u64, pub authority: AccountInfo<'info>, + pub system_program: AccountInfo<'info>, /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option>, } impl<'info> TransferCpi<'info> { @@ -64,14 +74,46 @@ impl<'info> TransferCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = Transfer::from(&self).instruction()?; - let account_infos = [self.source, self.destination, self.authority]; - invoke(&instruction, &account_infos) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.source, + self.destination, + self.authority, + self.system_program, + fee_payer, + ]; + invoke(&instruction, &account_infos) + } else { + let account_infos = [ + self.source, + self.destination, + self.authority, + self.system_program, + ]; + invoke(&instruction, &account_infos) + } } pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = Transfer::from(&self).instruction()?; - let account_infos = [self.source, self.destination, self.authority]; - invoke_signed(&instruction, &account_infos, signer_seeds) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.source, + self.destination, + self.authority, + self.system_program, + fee_payer, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } else { + let account_infos = [ + self.source, + self.destination, + self.authority, + self.system_program, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } } } @@ -83,14 +125,16 @@ impl<'info> From<&TransferCpi<'info>> for Transfer { amount: account_infos.amount, authority: *account_infos.authority.key, max_top_up: account_infos.max_top_up, + fee_payer: account_infos.fee_payer.as_ref().map(|a| *a.key), } } } impl Transfer { pub fn instruction(self) -> Result { - // Authority is writable only when max_top_up is set (for compressible top-up lamport transfer) - let authority_meta = if self.max_top_up.is_some() { + // Authority is writable only when max_top_up is set AND no fee_payer + // (authority pays for top-ups only if no separate fee_payer) + let authority_meta = if self.max_top_up.is_some() && self.fee_payer.is_none() { AccountMeta::new(self.authority, true) } else { AccountMeta::new_readonly(self.authority, true) @@ -100,14 +144,13 @@ impl Transfer { AccountMeta::new(self.source, false), AccountMeta::new(self.destination, false), authority_meta, + // System program required for rent top-up CPIs + AccountMeta::new_readonly(Pubkey::default(), false), ]; - // Include system program for compressible top-up when max_top_up is set - if self.max_top_up.is_some() { - accounts.push(AccountMeta::new_readonly( - solana_pubkey::pubkey!("11111111111111111111111111111111"), - false, - )); + // Add fee_payer if provided (must be signer and writable) + if let Some(fee_payer) = self.fee_payer { + accounts.push(AccountMeta::new(fee_payer, true)); } Ok(Instruction { diff --git a/sdk-libs/token-sdk/src/instruction/transfer_checked.rs b/sdk-libs/token-sdk/src/instruction/transfer_checked.rs index e2916046de..17c04ddefd 100644 --- a/sdk-libs/token-sdk/src/instruction/transfer_checked.rs +++ b/sdk-libs/token-sdk/src/instruction/transfer_checked.rs @@ -21,6 +21,7 @@ use solana_pubkey::Pubkey; /// decimals: 9, /// authority, /// max_top_up: None, +/// fee_payer: None, /// }.instruction()?; /// # Ok::<(), solana_program_error::ProgramError>(()) /// ``` @@ -34,6 +35,8 @@ pub struct TransferChecked { /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) /// When set to a non-zero value, includes max_top_up in instruction data pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option, } /// # Transfer ctoken checked via CPI: @@ -44,6 +47,7 @@ pub struct TransferChecked { /// # let mint: AccountInfo = todo!(); /// # let destination: AccountInfo = todo!(); /// # let authority: AccountInfo = todo!(); +/// # let system_program: AccountInfo = todo!(); /// TransferCheckedCpi { /// source, /// mint, @@ -51,7 +55,9 @@ pub struct TransferChecked { /// amount: 100, /// decimals: 9, /// authority, +/// system_program, /// max_top_up: None, +/// fee_payer: None, /// } /// .invoke()?; /// # Ok::<(), solana_program_error::ProgramError>(()) @@ -63,8 +69,11 @@ pub struct TransferCheckedCpi<'info> { pub amount: u64, pub decimals: u8, pub authority: AccountInfo<'info>, + pub system_program: AccountInfo<'info>, /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit) pub max_top_up: Option, + /// Optional fee payer for rent top-ups. If not provided, authority pays. + pub fee_payer: Option>, } impl<'info> TransferCheckedCpi<'info> { @@ -74,14 +83,50 @@ impl<'info> TransferCheckedCpi<'info> { pub fn invoke(self) -> Result<(), ProgramError> { let instruction = TransferChecked::from(&self).instruction()?; - let account_infos = [self.source, self.mint, self.destination, self.authority]; - invoke(&instruction, &account_infos) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.source, + self.mint, + self.destination, + self.authority, + self.system_program, + fee_payer, + ]; + invoke(&instruction, &account_infos) + } else { + let account_infos = [ + self.source, + self.mint, + self.destination, + self.authority, + self.system_program, + ]; + invoke(&instruction, &account_infos) + } } pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> { let instruction = TransferChecked::from(&self).instruction()?; - let account_infos = [self.source, self.mint, self.destination, self.authority]; - invoke_signed(&instruction, &account_infos, signer_seeds) + if let Some(fee_payer) = self.fee_payer { + let account_infos = [ + self.source, + self.mint, + self.destination, + self.authority, + self.system_program, + fee_payer, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } else { + let account_infos = [ + self.source, + self.mint, + self.destination, + self.authority, + self.system_program, + ]; + invoke_signed(&instruction, &account_infos, signer_seeds) + } } } @@ -95,14 +140,16 @@ impl<'info> From<&TransferCheckedCpi<'info>> for TransferChecked { decimals: account_infos.decimals, authority: *account_infos.authority.key, max_top_up: account_infos.max_top_up, + fee_payer: account_infos.fee_payer.as_ref().map(|a| *a.key), } } } impl TransferChecked { pub fn instruction(self) -> Result { - // Authority is writable only when max_top_up is set (for compressible top-up lamport transfer) - let authority_meta = if self.max_top_up.is_some() { + // Authority is writable only when max_top_up is set AND no fee_payer + // (authority pays for top-ups only if no separate fee_payer) + let authority_meta = if self.max_top_up.is_some() && self.fee_payer.is_none() { AccountMeta::new(self.authority, true) } else { AccountMeta::new_readonly(self.authority, true) @@ -113,14 +160,13 @@ impl TransferChecked { AccountMeta::new_readonly(self.mint, false), AccountMeta::new(self.destination, false), authority_meta, + // System program required for rent top-up CPIs + AccountMeta::new_readonly(Pubkey::default(), false), ]; - // Include system program for compressible top-up when max_top_up is set - if self.max_top_up.is_some() { - accounts.push(AccountMeta::new_readonly( - solana_pubkey::pubkey!("11111111111111111111111111111111"), - false, - )); + // Add fee_payer if provided (must be signer and writable) + if let Some(fee_payer) = self.fee_payer { + accounts.push(AccountMeta::new(fee_payer, true)); } Ok(Instruction { diff --git a/sdk-libs/token-sdk/src/instruction/transfer_interface.rs b/sdk-libs/token-sdk/src/instruction/transfer_interface.rs index 88eec28d56..f019081c09 100644 --- a/sdk-libs/token-sdk/src/instruction/transfer_interface.rs +++ b/sdk-libs/token-sdk/src/instruction/transfer_interface.rs @@ -135,6 +135,7 @@ impl TransferInterface { amount: self.amount, authority: self.authority, max_top_up: self.max_top_up, + fee_payer: None, } .instruction(), diff --git a/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/deposit.rs b/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/deposit.rs index c2b7b4b420..9ad42ce187 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/deposit.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/deposit.rs @@ -80,6 +80,7 @@ pub fn process_deposit(ctx: Context, lp_token_amount: u64) -> Result<() authority: ctx.accounts.authority.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), max_top_up: None, + fee_payer: None, } .invoke_signed(&[&[AUTH_SEED.as_bytes(), &[auth_bump]]])?; diff --git a/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/initialize.rs b/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/initialize.rs index 45ee1bef45..8aaa7fe1fa 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/initialize.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/initialize.rs @@ -237,6 +237,7 @@ pub fn process_initialize_pool<'info>( authority: ctx.accounts.authority.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), max_top_up: None, + fee_payer: None, } .invoke_signed(&[&[AUTH_SEED.as_bytes(), &[ctx.bumps.authority]]])?; diff --git a/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/withdraw.rs b/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/withdraw.rs index 72f7ff4e2f..d9472349e0 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/withdraw.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/amm_test/withdraw.rs @@ -75,7 +75,9 @@ pub fn process_withdraw(ctx: Context, lp_token_amount: u64) -> Result< mint: ctx.accounts.lp_mint.to_account_info(), amount: lp_token_amount, authority: ctx.accounts.owner.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), max_top_up: None, + fee_payer: None, } .invoke()?; 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 f20cd7120d..65446d194d 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/src/lib.rs @@ -340,6 +340,7 @@ pub mod csdk_anchor_full_derived_test { authority: ctx.accounts.mint_authority.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), max_top_up: None, + fee_payer: None, } .invoke()?; } @@ -352,6 +353,7 @@ pub mod csdk_anchor_full_derived_test { authority: ctx.accounts.mint_authority.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), max_top_up: None, + fee_payer: None, } .invoke()?; } diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/shared.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/shared.rs index 352d0b2ad8..729827351f 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/shared.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/shared.rs @@ -124,6 +124,7 @@ pub async fn setup_create_mint( amount: *amount, authority: mint_authority, max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); diff --git a/sdk-tests/sdk-light-token-test/src/burn.rs b/sdk-tests/sdk-light-token-test/src/burn.rs index b7d9fad600..d1b2bfbf2f 100644 --- a/sdk-tests/sdk-light-token-test/src/burn.rs +++ b/sdk-tests/sdk-light-token-test/src/burn.rs @@ -17,8 +17,9 @@ pub struct BurnData { /// - accounts[1]: mint (writable) /// - accounts[2]: authority (owner, signer) /// - accounts[3]: light_token_program +/// - accounts[4]: system_program pub fn process_burn_invoke(accounts: &[AccountInfo], amount: u64) -> Result<(), ProgramError> { - if accounts.len() < 4 { + if accounts.len() < 5 { return Err(ProgramError::NotEnoughAccountKeys); } @@ -27,7 +28,9 @@ pub fn process_burn_invoke(accounts: &[AccountInfo], amount: u64) -> Result<(), mint: accounts[1].clone(), amount, authority: accounts[2].clone(), + system_program: accounts[4].clone(), max_top_up: None, + fee_payer: None, } .invoke()?; @@ -41,11 +44,12 @@ pub fn process_burn_invoke(accounts: &[AccountInfo], amount: u64) -> Result<(), /// - accounts[1]: mint (writable) /// - accounts[2]: PDA authority (owner, program signs) /// - accounts[3]: light_token_program +/// - accounts[4]: system_program pub fn process_burn_invoke_signed( accounts: &[AccountInfo], amount: u64, ) -> Result<(), ProgramError> { - if accounts.len() < 4 { + if accounts.len() < 5 { return Err(ProgramError::NotEnoughAccountKeys); } @@ -63,7 +67,9 @@ pub fn process_burn_invoke_signed( mint: accounts[1].clone(), amount, authority: accounts[2].clone(), + system_program: accounts[4].clone(), max_top_up: None, + fee_payer: None, } .invoke_signed(&[signer_seeds])?; diff --git a/sdk-tests/sdk-light-token-test/src/ctoken_mint_to.rs b/sdk-tests/sdk-light-token-test/src/ctoken_mint_to.rs index 0709bf2a12..9b4d10c61b 100644 --- a/sdk-tests/sdk-light-token-test/src/ctoken_mint_to.rs +++ b/sdk-tests/sdk-light-token-test/src/ctoken_mint_to.rs @@ -30,6 +30,7 @@ pub fn process_mint_to_invoke(accounts: &[AccountInfo], amount: u64) -> Result<( authority: accounts[2].clone(), system_program: accounts[3].clone(), max_top_up: None, + fee_payer: None, } .invoke()?; @@ -68,6 +69,7 @@ pub fn process_mint_to_invoke_signed( authority: accounts[2].clone(), system_program: accounts[3].clone(), max_top_up: None, + fee_payer: None, } .invoke_signed(&[signer_seeds])?; diff --git a/sdk-tests/sdk-light-token-test/src/transfer.rs b/sdk-tests/sdk-light-token-test/src/transfer.rs index e75fb3475b..2ddcf397e5 100644 --- a/sdk-tests/sdk-light-token-test/src/transfer.rs +++ b/sdk-tests/sdk-light-token-test/src/transfer.rs @@ -20,11 +20,12 @@ pub struct TransferData { /// - accounts[0]: source ctoken account /// - accounts[1]: destination ctoken account /// - accounts[2]: authority (signer) +/// - accounts[3]: system_program pub fn process_transfer_invoke( accounts: &[AccountInfo], data: TransferData, ) -> Result<(), ProgramError> { - if accounts.len() < 3 { + if accounts.len() < 4 { return Err(ProgramError::NotEnoughAccountKeys); } @@ -34,7 +35,9 @@ pub fn process_transfer_invoke( destination: accounts[1].clone(), amount: data.amount, authority: accounts[2].clone(), + system_program: accounts[3].clone(), max_top_up: None, + fee_payer: None, } .invoke()?; @@ -52,11 +55,12 @@ pub fn process_transfer_invoke( /// - accounts[0]: source ctoken account (PDA-owned) /// - accounts[1]: destination ctoken account /// - accounts[2]: authority (PDA) +/// - accounts[3]: system_program pub fn process_transfer_invoke_signed( accounts: &[AccountInfo], data: TransferData, ) -> Result<(), ProgramError> { - if accounts.len() < 3 { + if accounts.len() < 4 { return Err(ProgramError::NotEnoughAccountKeys); } @@ -74,7 +78,9 @@ pub fn process_transfer_invoke_signed( destination: accounts[1].clone(), amount: data.amount, authority: accounts[2].clone(), + system_program: accounts[3].clone(), max_top_up: None, + fee_payer: None, }; // Invoke with PDA signing - the builder handles instruction creation and invoke_signed CPI diff --git a/sdk-tests/sdk-light-token-test/src/transfer_checked.rs b/sdk-tests/sdk-light-token-test/src/transfer_checked.rs index ffc0692a67..d31f1b2e4f 100644 --- a/sdk-tests/sdk-light-token-test/src/transfer_checked.rs +++ b/sdk-tests/sdk-light-token-test/src/transfer_checked.rs @@ -18,11 +18,12 @@ pub struct TransferCheckedData { /// - accounts[1]: mint (SPL, T22, or decompressed Mint) /// - accounts[2]: destination ctoken account /// - accounts[3]: authority (signer) +/// - accounts[4]: system_program pub fn process_transfer_checked_invoke( accounts: &[AccountInfo], data: TransferCheckedData, ) -> Result<(), ProgramError> { - if accounts.len() < 4 { + if accounts.len() < 5 { return Err(ProgramError::NotEnoughAccountKeys); } @@ -33,7 +34,9 @@ pub fn process_transfer_checked_invoke( amount: data.amount, decimals: data.decimals, authority: accounts[3].clone(), + system_program: accounts[4].clone(), max_top_up: None, + fee_payer: None, } .invoke()?; @@ -47,11 +50,12 @@ pub fn process_transfer_checked_invoke( /// - accounts[1]: mint (SPL, T22, or decompressed Mint) /// - accounts[2]: destination ctoken account /// - accounts[3]: authority (PDA) +/// - accounts[4]: system_program pub fn process_transfer_checked_invoke_signed( accounts: &[AccountInfo], data: TransferCheckedData, ) -> Result<(), ProgramError> { - if accounts.len() < 4 { + if accounts.len() < 5 { return Err(ProgramError::NotEnoughAccountKeys); } @@ -70,7 +74,9 @@ pub fn process_transfer_checked_invoke_signed( amount: data.amount, decimals: data.decimals, authority: accounts[3].clone(), + system_program: accounts[4].clone(), max_top_up: None, + fee_payer: None, }; // Invoke with PDA signing diff --git a/sdk-tests/sdk-light-token-test/tests/scenario_light_mint.rs b/sdk-tests/sdk-light-token-test/tests/scenario_light_mint.rs index d5556c69b3..3aac131d6a 100644 --- a/sdk-tests/sdk-light-token-test/tests/scenario_light_mint.rs +++ b/sdk-tests/sdk-light-token-test/tests/scenario_light_mint.rs @@ -90,6 +90,7 @@ async fn test_mint_to_ctoken_scenario() { amount: transfer_amount, authority: owner1.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); diff --git a/sdk-tests/sdk-light-token-test/tests/scenario_light_mint_compression_only.rs b/sdk-tests/sdk-light-token-test/tests/scenario_light_mint_compression_only.rs index c76d991fdc..be34bd4c2f 100644 --- a/sdk-tests/sdk-light-token-test/tests/scenario_light_mint_compression_only.rs +++ b/sdk-tests/sdk-light-token-test/tests/scenario_light_mint_compression_only.rs @@ -95,6 +95,7 @@ async fn test_mint_to_ctoken_scenario_compression_only() { amount: transfer_amount, authority: owner1.pubkey(), max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); diff --git a/sdk-tests/sdk-light-token-test/tests/shared.rs b/sdk-tests/sdk-light-token-test/tests/shared.rs index 748d673ceb..ab95972c50 100644 --- a/sdk-tests/sdk-light-token-test/tests/shared.rs +++ b/sdk-tests/sdk-light-token-test/tests/shared.rs @@ -124,6 +124,7 @@ pub async fn setup_create_mint( amount: *amount, authority: mint_authority, max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -244,6 +245,7 @@ pub async fn setup_create_mint_with_freeze_authority( amount: *amount, authority: mint_authority, max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); @@ -383,6 +385,7 @@ pub async fn setup_create_mint_with_compression_only( amount: *amount, authority: mint_authority, max_top_up: None, + fee_payer: None, } .instruction() .unwrap(); diff --git a/sdk-tests/sdk-light-token-test/tests/test_burn.rs b/sdk-tests/sdk-light-token-test/tests/test_burn.rs index 4c19906515..af5fc2a1e7 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_burn.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_burn.rs @@ -55,6 +55,7 @@ async fn test_burn_invoke() { AccountMeta::new(mint_pda, false), // mint AccountMeta::new_readonly(payer.pubkey(), true), // authority (signer) AccountMeta::new_readonly(light_token_program, false), // light_token_program + AccountMeta::new_readonly(Pubkey::default(), false), // system_program ], data: instruction_data, }; @@ -120,6 +121,7 @@ async fn test_burn_invoke_signed() { AccountMeta::new(mint_pda, false), // mint AccountMeta::new_readonly(pda_owner, false), // PDA authority (program signs) AccountMeta::new_readonly(light_token_program, false), // light_token_program + AccountMeta::new_readonly(Pubkey::default(), false), // system_program ], data: instruction_data, }; diff --git a/sdk-tests/sdk-light-token-test/tests/test_transfer.rs b/sdk-tests/sdk-light-token-test/tests/test_transfer.rs index a97960d43e..7d10707e77 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_transfer.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_transfer.rs @@ -50,6 +50,7 @@ async fn test_ctoken_transfer_invoke() { AccountMeta::new(source_ata, false), AccountMeta::new(dest_ata, false), AccountMeta::new_readonly(source_owner, true), + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(LIGHT_TOKEN_PROGRAM_ID, false), ], data: instruction_data, @@ -107,6 +108,7 @@ async fn test_ctoken_transfer_invoke_signed() { AccountMeta::new(source_ata, false), AccountMeta::new(dest_ata, false), AccountMeta::new_readonly(pda_owner, false), // PDA authority, not signer + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(LIGHT_TOKEN_PROGRAM_ID, false), ], data: instruction_data, diff --git a/sdk-tests/sdk-light-token-test/tests/test_transfer_checked.rs b/sdk-tests/sdk-light-token-test/tests/test_transfer_checked.rs index 92ca87a232..0ff53b826a 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_transfer_checked.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_transfer_checked.rs @@ -152,6 +152,7 @@ async fn test_ctoken_transfer_checked_spl_mint() { AccountMeta::new_readonly(mint, false), AccountMeta::new(dest_ata, false), AccountMeta::new_readonly(source_owner, true), + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(light_token_program, false), ], data: instruction_data, @@ -257,6 +258,7 @@ async fn test_ctoken_transfer_checked_t22_mint() { AccountMeta::new_readonly(mint, false), AccountMeta::new(dest_ata, false), AccountMeta::new_readonly(source_owner, true), + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(light_token_program, false), ], data: instruction_data, @@ -317,6 +319,7 @@ async fn test_ctoken_transfer_checked_mint() { AccountMeta::new_readonly(mint, false), AccountMeta::new(dest_ata, false), AccountMeta::new_readonly(source_owner, true), + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(light_token_program, false), ], data: instruction_data, diff --git a/sdk-tests/sdk-light-token-test/tests/test_transfer_interface.rs b/sdk-tests/sdk-light-token-test/tests/test_transfer_interface.rs index 68a68c1c1b..b2d62774a9 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_transfer_interface.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_transfer_interface.rs @@ -99,7 +99,7 @@ async fn test_transfer_interface_spl_to_ctoken_invoke() { AccountMeta::new_readonly(sender.pubkey(), true), // authority (signer) AccountMeta::new(payer.pubkey(), true), // payer AccountMeta::new_readonly(cpi_authority_pda, false), // compressed_token_program_authority - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(mint, false), // mint (for SPL bridge) AccountMeta::new(spl_interface_pda, false), // spl_interface_pda AccountMeta::new_readonly(anchor_spl::token::ID, false), // spl_token_program @@ -207,7 +207,7 @@ async fn test_transfer_interface_ctoken_to_spl_invoke() { AccountMeta::new_readonly(owner.pubkey(), true), AccountMeta::new(payer.pubkey(), true), AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), AccountMeta::new_readonly(mint, false), AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), @@ -237,7 +237,7 @@ async fn test_transfer_interface_ctoken_to_spl_invoke() { AccountMeta::new_readonly(owner.pubkey(), true), // authority AccountMeta::new(payer.pubkey(), true), // payer AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), AccountMeta::new_readonly(mint, false), AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), @@ -352,7 +352,7 @@ async fn test_transfer_interface_ctoken_to_ctoken_invoke() { AccountMeta::new_readonly(sender.pubkey(), true), AccountMeta::new(payer.pubkey(), true), AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), AccountMeta::new_readonly(mint, false), AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), @@ -383,7 +383,7 @@ async fn test_transfer_interface_ctoken_to_ctoken_invoke() { AccountMeta::new_readonly(sender.pubkey(), true), // authority AccountMeta::new(payer.pubkey(), true), // payer AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(Pubkey::default(), false), // system_program ]; let instruction = Instruction { @@ -500,7 +500,7 @@ async fn test_transfer_interface_spl_to_ctoken_invoke_signed() { AccountMeta::new_readonly(authority_pda, false), // authority (PDA, not signer) AccountMeta::new(payer.pubkey(), true), // payer AccountMeta::new_readonly(cpi_authority_pda, false), // compressed_token_program_authority - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(mint, false), AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), @@ -626,7 +626,7 @@ async fn test_transfer_interface_ctoken_to_spl_invoke_signed() { AccountMeta::new_readonly(temp_owner.pubkey(), true), AccountMeta::new(payer.pubkey(), true), AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), AccountMeta::new_readonly(mint, false), AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), @@ -657,7 +657,7 @@ async fn test_transfer_interface_ctoken_to_spl_invoke_signed() { AccountMeta::new_readonly(authority_pda, false), // authority (PDA) AccountMeta::new(payer.pubkey(), true), // payer AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), AccountMeta::new_readonly(mint, false), AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), @@ -784,7 +784,7 @@ async fn test_transfer_interface_ctoken_to_ctoken_invoke_signed() { AccountMeta::new_readonly(temp_owner.pubkey(), true), AccountMeta::new(payer.pubkey(), true), AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), AccountMeta::new_readonly(mint, false), AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), @@ -816,7 +816,7 @@ async fn test_transfer_interface_ctoken_to_ctoken_invoke_signed() { AccountMeta::new_readonly(authority_pda, false), // authority (PDA) AccountMeta::new(payer.pubkey(), true), // payer AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), ]; let instruction = Instruction { @@ -919,7 +919,7 @@ async fn test_transfer_interface_spl_to_spl_invoke() { AccountMeta::new_readonly(sender.pubkey(), true), // authority (signer) AccountMeta::new(payer.pubkey(), true), // payer AccountMeta::new_readonly(cpi_authority_pda, false), // compressed_token_program_authority - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(mint, false), // mint (for SPL transfer_checked) AccountMeta::new(spl_interface_pda, false), // spl_interface_pda (passed but not used) AccountMeta::new_readonly(anchor_spl::token::ID, false), // spl_token_program @@ -1038,7 +1038,7 @@ async fn test_transfer_interface_spl_to_spl_invoke_signed() { AccountMeta::new_readonly(authority_pda, false), // authority (PDA, not signer) AccountMeta::new(payer.pubkey(), true), // payer AccountMeta::new_readonly(cpi_authority_pda, false), // compressed_token_program_authority - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(mint, false), AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), @@ -1150,7 +1150,7 @@ async fn test_transfer_interface_t22_to_t22_invoke() { AccountMeta::new_readonly(sender.pubkey(), true), // authority (signer) AccountMeta::new(payer.pubkey(), true), // payer AccountMeta::new_readonly(cpi_authority_pda, false), // compressed_token_program_authority - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(mint, false), // mint (for T22 transfer_checked) AccountMeta::new(spl_interface_pda, false), // spl_interface_pda (passed but not used) AccountMeta::new_readonly(anchor_spl::token_2022::ID, false), // T22 token program @@ -1274,7 +1274,7 @@ async fn test_transfer_interface_t22_to_t22_invoke_signed() { AccountMeta::new_readonly(authority_pda, false), // authority (PDA, not signer) AccountMeta::new(payer.pubkey(), true), // payer AccountMeta::new_readonly(cpi_authority_pda, false), // compressed_token_program_authority - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), // system_program + AccountMeta::new_readonly(Pubkey::default(), false), // system_program AccountMeta::new_readonly(mint, false), AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token_2022::ID, false), // T22 token program diff --git a/sdk-tests/sdk-light-token-test/tests/test_transfer_spl_ctoken.rs b/sdk-tests/sdk-light-token-test/tests/test_transfer_spl_ctoken.rs index 4719b7a93b..dbf96fee7a 100644 --- a/sdk-tests/sdk-light-token-test/tests/test_transfer_spl_ctoken.rs +++ b/sdk-tests/sdk-light-token-test/tests/test_transfer_spl_ctoken.rs @@ -124,7 +124,7 @@ async fn test_spl_to_ctoken_invoke() { AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), ]; let instruction = Instruction { @@ -238,7 +238,7 @@ async fn test_ctoken_to_spl_invoke() { AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), ]; let instruction = Instruction { @@ -413,7 +413,7 @@ async fn test_spl_to_ctoken_invoke_signed() { AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), ]; let instruction = Instruction { @@ -551,7 +551,7 @@ async fn test_ctoken_to_spl_invoke_signed() { AccountMeta::new(spl_interface_pda, false), AccountMeta::new_readonly(anchor_spl::token::ID, false), AccountMeta::new_readonly(cpi_authority_pda, false), - AccountMeta::new_readonly(solana_sdk::system_program::ID, false), + AccountMeta::new_readonly(Pubkey::default(), false), ]; let instruction = Instruction {