跳转到内容

The Graph 数据查询

The Graph 数据查询

本页内容正在整理中,欢迎贡献

概述

直接通过 RPC 查询历史链上数据效率极低。The Graph 是去中心化的区块链数据索引协议,允许开发者定义 Subgraph(子图)来索引合约事件,并通过 GraphQL API 高效查询。Uniswap、Aave、ENS 等主流协议都使用 The Graph。

主要内容

核心概念

概念说明
Subgraph定义索引哪些合约事件、如何存储数据的配置
Indexer运行节点索引 Subgraph 数据的节点运营商
GraphQL API查询已索引数据的接口
Hosted ServiceThe Graph 的托管服务(开发测试用)
Subgraph Studio发布 Subgraph 的管理平台

使用现有 Subgraph

许多协议已发布公开 Subgraph,可直接查询:

// 查询 Uniswap V3 数据
const UNISWAP_V3_SUBGRAPH = "https://gateway.thegraph.com/api/YOUR_API_KEY/subgraphs/id/...";
const query = `
{
pools(first: 10, orderBy: totalValueLockedUSD, orderDirection: desc) {
id
token0 { symbol }
token1 { symbol }
totalValueLockedUSD
volumeUSD
}
}
`;
const response = await fetch(UNISWAP_V3_SUBGRAPH, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query }),
});
const { data } = await response.json();

使用 urql 或 Apollo Client

Terminal window
npm install urql graphql
# 或
npm install @apollo/client graphql
// 使用 urql(轻量级,推荐)
import { createClient, cacheExchange, fetchExchange } from "urql";
const client = createClient({
url: "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3",
exchanges: [cacheExchange, fetchExchange],
});
// React Hook 方式
import { useQuery } from "urql";
const TOKENS_QUERY = `
query GetTopTokens($first: Int!) {
tokens(first: $first, orderBy: totalValueLockedUSD, orderDirection: desc) {
id
symbol
name
totalValueLockedUSD
volumeUSD
}
}
`;
function TopTokens() {
const [{ data, fetching, error }] = useQuery({
query: TOKENS_QUERY,
variables: { first: 10 },
});
if (fetching) return <p>加载中...</p>;
if (error) return <p>错误: {error.message}</p>;
return (
<ul>
{data.tokens.map((token: any) => (
<li key={token.id}>{token.symbol} - TVL: ${Number(token.totalValueLockedUSD).toFixed(0)}</li>
))}
</ul>
);
}

创建自定义 Subgraph

1. 安装 Graph CLI

Terminal window
npm install -g @graphprotocol/graph-cli
graph init --studio my-subgraph

2. Subgraph Manifest(subgraph.yaml)

specVersion: 0.0.5
schema:
file: ./schema.graphql
dataSources:
- kind: ethereum
name: MyToken
network: mainnet
source:
address: "0x合约地址"
abi: MyToken
startBlock: 18000000 # 合约部署区块
mapping:
kind: ethereum/events
apiVersion: 0.0.7
language: wasm/assemblyscript
entities:
- Transfer
abis:
- name: MyToken
file: ./abis/MyToken.json
eventHandlers:
- event: Transfer(indexed address,indexed address,uint256)
handler: handleTransfer
file: ./src/mapping.ts

3. GraphQL Schema(schema.graphql)

type Transfer @entity {
id: ID!
from: Bytes!
to: Bytes!
value: BigInt!
blockNumber: BigInt!
timestamp: BigInt!
}
type Account @entity {
id: Bytes! # address
transfersFrom: [Transfer!]! @derivedFrom(field: "from")
transfersTo: [Transfer!]! @derivedFrom(field: "to")
balance: BigInt!
}

4. 映射处理器(src/mapping.ts)

import { Transfer as TransferEvent } from "../generated/MyToken/MyToken";
import { Transfer, Account } from "../generated/schema";
import { BigInt } from "@graphprotocol/graph-ts";
export function handleTransfer(event: TransferEvent): void {
// 保存 Transfer 记录
const transfer = new Transfer(
event.transaction.hash.concatI32(event.logIndex.toI32())
);
transfer.from = event.params.from;
transfer.to = event.params.to;
transfer.value = event.params.value;
transfer.blockNumber = event.block.number;
transfer.timestamp = event.block.timestamp;
transfer.save();
}

5. 部署 Subgraph

Terminal window
graph codegen && graph build
graph auth --studio YOUR_DEPLOY_KEY
graph deploy --studio my-subgraph

查询过滤与分页

{
# 过滤:查询某地址的转账
transfers(
where: { from: "0x地址" }
orderBy: timestamp
orderDirection: desc
first: 20
skip: 0
) {
id
from
to
value
timestamp
}
}

深入阅读