Surfpool Documentation

repository·main·Indexed 20 days ago

https://github.com/solana-foundation/surfpool

A local-first development platform for Solana that serves as a drop-in replacement for solana-test-validator. Surfpool enables developers to work with real mainnet state, manage infrastructure via code (IaC), and perform advanced simulations using Surfpool Scenarios. It features an IDL-to-SQL engine for on-chain data indexing, a visual management dashboard called Surfpool Studio, and provides Node.js bindings via @solana/surfpool and a Model Context Protocol (MCP) server for IDE integration.

Tokens
60K
Snippets
214
Records
312
Agent score
67%

What's inside Surfpool

  1. Understand the `slotsUpdatesSubscribe` WebSocket RPC

    main

    The slotsUpdatesSubscribe (and its counterpart slotsUpdatesUnsubscribe) is a WebSocket RPC method designed to stream tagged slot-lifecycle updates to clients. This method follows the Solana reference specification, providing notifications for specific lifecycle events as the chain advances.

    Supported Slot Update Variants

    Because Surfpool uses a simulated execution model without a shred/gossip layer, it supports a specific subset of the standard Solana SlotUpdate variants:

    • CreatedBank
    • Frozen
    • OptimisticConfirmation
    • Root

    Note: FirstShredReceived, Completed, and Dead are not currently produced by Surfpool's execution model.

    Wire Format and Payload

    The notification payload follows the solana_rpc_client_api::response::SlotUpdate schema. Crucially, the payload is not wrapped in an RpcResponse<...> object; instead, the SlotUpdate object is sent directly under params.result in the JSON-RPC notification.

    To ensure compatibility, the implementation uses #[serde(tag = "type", rename_all = "camelCase")] for serialization.

  2. Understand the `slotsUpdatesSubscribe` WebSocket RPC behavior

    main

    The slotsUpdatesSubscribe WebSocket RPC allows clients to subscribe to slot updates. When using Surfpool, users should be aware of the following implementation details regarding the emitted data:

    • Supported Variants: Surfpool emits a subset of the standard SlotUpdate variants. It focuses on the most common client use-cases, specifically validator-style monitoring of the bank lifecycle and finalization. It may not emit firstShredReceived, completed, or dead variants.
    • Timestamps: Timestamps provided in slotsUpdates notifications use real Unix timestamps via chrono::Utc::now().timestamp_millis(). Surfpool does not use simulated slot time for these timestamps, as clients historically expect real-world Unix time.
    • Transaction Data: In the confirm_transactions context, the num_failed_transactions field may be reported as 0 if the data is not readily available in the current version.
    • Emission Pattern: Notifications are delivered via a channel and a polling task (typically with a 50ms interval) rather than being emitted synchronously. This ensures that slow WebSocket clients do not stall the block production process.
  3. How the @solana/surfpool/kit plugin works

    main

    The @solana/surfpool/kit plugin is a drop-in replacement for solanaLocalRpc() or litesvm(). It provides a typed RPC client for every surfnet_* cheatcode. It supports two modes:

    1. Embedded Mode

    Boots an in-process Surfnet, installs a pre-funded payer, and provides the full local RPC stack. It attaches the native handle as client.surfnet and typed cheatcodes as client.cheatcodes.

    This mode is Disposable. You can use the using keyword to ensure Surfnet stops automatically when the client goes out of scope.

    using client = await createClient().use(surfpool());
    // Surfnet stops automatically when `client` goes out of scope.

    2. Attach Mode

    Connects to an already-running Surfpool instance (e.g., started via surfpool start). In this mode, no native module is loaded, and the client must already have a payer configured.

    import { createClient } from "@solana/kit";
    import { payer } from "@solana/kit-plugin-signer";
    import { surfpool } from "@solana/surfpool/kit";
    
    const client = await createClient()
      .use(payer(myPayer))
      .use(surfpool({ rpcUrl: "http://127.0.0.1:8899" }));
    import { createClient } from "@solana/kit";
    import { surfpool } from "@solana/surfpool/kit";
    
    const client = await createClient().use(surfpool());
    
    await client.cheatcodes.timeTravel({ absoluteSlot: 1_000_000 }).send();
    client.surfnet.fundSol(client.payer.address, 1_000_000_000);
    const slot = await client.rpc.getSlot().send();
    
    client.surfnet.stop();
  4. Use Override Templates in Surfpool Studio

    main

    Manually building account key maps for surfnet_registerScenario is complex. To simplify this, Surfpool uses Override Templates.

    Templates registered within the Surfpool repository automatically appear in the Surfpool Studio drag-and-drop UI. This UI allows you to visually build scenarios and automatically generates the correct surfnet_registerScenario payload, eliminating the need to manually map IDL-indexed fields.

  5. Core Surfpool Concepts

    main

    Surfpool is a developer toolset for Solana that provides several key abstractions:

    • Drop-in Replacement: It acts as a replacement for solana-test-validator, allowing you to work with mainnet state locally without downloading massive snapshots.
    • Infrastructure as Code (IaC): Inspired by Terraform, it allows you to define your on-chain and off-chain stack declaratively and reproducibly.
    • IDL-to-SQL: An engine that transforms your on-chain IDL into a queryable SQL schema (SQLite/Postgres) for instant indexing and analytics.
    • Surfpool Scenarios: A way to curate slot-by-slot account states, mixing live mainnet data with overridden states to stress test protocols in specific real-world conditions.
    • Surfpool Studio: A local dashboard to visualize and manage your networks, which can be extended to Surfpool Cloud for large-scale simulations and mainnet data indexing.
  6. Understand Surfpool Scenarios and account state overrides

    main

    Surfpool Scenarios allow you to create a time-sequence of account states for testing and simulation.

    As a scenario executes, each step is associated with a subsequent slot in the surfnet. Each step overrides the surfnet accounts database with specific account states defined in that step. This enables developers to observe how different account state transitions impact their protocols over time.

  7. Subscribe to slot updates via WebSocket

    main

    You can subscribe to a stream of slot lifecycle events using the slotsUpdatesSubscribe WebSocket RPC method. This provides real-time notifications as a slot progresses through its lifecycle.

    Request Format:

    {
      "jsonrpc": "2.0",
      "id": 1,
      "method": "slotsUpdatesSubscribe"
    }

    Notification Types: Upon successful subscription, you will receive slotsUpdatesNotification payloads. Each notification includes a millisecond timestamp and one of the following event types:

    • createdBank: Emitted when a new slot is computed.
    • frozen: Emitted when the slot is frozen, including a stats object with transaction counts (numTransactionEntries, numSuccessfulTransactions, numFailedTransactions, maxTransactionsPerEntry).
    • optimisticConfirmation: Emitted when the slot reaches optimistic confirmation.
    • root: Emitted when the slot becomes the root of the ledger.

    Unsubscribe: To stop receiving updates, use the slotsUpdatesUnsubscribe method with your subscription ID:

    {
      "jsonrpc": "2.0",
      "id": 2,
      "method": "slotsUpdatesUnsubscribe",
      "params": [<subscription_id>]
    }
    {
      "jsonrpc": "2.0",
      "id": 1,
      "method": "slotsUpdatesSubscribe"
    }
  8. Add native scenario support for a protocol

    main

    To provide native support for a protocol (which enables automatic IDL-parsing and populates the Surfpool Studio UI), follow these steps:

    1. Create a protocol folder: Inside crates/core/src/scenarios/protocols/, create a directory following the pattern [protocol_name]/[version]. Example: crates/core/src/scenarios/protocols/pyth/v2
    2. Add idl.json: Place the protocol's Anchor IDL file in the folder.
    3. Add overrides.yaml: Create an overrides.yaml file. This file defines the metadata that populates the Surfpool Studio UI.
    4. Update registry.rs: Modify the registry.rs file to wire the template registry, overrides.yaml, and idl.json together. Refer to load_pyth_overrides or load_jupiter_overrides in the source for implementation patterns.
  9. Install Surfpool

    main

    You can install Surfpool using the official installer script, by building from source, or by using Docker.

    Using the Installer

    Run the following command to install via the official script:

    curl -sL https://run.surfpool.run/ | bash

    Building from Source

    Clone the repository and use cargo surfpool-install to build:

    git clone https://github.com/solana-foundation/surfpool.git
    cd surfpool
    cargo surfpool-install

    Using Docker

    To run Surfpool via Docker with the default RPC (8899), WebSocket (8900), and Studio (18488) ports exposed:

    docker run --rm -p 8899:8899 -p 8900:8900 -p 18488:18488 surfpool/surfpool

    After installation, verify it by running:

    surfpool --version
    curl -sL https://run.surfpool.run/ | bash