7 分钟
Writing AssemblyScript Mappings
概述
映射将获取的以太坊数据转换为您的模式文件中定义的实体。 映射是用 TypeScript 的子集编写的,称为 AssemblyScript。 AssemblyScript 可以编译成 WASM (WebAssembly)。 AssemblyScript 比普通的 TypeScript 更严格,但提供了开发者熟悉的语法。
编写映射
对于在 mapping.eventHandlers 下的 subgraph.yaml 中定义的每个事件处理程序,都会创建一个同名的导出函数。 每个处理程序必须接受一个名为 event 的参数,其类型对应于正在处理的事件的名称。
在示例子图中,src/mapping.ts 包含 NewGravatar 和 UpdatedGravatar 事件的处理程序:
1import { NewGravatar, UpdatedGravatar } from '../generated/Gravity/Gravity'2import { Gravatar } from '../generated/schema'34export function handleNewGravatar(event: NewGravatar): void {5 let gravatar = new Gravatar(event.params.id)6 gravatar.owner = event.params.owner7 gravatar.displayName = event.params.displayName8 gravatar.imageUrl = event.params.imageUrl9 gravatar.save()10}1112export function handleUpdatedGravatar(event: UpdatedGravatar): void {13 let id = event.params.id14 let gravatar = Gravatar.load(id)15 if (gravatar == null) {16 gravatar = new Gravatar(id)17 }18 gravatar.owner = event.params.owner19 gravatar.displayName = event.params.displayName20 gravatar.imageUrl = event.params.imageUrl21 gravatar.save()22}第一个处理程序接受 NewGravatar 事件,而且使用 new Gravatar(event.params.id.toHex()) 创建一个新的 Gravatar 实体,使用相应的事件参数填充实体字段。 该实体实例由变量 gravatar 表示,id 值为 event.params.id.toHex()。
第二个处理程序尝试从 Graph 节点存储加载现有的 Gravatar。 如果尚不存在,则会按需创建。 然后更新实体以匹配新的事件参数,并使用 gravatar.save() 将其保存。
用于创建新实体的推荐 ID
强烈建议使用Bytes作为id字段的类型,并且只在确实包含人类可读文本的属性上使用String,例如代币的名称。以下是在创建新实体时需要考虑的一些推荐的id值。
-
transfer.id = event.transaction.hash -
let id = event.transaction.hash.concatI32(event.logIndex.toI32()) -
对于存储聚合数据的实体,例如每日交易量,
id通常包含天数。在这里,使用Bytes作为id是有益的。确定id的方式可能看起来像这样:
1let dayID = event.block.timestamp.toI32() / 864002let id = Bytes.fromI32(dayID)- 将固定地址转换为
Bytes。
const id = Bytes.fromHexString('0xdead...beef')
存在一个名为Graph Typescript Library的库,其中包含用于与Graph Node存储交互的工具以及处理智能合约数据和实体的便利功能。它可以从@graphprotocol/graph-ts导入到mapping.ts中。
处理具有相同ID的entity的策略
在创建并保存新实体时,如果已存在具有相同ID的实体,则在合并过程中始终优先考虑新实体的属性。这意味着现有的实体将会用新实体的值进行更新。这种方式确保了数据的最新性和一致性,使得每次操作都反映最近的变更和信息。
如果在具有相同ID的新实体中某个字段故意设置为null值,现有的实体将会被更新为该null值。这种做法允许数据显式地表示无值或已删除的状态,从而在数据处理和存储中提供更大的灵活性和控制。
如果在具有相同ID的新实体中某个字段没有设置任何值,该字段也将被设为null。这样的处理确保了数据的完整性,防止了意外的旧数据保留,确保了在数据更新时的透明性和一致性。这种做法适用于那些需要明确字段值缺失或未定义情况的应用场景。
代码生成
为了使与智能合约、事件和实体的代码编写工作变得简单且类型安全,Graph CLI 可以从子图的 GraphQL 模式和数据源中包含的合约 ABI 生成 AssemblyScript 类型。
这可以通过以下命令实现:
1graph codegen [--output-dir <OUTPUT_DIR>] [<MANIFEST>]但在大多数情况下,子图已经通过 package.json 进行了预配置,以允许您简单地运行以下命令之一来实现相同的目的:
1# Yarn2yarn codegen34# NPM5npm run codegen这将为 subgrap.yaml 中提到的 ABI 文件中的每个智能合约生成一个 AssemblyScript 类,允许您将这些合约绑定到映射中的特定地址,并针对正在处理的区块调用只读合约方法。它还将为每个合约事件生成一个类,以便于访问事件参数以及事件源自的区块和交易。所有这些类型都写入到<OUTPUT_DIR>/<DATA_SOURCE_NAME>/<ABI_NAME>.ts。在示例子图中,这将成为generated/Gravity/Gravity.ts,允许映射导入这些类型。
1import {2 // The contract class:3 Gravity,4 // The events classes:5 NewGravatar,6 UpdatedGravatar,7} from '../generated/Gravity/Gravity'除此之外,还会为子图的 GraphQL 模式中的每个实体类型生成一个类。 这些类提供类型安全的实体加载、对实体字段的读写访问以及一个 save() 方法来写入要存储的实体。 所有实体类都写入 <OUTPUT_DIR>/schema.ts,允许映射导入它们:
1import { Gravatar } from '../generated/schema'注意: 每次更改 GraphQL 模式文件或清单中包含的 ABI 后,都必须再次执行代码生成。 在构建或部署子图之前,它还必须至少执行一次。
代码生成不会检查 src/mapping.ts 中的映射代码。 如果您想在尝试将子图部署到 Graph Explorer 之前进行检查,您可以运行 yarn build,并修复 TypeScript 编译器可能发现的任何语法错误。