Viagem no Tempo

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.

AuxiliarAlvoRetorna
time_travel_to_slot / timeTravelToSlotNúmero de slot absolutoEpochInfo com o novo absolute_slot
time_travel_to_epoch / timeTravelToEpochNúmero de epoch absolutoEpochInfo com o novo epoch
time_travel_to_timestamp / timeTravelToTimestampTimestamp Unix em milissegundosEpochInfo 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:00Z
let 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. EpochInfo não possui campo de tempo. Leia o tempo Unix simulado do sysvar Clock: getAccountInfo em SysvarC1ock11111111111111111111111111111111 com codificação jsonParsed — 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 um clockCommand sempre 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:

CampoRustJS
Slot absolutoabsolute_slot: u64absoluteSlot: number
Slot dentro do epochslot_index: u64slotIndex: number
Slots por epochslots_in_epoch: u64slotsInEpoch: number
Número do epochepoch: u64epoch: number
Altura do blocoblock_height: u64blockHeight: number
Contagem de transaçõestransaction_count: Option<u64>transactionCount?: number

Is this page helpful?

Índice

Editar Página
© 2026 Fundação Solana. Todos os direitos reservados.