Writing AssemblyScript Mappings
Reading time: 5 min
The mappings take data from a particular source and transform it into entities that are defined within your schema. Mappings are written in a subset of called which can be compiled to WASM (). AssemblyScript is stricter than normal TypeScript, yet provides a familiar syntax.
For each event handler that is defined in subgraph.yaml
under mapping.eventHandlers
, create an exported function of the same name. Each handler must accept a single parameter called event
with a type corresponding to the name of the event which is being handled.
In the example subgraph, src/mapping.ts
contains handlers for the NewGravatar
and UpdatedGravatar
events:
import { NewGravatar, UpdatedGravatar } from '../generated/Gravity/Gravity'import { Gravatar } from '../generated/schema'export function handleNewGravatar(event: NewGravatar): void {let gravatar = new Gravatar(event.params.id)gravatar.owner = event.params.ownergravatar.displayName = event.params.displayNamegravatar.imageUrl = event.params.imageUrlgravatar.save()}export function handleUpdatedGravatar(event: UpdatedGravatar): void {let id = event.params.idlet gravatar = Gravatar.load(id)if (gravatar == null) {gravatar = new Gravatar(id)}gravatar.owner = event.params.ownergravatar.displayName = event.params.displayNamegravatar.imageUrl = event.params.imageUrlgravatar.save()}
The first handler takes a NewGravatar
event and creates a new Gravatar
entity with new Gravatar(event.params.id.toHex())
, populating the entity fields using the corresponding event parameters. This entity instance is represented by the variable gravatar
, with an id value of event.params.id.toHex()
.
The second handler tries to load the existing Gravatar
from the Graph Node store. If it does not exist yet, it is created on-demand. The entity is then updated to match the new event parameters before it is saved back to the store using gravatar.save()
.
It is highly recommended to use Bytes
as the type for id
fields, and only use String
for attributes that truly contain human-readable text, like the name of a token. Below are some recommended id
values to consider when creating new entities.
-
transfer.id = event.transaction.hash
-
let id = event.transaction.hash.concatI32(event.logIndex.toI32())
-
For entities that store aggregated data, for e.g, daily trade volumes, the
id
usually contains the day number. Here, using aBytes
as theid
is beneficial. Determining theid
would look like
let dayID = event.block.timestamp.toI32() / 86400let id = Bytes.fromI32(dayID)
- Convert constant addresses to
Bytes
.
const id = Bytes.fromHexString('0xdead...beef')
There is a which contains utilities for interacting with the Graph Node store and conveniences for handling smart contract data and entities. It can be imported into mapping.ts
from @graphprotocol/graph-ts
.
When creating and saving a new entity, if an entity with the same ID already exists, the properties of the new entity are always preferred during the merge process. This means that the existing entity will be updated with the values from the new entity.
If a null value is intentionally set for a field in the new entity with the same ID, the existing entity will be updated with the null value.
If no value is set for a field in the new entity with the same ID, the field will result in null as well.
为了使与智能合约、事件和实体的代码编写工作变得简单且类型安全,Graph CLI 可以从子图的 GraphQL 模式和数据源中包含的合约 ABI 生成 AssemblyScript 类型。
这可以通过以下命令实现
graph codegen [--output-dir <OUTPUT_DIR>] [<MANIFEST>]
but in most cases, subgraphs are already preconfigured via package.json
to allow you to simply run one of the following to achieve the same:
# Yarnyarn codegen# NPMnpm run codegen
This will generate an AssemblyScript class for every smart contract in the ABI files mentioned in subgraph.yaml
, allowing you to bind these contracts to specific addresses in the mappings and call read-only contract methods against the block being processed. It will also generate a class for every contract event to provide easy access to event parameters, as well as the block and transaction the event originated from. All of these types are written to <OUTPUT_DIR>/<DATA_SOURCE_NAME>/<ABI_NAME>.ts
. In the example subgraph, this would be generated/Gravity/Gravity.ts
, allowing mappings to import these types with.
import {// The contract class:Gravity,// The events classes:NewGravatar,UpdatedGravatar,} from '../generated/Gravity/Gravity'
In addition to this, one class is generated for each entity type in the subgraph's GraphQL schema. These classes provide type-safe entity loading, read and write access to entity fields as well as a save()
method to write entities to store. All entity classes are written to <OUTPUT_DIR>/schema.ts
, allowing mappings to import them with
import { Gravatar } from '../generated/schema'
Note: The code generation must be performed again after every change to the GraphQL schema or the ABIs included in the manifest. It must also be performed at least once before building or deploying the subgraph.
Code generation does not check your mapping code in src/mapping.ts
. If you want to check that before trying to deploy your subgraph to Graph Explorer, you can run yarn build
and fix any syntax errors that the TypeScript compiler might find.