Zeitreise

Die Surfpool-Laufzeitumgebung stellt drei Zeitreise-Hilfsfunktionen bereit. Jede bewegt die lokale Uhr zu einem absoluten Zielwert (kein relativer Offset) und gibt den aktualisierten EpochInfo zurück, damit Tests bestätigen können, dass die Laufzeitumgebung die angeforderte Zeit tatsächlich erreicht hat.

HelperZielRückgabe
time_travel_to_slot / timeTravelToSlotAbsolute slot-NummerEpochInfo mit dem neuen absolute_slot
time_travel_to_epoch / timeTravelToEpochAbsolute epoch-NummerEpochInfo mit dem neuen epoch
time_travel_to_timestamp / timeTravelToTimestampUnix-Zeitstempel in MillisekundenEpochInfo mit dem entsprechenden slot

Nur vorwärts

Die Zeitreise kann die Uhr nur vorwärts bewegen. Ein Ziel in der Vergangenheit wird abgelehnt und nicht ignoriert — die Rust-Helfer geben ein Err zurück und die JS- Helfer werfen eine Ausnahme. Die Fehlermeldung benennt die Einheit, die rückwärts ging, zum Beispiel Cannot travel to past slot: target=1000, current=2000.

Zu einem Slot springen

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

Zu einer Epoch springen

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

Zu einem Unix-Zeitstempel springen

Übergib den Zeitstempel in Millisekunden seit der Unix-Epoche — dieselbe Einheit wie JavaScripts Date.now() — nicht in Sekunden. Die Laufzeit berechnet den nächstgelegenen slot zu diesem Zeitstempel.

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

Die Uhr gibt Sekunden an, keine Millisekunden

Jeder Zeitstempel, den du von der Laufzeit zurückliest, ist in Sekunden angegeben — der unix_timestamp des On-Chain-Clock-Sysvars sowie der ClockValue im systemClockUpdated-Event. Wird einer dieser Werte direkt an einen Zeitreise-Helfer übergeben, wird ein Zeitpunkt Anfang 1970 aufgelöst, sodass der Aufruf als vergangenes Ziel abgelehnt wird. Vorher mit 1000 multiplizieren.

// WRONG — `unixTimestamp` is in seconds, so this is a past target.
surfnet.timeTravelToTimestamp(clock.unixTimestamp + 3600);
// RIGHT — convert to milliseconds.
surfnet.timeTravelToTimestamp((clock.unixTimestamp + 3600) * 1000);

Die Uhr anhalten und fortsetzen

Die Zeitreise springt zu einem Ziel; das Anhalten stoppt das Fortschreiten der Uhr vollständig. surfnet_pauseClock hält die slot-Produktion und den Zeitverlauf an, bis surfnet_resumeClock ausgeführt wird — verwende es, wenn ein Test slot und Zeitstempel über mehrere Assertions hinweg stabil halten muss.

Weder das Rust-SDK noch die Surfnet-Klasse in @solana/surfpool kapselt diese beiden Cheatcodes. Rufe sie über das Kit-Plugin oder direkt über JSON-RPC auf.

const paused = await client.cheatcodes.pauseClock().send();
// Nothing advances until the clock is resumed, so this is the only thing that
// moves the slot.
await client.cheatcodes
.timeTravel({ absoluteSlot: paused.absoluteSlot + 1_000n })
.send();
await client.cheatcodes.resumeClock().send();

Beide Cheatcodes geben ein EpochInfo zurück — den Uhr-Zustand zum Zeitpunkt des Anhaltens und nach dem Fortsetzen. Beachte die zwei Informationen, die diese Struktur nicht enthält:

  • Kein Zeitstempel. EpochInfo hat kein Zeitfeld. Lies die simulierte Unix-Zeit stattdessen aus dem Clock-Sysvar aus: getAccountInfo auf SysvarC1ock11111111111111111111111111111111 mit jsonParsed-Kodierung — erneut in Sekunden.
  • Kein Paused-Flag. Keine RPC-Methode meldet, ob die Uhr derzeit angehalten ist. Über die SDKs kannst du das clockUpdate-Event beobachten, das mit einem clockCommand ausgelöst wird, wenn ein Pause-, Fortsetze- oder Intervall-Änderungsbefehl ausgeführt wird. Andernfalls verfolge den Zustand in deinem Test.

Häufige Muster

Sperrzeit oder Vesting-Fenster testen

Das folgende Beispiel ist eine Skizze — assertWithdrawFails und assertWithdrawSucceeds sind Platzhalter für beliebige clientseitige Hilfsfunktionen, die deine Test-Suite verwendet, um das RPC-Verhalten zu prüfen.

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

Ein Multi-Epoch-Szenario steuern

Zwischenliegende slots werden übersprungen

Zeitreisen springen direkt zum Ziel — ein Sprung von epoch 1 zu epoch 5 überspringt die dazwischenliegenden slots. Wenn Ihr Programm jede zwischenliegende epoch-Grenze auslösen muss (zum Beispiel, um epochenbasierte Belohnungen gutzuschreiben), rufen Sie time_travel_to_epoch einmal pro epoch auf und fügen Sie die erforderlichen Transaktionen dazwischen ein.

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

EpochInfo-Struktur

Beide SDKs geben ein EpochInfo-Objekt mit folgenden Feldern zurück:

FeldRustJS
Absoluter slotabsolute_slot: u64absoluteSlot: number
slot innerhalb der epochslot_index: u64slotIndex: number
slots pro epochslots_in_epoch: u64slotsInEpoch: number
epoch-Nummerepoch: u64epoch: number
Blockhöheblock_height: u64blockHeight: number
Transaktionsanzahltransaction_count: Option<u64>transactionCount?: number

Is this page helpful?

Inhaltsverzeichnis

Seite bearbeiten
© 2026 Solana Foundation. Alle Rechte vorbehalten.