The Graph: consertando a consulta de dados da Web3
Desta vez, daremos uma olhada mais de perto no The Graph que, essencialmente, tornou-se parte do stack padrão para o desenvolvimento de dapps no último ano. Primeiro, vamos ver como faríamos as coisas da maneira tradicional...
Sem The Graph...
Então, vamos a um exemplo simples para fins de ilustração. Todos nós gostamos de jogos, então imagine um jogo simples com usuários fazendo apostas:
1pragma solidity 0.7.1;23contract Game {4 uint256 totalGamesPlayerWon = 0;5 uint256 totalGamesPlayerLost = 0;6 event BetPlaced(address player, uint256 value, bool hasWon);78 function placeBet() external payable {9 bool hasWon = evaluateBetForPlayer(msg.sender);1011 if (hasWon) {12 (bool success, ) = msg.sender.call{ value: msg.value * 2 }('');13 require(success, "Falha na transferência");14 totalGamesPlayerWon++;15 } else {16 totalGamesPlayerLost++;17 }1819 emit BetPlaced(msg.sender, msg.value, hasWon);20 }21}Exibir tudoAgora, digamos que em nosso dapp, queremos exibir o total de apostas, o total de jogos perdidos/ganhos e também atualizar isso sempre que alguém jogar novamente. A abordagem seria:
- Buscar
totalGamesPlayerWon. - Buscar
totalGamesPlayerLost. - Inscrever-se nos eventos
BetPlaced.
Podemos escutar o evento no Web3 (opens in a new tab) como mostrado à direita, mas isso requer lidar com alguns casos.
1GameContract.events.BetPlaced({2 fromBlock: 03}, function(error, event) { console.log(event); })4.on('data', function(event) {5 // evento disparado6})7.on('changed', function(event) {8 // evento foi removido novamente9})10.on('error', function(error, receipt) {11 // tx rejeitada12});Exibir tudoIsso ainda é aceitável para o nosso exemplo simples. Mas digamos que agora queiramos exibir os valores das apostas perdidas/ganhas apenas para o jogador atual. Bem, estamos sem sorte. É melhor implantar um novo contrato que armazene esses valores e os busque. E agora imagine um contrato inteligente e um dapp muito mais complicados. As coisas podem ficar confusas rapidamente.
Você pode ver como isso não é o ideal:
- Não funciona para contratos já implantados.
- Custos de gás extras para armazenar esses valores.
- Requer outra chamada para buscar os dados de um nó Ethereum.
Agora, vamos analisar uma solução melhor.
Deixe-me apresentar o GraphQL
Primeiro, vamos falar sobre o GraphQL, originalmente projetado e implementado pelo Facebook. Você deve estar familiarizado com o modelo tradicional de API REST. Agora imagine que, em vez disso, você pudesse escrever uma consulta para obter exatamente os dados que desejava:
As duas imagens capturam bem a essência do GraphQL. Com a consulta à direita, podemos definir exatamente quais dados queremos. Assim, obtemos tudo em uma única requisição e nada mais do que exatamente o que precisamos. Um servidor GraphQL cuida da busca de todos os dados necessários, então é incrivelmente fácil de usar para o frontend que o consome. Esta é uma boa explicação (opens in a new tab) de como exatamente o servidor lida com uma consulta, caso você tenha interesse.
Agora com esse conhecimento, vamos finalmente entrar no espaço da blockchain e no The Graph.
O que é The Graph?
Uma blockchain é um banco de dados descentralizado, mas, em contraste com o que geralmente acontece, não temos uma linguagem de consulta para esse banco de dados. As soluções para recuperar dados são penosas ou completamente impossíveis. The Graph é um protocolo descentralizado para indexar e consultar dados da blockchain. E, como você deve ter adivinhado, ele usa GraphQL como linguagem de consulta.
Exemplos são sempre a melhor forma de entender algo, então vamos usar o The Graph para nosso exemplo GameContract.
Como criar um Subgraph
A definição de como indexar dados é chamada de subgráfico. Requer três componentes:
- Manifesto (
subgraph.yaml) - Esquema (
schema.graphql) - Mapeamento (
mapping.ts)
Manifesto (subgraph.yaml)
O manifesto é nosso arquivo de configuração e define:
- quais contratos inteligentes indexar (endereço, rede, ABI...)
- a quais eventos escutar
- outras coisas para escutar, como chamadas de função ou blocos
- as funções de mapeamento que são chamadas (veja
mapping.tsabaixo)
Você pode definir vários contratos e manipuladores aqui. Uma configuração típica teria uma pasta de subgráfico dentro do projeto Hardhat com seu próprio repositório. Então você pode referenciar facilmente a ABI.
Por conveniência, você também pode querer usar uma ferramenta de template como o mustache. Então, você cria um subgraph.template.yaml e insere os endereços com base nas implantações mais recentes. Para um exemplo de configuração mais avançada, veja, por exemplo, o repositório do subgráfico da Aave (opens in a new tab).
E a documentação completa pode ser vista aqui (opens in a new tab).
1specVersion: 0.0.12description: Placing Bets on Ethereum3repository: - GitHub link -4schema:5 file: ./schema.graphql6dataSources:7 - kind: ethereum/contract8 name: GameContract9 network: mainnet10 source:11 address: '0x2E6454...cf77eC'12 abi: GameContract13 startBlock: 617524414 mapping:15 kind: ethereum/events16 apiVersion: 0.0.117 language: wasm/assemblyscript18 entities:19 - GameContract20 abis:21 - name: GameContract22 file: ../build/contracts/GameContract.json23 eventHandlers:24 - event: PlacedBet(address,uint256,bool)25 handler: handleNewBet26 file: ./src/mapping.tsExibir tudoEsquema (schema.graphql)
O esquema é a definição de dados do GraphQL. Ele permitirá que você defina quais entidades existem e seus tipos. Os tipos suportados pelo The Graph são
- Bytes
- ID
- String
- Booleano
- Int
- BigInt
- BigDecimal
Você também pode usar entidades como tipo para definir relacionamentos. Em nosso exemplo, definimos um relacionamento de 1 para muitos de jogador para apostas. O ! significa que o valor não pode estar vazio. A documentação completa pode ser vista aqui (opens in a new tab).
1type Bet @entity {2 id: ID!3 player: Player!4 playerHasWon: Boolean!5 time: Int!6}78type Player @entity {9 id: ID!10 totalPlayedCount: Int11 hasWonCount: Int12 hasLostCount: Int13 bets: [Bet]!14}Exibir tudoMapeamento (mapping.ts)
O arquivo de mapeamento no The Graph define nossas funções que transformam eventos recebidos em entidades. Ele é escrito em AssemblyScript, um subconjunto do Typescript. Isso significa que ele pode ser compilado em WASM (WebAssembly) para uma execução mais eficiente e portátil do mapeamento.
Você precisará definir cada função nomeada no arquivo subgraph.yaml, então, no nosso caso, precisamos de apenas uma: handleNewBet. Primeiro, tentamos carregar a entidade Player a partir do endereço do remetente como id. Se ela não existir, criamos uma nova entidade e a preenchemos com os valores iniciais.
Em seguida, criamos uma nova entidade Bet. O id para isso será event.transaction.hash.toHex() + "-" + event.logIndex.toString(), garantindo sempre um valor único. Usar apenas o hash não é suficiente, pois alguém pode chamar a função placeBet várias vezes em uma transação através de um contrato inteligente.
Por fim, podemos atualizar a entidade Player com todos os dados. Não é possível adicionar elementos a arrays diretamente, eles precisam ser atualizados como mostrado aqui. Usamos o id para referenciar a aposta. E .save() é necessário no final para armazenar uma entidade.
A documentação completa pode ser vista aqui: https://thegraph.com/docs/en/developing/creating-a-subgraph/#writing-mappings (opens in a new tab). Você também pode adicionar uma saída de registro ao arquivo de mapeamento, veja aqui (opens in a new tab).
1import { Bet, Player } from "../generated/schema"2import { PlacedBet } from "../generated/GameContract/GameContract"34export function handleNewBet(event: PlacedBet): void {5 let player = Player.load(event.transaction.from.toHex())67 if (player == null) {8 // cria se ainda não existir9 player = new Player(event.transaction.from.toHex())10 player.bets = new Array<string>(0)11 player.totalPlayedCount = 012 player.hasWonCount = 013 player.hasLostCount = 014 }1516 let bet = new Bet(17 event.transaction.hash.toHex() + "-" + event.logIndex.toString()18 )19 bet.player = player.id20 bet.playerHasWon = event.params.hasWon21 bet.time = event.block.timestamp22 bet.save()2324 player.totalPlayedCount++25 if (event.params.hasWon) {26 player.hasWonCount++27 } else {28 player.hasLostCount++29 }3031 // atualize o array assim32 let bets = player.bets33 bets.push(bet.id)34 player.bets = bets3536 player.save()37}Exibir tudoUsando no Frontend
Usando algo como o Apollo Boost, você pode integrar facilmente o The Graph ao seu dapp em React (ou Apollo-Vue). Especialmente ao usar hooks do React e Apollo, buscar dados é tão simples quanto escrever uma única consulta GraphQL em seu componente. Uma configuração típica pode ser assim:
1// Veja todos os subgráficos: https://thegraph.com/explorer/2const client = new ApolloClient({3 uri: "{{ subgraphUrl }}",4})56ReactDOM.render(7 <ApolloProvider client={client}>8 <App />9 </ApolloProvider>,10 document.getElementById("root")11)Exibir tudoE agora podemos escrever, por exemplo, uma consulta como esta. Isso buscará para nós
- quantas vezes o usuário atual ganhou
- quantas vezes o usuário atual perdeu
- uma lista de data e hora de todas as suas apostas anteriores
Tudo em uma única requisição para o servidor GraphQL.
1const myGraphQlQuery = gql`2 players(where: { id: $currentUser }) {3 totalPlayedCount4 hasWonCount5 hasLostCount6 bets {7 time8 }9 }10`1112const { loading, error, data } = useQuery(myGraphQlQuery)1314React.useEffect(() => {15 if (!loading && !error && data) {16 console.log({ data })17 }18}, [loading, error, data])Exibir tudoMas está faltando uma última peça do quebra-cabeça, que é o servidor. Você pode executá-lo ou usar o serviço hospedado.
O servidor do The Graph
Graph Explorer: o serviço hospedado
A maneira mais fácil é usar o serviço hospedado. Siga as instruções aqui (opens in a new tab) para implantar um subgráfico. Para muitos projetos, você pode encontrar subgráficos existentes no explorador (opens in a new tab).
Executar seu próprio nó
Como alternativa, você pode executar seu próprio nó. Documentação aqui (opens in a new tab). Uma razão para fazer isso pode ser usar uma rede que não é suportada pelo serviço hospedado. As redes atualmente suportadas podem ser encontradas aqui (opens in a new tab).
O futuro descentralizado
O GraphQL também suporta streams para eventos recém-chegados. Eles são suportados no The Graph através de Substreams (opens in a new tab), que estão atualmente em beta aberto.
Em 2021 (opens in a new tab), o The Graph iniciou sua transição para uma rede de indexação descentralizada. Você pode ler mais sobre a arquitetura desta rede de indexação descentralizada aqui (opens in a new tab).
Dois aspectos principais são:
- Os usuários pagam os indexadores por consultas.
- Os indexadores fazem stake de Graph Tokens (GRT).
Última atualização da página: 26 de fevereiro de 2026






