Skip to content

StorageProof

Get merkle paths in one of the state tries: global state, classes, or individual contract storage. A single request can query for any mix of the three types of storage proofs (classes, contracts, and storage).

Method Signature

func (provider *Provider) StorageProof(
    ctx context.Context,
    storageProofInput StorageProofInput,
) (*StorageProofResult, error)

Source: contract.go

Parameters

  • ctx (context.Context): Context for request cancellation and timeouts
  • storageProofInput (StorageProofInput): Input containing optional and required fields for the request

Returns

  • *StorageProofResult: The requested storage proofs. Note that if a requested leaf has the default value, the path to it may end in an edge node whose path is not a prefix of the requested leaf, thus effectively proving non-membership
  • error: Error if the block is not found or storage proofs are not supported

Type Definitions

StorageProofInput

type StorageProofInput struct {
    // Required. The hash of the requested block, or number (height) of the
    // requested block, or a block tag
    BlockID BlockID `json:"block_id"`
 
    // Optional. A list of the class hashes for which we want to prove
    // membership in the classes trie
    ClassHashes []*felt.Felt `json:"class_hashes,omitempty"`
 
    // Optional. A list of contracts for which we want to prove membership in the
    // global state trie
    ContractAddresses []*felt.Felt `json:"contract_addresses,omitempty"`
 
    // Optional. A list of (contract_address, storage_keys) pairs
    ContractsStorageKeys []ContractStorageKeys `json:"contracts_storage_keys,omitempty"`
}

Source: types_contract.go

ContractStorageKeys

type ContractStorageKeys struct {
    ContractAddress *felt.Felt   `json:"contract_address"`
    StorageKeys     []*felt.Felt `json:"storage_keys"`
}

Source: types_contract.go

StorageProofResult

The requested storage proofs. Note that if a requested leaf has the default value, the path to it may end in an edge node whose path is not a prefix of the requested leaf, thus effectively proving non-membership.

type StorageProofResult struct {
    ClassesProof           []NodeHashToNode   `json:"classes_proof"`
    ContractsProof         ContractsProof     `json:"contracts_proof"`
    ContractsStorageProofs [][]NodeHashToNode `json:"contracts_storage_proofs"`
    GlobalRoots            GlobalRoots        `json:"global_roots"`
}

Source: types_contract.go

NodeHashToNode

A node_hash -> node mapping of all the nodes in the union of the paths between the requested leaves and the root.

type NodeHashToNode struct {
    NodeHash *felt.Felt `json:"node_hash"`
    Node     MerkleNode `json:"node"`
}

Source: types_contract.go

MerkleNode

A node in the Merkle-Patricia tree, can be a binary node or an edge node.

type MerkleNode struct {
    Type string
    Data any
}

Source: types_contract.go

The MerkleNode unmarshals into either an EdgeNode or BinaryNode based on the JSON structure.

EdgeNode

Represents a path to the highest non-zero descendant node.

type EdgeNode struct {
    // an unsigned integer whose binary representation represents the path from
    // the current node to its highest non-zero descendant (bounded by 2^251)
    Path NumAsHex `json:"path"`
 
    // the length of the path (bounded by 251)
    Length uint `json:"length"`
 
    // the hash of the unique non-zero maximal-height descendant node
    Child *felt.Felt `json:"child"`
}

Source: types_contract.go

BinaryNode

An internal node whose both children are non-zero.

type BinaryNode struct {
    // the hash of the left child
    Left *felt.Felt `json:"left"`
 
    // the hash of the right child
    Right *felt.Felt `json:"right"`
}

Source: types_contract.go

ContractsProof

type ContractsProof struct {
    // The nodes in the union of the paths from the contracts tree root to the
    // requested leaves
    Nodes []NodeHashToNode `json:"nodes"`
 
    ContractLeavesData []ContractLeavesData `json:"contract_leaves_data"`
}

Source: types_contract.go

ContractLeavesData

The nonce and class hash for each requested contract address, in the order in which they appear in the request. These values are needed to construct the associated leaf node.

type ContractLeavesData struct {
    Nonce       *felt.Felt `json:"nonce"`
    ClassHash   *felt.Felt `json:"class_hash"`
    StorageRoot *felt.Felt `json:"storage_root,omitempty"`
}

Source: types_contract.go

GlobalRoots

type GlobalRoots struct {
    ContractsTreeRoot *felt.Felt `json:"contracts_tree_root"`
    ClassesTreeRoot   *felt.Felt `json:"classes_tree_root"`
 
    // the associated block hash (needed in case the caller used a block tag
    // for the block_id parameter)
    BlockHash *felt.Felt `json:"block_hash"`
}

Source: types_contract.go

Basic Usage

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
    godotenv.Load()
 
    // Get RPC URL from environment variable
    rpcURL := os.Getenv("STARKNET_RPC_URL")
    if rpcURL == "" {
        log.Fatal("STARKNET_RPC_URL environment variable is not set")
    }
 
    // Initialize provider
    provider, err := rpc.NewProvider(context.Background(), rpcURL)
    if err != nil {
        log.Fatal(err)
    }
 
    // Contract address to get storage proof for
    contractAddr, _ := new(felt.Felt).SetString("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7")
 
    // Storage keys to prove
    storageKey1, _ := new(felt.Felt).SetString("0x1")
    storageKey2, _ := new(felt.Felt).SetString("0x2")
 
    // Create storage proof input
    input := rpc.StorageProofInput{
        BlockID: rpc.BlockID{Tag: "latest"},
        ContractsStorageKeys: []rpc.ContractStorageKeys{
            {
                ContractAddress: contractAddr,
                StorageKeys:     []*felt.Felt{storageKey1, storageKey2},
            },
        },
    }
 
    // Get storage proof
    proof, err := provider.StorageProof(context.Background(), input)
    if err != nil {
        log.Fatal(err)
    }
 
    // Access proof data
    fmt.Printf("Contracts Tree Root: %s\n", proof.GlobalRoots.ContractsTreeRoot)
    fmt.Printf("Classes Tree Root: %s\n", proof.GlobalRoots.ClassesTreeRoot)
    fmt.Printf("Block Hash: %s\n", proof.GlobalRoots.BlockHash)
    fmt.Printf("Number of contract proof nodes: %d\n", len(proof.ContractsProof.Nodes))
    fmt.Printf("Number of storage proofs: %d\n", len(proof.ContractsStorageProofs))
}

For more comprehensive examples including proving class membership, proving contract existence, mixed proof requests, processing Merkle nodes, and historical state proofs, see RPC Examples - StorageProof.

Important Notes

  1. Non-membership Proofs: If a requested leaf has the default value, the path may end in an edge node whose path is not a prefix of the requested leaf, thus effectively proving non-membership.

  2. Node Support: Not all Starknet nodes support storage proofs. Make sure your RPC endpoint implements starknet_getStorageProof.

  3. Block Limitations:

    • Cannot use pending blocks (proofs require finalized state)
    • Historical proofs may not be available for very old blocks
    • When using block tags, the GlobalRoots.BlockHash field contains the actual block hash
  4. Performance: Storage proofs can be large for deep Merkle trees. Consider:

    • Batching multiple keys in a single request
    • Caching proofs for frequently verified values
    • Using recent blocks when possible (faster to generate)

RPC Specification

  • Method: starknet_getStorageProof
  • Version: RPC v0.9.0+

Related Methods