Mappings

Mappings describe how Substate events and transactions should be handled by Hydra processor

A mapping file is a standalone typescript module defining how the Substrate events of interest should be handled by the Hydra indexer.

The event handlers (aka mappings) should be defined in the manifest file:

mappings:
  eventHandlers:
    - event: balances.Transfer 
      handler: balancesTransfer
  extrinsicHandlers:
    - extrinsic: timestamp.set 
      handler: timestampCall

Handers receive a signle argument of type defined in @dzlzv/hydra-common

  • eventHanlder receives a single argument of type EventContext & StoreContext

  • extrinsicHandlerreceives a single argument of type ExtrinsicContext & StoreContext

  • pre/postBlockHooksreceive a single argument of type BlockContext & StoreContext

Let us look at the sample mapping generated by the scaffolder

import {
  Transfer,
  BlockTimestamp,
  BlockHook,
  HookType,
} from '../generated/graphql-server/model'

// run 'NODE_URL=<RPC_ENDPOINT> EVENTS=<comma separated list of events> yarn codegen:mappings-types'
// to genenerate typescript classes for events, such as Balances.TransferEvent
import { Balances, Timestamp } from './generated/types'
import BN from 'bn.js'
import {
  ExtrinsicContext,
  EventContext,
  BlockContext,
  StoreContext,
} from '@dzlzv/hydra-common'

export async function balancesTransfer({
  store,
  event,
  block,
  extrinsic,
}: EventContext & StoreContext) {
  const transfer = new Transfer()
  const [from, to, value] = new Balances.TransferEvent(event).params
  transfer.from = Buffer.from(from.toHex())
  transfer.to = Buffer.from(to.toHex())
  transfer.value = value.toBn()
  transfer.tip = extrinsic ? new BN(extrinsic.tip.toString(10)) : new BN(0)
  transfer.insertedAt = new Date(block.timestamp)

  transfer.block = block.height
  transfer.comment = `Transferred ${transfer.value} from ${transfer.from} to ${transfer.to}`
  transfer.timestamp = new BN(block.timestamp)
  console.log(`Saving transfer: ${JSON.stringify(transfer, null, 2)}`)
  await store.save<Transfer>(transfer)
}

export async function timestampCall({
  store,
  event,
  block,
}: ExtrinsicContext & StoreContext) {
  const call = new Timestamp.SetCall(event)
  ...
}

export async function preBlockHook({
  block: { height },
  store,
}: BlockContext & StoreContext) {
  ...
}

export async function postBlockHook({
  block: { height },
  store,
}: BlockContext & StoreContext) {
  ...
}

Note that required entity classes are exported from

../generated/graphql-server/model

and this is where all the auto-generated classes live by default.

Note how the handlers create the typed classes Balances.TransferEvent and Timestamp.SetCall generated by typegen

All the events and extrinsics for which we need the generate the types, should be defined in the typegen section like below.

typegen:
  metadata:
    source: wss://rpc.polkadot.io
    blockHash: '0xab5c9230a7dde8bb90a6728ba4a0165423294dac14336b1443f865b796ff682c'
  events:
    - balances.Transfer
  calls:
    - timestamp.set
  outDir: ./mappings/generated/types

Last updated