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.
| Helper | Ziel | Rückgabe |
|---|---|---|
time_travel_to_slot / timeTravelToSlot | Absolute slot-Nummer | EpochInfo mit dem neuen absolute_slot |
time_travel_to_epoch / timeTravelToEpoch | Absolute epoch-Nummer | EpochInfo mit dem neuen epoch |
time_travel_to_timestamp / timeTravelToTimestamp | Unix-Zeitstempel in Millisekunden | EpochInfo 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:00Zlet 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.
EpochInfohat kein Zeitfeld. Lies die simulierte Unix-Zeit stattdessen aus demClock-Sysvar aus:getAccountInfoaufSysvarC1ock11111111111111111111111111111111mitjsonParsed-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 einemclockCommandausgelö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:
| Feld | Rust | JS |
|---|---|---|
| Absoluter slot | absolute_slot: u64 | absoluteSlot: number |
| slot innerhalb der epoch | slot_index: u64 | slotIndex: number |
| slots pro epoch | slots_in_epoch: u64 | slotsInEpoch: number |
| epoch-Nummer | epoch: u64 | epoch: number |
| Blockhöhe | block_height: u64 | blockHeight: number |
| Transaktionsanzahl | transaction_count: Option<u64> | transactionCount?: number |
Is this page helpful?