Substrate typegen
Since @subsquid/substrate-typegen@8.0.0
The substrate typegen tool is a part of Subsquid SDK. It generates TypeScript wrappers for interfacing Substrate events and calls.
Usage:
npx squid-substrate-typegen typegen.json
Within the substrate-based templates there is also an sqd
shorthand command for executing this exact line:
sqd typegen
If necessary, multiple config files can be supplied:
npx squid-substrate-typegen typegen0.json typegen1.json ...
The structure of the typegen.json
config file is best illustrated with an example:
{
"outDir": "src/types",
"specVersions": "https://v2.archive.subsquid.io/metadata/kusama",
"pallets": {
// add one such section for each pallet
"Balances": {
"events": [
// list of events to generate wrappers for
"Transfer"
],
"calls": [
// list of calls to generate wrappers for
"transfer_allow_death"
],
"storage": [
"Account"
],
"constants": [
"ExistentialDeposit"
]
}
}
}
The specVersions
field is either
- a metadata service endpoint URL, like
or
https://v2.archive.subsquid.io/metadata/{network}
- a path to a
jsonl
file generated bysubstrate-metadata-explorer(1)
.
To generate all items defined by a given pallet, set any of the events
, calls
, storage
or constants
fields to true
, e.g.
{
"outDir": "src/types",
"specVersions": "kusamaVersions.jsonl",
"pallets": {
"Balances": {
// generate wrappers for all Balances pallet constants
"constants": true
}
}
}
TypeScript wrappers
Wrappers generated by the typegen command can be found in the specified outDir
(src/types
by convention). Assuming that this folder is imported as types
(e.g. with import * as types from './types'
), you'll be able to find the wrappers at:
- for events:
types.events.${palletName}.${eventName}
- for calls:
types.calls.${palletName}.${callName}
- for storage items:
types.storage.${palletName}.${storageItemName}
- for constants:
types.constants.${palletName}.${constantName}
All identifiers (pallet name, call name etc) are lowerCamelCased. E.g. the constant Balances.ExistentialDeposit
becomes types.events.balances.existentialDeposit
and the call Balances.set_balance
becomes types.calls.setBalance
.
Examples
Events and calls
import {events, calls} from './types'
processor.run(new TypeormDatabase(), async ctx => {
for (let block of ctx.blocks) {
for (let event of block.events) {
if (event.name == events.balances.transfer.name) {
let rec: {from: Bytes, to: Bytes, amount: bigint}
if (events.balances.transfer.v1020.is(event)) {
let [from, to, amount, fee] =
events.balances.transfer.v1020.decode(event)
rec = {from, to, amount}
}
// ... decode all runtime versions similarly
// with events.balances.transfer.${ver}.is/.decode
}
}
for (let call of block.calls) {
if (call.name == calls.balances.forceTransfer.name) {
let rec: {source: Bytes, dest: Bytes, value: bigint} | undefined
if (calls.balances.forceTransfer.v1020.is(call)) {
let res =
calls.balances.forceTransfer.v1020.decode(call)
assert(res.source.__kind === 'AccountId')
assert(res.dest.__kind === 'AccountId')
rec = {
source: res.source.value,
dest: res.dest.value,
value: res.value
}
}
// ... decode all runtime versions similarly
// with calls.balances.forceTransfer.${ver}.is/.decode
}
}
})
Storage
See Storage queries.
Constants
import {constants} from './types'
// ...
processor.run(new TypeormDatabase(), async ctx => {
for (let block of ctx.blocks) {
if (constants.balances.existentialDeposit.v1020.is(block.header)) {
let c = new constants.balances.existentialDeposit.v1020.get(block.header)
ctx.log.info(`Balances.ExistentialDeposit (runtime version V1020): ${c}`)
}
}
})