SubstrateBlock 是 signedBlock 的扩展接口类型,但还包括 specVersion 和 timestamp。
事件处理程序(Event Handler)
当一些事件包含在一个新块中时,你可以使用事件处理程序来捕获信息。 该事件是默认 Substrate 运行时间组成部分,一个块可能包含多个事件。
处理过程中,事件处理程序将接收一个 Substrate 事件作为参数,带有事件已键入的输入和输出。 任何类型的事件都会触发映射,导致产生捕获数据源的活动。 你应该在清单中使用映射过滤器(Mapping Filters,https://doc.subquery.network/create/manifest.html#mapping-filters)来过滤事件,减少索引数据所需的时间,并提高映射性能。
import {SubstrateEvent} from "@subql/types"; export async function handleEvent(event: SubstrateEvent): Promise<void> { const {event: {data: [account, balance]}} = event; // Retrieve the record by its ID const record = new starterEntity(event.extrinsic.block.block.header.hash.toString()); record.field2 = account.toString(); record.field3 = (balance as Balance).toBigInt(); await record.save();
SubstrateEvent 是 EventRecord 的扩展接口类型,除了事件数据之外,它还包括一个 id(这个事件所属的块)和这个块的外来内部信息。
调用处理程序(Call Handler)
当你想要捕获一些 Substrate 外来上链信息时,会使用到调用处理程序。
export async function handleCall(extrinsic: SubstrateExtrinsic): Promise<void> { const record = new starterEntity(extrinsic.block.block.header.hash.toString()); record.field4 = extrinsic.block.timestamp; await record.save(); }
SubstrateExtrinsic(substrate 外来上链信息), 扩展 GenericExtrinsic(泛型外来上链信息) 它被分配一个 id(该外来上链信息所属的块),提供外部属性扩展这个块之间的事件。 此外,它还记录了外来上链信息的成功状态。
查询状态(Query States)
我们的目标是覆盖用户用于映射处理程序的所有数据源(不仅仅是上述三种接口事件类型)。因
此,我们公开了一些@polkadot/api 接口以增加功能。
这些是我们目前支持的接口:
api.query.<module>.<method>() (打开新窗口/opens new window)
will query the current block./将查询当前块。
api.query.<module>.<method>() 将查询当前块。
api.query.<module>.<method>.multi() (opens new window/打开新窗口/)
will make multiple queries of the same type at the current block.
api.query.<module>.<method>.multi() 将在当前块进行多个相同类型的查询。
api.queryMulti() (opens new window/打开新窗口)
will make multiple queries of different types at the current block.
api.queryMulti() 将在当前块进行不同类型的多个查询。
这些是我们目前不支持的接口:
- api.tx.*
- api.derive.*
- api.query.<module>.<method>.at
- api.query.<module>.<method>.entriesAt
- api.query.<module>.<method>.entriesPaged
- api.query.<module>.<method>.hash
- api.query.<module>.<method>.keysAt
- api.query.<module>.<method>.keysPaged
- api.query.<module>.<method>.range
- api.query.<module>.<method>.sizeAt
在我们的验证器阈值示例中查看使用此 API 的示例(打开新窗口)https://github.com/subquery/tutorials-validator-threshold 示例适用案例
RPC 调用(RPC calls)
我们还支持一些 API RPC 方法,这些方法是远程调用,允许映射函数与实际节点、查询和提交进行交互。 SubQuery 的一个核心前提是它是确定性的,因此,为了保持结果一致,我们只允许历史 RPC 调用。
JSON-RPC 中的文档(打开新窗口)https://polkadot.js.org/docs/substrate/rpc/#rpc,提供一些将 BlockHash 作为输入参数的方法(例如 at?: BlockHash),这些方法现在是允许的。 我们还修改了这些方法以默认采用当前索引块哈希。
对于自定义Substrate链 RPC 调用,请参阅用法 https://doc.subquery.network/create/mapping/#usage。
模块和库(Modules and Libraries)
为了提高 SubQuery 的数据处理能力,我们允许 NodeJS 的一些内置模块在沙箱中运行映射函数,并允许用户调用第三方库。
请注意,这是一项实验性功能,你可能会遇到可能对您的映射功能产生负面影响的错误或问题。 请通过在 GitHub(https://github.com/subquery/subql)中创建问题来报告发现的任何错误。
内置模块(Built-in modules)
目前,我们允许使用以下 NodeJS 模块:assert、buffer、crypto、util 和 path。
我们建议只导入您需要的必需方法,而不是导入整个模块。 这些模块中的某些方法可能具有不受支持的依赖项,并且会在导入时失败。
import {hashMessage} from "ethers/lib/utils"; //Good way import {utils} from "ethers" //Bad way export async function handleCall(extrinsic: SubstrateExtrinsic): Promise<void> { const record = new starterEntity(extrinsic.block.block.header.hash.toString()); record.field1 = hashMessage(); await record.save(); }
第三方库(Third-party libraries)
由于我们沙箱中虚拟机的限制,目前我们只支持由 CommonJS 编写的第三方库。
我们还支持像 @polkadot/* 这样的混合库,它默认使用 ESM。 但是,如果任何其他库依赖于 ESM 格式的任何模块,虚拟机将不会编译并显示错误。
自定义Substrate链(Custom Substrate Chains)
SubQuery 可用于任何基于 Substrate 的链,而不仅仅是 Polkadot 或 Kusama。
您可以使用自定义的基于 Substrate 的链,我们提供了使用 @polkadot/typegen 自动导入类型、接口和其他方法的工具
在以下部分中,我们使用我们的 kitty (https://github.com/subquery/tutorials-kitty-chain) 示例解释集成过程。
准备(Preparation)
在项目 src 文件夹下创建一个新目录 api-interfaces 以存储所有必需和生成的文件。 我们还创建了一个 api-interfaces/kitties 目录,因为我们想从 kitties 模块在 API 中添加修饰。
元数据(Metadata)
我们需要元数据来生成实际的 API 端点。 在 kitty 示例中,我们使用了来自本地测试网的端点,它提供了其他类型的端点。 按照 PolkadotJS 元数据设置中的步骤操作,来从其 HTTP 端点检索一个节点的元数据。
curl -H "Content-Type: application/json" -d http://localhost:9933
或利用websocat形成websocket端
//Install the websocat brew install websocat //Get metadata echo state_getMetadata | websocat --jsonrpc
然后,复制黏贴输出到 JSON 文件,在我们 kitty 示例中,我们创建了 api-interface/kitty.json.
键入定义(Type definitions)
我们假定用户知道具体类型和来自区块链的RPC支持,并在清单中定义。
使用类型设置,我们创建:
- src/api-interfaces/definitions.ts – this exports all the sub-folder definitions
export { default as kitties } from ;
- src/api-interfaces/kitties/definitions.ts – type definitions for the kitties module
export default { // custom types types: { Address: "AccountId", LookupSource: "AccountId", KittyIndex: "u32", Kitty: "[u8; 16]" }, // custom rpc : api.rpc.kitties.getKittyPrice rpc: { getKittyPrice:{ description: , params: [ { name: , type: , isHistoric: true, isOptional: false }, { name: , type: , isOptional: false } ], type: } } }
包(Packages)
- 在package.json文件中,确保加上 @polkadot/typegen 作为开发依赖项,加上 @polkadot/api 作为常规依赖项 (最好是相同版本)。我们还需要 ts-node 作为开发依赖项帮助我们运行脚本。
- 我们加上 脚本运行2种类型:generate:defs 和metadata generate:meta 生成器 (此命令下,元数据可使用这些类型)。
下面是package.json的简化版本。确保脚本的包名是正确的,目录是有效的。
{ "name": "kitty-birthinfo", "scripts": { "generate:defs": "ts-node --skip-project node_modules/.bin/polkadot-types-from-defs --package kitty-birthinfo/api-interfaces --input ./src/api-interfaces", "generate:meta": "ts-node --skip-project node_modules/.bin/polkadot-types-from-chain --package kitty-birthinfo/api-interfaces --endpoint ./src/api-interfaces/kitty.json --output ./src/api-interfaces --strict" }, "dependencies": { "@polkadot/api": "^4.9.2" }, "devDependencies": { "typescript": "^4.1.3", "@polkadot/typegen": "^4.9.2", "ts-node": "^8.6.2" } }
键入生成(Type generation)
现在已完成准备,可以生成类型和元数据。运行以下命令:
# Yarn to install new dependencies yarn # Generate types yarn generate:defs
每个模块文件夹(eg /kitties),应有已生成的types.ts 可以定义按照该模块定义的所有接口,且文件 index.ts可以全部将他们导出。
# Generate metadata yarn generate:meta
该命令将生成元数据和新的针对 API 的 api- 函数。由于我们不想使用内置的 API, 我们需要在我们的 tsconfig.json.添加明确的覆盖来取代他们。更新后,配置种的路径如是(无备注):
{ "compilerOptions": { // this is the package name we use (in the interface imports, --package for generators) */ "kitty-birthinfo/*": ["src/*"], // here we replace the @polkadot/api augmentation with our own, generated from chain "@polkadot/api/augment": ["src/interfaces/augment-api.ts"], // replace the augmented types with our own, as generated from definitions "@polkadot/types/augment": ["src/interfaces/augment-types.ts"] } }
用法(Usage)
现在,在映射函数中,我们可以展示元数据和类型是如何装饰API的。RPC端点将支持我们上述模块和方法。要使用自定义rpc调用,请参阅自定义链 rpc 调用一节
export async function kittyApiHandler(): Promise<void> { //return the KittyIndex type const nextKittyId = await api.query.kitties.nextKittyId(); // return the Kitty type, input parameters types are AccountId and KittyIndex const allKitties = await api.query.kitties.kitties(,123) logger.info(`Next kitty id ${nextKittyId}`) //Custom rpc, set undefined to blockhash const kittyPrice = await api.rpc.kitties.getKittyPrice(undefined,nextKittyId); }
如果你想将这个项目发布到我们的资源管理器中,请在 src/api-interfaces 中该包含生成文件。
自定义链 rpc 调用(Custom chain rpc calls)
为了支持自定义链 RPC 调用,我们必须手动为 typesBundle 注入 RPC 定义,允许按规格进行配置。你可以在 project.yml 中定义 typesBundle。请记住,只支持历史类型的调用。
... types: { "KittyIndex": "u32", "Kitty": "[u8; 16]", } typesBundle: { spec: { chainname: { rpc: { kitties: { getKittyPrice:{ description: string, params: [ { name: , type: , isHistoric: true, isOptional: false }, { name: , type: , isOptional: false } ], type: "Balance", } } } } } }