Simulate a Solana transaction locally using LiteSVM (simulate, alias: sim).
sonar simulate --help groups options into Input & RPC, State Preparation,
Simulation Controls, and Output & Debug.
# Simulate a transaction with raw Base58/Base64 data
sonar simulate <BASE58_OR_BASE64_STRING> --rpc-url https://api.mainnet-beta.solana.com
# Simulate using transaction signature (auto-detected)
sonar simulate 2gTzNX3zLNhhmJaY44LycEgF8UMadrKeDLHz8rgcQVbXWVU4bs8fLBzWKhvAqKBeo2ttqyXsCeqUW47dfW6775Wu \
--rpc-url https://api.mainnet-beta.solana.com
# Bundle simulation (multiple transactions)
sonar simulate <TX1> <TX2> <TX3> --rpc-url https://api.mainnet-beta.solana.com
# Instruction input mode (one synthesized transaction)
sonar simulate --payer <PAYER_PUBKEY> \
--ix 'program=MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr data=0x68656c6c6f'
# Read transaction from stdin (omit TX to read from pipe)
cat ./transaction.txt | sonar simulate --rpc-url <RPC_URL>
# Show balance changes and instruction details
sonar simulate <TX> --rpc-url <RPC_URL> -b -d
# JSON output
sonar simulate <TX> --rpc-url <RPC_URL> --jsonUse --ix to simulate one or more raw instructions without first building a
signed transaction. Sonar creates one unsigned legacy transaction and runs
the normal simulation pipeline. --payer is optional: when omitted, sonar
uses a deterministic placeholder pubkey (sha256("sonar-payer"))
auto-funded with 1 SOL for the run; pass --payer <PUBKEY> to override.
--ix auto-detects each value's format, so you can mix DSL, JSON, and files
freely across repeated flags (they apply in CLI order):
- named-field DSL — anything that isn't JSON or a file path
- inline JSON — values starting with
{or[ @<path>— read from a file; the file's contents may themselves be either JSON or DSL.~is expanded and@/dev/stdinworks for piping.
# Single instruction via DSL. `data` is hex and may start with 0x.
sonar simulate --payer <PAYER_PUBKEY> \
--ix 'program=<PROGRAM_ID> accounts=<ACCOUNT>:sw data=0x01020304'
# Multiple instructions in one atomic transaction.
sonar simulate --payer <PAYER_PUBKEY> \
--ix 'program=<PROGRAM_A> data=0x01' \
--ix 'program=<PROGRAM_B> accounts=<ACCOUNT>:w data=0x02'
# Inline JSON when structured input is easier to generate.
sonar simulate --payer <PAYER_PUBKEY> \
--ix '{"program":"<PROGRAM_ID>","accounts":[{"pubkey":"<ACCOUNT>","is_signer":true,"is_writable":true}],"data":"0x01020304"}'
# Base64 / base58 data via the encoding field (works in DSL and JSON).
# base58 matches how Solana RPC encodes compiled-instruction data.
sonar simulate --payer <PAYER_PUBKEY> \
--ix 'program=<PROGRAM_ID> data=AQIDBA== encoding=base64'
sonar simulate --payer <PAYER_PUBKEY> \
--ix '{"program":"<PROGRAM_ID>","data":"<BASE58_DATA>","encoding":"base58"}'
# Read from a file (curl-style `@` prefix). `@/dev/stdin` works for piping.
sonar simulate --payer <PAYER_PUBKEY> --ix @instructions.jsonInstruction data is decoded as hex by default in both forms; set the
optional encoding field to hex, base64, or base58 to choose otherwise.
A leading 0x/0X is accepted (and stripped) for hex; it is not a format
switch, so base64/base58 must be selected explicitly via encoding.
DSL fields (the non-JSON, non-@ form):
program(orprogram_id): program pubkey. Required.accounts: optional comma-separated account metas. Account flags ares(signer) andw(writable). Omit the:flagssuffix for a read-only non-signer.data: optional instruction data. Decoded perencoding(default hex). Empty data via omitting the field, ordata=0x.encoding: optionalhex(default),base64, orbase58.
JSON fields (inline or from an @<path> file):
program(orprogram_id): program pubkey.accounts: optional ordered account metas. Each account requirespubkey,is_signer, andis_writable— same shape as Solana'sAccountMetaso an existing meta object copies over verbatim.data: optional instruction data. Decoded perencoding(default hex). Empty data via omitting the field,"", or"0x".encoding: optional"hex"(default),"base64", or"base58".
Override on-chain programs or accounts with local files for testing:
# Override a program with a local .so file
sonar simulate <TX> \
--rpc-url https://api.mainnet-beta.solana.com \
--override TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA=./custom_token.so
# Override an account with a local .json file
sonar simulate <TX> \
--rpc-url https://api.mainnet-beta.solana.com \
--override <PUBKEY>=./account.jsonFund system accounts with SOL or token accounts for testing:
# Fund a single account with SOL
sonar simulate <TX> \
--rpc-url https://api.mainnet-beta.solana.com \
--fund-sol DPLezAkFZ5sFaBXMWt3J2StQwYtcqecUipWSP7YfrLth=10.5sol
# Fund multiple accounts
sonar simulate <TX> \
--rpc-url https://api.mainnet-beta.solana.com \
--fund-sol <PUBKEY1>=100.0sol \
--fund-sol <PUBKEY2>=2.75sol
# Fund a token account (raw amount)
sonar simulate <TX> \
--rpc-url https://api.mainnet-beta.solana.com \
--fund-token <TOKEN_ACCOUNT>=1000000
# Fund a token account with explicit mint (mint auto-detected if account exists on-chain)
sonar simulate <TX> \
--rpc-url https://api.mainnet-beta.solana.com \
--fund-token <ACCOUNT>:<MINT>=1000000
# Fund a new token account with explicit mint and owner
# Owner is required when the token account does not already exist on-chain
sonar simulate <TX> \
--rpc-url https://api.mainnet-beta.solana.com \
--fund-token <ACCOUNT>:<MINT>:<OWNER>=1000000
# Fund using decimal amount (uses mint decimals, e.g. 1.5 USDC = 1500000 raw units)
sonar simulate <TX> \
--rpc-url https://api.mainnet-beta.solana.com \
--fund-token <TOKEN_ACCOUNT>=1.5# Patch an account inside instruction 2, account 3
# Format: <IX>.<ACCOUNT>=<NEW_PUBKEY>[:w] with 1-based indices
# Account flags follow the same grammar as `--ix accounts=`: append `:w` for
# writable, omit the suffix for read-only (the default). The signer flag `s` is
# rejected here — declare signers when building the instruction with `--ix`.
sonar simulate <TX> --rpc-url <RPC_URL> \
--patch-ix-account 2.3=<NEW_PUBKEY>:w
# Insert an account at a specific position within instruction 1's account list
# Format: <IX>.<POSITION>=<PUBKEY>[:w] with 1-based indices
# Existing accounts at and after POSITION shift right by one.
# POSITION may equal current_count + 1 to insert at the end (push semantics).
sonar simulate <TX> --rpc-url <RPC_URL> \
--insert-ix-account 1.3=<PUBKEY>:w
# Remove an account at a specific position from instruction 1's account list
# Format: <IX>.<POSITION> with 1-based indices
# Subsequent accounts shift left by one. The static account_keys table is left
# intact (unreferenced keys remain, mirroring --patch-ix-account behavior).
sonar simulate <TX> --rpc-url <RPC_URL> \
--remove-ix-account 1.4 \
--remove-ix-account 1.2
# Ordering note for instruction-account ops:
# Ops apply in flag order (all patches → all inserts → all removes); within each
# flag, CLI argument order is preserved. Positions are interpreted at apply time
# (not against the original list). To express positions relative to the
# pre-mutation list, list ops in descending position order — e.g. above we
# remove position 4 before position 2 so both refer to the original numbering.
#### Whole-instruction insert / remove
```bash
# Remove a whole instruction (1-based index). Subsequent instructions shift
# left by one. The account_keys table is left intact (unreferenced keys remain
# loadable, mirroring --remove-ix-account).
sonar simulate <TX> --rpc-url <RPC_URL> --remove-ix 2
# Insert a whole instruction at a 1-based position. POS=1 prepends;
# POS = current_count + 1 appends. The new instruction becomes the POS-th.
# <SPEC> reuses the --ix grammar: DSL, JSON object/array, or @file.
sonar simulate <TX> --rpc-url <RPC_URL> \
--insert-ix 1='program=MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr data=0x6869'
# A JSON array spec expands to consecutive positions starting at POS:
sonar simulate <TX> --rpc-url <RPC_URL> \
--insert-ix 2=@extra_instructions.json
# Signer accounts are supported: new signers are inserted into the message's
# signer section (with a placeholder signature), and the header's signer counts
# are bumped accordingly. An account already present as a non-signer cannot be
# promoted to signer in the same op.
#
# Privilege semantics: Solana message privileges are the union across all
# instructions, so referencing an existing account never weakens it. A key that
# is already writable stays writable even if this instruction lists it
# read-only, and a key already a signer stays a signer. Inserted references can
# only PROMOTE an existing key (e.g. readonly -> writable); they never demote
# it, so existing instructions keep the privileges they need.
# Accounts the inserted instruction introduces that weren't in the original
# transaction are fetched/dependency-resolved automatically before simulation.
# Ordering note for whole-instruction ops:
# They run as a restructuring phase BEFORE account-level ops (--patch-ix-account
# etc.) and data patches (--patch-ix-data), so the latter target the
# post-restructure list. Within the phase, all --insert-ix apply first (in CLI
# order) then all --remove-ix. Positions are interpreted at apply time; to
# express positions relative to the pre-mutation list, list ops in descending
# position order.sonar simulate --rpc-url <RPC_URL>
--patch-ix-data 1=8:0x01020304
sonar simulate --rpc-url <RPC_URL>
--patch-account-data =:<HEX_DATA>
sonar simulate --rpc-url <RPC_URL>
--close-account
### Cache & Offline Replay
```bash
# Cache accounts for offline replay
sonar simulate <TX> --rpc-url <RPC_URL> --cache
# Replay from cache (no network; uses ~/.sonar/cache/ when cache is complete)
sonar simulate <TX> --cache
# Force refresh cache
sonar simulate <TX> --rpc-url <RPC_URL> --cache --refresh-cache
# Override clock timestamp and slot (Unix or RFC3339)
sonar simulate <TX> --rpc-url <RPC_URL> \
--timestamp 1700000000 --slot 250000000
sonar simulate <TX> --rpc-url <RPC_URL> \
--timestamp 2024-01-01T00:00:00Z --slot 250000000
# Verify transaction signatures during simulation
sonar simulate <TX> --rpc-url <RPC_URL> --check-sig
# Load Anchor IDL files from a custom directory
sonar simulate <TX> --rpc-url <RPC_URL> --idl-dir /path/to/idl/files/# Always print raw instruction data, even when parser succeeds
sonar simulate <TX> --rpc-url <RPC_URL> --raw-ix-data
# Print raw logs and full instruction details
sonar simulate <TX> --rpc-url <RPC_URL> --raw-log --show-ix-detailDecode and display a raw transaction without simulation (decode, alias: dec):
sonar decode <TX> --rpc-url https://api.mainnet-beta.solana.com
sonar decode <TX> --rpc-url <RPC_URL> --json
# Bundle decode (multiple TXs): --json outputs a single JSON array [{...}, {...}]
# Parseable by jq: sonar decode <TX1> <TX2> --json --rpc-url <RPC_URL> | jq .
sonar decode <TX1> <TX2> --rpc-url <RPC_URL> --json
# Read transaction from stdin (omit TX to read from pipe)
cat ./transaction.txt | sonar decode --rpc-url <RPC_URL>
# Always print raw instruction data, even when parser succeeds
sonar decode <TX> --rpc-url <RPC_URL> --raw-ix-data# decode also uses cache by default (signature/raw-tx resolution + account loading)
sonar decode <TX_OR_SIGNATURE> --rpc-url <RPC_URL>
# --no-cache disables account cache but still allows cached raw-tx reuse for signatures
sonar decode <TX_OR_SIGNATURE> --rpc-url <RPC_URL> --no-cache
# --refresh-cache bypasses both raw-tx/account cache and forces RPC
sonar decode <TX_OR_SIGNATURE> --rpc-url <RPC_URL> --refresh-cache
sonar decode <TX_OR_SIGNATURE> --rpc-url <RPC_URL> --cache-dir /path/to/cacheReplay a confirmed transaction. Fetches it by signature from RPC and reconstructs the execution trace from on-chain metadata (no local simulation).
# Replay a confirmed transaction
sonar replay <SIGNATURE> --rpc-url https://api.mainnet-beta.solana.com
# Show balance changes and instruction details
sonar replay <SIGNATURE> --rpc-url <RPC_URL> -b -d
# JSON output
sonar replay <SIGNATURE> --rpc-url <RPC_URL> --json
# Print raw logs instead of structured output
sonar replay <SIGNATURE> --rpc-url <RPC_URL> --raw-log
# Always print raw instruction data, even when parser succeeds
sonar replay <SIGNATURE> --rpc-url <RPC_URL> --raw-ix-data
# Load Anchor IDL files from a custom directory
sonar replay <SIGNATURE> --rpc-url <RPC_URL> --idl-dir /path/to/idlsManage cached account data for offline simulation:
# Cache accounts for offline replay (writes to ~/.sonar/cache/ by default)
sonar simulate <TX> --rpc-url <RPC_URL> --cache
# Replay from cache (no network access when cache is complete)
sonar simulate <TX> --cache
# Force refresh cache
sonar simulate <TX> --rpc-url <RPC_URL> --refresh-cache
# Custom cache directory (implies --cache)
sonar simulate <TX> --rpc-url <RPC_URL> --cache-dir /path/to/cache
# Manage cache
sonar cache list
sonar cache clean --older-than 7d
sonar cache info <KEY>Cache metadata schema (_meta.json) uses a transactions array:
{
"type": "single",
"transactions": [
{ "input": "<original input>", "raw_tx": "<base64 tx>", "resolved_from": "raw_input|cache|rpc" }
]
}