O runtime do Surfpool expõe três auxiliares de viagem no tempo. Cada um move o
relógio local para um alvo absoluto (não um deslocamento relativo) e retorna o
EpochInfo atualizado, para que os testes possam verificar que o runtime
efetivamente atingiu o tempo solicitado.
| Auxiliar | Alvo | Retorna |
|---|---|---|
time_travel_to_slot / timeTravelToSlot | Número de slot absoluto | EpochInfo com o novo absolute_slot |
time_travel_to_epoch / timeTravelToEpoch | Número de epoch absoluto | EpochInfo com o novo epoch |
time_travel_to_timestamp / timeTravelToTimestamp | Timestamp Unix em milissegundos | EpochInfo refletindo o slot implícito |
Somente para frente
A viagem no tempo só pode mover o relógio para frente. Um alvo no passado é
rejeitado em vez de ignorado — os helpers Rust retornam um Err e os helpers JS
lançam uma exceção. A mensagem indica qual unidade retrocedeu, por exemplo
Cannot travel to past slot: target=1000, current=2000.
Saltar Para Um 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);
Saltar Para Um Epoch
let info = cheats.time_travel_to_epoch(420)?;assert_eq!(info.epoch, 420);
Saltar Para Um Timestamp Unix
Passe o timestamp em milissegundos desde o Unix epoch — a mesma unidade que
JavaScript's Date.now() — não segundos. O runtime calcula o slot mais próximo
nesse timestamp.
// 2030-01-01T00:00:00Zlet info = cheats.time_travel_to_timestamp(1_893_456_000_000)?;
O relógio reporta segundos, não milissegundos
Cada timestamp que você lê de volta do runtime está em segundos — o
unix_timestamp do sysvar Clock on-chain, e o
ClockValue transportado pelo
evento systemClockUpdated. Alimentar um desses valores diretamente em
um helper de viagem no tempo resulta em um momento no início de 1970, portanto a chamada é
rejeitada como alvo no passado. Multiplique por 1000 primeiro.
// 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);
Pausar e Retomar o Relógio
A viagem no tempo salta para um alvo; pausar impede o relógio de avançar.
surfnet_pauseClock interrompe a produção de slots e o progresso do tempo até que
surfnet_resumeClock seja executado — use-o quando um teste precisa que o slot e o
timestamp permaneçam estáticos em várias asserções.
Nem o SDK Rust nem a classe Surfnet em @solana/surfpool encapsulam esses
dois cheatcodes. Chame-os através do
plugin Kit, ou diretamente via JSON-RPC.
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();
Ambos os cheatcodes retornam um EpochInfo — o estado do relógio no momento da
pausa e após a retomada. Observe as duas coisas que essa estrutura não carrega:
- Sem timestamp.
EpochInfonão possui campo de tempo. Leia o tempo Unix simulado do sysvarClock:getAccountInfoemSysvarC1ock11111111111111111111111111111111com codificaçãojsonParsed— novamente, em segundos. - Sem flag de pausa. Nenhum método RPC informa se o relógio está atualmente
pausado. Pelos SDKs, monitore o
evento
clockUpdate, que é disparado com umclockCommandsempre que uma pausa, retomada ou alteração de intervalo é executada. Caso contrário, rastreie o estado no seu teste.
Padrões Comuns
Testar um Bloqueio ou Janela de Vesting
O exemplo abaixo é um esboço — assertWithdrawFails e assertWithdrawSucceeds
são marcadores de posição para os auxiliares do lado do cliente que sua suíte de
testes utiliza para verificar o comportamento do RPC.
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();
Simular um Cenário de Múltiplos Epochs
Slots intermediários são ignorados
O salto no tempo vai diretamente para o destino — mover do epoch 1 para o
epoch 5 ignora os slots intermediários. Se o seu programa precisar que cada
limite de epoch intermediário seja acionado (por exemplo, para creditar
recompensas por epoch), chame time_travel_to_epoch uma vez por epoch com as
transações necessárias entre elas.
for epoch in 2..=5 {cheats.time_travel_to_epoch(epoch)?;surfnet.rpc_client().send_transaction(&claim_rewards_tx)?;}
Estrutura do EpochInfo
Ambos os SDKs retornam um objeto do tipo EpochInfo com os seguintes campos:
| Campo | Rust | JS |
|---|---|---|
| Slot absoluto | absolute_slot: u64 | absoluteSlot: number |
| Slot dentro do epoch | slot_index: u64 | slotIndex: number |
| Slots por epoch | slots_in_epoch: u64 | slotsInEpoch: number |
| Número do epoch | epoch: u64 | epoch: number |
| Altura do bloco | block_height: u64 | blockHeight: number |
| Contagem de transações | transaction_count: Option<u64> | transactionCount?: number |
Is this page helpful?