The Graph:修复Web3数据查询
这次我们将仔细研究 The Graph,它在去年基本上成为了开发去中心化应用程序的标准堆栈的一部分。 让我们先看看我们会如何用传统的方式做事…
没有 The Graph...
为了说明起见,让我们举一个简单的例子。 我们都喜欢游戏,所以想象一个用户下注的简单游戏:
1pragma solidity 0.7.1;23contract Game {4 uint256 totalGamesPlayerWon = 0;5 uint256 totalGamesPlayerLost = 0;6 event BetPlaced(address player, uint256 value, bool hasWon);78 function placeBet() external payable {9 bool hasWon = evaluateBetForPlayer(msg.sender);1011 if (hasWon) {12 (bool success, ) = msg.sender.call{ value: msg.value * 2 }('');13 require(success, "Transfer failed");14 totalGamesPlayerWon++;15 } else {16 totalGamesPlayerLost++;17 }1819 emit BetPlaced(msg.sender, msg.value, hasWon);20 }21}22显示全部复制
现在假设在我们的去中心化应用程序中,我们想要显示总投注、输/赢的游戏总数,并且每当有人再次玩游戏时更新它。 该方法将是:
- 获取
totalGamesPlayerWon
。 - 获取
totalGamesPlayerLost
。 - 订阅
BetPlaced
事件。
我们可以侦听如右侧所示的Web3 中的事件(opens in a new tab),但它需要处理相当多的情况。
1GameContract.events.BetPlaced({2 fromBlock: 03}, function(error, event) { console.log(event); })4.on('data', function(event) {5 // event fired6})7.on('changed', function(event) {8 // event was removed again9})10.on('error', function(error, receipt) {11 // tx rejected12});13显示全部复制
现在,对于我们的简单示例来说,这在某种程度上还是不错的。 但是假设我们现在只想显示当前玩家输/赢的赌注金额。 嗯,我们运气不好,您最好部署一份新的合约来存储这些值,并将它们提取出来。 现在想象一个更复杂的智能合约和去中心化应用程序,事情可能很快就会变得一团糟。
您可以看到这并不是最优的:
- 不适用于已经部署的合约。
- 存储这些值需要额外的 gas 成本。
- 需要另一个调用来获取以太坊节点的数据。
现在让我们看看更好的解决方案。
让我向您介绍一下 GraphQL
首先我们来谈谈 GraphQL,它最初是由 Facebook 设计和实现的。 您可能熟悉传统的 Rest API 模型。 现在设想一下,您可以编写一个查询来精确查找您想要的数据:

这两张图片基本上抓住了 GraphQL 的精髓。 通过右边的查询,我们可以精确地定义我们想要的数据,这样我们就可以在一个请求中得到所有的东西,而不仅仅是我们需要的东西。 GraphQL 服务器处理所有所需数据的获取,因此前端用户端使用起来非常简单。 如果您感兴趣,这是一个很好的解释(opens in a new tab),说明服务器是如何处理查询的。
现在有了这些知识,让我们最终进入区块链空间和 The Graph。
什么是 The Graph?
区块链是一个去中心化的数据库,但与通常情况不同的是,我们没有适用于这个数据库的查询语言。 检索数据的解决方案是痛苦的,或者是完全不可能的。 The Graph 是一种用于为区块链数据建立索引并进行查询的去中心化协议。 您可能已经猜到了,它使用 GraphQL 作为查询语言。
示例总是最容易理解的,所以让我们使用 The Graph 作为 GameContract 示例。
如何创建子图
有关如何建立数据索引的定义称为子图. 它需要三个组件:
- 清单 (
subgraph.yaml
) - 模式 (
schema.graphql
) - 映射 (
mapping.ts
)
清单 (subgraph.yaml
)
清单是我们的配置文件,定义了:
- 要为哪些智能合约建立索引(地址、网络、ABI...)
- 侦听哪些事件
- 其他要侦听的东西,如函数调用或区块
- 被调用的映射函数(参见下面的
mapping.ts
)
您可以在此处定义多个智能合约和处理程序。 典型的设置在 Truffle/Hardhat 项目中会有一个子图文件夹,它有自己的存储库。 然后您可以轻松引用 ABI。
为方便起见,您可能还想使用像 mustache 这样的模板工具。 然后创建一个 subgraph.template.yaml
并根据最新部署插入地址。 有关更高级的示例设置,请参阅这个 Aave subgraph repo(opens in a new tab)