SimulateTransactions
Simulates a sequence of transactions on a specified state and generates execution traces and fee estimates without submitting the transactions to the network. Each transaction is simulated on the state resulting from applying all previous transactions in the sequence.
Method Signature
func (provider *Provider) SimulateTransactions(
ctx context.Context,
blockID BlockID,
txns []BroadcastTxn,
simulationFlags []SimulationFlag,
) ([]SimulatedTransaction, error)Source: trace.go
Parameters
ctx(context.Context): Context for request cancellation and timeoutblockID(BlockID): Block identifier specifying the state to simulate againsttxns([]BroadcastTxn): Array of transactions to simulate sequentiallysimulationFlags([]SimulationFlag): Flags controlling simulation behavior (e.g., skip validation, skip fee charging)
Returns
[]SimulatedTransaction: Array of simulation results with traces and fee estimates for each transactionerror: Error if the request fails
Type Definitions
- Result Type (
SimulatedTransaction) embeds bothTxnTrace(execution details) andFeeEstimation(gas and fee information) for each simulated transaction. - Trace Types (
TxnTrace) can beInvokeTxnTrace,DeclareTxnTrace,DeployAccountTxnTrace, orL1HandlerTxnTracewith complete call flow and revert information. - Input Types (
BroadcastTxn,SimulationFlag,BlockID) define the transactions to simulate, simulation behavior, and the state to simulate against. - Fee Estimation (
FeeEstimation) provides detailed gas consumption and pricing for each simulated transaction.
The method accepts an array of BroadcastTxn and returns an array of SimulatedTransaction, each containing both the execution trace and fee estimate without actually submitting transactions to the network.
SimulatedTransaction
type SimulatedTransaction struct {
TxnTrace `json:"transaction_trace"`
FeeEstimation `json:"fee_estimation"`
}Source: types_trace.go
TxnTrace
type TxnTrace interface{}
var (
_ TxnTrace = (*InvokeTxnTrace)(nil)
_ TxnTrace = (*DeclareTxnTrace)(nil)
_ TxnTrace = (*DeployAccountTxnTrace)(nil)
_ TxnTrace = (*L1HandlerTxnTrace)(nil)
)Source: types_trace.go
BroadcastTxn
type BroadcastTxn interface{}
var (
_ BroadcastTxn = (*BroadcastInvokeTxnV3)(nil)
_ BroadcastTxn = (*BroadcastDeclareTxnV3)(nil)
_ BroadcastTxn = (*BroadcastDeployAccountTxnV3)(nil)
)Source: types_broadcast_transaction.go
SimulationFlag
type SimulationFlag string
const (
SkipFeeCharge SimulationFlag = "SKIP_FEE_CHARGE"
SkipValidate SimulationFlag = "SKIP_VALIDATE"
)Source: types_trace.go
FeeEstimation
type FeeEstimation struct {
FeeEstimationCommon
// Units in which the fee is given, can only be FRI
Unit PriceUnitFri `json:"unit"`
}Source: types.go
BlockID
The blockID parameter specifies which block state to query. You can identify a block in three ways:
type BlockID struct {
Number *uint64 `json:"block_number,omitempty"`
Hash *felt.Felt `json:"block_hash,omitempty"`
Tag BlockTag `json:"tag,omitempty"`
}Source: types_block.go
There are helper functions for creating BlockID. So Instead of manually creating the BlockID struct, use these convenience functions.
-
By Block Number - Get a specific block by its height
blockID := rpc.WithBlockNumber(123456)Source: block.go
-
By Block Hash - Get a specific block by its hash
hash, _ := new(felt.Felt).SetString("0x1234...") blockID := rpc.WithBlockHash(hash)Source: block.go
-
By Block Tag - Get a dynamic block reference
blockID := rpc.WithBlockTag("latest") // Latest accepted block blockID := rpc.WithBlockTag("pending") // Block currently being builtSource: block.go
Usage Example
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/starknet.go/rpc"
"github.com/joho/godotenv"
)
func main() {
// Load environment variables from .env file
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
// Get RPC URL from environment variable
rpcURL := os.Getenv("STARKNET_RPC_URL")
if rpcURL == "" {
log.Fatal("STARKNET_RPC_URL not found in .env file")
}
// Initialize provider
provider, err := rpc.NewProvider(context.Background(), rpcURL)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// Get account address and current nonce
accountAddress, _ := new(felt.Felt).SetString("0x36d67ab362562a97f9fba8a1051cf8e37ff1a1449530fb9f1f0e32ac2da7d06")
nonce, err := provider.Nonce(ctx, rpc.WithBlockTag(rpc.BlockTagLatest), accountAddress)
if err != nil {
log.Fatal(err)
}
// Build an invoke transaction
contractAddress, _ := new(felt.Felt).SetString("0x669e24364ce0ae7ec2864fb03eedbe60cfbc9d1c74438d10fa4b86552907d54")
entrypoint, _ := new(felt.Felt).SetString("0x2f0b3c5710379609eb5495f1ecd348cb28167711b73609fe565a72734550354")
invokeTx := rpc.BroadcastInvokeTxnV3{
Type: rpc.TransactionTypeInvoke,
Version: rpc.TransactionV3,
Nonce: nonce,
SenderAddress: accountAddress,
Signature: []*felt.Felt{},
Calldata: []*felt.Felt{
new(felt.Felt).SetUint64(1),
contractAddress,
entrypoint,
new(felt.Felt).SetUint64(2),
new(felt.Felt).SetUint64(256),
new(felt.Felt).SetUint64(0),
},
ResourceBounds: &rpc.ResourceBoundsMapping{
L1DataGas: rpc.ResourceBounds{
MaxAmount: "0x1e0",
MaxPricePerUnit: "0x922",
},
L1Gas: rpc.ResourceBounds{
MaxAmount: "0x0",
MaxPricePerUnit: "0xfbfdefe2186",
},
L2Gas: rpc.ResourceBounds{
MaxAmount: "0x16eea0",
MaxPricePerUnit: "0x1830e58f7",
},
},
Tip: "0x0",
PayMasterData: []*felt.Felt{},
AccountDeploymentData: []*felt.Felt{},
NonceDataMode: rpc.DAModeL1,
FeeMode: rpc.DAModeL1,
}
// Simulate with SKIP_VALIDATE and SKIP_FEE_CHARGE flags
// SKIP_VALIDATE skips signature and nonce validation
// SKIP_FEE_CHARGE skips fee charging, useful when resource bounds may be outdated
simulationFlags := []rpc.SimulationFlag{rpc.SkipValidate, rpc.SkipFeeCharge}
results, err := provider.SimulateTransactions(
ctx,
rpc.WithBlockTag(rpc.BlockTagLatest),
[]rpc.BroadcastTxn{&invokeTx},
simulationFlags,
)
if err != nil {
log.Fatal(err)
}
// Analyze simulation results
for i, result := range results {
fmt.Printf("\nTransaction %d:\n", i+1)
// Check fee estimate
fmt.Printf("Fee Estimate:\n")
fmt.Printf(" Overall Fee: %s FRI\n", result.FeeEstimation.OverallFee)
fmt.Printf(" L2 Gas Consumed: %s\n", result.FeeEstimation.L2GasConsumed)
fmt.Printf(" L2 Gas Price: %s\n", result.FeeEstimation.L2GasPrice)
// Check for reverts
if invokeTrace, ok := result.TxnTrace.(rpc.InvokeTxnTrace); ok {
if invokeTrace.ExecuteInvocation.IsReverted {
fmt.Printf(" Status: REVERTED\n")
fmt.Printf(" Revert Reason: %s\n", invokeTrace.ExecuteInvocation.RevertReason)
} else {
fmt.Printf(" Status: SUCCESS\n")
fmt.Printf(" Nested Calls: %d\n", len(invokeTrace.ExecuteInvocation.NestedCalls))
}
}
}
}Error Handling
results, err := provider.SimulateTransactions(ctx, blockID, []rpc.BroadcastTxn{tx}, flags)
if err != nil {
if errors.Is(err, rpc.ErrTxnExec) {
log.Printf("Unexpected execution error during simulation")
return
}
if errors.Is(err, rpc.ErrBlockNotFound) {
log.Printf("Block not found")
return
}
log.Printf("Error simulating transactions: %v", err)
return
}
// Check for reverted transactions in results
for i, result := range results {
if invokeTrace, ok := result.TxnTrace.(rpc.InvokeTxnTrace); ok {
if invokeTrace.ExecuteInvocation.IsReverted {
fmt.Printf("Transaction %d reverted: %s\n", i, invokeTrace.ExecuteInvocation.RevertReason)
}
}
}Common Use Cases
- Testing transactions before submitting them to the network to avoid failures and wasted fees.
- Estimating gas costs for transactions with accurate fee calculations.
- Debugging transaction execution by examining detailed traces and identifying revert reasons.
- Simulating sequential transactions to verify state transitions across multiple operations.

