Docs
搜索⌘ K
  • 主页
  • 关于 The Graph
  • 支持的网络
  • 协议合约
  • 子图
    • 子流
      • 代币 API
        • AI Suite
          • 索引
            • 资源
              子图 > 开发 > 创建

              23 分钟

              子图清单

              概述

              子图清单 subgraph.yaml 定义了您的子图索引的智能合约和网络,这些合约中需要关注的事件,以及如何将事件数据映射到 Graph 节点存储并允许查询的实体。

              子图定义由几个文件组成:

              • subgraph.yaml: 包含子图清单

              • schema.graphql: 一个 GraphQL 模式文件,它定义了为您的子图存储哪些数据,以及如何通过 GraphQL 查询这些数据

              • mapping.ts:AssemblyScript映射⁠将事件数据转换为模式中定义的实体的代码(例如本指南中的mapping.ts)

              子图功能

              一个子图可以:

              • 索引来自多个智能合约(但不是多个网络)的数据。

              • 使用文件数据源对IPFS文件中的数据进行索引。

              • 为每个需要索引到dataSources数组的合约添加一个条目。

              子图清单的完整规范可以在这里⁠找到。

              对于上面列出的示例子图,subgraph.yaml是:

              1specVersion: 1.3.02description: Gravatar for Ethereum3repository: https://github.com/graphprotocol/graph-tooling4schema:5  file: ./schema.graphql6indexerHints:7  prune: auto8dataSources:9  - kind: ethereum/contract10    name: Gravity11    network: mainnet12    source:13      address: '0x2E645469f354BB4F5c8a05B3b30A929361cf77eC'14      abi: Gravity15      startBlock: 617524416      endBlock: 717524517    context:18      foo:19        type: Bool20        data: true21      bar:22        type: String23        data: 'bar'24    mapping:25      kind: ethereum/events26      apiVersion: 0.0.927      language: wasm/assemblyscript28      entities:29        - Gravatar30      abis:31        - name: Gravity32          file: ./abis/Gravity.json33      eventHandlers:34        - event: NewGravatar(uint256,address,string,string)35          handler: handleNewGravatar36        - event: UpdatedGravatar(uint256,address,string,string)37          handler: handleUpdatedGravatar38      callHandlers:39        - function: createGravatar(string,string)40          handler: handleCreateGravatar41      blockHandlers:42        - handler: handleBlock43        - handler: handleBlockWithCall44          filter:45            kind: call46      file: ./src/mapping.ts

              子图条目

              重要提示:请确保您将子图清单与所有处理程序和 entities一起填充。

              清单中要更新的重要条目是:

              • specVersion:标识子图支持的清单结构和功能的semver版本。最新版本是1.3.0。有关功能和版本的更多详细信息,请参阅specVersion版本 部分。

              • description:关于子图是什么的人类可读的描述。 当子图部署到Subgraph Studio时,Graph Explorer 会显示此描述。

              • repository:可以找到子图清单存储库的 URL。 这也由 Graph Explorer显示。

              • features:是所有使用的功能名称的列表。

              • indexerHints.prune:定义子图的历史区块数据的保留情况。请参见indexerHints 章节中的prune。

              • dataSources.source:智能合约子图源的地址,以及要使用的智能合约的ABI。 地址是可选的; 省略它允许索引来自所有合约的匹配事件。

              • dataSources.source.startBlock:数据源开始索引的区块的可选编号。 在大多数情况下,我们建议使用创建合约的区块。

              • dataSources.source.endBlock:数据源停止索引的区块的可选编号,包括该区块。所需的最低版本为:0.0.9。

              • dataSources.context:可以在子图映射中使用的键值对。支持各种数据类型,如Bool, String, Int, Int8, BigDecimal, Bytes, List, 和 BigInt。每个变量需要指定其type 和data。这些背景变量随后可以在映射文件中访问,为子图开发提供更多可配置选项。

              • dataSources.mapping.entities:数据源写入存储的实体。 每个实体的模式在 schema.graphql 文件中定义。

              • dataSources.mapping.abis:源合约以及您在映射中与之交互的任何其他智能合约的一个或多个命名 ABI 文件。

              • dataSources.mapping.eventHandlers:列出此子图响应的智能合约事件,映射中的处理程序—示例中为./src/mapping.ts—也将这些事件转换为存储中的实体。

              • dataSources.mapping.callHandlers:列出此子图响应的智能合约函数以及映射中的处理程序,该映射将输入和输出转换为函数调用到存储中的实体。

              • dataSources.mapping.blockHandlers:列出此子图响应的区块以及映射中的处理程序,以便在将区块附加到链时运行。 如果没有过滤器,区块处理程序将在每个区块中运行。 可以通过向处理程序添加为以下类型字段提供可选的调用filter-kind: call。 如果区块包含至少一个对数据源合约的调用,则调用筛子将运行处理程序。

              通过为每个需要将数据索引到 dataSources 数组的合约添加一个条目,单个子图可以索引来自多个智能合约的数据。

              事件处理程序

              子图中的事件处理程序对区块链上的智能合约发出的特定事件以及子图清单中定义的触发器处理程序做出反应。这使得子图能够根据定义的逻辑处理和存储事件数据。

              定义事件处理程序

              事件处理程序在子图的YAML配置中的数据源内声明。它指定了要监听的事件以及检测到这些事件时要执行的相应函数。

              1dataSources:2  - kind: ethereum/contract3    name: Gravity4    network: dev5    source:6      address: '0x731a10897d267e19b34503ad902d0a29173ba4b1'7      abi: Gravity8    mapping:9      kind: ethereum/events10      apiVersion: 0.0.911      language: wasm/assemblyscript12      entities:13        - Gravatar14        - Transaction15      abis:16        - name: Gravity17          file: ./abis/Gravity.json18      eventHandlers:19        - event: Approval(address,address,uint256)20          handler: handleApproval21        - event: Transfer(address,address,uint256)22          handler: handleTransfer23          topic1: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', '0xc8dA6BF26964aF9D7eEd9e03E53415D37aA96325'] # Optional topic filter which filters only events with the specified topic.

              调用处理程序

              虽然事件提供了一种收集合约状态相关变换的有效方法,但许多合约避免生成日志以优化燃气成本。 在这些情况下,子图可以订阅对数据源合约的调用。 这是通过定义引用函数签名的调用处理程序,及处理对该函数调用的映射处理程序来实现的。 为了处理这些调用,映射处理程序将接收一个 ethereum.Call 作为参数,其中包含调用的类型化输入和输出。 在交易调用链中的任何深度进行的调用都会触发映射,从而捕获通过代理合约与数据源合约的交互活动。

              调用处理程序只会在以下两种情况之一触发:当指定的函数被合约本身以外的账户调用时,或者当它在 Solidity 中被标记为外部,并作为同一合约中另一个函数的一部分被调用时。

              **注意: **调用处理程序目前依赖于 Parity 跟踪 API。某些网络,如 BNB 链和 Arbitrum,不支持此 API。如果索引其中一个网络的子图包含一个或多个调用处理程序,它将不会开始同步。子图开发人员应该使用事件处理程序。它们比调用处理程序性能好得多,并且在每个 evm 网络上都受到支持。

              定义调用处理程序

              要在清单中定义调用处理程序,只需在您要订阅的数据源下添加一个 callHandlers 数组。

              1dataSources:2  - kind: ethereum/contract3    name: Gravity4    network: mainnet5    source:6      address: '0x731a10897d267e19b34503ad902d0a29173ba4b1'7      abi: Gravity8    mapping:9      kind: ethereum/events10      apiVersion: 0.0.911      language: wasm/assemblyscript12      entities:13        - Gravatar14        - Transaction15      abis:16        - name: Gravity17          file: ./abis/Gravity.json18      callHandlers:19        - function: createGravatar(string,string)20          handler: handleCreateGravatar

              function 是用于筛选调用的规范化函数签名。 handler 属性是映射中您希望在数据源合约中调用目标函数时执行的函数名称。

              映射函数

              每个调用处理程序都有一个参数,该参数的类型对应于被调用函数的名称。 在上面的示例子图中,映射包含一个处理程序,用于调用 createGravatar 函数并接收 CreateGravatarCall 参数作为参数:

              1import { CreateGravatarCall } from '../generated/Gravity/Gravity'2import { Transaction } from '../generated/schema'34export function handleCreateGravatar(call: CreateGravatarCall): void {5  let id = call.transaction.hash6  let transaction = new Transaction(id)7  transaction.displayName = call.inputs._displayName8  transaction.imageUrl = call.inputs._imageUrl9  transaction.save()10}

              handleCreateGravatar 函数接受一个新的 CreateGravatarCall,它是 @graphprotocol/graph-ts提供的ethereum.Call 的子类,包括调用的输入和输出。 CreateGravatarCall 类型是在您运行 graph codegen 时为您生成的。

              区块处理程序

              除了订阅合约事件或函数调用之外,子图可能还希望在将新区块附加到链上时更新其数据。 为了实现这一点,子图可以在每个区块之后,或匹配预定义过滤器的区块之后,运行一个函数。

              支持的过滤器

              调用过滤器

              1filter:2  kind: call

              对于每个包含对定义处理程序的合约(数据源)调用的区块,相应的处理程序都会被调用一次。

              注意: 调用过滤器目前依赖于 Parity 跟踪 API。某些网络,如 BNB 链和 Arbitrum,不支持此 API。如果索引其中一个网络的子图包含一个或多个带过滤器的区块调用处理程序,它将不会开始同步。

              块处理程序没有过滤器将确保每个块都调用处理程序。对于每种过滤器类型,一个数据源只能包含一个块处理程序。

              1dataSources:2  - kind: ethereum/contract3    name: Gravity4    network: dev5    source:6      address: '0x731a10897d267e19b34503ad902d0a29173ba4b1'7      abi: Gravity8    mapping:9      kind: ethereum/events10      apiVersion: 0.0.911      language: wasm/assemblyscript12      entities:13        - Gravatar14        - Transaction15      abis:16        - name: Gravity17          file: ./abis/Gravity.json18      blockHandlers:19        - handler: handleBlock20        - handler: handleBlockWithCallToContract21          filter:22            kind: call

              投票过滤器

              要求 规范版本 >= 0.0.8

              注意: 投票过滤器仅适用于kind: ethereum的数据源。

              1blockHandlers:2  - handler: handleBlock3    filter:4      kind: polling5      every: 10

              所定义的处理程序将在每n个块上被调用一次,其中n的值由every字段提供。这种配置允许子图以固定的区块间隔执行特定的操作。

              一次性过滤器

              要求 规范版本 >= 0.0.8

              注意:一次性过滤器仅适用于kind: ethereum的数据源。

              1blockHandlers:2  - handler: handleOnce3    filter:4      kind: once

              带有 “once filter” 的所定义处理程序将在所有其他处理程序运行之前仅被调用一次。这种配置允许子图将该处理程序用作初始化处理程序,在索引开始时执行特定任务。

              1export function handleOnce(block: ethereum.Block): void {2  let data = new InitialData(Bytes.fromUTF8('initial'))3  data.data = 'Setup data here'4  data.save()5}

              映射函数

              映射函数将接收 ethereum.Block 作为其唯一参数。 与事件的映射函数一样,此函数可以访问存储中现有的子图实体、调用智能合约、以及创建或更新实体。

              1import { ethereum } from '@graphprotocol/graph-ts'23export function handleBlock(block: ethereum.Block): void {4  let id = block.hash5  let entity = new Block(id)6  entity.save()7}

              匿名事件

              如果您需要在 Solidity 中处理匿名事件,可以通过提供事件的主题 0 来实现,如示例所示:

              1eventHandlers:2  - event: LogNote(bytes4,address,bytes32,bytes32,uint256,bytes)3    topic0: '0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31'4    handler: handleGive

              只有当签名和主题0都匹配时,才会触发事件。默认情况下,topic0等于事件签名的哈希值。

              事件处理程序中的交易接收

              从specVersion 0.0.5 和 apiVersion 0.0.7开始,事件处理程序可以访问发出它们的交易接收。

              为此,必须在子图清单中使用新的receict:true键声明事件处理程序,该键是可选的,默认为false。

              1eventHandlers:2  - event: NewGravatar(uint256,address,string,string)3    handler: handleNewGravatar4    receipt: true

              在处理函数中,收据可以在 Event.receivt 字段中访问。 当receipt键设置为 false 或在清单中省略时,将返回null值。

              触发处理程序的顺序

              区块内数据源的触发器使用以下流程进行排序:

              1. 事件和调用触发器首先按区块内的交易索引排序。
              2. 同一交易中的事件和调用触发器使用约定进行排序:首先是事件触发器,然后是调用触发器,每种类型都遵循它们在清单中定义的顺序。
              3. 区块触发器按照它们在清单中定义的顺序,在事件和调用触发器之后运行。

              这些排序规则可能会发生变化。

              注意:当创建新的动态数据源时,为动态数据源定义的处理程序只会在所有现有数据源处理程序处理完毕后开始处理,并且每次触发时都会按相同的顺序重复处理。

              数据源模板

              EVM兼容智能合约中的一种常见模式是使用注册表或工厂合约,其中一个合约创建、管理或引用任意数量的其他合约,每个合约都有自己的状态和事件。

              这些子合约的地址可能事先知道,也可能不知道,其中许多合约可能会随着时间的推移而创建和/或添加。这就是为什么在这种情况下,定义单个数据源或固定数量的数据源是不可能的,需要一种更动态的方法:data source templates。

              主合约的数据源

              首先,您需要为主合约定义一个常规数据源。 下面的代码片段显示了Uniswap⁠ 交换工厂合约的简化示例数据源。 注意 NewExchange(address,address) 事件处理程序。 当工厂合约在链上创建新交换合约时,会发出此消息。

              1dataSources:2  - kind: ethereum/contract3    name: Factory4    network: mainnet5    source:6      address: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95'7      abi: Factory8    mapping:9      kind: ethereum/events10      apiVersion: 0.0.911      language: wasm/assemblyscript12      file: ./src/mappings/factory.ts13      entities:14        - Directory15      abis:16        - name: Factory17          file: ./abis/factory.json18      eventHandlers:19        - event: NewExchange(address,address)20          handler: handleNewExchange

              动态创建合约的数据源模板

              然后,将data source templates 添加到清单中。 它们与常规数据源相同,只是在 source 下缺少预先定义的合约地址。 通常,您需要为母合约管理或引用的每种类型的子合约定义一个模板。

              1dataSources:2  - kind: ethereum/contract3    name: Factory4    # ... other source fields for the main contract ...5templates:6  - name: Exchange7    kind: ethereum/contract8    network: mainnet9    source:10      abi: Exchange11    mapping:12      kind: ethereum/events13      apiVersion: 0.0.614      language: wasm/assemblyscript15      file: ./src/mappings/exchange.ts16      entities:17        - Exchange18      abis:19        - name: Exchange20          file: ./abis/exchange.json21      eventHandlers:22        - event: TokenPurchase(address,uint256,uint256)23          handler: handleTokenPurchase24        - event: EthPurchase(address,uint256,uint256)25          handler: handleEthPurchase26        - event: AddLiquidity(address,uint256,uint256)27          handler: handleAddLiquidity28        - event: RemoveLiquidity(address,uint256,uint256)29          handler: handleRemoveLiquidity

              实例化数据源模板

              在最后一步中,您可以更新主合约映射,以便从其中一个模板创建动态数据源实例。 在此示例中,您将更改主合约映射以导入 Exchange 模板,并在其上调用 Exchange.create(address) 方法,从而开始索引新交换合约。

              1import { Exchange } from '../generated/templates'23export function handleNewExchange(event: NewExchange): void {4  // Start indexing the exchange; `event.params.exchange` is the5  // address of the new exchange contract6  Exchange.create(event.params.exchange)7}

              注意: 新的数据源只会处理创建它的区块和所有后续区块的调用和事件,而不会处理历史数据,也就是包含在先前区块中的数据。

              如果先前的区块包含与新数据源相关的数据,最好通过读取合约的当前状态,并在创建新数据源时创建表示该状态的实体来索引该数据。

              数据源背景

              数据源上下文允许在实例化模板时传递额外的配置。在我们的示例中,假设交易所与特定的交易对相关联,该交易对包含在NewExchange事件中。这些信息可以传递到实例化的数据源中,如下所示:

              1import { Exchange } from '../generated/templates'23export function handleNewExchange(event: NewExchange): void {4  let context = new DataSourceContext()5  context.setString('tradingPair', event.params.tradingPair)6  Exchange.createWithContext(event.params.exchange, context)7}

              在 Exchange 模板的映射中,可以访问背景:

              1import { dataSource } from '@graphprotocol/graph-ts'23let context = dataSource.context()4let tradingPair = context.getString('tradingPair')

              对于所有的值类型,都有像 setString 和 getString 这样的 setter 和 getter。

              起始区块

              startBlock 是一个可选配置,允许您定义数据源从区块链中的哪个区块开始索引。 设置起始区块允许数据源跳过潜在的数百万个不相关的区块。 通常,子图开发人员会将 startBlock 设置为创建数据源智能合约的区块。

              1dataSources:2  - kind: ethereum/contract3    name: ExampleSource4    network: mainnet5    source:6      address: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95'7      abi: ExampleContract8      startBlock: 66279179    mapping:10      kind: ethereum/events11      apiVersion: 0.0.912      language: wasm/assemblyscript13      file: ./src/mappings/factory.ts14      entities:15        - User16      abis:17        - name: ExampleContract18          file: ./abis/ExampleContract.json19      eventHandlers:20        - event: NewEvent(address,address)21          handler: handleNewEvent

              注意: 合约创建区块可以在 Etherscan 上快速查找:

              1. 通过在搜索栏中输入合约地址来搜索合约。
              2. 单击 Contract Creator 部分中的创建交易hash。
              3. 加载交易详情页面,您将在其中找到该合约的起始区块。

              索引人提示

              indexerHints设置位于子图的清单文件中,为索引人提供有关处理和管理子图的指令。它影响数据处理、索引策略和优化等操作决策。目前,它包括prune选项,用于管理历史数据的保留或修剪。

              此功能可从specVersion:1.0.0获得

              修剪

              indexerHints.prune:定义子图的历史区块数据的保留策略。可选项包括:

              1. never:不进行历史数据的修剪;保留全部历史记录。
              2. auto:保留索引人设置的最小必要历史记录,优化查询性能。
              3. 指定一个具体的数字:设置保留历史区块的自定义限制数量。这允许开发者根据特定的应用需求和存储容量,精确控制保留的历史区块数,从而在维持历史数据完整性和优化资源使用之间找到平衡。
              1indexerHints:2  prune: auto

              在子图的上下文中,“历史”一词是关于存储反映可变实体旧状态的数据。

              以下情况需要给定区块的历史记录:

              • Time travel queries,可以查询子图历史中特定块处这些实体的过去状态。
              • 在该区块,将子图用作另一个子图中的graft base。
              • 将子图倒回该块。

              如果截至该块的历史数据已被修剪,则上述功能将不可用。

              通常建议使用自动,因为它可以最大限度地提高查询性能,对于大多数不需要访问大量历史数据的用户来说已经足够了。

              对于利用time travel querie的子图,建议设置一个特定的区块数来保留历史数据,或使用prune: never以保留所有历史实体状态。以下是在子图设置中配置这两个选项的示例:

              要保留特定数量的历史数据:

              1indexerHints:2  prune: 1000 # Replace 1000 with the desired number of blocks to retain

              要保留实体状态的完整历史记录:

              1indexerHints:2  prune: never

              视图版本发布

              版本Release 说明
              1.3.0添加了对 Subgraph 合成 的支持。
              1.2.0添加了对索引参数过滤器 的支持,并声明了eth_call。
              1.1.0支持时间序列和聚合。为id添加了对Int8类型的支持。
              1.0.0支持indexerHints功能以修剪子图。
              0.0.9支持endBlock功能。
              0.0.8添加了对轮询块处理程序和初始化处理程序的支持。
              0.0.7添加了对文件数据源的支持。
              0.0.6支持快速的索引证明 计算变体。
              0.0.5添加了对可以访问交易收据的事件处理程序的支持。
              0.0.4添加了对管理子图功能的支持。
              ⁠在GitHub上编辑⁠

              安装 Graph CLIGraphQL 模式
              在此页面上
              • 概述
              • 子图功能
              • 子图条目
              • 事件处理程序
              • 定义事件处理程序
              • 调用处理程序
              • 定义调用处理程序
              • 映射函数
              • 区块处理程序
              • 支持的过滤器
              • 映射函数
              • 匿名事件
              • 事件处理程序中的交易接收
              • 触发处理程序的顺序
              • 数据源模板
              • 主合约的数据源
              • 动态创建合约的数据源模板
              • 实例化数据源模板
              • 数据源背景
              • 起始区块
              • 索引人提示
              • 修剪
              • 视图版本发布
              The GraphStatusTestnetBrand AssetsForum安全Privacy PolicyTerms of Service