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 { createClient, generateKeyPairSigner } from '@solana/kit';
import { litesvm } from '@solana/kit-plugin-litesvm';
import { signer } from '@solana/kit-plugin-signer';

const mySigner = await generateKeyPairSigner();
const client = createClient().use(signer(mySigner)).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 { createClient, address, generateKeyPairSigner, lamports } from '@solana/kit';
import { litesvm } from '@solana/kit-plugin-litesvm';
import { signer } from '@solana/kit-plugin-signer';

const mySigner = await generateKeyPairSigner();
const client = createClient().use(signer(mySigner)).use(litesvm());

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);
}

Reading the Epoch Schedule

You can read the epoch schedule to understand the slot/epoch relationship on the cluster:

import { createClient, generateKeyPairSigner } from '@solana/kit';
import { litesvm } from '@solana/kit-plugin-litesvm';
import { signer } from '@solana/kit-plugin-signer';

const mySigner = await generateKeyPairSigner();
const client = createClient().use(signer(mySigner)).use(litesvm());
client.svm.withSysvars();

const epochSchedule = client.svm.getEpochSchedule();
console.log('Slots per epoch:', epochSchedule.slotsPerEpoch);
console.log('First normal epoch:', epochSchedule.firstNormalEpoch);
console.log('First normal slot:', epochSchedule.firstNormalSlot);

warpToSlot advances the clock's slot field but does not automatically update epoch or unixTimestamp. Use setClock for full control over clock fields.

Manual Clock Manipulation

When your program reads epoch or unix_timestamp from the Clock sysvar, use setClock to set those fields directly:

import { createClient, generateKeyPairSigner } from '@solana/kit';
import { litesvm } from '@solana/kit-plugin-litesvm';
import { signer } from '@solana/kit-plugin-signer';

const mySigner = await generateKeyPairSigner();
const client = createClient().use(signer(mySigner)).use(litesvm());
client.svm.withSysvars();

// Get clock, mutate, write back
const clock = client.svm.getClock();
clock.slot = 50000n;
clock.epoch = 5n;
clock.unixTimestamp = 1700000000n;
client.svm.setClock(clock);

const after = client.svm.getClock();
console.log('Slot:', after.slot);           // 50000n
console.log('Epoch:', after.epoch);         // 5n
console.log('Timestamp:', after.unixTimestamp); // 1700000000n

This mirrors the Rust set_sysvar(&clock) pattern. Use warpToSlot for simple slot advancement, setClock when you need epoch or timestamp control.

Use Cases

Time manipulation is useful for testing:

ScenarioApproach
Token vestingWarp past vesting cliff/milestones
Auction endingsWarp past auction end slot
Staking rewardsUse setClock to set epoch
Time-locked withdrawalsWarp past unlock slot
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 the slot
  3. Full Control: Use setClock when you need to set epoch, unixTimestamp, or other clock fields
  4. Read Clock: Use getClock() to read current slot, epoch, timestamp
  5. Test Pattern: Test before and after time boundaries