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); // 1700000000nThis 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:
| Scenario | Approach |
|---|---|
| Token vesting | Warp past vesting cliff/milestones |
| Auction endings | Warp past auction end slot |
| Staking rewards | Use setClock to set epoch |
| Time-locked withdrawals | Warp past unlock slot |
| Rate limiting | Warp between allowed intervals |
| Expiring orders | Warp past order expiration |
Remember that warpToSlot only changes the slot. The unix timestamp may not update proportionally depending on the SVM implementation.
Key Points
- Enable Sysvars: Call
withSysvars()before using clock methods - Warp Forward: Use
warpToSlot(slot)to advance the slot - Full Control: Use
setClockwhen you need to setepoch,unixTimestamp, or other clock fields - Read Clock: Use
getClock()to read current slot, epoch, timestamp - Test Pattern: Test before and after time boundaries