使用 The Graph 來取得以太坊上的資料

一種取得以太坊資料的新方式

Arthur Hsiao (蕭博文)
Pelith
8 min readAug 19, 2020

--

Source: https://thegraph.com/docs/introduction#how-the-graph-works

介紹

The Graph is an indexing protocol for querying networks like Ethereum and IPFS. Anyone can build and publish open APIs, called subgraphs, making data easily accessible.

根據 The Graph 官方對自家服務的描述,The Graph 提供一種更加便利的途徑來訪問以太坊上的資料。任何人都可以根據 The Graph 協議來架設節點,並對外發布稱為 “subgraph” 的 API,讓其他人可以使用 GraphQL 的方式從 subgraph 中取得有意義的資訊。

在 The Graph 還沒有出現之前,想要獲得以太坊上資料主要有兩種方式:

<1> 使用 web3.js /ethers.js 直接從以太坊節點取得

優點

  • 100% 去中心化的服務
  • 直接取得鏈上資料

缺點

  • 節點對 request 的回應時間較長
  • 為了獲得某些特定資訊,必須發送數筆請求(request) ,再進行計算。如果是使用 infura ,可能一不小心就超過免費額度了;如果是使用自架的節點,非常有機會被誤認為 DDoS。
  • 如果說只需要當前以太坊上的資料,可以使用 multicall 就可以解決。但是要取得歷史資料,就只能一筆一筆發送 dry-run 的請求了。
  • 當發送請求過多時,會造成網頁的 loading 時間太久或是畫面延遲。

<2> 自行架設 Server 爬梳以太坊上的資料

優點

  • 免除第一種方法可能遭遇的問題。
  • 優異的使用者體驗。

缺點

  • 非 100% 去中心化的服務(Server ≠ 節點)
  • 需要大量硬體資源架設 Archive Node
  • 維護 Server 需要花費不少開銷

The Graph

The Graph 為我們提供一個全新的方式,得以方便且有效率地獲得以太坊上的資訊。The Graph 服務有以下優缺點:

優點

  • 支援 GraphQL API,從前端訪問即能一次獲得所有資訊。
  • 100% 去中心化的服務(部分工程仍建置中)。
  • 許多 DeFi 協議已經將 The Graph 應用到產品的 production 版本中,例如:Synthetix、Uniswap、 Aragon、 Decentraland。

缺點

  • The Graph 雖然有 VC 投資,但專案還在早期階段。
  • 去中心化網路尚未建置完成。(目前僅上在測試網)
  • 需要熟悉 AssemblyScript ,開發的學習曲線較高。

在 The Graph 協議上開發之前…

首先,由於有幾個詞看起來非常的相像,為了避免誤會與誤用,我們需要先了解以下文章會用到的詞彙。

  • The Graph 是協議名稱,運行協議的節點稱作 Graph Node,The graph 協議的目的是為區塊鏈上的資料製作索引 (index),使鏈上的資訊更易於獲取。
  • Subgraph 是指為了某些感興趣的合約而製作出來的索引,可以理解為已經整理好的資訊。
  • GraphQL 是前後端互動的一種方式,有別於 restful API ,GraphQL 藉由定義資料結構與資料間的關係,讓前端可以有彈性地取得需要的資料。

The Graph 節點的運作機制

Graph Node 是依據 The Graph 協議而架設的節點,與 Archive Node 不同。Archive Node 將鏈上的所有區塊依原始的資料結構儲存下來;Graph Node 則是會不斷監聽以太坊上的所發生的各種事情,並且只將「對使用者有意義的資訊」儲存起來。

因此,我們可以簡單地理解 The Graph 的運作機制為:

  1. The Graph 會監聽以太坊上交易 (transaction) 被執行而觸發的事件 (event)。
  2. 當 The Graph 收到一個需要更新 subgraph 的事件時,會根據 subgraph 中的定義去抓取鏈上的資料,處理好資訊後儲存至資料庫中。
  3. 這時前端就可以用 GraphQL 的方式向 The Graph 取得資訊。

在 The Graph 協議上實作 Subgraph

首先,必須準備開發環境。(參閱官方文件

  1. 開啟 Ganach 並部署合約。
npm install -g truffle ganache-cli
ganache-cli -h 0.0.0.0

2. 執行 Local Graph Node。

git clone https://github.com/graphprotocol/graph-node/
cd graph-node/docker
docker-compose up

3. 將 Local Graph Node 停止運作。

(官方文件沒說明清楚的部分是:Ganache 每次執行時,狀態都會被重設,導致 Ganache 與 Graph Node 重新連線時發生錯誤,所以需要一併將 Graph Node 之前紀錄的資料刪除。)

docker-compose kill
docker-compose rm -f
rm -rf ../data

準備開發 subgraph

(參閱官方文件

1. 安裝 Graph CLI,並初始化專案。

npm install -g @graphprotocol/graph-cli
graph init --from-example <GITHUB_USERNAME>/<SUBGRAPH_NAME>

2. 設定 subgraph.yaml

這份檔案會告訴 The Graph 要為哪些合約進行索引,規定特定事件發生時,應該要執行的 function 來處理資料。

以索引 Compound DAI 為範例:

mapping.entities:需要儲存哪些資訊。

mapping.abis :需要用到哪些合約的 ABI。

mapping.eventHandlers :將合約事件對應到索引方法。

3. 設定 schema.graphql

這份檔案定義哪些資訊需要儲存至 subgraph,並可以被查詢。

這部分需要仔細地思考儲存哪些資訊是有意義的。以 EasyDAI 為例,由於餘額會不斷增加,直接存餘額是無意義的。儲存 cDAI 的餘額和 exchangeRate 會更加理想,因為這些資料只會隨著個別事件的發生而改變數值,經過思考後,當前獲利與累積利息可以用以下的方法計算:

  • 餘額 = cDAI balance * exchangeRateCurrent
  • 當前獲利 = 餘額 − 本金
  • 累積利息 = 餘額 + 生涯累積提領的金額 − 生涯累積存款金額

等式右邊的每一個資訊都可以依照單獨事件的發生而被更新,例如:balance 會隨著 Transfer 事件而更新;exchangeRateCurrent 會隨著 AccrueInterest 事件而更新,記錄下等式右邊的資訊後,就可以在前端輕易計算出想要呈現給使用者的資訊。

4. 開發 AssemblyScript Mappings。

Mapping 定義如何進行索引。在 subgraph.yaml 可以找到事件對應到的 mapping function 的定義。以 EasyDAI 來舉例,mapping function 的實作如下:

(所有的 mapping 都以 AssemblyScript 進行開發,AssemblyScript 會將 TypeScript 編譯成 WebAssembly。)

部署 subgraph 到 Local Graph Node。(這個部分請你閱官方文件 與自身的程式碼)

// 根據 ABI 來產生合約的型別。
npm run codegen
// 在 Graph Node 上註冊 subgraph。
npm run create-local
// 將 mapping function 部署到 Graph Node 上。
npm run deploy-local

接下來就可以開始用 GraphQL 獲取資料。
推薦使用 GraphQL IDE 來進行測試。

// get account data
query GetAccountBond($account: String!, $market: String!) {
account(id: $account) {
id
bonds(where: { market: $market }) {
id
normalizedBalance
principalBalance
totalUnderlyingSupplied
totalUnderlyingRedeemed
}
}
}

技術總結

  1. 總體來說,The Graph 算是一個非常棒的技術解決方案,在 EasyDAI 採用 The Graph 之後,請求 (request) 的數量明顯的減少許多,網頁初次載入的時間明顯縮短,頁面上數據更新的反應時間也大幅減少。
  2. 開發上比較麻煩的地方是必須要對想要索引的合約有一定程度的了解。需要弄清楚哪些資訊在特定事件發生時必須進行更新,以及這些資訊如何被計算得出。例如遇到像 AAVE 上 aToken 的 balanceOf 會持續改變的情況,可以參考 easydai-subgraph 的原始碼,查看我們如何在這種狀態下進行索引。
  3. EasyDAI 使用的 subgraph 範例:https://github.com/pelith/easydai-subgraph

--

--