Simulating Transactions
Simulating Transactions
POST /v2/transaction-simulations runs a transaction against the cluster without submitting it. It's the cheapest way to catch instruction errors, estimate compute budget, and confirm post-state token balances before you commit to a signature.
Request shape
curl -X POST https://api.piratecrew.fun/v2/transaction-simulations \
-H "Authorization: Bearer $PIRATE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transaction": "AQABAo...<base64 VersionedTransaction>...",
"sig_verify": false
}'| Field | Type | Default | Notes |
|---|---|---|---|
transaction | string (base64) | required | A serialized VersionedTransaction. Signed or unsigned. |
sig_verify | boolean | false | If true, the cluster verifies signatures before simulating. Keep false for pre-flight on unsigned transactions. |
Response shape
The response wraps the standard Solana RpcResponseAndContext<SimulatedTransactionResponse>:
{
"data": {
"context": { "slot": 271234567 },
"value": {
"err": null,
"logs": [
"Program ComputeBudget111111111111111111111111111111 invoke [1]",
"Program ComputeBudget111111111111111111111111111111 success",
"Program dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN invoke [1]",
"Program log: Instruction: ClaimCreatorTradingFee",
"Program dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN success"
],
"units_consumed": 87420,
"return_data": null,
"accounts": null,
"post_balances": ["9991820000", "5097600", "1"],
"post_token_balances": [
{
"account_index": 4,
"mint": "MintAddr...",
"owner": "OwnerPubkey...",
"ui_token_amount": {
"amount": "1234567",
"decimals": 6,
"ui_amount": 1.234567,
"ui_amount_string": "1.234567"
}
}
]
}
},
"meta": { "request_id": "req_…" }
}The two fields you care about most:
data.value.err—nullmeans the transaction would land. Anything else is a structured Solana error (InstructionError,InsufficientFundsForRent,BlockhashNotFound, etc).data.value.logs— the program log stream. If a program panicked, the panic message is here. If youmsg!()'d something from on-chain code, it's here.
Use cases
Pre-flight a complex tx
The DBC fee-split flow builds a single transaction that claims partner fees, swaps if needed, and distributes to N recipients. Simulating before submitting tells you up front whether the entire sequence would land — without burning a slot or a priority fee:
curl -X POST https://api.piratecrew.fun/v2/pools/POOL_ADDRESS/fee-claims \
-H "Authorization: Bearer $PIRATE_API_KEY" \
-d '{ "kind": "split", "payer": "PAYER", "splits": [...], "mode": "unsigned" }'
# -> { "data": { "transaction": "AQA...", ... } }
curl -X POST https://api.piratecrew.fun/v2/transaction-simulations \
-H "Authorization: Bearer $PIRATE_API_KEY" \
-d '{ "transaction": "AQA...", "sig_verify": false }'If value.err is null and units_consumed is reasonable, sign and submit. If not, fix and re-build.
Debug an InstructionError
InstructionErrorWhen value.err looks like:
{
"err": {
"InstructionError": [
1,
{ "Custom": 6001 }
]
}
}The first element (1) is the instruction index that failed. The second is the program's custom error code — for Anchor programs, look up 6001 in the program's errors.rs (e.g. 6001 = NotEnoughBalance). The logs array usually surfaces the Anchor error name too:
"Program log: AnchorError occurred. Error Code: NotEnoughBalance. ..."
Fix the inputs (more SOL in the payer, correct PDA, etc) and re-simulate.
Estimate compute units for a priority fee
value.units_consumed is the actual CU spend in the simulated context — perfect for sizing a compute-budget instruction. A typical pattern:
- Simulate the tx with no compute-budget instruction.
- Read
units_consumed— call itcu. - Build the real tx with
ComputeBudgetProgram.setComputeUnitLimit({ units: Math.ceil(cu * 1.1) })and a priority-fee instruction tuned to current network conditions. - Re-simulate to confirm the new limit is enough.
The 10% buffer absorbs noise from cluster state changes between simulation and submission.
Verify post-state token balances
post_token_balances shows what each token account would look like after the simulated transaction. Great for confirming that:
- The expected number of tokens lands in the recipient ATA.
- A swap's slippage didn't tip past your tolerance.
- A burn actually decremented the supply by the right amount.
This is cheaper than submitting, polling for confirmation, then re-fetching all the accounts.
Limits
- Simulation reads against the current cluster state — not a fork. State changes between simulation and submission can still cause the real transaction to fail.
- Simulation does not capture priority-fee competition. A simulation can pass and the real submission still get dropped if the priority fee is too low at landing time.
- Compute budget caps still apply. A tx that exceeds the per-tx CU limit will fail both simulation and submission.
Treat simulation as a high-signal pre-flight, not a guarantee.