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:
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:
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:
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 Type | Key Structure | Example |
|---|---|---|
| Value | xxhash128(Pallet) + xxhash128(Item) | Timestamp.Now |
| Map | xxhash128(Pallet) + xxhash128(Item) + hasher(Key) | System.Account(accountId) |
| Double Map | xxhash128(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 Code | Description | Solution |
|---|---|---|
| -32603 | State not available | The requested block's state may be pruned or unavailable on this node -- use an archive node for deep historical queries |
| -32602 | Invalid params | Verify the storage key is a valid hex string with 0x prefix |
| -32005 | Rate limit exceeded | Reduce request frequency or implement client-side rate limiting |
null result | No value at key | This 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.
Related Methods
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 requeststate_getMetadata-- Get runtime metadata including storage definitions, types, and hashing algorithmsstate_call-- Call runtime APIs for computed state that is not directly in storagestate_subscribeStorage-- Subscribe to storage changes in real time via WebSocket
chain_subscribeFinalizedHeads
Subscribe to finalized block headers on Moonbeam. Real-time WebSocket notifications for blocks that have achieved GRANDPA finality — essential for exchanges, bridges, and applications requiring irreversibility on the cross-chain connected EVM platform on Polkadot with $65M+ TVL and 100+ projects.
state_getMetadata
Get runtime metadata on Moonbeam. Essential for decoding storage, building extrinsics, and discovering available pallets and calls on the cross-chain connected EVM platform on Polkadot with $65M+ TVL and 100+ projects.