25 KiB
title, description
| title | description |
|---|---|
| Surfpool | A drop-in replacement for solana-test-validator with sub-second startup, automatic mainnet state cloning, transaction profiling, and a built-in web UI. |
Surfpool Reference
Contents
- What is Surfpool
- Installation
- Agent Usage (NO_DNA)
- Quick Start
- Embedded SDK (@solana/surfpool)
- When to Use Surfpool
- Migration from solana-test-validator
- Additional Capabilities
- CLI Reference
- Infrastructure as Code
- MCP Integration
- Cheatcodes Overview
- Scenarios Overview
- Common Agent Workflows
What is Surfpool
Surfpool (solana-foundation/surfpool, docs at solana.com/docs/tools, latest release v1.5.0) is a drop-in replacement for solana-test-validator built on LiteSVM. It provides a local Solana network (called a "surfnet") with sub-second startup, automatic mainnet state cloning, transaction profiling, and a built-in web UI (Studio).
Key differences from solana-test-validator:
- Instant startup — no genesis ledger to bootstrap; the SVM runs in-process.
- Mainnet state on demand — accounts are lazily fetched from a remote RPC and cached locally. No need to pre-clone accounts.
- Cheatcodes — 26
surfnet_*RPC methods to manipulate time, accounts, programs, and scenarios without restarting. - Transaction profiling — compute-unit estimation with before/after account snapshots.
- Scenario system — override protocol state (Pyth, Jupiter, Raydium, etc.) to simulate market conditions.
- Embedded SDK — run a full surfnet in-process from tests via
@solana/surfpool(TS) orsurfpool-sdk(Rust). - Infrastructure as Code — define deployment runbooks in
txtx.ymland auto-execute on start. - MCP server — expose surfnet operations as tool calls for AI agents.
Installation
curl -sL https://run.surfpool.run/ | bash
Other methods:
# From source (clone the repo first)
cargo surfpool-install
# Docker
docker run --rm -p 8899:8899 -p 8900:8900 -p 18488:18488 surfpool/surfpool
# Snap
snap install surfpool
Warning: Do NOT run
cargo install surfpool— the crates.io name is squatted by an unrelated crate. Thetxtx/tapsHomebrew tap is stale (pinned to v1.0.0); don't use it either.
Verify and self-update (v1.3.0+, SHA256-verified):
surfpool --version
surfpool update # flags: --yes, --version <v>
Agent Usage (NO_DNA)
When running surfpool commands as an agent, always prefix with NO_DNA=1. This disables TUI, interactive prompts, and enables verbose structured output:
NO_DNA=1 surfpool start
NO_DNA=1 surfpool start --watch
NO_DNA=1 surfpool run deployment --unsupervised --output-json ./outputs/
See no-dna.org for the full standard.
Quick Start
Anchor or Pinocchio Project
Start surfpool in the project root. It detects Anchor and Pinocchio projects (Pinocchio detection added in v1.4.0), scaffolds txtx runbooks (program names read from Anchor.toml), and deploys programs automatically:
cd my-project
surfpool start
Note: in Anchor 1.0+, anchor test and anchor localnet use surfpool as the default test runner.
Use --watch to auto-redeploy when .so files change in target/deploy/:
surfpool start --watch
For Anchor test suites, enable compatibility mode:
surfpool start --legacy-anchor-compatibility
Mainnet State Cloning
Surfpool forks mainnet by default — accounts are fetched lazily when accessed, with zero config:
surfpool start # mainnet fork (default)
surfpool start --network devnet # or devnet/testnet
Use a custom RPC for better rate limits:
surfpool start --rpc-url https://my-rpc-provider.com
Run fully offline (no remote fetching):
surfpool start --offline
CI Mode
Start with CI-optimized defaults (no TUI, no Studio, no profiling, no logs):
surfpool start --ci
Run as a background daemon (Linux only):
surfpool start --ci --daemon
Embedded SDK (@solana/surfpool)
Since v1.2.0, a full surfnet can be embedded in-process — no shelling out, no fixed ports. Ideal for integration test suites.
- TypeScript: npm package
@solana/surfpool(1.5.0; napi-rs native bindings for macOS x64/arm64, Linux x64 GNU). Ships a Kit plugin at the@solana/surfpool/kitsubpath. - Rust: crate
surfpool-sdk = "1.5.0"—Surfnet::builder()with options such asBlockProductionMode
With @solana/kit, use the plugin rather than the raw class — it boots the surfnet, installs a pre-funded payer and the RPC stack, and adds a typed cheatcodes RPC in one call:
import { createClient } from "@solana/kit";
import { surfpool } from "@solana/surfpool/kit";
const client = await createClient().use(surfpool()); // dynamic ports, pre-funded payer
await client.cheatcodes.timeTravel({ absoluteSlot: 1_000_000n }).send();
client.surfnet.stop(); // idempotent graceful shutdown — wire into afterAll()
The Surfnet class remains the entry point for non-Kit clients:
import { Surfnet } from "@solana/surfpool";
const surfnet = Surfnet.start(); // dynamic port, pre-funded payer, cheatcode helpers
console.log(surfnet.rpcUrl); // point any RPC client here
surfnet.stop();
See kit-plugin.md for the full plugin reference and testing.md for a vitest example.
When to Use Surfpool
| Criterion | surfpool | solana-test-validator | litesvm / bankrun |
|---|---|---|---|
| Startup time | Sub-second | 10-30 seconds | Sub-second |
| Architecture | In-process SVM (LiteSVM) | Full validator runtime | In-process SVM |
| RPC server | Full JSON-RPC on port 8899 | Full JSON-RPC on port 8899 | No RPC server (bankrun has limited BanksClient) |
| WebSocket support | Yes (port 8900) | Yes | No |
| Mainnet state | Lazy clone on first access | Manual --clone per account |
Manual account setup |
| Account manipulation | 26 cheatcode RPC methods | None (restart + --account files) |
Direct set_account() in-process |
| Time control | surfnet_timeTravel, pauseClock, resumeClock |
--slots-per-epoch, warp via CLI |
warp_to_slot() in-process |
| Transaction profiling | Built-in CU profiling with snapshots | None | None |
| Program hot-reload | --watch flag |
Restart required | Restart required |
| Web UI | Studio (port 18488) | None | None |
| Protocol scenarios | 8 built-in protocols | None | None |
| MCP server | Built-in (surfpool mcp) |
None | None |
| Geyser plugins | Supported | Supported | Not supported |
| CI mode | --ci flag |
Manual config | Native (no server needed) |
| Infrastructure as Code | txtx.yml runbooks | None | None |
| Offline mode | --offline flag |
Always offline | Always offline |
| Persistent state | --db flag (SQLite) |
Ledger directory | None |
Use surfpool for local development, integration testing, mainnet forking, and CI pipelines that need an RPC endpoint.
Use litesvm/bankrun for unit-level program tests that run in-process without an RPC server.
Use solana-test-validator only when specific validator runtime behavior is required that surfpool does not yet replicate (vote processing, leader schedule, etc.).
Decision Tree
- Unit test exercising a single instruction in isolation? Use litesvm (Rust) or bankrun (JS/Python).
- Need a full JSON-RPC endpoint? Use surfpool.
- Need mainnet account state (tokens, programs, oracles)? Use surfpool (lazy cloning).
- Need to manipulate time, accounts, or protocol state at runtime? Use surfpool (cheatcodes).
- Local dev environment with hot-reload? Use surfpool (
--watch). - CI pipeline needing RPC? Use
surfpool start --ci. - DeFi scenario simulation? Use surfpool (scenario system).
- Full validator fidelity? Use solana-test-validator.
Summary Table
| Use Case | Recommended Tool |
|---|---|
| Unit testing a single instruction | litesvm / bankrun |
| Integration testing with RPC | surfpool |
| Local development with hot-reload | surfpool |
| Mainnet fork testing | surfpool |
| CI pipeline (needs RPC) | surfpool (--ci) |
| CI pipeline (in-process only) | litesvm / bankrun |
| DeFi scenario simulation | surfpool |
| AI agent-assisted development | surfpool (MCP server) |
| Full validator fidelity testing | solana-test-validator |
Migration from solana-test-validator
Replace:
solana-test-validator \
--clone TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA \
--clone <account-1> \
--clone <account-2> \
--url https://api.mainnet-beta.solana.com \
--reset
With:
surfpool start
Accounts are cloned lazily — no need to specify them upfront.
Replace account file loading:
solana-test-validator --account <pubkey> ./account.json
With snapshot loading:
surfpool start --snapshot ./accounts.json
Or use cheatcodes at runtime:
curl -X POST http://127.0.0.1:8899 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"surfnet_setAccount","params":["<pubkey>",{"lamports":1000000000}]}'
From litesvm/bankrun (adding RPC layer)
If in-process tests need to be promoted to integration tests with an RPC endpoint, start surfpool alongside:
surfpool start --ci
Then point the test client to http://127.0.0.1:8899 instead of using the in-process bank. Or skip the daemon entirely and use the embedded SDK (see above).
Additional Capabilities
- Jito support (v1.3–1.4) — atomic bundle execution plus
getBundleStatusesandsimulateBundle, for testing bundle-dependent flows locally. - WebSocket subscriptions —
programSubscribe(v1.1) andslotUpdatesSubscribe(v1.4) on the WS port (8900), in addition to standard subscriptions. - Persistent state —
--db ./surfnet.sqlite --surfnet-id <id>persists surfnet state across restarts; combine withsurfnet_exportSnapshot+--snapshotfor portable fixtures. - Docker images — published per release (
surfpool/surfpool). - Prometheus metrics —
--metrics-enabledexposes an endpoint (default0.0.0.0:9000). - Studio — web UI at
localhost:18488with transaction diffs, CU profiling, and a universal faucet.
CLI Reference
surfpool start
Start a local Solana network (surfnet). Alias: surfpool simnet.
Network Configuration
| Flag | Short | Default | Env Var | Description |
|---|---|---|---|---|
--port |
-p |
8899 |
— | RPC port |
--ws-port |
-w |
8900 |
— | WebSocket port |
--host |
-o |
127.0.0.1 |
SURFPOOL_NETWORK_HOST |
Bind address |
--rpc-url |
-u |
— | SURFPOOL_DATASOURCE_RPC_URL |
Custom datasource RPC URL (conflicts with --network) |
--network |
-n |
— | — | Predefined network: mainnet, devnet, testnet (conflicts with --rpc-url) |
--offline |
— | false |
— | Start without a remote RPC client |
Block Production
| Flag | Short | Default | Description |
|---|---|---|---|
--slot-time |
-t |
400 |
Slot time in milliseconds |
--block-production-mode |
-b |
clock |
Block production mode: clock, transaction, manual |
Modes:
clock— advance slots at a fixed interval (default,400ms)transaction— advance a slot only when a transaction is receivedmanual— slots only advance via explicit RPC calls
Airdrops
| Flag | Short | Default | Description |
|---|---|---|---|
--airdrop |
-a |
— | Pubkey(s) to airdrop SOL to on start. Repeatable. |
--airdrop-amount |
-q |
10000000000000 |
Amount of lamports to airdrop (default ~10,000 SOL) |
--airdrop-keypair-path |
-k |
~/.config/solana/id.json |
Keypair file(s) to airdrop to. Repeatable. |
Deployment & Runbooks
| Flag | Short | Default | Description |
|---|---|---|---|
--manifest-file-path |
-m |
./txtx.yml |
Path to the runbook manifest |
--runbook |
-r |
deployment |
Runbook ID(s) to execute. Repeatable. |
--runbook-input |
-i |
— | JSON input file(s) for runbooks. Repeatable. |
--no-deploy |
— | false |
Disable auto deployment |
--yes |
-y |
false |
Skip runbook generation prompts |
--watch |
— | false |
Auto re-execute deployment on .so file changes in target/deploy/ |
Anchor Compatibility
| Flag | Short | Default | Description |
|---|---|---|---|
--legacy-anchor-compatibility |
— | false |
Apply Anchor test suite defaults |
--anchor-test-config-path |
— | — | Path(s) to Test.toml files. Repeatable. |
Studio
| Flag | Short | Default | Description |
|---|---|---|---|
--studio-port |
-s |
18488 |
Studio web UI port |
--no-studio |
— | false |
Disable Studio |
Profiling & Logging
| Flag | Short | Default | Description |
|---|---|---|---|
--disable-instruction-profiling |
— | false |
Disable instruction profiling |
--max-profiles |
-c |
200 |
Max transaction profiles to hold in memory |
--log-level |
-l |
info |
Log level: trace, debug, info, warn, error, none |
--log-path |
— | .surfpool/logs |
Log file directory |
--log-bytes-limit |
— | 10000 |
Max bytes in transaction logs (0 = unlimited) |
SVM Features
| Flag | Short | Default | Description |
|---|---|---|---|
--feature |
-f |
— | Enable specific SVM features. Repeatable. |
--disable-feature |
— | — | Disable specific SVM features. Repeatable. |
--features-all |
— | false |
Enable all SVM features (override mainnet defaults) |
By default, surfpool uses mainnet feature flags.
Plugins & Subgraphs
| Flag | Short | Default | Description |
|---|---|---|---|
--geyser-plugin-config |
-g |
— | Geyser plugin config file(s). Repeatable. |
--subgraph-db |
-d |
:memory: |
Subgraph database URL (SQLite or Postgres) |
Persistence & Snapshots
| Flag | Short | Default | Description |
|---|---|---|---|
--db |
— | — | Surfnet database URL for persistent state (:memory: or *.sqlite) |
--surfnet-id |
— | default |
Unique ID to isolate database storage across instances |
--snapshot |
— | — | JSON snapshot file(s) to preload accounts from. Repeatable. |
Telemetry
| Flag | Short | Default | Env Var | Description |
|---|---|---|---|---|
--metrics-enabled |
— | false |
SURFPOOL_METRICS_ENABLED |
Enable Prometheus metrics |
--metrics-addr |
— | 0.0.0.0:9000 |
SURFPOOL_METRICS_ADDR |
Prometheus endpoint address |
Process Control
| Flag | Short | Default | Description |
|---|---|---|---|
--no-tui |
— | false |
Stream logs instead of terminal UI |
--daemon |
— | false |
Run as background process (Linux only) |
--ci |
— | false |
CI mode (sets --no-tui, --no-studio, --disable-instruction-profiling, --log-level none) |
--skip-signature-verification |
— | false |
Skip signature verification for all transactions |
surfpool run
Execute a runbook from the manifest.
| Flag | Short | Default | Description |
|---|---|---|---|
--manifest-file-path |
-m |
./txtx.yml |
Path to the manifest |
--unsupervised |
-u |
false |
Execute without interactive supervision |
--browser |
-b |
false |
Supervise via browser UI |
--terminal |
-t |
false |
Supervise via terminal (coming soon) |
--output-json |
— | — | Output results as JSON. Optional directory path. |
--output |
— | — | Pick a specific output to stdout |
--explain |
— | false |
Explain execution plan without running |
--env |
— | — | Environment from txtx.yml |
--input |
— | — | Input file(s) for batch processing. Repeatable. |
--force |
-f |
false |
Execute even if cached state shows already executed |
--log-level |
-l |
info |
Log level |
--log-path |
— | .surfpool/logs |
Log directory |
Positional argument: <runbook> — runbook name or .tx file path.
# Execute interactively in browser
surfpool run deployment
# Execute without supervision, output JSON
surfpool run deployment --unsupervised --output-json ./outputs/
# Force re-execution
surfpool run deployment --unsupervised --force
surfpool ls
List runbooks in the current directory.
| Flag | Short | Default | Description |
|---|---|---|---|
--manifest-file-path |
-m |
./txtx.yml |
Path to the manifest |
surfpool mcp
Start the MCP (Model Context Protocol) server for AI agent integrations. No additional flags.
surfpool mcp
surfpool completions
Generate shell completion scripts. Alias: surfpool completion.
Positional argument: <shell> — bash, zsh, fish, elvish, powershell.
surfpool completions zsh
Environment Variables
| Variable | Description | Used By |
|---|---|---|
NO_DNA |
Non-human operator signal — disables TUI, prompts; enables verbose/structured output | All commands |
SURFPOOL_DATASOURCE_RPC_URL |
Default datasource RPC URL | --rpc-url |
SURFPOOL_NETWORK_HOST |
Override bind host | --host |
SURFPOOL_METRICS_ENABLED |
Enable Prometheus metrics | --metrics-enabled |
SURFPOOL_METRICS_ADDR |
Prometheus endpoint address | --metrics-addr |
Infrastructure as Code
Surfpool uses txtx.yml manifests and runbooks to define deployment workflows.
Manifest Structure
Place a txtx.yml at the project root:
name: my-project
runbooks:
- name: deployment
description: Deploy programs to localnet
file: ./runbooks/deployment.tx
Auto-Deploy with Watch
Combine --watch with runbooks to auto-redeploy on .so file changes:
surfpool start --watch --runbook deployment
Runbook Inputs
Pass inputs to runbooks for parameterized deployments:
surfpool start --runbook-input params.json
MCP Integration
Surfpool includes a built-in MCP (Model Context Protocol) server for AI agent integrations. The server communicates over stdio using the MCP protocol.
Configuration
The MCP server uses stdio transport. Add the server entry to your tool's MCP config file:
| Tool | Config file |
|---|---|
| Claude Code | .claude/settings.json (project) or ~/.claude/settings.json (global) |
| Claude Desktop | claude_desktop_config.json |
| Cursor | .cursor/mcp.json |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
| VS Code / Copilot | .vscode/mcp.json |
| Codex | codex.json or MCP config via CLI |
Standard MCP config (Claude Code, Claude Desktop, Cursor, Windsurf):
{
"mcpServers": {
"surfpool": {
"command": "surfpool",
"args": ["mcp"]
}
}
}
VS Code / Copilot (uses servers instead of mcpServers):
{
"servers": {
"surfpool": {
"command": "surfpool",
"args": ["mcp"]
}
}
}
For any other MCP-compatible tool, use the stdio transport with command surfpool and args ["mcp"].
Available MCP Tools
| Tool | Description |
|---|---|
start_surfnet |
Start a local Solana network. Default returns a shell command; run_as_subprocess: true starts in background. |
set_token_accounts |
Set SOL/SPL token balances for accounts on a running surfnet |
start_surfnet_with_token_accounts |
Start network + fund accounts in one call (background process) |
call_rpc_method |
Call any RPC method (standard Solana or surfnet_* cheatcodes) on a running surfnet. Renamed from call_surfnet_rpc. |
create_scenario |
Create a protocol state scenario. Read override_templates resource first. |
get_override_templates |
List available override templates |
MCP Resources
| Resource URI | Description |
|---|---|
str:///rpc_endpoints |
List of all available RPC endpoints |
str:///override_templates |
All available scenario override templates |
Agent Workflow via MCP
- Start surfnet: Call
start_surfnetorstart_surfnet_with_token_accounts - Set up state: Use
set_token_accountsto fund accounts, orcall_rpc_methodwith cheatcodes - Create scenarios: Read
override_templates, then callcreate_scenario - Execute transactions: Use
call_rpc_methodwithsendTransactionorsimulateTransaction - Inspect results: Use
call_rpc_methodwithsurfnet_getTransactionProfileorsurfnet_exportSnapshot
Cheatcodes Overview
Surfpool exposes 26 surfnet_* JSON-RPC methods on the same port as the standard Solana RPC:
Account Manipulation
surfnet_setAccount— set lamports, data, owner, executable flag on any accountsurfnet_setTokenAccount— set SPL token account balances, delegates, statesurfnet_resetAccount— restore an account to its initial statesurfnet_streamAccount— mark an account for automatic remote fetching and cachingsurfnet_streamAccounts— register multiple accounts for streaming in one callsurfnet_getStreamedAccounts— list all streamed accountssurfnet_offlineAccount— pin an account locally so it is never re-fetched from the remote
Program Management
surfnet_cloneProgramAccount— copy a program from one address to anothersurfnet_setProgramAuthority— change a program's upgrade authoritysurfnet_writeProgram— deploy program data in chunks (bypasses TX size limits)surfnet_registerIdl— register an IDL for a program in memorysurfnet_getActiveIdl— retrieve the registered IDL for a program
Time Control
surfnet_timeTravel— jump to an absolute timestamp, slot, or epochsurfnet_pauseClock— freeze slot advancementsurfnet_resumeClock— resume slot advancement
Transaction Profiling
surfnet_profileTransaction— simulate a transaction and return CU estimates with account snapshotssurfnet_getTransactionProfile— retrieve a stored profile by signature or UUIDsurfnet_getProfileResultsByTag— retrieve all profiles for a given tag
Network State
surfnet_setSupply— configure whatgetSupplyreturnssurfnet_resetNetwork— reset the entire network to initial statesurfnet_getLocalSignatures— get recent transaction signatures with logssurfnet_getSurfnetInfo— get network info including runbook execution historysurfnet_exportSnapshot— export all account state as a JSON snapshot
Scenarios
surfnet_registerScenario— register a set of account overrides on a timeline
Meta / Control
surfnet_enableCheatcode— re-enable previously disabled cheatcodessurfnet_disableCheatcode— disable specific cheatcodes (lockout mechanism for hardened environments)
See cheatcodes.md for full parameter schemas and JSON-RPC examples.
Scenarios Overview
The scenario system allows overriding protocol account state to simulate market conditions, liquidation events, and oracle price movements without deploying mock contracts.
Supported Protocols
| Protocol | Version | Account Types | Templates |
|---|---|---|---|
| Pyth | v2 | PriceUpdateV2 | SOL/USD, BTC/USD, ETH/USD, ETH/BTC |
| Jupiter | v6 | TokenLedger | Token ledger override |
| Raydium | CLMM v3 | PoolState | SOL/USDC, BTC/USDC, ETH/USDC |
| Switchboard | on-demand | SwitchboardQuote | Quote override |
| Meteora | DLMM v1 | LbPair | SOL/USDC, USDT/SOL |
| Kamino | v1 | Reserve, Obligation | Reserve state, reserve config, obligation health |
| Drift | v2 | PerpMarket, SpotMarket, User, State | Perp market, spot market, user state, global state |
| Whirlpool | v0.7.0 | Whirlpool | SOL/USDC, SOL/USDT, mSOL/SOL, ORCA/USDC |
Protocol templates and scenario coverage are summarized in this section.
Common Agent Workflows
Note: All commands below should be prefixed with
NO_DNA=1when run by an agent.
1. Start a Local Network and Deploy a Program
NO_DNA=1 surfpool start --watch
Surfpool detects txtx.yml, generates a deployment runbook if needed, deploys programs, and airdrops SOL to the default keypair. The RPC is available at http://127.0.0.1:8899.
2. Set Up Token Accounts for Testing
curl -X POST http://127.0.0.1:8899 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"surfnet_setTokenAccount","params":["<OWNER>","<MINT>",{"amount":"1000000000"}]}'
3. Test Time-Sensitive Logic
# Pause
curl -X POST http://127.0.0.1:8899 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"surfnet_pauseClock","params":[]}'
# Time travel to a future epoch
curl -X POST http://127.0.0.1:8899 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"surfnet_timeTravel","params":[{"absoluteEpoch":100}]}'
# Resume
curl -X POST http://127.0.0.1:8899 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"surfnet_resumeClock","params":[]}'
4. Profile Transaction Compute Units
curl -X POST http://127.0.0.1:8899 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"surfnet_profileTransaction","params":["<BASE64_TX>"]}'
5. Export and Restore State
# Export snapshot
curl -X POST http://127.0.0.1:8899 -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"surfnet_exportSnapshot","params":[]}'
# Load on next start
NO_DNA=1 surfpool start --snapshot ./snapshot.json