Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions forester/tests/test_compressible_mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use light_client::{
local_test_validator::{spawn_validator, LightValidatorConfig},
rpc::{LightClient, LightClientConfig, Rpc},
};
use light_compressible::rent::SLOTS_PER_EPOCH;
use light_token::instruction::{
derive_mint_compressed_address, find_mint_address, CreateMint, CreateMintParams,
};
Expand Down Expand Up @@ -89,7 +90,7 @@ async fn create_decompressed_mint(
.unwrap()
.value;

// Build params - rent_payment = 0 makes the mint immediately compressible (no auto-decompress period)
// Build params - rent_payment = 2 is the minimum required by the program
let params = CreateMintParams {
decimals,
address_merkle_tree_root_index: rpc_result.addresses[0].root_index,
Expand All @@ -100,7 +101,7 @@ async fn create_decompressed_mint(
bump,
freeze_authority: None,
extensions: None,
rent_payment: 0, // Immediately compressible for testing
rent_payment: 2, // Minimum required epochs of rent prepayment
write_top_up: 0,
};

Expand Down Expand Up @@ -267,7 +268,7 @@ async fn test_compressible_mint_bootstrap() {

/// Test that MintCompressor can compress decompressed mints
///
/// This test creates a mint with rent_payment=0 (immediately compressible),
/// This test creates a mint with rent_payment=2, warps past the rent period,
/// then verifies the compressor can close the on-chain mint account.
///
/// Run with: cargo test -p forester --test test_compressible_mint -- --nocapture
Expand Down Expand Up @@ -381,14 +382,18 @@ async fn test_compressible_mint_compression() {
.expect("Failed to create RPC pool"),
);

// Get ready accounts - with rent_payment=0, the mint is immediately compressible
// Warp past the rent prepayment period so the mint becomes compressible
let current_slot = rpc.get_slot().await.unwrap();
let future_slot = current_slot + 2 * SLOTS_PER_EPOCH;
rpc.warp_to_slot(future_slot).await.expect("warp_to_slot");

let current_slot = rpc.get_slot().await.unwrap();
let ready_accounts = tracker.get_ready_to_compress(current_slot);
println!("Ready to compress: {} mints", ready_accounts.len());

assert!(
!ready_accounts.is_empty(),
"Mint should be ready to compress with rent_payment=0"
"Mint should be ready to compress after rent period expires"
);

// Create compressor and compress
Expand Down Expand Up @@ -447,9 +452,9 @@ async fn test_compressible_mint_compression() {
///
/// This test verifies the full subscription flow:
/// 1. Start AccountSubscriber with MintAccountTracker
/// 2. Create two decompressed mints: one with rent, one immediately compressible
/// 2. Create two decompressed mints with rent_payment=2
/// 3. Assert subscriber picks up both accounts (tracker.len() == 2)
/// 4. Run MintCompressor to compress the immediately compressible mint
/// 4. Warp past rent period, run MintCompressor to compress one mint
/// 5. Assert account is closed and tracker is updated via direct removal
///
/// Run with: cargo test -p forester --test test_compressible_mint test_compressible_mint_subscription -- --nocapture
Expand Down Expand Up @@ -511,7 +516,7 @@ async fn test_compressible_mint_subscription() {
// Give subscribers time to connect
sleep(Duration::from_secs(2)).await;

// Create first decompressed mint (immediately compressible with rent_payment=0)
// Create first decompressed mint (rent_payment=2)
let (mint_pda_1, compression_address_1, _mint_seed_1, _bump_1) =
create_decompressed_mint(&mut rpc, &payer, payer.pubkey(), 9).await;
println!("Created first decompressed mint at: {}", mint_pda_1);
Expand Down Expand Up @@ -575,6 +580,11 @@ async fn test_compressible_mint_subscription() {
.expect("Failed to create RPC pool"),
);

// Warp past the rent prepayment period so mints become compressible
let current_slot = rpc.get_slot().await.unwrap();
let future_slot = current_slot + 2 * SLOTS_PER_EPOCH;
rpc.warp_to_slot(future_slot).await.expect("warp_to_slot");

// Get ready-to-compress accounts
let current_slot = rpc.get_slot().await.unwrap();
let ready_accounts = tracker.get_ready_to_compress(current_slot);
Expand All @@ -584,7 +594,7 @@ async fn test_compressible_mint_subscription() {
current_slot
);

// Both mints should be ready (rent_payment=0)
// Both mints should be ready after rent period expires
assert_eq!(
ready_accounts.len(),
2,
Expand Down
6 changes: 3 additions & 3 deletions forester/tests/test_indexer_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ async fn test_indexer_interface_scenarios() {
address_sig
);

// ============ Scenario 4: Decompressed Mint (CreateMint with rent_payment=0) ============
// ============ Scenario 4: Decompressed Mint (CreateMint with rent_payment=2) ============
// This creates a compressed mint that is immediately decompressed to an on-chain CMint account.
// The compressed account only contains the 32-byte mint_pda reference (DECOMPRESSED_PDA_DISCRIMINATOR).
// Full mint data is on-chain in the CMint account owned by LIGHT_TOKEN_PROGRAM_ID.
Expand Down Expand Up @@ -277,7 +277,7 @@ async fn test_indexer_interface_scenarios() {
bump: decompressed_mint_bump,
freeze_authority: None,
extensions: None,
rent_payment: 0, // Immediately compressible
rent_payment: 2, // Minimum required epochs of rent prepayment
write_top_up: 0,
};

Expand Down Expand Up @@ -342,7 +342,7 @@ async fn test_indexer_interface_scenarios() {
bump: compressed_mint_bump,
freeze_authority: Some(payer.pubkey()), // Add freeze authority for variety
extensions: None,
rent_payment: 0, // Immediately compressible
rent_payment: 2, // Minimum required epochs of rent prepayment
write_top_up: 0,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
///
/// ## Process Steps
/// 1. **State Validation**: Ensure mint is not already decompressed
/// 2. **Rent Payment Validation**: rent_payment must be 0 or >= 2
/// 2. **Rent Payment Validation**: rent_payment must be >= 2
/// 3. **Config Validation**: Validate CompressibleConfig account
/// 4. **Write Top-Up Validation**: write_top_up must not exceed max_top_up
/// 5. **Add Compressible Extension**: Add CompressionInfo to the compressed mint extensions
Expand All @@ -51,10 +51,13 @@ pub fn process_decompress_mint_action(
return Err(ErrorCode::CMintAlreadyExists.into());
}

// 2. rent_payment == 1 is rejected - epoch boundary edge case
if action.rent_payment == 1 {
msg!("Prefunding for exactly 1 epoch is not allowed. Use 0 or 2+ epochs.");
return Err(ErrorCode::OneEpochPrefundingNotAllowed.into());
// 2. CMint requires at least 2 epochs of rent prepayment (always compressible)
if action.rent_payment < 2 {
msg!(
"CMint requires at least 2 epochs of rent prepayment. Got {}.",
action.rent_payment
);
return Err(ErrorCode::InvalidRentPayment.into());
}

let executing = validated_accounts
Expand Down
Loading