跳至主要內容

The Graph:解決 Web3 資料查詢的問題

solidity
smart contracts
querying
the graph
react
中等
Markus Waas
2020年9月6日
12 分鐘閱讀

這次,我們將深入探討 The Graph,它在去年已然成為開發去中心化應用程式 (dapp) 的標準技術堆疊之一。 讓我們先來看看傳統的做法......

不使用 The Graph......

為了方便說明,讓我們先看一個簡單的範例。 我們都喜歡玩遊戲,所以想像一下有個讓使用者可以下注的簡單遊戲:

現在假設在我們的去中心化應用程式中,我們想要顯示總下注次數、輸贏的總場次,並且在有玩家再次遊玩時更新這些資訊。 做法會是:

  1. 擷取 totalGamesPlayerWon
  2. 擷取 totalGamesPlayerLost
  3. 訂閱 BetPlaced 事件。

我們可以如右方所示,在 Web3 中監聽 事件 (opens in a new tab),但這需要處理好幾種情況。

就我們這個簡單的範例而言,這樣做還算可以。 但假設我們現在只想顯示當前玩家輸/贏的下注總額。 這樣的話我們就沒輒了,你最好部署一份新合約來儲存並擷取那些數值。 現在想像一下更複雜的智能合約和去中心化應用程式,情況很快就會變得一團亂。

可不是簡簡單單就能查詢的

你可以看到為何這不是最佳做法:

  • 對已經部署的合約不管用。
  • 儲存這些數值會產生額外的 gas 費用。
  • 需要另一次呼叫才能從以太坊節點擷取資料。

那樣不夠好

現在讓我們來看看一個更好的解決方案。

為您介紹 GraphQL

首先我們來談談 GraphQL,它最初是由 Facebook 設計和實作的。 您可能熟悉傳統的 REST API 模型。 現在,想像一下,您可以編寫一個查詢,精準地取得您想要的資料:

GraphQL API 與 REST API 的比較

The Graph Playground 中 GraphQL 查詢的動畫演示

這兩張圖幾乎掌握了 GraphQL 的精髓。 透過右方的查詢,我們可以精確定義我們想要的資料,因此我們可以在一次請求中得到所有東西,而且不多不少,正好是我們需要的。 GraphQL 伺服器會處理所有必要資料的擷取,因此對於前端取用方來說,使用上非常簡單。 如果您有興趣,可以在這裡 (opens in a new tab)找到關於伺服器如何處理查詢的詳細說明。

了解了這些知識之後,讓我們終於可以進入區塊鏈領域和 The Graph 的世界了。

什麼是 The Graph?

區塊鏈是一種去中心化資料庫,但與通常情況不同的是,我們沒有用於此資料庫的查詢語言。 檢索資料的解決方案既痛苦又或者完全不可能。 The Graph 是一種用於索引和查詢區塊鏈資料的去中心化協定。 您可能已經猜到了,它使用 GraphQL 作為查詢語言。

The Graph

範例永遠是理解事物的最好方法,所以讓我們在 GameContract 範例中使用 The Graph。

如何建立子圖

關於如何索引資料的定義被稱為「子圖」。 它需要三個元件:

  1. 資訊清單 (subgraph.yaml)
  2. 結構 (schema.graphql)
  3. 映射 (mapping.ts)

資訊清單 (subgraph.yaml)

資訊清單是我們的設定檔,它定義了:

  • 要索引哪些智能合約(位址、網路、ABI......)
  • 要監聽哪些事件
  • 其他要監聽的東西,例如函式呼叫或區塊
  • 被呼叫的映射函式(請見下方的 mapping.ts

您可以在這裡定義多個合約和處理常式。 一個典型的設定,會在 Hardhat 專案中,有一個 subgraph 資料夾,並有自己的儲存庫。 然後您就可以輕易地引用 ABI。

為了方便起見,您可能也會想使用像是 mustache 這樣的樣板工具。 然後您可以建立一個 subgraph.template.yaml,並根據最新的部署插入位址。 如需更進階的設定範例,請參考 Aave 子圖儲存庫 (opens in a new tab)

完整的說明文件可以在這裡 (opens in a new tab)查看。

結構 (schema.graphql)

結構是 GraphQL 的資料定義。 它能讓您定義有哪些實體存在及其型別。 The Graph 支援的型別有

  • 位元組
  • ID
  • 字串
  • 布林值
  • 整數
  • 大整數
  • 大十進位數

您也可以使用實體作為型別來定義關聯。 在我們的範例中,我們定義了從玩家到下注的一對多關聯。 驚嘆號 (!) 代表該值不可為空。 完整的說明文件可以在這裡 (opens in a new tab)查看。

映射 (mapping.ts)

The Graph 中的映射檔案定義了我們的函式,用以將傳入的事件轉換為實體。 它以 AssemblyScript(Typescript 的一個子集)撰寫。 這代表它可以被編譯成 WASM (WebAssembly),讓映射的執行更有效率且具可攜性。

您將需要定義 subgraph.yaml 檔案中命名的每個函式,所以在我們的例子中,我們只需要一個:handleNewBet。 我們首先嘗試從傳送者位址載入 Player 實體作為 ID。 如果它不存在,我們就建立一個新的實體,並填入初始值。

然後我們建立一個新的 Bet 實體。 這個實體的 ID 會是 event.transaction.hash.toHex() + "-" + event.logIndex.toString(),以確保其值永遠是唯一的。 只使用哈希是不夠的,因為有人可能會透過一份智能合約,在單一筆交易中多次呼叫 placeBet 函式。

最後,我們可以用所有資料來更新 Player 實體。 陣列無法直接推送,而需要如此處所示來更新。 我們使用 ID 來引用該筆下注。 最後需要使用 .save() 來儲存實體。

完整的說明文件可以在這裡查看:https://thegraph.com/docs/en/developing/creating-a-subgraph/#writing-mappings。 (opens in a new tab) 您也可以在映射檔案中加入紀錄輸出,請見這裡 (opens in a new tab)

在前端使用

使用像是 Apollo Boost 的工具,您可以輕易地將 The Graph 整合進您的 React 去中心化應用程式(或 Apollo-Vue)。 特別是使用 React hooks 和 Apollo 時,擷取資料就跟在您的元件中寫一個 GraphQL 查詢一樣簡單。 一個典型的設定可能像這樣:

現在我們就可以寫一個像這樣的查詢。 這樣我們就能擷取到

  • 目前使用者贏了幾次
  • 目前使用者輸了幾次
  • 所有先前下注的時間戳清單

全部都在對 GraphQL 伺服器的一次請求中完成。

魔法

但我們還缺最後一塊拼圖,那就是伺服器。 您可以自行執行,或使用託管服務。

The Graph 伺服器

Graph Explorer:託管服務

最簡單的方法是使用託管服務。 請遵循這裡 (opens in a new tab)的指示來部署子圖。 對於許多專案,您其實可以在 explorer (opens in a new tab) 中找到現有的子圖。

The Graph Explorer

執行你自己的節點

或者,您也可以執行您自己的節點。 文件在這裡 (opens in a new tab)。 這樣做的一個原因可能是,您使用的網路不受託管服務支援。 目前支援的網路可在此處找到 (opens in a new tab)

去中心化的未來

GraphQL 也支援用於新傳入事件的串流。 The Graph 透過 Substreams (opens in a new tab) 支援這些功能,目前正在公開測試中。

2021 年 (opens in a new tab),The Graph 開始轉型為去中心化索引網路。 您可以在此處 (opens in a new tab)閱讀更多關於此去中心化索引網路的架構。

兩個關鍵面向是:

  1. 使用者為查詢向索引者付費。
  2. 索引者質押 Graph 代幣 (GRT)。

頁面最後更新: 2026年3月3日

這篇教學對您有幫助嗎?