Docs

state_queryStorageAt - Astar RPC Method

Batch query multiple storage keys at a specific block on Astar. Efficiently retrieve consistent multi-key state snapshots for indexers, dashboards, and analytics.

Queries multiple storage keys at a specific block on Astar, returning all values in a single call. This is the preferred method for fetching consistent multi-key state snapshots, as all values are read from the same block.

Why Astar? Build on Polkadot's leading dApp hub supporting EVM, WASM, and upcoming PolkaVM environments with EVM + WASM + PolkaVM support, Build2Earn developer rewards, dApp Staking, and Soneium cross-layer integration.

When to Use This Method

state_queryStorageAt is essential for multi-chain dApp developers, DeFi builders, and teams leveraging Polkadot + Ethereum ecosystems:

  • Consistent State Snapshots -- Fetch multiple storage values from the same block to ensure data consistency on Astar
  • Batch Raw Storage Reads -- Retrieve several known storage keys in one RPC call
  • Indexer and Analytics -- Build efficient data pipelines by querying all required storage keys at once
  • Historical State Analysis -- Compare storage state across different blocks for auditing and data analysis

Code Examples

Common Use Cases

1. Multi-Key Snapshot

Read multiple storage keys from the same block:

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

async function getStorageSnapshot(api, addresses) {
  const keys = await Promise.all(addresses.map((address) => api.query.system.account.key(address)));
  const results = await api.rpc.state.queryStorageAt(keys);

  return results[0].changes.map(([key, value], idx) => ({
    address: addresses[idx],
    key: key.toHex(),
    raw: value?.toHex() ?? null
  }));
}

const snapshot = await getStorageSnapshot(api, [
  '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'
]);

snapshot.forEach((entry) => {
  console.log(`${entry.address}: ${entry.raw}`);
});

2. Historical State Comparison

Compare storage state between two blocks for auditing:

JavaScript
async function compareStorageAtBlocks(api, keys, blockHash1, blockHash2) {
  const [result1, result2] = await Promise.all([
    api.rpc.state.queryStorageAt(keys, blockHash1),
    api.rpc.state.queryStorageAt(keys, blockHash2)
  ]);

  const changes1 = new Map(result1[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));
  const changes2 = new Map(result2[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()]));

  const diffs = [];
  for (const [key, val1] of changes1) {
    const val2 = changes2.get(key);
    if (val1 !== val2) {
      diffs.push({ key, before: val1, after: val2 });
    }
  }

  console.log(`Found ${diffs.length} storage changes between blocks`);
  return diffs;
}

3. Efficient Indexer State Fetching

Fetch all required storage in a single batch for indexer pipelines:

JavaScript
async function fetchBlockState(api, blockHash) {
  // Build storage keys for multiple storage items
  const keys = [
    api.query.system.number.key(),              // block number
    api.query.timestamp.now.key(),               // timestamp
    api.query.system.eventCount.key(),           // event count
    api.query.system.extrinsicCount.key()        // extrinsic count
  ];

  const result = await api.rpc.state.queryStorageAt(keys, blockHash);
  const changes = new Map(
    result[0].changes.map(([k, v]) => [k.toHex(), v?.toHex()])
  );

  return {
    block: blockHash,
    keyCount: changes.size,
    entries: Object.fromEntries(changes)
  };
}

Error Handling

Common errors and solutions:

Error CodeDescriptionSolution
-32603Internal errorVerify storage keys are correctly encoded hex strings with 0x prefix
-32602Invalid paramsEnsure keys is an array of hex strings and blockHash is a valid block hash
-32005Rate limit exceededReduce the number of keys per batch or implement rate limiting
State prunedHistorical state unavailableThe requested block may be pruned on this node -- use an archive node for deep historical queries