Subgraph其实非常简单。我这边大概就学了3个小时吧,就可以直接开发了。这里简单整理一下要点。
介绍
首先要知道它是做什么的。Subgraph是用来做链上数据索引的。熟悉区块链的底层的小伙伴肯定知道,假如要监听链上数据的事件/合约状态的话,我们一般是要写一个死循环,然后调用sdk来监听事件/合约,然后把数据存到数据库/处理。诸如此类的。但这很非常累。
我们很多的业务逻辑都是要监听链上数据然后进行分析的。基于此,Subgraph就诞生了。
核心--- 链上数据索引(自定义的链上数据库)
如果要快速入门subgraph,我觉得只需要理解到它是一个链上数据索引那么就没啥问题。直接可以看语法了。因为接下来的内容就和传统数据库的学习很相近了。
create subgraph == 创建数据库
创建图呢,有两种方法,法一:
graph init \
--product subgraph-studio
--from-contract <CONTRACT_ADDRESS> \
[--network <ETHEREUM_NETWORK>] \
[--abi <FILE>] \
<SUBGRAPH_SLUG> [<DIRECTORY>]
法二:
graph init --studio <SUBGRAPH_SLUG>
The <SUBGRAPH_SLUG>is the ID of your subgraph in Subgraph Studio, it can be found on your subgraph details page.
修改subgraph配置 == 修改数据库配置
Subgraph的配置十分重要,但是方便的一点在于你几乎不用去做修改。graph-cli这个脚手架你可以基于你输入的合约地址,链的选择,项目名,合约名。去自动生成以下的文件。其中,最重要的应该是:eventHandlers,callHandlers和blockHandlers。这些是用来处理捕捉到事件/信号的时候用来处理数据的地方。
specVersion: 0.0.4
description: Gravatar for Ethereum
repository: https://github.com/graphprotocol/example-subgraph
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum/contract
name: Gravity
network: mainnet
source:
address: '0x2E645469f354BB4F5c8a05B3b30A929361cf77eC'
abi: Gravity
startBlock: 6175244
mapping:
kind: ethereum/events
apiVersion: 0.0.6
language: wasm/assemblyscript
entities:
- Gravatar
abis:
- name: Gravity
file: ./abis/Gravity.json
eventHandlers:
- event: NewGravatar(uint256,address,string,string)
handler: handleNewGravatar
- event: UpdatedGravatar(uint256,address,string,string)
handler: handleUpdatedGravatar
callHandlers:
- function: createGravatar(string,string)
handler: handleCreateGravatar
blockHandlers:
- function: handleBlock
- function: handleBlockWithCall
filter:
kind: call
file: ./src/mapping.ts
Entity Relation == 数据库设计
这一步几乎是最重要的。是对schema的设计,其实本质上可以理解成数据库设计。主要分为几种:
一对一关系
type Transaction @entity {
id: ID!
transactionReceipt: TransactionReceipt
}
type TransactionReceipt @entity {
id: ID!
transaction: Transaction
}
一对多关系
type Token @entity {
id: ID!
}
type TokenBalance @entity {
id: ID!
amount: Int!
token: Token!
}
多对多关系
type Organization @entity {
id: ID!
name: String!
members: [User!]!
}
type User @entity {
id: ID!
name: String!
organizations: [Organization!]! @derivedFrom(field: "members")
}
Data Source == Web开发中的 DAO层(或者数据输入)
Emmm,这里本质就是当遇到callback的时候,要把什么数据写进graph上。这里就是具体逻辑具体分析了。我这里挑了abracadabra的一个MIM合约的Transfer函数做了监听,假如发生了Transfer,我就记下来。也是非常简单的。
import { Address, BigInt } from "@graphprotocol/graph-ts"
import {
MagicInternetMoney,
Approval,
OwnershipTransferred,
Transfer
} from "../generated/MagicInternetMoney/MagicInternetMoney"
import { MintRecord, MagicInternetMoneyEntity} from "../generated/schema"
export function handleTransfer(event: Transfer): void {
const MagicInternetMoneyV1Instance = MagicInternetMoney.bind(event.address)
const magicInternetMoneyID = event.address.toString()
let magicInternetMoney = MagicInternetMoneyEntity.load(magicInternetMoneyID)
let mintRecord = MintRecord.load(event.transaction.from.toHex())
// update MagicInternetMoney
if (!magicInternetMoney) {
magicInternetMoney = new MagicInternetMoneyEntity(magicInternetMoneyID)
magicInternetMoney.decimals = MagicInternetMoneyV1Instance.decimals()
magicInternetMoney.totalSupply = MagicInternetMoneyV1Instance.totalSupply()
magicInternetMoney.save()
}
if (!mintRecord) {
mintRecord = new MintRecord(event.transaction.from.toHex())
const _from = event.parameters[0].value.toAddress()
const _to = event.parameters[1].value.toAddress()
const _value = event.parameters[2].value.toBigInt()
const _lastMint = MagicInternetMoneyV1Instance.lastMint()
mintRecord.tokenReceiver = _from
mintRecord.tokenReceiver = _to
mintRecord.amount = _value
mintRecord.time = _lastMint.value0
mintRecord.token = magicInternetMoney.id
mintRecord.save()
}
}
export function handleApproval(event: Approval): void {}
export function handleOwnershipTransferred(event: OwnershipTransferred): void {}
编译 + 部署
两行命令就可以发布到子网/托管网络,实现数据监听了。非常方便。
graph codegen && graph build
graph deploy --studio clink-demo
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。