Docs

state_getStorage - Moonbeam RPC Method

Read a storage value by key on Moonbeam. Query account balances, pallet state, and any on-chain data using SCALE-encoded storage keys for cross-chain DeFi, multi-chain dApps, and Ethereum-to-Polkadot bridging via XCM, Axelar, LayerZero, and Wormhole.

Returns the SCALE-encoded storage value for a given key on Moonbeam. Storage keys are constructed by hashing the pallet name and storage item name (and any map keys) using the hashing algorithms specified in the runtime metadata. This is the fundamental method for reading any on-chain state.

Why Moonbeam? Build on the cross-chain connected EVM platform on Polkadot with $65M+ TVL and 100+ projects with full EVM compatibility on Polkadot, native XCM cross-chain messaging, 10K+ TPS, 24% staking APR, and $0.015 transaction costs.

When to Use This Method

state_getStorage is essential for cross-chain dApp developers, Polkadot builders, and teams requiring multi-chain interoperability:

  • Low-Level State Access -- Read the raw SCALE-encoded value stored under a known key on Moonbeam
  • Metadata-Aware Tooling -- Pair runtime metadata with raw storage reads when building custom indexers, explorers, or debugging tools
  • Historical State Queries -- Read storage values at a specific block hash to analyze state changes over time
  • Pallet Storage Inspection -- Inspect pallet state directly when higher-level client helpers are unavailable or too opinionated

Code Examples

Common Use Cases

1. Raw Storage Watcher

Query and track changes for a specific storage key over time:

JavaScript
import { ApiPromise, WsProvider } from '@polkadot/api';

async function monitorStorageKey(api, storageKey, intervalMs = 12000) {
  let previousValue = null;

  setInterval(async () => {
    const current = await api.rpc.state.getStorage(storageKey);
    const raw = current?.toHex() ?? null;

    if (previousValue !== null && raw !== previousValue) {
      console.log(`Storage value changed: ${previousValue} -> ${raw}`);
    }

    previousValue = raw;
  }, intervalMs);
}

2. Metadata-Aware Decode

Use a higher-level library to decode the value after you confirm the raw storage key:

JavaScript
async function decodeAccountStorage(api, address) {
  const storageKey = api.query.system.account.key(address);
  const raw = await api.rpc.state.getStorage(storageKey);
  const decoded = await api.query.system.account(address);

  return {
    storageKey: storageKey.toHex(),
    raw: raw?.toHex() ?? null,
    decoded: decoded.toJSON()
  };
}

3. Historical State Comparison

Compare storage values between two blocks to detect state transitions:

JavaScript
async function compareStateAtBlocks(api, storageQuery, params, blockHashA, blockHashB) {
  const [apiAtA, apiAtB] = await Promise.all([
    api.at(blockHashA),
    api.at(blockHashB)
  ]);

  // Navigate the nested query path (e.g., 'system.account')
  const parts = storageQuery.split('.');
  let queryA = apiAtA.query;
  let queryB = apiAtB.query;
  for (const part of parts) {
    queryA = queryA[part];
    queryB = queryB[part];
  }

  const [valueA, valueB] = await Promise.all([
    queryA(...params),
    queryB(...params)
  ]);

  const jsonA = valueA.toJSON();
  const jsonB = valueB.toJSON();

  console.log(`Block A: ${JSON.stringify(jsonA, null, 2)}`);
  console.log(`Block B: ${JSON.stringify(jsonB, null, 2)}`);

  return { before: jsonA, after: jsonB };
}

// Example: compare account state between two blocks
// compareStateAtBlocks(api, 'system.account', ['5GrwvaEF...'], blockHashOld, blockHashNew);

Storage Key Construction

For developers who need to construct storage keys manually (without a high-level library):

Storage TypeKey StructureExample
Valuexxhash128(Pallet) + xxhash128(Item)Timestamp.Now
Mapxxhash128(Pallet) + xxhash128(Item) + hasher(Key)System.Account(accountId)
Double Mapxxhash128(Pallet) + xxhash128(Item) + hasher1(Key1) + hasher2(Key2)Staking.ErasStakers(era, validatorId)

Common hashers used in Substrate:

  • Blake2_128Concat -- 16-byte Blake2b hash followed by the raw key (allows key enumeration)
  • Twox64Concat -- 8-byte xxhash followed by the raw key (faster, for trusted keys)
  • Identity -- Raw key with no hashing (used for already-unique keys)

Error Handling

Common errors and solutions:

Error CodeDescriptionSolution
-32603State not availableThe requested block's state may be pruned or unavailable on this node -- use an archive node for deep historical queries
-32602Invalid paramsVerify the storage key is a valid hex string with 0x prefix
-32005Rate limit exceededReduce request frequency or implement client-side rate limiting
null resultNo value at keyThis is not an error -- the storage key has no value (default state). Verify the key is correctly constructed

Important: Historical-state availability depends on the node's pruning configuration. Archive nodes retain full history, while pruned/full nodes may not serve older state.

  • state_getKeysPaged -- Enumerate storage keys matching a prefix (useful for iterating map entries)
  • state_queryStorageAt -- Query multiple storage keys at a specific block in a single request
  • state_getMetadata -- Get runtime metadata including storage definitions, types, and hashing algorithms
  • state_call -- Call runtime APIs for computed state that is not directly in storage
  • state_subscribeStorage -- Subscribe to storage changes in real time via WebSocket