LiteSVM Docs
Examples

Time-Based Testing

Test time-dependent logic with LiteSVM clock manipulation

Time-Based Testing Example

Demonstrates using warpToSlot to test time-dependent program logic.

Basic Clock Manipulation

import { createEmptyClient } from '@solana/kit';
import { litesvm } from '@solana/kit-plugins';

const client = createEmptyClient().use(litesvm());

// Enable sysvars for clock access
client.svm.withSysvars();

// Check initial clock
const initialClock = client.svm.getClock();
console.log('Initial state:');
console.log('  Slot:', initialClock.slot);
console.log('  Epoch:', initialClock.epoch);
console.log('  Unix timestamp:', initialClock.unixTimestamp);

// Warp to slot 1000
client.svm.warpToSlot(1000n);

const afterWarp = client.svm.getClock();
console.log('\nAfter warpToSlot(1000):');
console.log('  Slot:', afterWarp.slot);
console.log('  Epoch:', afterWarp.epoch);

// Warp further
client.svm.warpToSlot(100000n);

const afterWarp2 = client.svm.getClock();
console.log('\nAfter warpToSlot(100000):');
console.log('  Slot:', afterWarp2.slot);
console.log('  Epoch:', afterWarp2.epoch);

Testing Time-Locked Logic

Example pattern for testing time-locked functionality:

import { address, createEmptyClient, lamports } from '@solana/kit';
import { litesvm, payerFromFile } from '@solana/kit-plugins';

const client = createEmptyClient()
    .use(litesvm())
    .use(payerFromFile('path/to/keypair.json'));

client.svm
    .withSigverify(false)
    .withBlockhashCheck(false)
    .withSysvars();

// Fund payer
client.svm.airdrop(client.payer.address, lamports(10_000_000_000n));

// Set up a time-locked vault account
// Structure: [u8 discriminator, u64 unlock_slot, u64 amount]
const unlockSlot = 10000n;
const lockedAmount = 5_000_000_000n;

const vaultData = new Uint8Array(1 + 8 + 8);
const view = new DataView(vaultData.buffer);
vaultData[0] = 0x01; // discriminator
view.setBigUint64(1, unlockSlot, true); // unlock_slot
view.setBigUint64(9, lockedAmount, true); // amount

const programId = address('TimeLockedVault11111111111111111111111'); // replace or generateKeyPairSigner()
const vaultAddress = address('Vault111111111111111111111111111'); // replace or generateKeyPairSigner()
const minBalance = client.svm.minimumBalanceForRentExemption(BigInt(vaultData.length));

client.svm.setAccount({
    address: vaultAddress,
    data: vaultData,
    executable: false,
    lamports: lamports(minBalance + lockedAmount),
    programAddress: programId,
    space: BigInt(vaultData.length),
});

console.log('Vault created with unlock slot:', unlockSlot);
console.log('Locked amount:', Number(lockedAmount) / 1e9, 'SOL');

// Test 1: Try to withdraw before unlock (should fail)
console.log('\n--- Test 1: Before unlock ---');
const currentClock = client.svm.getClock();
console.log('Current slot:', currentClock.slot);

// In a real test, you would:
// 1. Build a withdraw instruction
// 2. Send it and expect it to fail
console.log('Withdrawal attempt: EXPECTED TO FAIL (too early)');

// Test 2: Warp to after unlock slot
console.log('\n--- Test 2: After unlock ---');
client.svm.warpToSlot(unlockSlot + 100n);

const newClock = client.svm.getClock();
console.log('Current slot:', newClock.slot);

// In a real test:
// 1. Build a withdraw instruction
// 2. Send it and expect it to succeed
console.log('Withdrawal attempt: EXPECTED TO SUCCEED (after unlock)');

// Verify vault state
const updatedVault = client.svm.getAccount(vaultAddress);
if (updatedVault.exists) {
    console.log('\nVault balance:', updatedVault.lamports);
}

Testing Epoch-Based Logic

import { createEmptyClient } from '@solana/kit';
import { litesvm } from '@solana/kit-plugins';

const client = createEmptyClient().use(litesvm());
client.svm.withSysvars();

// Get epoch schedule to understand slot/epoch relationship
const epochSchedule = client.svm.getEpochSchedule();
console.log('Slots per epoch:', epochSchedule.slotsPerEpoch);

// Start in epoch 0
let clock = client.svm.getClock();
console.log('\nInitial - Slot:', clock.slot, 'Epoch:', clock.epoch);

// Warp to epoch 1
const epoch1Start = epochSchedule.slotsPerEpoch;
client.svm.warpToSlot(epoch1Start);

clock = client.svm.getClock();
console.log('After warp - Slot:', clock.slot, 'Epoch:', clock.epoch);

// Warp to epoch 5
const epoch5Start = epochSchedule.slotsPerEpoch * 5n;
client.svm.warpToSlot(epoch5Start);

clock = client.svm.getClock();
console.log('Epoch 5 - Slot:', clock.slot, 'Epoch:', clock.epoch);

// Use this for testing epoch-based reward distribution,
// staking logic, or any epoch-dependent functionality

Use Cases

Time manipulation is useful for testing:

ScenarioApproach
Token vestingWarp past vesting cliff/milestones
Auction endingsWarp past auction end slot
Staking rewardsWarp through multiple epochs
Time-locked withdrawalsWarp past unlock time
Rate limitingWarp between allowed intervals
Expiring ordersWarp past order expiration

Remember that warpToSlot only changes the slot. The unix timestamp may not update proportionally depending on the SVM implementation.

Key Points

  1. Enable Sysvars: Call withSysvars() before using clock methods
  2. Warp Forward: Use warpToSlot(slot) to advance time
  3. Read Clock: Use getClock() to get current slot, epoch, timestamp
  4. Epoch Schedule: Use getEpochSchedule() to understand slot/epoch mapping
  5. Test Pattern: Test before and after time boundaries