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 functionalityUse 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 | Warp through multiple epochs |
| Time-locked withdrawals | Warp past unlock time |
| 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 time - Read Clock: Use
getClock()to get current slot, epoch, timestamp - Epoch Schedule: Use
getEpochSchedule()to understand slot/epoch mapping - Test Pattern: Test before and after time boundaries