Surfpool v1.2.0 Release Notes
Introducing Surfpool SDK
Surfpool now ships surfpool-sdk for embedding a full Surfpool Surfnet directly
inside tests. You can start an isolated local Solana-compatible runtime without
shelling out to surfpool start, get dynamic HTTP and WebSocket endpoints, use a
pre-funded payer, and call cheatcode helpers for fast state setup.
The SDK is available as:
- Rust crate:
surfpool-sdk - Node.js package:
surfpool-sdk, backed bynapi-rsnative bindings and
prebuilt package artifacts for macOS x64, macOS arm64, and Linux x64 GNU
Rust Usage
Add the SDK to integration-test dependencies:
[dev-dependencies]
surfpool-sdk = "1.2.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }Start a Surfnet and use the built-in RPC client:
use surfpool_sdk::{Signer, Surfnet};
#[tokio::test]
async fn starts_embedded_surfnet() {
let surfnet = Surfnet::start().await.unwrap();
let rpc = surfnet.rpc_client();
let payer_balance = rpc.get_balance(&surfnet.payer().pubkey()).unwrap();
assert!(payer_balance > 0);
}Configure startup behavior when you need deterministic test setup:
use surfpool_sdk::{BlockProductionMode, Pubkey, Surfnet};
#[tokio::test]
async fn starts_with_custom_state() {
let recipient = Pubkey::new_unique();
let surfnet = Surfnet::builder()
.offline(true)
.block_production_mode(BlockProductionMode::Transaction)
.airdrop_addresses(vec![recipient])
.airdrop_sol(10_000_000_000)
.start()
.await
.unwrap();
surfnet.cheatcodes().fund_sol(&recipient, 5_000_000_000).unwrap();
}Use cheatcodes for common integration-test setup:
use surfpool_sdk::{Pubkey, Surfnet};
#[tokio::test]
async fn seeds_accounts() {
let surfnet = Surfnet::start().await.unwrap();
let cheats = surfnet.cheatcodes();
let owner = Pubkey::new_unique();
let mint = Pubkey::new_unique();
cheats.fund_sol(&owner, 1_000_000_000).unwrap();
cheats.fund_token(&owner, &mint, 1_000, None).unwrap();
cheats.time_travel_to_slot(1_000).unwrap();
}Deploy a local Solana program from build artifacts:
use surfpool_sdk::Surfnet;
#[tokio::test]
async fn deploys_program() {
let surfnet = Surfnet::start().await.unwrap();
let cheats = surfnet.cheatcodes();
// Looks for target/deploy/my_program.so,
// target/deploy/my_program-keypair.json,
// and target/idl/my_program.json when present.
let program_id = cheats.deploy_program("my_program").unwrap();
assert!(surfnet.rpc_client().get_account(&program_id).is_ok());
}Use an explicit program id and artifact path when your build output is custom:
use surfpool_sdk::{cheatcodes::builders::DeployProgram, Pubkey, Surfnet};
#[tokio::test]
async fn deploys_from_explicit_artifacts() {
let surfnet = Surfnet::start().await.unwrap();
let program_id = Pubkey::new_unique();
surfnet
.cheatcodes()
.deploy(
DeployProgram::new(program_id)
.so_path("fixtures/programs/my_program.so")
.idl_path("fixtures/idls/my_program.json"),
)
.unwrap();
}The Rust API also exposes typed cheatcode builders for setting accounts,
resetting accounts, streaming accounts, setting token-account fields, and
deploying local program artifacts.
Node.js Usage
Install the package in a Node.js 18+ test project:
npm install surfpool-sdkStart Surfnet and seed state from a Node test:
const assert = require("node:assert/strict");
const test = require("node:test");
const { Surfnet } = require("surfpool-sdk");
test("uses embedded surfpool", async () => {
const surfnet = Surfnet.start();
const owner = Surfnet.newKeypair().publicKey;
const mint = Surfnet.newKeypair().publicKey;
surfnet.fundSol(owner, 1_000_000_000);
surfnet.fundToken(owner, mint, 1_000);
assert.match(surfnet.rpcUrl, /^http:\/\/127\.0\.0\.1:/);
});For custom startup options, use startWithConfig:
const payer = Surfnet.newKeypair();
const surfnet = Surfnet.startWithConfig({
offline: true,
blockProductionMode: "manual",
slotTimeMs: 1,
airdropSol: 1_000_000_000,
payerSecretKey: Uint8Array.from(payer.secretKey),
});Deploy a local Solana program from build artifacts:
const surfnet = Surfnet.start();
// Looks for target/deploy/my_program.so,
// target/deploy/my_program-keypair.json,
// and target/idl/my_program.json when present.
const programId = surfnet.deployProgram("my_program");
console.log(`deployed ${programId} to ${surfnet.rpcUrl}`);Use explicit deployment options when you already have the program id and .so
path:
const programId = Surfnet.newKeypair().publicKey;
surfnet.deploy({
programId,
soPath: "fixtures/programs/my_program.so",
idlPath: "fixtures/idls/my_program.json",
});The Node API includes helpers for SOL and token funding, arbitrary account
updates, token-account state, account reset, remote account streaming, time
travel, program deployment, ATA derivation, event draining, and keypair
generation.
New Features
New Cheatcodes
-
feat(rpc): add rpc methods to enable/disable other cheatcodes(#557) (by @0xzrf)Surfnet cheatcode access can now be changed at runtime with
surfnet_enableCheatcodeandsurfnet_disableCheatcode. This lets tests or
hosted environments narrow the mutable RPC surface after setup.{ "jsonrpc": "2.0", "id": 1, "method": "surfnet_disableCheatcode", "params": [["surfnet_setAccount", "surfnet_timeTravel"]] } -
feat: add `surfnet_offlineAccount` RPC method(#566) (by @CanardMandarin)surfnet_offlineAccountprevents Surfpool from downloading a specific account
from the configured remote RPC. PassincludeOwnedAccountsto also block
accounts owned by that pubkey, which is useful when a test must prove behavior
without remote fallback.{ "jsonrpc": "2.0", "id": 1, "method": "surfnet_offlineAccount", "params": ["<account-pubkey>", { "includeOwnedAccounts": true }] } -
feat: add `surfnet_streamAccounts` RPC method(#598) (by @KABBOUCHI)Multiple remote accounts can now be registered for streaming in a single
request. This complementssurfnet_streamAccountand helps tests seed a set
of account subscriptions before exercising transaction behavior.{ "jsonrpc": "2.0", "id": 1, "method": "surfnet_streamAccounts", "params": [[ { "pubkey": "<account-a>", "includeOwnedAccounts": true }, { "pubkey": "<account-b>" } ]] }
Jito sendBundle
-
feat(core): add Jito sendBundle RPC support(#480) (by @serejke)Surfpool now implements the Jito-compatible
sendBundleRPC method for local
bundle testing. This makes it possible to exercise bundle submission flows
against Surfpool before pointing the same client code at a Jito block engine
or bundle-aware infrastructure.sendBundleaccepts up to five serialized transactions, matching Jito's
bundle size limit. Surfpool processes them sequentially in the provided order
and forcesskip_preflightto match block-engine send behavior. On success,
it returns a bundle ID calculated as the SHA-256 hash of the comma-separated
transaction signatures.The first version is intentionally a local compatibility surface, not full
block-engine semantics. If a later transaction in the bundle fails, Surfpool
returns an error that includes the failing bundle index, but earlier
successful transactions are not rolled back yet.{ "jsonrpc": "2.0", "id": 1, "method": "sendBundle", "params": [["<base64-transaction-1>", "<base64-transaction-2>"], { "encoding": "base64" }] }
Additional Features
-
feat(cli): allow passing feature pubkey instead of name on startup(#577) (by @MicaiahReid)surfpool start --featureandsurfpool start --disable-featurenow accept
base58 Agave feature-gate pubkeys. Named features still work for compatibility,
but the CLI warns through help text that names are deprecated.surfpool start --feature <feature-pubkey> --disable-feature <feature-pubkey> -
feat: enable register-tracing feature for litesvm by default(#574) (by @procdump)Default CLI builds now enable LiteSVM register tracing through Surfpool's
register-tracingfeature, making register-level execution data available to
tracing-enabled builds without requiring a custom feature selection. -
feat(cli): add --artifacts-path flag to override .so program location(#573) (by @procdump)surfpool startcan now deploy program binaries from a custom artifacts
directory instead of assumingtarget/deploy. This is useful for alternate
build directories, debug artifacts, or CI layouts.surfpool start --artifacts-path ./target/deploy/debug -
feat: add prometheus metrics(#586) (by @dvansari65)Builds compiled with the
prometheusfeature can expose a/metricsendpoint
with counters and histograms for transaction outcomes, transaction latency, RPC
requests, RPC latency, and remote account fetch latency.
cargo run -p surfpool-cli --features prometheus -- start --metrics-enabled --metrics-addr 127.0.0.1:9000
curl http://127.0.0.1:9000/metrics-
feat: add option to skip blockhash validation for transactions(#632) (by @MicaiahReid)Surfpool can now disable transaction blockhash validation for workflows that
intentionally replay old transactions or bypass recent-blockhash freshness.
The CLI exposes--skip-blockhash-check, and the Rust SDK exposes
SurfnetBuilder::skip_blockhash_check(true).surfpool start --skip-blockhash-check
Bug Fixes
-
fix: use the config arg in the get_balance rpc method(#547) (by @0xzrf)getBalancenow respects the request configuration instead of ignoring it, so
callers get results according to the commitment/config they passed. -
fix: reject duplicate accounts between static keys and ALT(#564) (by @lgalabru)Transactions that duplicate an account between static keys and address lookup
table entries are now rejected, matching Solana transaction validation more
closely. -
fix(rpc): respect commitment visibility in getBlock(#572) (by @sergeytimoshin)getBlocknow filters block visibility by commitment, avoiding responses that
expose blocks above the requested commitment level. -
fix: increase max_request_body_size to 15 MiB(#583) (by @arihantbansal)The RPC server now accepts larger request bodies, which prevents large
transactions, programs, or cheatcode payloads from being rejected too early. -
Set epoch schedule sysvar to match mainnet in surfnet init(#435) (by @rajgoesout)Surfnet initialization now seeds the epoch schedule sysvar with mainnet-shaped
values, improving compatibility for programs that inspect epoch schedule data. -
fix(core): make 'until' param exclusive for `getSignaturesForAddress`(#582) (by @MicaiahReid)getSignaturesForAddressnow treats theuntilsignature as an exclusive
boundary, matching expected Solana RPC pagination semantics. -
fix: improve write_program logic; add tests(#579) (by @MicaiahReid)Program writes now use improved chunking and registration logic, with tests
covering small-program handling and program deployment behavior. -
fix: add back in admin plugin methods(2d49771) (by @MicaiahReid)Admin plugin RPC methods were restored and covered with plugin tests after
being dropped during prior cleanup. -
fix: enable register-tracing feature by default in cli(#606) (by @procdump)The CLI feature wiring now actually enables register tracing by default,
aligning the binary configuration with the intended LiteSVM tracing behavior. -
fix(core): add back litesvm precompiles(#607) (by @MicaiahReid)LiteSVM precompiles are restored during Surfnet setup, fixing transactions
that depend on built-in precompile program behavior. -
fix(surfnet): terminates the cli program on sigterm(#589) (by @0xzrf)The TUI now handles SIGTERM so Surfpool exits cleanly when managed by process
supervisors or container runtimes. -
fix: enable clap help and usage features(ffc1a64) (by @MicaiahReid)Clap's help and usage features are enabled again, restoring generated command
help output. -
fix: dont reenable ix profiling if not initially configured(701f5a8) (by @MicaiahReid)Instruction profiling now stays disabled when it was not configured at
startup, avoiding unexpected profiling state changes later in the runloop. -
fix: broken build; remove unused imports(3b2881e) (by @MicaiahReid)The build was fixed by removing stale imports and adjusting affected CLI/type
references. -
fix(core): handle divide by zero in time travel calculations(#614) (by @MicaiahReid)Time travel now guards against zero-valued slot-time calculations instead of
panicking or producing invalid epoch math. -
fix(core): shutdown rpc servers on terminate command(#612) (by @MicaiahReid)Termination now shuts down RPC servers as part of Surfnet shutdown, avoiding
lingering server threads and occupied ports. -
fix(docker): set default network host to 127.0.0.1(#622) (by @MicaiahReid)Docker startup now defaults Surfpool's network host to localhost, matching the
expected local binding behavior. -
fix: add serde feature to solana-account and solana-transaction-context dependencies(710798c) (by @MicaiahReid)Serialization support is enabled for Solana account and transaction-context
types that need serde in Surfpool's type layer.
Maintenance, Tests, CI, and Release
test: add test using skipSigVerify in sendTransaction call(#581) (by @MicaiahReid)ci: simplify approach for releasing crates(#585) (by @MicaiahReid)ci: version workspace crates on import(#590) (by @MicaiahReid)chore: fix formatting and tests(c897dbc) (by @MicaiahReid)chore: remove deprecated crates/imports(#591) (by @MicaiahReid)chore: remove automatic subgraph runbook generation(d392288) (by @MicaiahReid)chore: remove submodule(676afbc) (by @MicaiahReid)ci: remove subgraphs feature from release binary build(d31a437) (by @MicaiahReid)chore: bump litesvm to v0.11.0(#604) (by @MicaiahReid)chore: update studio build-rs to pull from gh releases(#596) (by @MicaiahReid)chore: remove errant println(dda9708) (by @MicaiahReid)chore: remove some clones(007813f) (by @MicaiahReid)chore: update references to txtx org for repo url(14c62f4) (by @MicaiahReid)ci: add workflow dispatch for npm publish of sdk-node(fd37462) (by @MicaiahReid)ci: fix sdk-node artiface upload path(38ac258) (by @MicaiahReid)ci: rename install step and update npm install command(6a95df2) (by @MicaiahReid)chore: various cleanups(#615) (by @cds-amal)ci: update Node.js version and switch to npm ci for package installation(#631) (by @MicaiahReid)refactor(cli/core/sdk): revamp how surfnet svm is instantiated(#630) (by @MicaiahReid)ci: add some scripts to prepare node release(b6ac5fc) (by @MicaiahReid)chore(release): publish v1.2.0(d279682) (by @MicaiahReid)
New Contributors
Full Changelog: v1.1.1...v1.2.0