使用 TheGraph 完善Web3 事件資料檢索

買賣虛擬貨幣
為什麼我們需要TheGraph以及如何使用它以前我們看過Solidity的大圖和create-eth-app,它們之前已經提到過TheGraph。這次,我們將仔細研究TheGraph,它在去年已成為開發Dapps的標準堆疊的一部分。但首先讓我們看看傳統方式下如何開發...沒有TheGraph時...因此,讓我們來看一個簡單的示例,以進行說明。我們都喜歡遊戲,所以想象一個簡單的遊戲,使用者下注:pragma solidity 0.7.1;
contract Game {    uint256 totalGamesPlayerWon = 0;    uint256 totalGamesPlayerLost = 0;    event BetPlaced(address player, uint256 value, bool hasWon);    function placeBet() external payable {        bool hasWon = evaluateBetForPlayer(msg.sender);
        if (hasWon) {            (bool success, ) = msg.sender.call{ value: msg.value * 2 }('');            require(success, "Transfer failed");            totalGamesPlayerWon++;        } else {            totalGamesPlayerLost++;
        }        emit BetPlaced(msg.sender, msg.value, hasWon);    }}現在讓我們在Dapp中說,我們要顯示輸/贏的遊戲總數,並在有人再次玩時更新它。該方法將是:獲取totalGamesPlayerWon。
獲取totalGamesPlayerLost。訂閱BetPlaced事件。如下程式碼所示,我們可以監聽Web3中的事件,但這需要處理很多情況。GameContract.events.BetPlaced({    fromBlock: 0}, function(error, event) { console.log(event); })
.on('data', function(event) {    // event fired}).on('changed', function(event) {    // event was removed again})
.on('error', function(error, receipt) {    // tx rejected});

現在,對於我們的簡單示例來說,這還是可以的。但是,假設我們現在只想顯示當前玩家輸/贏的賭注數量。好吧,我們不走運,你最好部署一個新合約來儲存這些值並獲取它們。現在想象一個更復雜的智慧合約和Dapp,事情會很快變得混亂。

你可以看到以上方案不是最佳的選擇:

· 不適用於已部署的合約。
· 儲存這些值需要額外的 gas 費用。
· 需要額外的呼叫來獲取以太坊節點的資料。

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

讓我向你介紹GraphQL

首先讓我們談談最初由Facebook設計和實現的GraphQL,。你可能熟悉傳統的Rest API 模型.,現在想像一下,你可以為所需的資料編寫查詢:

這兩個影象幾乎包含了GraphQL的本質。透過第二個圖的查詢,我們可以準確定義所需的資料,因此可以在一個請求中獲得所有內容,僅此而已。GraphQL伺服器處理所有所需資料的提取,因此前端消費者使用起來非常容易。如果你有興趣對伺服器如何精確地處理查詢,這裡有一個很好的解釋。

現在有了這些知識,讓我們最終進入區塊鏈部分和TheGraph。

什麼是TheGraph?

區塊鏈是一個去中心化的資料庫,但是與通常的情況相反,我們沒有該資料庫的查詢語言。檢索資料的解決方案是痛苦或完全不可能的。TheGraph是用於索引和查詢區塊鏈資料的去中心化協議。你可能已經猜到了,它使用GraphQL作為查詢語言。

示例始終是最好的理解方法,因此讓我們在遊戲合約示例中使用TheGraph。

如何建立Subgraph

定義如何為資料建立索引,稱為Subgraph。它需要三個元件:

Manifest 清單(subgraph.yaml)
Schema 模式(schema.graphql)
Mapping 對映(mapping.ts)

清單(subgraph.yaml)

清單是我們的配置檔案,它定義:

· 要索引哪些智慧合約(地址,網路,ABI...)
· 監聽哪些事件
· 其他要監聽的內容,例如函式呼叫或塊
· 被呼叫的對映函式(請參見下面的mapping.ts)

你可以在此處定義多個合約和處理程式。一個典型的設定是Truffle/Buidler專案程式碼庫中有一個subgraph資料夾。然後,你可以輕鬆引用到ABI。

為了方便起見,你可能還需要使用mustache之類的模板工具,然後建立一個subgraph.template.yaml並根據最新部署插入地址。有關更高階的示例設定,請參見例如:Aave sub graph repo.

完整的文件可以在這裡找到:https://thegraph.com/docs/define-a-subgraph#the-subgraph-manifest。

specVersion: 0.0.1
description: Placing Bets on Ethereum
repository: - Github link -
schema:
  file: ./schema.graphql
dataSources:
  - kind: ethereum/contract
    name: GameContract
    network: mainnet
    source:
      address: '0x2E6454...cf77eC'
      abi: GameContract
      startBlock: 6175244
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.1
      language: wasm/assemblyscript
      entities:
        - GameContract
      abis:
        - name: GameContract
          file: ../build/contracts/GameContract.json
      eventHandlers:
        - event: PlacedBet(address,uint256,bool)
          handler: handleNewBet
      file: ./src/mapping.ts

模式(schema.graphql)

模式是GraphQL資料定義。它將允許你定義存在的實體及其型別。TheGraph支援的型別有

Bytes(位元組)
ID
String(字串)
Boolean(布林值)
Int(整型)
BigInt(大整數)
BigDecimal(大浮點數)

還可以使用實體作為型別來定義關係。在我們的示例中,我們定義了從玩家到下注的一對多關係。!表示該值不能為空。完整的文件可以在這裡找到:https://thegraph.com/docs/define-a-subgraph#the-graphql-schema。

type Bet @entity {
  id: ID!
  player: Player!
  playerHasWon: Boolean!
  time: Int!
}

type Player @entity {
  id: ID!
  totalPlayedCount: Int
  hasWonCount: Int
  hasLostCount: Int
  bets: [Bet]!
}

對映(mapping.ts)

TheGraph中的對映檔案定義了將傳入事件轉換為實體的函式。它用TypeScript的子集AssemblyScript編寫。這意味著可以將其編譯為WASM(WebAssembly),以更高效,更行動式地執行對映。

你將需要定義subgraph.yaml檔案中命名的每個函式,因此在我們的例子中,我們只需要一個函式:handleNewBet。我們首先嚐試從發起人地址作為ID載入為為Player實體。如果不存在,我們將建立一個新實體,並用起始值填充它。

然後,我們建立一個新的Bet實體。此ID為event.transaction.hash.toHex() + “-” + event.logIndex.toString(),確保始終為唯一值。僅使用雜湊是不夠的,因為有人可能在一次交易中會多次呼叫智慧合約的placeBet函式。

最後我們可以更新Player實體的所有資料。不能將陣列直接壓入,而需要按如下所示進行更新。我們使用ID來代表下注。最後需要.save()來儲存實體。

完整的文件可以在這裡找到:https://thegraph.com/docs/define-a-subgraph#writing-mappings。你還可以將日誌輸出新增到對映檔案中,請參閱這裡。

import { Bet, Player } from '../generated/schema';
import { PlacedBet }
    from '../generated/GameContract/GameContract';

export function handleNewBet(event: PlacedBet): void {
  let player = Player.load(
    event.transaction.from.toHex()
  );

  if (player == null) {
    // create if doesn't exist yet
    player = new Player(event.transaction.from.toHex());
    player.bets = new Array<string>(0);
    player.totalPlayedCount = 0;
    player.hasWonCount = 0;
    player.hasLostCount = 0;
  }

  let bet = new Bet(
    event.transaction.hash.toHex()
        + '-'
        + event.logIndex.toString()
  );
  bet.player = player.id;
  bet.playerHasWon = event.params.hasWon;
  bet.time = event.block.timestamp;
  bet.save();

  player.totalPlayedCount++;
  if (event.params.hasWon) {
    player.hasWonCount++;
  } else {
    player.hasLostCount++;
  }

  // update array like this
  let bets = player.bets;
  bets.push(bet.id);
  player.bets = bets;

  player.save();
}

在前端使用

使用類似ApolloBoost的東西,你可以輕鬆地將TheGraph整合到ReactDapp(或Apollo-Vue)中,尤其是當使用React hooks和Apollo時,獲取資料就像編寫單個程式碼一樣簡單的在元件中進行GraphQl查詢,典型的程式碼如下所示:

// See all subgraphs: https://thegraph.com/explorer/
const client = new ApolloClient({
  uri: "{{ subgraphUrl }}",
});

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById("root"),
);
const { loading, error, data } = useQuery(myGraphQlQuery);

React.useEffect(() => {
    if (!loading && !error && data) {
        console.log({ data });
    }
}, [loading, error, data]);

現在,我們可以編寫例如這樣的查詢。這將帶給我們

· 當前使用者贏得了多少次
· 當前使用者輸了多少次
· 他之前所有下注的時間戳列表

僅需要對GraphQL伺服器進行一個請求。

const myGraphQlQuery = gql`
    players(where: { id: $currentUser }) {
      totalPlayedCount
      hasWonCount
      hasLostCount
      bets {
        time
      }
    }
`;

但是,我們錯過了最後一個難題,那就是伺服器。你可以自己執行它,也可以使用託管服務。

Graph伺服器

GraphExplorer:託管服務

最簡單的方法是使用託管服務。按照此處的說明部署subgraph。對於許多專案,你實際上可以在資源管理器中找到現有的subgraph,網址為https://thegraph.com/explorer/.

執行自己的節點

或者,你可以執行自己的節點:https://github.com/graphprotocol/graph-node#quick-start。這樣做的原因之一可能是使用託管服務不支援的網路。當前僅支援主網,Kovan,Rinkeby,Ropsten,Goerli,PoA-Core,xDAI和Sokol。

去中心化的未來

GraphQL還為新進入的事件進行“流”支援。TheGraph尚未完全支援,但即將釋出。

缺少的一方面仍然是權力下放。TheGraph未來計劃具有最終成為完全去中心化協議。這兩篇很棒的文章更詳細地說明了該計劃:

· https://thegraph.com/blog/the-graph-network-in-depth-part-1
· https://thegraph.com/blog/the-graph-network-in-depth-part-2

兩個關鍵方面是:

1. 使用者將向索引器支付查詢費用。
2. 索引器將使用Graph通證(GRT)。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;