Uniswap 數據查詢全攻略:深入理解 The Graph 協議與 GraphQL 範例操作

作者: Calpa Liu
字數:3219
出版:2025 年 4 月 15 日
分類: Web3
在 Web3 與 DeFi 蓬勃發展的時代,如何高效查詢與分析區塊鏈數據成為開發者的核心挑戰。本文全面解析 The Graph 去中心化索引協議,結合 GraphQL API 實例,手把手教你如何查詢 Uniswap 交易、理解子圖運作、優化查詢效能,並整合到現代前端應用。無論你是區塊鏈新手還是資深開發者,都能從這篇教學中掌握 Web3 數據分析的實用技巧與最佳實踐。

The Graph 協議簡介

The Graph 是一個去中心化的索引協議,專門用於從區塊鏈及相關去中心化網路中索引和查詢資料。它的作用類似於 Google 對傳統網路的索引方式,將區塊鏈上的資料組織成易於查詢和訪問的格式。透過提供這種中間層服務,The Graph 使開發者能夠高效地檢索和利用區塊鏈資料,而無需自行構建複雜的索引系統。

The Graph 最初專注於以太坊區塊鏈,但現已擴展到支援多種區塊鏈網路,包括 IPFS 和 POA 等。它的核心價值在於將區塊鏈的複雜性抽象化,讓開發者可以專注於構建優質的使用者體驗,而不是陷入資料檢索的技術難題中。The Graph 通過組織和供應 Web3 數據,大大簡化了去中心化應用(dApps)的開發過程。在 The Graph 出現之前,直接從區塊鏈讀取數據是一個緩慢且低效的過程,特別是在處理複雜的智能合約或跨越大量區塊的交易時。The Graph 成功解決了這些問題,不僅消除了開發者自建數據服務器的需求,還顯著降低了每月 60-98% 的基礎設施成本。更重要的是,The Graph 提供了高達 99.99%+ 的運行時間,確保了應用數據流的穩定性和可靠性。這些優勢使得 The Graph 成為 Web3 開發中不可或缺的基礎設施工具。

The Graph 的核心架構與運作

The Graph 生態系統由子圖(Subgraphs)、索引器(Indexers)、策展人(Curators)、委託人(Delegators)及 GRT 代幣等五大角色組成。子圖由開發者定義鏈上資料的擷取與結構,索引器負責運行節點與處理查詢,策展人標記與推廣重要子圖,委託人則透過委託 GRT 參與網路運作。GRT 代幣則作為網路的經濟激勵與安全保障。整體流程包括子圖建構、鏈上資料索引、資料儲存,最終透過 GraphQL API 查詢結構化資料。

GraphQL 查詢的優勢

GraphQL 作為 The Graph 的查詢語言,具備高效、彈性與單一端點等優點。開發者可精確取得所需資料,避免過度或不足獲取,顯著降低網路負載。單一端點設計簡化 API 架構與維護,並方便版本管理。GraphQL 也能聚合多來源資料,適應 Web3 多鏈、多協議的需求。

開發體驗與生態整合

GraphQL 不僅提升查詢效率,也帶來更佳的開發體驗。強型別設計支援自動補全與即時查詢驗證,減少錯誤。GraphiQL、GraphQL Playground 等工具提供互動式查詢與除錯環境。與現代前端框架如 Apollo Client、Relay 的無縫整合,讓開發者能輕鬆管理應用狀態與資料流,提升 Web3 應用的開發效率與品質。

Uniswap 子圖概述

Uniswap 是去中心化交易所的代表項目,它利用 The Graph 索引和組織其智能合約數據。Uniswap 為其每個版本都創建了專用的子圖。

Uniswap 子圖版本和端點

Uniswap 主要版本的子圖端點如下:

每個子圖都有相應的 GitHub 代碼庫,可用於深入了解其架構。

為 Uniswap 編寫 GraphQL 查詢

基本查詢結構

在 The Graph 中,每個實體類型都會生成相應的查詢字段。例如,為名為 Token 的實體類型,會自動生成 tokentokens 查詢字段。

基本查詢結構如下:

{
  entity(id: "specificID") {
    attribute1
    attribute2
    nestedObject {
      nestedAttribute1
      nestedAttribute2
    }
  }
}

Uniswap 單筆交換查詢範例

舉個例子,例如我們想要查詢交易 ID 為 0x000007e1111cbd97f74cfc6eea2879a5b02020f26960ac06f4af0f9395372b64#66785 的某筆交換細節,我們希望可以獲得交換的參與者、代幣金額、交易細節等資訊。我們可以使用以下 GraphQL 查詢:

{
  swap(
    id: "0x000007e1111cbd97f74cfc6eea2879a5b02020f26960ac06f4af0f9395372b64#66785"
  ) {
    sender
    recipient
    amount0
    amount1
    transaction {
      id
      blockNumber
      gasUsed
      gasPrice
    }
    timestamp
    token0 {
      id
      symbol
      name
      decimals
    }
    token1 {
      id
      symbol
      name
      decimals
    }
    pool {
      id
      feeTier
    }
  }
}

你可以透過 fetch 發送查詢請求,並獲取回應的 JSON 格式數據。以下是使用 JavaScript 的 fetch API 的範例:

const v3 = "5zvR82QoaXYFyDEKLZ9t6v9adgnptxYpKpSbxtgVENFV";
const url = `https://gateway.thegraph.com/api/subgraphs/id/${v3}`;

const response = await fetch(url, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer <YOUR_API_KEY>",
  },
  body: JSON.stringify({
    query: `<!--完整 GraphQL 查詢代碼-->`,
  }),
});
const data = await response.json();

此查詢可獲得以下詳細資訊:

  • 發送者 (sender):交換操作的發起地址,通常是用戶的錢包地址或智能合約地址。
  • 接收者 (recipient):交換後代幣的接收地址,可能與發送者相同或不同。
  • 交換金額
    • amount0:第一種代幣的交換數量,負值表示賣出,正值表示買入。
    • amount1:第二種代幣的交換數量,與 amount0 相反。
  • 交易細節
    • transaction.id:唯一標識此交易的鏈上交易哈希,可用於在區塊鏈瀏覽器中查詢完整交易信息。
    • blockNumber:交易被打包的區塊高度,反映交易在鏈上的時間順序。
    • gasUsed:此交易實際消耗的 gas 量,反映交易的複雜度。
    • gasPrice:交易的 gas 價格,影響交易的確認速度和總成本。
  • 時間戳 (timestamp):交換發生的精確 Unix 時間戳,可轉換為人類可讀的日期和時間。
  • 代幣資訊:對於 token0 和 token1,均包含:
    • id:代幣的智能合約地址。
    • symbol:代幣的簡稱,如 WBTCWETH
    • name:代幣的全名,如 Wrapped BTCWrapped Ether
    • decimals:代幣的小數位數,用於正確解析代幣金額。
  • 流動性池資訊
    • pool.id:流動性池的合約地址,唯一標識特定的交易對。
    • feeTier:交易手續費等級,如 500 表示 0.05% 的費率。

範例回應

這是 Uniswap V3 的交換回應範例,其中包含多個重要字段的詳細信息:

{
  "swap": {
    "amount0": "-0.01713487",
    "amount1": "0.25",
    "pool": {
      "feeTier": "500",
      "id": "0x4585fe77225b41b697c938b018e2ac67ac5a20c0"
    },
    "recipient": "0xd4d63cb0e661df7bd54b27d8ee38013d68e8cc8f",
    "sender": "0xe592427a0aece92de3edee1f18e0157c05861564",
    "timestamp": "1628534827",
    "token0": {
      "decimals": "8",
      "id": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
      "name": "Wrapped BTC",
      "symbol": "WBTC"
    },
    "token1": {
      "decimals": "18",
      "id": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
      "name": "Wrapped Ether",
      "symbol": "WETH"
    },
    "transaction": {
      "blockNumber": "12992661",
      "gasPrice": "75000000000",
      "gasUsed": "177045",
      "id": "0x000007e1111cbd97f74cfc6eea2879a5b02020f26960ac06f4af0f9395372b64"
    }
  }
}

此筆交易發生於 2021 年 8 月 9 日 11:47:07(UTC),由地址 0xe592427a0aece92de3edee1f18e0157c05861564 發起,將 0.01713487 顆 WBTC 兌換為 0.25 顆 WETH,並將 WETH 發送至地址 0xd4d63cb0e661df7bd54b27d8ee38013d68e8cc8f。這筆交換是在 Uniswap V3 的流動性池(ID:0x4585fe77225b41b697c938b018e2ac67ac5a20c0)中完成,手續費等級為 0.05%。交易在以太坊區塊高度 12992661 被確認,消耗了 177,045 單位的 gas,gas 價格為 75 Gwei。這代表交易者用 WBTC 成功換得 WETH,並為此支付了相應的鏈上手續費,整個過程安全且透明地記錄在區塊鏈上。

最佳實踐與優化策略

查詢效能優化

  1. 避免使用大的 skip 值

    • 原因:大 skip 值會導致查詢效能顯著下降。
    • 建議:對於大量數據,採用基於特定屬性(如 ID 或時間戳)的游標分頁方法。
    • 示例:使用 where: { id_gt: "lastId" } 替代 skip: 1000
  2. 精確指定所需字段

    • 原則:只查詢應用程序實際需要的字段,避免過度獲取。
    • 好處:減少數據傳輸量,降低網絡負載,提升查詢速度。
    • 技巧:使用 GraphQL 片段(Fragments)來組織和重用常用字段集。
  3. 優化排序和分頁策略

    • 參數使用:靈活運用 orderByorderDirectionfirstskip
    • 性能考慮:對大數據集進行排序可能會影響查詢速度,考慮在索引器端預先排序。
    • 分頁最佳實踐:結合 firstorderBy 實現高效分頁,如 first: 20, orderBy: createdAt, orderDirection: desc
  4. 利用複合查詢

    • 概念:在單一請求中組合多個相關查詢。
    • 優勢:減少網絡往返,提高數據獲取效率。
    • 示例:同時查詢用戶信息和其最近交易,而不是發送兩個獨立請求。
  5. 實施緩存策略

    • 客戶端緩存:利用 Apollo Client 等工具的內建緩存機制。
    • 服務器端緩存:對於變化不頻繁的數據,考慮在應用服務器層實施緩存。
    • 緩存失效:制定合理的緩存更新策略,確保數據及時性與查詢效能的平衡。

實際應用場景

The Graph 在 Uniswap 等 DeFi 應用中的典型用例包括:

特定池子數據

在使用 The Graph 查詢 Uniswap 區塊鏈數據時,開發者常見的一個操作就是針對某個特定的流動性池進行資訊擷取,例如查詢其價格、流動性與交易對細節等。這些資訊對於建構前端 DApp 介面、執行價格分析或實作自動化交易策略而言,都是不可或缺的基礎資料。

舉例來說,假設我們想查詢知名的 USDC/WETH 交易對在 Uniswap V3 上的流動性池,其池子合約地址為 0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8,我們可以透過以下 GraphQL 查詢來擷取池子的核心指標:

{
  pool(id: "0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8") {
    tick
    token0 {
      symbol
      id
      decimals
    }
    token1 {
      symbol
      id
      decimals
    }
    feeTier
    sqrtPrice
    liquidity
  }
}

這份查詢會回傳如下資訊:

{
  "pool": {
    "feeTier": "3000",
    "liquidity": "3143901592156277673",
    "sqrtPrice": "1961583311187181647181742186234869",
    "tick": "202348",
    "token0": {
      "decimals": "6",
      "id": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
      "symbol": "USDC"
    },
    "token1": {
      "decimals": "18",
      "id": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
      "symbol": "WETH"
    }
  }
}

這些資料涵蓋了以下幾個層面:

  • token0 與 token1 資訊:分別代表此交易對的兩種資產(在此例中為 USDC 與 WETH),包含代幣地址、簡稱與小數位數,對於解析與格式化金額極為重要。
  • feeTier:該池子的手續費等級(如 3000 表示 0.3%),對於預測用戶交易成本與流動性策略有指標意義。
  • tick:Uniswap V3 使用的價格刻度表示,與 sqrtPrice 搭配可進一步解析價格與範圍定位。
  • sqrtPrice:當前價格的平方根,用於推導實際價格,採用 Q96 固定點格式儲存。
  • liquidity:目前在該價格範圍內的有效流動性,用於判斷此池子資金深度與市場承載力。

綜合以上數據,我們可以快速描繪出這個池子的現況,不僅能了解其提供交易的成本與價格區間,也能進一步進行套利機會分析或資金配置決策。

sqrtPrice 需要進行以下的處理,才能得到實際價格:

import Decimal from "decimal.js";

/**
 * 解碼 Uniswap V3 的 sqrtPriceX96 為實際價格。
 *
 * @param {string} sqrtPriceX96 - 固定點表示的 sqrt(price),以字串避免精度誤差。
 * @param {number} decimalsToken0 - token0 的 decimals(例如 USDC 為 6)
 * @param {number} decimalsToken1 - token1 的 decimals(例如 WETH 為 18)
 * @returns {Object} - 實際價格(token1/token0)與其倒數(token0/token1)
 */
function decodeUniswapV3Price(
  sqrtPriceX96: string,
  decimalsToken0: number,
  decimalsToken1: number
) {
  const Q96 = new Decimal(2).pow(96);
  const sqrtPrice = new Decimal(sqrtPriceX96);

  // 還原價格(未考慮小數位差)
  const rawPrice = sqrtPrice.div(Q96).pow(2);

  // 考慮 decimals 修正
  const decimalAdjustment = new Decimal(10).pow(
    decimalsToken0 - decimalsToken1
  );
  const adjustedPrice = rawPrice.mul(decimalAdjustment);

  return {
    token1PerToken0: adjustedPrice.toFixed(10),
    token0PerToken1: adjustedPrice.eq(0)
      ? "Infinity"
      : new Decimal(1).div(adjustedPrice).toFixed(10),
  };
}

使用這個函數,我們可以得到實際價格:

const { token1PerToken0, token0PerToken1 } = decodeUniswapV3Price(
  "1961583311187181647181742186234869",
  6,
  18
);

token1PerToken0 的值為 1631.34,表示 1 WETH 為 1631.34 USDC,而 token0PerToken1 的值為 0.00061,表示 1 USDC 為 0.00061 WETH。

結論

The Graph,作為去中心化索引協議,解決了區塊鏈資料檢索的核心挑戰,為 Web3 生態系統提供關鍵基礎設施支持。透過結合 GraphQL 的強大功能,它實現了高效、靈活且開發者友好的區塊鏈資料查詢方案。隨著 Web3 應用的不斷發展,對高效資料檢索的需求只會增加,而 The Graph 的去中心化特性不僅提供技術優勢,還與 Web3 的核心理念保持一致,創建更安全、透明和開放的去中心化網絡。

對開發者而言,The Graph 代表了一種範式轉變,從手動處理繁瑣的區塊鏈資料索引,到使用專業設計的協議來無縫處理這些複雜任務。這不僅提高了開發效率,也為創建更複雜、更用戶友好的去中心化應用鋪平了道路。隨著區塊鏈技術和 Web3 生態系統的持續演進,The Graph 將繼續扮演連接區塊鏈原始資料與實用應用之間的橋樑角色,為開發者提供所需工具,以構建下一代去中心化互聯網的願景。

關於 Calpa

Calpa 擅長使用 TypeScriptReact.jsVue.js 建立 Responsive Website。

他積極參與開源社區,曾在 2019 年的香港開源大會上擔任講者,提供工作經驗和見解。此外,他也在 GitHub 上公開分享個人博客程式碼,已獲得超過 300 顆星星和 60 個分支的支持。

他熱愛學習新技術,並樂意分享經驗。他相信,唯有不斷學習才能跟上快速演變的技術環境。

熱門文章

最新文章

圖片管理中心
管理圖片資源
IP 查詢
快速查詢和定位 IP 地址的地理位置和相關信息
Python 運行器
無需後端、無需登入,只需打開瀏覽器即可運行 Python 代碼(由 Pyodide 提供支持)
封面圖生成器
自動創建適合各種平台的文章封面圖
原作(青山剛昌)產生器
一鍵創建原作(青山剛昌)的封面圖
日本色彩
探索和使用傳統日本色彩
部落格內容洞察儀表板
以視覺化儀表板方式追蹤文章成效、分享熱度與分類分布,協助創作者掌握內容表現。
蒙特卡羅估算 π
使用蒙特卡羅方法演示 π 值的估算過程
LLM
使用 LLM 模型進行聊天
活動圖生成器
一鍵創建活動的封面圖
Wagmi Card
一鍵創建 Wagmi 的封面圖
Facebook Quote
Facebook Quote
Music Macro Language (MML) Studio
用程式語法編寫旋律,用音符構築想像
Blurhash
一鍵創建 Blurhash
文字分類器
使用 MediaPipe TextClassifier 分類文字
前端工程師免費工具資源
前端工程師免費工具資源
後端工程師免費工具資源
後端工程師免費工具資源
全端工程師免費工具資源
全端工程師免費工具資源
Web3 工程師免費工具資源
Web3 工程師免費工具資源
紫微斗數排盤系統|結合 AI 的命盤性格與事業財務分析生成器
紫微斗數排盤工具,輸入生日與時辰,自動生成完整命盤分析提示(Prompt)。結合最專業紫微理論與 AI 助力,助你深入解析性格、事業、財務與人際課題。免費使用,適合命理師及紫微愛好者。
PixAI Prompt 組合器|快速打造可用於 AI 繪圖的語言拼圖
使用 PixAI 卻不會寫 prompt?這個工具幫你一鍵組裝角色、表情、風格語彙,輸出高品質繪圖提示語句(Prompt),可直接貼入 PixAI 使用。適合插畫師、創作者、AI 新手與 VTuber 角色開發者。