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).
| Builder | Constructor | Common setters |
|---|---|---|
SetAccount | new(address: Pubkey) | lamports, data, owner, rent_epoch, executable |
SetTokenAccount | new(owner: Pubkey, mint: Pubkey) | amount, delegate, clear_delegate, state, delegated_amount, close_authority, clear_close_authority, token_program |
ResetAccount | new(address: Pubkey) | include_owned_accounts(bool) |
StreamAccount | new(address: Pubkey) | include_owned_accounts(bool) |
DeployProgram | new(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.
Configuration
Configure a Surfnet at startup — remote RPC fallback, block production mode, slot timing, airdrops, feature gates, and custom payers.
Time Travel
Jump the Surfnet clock forward to an absolute slot, epoch, or Unix timestamp. Useful for tests that exercise vesting schedules, lockups, expiration windows, or epoch boundaries.