The Graph GraphNode 和 Subgraph 本地搭建
什么是 The Graph?
- The Graph 是一个去中心化的协议,用于索引和查询区块链的数据。它的目标是使那些难以直接查询的数据变得可访问。
- 例如,像 Uniswap 这样具有复杂智能合约的项目,以及像 Bored Ape Yacht Club 这样的 NFT 倡议,都在以太坊区块链上存储数据。除了直接从区块链上读取基本数据外,要获取更高级的现实世界查询和操作,比如聚合、搜索、关系和非粗略的过滤,是非常困难的。
The Graph 的工作原理:
- 子图描述:The Graph 基于子图描述(也称为子图清单)来学习如何索引以太坊数据。子图描述定义了子图感兴趣的智能合约、要注意的合约中的事件,以及如何将事件数据映射到存储在 The Graph 数据库中的数据。
- 编制索引:一旦编写了子图清单,就可以使用 The Graph CLI 将定义存储在 IPFS 中,并告知索引人开始为该子图的数据编制索引。Graph 节点会不断扫描以太坊的新区块,为你的子图找到以太坊事件并运行你提供的映射处理程序。
- 查询数据:去中心化的应用程序使用 The Graph 节点的 GraphQL 端点,从区块链的索引中查询数据。The Graph 节点将 GraphQL 查询转化为对其底层数据存储的查询,以便获取这些数据。
Subgraph 创建
打开graph studio 后台 https://thegraph.com/studio/
创建好之后 https://thegraph.com/studio/subgraph/[Your subgraph name ]/
INSTALL GRAPH CLI
bash
npm install -g @graphprotocol/graph-cli
Initialize your subgraph.
bash
graph init --studio xxx-test
logs
ts
ubuntu@xxx:~$ graph init --studio xxx-test
› Warning: In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of
› Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/
› Warning: In next major version, this flag will be removed. By default we will deploy to the Graph Studio. Learn more about Sunrise of
› Decentralized Data https://thegraph.com/blog/unveiling-updated-sunrise-decentralized-data/
› Warning: In next major version, this flag will be removed. By default we will stop initializing a Git repository.
✔ Protocol · [ Your Protocol ]
✔ Subgraph slug · [ Your Subgraph slug ]
✔ Directory to create the subgraph in · [ Your Local Dir Name ]
✔ Ethereum network · [ Your Ethereum network ]
✔ Contract address · [ Your Contract address ]
✖ Failed to fetch ABI from Etherscan: ABI not found, try loading it from a local file
✔ Do you want to retry? (Y/n) · false
✖ Failed to fetch Start Block: Failed to fetch contract creation transaction hash
✔ Do you want to retry? (Y/n) · false
✖ Failed to fetch Contract Name: Failed to fetch contract source code
✔ Do you want to retry? (Y/n) · false
✔ ABI file (path) · [ Your ABI file path ]
✔ Start Block · 0
✔ Contract Name · xxx
✔ Index contract events as entities (Y/n) · true
Generate subgraph
Write subgraph to directory
✔ Create subgraph scaffold
✔ Initialize networks config
✔ Initialize subgraph repository
⠇ Install dependencies with yarn
✔ Install dependencies with yarn
✔ Generate ABI and schema types with yarn codegen
Add another contract? (y/n):
Subgraph xxx-test created in xxx
Next steps:
1. Run `graph auth` to authenticate with your deploy key.
2. Type `cd xxx` to enter the subgraph.
3. Run `yarn deploy` to deploy the subgraph.
Make sure to visit the documentation on https://thegraph.com/docs/ for further information.
初始化完成会在本地创建一个文件夹 [ Your Local Dir Name ]
进入文件夹修改docker-compose.yml
文件,找到这一行:
yaml
ethereum: "mainnet:https://rpc.alg2-test.algen.network"
该行指定了使用的网络和节点,具体参考这个README https://github.com/graphprotocol/graph-node/tree/master/docker
支持的网络:https://thegraph.com/docs/zh/developing/supported-networks/
如果你使用 ARM 芯片的 Mac 需要本地构建镜像
修改完成之后,启动容器,如果没问题了,就可以开始下一步了
yaml
docker-compose up -d
Allocating subgraph name
ts
ubuntu@xxx:~/xxx$ yarn create-local
yarn run v1.22.22
$ graph create --node http://localhost:8020/ xxx-test
› Warning: In next major version, this command will be merged as a subcommand for `graph local`.
Created subgraph: xxx-test
Done in 0.30s.
Local deployment
ts
ubuntu@xxx:~/xxx$ yarn deploy-local
yarn run v1.22.22
$ graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 xxx-test
Which version label to use? (e.g. "v0.0.1"): v1.0.0
Skip migration: Bump mapping apiVersion from 0.0.1 to 0.0.2
Skip migration: Bump mapping apiVersion from 0.0.2 to 0.0.3
Skip migration: Bump mapping apiVersion from 0.0.3 to 0.0.4
Skip migration: Bump mapping apiVersion from 0.0.4 to 0.0.5
Skip migration: Bump mapping apiVersion from 0.0.5 to 0.0.6
Skip migration: Bump manifest specVersion from 0.0.1 to 0.0.2
Skip migration: Bump manifest specVersion from 0.0.2 to 0.0.4
✔ Apply migrations
✔ Load subgraph from subgraph.yaml
Compile data source: XXX => build/XXX/XXX.wasm
✔ Compile subgraph
Copy schema file build/schema.graphql
Write subgraph file build/XXX/abis/XXX.json
Write subgraph manifest build/subgraph.yaml
✔ Write compiled subgraph to build/
Add file to IPFS build/schema.graphql
.. xxx
Add file to IPFS build/XXX/abis/XXX.json
.. xxx
Add file to IPFS build/XXX/XXX.wasm
.. xxx
✔ Upload subgraph to IPFS
Build completed: xxx
Deployed to http://localhost:8000/subgraphs/name/xxx-test/graphql
Subgraph endpoints:
Queries (HTTP): http://localhost:8000/subgraphs/name/xxx-test
Done in 10.23s.
部署完成之后,使用上面日志输出的链接进行查询 eg:
query {
_meta {
block {
number
}
deployment
hasIndexingErrors
}
}
query {
transfers(first: 5) {
id
from
to
transactionHash
amount
blockNumber
blockTimestamp
}
deductionFees(first: 5) {
id
from
transactionHash
amount
}
}
添加新的合约地址
可以使用graph add命令添加新的合约地址,但是会卡在✔ Running codegen
, 后续使用graph codegen
重新生成代码
bash
graph add 0x1234[Your Contract Address] --abi [Your abi file path]
bash
graph codegen
bash
graph build
添加完新的重新部署到graph node
bash
yarn create-local
bash
yarn deploy-local
附:在 subgraph 上进行合约方法调用
typescript
import {OwnershipTransferred} from "../generated/schema"
import {counterEvent} from "./counter";
import {buildDefaultEvent, buildOffer, buildToken} from "./utils";
import {BigInt, Bytes, dataSource} from "@graphprotocol/graph-ts";
let fusionProtocol = FusionProtocol.bind(dataSource.address());
export function handleMakeOffer(event: MakeOfferEvent): void {
...
// get highestBid from contract
let offerList = fusionProtocol.getOfferList(entity.tokenId);
...
token.save();
entity.token = token.id;
entity.save();
}
附:在 subgraph 上查询 ipfs 数据 和 全文搜索
yaml
# subgraph.yaml
specVersion: 1.0.0
features:
- fullTextSearch # 启用全文搜索
- ipfsOnEthereumContracts # 启用ipfs查找
indexerHints:
prune: auto
schema:
file: ./schema.graphql
dataSources:
...
yaml
# docker-compose.yaml
version: "3"
services:
graph-node:
image: graphprotocol/graph-node:db8de9e
ports:
- "8000:8000"
- "8001:8001"
- "8020:8020"
- "8030:8030"
- "8040:8040"
depends_on:
- ipfs
- postgres
extra_hosts:
- host.docker.internal:host-gateway
environment:
postgres_host: postgres
postgres_user: graph-node
postgres_pass: let-me-in
postgres_db: graph-node
ipfs: "ipfs:5001"
ethereum: mainnet:https://rpc.alg2-test.algen.network
GRAPH_LOG: info
GRAPH_ALLOW_NON_DETERMINISTIC_FULLTEXT_SEARCH: true
ipfs:
...
postgres:
...
查询 ipfs 数据
typescript
import {Address, BigInt, Bytes, dataSource, JSONValue} from "@graphprotocol/graph-ts";
import {ipfs, json} from '@graphprotocol/graph-ts'
export function handleTransfer1(event: Transfer1Event): void {
...
// 从本地的 ipfs 上面取数据
let ipfshash = 'QmaaTXNNw2oY9Dp1CdvBWxyjnEfLjsxDkBVWD2sYrazptL' // eg
let metadata = ipfs.cat(ipfshash)
if (metadata) {
const value = json.fromBytes(metadata).toObject().get("license")
if (value === null) {
token.tokenMetaData = "_null";
}else {
token.tokenMetaData = value.toString()
}
}
...
token.save();
entity.token = token.id
entity.save()
}
全文搜索(仅支持前缀搜索)
graphql
schema.graphql
type _Schema_
@fulltext(
name: "tokenSearch"
language: en
algorithm: rank
include: [{ entity: "Token", fields: [{ name: "id" },{name:"tokenIDSuffix4"}] }]
)
type Token @entity {
id: String! #
tokenID: BigInt!
tokenIDSuffix4: String! # 支持后缀搜索添加
...
}
query
graphql
query MyQuery {
tokenSearch(text: "388:*") {
id
tokenID
}
}
参考链接
- https://thegraph.academy/developers/local-development/
- https://thegraph.com/docs/zh/deploying/deploying-a-subgraph-to-studio/
- https://thegraph.com/docs/zh/developing/creating-a-subgraph/
- https://blog.csdn.net/qq_37106501/article/details/126975248
- https://blog.csdn.net/qq_37106501/article/details/126832402?spm=1001.2014.3001.5502