SDK

Time Travel

Jump the Surfnet clock forward to an absolute slot, epoch, or Unix timestamp. Useful for tests that exercise vesting schedules, lockups, expiration windows, or epoch boundaries.

The Surfpool runtime exposes three time-travel helpers. Each one moves the local clock to an absolute target (not a relative offset) and returns the updated EpochInfo so tests can assert that the runtime actually reached the requested time.

HelperTargetReturns
time_travel_to_slot / timeTravelToSlotAbsolute slot numberEpochInfo with the new absolute_slot
time_travel_to_epoch / timeTravelToEpochAbsolute epoch numberEpochInfo with the new epoch
time_travel_to_timestamp / timeTravelToTimestampUnix timestamp in millisecondsEpochInfo reflecting the implied slot

Forward only

Time travel can only move the clock forward. Calling these methods with a target in the past is a no-op — the helper returns the current EpochInfo unchanged.

Jump To A Slot

use surfpool_sdk::Surfnet;

let surfnet = Surfnet::start().await?;
let cheats = surfnet.cheatcodes();

let info = cheats.time_travel_to_slot(1_000_000)?;
assert!(info.absolute_slot >= 1_000_000);

Jump To An Epoch

let info = cheats.time_travel_to_epoch(420)?;
assert_eq!(info.epoch, 420);

Jump To A Unix Timestamp

Pass the timestamp in milliseconds, not seconds. The runtime computes the closest slot at that timestamp.

// 2030-01-01T00:00:00Z
let info = cheats.time_travel_to_timestamp(1_893_456_000_000)?;

Common Patterns

Test A Lockup Or Vesting Window

The example below is a sketch — assertWithdrawFails and assertWithdrawSucceeds are placeholders for whatever client-side helpers your test suite uses to assert on RPC behavior.

import { Surfnet } from "@solana/surfpool";

const surfnet = Surfnet.start();
const beneficiary = Surfnet.newKeypair();

// 1. Set up a vesting account that unlocks at slot 1,000,000.
surfnet.setAccount(/* ...lockup program state... */);

// 2. Verify withdrawal fails before unlock.
await assertWithdrawFails(surfnet.rpcUrl, beneficiary);

// 3. Travel past the unlock slot.
surfnet.timeTravelToSlot(1_000_001);

// 4. Verify withdrawal succeeds.
await assertWithdrawSucceeds(surfnet.rpcUrl, beneficiary);

surfnet.stop();

Drive A Multi-Epoch Scenario

Intermediate slots are skipped

Time travel jumps directly to the target — moving from epoch 1 to epoch 5 skips the slots in between. If your program needs each intermediate epoch boundary to fire (for example, to credit per-epoch rewards), call time_travel_to_epoch once per epoch with any required transactions in between.

for epoch in 2..=5 {
    cheats.time_travel_to_epoch(epoch)?;
    surfnet.rpc_client().send_transaction(&claim_rewards_tx)?;
}

EpochInfo Shape

Both SDKs return an EpochInfo-shaped object with these fields:

FieldRustJS
Absolute slotabsolute_slot: u64absoluteSlot: number
Slot within epochslot_index: u64slotIndex: number
Slots per epochslots_in_epoch: u64slotsInEpoch: number
Epoch numberepoch: u64epoch: number
Block heightblock_height: u64blockHeight: number
Transaction counttransaction_count: Option<u64>transactionCount?: number

On this page