Skip to content

State API

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.

Query Runtime Version
import 'package:polkadart/apis/apis.dart';
import 'package:polkadart/polkadart.dart' show Provider, StateApi;
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}');
}
{
specName: polkadot,
implName: parity-polkadot,
authoringVersion: 0,
specVersion: 1003003,
implVersion: 0,
apis: [...],
transactionVersion: 26,
stateVersion: 1
}

The StateApi class provides comprehensive methods for querying blockchain state:

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.

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
);
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,
);
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.

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.

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

Query storage changes over a block range.

Example:

// Track balance changes over 100 blocks
final changes = await stateApi.queryStorage(
[balanceStorageKey],
fromBlockHash,
toBlock: toBlockHash,
);
Future<List<StorageChangeSet>> queryStorageAt(
List<StorageKey> keys,
{BlockHash? at}
)

Query multiple storage entries at a specific block.

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.

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

Fetch the complete runtime metadata.

Contains:

  • All pallets and their functions
  • Storage layouts
  • Types registry
  • Runtime APIs
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
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();
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"}');
}
},
);
  • 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