根据The Graph官方对自家服务的描述,The Graph提供一种更加便利的途径来访问以太坊上的资料。任何人都可以根据The Graph协议来架设节点,并对外发布称为“subgraph”的API,让其他人可以使用GraphQL的方式从subgraph中取得有意义的资讯。
在The Graph 还没有出现之前,想要获得以太坊上资料主要有两种方式:
<1> 使用web3.js /ethers.js 直接从以太坊节点取得
优点
- 100% 去中心化的服务
- 直接取得链上资料
缺点
- 节点对request 的回应时间较长
- 为了获得某些特定资讯,必须发送数笔请求(request) ,再进行计算。如果是使用infura ,可能一不小心就超过免费额度了;如果是使用自架的节点,非常有机会被误认为DDoS。
- 如果说只需要当前以太坊上的资料,可以使用multicall 就可以解决。但是要取得历史资料,就只能一笔一笔发送dry-run 的请求了。
- 当发送请求过多时,会造成网页的loading 时间太久或是画面延迟。
<2> 自行架设Server 爬梳以太坊上的资料
优点
- 免除第一种方法可能遭遇的问题。
- 优异的使用者体验。
缺点
- 非100% 去中心化的服务(Server ≠ 节点)
- 需要大量硬体资源架设Archive Node
- 维护Server 需要花费不少开销
The Graph
The Graph 为我们提供一个全新的方式,得以方便且有效率地获得以太坊上的资讯。The Graph 服务有以下优缺点:
优点
- 支援GraphQL API,从前端访问即能一次获得所有资讯。
- 100% 去中心化的服务(部分工程仍建置中)。
- 许多DeFi 协议已经将The Graph 应用到产品的production 版本中,例如:Synthetix、Uniswap、 Aragon、 Decentraland。
缺点
- The Graph 虽然有VC 投资,但专案还在早期阶段。
- 去中心化网路尚未建置完成。(目前仅上在测试网)
- 需要熟悉AssemblyScript ,开发的学习曲线较高。
在The Graph 协议上开发之前…
首先,由于有几个词看起来非常的相像,为了避免误会与误用,我们需要先了解以下文章会用到的词汇。
- The Graph是协议名称,运行协议的节点称作Graph Node,The graph协议的目的是为区块链上的资料制作索引(index),使链上的资讯更易于获取。
- Subgraph是指为了某些感兴趣的合约而制作出来的索引,可以理解为已经整理好的资讯。
- GraphQL是前后端互动的一种方式,有别于restful API ,GraphQL藉由定义资料结构与资料间的关系,让前端可以有弹性地取得需要的资料。
The Graph 节点的运作机制
Graph Node 是依据The Graph 协议而架设的节点,与Archive Node 不同。Archive Node 将链上的所有区块依原始的资料结构储存下来;Graph Node 则是会不断监听以太坊上的所发生的各种事情,并且只将「对使用者有意义的资讯」储存起来。
因此,我们可以简单地理解The Graph 的运作机制为:
- The Graph 会监听以太坊上交易(transaction) 被执行而触发的事件(event)。
- 当The Graph 收到一个需要更新subgraph 的事件时,会根据subgraph 中的定义去抓取链上的资料,处理好资讯后储存至资料库中。
- 这时前端就可以用GraphQL 的方式向The Graph 取得资讯。
在The Graph 协议上实作Subgraph
首先,必须准备开发环境。(参阅官方文件)
- 开启Ganach 并部署合约。
npm install -g truffle ganache-cli ganache-cli -h 0.0.0.0
2. 执行Local Graph Node。
git clone https://github.com/graphprotocol/graph-node/ cd graph-node/docker docker-compose up
3. 将Local Graph Node 停止运作。
(官方文件没说明清楚的部分是:Ganache 每次执行时,状态都会被重设,导致Ganache 与Graph Node 重新连线时发生错误,所以需要一并将Graph Node 之前纪录的资料删除。)
docker-compose kill docker-compose rm -f rm -rf ../data
准备开发subgraph
(参阅官方文件)
1. 安装Graph CLI,并初始化专案。
npm install -g @graphprotocol/graph-cli graph init --from-example <GITHUB_USERNAME>/<SUBGRAPH_NAME>
2.设定subgraph.yaml。
这份档案会告诉The Graph 要为哪些合约进行索引,规定特定事件发生时,应该要执行的function 来处理资料。
以索引Compound DAI 为范例:
mapping.entities:需要储存哪些资讯。
mapping.abis :需要用到哪些合约的ABI。
mapping.eventHandlers :将合约事件对应到索引方法。
3.设定schema.graphql。
这份档案定义哪些资讯需要储存至subgraph,并可以被查询。
这部分需要仔细地思考储存哪些资讯是有意义的。以EasyDAI 为例,由于余额会不断增加,直接存余额是无意义的。储存cDAI 的余额和exchangeRate 会更加理想,因为这些资料只会随着个别事件的发生而改变数值,经过思考后,当前获利与累积利息可以用以下的方法计算:
- 余额= cDAI balance*exchangeRateCurrent
- 当前获利= 余额− 本金
- 累积利息= 余额+ 生涯累积提领的金额− 生涯累积存款金额
等式右边的每一个资讯都可以依照单独事件的发生而被更新,例如:balance会随着Transfer事件而更新;exchangeRateCurrent会随着AccrueInterest事件而更新,记录下等式右边的资讯后,就可以在前端轻易计算出想要呈现给使用者的资讯。
4. 开发AssemblyScript Mappings。
Mapping定义如何进行索引。在subgraph.yaml可以找到事件对应到的mapping function的定义。以EasyDAI来举例,mapping function的实作如下:
(所有的mapping都以AssemblyScript进行开发,AssemblyScript会将TypeScript编译成WebAssembly。)
部署subgraph到Local Graph Node。(这个部分请你阅官方文件与自身的程式码)
//根据ABI来产生合约的型别。 npm run codegen//在Graph Node上注册subgraph。 npm run create-local//将mapping function部署到Graph Node上。 npm run deploy-local
接下来就可以开始用GraphQL获取资料。
推荐使用GraphQL IDE来进行测试。
// get account data query GetAccountBond($account: String!, $market: String!) { account(id: $account) { id bonds(where: { market: $market }) { id normalizedBalance principalBalance totalUnderlyingSupplied totalUnderlyingRedeemed } } }
技术总结
- 总体来说,The Graph算是一个非常棒的技术解决方案,在EasyDAI采用The Graph之后,请求(request)的数量明显的减少许多,网页初次载入的时间明显缩短,页面上数据更新的反应时间也大幅减少。
- 开发上比较麻烦的地方是必须要对想要索引的合约有一定程度的了解。需要弄清楚哪些资讯在特定事件发生时必须进行更新,以及这些资讯如何被计算得出。例如遇到像AAVE 上aToken 的balanceOf 会持续改变的情况,可以参考easydai-subgraph 的原始码,查看我们如何在这种状态下进行索引。
- EasyDAI使用的subgraph范例:https://github.com/pelith/easydai-subgraph。
本文链接地址:https://www.wwsww.cn/ytf/6483.html
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。