Polkadart Logo
Api

State API

Query blockchain state, storage, metadata, and runtime information

The State API is your gateway to querying blockchain state, including storage data, metadata, and runtime information. This API provides low-level access to the blockchain's state trie and is available through the StateApi class.

Quick Example

Query Runtime Version

void main() async {
  final provider = Provider.fromUri(Uri.parse('wss://rpc.polkadot.io'));
  final stateApi = StateApi(provider);
  
  // Get current runtime version
  final runtimeVersion = await stateApi.getRuntimeVersion();
  print(runtimeVersion.toJson());
  
  // Get metadata
  final metadata = await stateApi.getMetadata();
  print('Metadata version: ${metadata.version}');
}

Expected Output

{
    specName: polkadot,
    implName: parity-polkadot,
    authoringVersion: 0,
    specVersion: 1003003,
    implVersion: 0,
    apis: [...],
    transactionVersion: 26,
    stateVersion: 1
}

Core Methods

All methods are asynchronous and return Future types. Most methods accept an optional BlockHash at parameter to query historical state.

The StateApi class provides comprehensive methods for querying blockchain state:

Runtime Calls

call

Future<Uint8List> call(String method, Uint8List bytes, {BlockHash? at})

Execute a runtime API call at a specific block's state.

Use case: Call runtime APIs for specialized queries not exposed through standard RPC.

Storage Queries

getPairs

Future<List<KeyValue>> getPairs(StorageKey prefix, {BlockHash? at})

Retrieve all key-value pairs matching a prefix.

Example:

// Get all account balances
final pairs = await stateApi.getPairs(
  hex.decode('26aa394eea5630e07c48ae0c9558cef7'), // System.Account prefix
);

getKeysPaged

Future<List<StorageKey>> getKeysPaged({
  required StorageKey key,
  required int count,
  StorageKey? startKey,
  BlockHash? at
})

Query storage keys with pagination support.

Example:

// Get first 100 account keys
final keys = await stateApi.getKeysPaged(
  key: accountPrefix,
  count: 100,
);

// Get next 100 keys
final nextKeys = await stateApi.getKeysPaged(
  key: accountPrefix,
  count: 100,
  startKey: keys.last,
);

getStorage

Future<StorageData?> getStorage(StorageKey key, {BlockHash? at})

Retrieve raw storage data for a specific key.

Returns: Raw SCALE-encoded data or null if the key doesn't exist.

getStorageHash & getStorageSize

Future<BlockHash?> getStorageHash(StorageKey key, {BlockHash? at})
Future<int?> getStorageSize(StorageKey key, {BlockHash? at})

Get the hash or size of storage data without fetching the full value.

Use case: Efficiently check if storage has changed or estimate data size.

Historical Queries

queryStorage

Future<List<StorageChangeSet>> queryStorage(
  List<StorageKey> keys,
  BlockHash fromBlock,
  {BlockHash? toBlock}
)

Query storage changes over a block range.

The first result contains the initial state. Subsequent results show only changes (diffs).

Example:

// Track balance changes over 100 blocks
final changes = await stateApi.queryStorage(
  [balanceStorageKey],
  fromBlockHash,
  toBlock: toBlockHash,
);

queryStorageAt

Future<List<StorageChangeSet>> queryStorageAt(
  List<StorageKey> keys,
  {BlockHash? at}
)

Query multiple storage entries at a specific block.

getReadProof

Future<ReadProof> getReadProof(List<StorageKey> keys, {BlockHash? at})

Generate a merkle proof for storage entries.

Use case: Prove storage values to light clients or cross-chain bridges.

Metadata & Runtime

getMetadata

Future<RuntimeMetadata> getMetadata({BlockHash? at})

Fetch the complete runtime metadata.

Contains:

  • All pallets and their functions
  • Storage layouts
  • Types registry
  • Runtime APIs

getRuntimeVersion

Future<RuntimeVersion> getRuntimeVersion({BlockHash? at})

Get runtime version information.

Includes:

  • specVersion: Runtime specification version
  • specName: Chain identifier (e.g., "polkadot")
  • implVersion: Implementation version
  • apis: Available runtime APIs

Real-time Subscriptions

subscribeRuntimeVersion

Future<StreamSubscription<RuntimeVersion>> subscribeRuntimeVersion(
  Function(RuntimeVersion) onData
)

Subscribe to runtime version updates.

Example:

final subscription = await stateApi.subscribeRuntimeVersion((version) {
  print('Runtime upgraded to version ${version.specVersion}');
});

// Don't forget to cancel when done
await subscription.cancel();

subscribeStorage

Future<StreamSubscription<StorageChangeSet>> subscribeStorage(
  List<Uint8List> storageKeys,
  Function(StorageChangeSet) onData
)

Monitor storage changes in real-time.

Example:

// Watch balance changes for multiple accounts
final subscription = await stateApi.subscribeStorage(
  [aliceBalanceKey, bobBalanceKey],
  (changes) {
    print('Storage updated at block ${changes.block}');
    for (final change in changes.changes) {
      print('Key: ${hex.encode(change.$1)}');
      print('New value: ${change.$2 != null ? hex.encode(change.$2!) : "deleted"}');
    }
  },
);

Best Practices

  • Use getKeysPaged for large datasets to avoid timeouts
  • Query specific blocks for consistent reads across multiple calls
  • Use getStorageHash to detect changes before fetching full data
  • Always cancel subscriptions when done to prevent memory leaks
  • Handle reconnection logic for long-running subscriptions
  • Use error handlers for subscription failures
  • Limit block ranges in queryStorage to avoid overwhelming responses
  • Cache metadata for specific block heights when querying historical data
  • Consider using archive nodes for deep historical queries
  • Next Steps