The Graph 概述
The Graph 是一个去中心化的区块链数据索引和查询协议,旨在解决区块链数据访问的难题。它为开发者提供了一种高效的方式来索引、组织和查询区块链数据,使得构建数据密集型的去中心化应用(DApp)变得简单高效。The Graph 通过 GraphQL API 提供数据服务,已成为 Web3 基础设施的重要组成部分,被称为"区块链的 Google"。
The Graph 的核心概念
1. 子图(Subgraph)
子图是 The Graph 的核心组件:
- 数据定义: 定义要索引的智能合约和事件
- Schema: 使用 GraphQL Schema 定义数据结构
- Mapping: 编写数据转换逻辑
- 部署: 部署到去中心化网络
- 查询: 通过 GraphQL 查询索引数据
2. Graph Node
运行索引服务的节点:
- 事件监听: 监听区块链上的事件
- 数据提取: 从区块链提取相关数据
- 数据转换: 执行 Mapping 转换数据
- 数据存储: 将数据存储到数据库
- 查询服务: 响应 GraphQL 查询请求
3. 去中心化网络
The Graph 网络的参与者:
- 索引器(Indexer): 运行节点索引数据,获得奖励
- 策展人(Curator): 标识高质量子图,获得奖励
- 委托人(Delegator): 将 GRT 委托给索引器
- 开发者: 创建子图并支付查询费用
- 消费者: 使用子图数据的应用和用户
4. GRT 代币
The Graph 的原生代币:
- 查询费用: 支付数据查询费用
- 索引奖励: 奖励索引器和策展人
- 治理: 参与协议治理
- 质押: 索引器质押 GRT 参与网络
The Graph 的工作原理
数据索引流程
- 定义子图: 开发者定义要索引的合约和事件
- 部署子图: 将子图部署到 The Graph 网络
- 索引数据: Graph Node 监听区块链并索引数据
- 数据转换: Mapping 函数转换原始数据
- 存储数据: 转换后的数据存储到数据库
- 提供查询: 通过 GraphQL API 提供查询服务
GraphQL 查询
The Graph 使用 GraphQL 作为查询语言:
优势:
- 精确查询: 只请求需要的字段
- 单次请求: 一次请求获取多个资源
- 类型安全: 强类型系统减少错误
- 实时文档: 自动生成 API 文档
- 灵活高效: 避免过度获取和不足获取
查询示例:
{
tokens(first: 10, orderBy: totalSupply, orderDirection: desc) {
id
name
symbol
totalSupply
decimals
}
}
创建子图
1. 安装 Graph CLI
npm install -g @graphprotocol/graph-cli
2. 初始化子图
graph init --product hosted-service username/subgraph-name
3. 定义 Schema
schema.graphql:
type Token @entity {
id: ID!
name: String!
symbol: String!
decimals: Int!
totalSupply: BigInt!
holders: [Holder!]! @derivedFrom(field: "token")
}
type Holder @entity {
id: ID!
address: Bytes!
balance: BigInt!
token: Token!
}
4. 编写 Mapping
src/mapping.ts:
import { Transfer } from "../generated/Token/Token"
import { Token, Holder } from "../generated/schema"
export function handleTransfer(event: Transfer): void {
let token = Token.load(event.address.toHex())
if (token == null) {
token = new Token(event.address.toHex())
token.name = "Token Name"
token.symbol = "TKN"
token.decimals = 18
token.totalSupply = BigInt.fromI32(0)
}
// 更新发送者余额
let sender = Holder.load(event.params.from.toHex())
if (sender != null) {
sender.balance = sender.balance.minus(event.params.value)
sender.save()
}
// 更新接收者余额
let receiver = Holder.load(event.params.to.toHex())
if (receiver == null) {
receiver = new Holder(event.params.to.toHex())
receiver.address = event.params.to
receiver.balance = BigInt.fromI32(0)
receiver.token = token.id
}
receiver.balance = receiver.balance.plus(event.params.value)
receiver.save()
token.save()
}
5. 配置子图清单
subgraph.yaml:
specVersion: 0.0.4
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum
name: Token
network: mainnet
source:
address: "0x..."
abi: Token
startBlock: 12000000
mapping:
kind: ethereum/events
apiVersion: 0.0.6
language: wasm/assemblyscript
entities:
- Token
- Holder
abis:
- name: Token
file: ./abis/Token.json
eventHandlers:
- event: Transfer(indexed address,indexed address,uint256)
handler: handleTransfer
file: ./src/mapping.ts
6. 部署子图
# 代码生成
graph codegen
# 构建
graph build
# 部署到 Hosted Service
graph deploy --product hosted-service username/subgraph-name
# 部署到去中心化网络
graph deploy --node https://api.thegraph.com/deploy/ subgraph-name
查询子图数据
使用 GraphQL Playground
在浏览器中访问子图 URL:
https://api.thegraph.com/subgraphs/name/username/subgraph-name
在应用中集成
使用 Apollo Client:
import { ApolloClient, InMemoryCache, gql } from '@apollo/client'
const client = new ApolloClient({
uri: 'https://api.thegraph.com/subgraphs/name/username/subgraph-name',
cache: new InMemoryCache()
})
const GET_TOKENS = gql`
query GetTokens {
tokens(first: 10, orderBy: totalSupply, orderDirection: desc) {
id
name
symbol
totalSupply
}
}
`
const { data } = await client.query({ query: GET_TOKENS })
使用 fetch:
const query = `
{
tokens(first: 10) {
id
name
symbol
}
}
`
const response = await fetch('https://api.thegraph.com/subgraphs/name/username/subgraph-name', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query })
})
const data = await response.json()
The Graph 网络
去中心化网络 vs Hosted Service
| 特性 | 去中心化网络 | Hosted Service |
|---|---|---|
| 去中心化 | 完全去中心化 | 中心化服务 |
| 费用 | 需支付 GRT | 免费(已停止新子图) |
| 可靠性 | 更高 | 依赖单点 |
| 审查抵抗 | 是 | 否 |
| 迁移 | 推荐 | 逐步淘汰 |
网络参与者
索引器:
- 运行 Graph Node
- 质押 GRT
- 索引子图数据
- 获得查询费用和索引奖励
策展人:
- 标识优质子图
- 质押 GRT 信号
- 获得查询费用分成
委托人:
- 将 GRT 委托给索引器
- 不需要运行节点
- 获得部分索引奖励
支持的区块链
The Graph 支持多个区块链:
- 以太坊: Mainnet、Goerli、Sepolia
- Polygon: Mainnet、Mumbai
- Arbitrum: One、Nova
- Optimism: Mainnet
- Avalanche: C-Chain
- Celo: Mainnet
- BNB Chain: Mainnet
- Gnosis Chain: Mainnet
高级特性
1. 实体关系
定义实体间的关系:
type User @entity {
id: ID!
posts: [Post!]! @derivedFrom(field: "author")
}
type Post @entity {
id: ID!
author: User!
content: String!
}
2. 全文搜索
添加全文搜索功能:
type Article @entity {
id: ID!
title: String! @fulltext(name: "articleSearch", language: en)
content: String! @fulltext(name: "articleSearch", language: en)
}
查询:
{
articleSearch(text: "blockchain") {
id
title
}
}
3. 时间旅行查询
查询历史状态:
{
tokens(block: { number: 15000000 }) {
id
totalSupply
}
}
4. 订阅
实时数据更新:
subscription {
transfers(orderBy: blockNumber, orderDirection: desc) {
from
to
value
}
}
最佳实践
1. Schema 设计
- 规范化: 避免数据冗余
- 索引: 为常查询字段添加索引
- 命名: 使用清晰的实体和字段名
- 类型: 选择合适的数据类型
2. Mapping 优化
- 批量处理: 减少数据库操作
- 条件检查: 避免不必要的操作
- 错误处理: 妥善处理异常情况
- Gas 优化: 优化链上数据读取
3. 查询优化
- 分页: 使用 first 和 skip 分页
- 过滤: 减少返回数据量
- 排序: 合理使用 orderBy
- 缓存: 利用客户端缓存
监控与调试
Graph Explorer
- 查询统计: 查看查询次数和费用
- 索引状态: 监控索引进度
- 错误日志: 查看错误信息
- 性能分析: 分析查询性能
本地开发
使用 Graph Node 本地开发:
git clone https://github.com/graphprotocol/graph-node
cd graph-node/docker
docker-compose up
应用场景
相关概念与技术
总结
The Graph 通过提供高效的区块链数据索引和查询服务,解决了 DApp 开发中数据访问的痛点。其去中心化的架构、灵活的 GraphQL 查询和丰富的生态系统,使其成为 Web3 基础设施的关键组成部分。无论是 DeFi 协议、NFT 市场还是链上分析工具,The Graph 都提供了强大的数据支持。随着更多区块链的集成和网络的不断完善,The Graph 将继续在 Web3 数据层发挥重要作用。