Skip to main content
Version: FireSquid

ink! contracts support

This section describes additional options available for indexing ink!-based WASM contracts, supported by chains with a Contracts pallet. At the moment of writing, AlephZero, Shibuya (Astar testnet), Shiden (Kusama parachain) and Astar (Polkadot parachain) are the most popular chains for deploying ink! contracts.

Generate an ink! indexing squid automatically, follow the WASM squid tutorial for a step-by-step instruction or check out the squid-wasm-template reference project.

Processor options

addContractsContractEmitted(contractAddress: string, options?: {data?, range?}): Subscribe to the ink! events of the WASM runtime emitted by a contract deployed at the specified address. The address must be specified as a hex string, so make sure to decode it if you have an ss58 encoded one. The options argument and the data selectors are similar to that of addEvent().

Example

import * as ss58 from "@subsquid/ss58"
import {toHex} from "@subsquid/util-internal-hex"

const processor = new SubstrateBatchProcessor()
.setDataSource({
archive: lookupArchive("shibuya")
})
.addContractsContractEmitted(
toHex(ss58.decode('XnrLUQucQvzp5kaaWLG9Q3LbZw5DPwpGn69B5YcywSWVr5w').bytes),
{
data: {
event: {args: true}
}
} as const
)

ink! Typegen

Use squid-ink-typegen to generate facade classes for decoding ink! smart contract data from JSON ABI metadata.

Usage

npx squid-ink-typegen --abi abi/erc20.json --output src/abi/erc20.ts

The generated sources expose the decoding methods and some useful types, using @subsquid/ink-abi under the hood:

src/abi/erc20.ts
const _abi = new Abi(metadata)

export function decodeEvent(hex: string): Event {
return _abi.decodeEvent(hex)
}

export function decodeMessage(hex: string): Message {
return _abi.decodeMessage(hex)
}

export function decodeConstructor(hex: string): Constructor {
return _abi.decodeConstructor(hex)
}

Example

The usage in a batch handler is straightforward:

processor.run(new TypeormDatabase(), async ctx => {
for (let block of ctx.blocks) {
for (let item of block.items) {
if (item.name == 'Contracts.ContractEmitted' && item.event.args.contract == CONTRACT_ADDRESS) {
let event = erc20.decodeEvent(item.event.args.data)
if (event.__kind === 'Transfer') {
// event is of type `Event_Transfer`
}
}
}
}
})

State queries

Available since @subsquid/ink-typegen@0.2.0

The generated Contract class provides facades for all state calls that don't mutate the contract state. The info about the state mutability is taken from the contract metadata.

// Generated code:
export class Contract {
// optional blockHash indicates the block at which the state is queried
constructor(private ctx: ChainContext, private address: string, private blockHash?: string) { }

total_supply(): Promise<Balance> {
return this.stateCall('0xdb6375a8', [])
}

balance_of(owner: Uint8Array): Promise<bigint> {
return this.stateCall('0x6568382f', [owner])
}
}

Example

processor.run(new TypeormDatabase(), async ctx => {
for (let block of ctx.blocks) {
for (let item of block.items) {
// query the balance at the current block
conts contract = new Contract(ctx, CONTRACT_ADDRESS, block.header.hash)
let aliceAddress = ss58.decode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY').bytes
await contract.balance_of(aliceAddress)
}
}
})