封面:Creativeart/freepik.com
作者:Irara/token.im
區塊鏈的崛起帶來了無數的資料,然而,如何高效地檢索和查詢這些資料一直是一個挑戰。在這樣的背景下,The Graph 應運而生,作為一個去中心化的 GraphQL 索引協定,為我們提供了一個嶄新的方法來處理區塊鏈資料。
註:GraphQL 是一個可由 Client 端決定回傳資料格式的資料庫查詢語言
The Graph 的關鍵在於其 Subgraph 概念,它可以被視為 DApp 的索引專案。Subgraph 會遍歷每個區塊儲存的 Event 狀態,然後透過 Event Handler 處理這些 Event,將它們儲存至 Entity 中,相當於 GraphQL Schema 的實例。這使得我們能夠自定義和客製化查詢,從而在需要的時候輕鬆地檢索特定資料,例如:NFT 的歷史最高價。
Entity 在 Subgraph Handler 中具有關鍵作用。每次觸發 Event Handler 時,我們可以決定是要續用已有的 Entity 資料,還是進行覆蓋。Entity 由 Name 和 ID 來區分,就像是關聯式資料庫中的 Table Name 和唯一識別用的 Primary Key。
註:實際撰寫 Handler 邏輯時,Entity 需在每次賦值處理後要再執行 .save() 函數將資料回存
以 Tokenlon Buyback 頁面為例,透過 The Graph,我們可以輕鬆地查詢 Entity,並使用 GraphQL 和支付 GRT(The Graph 去中心化查詢協議的代幣)來執行查詢。這種方式相對於以往僅能使用 Etherscan 查詢每個區塊的 Event 的方式,更加高效、靈活且客製化。

在區塊鏈技術的迅速發展下,The Graph 成為了一個引人注目的去中心化數據查詢協議。該協議的核心在於去中心化,並且涵蓋了不同角色的參與,包括開發者(Developer)、消費者(Consumer)、索引者(Indexer)、策展者(Curator)以及委托人(Delegator),各自扮演著協議生態系統中不可或缺的角色。
開發者在 The Graph 生態系統中扮演著極為關鍵的角色,他們是去中心化應用(DAPP)項目的推動者。為了讓自己的 DAPP 能夠順利運行並獲得查詢支持,開發者需要建立 Subgraph,這是一個能夠被查詢的數據子集。此外,為了吸引索引者(Indexer)參與索引,開發者需要質押 GRT 作為激勵。
對於開發者而言,建議他們在建立 Subgraph 時至少質押 10,000 GRT,並且最好能夠將這份質押保持 1 到 2 年以上,以確保項目的穩定運行與持續發展。
作為協議的最終用戶,消費者可以透過支付 GRT 來進行 GraphQL 查詢,從而獲得所需的數據。這些查詢不僅幫助消費者獲取所需的資訊,同時也為生態系統中的其他角色提供了收入來源。
索引者是 The Graph 協議中的重要參與者,他們負責維護節點的索引並提供查詢服務。索引者根據信號(Signal)的多寡來建立索引,同時也會收取一定的 GRT 作為報酬。要成為一名索引者,需要質押至少 100,000 GRT 作為激勵,並且可以參與查詢的 GRT 定價。
策展者在 The Graph 生態系統中負責評價不同的 Subgraph,以幫助吸引索引者參與索引。他們可以透過質押 GRT 並對 Subgraph 進行信號標記(Signal),從而提升該 Subgraph 的價值。這些標記的信號將轉化為 Curator Shares(CS),並最終可以兌換回 GRT,同時也提升了相關 Subgraph 的吸引力。
作為 Curator,每一次透過質押 GRT 並對 Subgraph 進行 Signal,均會支付其中的 1% 作為給 The Graph 的管理稅
委託人可以透過將 GRT 質押給索引者來分享他們的收入。這種方式使得更多的參與者可以在協議的生態系統中獲得收益,同時也加強了去中心化的特性。

隨著區塊鏈技術的不斷演進,The Graph 在去中心化數據查詢領域中嶄露頭角。然而,一個有效的定價策略對於確保該協議的可持續運行和發展至關重要。在 The Graph 的生態系統中,定價策略涵蓋了不同的服務和角色,讓我們一起來了解其中的細節。
在過去的一段時間裡,The Graph 提供了免費的 Hosted Service,使開發者能夠輕鬆建立和管理 Subgraph,並透過中心化的方式獲得查詢支持。然而,隨著時間的推移,Hosted Service 將於 2023 年 Q1 開始啟動關閉計劃,這將是一個轉折點,也意味著使用者需要移轉至 Graph Explorer 去中心化解決方案。
對於那些計劃在 Graph Explorer 平臺上移轉或新建 Subgraph 的開發者來說,Subgraph Studio 提供了一個極具價值的中心化開發者後臺。在將 Subgraph 部署到去中心化 Graph Explorer 平臺之前,開發者可以在 Subgraph Studio 的不公開環境中進行詳細的測試和調校工作。儘管該環境提供了一個免費的 GraphQL URL 供查詢使用(雖然時常斷線進行維護),但它已足以支持開發者進行全面的 Subgraph 調校。
在經過調校和測試後,開發者便可以正式付費以 Gas 費用和 ArbitrumGRT 代幣將 Subgraph 部署及質押到去中心化且公開的 Graph Explorer 平臺上,使 Consumer 能夠進行付費查詢。這個過程保證了 Subgraph 在最終用戶查詢之前經過了嚴格的測試,確保了高品質和可靠的查詢體驗。
註:目前 GRT 代幣有部署至 Mainnet、Goerli、Arbitrum 及 Arbitrum Goerli,詳情可參考官方文件
隨著 Hosted Service 即將關閉,The Graph 推出了付費的 Graph Explorer 服務,這將是未來去中心化數據查詢的主要通道。使用者可以透過支付 Gas 費用來鑄造 ERC-721 代幣 Subgraph,並在其中發佈和更新 Subgraph。然而,每次更新都需要支付 0.25% 的總 Signal 金額作為更新稅,其中 0.5% 會平分給 Curator 和 Developer。
此外,在使用 Graph Explorer 進行查詢時,每筆查詢都需要支付 0.00025 ~ 0.06 ArbitrumGRT(根據查詢複雜度而定)。這些付費將被用來激勵去中心化的 Indexer,他們根據 Signal 的多寡來進行索引,同時也決定了每筆查詢的費用。
在撰寫此文時,Graph Explorer 上總共有 214 位 Indexer 為我們進行去中心化的索引
對於那些希望儲值並根據每週 API Keys 呼叫次數扣款的使用者,Graph Explorer 提供了方便的方式來管理查詢費用。而對於在特定 Subgraph 上進行查詢的需求,也可以透過限制 API Keys 的方式來實現。
作為 Curator,您使用 EOA 錢包、或 Multisig 多簽合約錢包來進行儲值與取款,詳細流程請參考官方文件
然而,值得注意的是,這些付費和費用在 Goerli 測試網上的測試中不會被計入統計,以保證用戶能夠在安全的環境中進行實驗和測試。
The Graph 作為去中心化數據查詢的重要工具之一。要充分發揮 The Graph 的優勢,我們需要了解其開發流程以及相關的操作步驟。本章節將介紹去中心化 The Graph 的開發流程,讓我們一起來探索吧。
The Graph 提供了一個清晰的 Subgraph 開發流程,幫助開發者輕鬆地建立和管理自己的數據子集。以下是 Subgraph 開發流程的主要步驟:
Create(創建): 開始創建你的 Subgraph,這可以在 Subgraph Studio 中進行。這是一個中心化的開發者後臺,讓你能夠輕鬆部署 Subgraph 到 IPFS(InterPlanetary File System)。
編輯 Entity Handler(實體處理): 在本機編輯 Subgraph 的 Entity Handler,這是用於處理區塊鏈事件和數據的函數。這一步驟的目的是確保 Subgraph 能夠準確地處理區塊鏈事件。
Deploy(部署): 將經過編輯的 Entity Handler 部署到 Subgraph Studio 中,這是一個在中心化環境中進行的操作。
Publish(發佈): 一旦 Subgraph 在 Subgraph Studio 中成功部署,你可以將其發佈到去中心化的 Graph Explorer 平臺中。這將使你的 Subgraph 可供 Consumer 尋找與查詢。
在 Subgraph 的開發過程中,你將會遇到不同的狀態,這些狀態反映了 Subgraph 的部署和可用性
UNDEPLOYED(未部署):Subgraph 還未部署到任何平臺上。
DEPLOYED(已部署) Subgraph 已成功部署,可以在特定平臺上進行查詢。
GOERLI/MAINNET(Goerli 測試網/主網):Subgraph 可在 Goerli 測試網或以太坊主網上進行查詢。
Subgraph 在本機的開發流程中,有一些關鍵步驟需要知道:
連結合約 Event 與 Entity:通過定義 Event 和 Entity,你可以確保 Subgraph 正確地捕獲區塊鏈事件並處理數據。
定義 Event 和 Entity:Event 定義了你希望 Subgraph 跟踪的區塊鏈事件,而 Entity 則定義了 Subgraph 要存儲和處理的數據結構。
編譯 Entity Handler:Entity Handler 是用於處理 Event 的函數,你需要確保它們在部署和運行時能夠正確運作。
Deploy 至 Subgraph Studio:將經過編譯的 Entity Handler 發佈到中心化的 Subgraph Studio 測試平臺上。以進行 Subgraph 正式發佈至公開的 Graph Explorer 之前的測試與調校。
在本機開發時您需要知道的 Subgraph 檔案意義:
Contract ABI 檔:取得 Event 變數(定義 Event)
Entity GraphQL 檔:定義 GraphQL API 對外可被 Query 的 Schema 資料(定義 Entity 變數)
Manifest YAML 檔:定義監聽哪個合約的 Event,及取得 Event 後對應的 Handler 處理函數(連結合約 Event 與 Entity)
Handler TypeScript 檔:定義被監聽合約的 Event 發生後,要如何處理 Event 狀態並儲存至 Entity 中

安裝 MetaMask
請至 MetaMask 網站下載
取得 GoerliETH
請至 Goerli PoW Faucet 網站,輸入您的錢包地址,並點選【Start Mining】透過電腦算力來交換 GoerliETH
取得 GoerliGRT 代幣
請點選【#🚰︱testnet-faucet 】頻道,再輸入以下指令:
!grt [錢包地址]
安裝 Graph-Cli 與 VSCode
請輸入以下指令:
~ % brew install pnpm
~ % brew install --cask visual-studio-code
~ % pnpm --global add @graphprotocol/graph-cli
請至 Subgraph Studio 網站
請點選右上角【Create a Subgraph】鈕
點選【Ethereum Goerli】網路 → 在「Subgraph name」欄位輸入名稱【LON on Goerli】→【Continue】
來到「LON on Goerli」頁面,請【複製】右下角「AUTHENTICATE IN CLI」認證指令
註 1:[DEPLOY KEY] 可在 Subgraph Studio 網站右上方查詢或重建
註 2:認證指令應如下所示
# 請自行取代 [DEPLOY KEY]
~ % graph auth --studio [DEPLOY KEY]
請在本機 Terminal【執行】上個步驟複製的認證指令
請執行以下指令
~ % mkdir -p "$HOME/GitHub/subgraph"
~ % cd $HOME/GitHub/subgraph
subgraph % graph init --studio lon-on-goerli
請輸入以下參數
✔ Protocol · ethereum
✔ Subgraph slug · lon-on-goerli
✔ Directory to create the subgraph in · lon-on-goerli
✔ Ethereum network · goerli
✔ Contract address · 0x6dA0e6ABd44175f50C563cd8b860DD988A7C3433
✔ Fetching ABI from Etherscan
✔ Start Block · 4691265
✔ Contract Name · LON
✔ Index contract events as entities (y/N) · true
✔ Add another contract? (y/N) · N

開啟「LON on Goerli」Subgraph
subgraph % code $HOME/GitHub/subgraph/lon-on-goerli/
編輯 schema.graphql
lon-on-goerli % code schema.graphql
// …上略
# 紀錄執行 Transfer 次數
type TransferTotal @entity {
id: Bytes!
total: BigInt!
}
# 紀錄 Lon 餘額與互動對象
type User @entity {
id: Bytes!
tradeCount: Int!
lonAmount: BigInt!
interactedAddr: [Bytes!]!
}

請執行以下指令,以建立 Entity TypeScript:generated/schema.ts
lon-on-goerli % graph codegen
編輯 Git 忽略清單
% code .gitignore
# dependency
node_modules/
generated/

編輯 src/lon.ts
lon-on-goerli % code src/lon.ts
// …上略
import { BigInt, Bytes, Address, log } from "@graphprotocol/graph-ts";
// …中略…
import {
TransferTotal as TransferTotalEntity,
User as UserEntity,
} from "../generated/schema";
export function handleTransfer(event: TransferEvent): void {
// …中略…
// TransferTotalEntity 儲存觸發次數
let transferTotalEntity = TransferTotalEntity.load(Bytes.fromI32(1));
if (!transferTotalEntity) {
transferTotalEntity = new TransferTotalEntity(Bytes.fromI32(1));
transferTotalEntity.total = BigInt.fromI32(0);
log.info(
"A new 'TransferTotal' Entity is created, TransferTotal.id = {}}",
[transferTotalEntity.id.toString()]
);
}
transferTotalEntity.total = transferTotalEntity.total.plus(BigInt.fromI32(1));
transferTotalEntity.save();
// UserEntity 儲存交易次數、交易數量、交易對象
const fromEntity = _getUser(event.params.from);
fromEntity.tradeCount += 1;
fromEntity.lonAmount = fromEntity.lonAmount.minus(event.params.value);
const fromInteractedAddr = fromEntity.interactedAddr;
fromInteractedAddr.push(event.params.to as Bytes);
fromEntity.interactedAddr = fromInteractedAddr;
fromEntity.save();
const toEntity = _getUser(event.params.to);
toEntity.tradeCount += 1;
toEntity.lonAmount = toEntity.lonAmount.plus(event.params.value);
const toInteractedAddr = toEntity.interactedAddr;
toInteractedAddr.push(event.params.from as Bytes);
toEntity.interactedAddr = toInteractedAddr;
toEntity.save();
// 在 Subgraph Studio 印出 Debug Log
log.debug(
"Some Entities have been updated: Transfer.id: {}, value = {}; TransferTotal.id: {}, total = {}; From.id: {}, tradeCount = {}; To.id: {}, tradeCount = {}",
[
entity.id.toString(),
entity.value.toString(),
transferTotalEntity.id.toString(),
transferTotalEntity.total.toString(),
fromEntity.id.toString(),
fromEntity.tradeCount.toString(),
toEntity.id.toString(),
toEntity.tradeCount.toString(),
]
);
}
const _getUser = (userAddr: Address): UserEntity => {
const userAddrStr = userAddr.toHex();
let user = UserEntity.load(userAddrStr);
if (!user) {
user = new UserEntity(userAddrStr);
user.tradeCount = 0;
user.lonAmount = BigInt.fromI32(0);
user.interactedAddr = [];
log.info("A new 'User' Entity is created, User.id = {}}", [user.id]);
}
return user;
};

編譯 Handler 程式碼
lon-on-goerli % graph build
請執行以下指令,以 Deploy 本機 Subgraph 至 Subgraph Studio
lon-on-goerli % graph auth --studio [DEPLOY KEY]
lon-on-goerli % graph deploy --studio lon-on-goerli
請輸入以下參數
注意:不可與已 Deploy 的版本重複
✔ Version Label (e.g. v0.0.1) · v0.0.1
可至「Lon on Goerli」頁面查看 Indexer 同步情形
請點選【Playground】鈕,並執行以下 GraphQL 程式碼
{
transferTotals {
id
total
}
users(
first: 3
orderBy: id
skip: 6
orderDirection: asc
where: {id_not: "0x0000000000000000000000000000000000000000", tradeCount_lte: 9}
) {
id
interactedAddr
lonAmount
tradeCount
}
}

請【複製】右上角「DEVELOPMENT QUERY URL - LASTEST VERSION」
註:URL 應如下所示
https://api.studio.thegraph.com/query/34764/lon-on-goerli/v0.0.1
請至 GraphiQL 網站
請在網站右側貼入上個步驟複製的 URL → 點選【Connect to Endpoint】
請執行之前步驟的 GraphQL 程式碼

請至您的「Lon on Goerli」頁面
請點選右上角【Publish】鈕
請點選【Ethereum Goerli】Network → 勾選【Be the first to signal on this subgraph】→ 輸入【10000】GRT → 點選【Allow GRT Access】鈕(出現 Metamask Confirm 視窗)
請點選【Publish】鈕開始發佈 Subgraph(出現 Metamask Confirm 視窗)
可至 Graph Explorer on Goerli 網站查看

方法與 Subgraph Studio 查詢方式相同
{
transferTotals {
id
total
}
users(
first: 3
orderBy: id
skip: 6
orderDirection: asc
where: {id_not: "0x0000000000000000000000000000000000000000", tradeCount_lte: 9}
) {
id
interactedAddr
lonAmount
tradeCount
}
}
https://gateway.testnet.thegraph.com/api/[api-key]/subgraphs/id/[subgraph-id]
請至 GraphiQL 網站
請在網站右側貼入上個步驟複製的 URL → 點選【Connect to Endpoint】
請執行之前步驟的 GraphQL 程式碼
完成 Wallet 連結(登入)後,請至 The Graph API Keys 頁面
點選【Create API Key】鈕 → 建立名為【Tokenlon】的 API Key
註:若是第一次建 Key,頁面會提示可輸入 Email、姓名、組織來取得前 1,000 次的免費 Query

請至剛建立的「Tokenlon」Key 管理頁面
請點選左側【Security】鈕 → 點選【Restrict to a Subgraph】鈕
輸入想要限制 API Key 僅能 Query 指定的一至多個【Subgraph ID】即可
註:不同的 Subgraph ID 可從 Graph Explorer 頁面中查詢(搜尋 → 點選 Subgraph 即可看到 ID)

注意:此功能僅對於有儲值 GRT 幣的帳戶
因一個 Subgraph 可同時有多個 Indexer 來索引,我們可分配偏好點數來決定該對哪一個 Indexer 進行 Query
請點選左側【Indexer Preference】鈕,有以下偏好進行選擇
最快速度
最低價格
最新索引數據
因不同 Indexer 索引速度不同,故會傾向 Query 擁有最新區塊數據的 Indexer 為主
最正確數據
因給予錯誤數據的 Indexer 會損失 GRT 幣,故會傾向 Query 擁有最多 Stake 的 Indexer 為主

請點選右上角【帳戶】→【Settings】即可新增告警 Email
可根據需要開關接收告警的 Email,告警條件如下:
餘額將在 2 週後不足時告警
餘額成負數時告警
當 Query 因未繳費而不再被接受時告警

方法一:因餘額儲存在從 Arbitrum Billing 合約上,可使用「6. userBlanaces」方法來查詢
方法二:查詢 Billing Subgraph(資料較舊不建議):
請至 https://thegraph.com/hosted-service/subgraph/graphprotocol/billing 頁面
可透過 GraphQL 來進行 Billing 的查詢,範例如下:
{
users(first: 5, where: {id: "0xe3c19b6865f2602f30537309e7f8d011ef99c1e0"}) {
billingBalance
id
polygonGRTBalance
totalTokensAdded
totalTokensPulled
totalTokensRemoved
transactions {
id
}
}
}

區塊鏈技術的迅速發展帶來了眾多應用和可能性,然而,區塊鏈數據的處理一直是一個具有挑戰性的問題。在這個背景下,The Graph 的出現為我們提供了一個強大的解決方案,讓我們能夠更便捷地查詢和檢索特定資料,極大地提高了區塊鏈應用的效率和可靠性。
The Graph 協議的去中心化生態系統是實現其強大功能的基石,其中包含著多個關鍵角色。每個角色在確保協議的穩定運行和為用戶提供支持方面都扮演著不可或缺的角色。開發者(Developer)、消費者(Consumer)、索引者(Indexer)、策展者(Curator)和委託人(Delegator)在協同合作中,共同促進了區塊鏈數據查詢的多元發展。這個生態系統的活力和多樣性,使得 The Graph 成功地搭建起了一個強大而有活力的區塊鏈數據查詢生態系統。
在不同階段,The Graph 提供了多樣性的定價策略,這反映了去中心化數據查詢生態系統的成熟和發展。從免費的 Hosted Service 到付費的 Graph Explorer,不同的選擇為用戶和開發者提供了更多的彈性。這種多樣性的定價策略不僅確保了協議的持續運行,也激勵著參與者共同推動區塊鏈技術的創新和應用,為整個生態系統的發展注入了新的活力。
在探索 The Graph 的世界中,去中心化的 Subgraph 開發流程成為了開發者們的指南。從創建到部署,從編輯 Entity Handler 到 Subgraph 的最終發佈,這一系列步驟為開發者們提供了清晰的方向。通過這個開發流程,開發者能夠確保他們的 Subgraph 在去中心化環境中運行正常,為用戶提供高質量的數據查詢服務。這一過程將有助於推動區塊鏈技術的應用和創新,實現更廣泛的去中心化數據查詢生態系統的發展。

官網:The Graph
Discord:Discord - A New Way to Chat with Friends & Communities
Telegram:The Graph Official Community
官方部落:Blog | The Graph
Hosted Service 終止時程:The Road to Sunsetting the Hosted Service: Advancing Fully Decentralized Infrastructure
從 Hosted Service 遷移至 Graph Explorer 指南:How to Migrate Your Subgraph to the Decentralized Network: A Step-by-Step Guide
遷移與補助約於 2023/03 截止:The Graph Network: Subgraph Migration Is in Full Swing
AssemblyScript 遷移指南:AssemblyScript Migration Guide
AssemblyScript API - Handling Reverted Calls:Handling Reverted Calls
Creating a Subgraph - Enums:Enums
去中心化的索引協議:web3 的谷歌——去中心化索引协议 The Graph Decentralized Indexing Protocol
提高 DAPP 開發效率:【登链公开课】The Graph 如何提高 Dapp 开发效率
在 Ethereum Mainnet 上的 GRT 代幣地址:https://etherscan.io/token/0xc944e90c64b2c07662a292be6244bdf05cda44a7
在 Arbitrum One 上的 GRT 代幣地址:https://arbiscan.io/address/0x9623063377AD1B27544C965cCd7342f7EA7e88C7
在 Ethereum Goerli Testnet 上的 GRT 代幣地址:https://goerli.etherscan.io/address/0x5c946740441C12510a167B447B7dE565C20b9E3C
在 Arbitrum Goerli Testnet 上的 GRT 代幣地址:https://goerli.arbiscan.io/address/0x18c924bd5e8b83b47efadd632b7178e2fd36073d
Testnet GoerliETH 領取工具:https://faucet.paradigm.xyz/
查詢 Subgraph 健康度:https://okgraph.xyz/
查詢 Subgraph 健康度(GraphQL):https://discord.com/channels/438038660412342282/438070183794573313/869643328138919946
查詢 Subgraph 查詢率:michael.lesane.net/graphdash/billing#/
查詢 DAPP 熱門程度:https://www.similarweb.com/
查詢 DEX 收益排行:https://tokenterminal.com/leaderboards/earnings
Mustache 套件(Yaml 檔變數取代工具):https://github.com/mustache/mustache.github.com
Postman Querying with GraphQL:https://learning.postman.com/docs/sending-requests/graphql/graphql/
GraphiQL Playground:https://cloud.hasura.io/public/graphiql
另一套 graphql-playground 可用此指令安裝:
brew install --cask graphql-playground
Diffchecker:https://www.diffchecker.com/
Unix 時戳轉換工具:https://www.epochconverter.com/
StarkNet 工具:http://util.turbofish.co/

