Skip to content

TraceBlockTransactions

Retrieves execution traces for all transactions in a specific block, providing detailed call flow, events, messages, and state changes for each transaction. This method is useful for analyzing block-level activity and debugging multiple transactions at once.

Method Signature

func (provider *Provider) TraceBlockTransactions(
	ctx context.Context,
	blockID BlockID,
) ([]Trace, error)

Source: trace.go

Parameters

  • ctx (context.Context): Context for request cancellation and timeout
  • blockID (BlockID): Block identifier (number, hash, or tag)

Returns

  • []Trace: Array of execution traces, one for each transaction in the block
  • error: Error if the request fails

Type Definitions

  1. Container Type (Trace) wraps each transaction's TxnTrace with its transaction hash for identification.
  2. Trace Types (TxnTrace) can be InvokeTxnTrace, DeclareTxnTrace, DeployAccountTxnTrace, or L1HandlerTxnTrace depending on the transaction type.
  3. Invocation Types (FnInvocation, ExecInvocation) represent function calls with parameters, results, nested calls, events, and messages.
  4. State Changes (StateDiff) show storage updates, deployed contracts, declared classes, and nonce changes caused by the transactions.

The method accepts a BlockID and returns an array of Trace objects, each containing the full execution trace and transaction hash for every transaction in the block.

Trace

type Trace struct {
	TraceRoot TxnTrace   `json:"trace_root,omitempty"`
	TxnHash   *felt.Felt `json:"transaction_hash,omitempty"`
}

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

InvokeTxnTrace

type InvokeTxnTrace struct {
	ValidateInvocation *FnInvocation `json:"validate_invocation,omitempty"`
	// the trace of the __execute__ call
	ExecuteInvocation     ExecInvocation     `json:"execute_invocation"`
	FeeTransferInvocation *FnInvocation      `json:"fee_transfer_invocation,omitempty"`
	StateDiff             *StateDiff         `json:"state_diff,omitempty"`
	Type                  TransactionType    `json:"type"`
	ExecutionResources    ExecutionResources `json:"execution_resources"`
}

Source: types_trace.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 built

    Source: block.go

Usage Example

package main
 
import (
	"context"
	"fmt"
	"log"
	"os"
 
	"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()
 
	// Trace all transactions in a specific block
	blockID := rpc.WithBlockNumber(99433)
	traces, err := provider.TraceBlockTransactions(ctx, blockID)
	if err != nil {
		log.Fatal(err)
	}
 
	fmt.Printf("Block has %d transactions\n\n", len(traces))
 
	// Analyze each transaction trace
	for i, trace := range traces {
		fmt.Printf("Transaction %d:\n", i+1)
		fmt.Printf("  Hash: %s\n", trace.TxnHash)
 
		// Type assert to analyze specific trace types
		if invokeTrace, ok := trace.TraceRoot.(rpc.InvokeTxnTrace); ok {
			fmt.Printf("  Type: %s\n", invokeTrace.Type)
			fmt.Printf("  Total L2 Gas: %d\n", invokeTrace.ExecutionResources.L2Gas)
 
			// Count nested calls
			nestedCallCount := len(invokeTrace.ExecuteInvocation.NestedCalls)
			fmt.Printf("  Nested Calls: %d\n", nestedCallCount)
 
			// Check for state changes
			if invokeTrace.StateDiff != nil {
				fmt.Printf("  Storage Updates: %d contracts\n", len(invokeTrace.StateDiff.StorageDiffs))
				fmt.Printf("  Nonce Updates: %d accounts\n", len(invokeTrace.StateDiff.Nonces))
			}
		}
 
		fmt.Println()
	}
}

Error Handling

traces, err := provider.TraceBlockTransactions(ctx, blockID)
if err != nil {
	if errors.Is(err, rpc.ErrBlockNotFound) {
		log.Printf("Block not found")
		return
	}
	log.Printf("Error retrieving traces: %v", err)
	return
}
 
// Process traces
totalGas := uint(0)
for _, trace := range traces {
	if invokeTrace, ok := trace.TraceRoot.(rpc.InvokeTxnTrace); ok {
		totalGas += invokeTrace.ExecutionResources.L2Gas
	}
}
fmt.Printf("Total L2 gas for block: %d\n", totalGas)

Common Use Cases

  • Analyzing block-level gas consumption across all transactions.
  • Debugging multiple failed transactions in the same block.
  • Building block explorers with detailed execution traces.
  • Monitoring state changes and contract interactions at the block level.