Overview
Replay is a time-travel debugger for Solana transactions. Paste any mainnet signature, and Replay fetches the exact account state that existed at that slot, reconstructs it in a local LiteSVM sandbox, and re-executes the transaction. From there you can fork the state, mutate any field, re-run, and see a side-by-side diff.
Fetch
Pull tx + all accounts at exact slot via Helius
Reconstruct
Hydrate a LiteSVM sandbox with historical state
Fork
Snapshot sandbox into a mutable session
Mutate
IDL-decoded fields or raw byte splices
Re-run
Execute the mutated transaction
Diff
Result changed? CU delta? Which accounts?
API Reference
Base URL: https://replay-y4wq.onrender.com. All endpoints accept and return JSON. Rate limit: 20 req/min per IP.
Health
GET /health
# → {"status":"ok","version":"0.1.0"}
GET /version
# → {"name":"replay-api","version":"0.1.0"}POST /replay
One-shot replay. Returns a full Trace.
POST /replay
Content-Type: application/json
{"signature": "5xYourSigHere..."}
# Response: Trace
{
"frames": [...], // CPI call tree
"account_deltas": [...], // accounts that changed
"total_cu": 145823, // compute units consumed
"log_divergence": null // null if logs match mainnet
}POST /fork
Fork a transaction into a mutable session.
POST /fork
{"signature": "5xYourSigHere..."}
# → {"session_id": "abc123"}POST /session/:id/mutate
Apply a mutation to the forked state. Three mutation types:
# 1. IDL field (dot-path into decoded account struct)
{"pubkey": "...", "mutation": {"type":"field","path":"feeRate","new_value":9999}}
# 2. Raw bytes splice
{"pubkey": "...", "mutation": {"type":"raw_bytes","offset":8,"bytes":"0f270000","extend":false}}
# 3. Lamports
{"pubkey": "...", "mutation": {"type":"lamports","new_value":1000000000}}POST /session/:id/execute
POST /session/abc123/execute # → Trace (same shape as /replay)
GET /session/:id/diff
GET /session/abc123/diff
{
"result_changed": true,
"total_cu_delta": -12345,
"changed_accounts": ["pubkeyA", "pubkeyB"],
"baseline": { ...Trace },
"latest": { ...Trace }
}Error shape
{
"error": {
"code": "tx_not_found",
"message": "transaction not found on-chain"
}
}
HTTP 400 — invalid signature / malformed body
HTTP 404 — tx or session not found
HTTP 422 — state reconstruction failure / bad mutation path
HTTP 429 — rate limit (20 req/min per IP)
HTTP 502 — Helius RPC errorTypeScript SDK
npm install @zaxcoraider/replay-sdk
One-shot replay
import { ReplayClient } from '@zaxcoraider/replay-sdk';
const client = new ReplayClient({ apiUrl: 'https://replay-y4wq.onrender.com' });
const trace = await client.replay('5xYourSigHere...');
console.log('CU:', trace.total_cu);
console.log('Frames:', trace.frames.length);Fork → mutate → re-run → diff
const session = await client.fork('5xYourSigHere...');
// Mutate an IDL-decoded field
await session.mutate(poolPubkey, {
type: 'field',
path: 'feeRate',
new_value: 9999,
});
const newTrace = await session.execute();
const diff = await session.diff();
console.log('Result changed:', diff.result_changed);
console.log('CU delta:', diff.total_cu_delta);CI regression helper
import { replayHistorical, loadSignatures } from '@zaxcoraider/replay-sdk/testing';
const report = await replayHistorical({
apiUrl: 'https://replay-y4wq.onrender.com',
signatures: await loadSignatures('./fixtures/historical-swaps.txt'),
});
if (report.failures.length > 0) {
throw new Error(`Historical replay regressed: ${report.failures.length} failures`);
}Types
interface Trace {
frames: CpiFrame[];
account_deltas: AccountDelta[];
total_cu: number;
mainnet_result: TxResult;
replay_result: TxResult;
log_divergence: LogDivergence | null;
}
interface CpiFrame {
program_id: string;
program_name: string | null;
depth: number;
cu_used: number;
inner: CpiFrame[];
}
interface TraceDiff {
result_changed: boolean;
total_cu_delta: number;
changed_accounts: string[];
baseline: Trace;
latest: Trace;
}Rust SDK
# Cargo.toml [dependencies] replay-sdk = "0.1"
One-shot replay
use replay_sdk::{ReplayClient, Error};
#[tokio::main]
async fn main() -> Result<(), Error> {
// Reads HELIUS_API_KEY or REPLAY_RPC_URL from env
let client = ReplayClient::from_env()?;
let trace = client.replay("5xYourSigHere...").await?;
println!("CU: {}", trace.total_cu);
Ok(())
}Fork → mutate → re-run → diff
let mut session = client.fork("5xYourSigHere...").await?;
session.mutate_field(
"whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc".parse().unwrap(),
"feeRate",
serde_json::json!(9999),
)?;
let _trace = session.execute().await?;
let diff = session.diff().unwrap();
println!("Result changed: {}", diff.result_changed);Historical regression batch
let report = replay_sdk::replay_historical(&client, &[
"5xYourSigHere...",
"3aBanotherSig...",
]).await?;
if report.has_failures() {
eprintln!("{} signatures regressed", report.failures().count());
std::process::exit(1);
}CLI
cargo install replay-cli # Set your Helius key export HELIUS_API_KEY=your_key_here # Replay a transaction (full CPI trace + CU table) replay replay 5xYourSigHere... # Show log diff vs mainnet replay replay 5xYourSigHere... --diff-logs # Inspect a specific account (IDL-decoded) replay inspect 5xYourSigHere... --account <PUBKEY> # Fetch raw transaction JSON replay fetch 5xYourSigHere... --json
Example output
⠙ Fetching transaction… ⠹ Reconstructing state (42 accounts)… ⠸ Replaying… ✓ Replayed in 234ms · CU: 145,823 · Success ┌─────────────────────────────────────────────┬────────────┬──────┐ │ Program │ CU Used │ Depth│ ├─────────────────────────────────────────────┼────────────┼──────┤ │ JUP6Lkbzwr3WkFBkUNHkNVTvsB8GpfZwMnzRmRt7Yf │ 98,234 │ 0 │ │ whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uct │ 32,100 │ 1 │ │ TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ │ 4,512 │ 2 │ └─────────────────────────────────────────────┴────────────┴──────┘
Self-hosting
You need a Helius API key. Free tier is sufficient for development.
Docker (recommended)
# Clone git clone https://github.com/zaxcoraider/replay cd replay # Set env echo "HELIUS_API_KEY=your_key" > .env # Build + run docker build -t replay-api . docker run -p 8787:8787 --env-file .env replay-api # Verify curl http://localhost:8787/health
From source
cp .env.example .env # edit .env → add HELIUS_API_KEY # API cargo run -p replay-api # http://localhost:8787 # Web UI (separate terminal) cd web && pnpm install && pnpm dev # http://localhost:3000
Environment variables
| Variable | Required | Description |
|---|---|---|
| HELIUS_API_KEY | ✅ | Helius RPC key for mainnet |
| REPLAY_BIND_ADDR | — | Listen address (default: 0.0.0.0:8787) |
| REPLAY_SESSION_TTL_SECS | — | Session expiry in seconds (default: 3600) |
| REPLAY_MAX_SESSIONS | — | Max concurrent sessions (default: 100) |
| RUST_LOG | — | Log level (default: warn) |
Architecture
┌─────────────────────────────────────────────────────┐ │ Replay │ │ │ │ ┌──────────┐ ┌──────────────┐ ┌──────────┐ │ │ │ Web UI │───▶│ replay-api │───▶│ Helius │ │ │ │ (Next.js)│ │ (axum) │ │ RPC │ │ │ └──────────┘ └──────┬───────┘ └──────────┘ │ │ │ │ │ ┌──────▼───────┐ │ │ │ replay-core │ │ │ │ │ │ │ │ • fetch │ │ │ │ • reconstruct│ │ │ │ • LiteSVM │ │ │ │ • IDL decode │ │ │ │ • fork/diff │ │ │ └──────────────┘ │ └─────────────────────────────────────────────────────┘
Crate layout
| Crate | Role |
|---|---|
| replay-core | Engine: fetch, reconstruct, execute, IDL decode, fork sessions |
| replay-api | Axum HTTP server + session store + rate limiting |
| replay-cli | CLI binary with spinner, CPI table, inspect subcommand |
| replay-sdk | Stable Rust SDK (ReplayClient, Session, replay_historical) |
Key design decisions
- LiteSVM sandbox — Full SVM execution without a validator — fast enough for interactive use (~200ms for most txs)
- Helius getTransaction v0 — Provides account data at the exact slot, so we get historical state without archival node access
- Fork sessions — Snapshots are cheap (clone the LiteSVM state); sessions expire after 1h to cap memory
- IDL bundles — Jupiter, Whirlpool, Drift, and Kamino IDLs are bundled at compile time — no runtime IDL fetch needed
- CorsLayer outermost — tower-http CORS middleware must wrap GovernorLayer so OPTIONS preflight requests aren't rate-limited
MIT licensed · Built for Colosseum Frontier 2026GitHub ↗