Skip to content

StorageAt

Retrieves the storage value of a contract at a specific key and block state. This method allows you to read the persistent state variables stored in a contract's storage.

Method Signature

func (provider *Provider) StorageAt(
    ctx context.Context,
    contractAddress *felt.Felt,
    key string,
    blockID BlockID,
) (string, error)

Source: contract.go

Parameters

  • ctx (context.Context): Context for request cancellation and timeout
  • contractAddress (*felt.Felt): The address of the contract
  • key (string): The storage key to retrieve (can be a variable name or hex string)
  • blockID (BlockID): Block identifier (number, hash, or tag)

Returns

  • string: The storage value at the given key (as hex string)
  • error: Error if the request fails

Type Definitions

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"
    "encoding/hex"
    "fmt"
    "log"
    "math/big"
    "os"
    "strings"
 
    "github.com/NethermindEth/starknet.go/rpc"
    "github.com/NethermindEth/starknet.go/utils"
    "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()
 
    // ETH contract address on Starknet (SPELOIA)
    ethContract, err := utils.HexToFelt("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7")
    if err != nil {
        log.Fatal(err)
    }
 
    // Read token name from storage using variable name
    // The method automatically converts "ERC20_name" to its storage key
    nameValue, err := provider.StorageAt(ctx, ethContract, "ERC20_name", rpc.WithBlockTag("latest"))
    if err != nil {
        log.Fatal(err)
    }
 
    // Decode the hex-encoded string to readable text
    tokenName := decodeShortString(nameValue)
    fmt.Printf("Token Name: %s (raw: %s)\n", tokenName, nameValue)
 
    // Read token symbol from storage
    symbolValue, err := provider.StorageAt(ctx, ethContract, "ERC20_symbol", rpc.WithBlockTag("latest"))
    if err != nil {
        log.Fatal(err)
    }
    tokenSymbol := decodeShortString(symbolValue)
    fmt.Printf("Token Symbol: %s (raw: %s)\n", tokenSymbol, symbolValue)
 
    // Read decimals value and convert to integer
    decimalsValue, err := provider.StorageAt(ctx, ethContract, "ERC20_decimals", rpc.WithBlockTag("latest"))
    if err != nil {
        log.Fatal(err)
    }
    decimals := new(big.Int)
    decimals.SetString(strings.TrimPrefix(decimalsValue, "0x"), 16)
    fmt.Printf("Token Decimals: %s (raw: %s)\n", decimals.String(), decimalsValue)
 
    // Query historical total supply at a specific block number
    totalSupplyValue, err := provider.StorageAt(ctx, ethContract, "ERC20_total_supply", rpc.WithBlockNumber(100000))
    if err != nil {
        log.Fatal(err)
    }
    totalSupply := new(big.Int)
    totalSupply.SetString(strings.TrimPrefix(totalSupplyValue, "0x"), 16)
    fmt.Printf("Total Supply at block 100000: %s (raw: %s)\n", totalSupply.String(), totalSupplyValue)
}
 
// decodeShortString decodes a hex-encoded short string (Cairo felt string)
func decodeShortString(hexStr string) string {
    hexStr = strings.TrimPrefix(hexStr, "0x")
    if len(hexStr)%2 != 0 {
        hexStr = "0" + hexStr
    }
 
    bytes, err := hex.DecodeString(hexStr)
    if err != nil {
        return ""
    }
 
    return string(bytes)
}

Error Handling

storageValue, err := provider.StorageAt(ctx, contractAddress, key, blockID)
if err != nil {
    if errors.Is(err, rpc.ErrContractNotFound) {
        log.Printf("Contract not found at address: %s", contractAddress)
        return
    }
    if errors.Is(err, rpc.ErrBlockNotFound) {
        log.Printf("Block not found")
        return
    }
    log.Printf("Error retrieving storage: %v", err)
    return
}
 
fmt.Printf("Storage value: %s\n", storageValue)

Common Use Cases

  • ead public state variables from deployed contracts.
  • Check token balances stored in contract storage.
  • Retrieve contract configuration values and settings.