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 error

TypeScript 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

VariableRequiredDescription
HELIUS_API_KEYHelius RPC key for mainnet
REPLAY_BIND_ADDRListen address (default: 0.0.0.0:8787)
REPLAY_SESSION_TTL_SECSSession expiry in seconds (default: 3600)
REPLAY_MAX_SESSIONSMax concurrent sessions (default: 100)
RUST_LOGLog 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

CrateRole
replay-coreEngine: fetch, reconstruct, execute, IDL decode, fork sessions
replay-apiAxum HTTP server + session store + rate limiting
replay-cliCLI binary with spinner, CPI table, inspect subcommand
replay-sdkStable Rust SDK (ReplayClient, Session, replay_historical)

Key design decisions

  • LiteSVM sandboxFull SVM execution without a validator — fast enough for interactive use (~200ms for most txs)
  • Helius getTransaction v0Provides account data at the exact slot, so we get historical state without archival node access
  • Fork sessionsSnapshots are cheap (clone the LiteSVM state); sessions expire after 1h to cap memory
  • IDL bundlesJupiter, Whirlpool, Drift, and Kamino IDLs are bundled at compile time — no runtime IDL fetch needed
  • CorsLayer outermosttower-http CORS middleware must wrap GovernorLayer so OPTIONS preflight requests aren't rate-limited
MIT licensed · Built for Colosseum Frontier 2026GitHub ↗