WEB3DEV

Cover image for Web 3 Parte II: Como indexar eventos blockchain usando o protocolo Graph
Banana Labs
Banana Labs

Posted on • Atualizado em

Web 3 Parte II: Como indexar eventos blockchain usando o protocolo Graph

capa

Por muitos anos, o desenvolvimento de aplicativos descentralizados (DAPP) sofreu com a acessibilidade de dados, consultar uma informação específica ou usar dados diretamente persistidos na blockchain costumava ser um desafio até que uma equipe de três desenvolvedores decidiu construir um protocolo de indexação de dados para Web3 que permitiria aos desenvolvedores criar DAPPs, totalmente alimentados por uma infraestrutura pública conhecida hoje como The Graph.


Sumário

1 . O que é o protocolo The Graph

2 . O que é necessário

3 . Configuração do projeto

4 . Configuração do projeto 2

5 . Definição de Schema

6 . Implementação de Mapping

7 . Construção do Projeto

8 . Implantação do Projeto

9 . Conclusão


O que é o protocolo The Graph

Como mencionado anteriormente, o Protocolo The Graph é um protocolo descentralizado para indexação e consulta de dados blockchains.

O que é necessário

Para aproveitar ao máximo este tutorial e acompanhá-lo, leia a primeira parte desta série aqui . Acesse o site do The Graph, crie uma conta, selecione hosted services como opção de implantação e crie um novo projeto de subgráfico da seguinte forma:

the graph

Configuração do projeto

Crie um novo diretório para guardar o projeto.

mkdir ongama-nft-subgraph && cd ongama-nft-subgraph
Enter fullscreen mode Exit fullscreen mode

Crie uma pasta no diretório do projeto chamada abi onde você pode adicionar a abi do contrato NFT (NFT.json) gerado na primeira parte desta série após executar truffle compile

Em seguida, instale a CLI Graph:

npm install -g @graphprotocol/graph-cli
Enter fullscreen mode Exit fullscreen mode

Como já implantamos nosso Contrato Inteligente NFT na testnet (Mumbai), podemos executar o seguinte comando para inicializar o projeto:

graph init --from-contract <CONTRACT_ADDRESS> \
  [--network <ETHEREUM_NETWORK>] \
  [--abi <FILE>] \
  <GITHUB_USER>/<SUBGRAPH_NAME> [<DIRECTORY>]
Enter fullscreen mode Exit fullscreen mode

Vamos pegar o endereço do contrato da implantação que fizemos na primeira parte deste artigo, o comando init deve ser o seguinte:

graph init --from-contract 0x0b689AC2f5e1D925A0441cE4B3E949f3A15bD0f4 --protocol ethereum \
--network testnet --contract-name NFT --index-events
? Product for which to initialize › hosted-service
? Subgraph name › your-username/your-subgraph-project-name
? Directory to create the subgraph in › your-subgraph-project-name
? Ethereum network › mumbai
? Contract address › 0x0b689AC2f5e1D925A0441cE4B3E949f3A15bD0f4
? ABI file (path) ./abi/NFT.json
? Contract Name · NFT
Enter fullscreen mode Exit fullscreen mode

Este comando irá gerar um projeto subgrafo básico com base no endereço do contrato passado como argumento para --from-contract. Ao usar esse endereço de contrato, a CLI inicializará algumas coisas em seu projeto para você começar (incluindo buscar as abis e salvá-las no diretório abis).

Ao passar --index-events, a CLI preencherá automaticamente algum código para nós tanto em schema.graphql quanto em src/mapping.ts com base nos eventos emitidos do contrato.

A base de código do subgrafo consiste em alguns poucos arquivos:

  • subgraph.yaml: um arquivo YAML contendo a configuração principal e a definição do subgrafo
  • schema.graphql: um esquema GraphQL que define quais dados são armazenados para seu subgrafo e como consultá-los via GraphQL
  • AssemblyScript Mappings: código AssemblyScript que converte os dados do evento na Ethereum para as entidades definidas em seu esquema (por exemplo, mapping.ts neste tutorial)

Configuração do projeto 2

specVersion: 0.0.2
schema:
  file: ./schema.graphql
dataSources:
  - kind: ethereum
    name: NFT
    network: mumbai
    source:
      address: "0xB08347548b9DC9B1211D37913CE0f305FF477AcE"
      abi: NFT
      startBlock: 25957921
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.5
      language: wasm/assemblyscript
      entities:
        - User
        - NFT
        - Activity
      abis:
        - name: NFT
          file: ./abis/NFT.json
      eventHandlers:
        - event: Minted(indexed address,uint256,uint256,string)
          handler: handleNFTCreation
        - event: PriceUpdate(indexed address,uint256,uint256,uint256)
          handler: handleNFTPriceUpdate
      file: ./src/mapping.ts
Enter fullscreen mode Exit fullscreen mode

Como podemos ver, o arquivo subgraph.yaml define o seguinte:

  • name: define o nome do contrato, nesta configuração temos NFT como nome do contrato.

  • network: especificamos Mumbai, pois é o nome da rede de teste da Polygon.

  • dataSources.source: o endereço do contrato inteligente, as fontes do subgrafo e a abi do contrato inteligente a ser usado. Nesse caso, temos 0xB08347548b9DC9B1211D37913CE0f305FF477AcE.

  • dataSources.source.startBlock (opcional): o número do bloco a partir do qual a fonte de dados inicia a indexação. Estamos indexando do bloco número 25957921.

  • dataSources.mapping.entities: as entidades que a fonte de dados grava no armazenamento. O esquema para cada entidade é definido no arquivo schema.graphql. Vamos definir as entidades User, NFT & Activity dentro do arquivo de esquema.

  • dataSources.mapping.abis: um ou mais arquivos ABI nomeados para o contrato de origem, bem como quaisquer outros contratos inteligentes com os quais você interage a partir dos mapeamentos.

  • dataSources.mapping.eventHandlers: lista os eventos de contratos inteligentes aos quais este subgrafo reage e os manipuladores no mapeamento — ./src/mapping.ts no exemplo — que transformam esses eventos em entidades no armazenamento. Nosso contrato suporta apenas dois eventos que especificamos na seção eventHandlers.

Definição de Schema

As entidades/dados que iremos indexar são NFT, Activity e User. Desta forma podemos indexar as informações dos NFTs, suas atividades e usuários associados a eles.

Vamos atualizar schema.graphql com o seguinte código:

type User @entity {
  id: ID!
  creations: [NFT!] @derivedFrom(field: "creator")
  collections: [NFT!] @derivedFrom(field: "owner")
}

type NFT @entity {
  id: ID!
  owner: User! 
  creator: User!
  price: BigInt! 
  tokenID: BigInt! 
  tokenUri: String! 
  txHash: Bytes!
  actitity: [Activity!] @derivedFrom(field: "nft")
}

type Activity @entity {
  id: ID!
  nft: NFT
  type: ActivityType!
  from: User
  to: User
  txHash: Bytes!
  price: BigInt
  timestamp: BigInt!
}


enum ActivityType {
  MINT
  PRICE_UPDATE
}
Enter fullscreen mode Exit fullscreen mode

O ponto de exclamação à direita (!) é usado para denotar um campo não-anulável e @derivedFrom cria um campo virtual na entidade que pode ser consultado, mas não pode ser definido manualmente por meio da API de mapeamentos. Em vez disso, é derivado do relacionamento definido na outra entidade.

Para relacionamentos um-para-muitos, o relacionamento sempre deve ser armazenado no lado 'um' e o lado 'muitos' sempre deve ser derivado. Podemos gerar as entidades localmente para começar a usar nos mappings criados executando este comando:

graph codegen
Enter fullscreen mode Exit fullscreen mode

Depois de executar o comando acima, a Graph CLI gera tipos de AssemblyScript a partir de uma combinação do esquema GraphQL do subgrafo e das ABIs de contrato incluídas nas fontes de dados.

Implementação de Mapping

Agora, vamos atualizar src/mappings.ts adicionando a implementação de nossos dois manipuladores especificados dentro do arquivo de configuração.

import {
  Minted as MintedEvent,
  PriceUpdate as PriceUpdateEvent,
} from "../generated/NFT/NFT";
import { NFT, Activity } from "../generated/schema";
import { getOrCreateUser, getNFTByID } from "./util";

export function handleNFTCreation(event: MintedEvent): void {
  let nft = new NFT(event.params.nftID.toString());
  nft.owner = getOrCreateUser(event.params.minter).id;
  nft.creator = getOrCreateUser(event.params.minter).id;
  nft.price = event.params.price;
  nft.tokenID = event.params.nftID;
  nft.tokenUri = event.params.uri;
  nft.txHash = event.transaction.hash;
  nft.save();

  let activity = new Activity(`${event.transaction.hash.toHex()}-${nft.id}`);
  activity.nft = nft.id;
  activity.type = "MINT";
  activity.to = getOrCreateUser(event.params.minter).id;
  activity.price = nft.price;
  activity.timestamp = event.block.timestamp;
  activity.txHash = event.transaction.hash;
  activity.save();
}

export function handleNFTPriceUpdate(event: PriceUpdateEvent): void {
  let nft = getNFTByID(event.params.nftID.toString());
  if (!nft) return;
  nft.price = event.params.newPrice;
  nft.save();

  let activity = new Activity(`${event.transaction.hash.toHex()}-${nft.id}`);
  activity.nft = nft.id;
  activity.type = "PRICE_UPDATE";
  activity.to = getOrCreateUser(event.params.owner).id;
  activity.price = nft.price;
  activity.timestamp = event.block.timestamp;
  activity.txHash = event.transaction.hash;
  activity.save();
}
Enter fullscreen mode Exit fullscreen mode

Esses mappings manipularão eventos para quando um novo NFT for cunhado e quando seu preço for atualizado. Os mappings garantirão que os dados sejam salvos no subgrafo quando esses eventos forem acionados.

Vamos criar um arquivo na pasta src chamado util que conterá a implementação das funções getOrCreateUser e getNFTByID.

import { Bytes } from "@graphprotocol/graph-ts";
import { User, NFT } from "../generated/schema";

export function getOrCreateUser(address: Bytes): User {
  return getOrCreateUserFromString(address.toHex());
}

export function getOrCreateUserFromString(address: string): User {
  let user = User.load(address);
  if (!user) {
    user = new User(address);
    user.save();
  }
  return user as User;
}


export function getNFTByID(id: string): NFT | null {
  return NFT.load(id);
}
Enter fullscreen mode Exit fullscreen mode

Construção do Projeto

Em seguida, vamos executar uma compilação para garantir que tudo esteja configurado corretamente. Para fazer isso, execute o comando build:

graph build
Enter fullscreen mode Exit fullscreen mode

Se a compilação for bem-sucedida, você deverá ver uma nova pasta de compilação gerada em seu diretório raiz.

Implantação do Projeto

Para implantar, primeiro você precisará copiar o token de acesso para sua conta, disponível no Graph Explorer:

graph explorer

Em seguida, execute o seguinte comando:

$ graph auth
✔ Product for which to initialize · hosted-service
✔ Deploy key · ********************************
yarn deploy
Enter fullscreen mode Exit fullscreen mode

Depois que o subgrafo for implantado, você deverá vê-lo em seu painel:

subgraph

Seu endpoint do Graph deve ficar assim:

https://thegraph.com/explorer/subgraph/your-username/your-subgraph-name
https://api.thegraph.com/subgraphs/name/verdotte/ongama
Enter fullscreen mode Exit fullscreen mode

Agora, devemos começar a consultar dados usando várias consultas que podemos projetar com base em nossa definição de esquema. Por exemplo:

{
    nfts {
     id
     price
     tokenID
     tokenUri
     owner {
      id
     }
     creator {
      id
     }
     activities {
       price
       type
       txHash
       timestamp
     }
    }
}
Enter fullscreen mode Exit fullscreen mode

Também podemos adicionar parâmetros de filtragem extras como:

{
  activities (
    where: { type: "MINT" },
    orderBy: price,
    orderDirection: desc,
    first: 10,
    skip: 100
  ) {
    price
    type
    txHash
    timestamp
  }
}
Enter fullscreen mode Exit fullscreen mode

Conclusão

Aplicativos descentralizados começaram a ganhar popularidade entre os usuários. À medida que o espaço continua a crescer, espera-se que o desenvolvimento, a adoção e o uso de DApps também cresçam exponencialmente. É aqui que o Protocolo The Graph pode desempenhar um grande papel para os desenvolvedores. Ao tornar os dados blockchain e APIs facilmente acessíveis, o Protocolo The Graph está se tornando uma opção para gravar dados de blockchains de forma descentralizada.

Confira estes recursos para obter mais detalhes sobre o Protocolo The Graph:

https://github.com/dabit3/building-a-subgraph-workshop

https://portal.gitnation.org/contents/building-graphql-apis-on-top-of-ethereum-with-the-graph


Esse artigo é uma tradução feita por @bananlabs. Você pode encontrar o artigo original aqui

Latest comments (0)