SDK

Deploying Programs

Deploy Solana programs into a Surfnet from local artifacts. Auto-discover Anchor workspaces or pass an explicit .so path, raw bytes, and IDL.

The SDK can deploy a program from local build artifacts in one call. There are two flavors: auto-discovery for Anchor/Agave workspaces with conventional paths, and explicit deployment when you want to control where the bytes come from.

Both flavors register the program at the IDL's declared address (or at the keypair file's public key) and, if an IDL is available, register it with the runtime via surfnet_registerIdl so RPC clients can decode account data.

Auto-Discover An Anchor Workspace

deployProgram / deploy_program takes the program name and looks for these files relative to the current working directory:

FilePurpose
target/deploy/{name}.soCompiled program bytecode (required)
target/deploy/{name}-keypair.jsonProgram keypair — the public key becomes the program ID (required)
target/idl/{name}.jsonAnchor IDL JSON (optional; registered if present)
use surfpool_sdk::Surfnet;

let surfnet = Surfnet::start().await?;
let cheats = surfnet.cheatcodes();

let program_id = cheats.deploy_program("my_program")?;
println!("deployed at {program_id}");

Build before you deploy

Run anchor build (or cargo build-sbf) before calling deployProgram so the .so and keypair artifacts actually exist on disk. The SDK does not invoke the build for you.

Explicit Deployment

When the artifacts live somewhere non-standard — a built CI artifact, a pre-baked test fixture, an embedded byte slice — pass an explicit deploy configuration.

use surfpool_sdk::cheatcodes::builders::DeployProgram;
use surfpool_sdk::Surfnet;

let cheats = Surfnet::start().await?.cheatcodes();

// Derive the program ID from a keypair file.
let program_id = cheats.deploy(
    DeployProgram::from_keypair_path("fixtures/my_program-keypair.json")?
        .so_path("fixtures/my_program.so")
        .idl_path("fixtures/my_program.idl.json"),
)?;

Deploy From Raw Bytes

When the bytecode is already in memory — generated by a build step, downloaded from an artifact store, or embedded via include_bytes! — skip the file system entirely.

use surfpool_sdk::cheatcodes::builders::DeployProgram;
use surfpool_sdk::{Pubkey, Surfnet};

const PROGRAM_BYTES: &[u8] = include_bytes!("../fixtures/my_program.so");

let cheats = Surfnet::start().await?.cheatcodes();
let program_id: Pubkey = "DemoProgram111111111111111111111111111111111".parse()?;

cheats.deploy(
    DeployProgram::new(program_id).so_bytes(PROGRAM_BYTES.to_vec()),
)?;

DeployProgram Builder Reference (Rust)

Constructor / setterSignaturePurpose
DeployProgram::new(program_id: Pubkey) -> SelfBuild a deployment for a known program ID.
DeployProgram::from_keypair_path(path: impl AsRef<Path>) -> SurfnetResult<Self>Read a Solana keypair file; the public key becomes the program ID.
.so_path(path: impl Into<PathBuf>)Path to the compiled .so artifact on disk.
.so_bytes(bytes: Vec<u8>)Raw program bytes (mutually exclusive with so_path).
.idl_path(path: impl Into<PathBuf>)Optional Anchor IDL JSON to register after deployment.

DeployOptions Type Reference (JS)

type ByteArrayLike = Uint8Array | number[];

interface DeployOptions {
  programId: string;       // base58 address to deploy at
  soPath?: string;         // path to .so on disk
  soBytes?: ByteArrayLike; // or raw bytes in memory
  idlPath?: string;        // optional Anchor IDL JSON
}

soPath and soBytes are mutually exclusive — provide exactly one.

What Gets Registered

After a successful deploy, the runtime does three things:

  1. Writes the program account at programId with the .so bytes via surfnet_writeProgram.
  2. Marks the account executable so it can be invoked through normal RPC calls.
  3. Registers the IDL (if provided) via surfnet_registerIdl, which lets RPC clients decode account data using the program's IDL schema.

Rare: non-executable program account

If you want to write program bytes to an account but keep it non-executable, use SetAccount directly instead of deploy. The deploy path always marks the account executable.

Errors

CauseSymptom
.so file is missing or unreadableSurfnetError::Cheatcode("...failed to read...") or thrown JS error
Program ID does not match the keypair fileSurfnetError::Cheatcode("...mismatched program id...")
IDL JSON is malformedSurfnetError::Cheatcode("...invalid IDL...")
Surfnet is offline and the program account already exists upstreamDeploy still succeeds; the local copy overrides the upstream version

On this page