SDK

Cheatcodes

Skip transactions and directly mutate Surfnet state. Fund SOL and tokens, set arbitrary account data, reset upstream-cached accounts, and stream live mainnet accounts.

Cheatcodes are state mutations that bypass the normal transaction flow. They run instantly without consuming a blockhash or paying fees, which is exactly what you want for test setup.

In Rust, cheatcodes live under Surfnet::cheatcodes(). In JS, they're methods directly on the Surfnet instance.

Fund SOL

fundSol / fund_sol sets the lamport balance on any account, creating the account if it doesn't exist.

use surfpool_sdk::{Pubkey, Surfnet};

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

cheats.fund_sol(&wallet, 1_000_000_000)?;

// Fund several accounts in one call.
let bob = Pubkey::new_unique();
let carol = Pubkey::new_unique();
cheats.fund_sol_many(&[(&bob, 2_000_000_000), (&carol, 3_000_000_000)])?;

Fund Tokens

fundToken / fund_token mints tokens to a wallet by computing the associated token account, creating it if needed, and setting the amount.

Token-2022 mints

By default fundToken uses the classic SPL Token program. For Token-2022 mints, pass the Token-2022 program ID (TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb) as the final argument.

use surfpool_sdk::{Pubkey, Surfnet};

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

let wallet = Pubkey::new_unique();
let mint: Pubkey = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
    .parse()
    .unwrap();

// Classic SPL Token mint.
cheats.fund_token(&wallet, &mint, 5_000_000, None)?;

// Token-2022 mint — pass the program id explicitly.
let token_2022: Pubkey = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
    .parse()
    .unwrap();
cheats.fund_token(&wallet, &mint, 5_000_000, Some(&token_2022))?;

// Fund many wallets with the same mint.
let mob = vec![Pubkey::new_unique(), Pubkey::new_unique()];
cheats.fund_token_many(&mob.iter().collect::<Vec<_>>(), &mint, 1_000_000, None)?;

To derive the ATA without funding, use getAta / get_ata:

let ata = cheats.get_ata(&wallet, &mint, None);

Set Arbitrary Account State

The setAccount method writes lamports, owner, and raw data bytes for any account in one call. The Rust SDK additionally exposes a SetAccount builder for more advanced fields like rent_epoch and executable.

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

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

let address = Pubkey::new_unique();
let owner = Pubkey::new_unique();

// Direct method.
cheats.set_account(&address, 500_000, &[1, 2, 3], &owner)?;

// Builder when you need finer control.
cheats.execute(
    SetAccount::new(address)
        .lamports(500_000)
        .owner(owner)
        .data(vec![1, 2, 3])
        .rent_epoch(0)
        .executable(false),
)?;

Mutate Token Account Fields

setTokenAccount updates the advanced fields of an existing token account — delegate, state, close authority, delegated amount. Use it when fundToken alone isn't enough.

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

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

let owner = Pubkey::new_unique();
let mint: Pubkey = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v".parse()?;
let delegate = Pubkey::new_unique();

cheats.execute(
    SetTokenAccount::new(owner, mint)
        .amount(2_000_000)
        .delegate(delegate)
        .delegated_amount(500_000)
        .state("initialized"),
)?;

// Later: clear the delegation.
cheats.execute(SetTokenAccount::new(owner, mint).clear_delegate())?;

Reset Accounts To Upstream State

resetAccount discards any local mutations and re-fetches the account from the upstream RPC. Pass includeOwnedAccounts: true to also reset every account owned by the target — useful when resetting a program and all of its PDAs.

Needs `remoteRpcUrl` to re-fetch

On an offline Surfnet, resetAccount clears the local copy but has no upstream to fetch from. Configure remoteRpcUrl at startup to make resets restore upstream state.

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

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

cheats.execute(
    ResetAccount::new(token_program).include_owned_accounts(true),
)?;

Stream Live Accounts

streamAccount registers an account for background polling from the upstream RPC. The local Surfnet copy stays in sync with mainnet without the test having to poll explicitly. Combine with includeOwnedAccounts to stream every PDA owned by a program.

Requires `remoteRpcUrl`

streamAccount only works on a Surfnet started with remoteRpcUrl (or remote_rpc_url in Rust). Without an upstream RPC there is nothing to stream from, and the call will return an error.

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

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

let oracle: Pubkey = "H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG".parse()?;

cheats.execute(StreamAccount::new(oracle).include_owned_accounts(false))?;

Cheatcode Builders (Rust)

The Rust SDK exposes typed builders under surfpool_sdk::cheatcodes::builders for cases where the convenience methods aren't enough. Every builder implements the CheatcodeBuilder trait and is executed via cheats.execute(builder).

BuilderConstructorCommon setters
SetAccountnew(address: Pubkey)lamports, data, owner, rent_epoch, executable
SetTokenAccountnew(owner: Pubkey, mint: Pubkey)amount, delegate, clear_delegate, state, delegated_amount, close_authority, clear_close_authority, token_program
ResetAccountnew(address: Pubkey)include_owned_accounts(bool)
StreamAccountnew(address: Pubkey)include_owned_accounts(bool)
DeployProgramnew(program_id: Pubkey), from_keypair_path(path)so_path, so_bytes, idl_path

For the full JS surface, see JS Reference. For the full Rust surface, see Rust Reference.

On this page