From 9524c490aa0370a9ef43aa53cf6bbf2e0caff3a9 Mon Sep 17 00:00:00 2001 From: ananas Date: Mon, 17 Nov 2025 22:50:18 +0000 Subject: [PATCH] fix: calculate_top_up_lamports --- .../compressible/src/compression_info.rs | 20 ++++++++++++------- .../compressible/tests/compression_info.rs | 16 +++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/program-libs/compressible/src/compression_info.rs b/program-libs/compressible/src/compression_info.rs index 5371065817..e838c6a44e 100644 --- a/program-libs/compressible/src/compression_info.rs +++ b/program-libs/compressible/src/compression_info.rs @@ -10,7 +10,7 @@ use crate::{ error::CompressibleError, rent::{ get_last_funded_epoch, get_rent_exemption_lamports, AccountRentState, RentConfig, - RentConfigTrait, SLOTS_PER_EPOCH, + SLOTS_PER_EPOCH, }, AnchorDeserialize, AnchorSerialize, }; @@ -109,13 +109,19 @@ macro_rules! impl_is_compressible { if let Some(rent_deficit) = is_compressible { Ok(lamports_per_write as u64 + rent_deficit) } else { - // Calculate epochs funded ahead using available balance - let available_balance = state.get_available_rent_balance( + let last_funded_epoch_number = self.get_last_funded_epoch( + num_bytes, + current_lamports, rent_exemption_lamports, - self.rent_config.compression_cost(), - ); - let rent_per_epoch = self.rent_config.rent_curve_per_epoch(num_bytes); - let epochs_funded_ahead = available_balance / rent_per_epoch; + )?; + + // Calculate how many epochs ahead of current epoch the account is funded + // last_funded_epoch_number is the epoch number (e.g., 1), so we add 1 to get count + // (epochs 0 and 1 = 2 epochs funded) + let current_epoch = crate::rent::slot_to_epoch(current_slot); + let epochs_funded_ahead = + (last_funded_epoch_number.saturating_add(1)).saturating_sub(current_epoch); + // Skip top-up if already funded for max_funded_epochs or more if epochs_funded_ahead >= self.rent_config.max_funded_epochs as u64 { Ok(0) diff --git a/program-libs/compressible/tests/compression_info.rs b/program-libs/compressible/tests/compression_info.rs index ae755c4e5b..11779fc338 100644 --- a/program-libs/compressible/tests/compression_info.rs +++ b/program-libs/compressible/tests/compression_info.rs @@ -405,18 +405,18 @@ fn test_calculate_top_up_lamports() { expected_top_up: lamports_per_write as u64 + 1 + FULL_COMPRESSION_COSTS, description: "Epoch 1: available_balance=775 (1.997 epochs), required=776 (2 epochs), compressible with 1 lamport deficit", }, + // ============================================================ + // PATH 2: NOT COMPRESSIBLE, NEEDS TOP-UP (lamports_per_write) + // ============================================================ TestCase { - name: "exact boundary - not compressible by exact match", + name: "exact boundary - lagging claim requires top-up", current_slot: SLOTS_PER_EPOCH, current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2), last_claimed_slot: 0, lamports_per_write, - expected_top_up: 0, - description: "Epoch 1: available_balance=776 == required=776 (2 epochs), not compressible, epochs_funded_ahead=2", + expected_top_up: lamports_per_write as u64, + description: "Epoch 1: last_claimed=epoch 0, funded through epoch 1, epochs_funded_ahead=1 < max=2", }, - // ============================================================ - // PATH 2: NOT COMPRESSIBLE, NEEDS TOP-UP (lamports_per_write) - // ============================================================ TestCase { name: "exactly 1 epoch funded (max is 2)", current_slot: 0, @@ -472,7 +472,7 @@ fn test_calculate_top_up_lamports() { last_claimed_slot: 0, lamports_per_write, expected_top_up: 0, - description: "Epoch 0: not compressible, epochs_funded_ahead=2 >= max_funded_epochs=2, no top-up needed", + description: "Epoch 0: last_claimed=epoch 0, funded through epoch 1, epochs_funded_ahead=2 >= max=2", }, TestCase { name: "3 epochs when max is 2", @@ -487,7 +487,7 @@ fn test_calculate_top_up_lamports() { name: "2 epochs at epoch 1 boundary", current_slot: SLOTS_PER_EPOCH, current_lamports: rent_exemption_lamports + FULL_COMPRESSION_COSTS + (RENT_PER_EPOCH * 2), - last_claimed_slot: 0, + last_claimed_slot: SLOTS_PER_EPOCH, lamports_per_write, expected_top_up: 0, description: "Epoch 1: not compressible (has 776 for required 776), epochs_funded_ahead=2 >= max_funded_epochs=2",