From 800164e0b83d784bf5009cc0463f683e5e34975e Mon Sep 17 00:00:00 2001 From: febo Date: Wed, 4 Mar 2026 11:57:52 +0000 Subject: [PATCH 1/3] Add optional rent sysvar account --- pinocchio/interface/src/instruction.rs | 6 ++++++ pinocchio/program/src/processor/sync_native.rs | 16 +++++++++++++--- pinocchio/program/tests/sync_native.rs | 16 ++++++++++++---- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/pinocchio/interface/src/instruction.rs b/pinocchio/interface/src/instruction.rs index be946987..f74ac950 100644 --- a/pinocchio/interface/src/instruction.rs +++ b/pinocchio/interface/src/instruction.rs @@ -383,8 +383,14 @@ pub enum TokenInstruction { /// /// Accounts expected by this instruction: /// + /// * Using runtime Rent sysvar /// 0. `[writable]` The native token account to sync with its underlying /// lamports. + /// + /// * Using Rent sysvar account + /// 0. `[writable]` The native token account to sync with its underlying + /// lamports. + /// 1. `[]` Rent sysvar. SyncNative, /// Like [`TokenInstruction::InitializeAccount2`], but does not require the diff --git a/pinocchio/program/src/processor/sync_native.rs b/pinocchio/program/src/processor/sync_native.rs index caef9377..e0003b50 100644 --- a/pinocchio/program/src/processor/sync_native.rs +++ b/pinocchio/program/src/processor/sync_native.rs @@ -14,12 +14,22 @@ use { #[inline(always)] pub fn process_sync_native(accounts: &[AccountInfo]) -> ProgramResult { - let native_account_info = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?; + let [native_account_info, remaining @ ..] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; check_account_owner(native_account_info)?; - let rent = Rent::get()?; - let rent_exempt_reserve = rent.minimum_balance(native_account_info.data_len()); + let rent_exempt_reserve = if remaining.is_empty() { + Rent::get()?.minimum_balance(native_account_info.data_len()) + } else { + // SAFETY: `remaining` is guaranteed to not be empty. + let rent_sysvar_info = unsafe { remaining.get_unchecked(0) }; + // SAFETY: single immutable borrow to `rent_sysvar_info`; account ID and length + // are checked by `from_account_info_unchecked`. + let rent = unsafe { Rent::from_account_info_unchecked(rent_sysvar_info)? }; + rent.minimum_balance(native_account_info.data_len()) + }; // SAFETY: single mutable borrow to `native_account_info` account data and // `load_mut` validates that the account is initialized. diff --git a/pinocchio/program/tests/sync_native.rs b/pinocchio/program/tests/sync_native.rs index 31d9b6da..bb9b9a62 100644 --- a/pinocchio/program/tests/sync_native.rs +++ b/pinocchio/program/tests/sync_native.rs @@ -2,7 +2,7 @@ mod setup; use { crate::setup::TOKEN_PROGRAM_ID, - mollusk_svm::{result::Check, Mollusk}, + mollusk_svm::{result::Check, sysvar::Sysvars, Mollusk}, pinocchio_token_interface::{ native_mint, state::{ @@ -11,10 +11,11 @@ use { }, }, solana_account::Account, + solana_instruction::AccountMeta, solana_program_pack::Pack, solana_program_test::tokio, solana_pubkey::Pubkey, - solana_rent::Rent, + solana_rent::{sysvar, Rent}, solana_sdk_ids::bpf_loader_upgradeable, }; @@ -75,15 +76,22 @@ async fn sync_native() { create_token_account(&native_mint, &authority_key, true, 0, &TOKEN_PROGRAM_ID); source_account.lamports += 2_000_000_000; - let instruction = + let mut instruction = spl_token_interface::instruction::sync_native(&TOKEN_PROGRAM_ID, &source_account_key) .unwrap(); + // Add the optional rent sysvar account. + instruction + .accounts + .push(AccountMeta::new_readonly(sysvar::id(), false)); // Executes the sync_native instruction. let result = mollusk().process_and_validate_instruction_chain( &[(&instruction, &[Check::success()])], - &[(source_account_key, source_account)], + &[ + (source_account_key, source_account), + Sysvars::default().keyed_account_for_rent_sysvar(), + ], ); result.resulting_accounts.iter().for_each(|(key, account)| { From b42c3f127ec787c16ae8533a9d7b2ec80d3b8b9f Mon Sep 17 00:00:00 2001 From: febo Date: Wed, 4 Mar 2026 12:21:44 +0000 Subject: [PATCH 2/3] Improve remaining check --- pinocchio/program/src/processor/sync_native.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pinocchio/program/src/processor/sync_native.rs b/pinocchio/program/src/processor/sync_native.rs index e0003b50..45785129 100644 --- a/pinocchio/program/src/processor/sync_native.rs +++ b/pinocchio/program/src/processor/sync_native.rs @@ -20,15 +20,13 @@ pub fn process_sync_native(accounts: &[AccountInfo]) -> ProgramResult { check_account_owner(native_account_info)?; - let rent_exempt_reserve = if remaining.is_empty() { - Rent::get()?.minimum_balance(native_account_info.data_len()) - } else { - // SAFETY: `remaining` is guaranteed to not be empty. - let rent_sysvar_info = unsafe { remaining.get_unchecked(0) }; + let rent_exempt_reserve = if let Some(rent_sysvar_info) = remaining.first() { // SAFETY: single immutable borrow to `rent_sysvar_info`; account ID and length // are checked by `from_account_info_unchecked`. let rent = unsafe { Rent::from_account_info_unchecked(rent_sysvar_info)? }; rent.minimum_balance(native_account_info.data_len()) + } else { + Rent::get()?.minimum_balance(native_account_info.data_len()) }; // SAFETY: single mutable borrow to `native_account_info` account data and From 78c3ffa67ce962da382d703325f688f3e12dbc44 Mon Sep 17 00:00:00 2001 From: febo Date: Wed, 4 Mar 2026 14:12:23 +0000 Subject: [PATCH 3/3] Add sync native with rent helper --- Cargo.lock | 2 +- Cargo.toml | 1 + interface/src/instruction.rs | 14 ++++++++++++++ pinocchio/program/Cargo.toml | 2 +- pinocchio/program/tests/sync_native.rs | 15 ++++++--------- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 73ef4d92..222acc65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3284,7 +3284,7 @@ dependencies = [ "solana-transaction", "solana-transaction-error", "spl-token-2022-interface", - "spl-token-interface 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token-interface 2.0.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b46d61fe..33a930d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,3 +34,4 @@ solana-program-option = "3.0.0" solana-program-pack = "3.0.0" solana-pubkey = "3.0.0" solana-system-interface = { version = "2.0", features = ["bincode"] } +spl-token-interface = { path = "interface" } diff --git a/interface/src/instruction.rs b/interface/src/instruction.rs index 524afa49..aa6efbb2 100644 --- a/interface/src/instruction.rs +++ b/interface/src/instruction.rs @@ -1373,6 +1373,20 @@ pub fn sync_native( }) } +/// Creates a `SyncNative` instruction with the Rent sysvar account +/// added to the accounts list. +pub fn sync_native_with_rent_sysvar( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, +) -> Result { + let mut instruction = sync_native(token_program_id, account_pubkey)?; + instruction + .accounts + .push(AccountMeta::new_readonly(sysvar::rent::id(), false)); + + Ok(instruction) +} + /// Creates a `GetAccountDataSize` instruction pub fn get_account_data_size( token_program_id: &Pubkey, diff --git a/pinocchio/program/Cargo.toml b/pinocchio/program/Cargo.toml index c9eea7e5..fc3e0a8c 100644 --- a/pinocchio/program/Cargo.toml +++ b/pinocchio/program/Cargo.toml @@ -41,7 +41,7 @@ solana-signer = "3.0.0" solana-transaction = "3.0.0" solana-transaction-error = "3.0.0" solana-system-interface = { workspace = true } -spl-token-interface = "2" +spl-token-interface = { workspace = true } spl-token-2022-interface = "2" [lints] diff --git a/pinocchio/program/tests/sync_native.rs b/pinocchio/program/tests/sync_native.rs index bb9b9a62..63f043d3 100644 --- a/pinocchio/program/tests/sync_native.rs +++ b/pinocchio/program/tests/sync_native.rs @@ -11,11 +11,10 @@ use { }, }, solana_account::Account, - solana_instruction::AccountMeta, solana_program_pack::Pack, solana_program_test::tokio, solana_pubkey::Pubkey, - solana_rent::{sysvar, Rent}, + solana_rent::Rent, solana_sdk_ids::bpf_loader_upgradeable, }; @@ -76,13 +75,11 @@ async fn sync_native() { create_token_account(&native_mint, &authority_key, true, 0, &TOKEN_PROGRAM_ID); source_account.lamports += 2_000_000_000; - let mut instruction = - spl_token_interface::instruction::sync_native(&TOKEN_PROGRAM_ID, &source_account_key) - .unwrap(); - // Add the optional rent sysvar account. - instruction - .accounts - .push(AccountMeta::new_readonly(sysvar::id(), false)); + let instruction = spl_token_interface::instruction::sync_native_with_rent_sysvar( + &TOKEN_PROGRAM_ID, + &source_account_key, + ) + .unwrap(); // Executes the sync_native instruction.