solana_kit_surfpool 0.2.1 copy "solana_kit_surfpool: ^0.2.1" to clipboard
solana_kit_surfpool: ^0.2.1 copied to clipboard

Surfpool SDK helpers for Solana Kit Dart tests.

solana_kit_surfpool #

Coverage website

Surfpool SDK helpers for Solana Kit Dart tests.

This package ports Surfpool's TypeScript/Rust SDK surface to idiomatic Dart by using Surfpool's JSON-RPC cheatcodes. Surfnet.start() is CLI-backed: it starts surfpool start on random ports and then talks to that process over HTTP. This keeps the package pure Dart and avoids native napi or flutter_rust_bridge bindings.

Installation #

Installation #

Install the package directly:

dependencies:
  "solana_kit_surfpool": ^0.2.1

If your app uses several Solana Kit packages together, you can also depend on the umbrella package instead:

dart pub add solana_kit

Inside this monorepo, Dart workspace resolution uses the local package automatically.

solana_kit_surfpool is not re-exported by the umbrella solana_kit package; add and import this package directly when you need Surfpool helpers.

The CLI-backed runtime requires the surfpool executable to be available on PATH. In this repository, use the configured devenv shell.

Usage #

import 'package:solana_kit_surfpool/solana_kit_surfpool.dart';

Future<void> main() async {
  final surfnet = await Surfnet.start();
  final alice = Surfnet.newKeypair();

  try {
    await surfnet.fundSol(alice.address, 1_000_000_000);

    final epoch = await surfnet.timeTravelToSlot(1_000);
    print('RPC: ${surfnet.rpcUrl}');
    print('Payer: ${surfnet.payer.value}');
    print('Slot: ${epoch.absoluteSlot}');
  } finally {
    await surfnet.stop();
  }
}

Connect to an existing Surfpool process when you manage surfpool start yourself:

final surfnet = Surfnet.connect(
  rpcUrl: Uri.parse('http://127.0.0.1:8899'),
  wsUrl: Uri.parse('ws://127.0.0.1:8900'),
);

Solana Kit client (kit plugin) #

createSurfpoolClient() mirrors the @solana/surfpool/kit plugin for TypeScript: it starts a fresh Surfnet and returns a SurfpoolClient with a Solana Kit RPC client, an RPC subscriptions client, the Surfnet's pre-funded payer signer, and a typed cheatcode RPC — so tests can build, sign, send, and confirm transactions without managing a validator or RPC plumbing by hand.

import 'package:solana_kit/solana_kit.dart';
import 'package:solana_kit_surfpool/solana_kit_surfpool.dart';

Future<void> main() async {
  final client = await createSurfpoolClient();
  try {
    // Pre-funded payer installed by the plugin.
    final payer = client.payer;

    // Standard Solana RPC and subscriptions clients.
    final slot = await client.rpc.getSlot().send();
    print('Slot: $slot');

    // Surfpool cheatcodes with the `surfnet_` prefix stripped.
    await client.cheatcodes.pauseClock();

    // Helpers.
    await client.airdrop(payer.address, BigInt.from(1_000_000_000));
    final rent = await client.getMinimumBalance(BigInt.zero);
    print('Rent-exempt minimum: $rent');
  } finally {
    await client.stop();
  }
}

Stopping a freshly created client clears Surfnet's in-memory payer bytes and disposes the client-owned payer signer. A signer supplied to connectSurfpoolClient remains caller-owned and is not disposed.

Attach to an already-running Surfpool with connectSurfpoolClient; the [payer] must be a funded signer you provide:

final client = connectSurfpoolClient(
  rpcUrl: Uri.parse('http://127.0.0.1:8899'),
  payer: myFundedSigner,
);

Cheatcodes #

import 'package:solana_kit_addresses/solana_kit_addresses.dart';

final owner = Surfnet.newKeypair().address;
final mint = address('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');

await surfnet.fundToken(owner, mint, 5_000_000);

final ata = surfnet.getAta(owner, mint);
print('ATA: ${ata.value}');

await surfnet.setTokenAccount(
  owner,
  mint,
  const SetTokenAccountUpdate(
    state: 'initialized',
    delegatedAmount: 500_000,
  ),
);

For advanced account fields, use the builder API:

import 'dart:typed_data';

await surfnet.execute(
  SetAccount(Surfnet.newKeypair().address)
      .withLamports(500_000)
      .withData(Uint8List.fromList([1, 2, 3]))
      .withOwner(surfnet.payer)
      .withRentEpoch(0)
      .withExecutable(executable: false),
);

Deploy programs #

final programId = await surfnet.deployProgram('my_program');
print('deployed at ${programId.value}');

deployProgram discovers conventional Anchor/Agave artifacts under target/deploy and target/idl. Use deploy with DeployOptions when bytes or paths live elsewhere.

Runtime events #

The upstream Rust and JS SDKs expose an in-process event channel. This Dart package does not embed the Rust runtime, so drainEvents() currently returns best-effort stdoutLog and stderrLog events captured from the CLI-backed process. Use RPC assertions for deterministic tests.

Key APIs #

API Purpose
createSurfpoolClient() / connectSurfpoolClient() Kit-plugin style client: RPC + subscriptions + payer + cheatcodes.
SurfpoolClient.rpc / rpcSubscriptions / payer / cheatcodes Wired Solana Kit clients and the pre-funded payer.
SurfpoolClient.airdrop / getMinimumBalance Funding and rent-exemption helpers.
Surfnet.start() / Surfnet.startWithConfig() Start a CLI-backed local Surfnet.
Surfnet.connect() Attach to an existing Surfpool RPC endpoint.
fundSol, fundToken, setAccount, setTokenAccount Mutate local account state through Surfpool cheatcodes.
resetAccount, streamAccount Re-fetch or stream accounts from an upstream RPC.
timeTravelToSlot, timeTravelToEpoch, timeTravelToTimestamp Move the local Surfnet clock forward.
deployProgram, deploy Write program bytes and optionally register an Anchor IDL.