ランタイムイベント

Surfnet ランタイムは、トランザクション処理、slot ティック、アカウントストリーミング、タイムトラベル、およびエラーをカバーする構造化イベントのストリームを出力します。SDK はこのストリームを公開しているため、テストはログを解析することなくランタイムの動作をアサートできます。

Rust: チャンネルレシーバー

Surfnet::events() は、ランタイムが発行する crossbeam_channel::Receiver<SimnetEvent> への参照を返します。try_iter()(ノンブロッキング)でドレインするか、recv() / recv_timeout() で個々のイベントを読み取ります。

use surfpool_sdk::{SimnetEvent, Surfnet};
let surfnet = Surfnet::start().await?;
// Drain whatever has accumulated so far.
for event in surfnet.events().try_iter() {
match event {
SimnetEvent::TransactionProcessed(_ts, metadata, error) => {
println!("tx {} -> {:?}", metadata.signature, metadata.logs);
if let Some(err) = error {
eprintln!(" failed: {err:?}");
}
}
SimnetEvent::ErrorLog(_ts, message) => {
eprintln!("runtime error: {message}");
}
other => {
println!("event: {other:?}");
}
}
}

レシーバーは共有されています

バックグラウンドタスクを生成してイベントを消費する場合、メインスレッドから try_iter を呼び出さないでください。イベントは2つのコンシューマー間で予測不能に分割されます。

JS: ドレインメソッド

JS バインディングはイベントを内部的にバッファリングし、drainEvents を通じて公開します。各呼び出しは前回の呼び出し以降に蓄積されたものを返し、バッファをクリアします。

import { Surfnet } from "@solana/surfpool";
const surfnet = Surfnet.start();
try {
// ... do test setup that triggers events ...
const events = surfnet.drainEvents();
for (const event of events) {
switch (event.kind) {
case "transactionProcessed":
console.log("tx", event.transactionSignature, event.logs);
break;
case "errorLog":
console.error("runtime error:", event.message);
break;
default:
console.log(event.kind, event.message ?? "");
}
}
} finally {
surfnet.stop();
}

バッファに上限なし

drainEvents を一度も呼び出さない場合、バッファはインスタンスの存続期間中増大し続けます。短いテストであれば問題ありませんが、メモリが膨張しないよう、長時間実行されるフィクスチャでは定期的に呼び出してください。

SimnetEventValue の形状 (JS)

JS のイベント型は、kind ディスクリミネーターとオプションフィールドを持つフラットなオブジェクトです。すべてのバリアントですべてのフィールドが設定されるわけではありません。kind に基づいてデストラクチャリングしてください。

interface SimnetEventValue {
kind: string; // discriminator, e.g. "TransactionProcessed"
message?: string; // human-readable message
timestamp?: string; // ISO timestamp
// startup
initialTransactionCount?: number;
clock?: ClockValue;
// slot / clock
epochInfo?: EpochInfoValue;
clockCommand?: string;
slotIntervalMs?: number;
// accounts
accountPubkey?: string;
// transactions
transactionSignature?: string;
logs?: string[];
computeUnitsConsumed?: number;
fee?: number;
// errors
errorMessage?: string;
// misc
tag?: string;
profileKey?: string;
profileSlot?: number;
runbookId?: string;
runbookErrors?: string[];
}

イベントの種類

JS 側の種類文字列はキャメルケースで、対応する Rust バリアントはパスカルケースを使用します。これらの正確な文字列でマッチしてください。バージョン間で大文字小文字が区別され、安定しています。

JS kindRust バリアント発行タイミング
readySimnetEvent::Readyランタイムの起動完了時。initialTransactionCount を含む。
connectedSimnetEvent::Connected上流のリモート RPC に接続済み。message(URL)を含む。
abortedSimnetEvent::Aborted起動が中断された。message(理由)を含む。
shutdownSimnetEvent::Shutdownグレースフルシャットダウンが開始される。
systemClockUpdatedSimnetEvent::SystemClockUpdatedシステムクロックが進んだ。clock を含む。
clockUpdateSimnetEvent::ClockUpdateクロックコマンド(一時停止/再開/インターバル更新)が実行された。clockCommand と任意で slotIntervalMs を含む。
epochInfoUpdateSimnetEvent::EpochInfoUpdate新しい epoch 情報スナップショットが利用可能になった。epochInfo を含む。
blockHashExpiredSimnetEvent::BlockHashExpired使用中だったブロックハッシュが期限切れになった。
transactionReceivedSimnetEvent::TransactionReceivedトランザクションがキューに追加された。timestamptransactionSignature を含む。
transactionProcessedSimnetEvent::TransactionProcessedトランザクションが実行された。timestamptransactionSignaturelogscomputeUnitsConsumedfee を含み、失敗時は errorMessage も含む。
accountUpdateSimnetEvent::AccountUpdateアカウントが変更された。ストリーミングされたアカウントおよびチートコードによる変更を含む。timestampaccountPubkey を含む。
infoLog / warnLog / errorLog / debugLogSimnetEvent::InfoLog / WarnLog / ErrorLog / DebugLogランタイムがログ行を出力した。timestampmessage を含む。
pluginLoadedSimnetEvent::PluginLoadedGeyser プラグインがロードされた。message(プラグイン名)を含む。
taggedProfileSimnetEvent::TaggedProfileプロファイラーの結果にタグが付けられた。tagprofileKeyprofileSlotlogscomputeUnitsConsumed を含む。
runbookStarted / runbookCompletedSimnetEvent::RunbookStarted / RunbookCompletedランブックが開始または完了した。runbookId と(完了時)任意の runbookErrors を含む。

アサーションパターン

トランザクションの着地を確認する

import { Surfnet } from "@solana/surfpool";
const surfnet = Surfnet.start();
try {
const sig = await sendTransaction(surfnet.rpcUrl /* ... */);
// ... wait for confirmation via RPC ...
const landed = surfnet
.drainEvents()
.some(
(e) => e.kind === "transactionProcessed" && e.transactionSignature === sig
);
console.assert(landed, "expected transaction to land");
} finally {
surfnet.stop();
}

ランタイムエラーが発生していないことを確認する

use surfpool_sdk::{SimnetEvent, Surfnet};
let mut surfnet = Surfnet::start().await?;
// ... run the test ...
let errors: Vec<_> = surfnet
.events()
.try_iter()
.filter(|e| matches!(e, SimnetEvent::ErrorLog(_, _)))
.collect();
assert!(errors.is_empty(), "runtime errors: {errors:?}");
surfnet.stop()?;

イベントがログ解析より優れている理由

stdoutよりもイベントストリームを優先する

ランタイムはtracingを通じて構造化イベントを出力します。これをstdoutからキャプチャして解析したくなるかもしれませんが、そうしないでください。構造化イベントストリームには次の利点があります:

  • ログレベルの変更に影響されない — イベントはRUST_LOGに関係なく出力されます。
  • 型付きペイロードを持つ — 文字列の解析も、バージョン間のフォーマットのずれもありません。
  • 安定したイベントの種類を持つ — ログ行は人間向けにフォーマットされており自由に変更されますが、イベントの種類はSDKの仕様の一部です。

Is this page helpful?

© 2026 Solana Foundation. 無断転載を禁じます。