SDK

Configuration

Configure a Surfnet at startup — remote RPC fallback, block production mode, slot timing, airdrops, feature gates, and custom payers.

Surfnet::start() (Rust) and Surfnet.start() (JS) boot an offline Surfnet with transaction-mode block production and a payer pre-funded with 10 SOL. When you need anything else — a mainnet-forked Surfnet, a slower clock, deterministic funding, a specific payer keypair — reach for the builder (Rust) or startWithConfig (JS).

Defaults

If you call start() with no arguments, you get:

SettingValue
ModeOffline (no upstream RPC)
Block productionTransaction (blocks advance on each tx)
Slot time1 ms
PayerRandom keypair funded with 10 SOL (10,000,000,000 lamports)
RPC URLhttp://127.0.0.1:<random>
WS URLws://127.0.0.1:<random>
Feature configDefault mainnet feature set

Custom Configuration

tests/configured.rs
use surfpool_sdk::{BlockProductionMode, Pubkey, Surfnet};

#[tokio::test]
async fn starts_with_custom_configuration() {
    let alice = Pubkey::new_unique();

    let surfnet = Surfnet::builder()
        .remote_rpc_url("https://api.mainnet-beta.solana.com")
        .block_production_mode(BlockProductionMode::Transaction)
        .slot_time_ms(10)
        .airdrop_addresses(vec![alice])
        .airdrop_sol(5_000_000_000)
        .skip_blockhash_check(true)
        .start()
        .await
        .unwrap();

    assert_eq!(
        surfnet.rpc_client().get_balance(&alice).unwrap(),
        5_000_000_000
    );
}

Builder Reference

The Rust builder and the JS config object expose the same logical options under camelCase / snake_case naming.

OptionRust setterJS fieldTypeDefault
Offline modeoffline(bool)offline?: booleanbooltrue
Upstream RPCremote_rpc_url(impl Into<String>)remoteRpcUrl?: stringURL stringnone
Block productionblock_production_mode(BlockProductionMode)blockProductionMode?: stringenum / "manual" | "clock" | "transaction"Transaction
Slot timeslot_time_ms(u64)slotTimeMs?: numbermilliseconds1
Airdrop addressesairdrop_addresses(Vec<Pubkey>)airdropAddresses?: string[]list of pubkeysempty
Airdrop amountairdrop_sol(u64)airdropSol?: numberlamports10_000_000_000
Skip blockhashskip_blockhash_check(bool)n/aboolfalse
Custom payerpayer(Keypair)payerSecretKey?: Uint8Array | number[]Keypair / bytesgenerated
Enable featureenable_feature(Pubkey)enableFeatures?: string[]feature ID(s)none
Disable featuredisable_feature(Pubkey)disableFeatures?: string[]feature ID(s)none
All featuresn/aallFeatures?: booleanboolfalse
Feature configfeature_config(SvmFeatureConfig)n/afull configdefault mainnet

Remote RPC Fallback

Setting remoteRpcUrl (or remote_rpc_url in Rust) flips the Surfnet out of offline mode. Accounts not present locally are fetched on demand from the upstream RPC, which is how mainnet-fork tests work.

let surfnet = Surfnet::builder()
    .remote_rpc_url("https://api.mainnet-beta.solana.com")
    .start()
    .await?;

Use a high-quality upstream RPC

Use a paid endpoint (Helius, Triton, QuickNode) for tests that exercise many forked accounts. Public mainnet-beta is heavily rate limited and will flake under parallel test loads.

Block Production Mode

ModeWhen blocks advance
Transaction (default)After every transaction. Best for tests that send and immediately assert.
ClockAt a fixed interval set by slot_time_ms. Best for tests that depend on multiple slots passing without explicit transactions.
ManualOnly when a SimnetCommand::AdvanceClock is sent. Best for time-sensitive tests where the test drives the clock.

Custom Payer

By default the payer is a freshly generated keypair. Provide your own when tests need a known address, for example to pre-build instructions referencing the payer's public key.

use surfpool_sdk::{Keypair, Signer, Surfnet};

let payer = Keypair::new();
let surfnet = Surfnet::builder().payer(payer.insecure_clone()).start().await?;

assert_eq!(surfnet.payer().pubkey(), payer.pubkey());

Feature Gates

Toggle individual SVM feature gates by ID, or activate every known feature with allFeatures: true (JS only — the Rust builder accepts feature_config for the full set).

use surfpool_sdk::{Pubkey, Surfnet};

let feature_id: Pubkey = "9bn2vTJUsUcnpiZWbu2woSKtTGW3ErZC9ERv88SDqQjK"
    .parse()
    .unwrap();

let surfnet = Surfnet::builder()
    .enable_feature(feature_id)
    .start()
    .await?;

Skip Blockhash Check

skip_blockhash_check(true) (Rust only) disables blockhash validation on all transactions. Useful for tests that sign transactions far in advance of submission or that don't care about expiry.

Errors

A misconfigured builder fails at start(). The Rust SDK returns SurfnetError:

VariantWhen
PortAllocation(String)Could not bind to a free RPC or WebSocket port.
Startup(String)The runtime failed to initialize (bad config, bad remote URL, etc.).
Runtime(String)The runtime crashed during startup before becoming ready.
Aborted(String)Startup was cancelled (e.g., shutdown signal during boot).
Cheatcode(String)A cheatcode call inside the builder failed.

The JS SDK throws an Error whose message wraps the same underlying variant.

On this page