Skip to main content
Version: ArrowSquid

Storage calls and state queries

danger

Interface of typegen-generated classes, including the storage access classes, is under active development and will likely change in near future.

It is sometimes impossible to extract the required data with only event and call data without querying the runtime state. The context exposes a lightweight gRPC client to the chain node accessible via ctx._chain. It exposes low-level methods for accessing the storage. However, the recommended way to query the storage is with type-safe access classes generated with Substrate typegen.

Type-safe storage access with typegen

Substrate typegen tool generates storage access classes at src/types/storage.ts. One class is exposed for every version of every pallet-key pair in ${Pallet}${StorageName}StorageV${Version} (example: BalancesFreeBalanceStorageV1020). Each such class exposes a generated get() query method and, if available, methods for multi-key queries, listing keys, key-value pairs and retrieving the default value.

Note that the generated getters always query historical blockchain state at the height derived from their block argument.

To generate the storage classes with typegen:

  • In typegen.json, set specVersions as described on the typegen page.
  • List fully qualified names of the storage items at the storage section of typegen.json. The format is ${Pallet}.${StorageName}. Alternatively, generate classes for all versions of all storage items in the runtime by setting storage: true.
  • Rerun the typegen with
sqd typegen

Here's an example of typegen.json for generating an access class for Balances.Account on Kusama:

typegen.json
{
"outDir": "src/types",
"specVersions": "https://v2.archive.subsquid.io/metadata/kusama",
"storage": [
"Balances.Account"
]
}

Inspect the generated storage access classes at src/types/storage.ts. The file contents should look similar to this:

src/types/storage.ts
/**
* This code is generated by typegen
*/
import {StorageType, sts, Block, Bytes, Option, Result} from './support'
import * as v1050 from './v1050'
import * as v9420 from './v9420'

export const BalancesAccountStorageV1050: IBalancesAccountStorageV1050 = new StorageType(
'Balances.Account',
'Default',
[sts.bytes()],
v1050.AccountData
)

export interface IBalancesAccountStorageV1050 {
getDefault(block: Block): v1050.AccountData
get(block: Block, key: Bytes): Promise<v1050.AccountData | undefined>
getMany(block: Block, keys: Bytes[]): Promise<v1050.AccountData | undefined[]>
}

export const BalancesAccountStorageV9420: IBalancesAccountStorageV9420 = new StorageType(
'Balances.Account',
'Default',
[sts.bytes()],
v9420.AccountData
)

export interface IBalancesAccountStorageV9420 {
getDefault(block: Block): v9420.AccountData
get(block: Block, key: Bytes): Promise<v9420.AccountData | undefined>
getMany(block: Block, keys: Bytes[]): Promise<v9420.AccountData | undefined[]>
getKeys(block: Block): Promise<Bytes[]>
getKeys(block: Block, keyPrefix: Bytes): Promise<Bytes[]>
getKeysPaged(pageSize: number, block: Block): AsyncIterable<Bytes[]>
getKeysPaged(pageSize: number, block: Block, keyPrefix: Bytes): AsyncIterable<Bytes[]>
getPairs(block: Block): Promise<[k: Bytes, v: v9420.AccountData | undefined][]>
getPairs(block: Block, keyPrefix: Bytes): Promise<[k: Bytes, v: v9420.AccountData | undefined][]>
getPairsPaged(pageSize: number, block: Block): AsyncIterable<[k: Bytes, v: v9420.AccountData | undefined][]>
getPairsPaged(pageSize: number, block: Block, keyPrefix: Bytes): AsyncIterable<[k: Bytes, v: v9420.AccountData | undefined][]>
}

The generated access interface provides methods for accessing:

  • a single storage value with get(block, key)
  • multiple values in a batch call with getMany(block, keys[])
  • the default storage value with getDefault(block)
  • all storage keys with getKeys(block)
  • all keys with a given prefix with getKeys(block, keyPrefix) (only if the storage keys are decodable)
  • paginated keys via getKeysPaged(pageSize, block) and getKeysPaged(pageSize, block, keyPrefix)
  • key-value pairs via getPairs(block) and getPairs(block, keyPrefix)
  • paginated key-value pairs via getPairsPaged(pageSize, block) and getPairsPaged(pageSize, block, keyPrefix)

Example

src/main.ts
import {BalancesAccountStorageV1050} from './types/storage'

processor.run(new TypeormDatabase(), async ctx => {
for (const blockData of ctx.blocks) {
if (blockData.header.implVersion === 1050) {
let aliceAddress = ss58.decode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY').bytes
let aliceBalance = (await BalancesAccountStorageV1050.get(blockData.header, aliceAddress))?.free
ctx.log.info(`Alice free account balance at block ${blockData.header.height}: ${aliceBalance}`)
}
}
})