Como buscar e atualizar dados do Ethereum com React e SWR
NewsDevelopersEnterpriseBlockchain ExplainedEvents and ConferencesPressboletins informativos
Assine a nossa newsletter.
Endereço de email
Nós respeitamos sua privacidade
HomeBlogBlockchain Development
Como buscar e atualizar dados do Ethereum com React e SWR
Veja como configurar o front-end de seu dapp para que os saldos de token e as transferências de fundos sejam atualizados nas carteiras Ethereum de seus usuários.
Ethereum nos permite construir aplicativos descentralizados (dapps). A principal diferença entre um aplicativo típico e um dapp é que você não precisa implantar um back-end – pelo menos, desde que você aproveite os outros contratos inteligentes implantados na rede principal Ethereum.
Por causa disso, o frontend desempenha um papel importante. É responsável pelo empacotamento e desempacotamento dos dados dos contratos inteligentes, tratando das interações com a carteira (hardware ou software) e, como de costume, gerenciando a UX. Além disso, por design, um dapp usa chamadas JSON-RPC e pode abrir uma conexão de soquete para receber atualizações.
Como você pode ver, há algumas coisas para orquestrar, mas não se preocupe, o ecossistema amadureceu bastante nos últimos meses.
Pré-requisitos
Durante este tutorial, assumirei que você já possui o seguinte:
Uma carteira para conectar a um nó Geth
A abordagem mais simples é instalando MetaMask para que você possa usar Infura infraestrutura pronta para uso.
Algum éter em sua conta
Quando você está desenvolvendo com Ethereum, eu recomendo fortemente que você mude para um testnet e use o Ether de teste. Se precisar de fundos para fins de teste, você pode usar uma torneira, por exemplo. https://faucet.rinkeby.io/
Compreensão básica do React
Vou guiá-lo passo a passo, mas presumo que você saiba como o React funciona (incluindo ganchos). Se algo parecer estranho, consulte o Documentação de reação.
Um playground funcional do React
Eu escrevi este tutorial com Typescript, mas apenas algumas coisas são digitadas, portanto, com mudanças mínimas, você pode usá-lo como está em Javascript também. eu usei Parcel.js mas fique à vontade para usar Criar aplicativo React também ou outro empacotador de aplicativo da web.
Conecte-se ao Ethereum Mainnet
Depois de ter o MetaMask pronto, vamos usar web3-react para lidar com a interação com a rede. Isso lhe dará um gancho bastante útil useWeb3React, que contém muitos utilitários úteis para brincar com Ethereum.
yarn add @ web3-react / core @ web3-react / injeted-connectorLinguagem de código: CSS (css)
Então você precisa de um provedor. Um provedor abstrai uma conexão com o blockchain Ethereum, para emitir consultas e enviar transações de mudança de estado assinadas.
Usaremos Web3Provider de Ether.js.
Parece que já tem algumas bibliotecas, mas ao interagir com o Ethereum você precisa traduzir os tipos de dados Javascript para os do Solidity. E você também deve assinar as transações quando quiser executar uma ação. Ether.js elegantemente fornece essas funcionalidades.
yarn add @ ethersproject / ownersCode language: CSS (css)
aviso: o pacote Ether.js acima é o v5 atualmente em beta.
Depois disso, estamos prontos para anotar um hello world mínimo para verificar se temos tudo o que precisamos:
import React de ‘react’ import {Web3ReactProvider} de ‘@ web3-react / core’ import {Web3Provider} de ‘@ ethersproject / prestadores’ import {useWeb3React} de ‘@ web3-react / core’ import {InjectedConnector} de ‘@ web3-react / injected-connector ‘export const injectedConnector = new InjectedConnector ({supportedChainIds: [1, // Mainet 3, // Ropsten 4, // Rinkeby 5, // Goerli 42, // Kovan],}) function getLibrary (provedor: qualquer): Web3Provider {biblioteca const = new Web3Provider (provedor) library.pollingInterval = 12000 biblioteca de retorno} export const Wallet = () => {const {chainId, conta, ativar, ativo} = useWeb3React () const onClick = () => {activate (injectedConnector)} return ( <div> <div>ChainId: {chainId} div> <div>Conta: {account} div> {ativo ? ( <div>✅ div> ): ( <tipo de botão ="botão" onClick = {onClick}> Botão de conexão> )} div> )} export const App = () => { Retorna ( <Web3ReactProvider getLibrary = {getLibrary}> <Carteira /> Web3ReactProvider> )} Linguagem de código: JavaScript (javascript)
Se você fez sua lição de casa, você deve ter algo assim:
Aqui está o que fizemos até agora: GIT – etapa 1
Como obter dados da rede principal
usarei SWR para gerenciar a busca de dados.
Isso é o que eu quero alcançar.
const {data: balance} = useSWR (["getBalance", conta, "Mais recentes"]) Linguagem de código: JavaScript (javascript)
Bem legal &# 128578;
Vamos desvendar o truque! SWR significa Stale-While-Revalidate, uma estratégia de invalidação de cache HTTP popularizada por RFC 5861.
SWR primeiro retorna os dados do cache (obsoleto), em seguida, envia a solicitação de busca (revalidar) e, finalmente, vem com os dados atualizados novamente.
SWR aceita uma chave e nos bastidores vai conseguir resolver
Para fazer isso, o SWR permite passar um buscador capaz de resolver a chave retornando uma promessa. O hello world de SWR é baseado em solicitações REST API com um buscador baseado em fetch API ou Axios.
O que é brilhante sobre SWR é que o único requisito para criar um buscador é retornar uma promessa.
Então, aqui está minha primeira implementação de um buscador para Ethereum:
const fetcher = (biblioteca) => (… args) => {const [method, … params] = args console.log (method, params) return library [method] (… params)} Linguagem de código: JavaScript (javascript)
Como você pode ver, é uma função parcialmente aplicada. Dessa forma, posso injetar a biblioteca (meu Web3Provider) ao configurar o buscador. Posteriormente, toda vez que uma chave muda, a função pode ser resolvida retornando a promessa exigida.
Agora posso criar meu componente
exportar const Saldo = () => {const {account, library} = useWeb3React () const {data: balance} = useSWR ([‘getBalance’, account, ‘mais recente’], {fetcher: fetcher (library),}) if (! balance) {return <div>…div> } Retorna <div>Saldo: {balance.toString ()} div> } Linguagem de código: JavaScript (javascript)
O objeto de saldo retornado é um BigNumber.
Como você pode ver, o número não está formatado e é extremamente grande. Isso ocorre porque o Solidity usa números inteiros de até 256 bits.
Para exibir o número em um formato legível por humanos, a solução é usar um dos utilitários mencionados dos utilitários Ether.js: formatEther (balance)
yarn install @ ethersproject / unitsCode language: CSS (css)
Agora que posso retrabalhar meu componente para manipular e formatar o BitInt em uma forma legível por humanos:
exportar const Saldo = () => {const {account, library} = useWeb3React () const {data: balance} = useSWR ([‘getBalance’, account, ‘mais recente’], {fetcher: fetcher (library),}) if (! balance) {return <div>…div> } Retorna <div>Ξ {parseFloat (formatEther (balance)). ToPrecision (4)} div> } Linguagem de código: JavaScript (javascript)
isso que fizemos até agora: Etapa 2 do GIT
Como atualizar os dados em tempo real
SWR expõe uma função mutate para atualizar seu cache interno.
const {data: balance, mutate} = useSWR ([‘getBalance’, account, ‘latest’], {fetcher: fetcher (library),}) const onClick = () => {mutate (new BigNumber (10), false)} Linguagem de código: JavaScript (javascript)
A função mutate é automaticamente associada à chave (por exemplo, [‘getBalance’, conta, ‘mais recente’] a partir da qual foi gerada. Ela aceita dois parâmetros. Os novos dados e se uma validação deve ser acionada. Em caso afirmativo, SWR usará automaticamente o buscador para atualizar o cache &# 128165;
Como antecipado, os eventos do Solidity fornecem uma pequena abstração além da funcionalidade de registro do EVM. Os aplicativos podem se inscrever e ouvir esses eventos por meio da interface RPC de um cliente Ethereum.
Ether.js tem uma API simples para se inscrever em um evento:
const {conta, biblioteca} = useWeb3React () library.on ("número do bloco", (blockNumber) => {console.log ({blockNumber})}) Linguagem de código: JavaScript (javascript)
Agora vamos combinar as duas abordagens no novo componente
exportar const Saldo = () => {const {conta, biblioteca} = useWeb3React () const {dados: equilíbrio, mutação} = useSWR ([‘getBalance’, conta, ‘mais recente’], {fetcher: fetcher (biblioteca),}) useEffect (() => {// escute as mudanças em um endereço Ethereum console.log (`escutando os blocos …`) library.on (‘block’, () => {console.log (‘atualizar saldo …’) mutate (undefined, true)}) // remover ouvinte quando o componente for desmontado return () => {library.removeAllListeners (‘block’)} // aciona o efeito apenas na montagem do componente}, []) if (! balance) {return <div>…div> } Retorna <div>Ξ {parseFloat (formatEther (balance)). ToPrecision (4)} div> } Linguagem de código: JavaScript (javascript)
Inicialmente, o SWR irá buscar o saldo da conta e, em seguida, toda vez que receber um evento de bloco, ele usará mutate para acionar uma nova busca.
aviso: usamos mutate (indefinido, verdadeiro) porque não podemos recuperar do evento atual o saldo real, apenas acionamos uma nova busca do saldo.
Abaixo está uma demonstração rápida com duas carteiras Ethereum que estão trocando alguns ETH.
Aqui está o que fizemos até agora: GIT passo-3
Como interagir com um contrato inteligente
Até agora, ilustramos os fundamentos do uso de SWR e como fazer uma chamada básica por meio de um Web3Provider. Vamos agora descobrir como interagir com um contrato inteligente.
Ether.js lida com interação de contrato inteligente usando a interface binária de aplicativo de contrato (ABI) ABI gerada pelo Solidity Compiler.
O Contract Application Binary Interface (ABI) é a forma padrão de interagir com contratos no ecossistema Ethereum, tanto de fora do blockchain quanto para interação de contrato a contrato.
Por exemplo, dado o contrato inteligente simples abaixo:
solidez do pragma ^ 0,5,0; contrato Teste {construtor () público {b = hex"12345678901234567890123456789012"; } evento de evento (uint indexado a, bytes32 b); evento Event2 (uint indexado a, bytes32 b); função foo (uint a) public {emit Event (a, b); } bytes32 b; } Linguagem de código: JavaScript (javascript)
esta é a ABI gerada
[{ "modelo": "evento", "entradas": [{ "nome": "uma", "modelo": "uint256", "indexado": verdade }, { "nome": "b", "modelo": "bytes32", "indexado": false}], "nome": "Evento" }, { "modelo": "evento", "entradas": [{ "nome": "uma", "modelo": "uint256", "indexado": verdade }, { "nome": "b", "modelo": "bytes32", "indexado": false}], "nome": "Evento 2" }, { "modelo": "função", "entradas": [{ "nome": "uma", "modelo": "uint256" }], "nome": "foo", "saídas": []}] Linguagem de código: JSON / JSON com comentários (json)
Para usar os ABIs, podemos simplesmente copiá-los diretamente para o seu código e importá-los onde for necessário. Nesta demonstração, usaremos um padrão ERC20 ABI porque queremos recuperar os saldos de dois tokens: DAI e MKR.
A próxima etapa é criar o componente
export const TokenBalance = ({símbolo, endereço, decimais}) => {const {account, library} = useWeb3React () const {data: balance, mutate} = useSWR ([address, ‘balanceOf’, account], {fetcher: fetcher (library, ERC20ABI),}) useEffect (() => {// ouvir alterações em um endereço Ethereum console.log (`ouvindo Transferência …`) const contract = new Contract (address, ERC20ABI, library.getSigner ()) const fromMe = contract.filters.Transfer (account, null) library.on (fromMe, (from, to, amount, event) => {console.log (‘Transfer | enviado’, {de, para, quantidade, evento}) mutate (indefinido, verdadeiro)}) const toMe = contract.filters.Transfer (null, account) library.on (toMe, (from , para, quantidade, evento) => {console.log (‘Transferência | recebido’, {de, para, quantidade, evento}) mutate (indefinido, verdadeiro)}) // remove ouvinte quando o componente é desmontado return () => {library.removeAllListeners (toMe) library.removeAllListeners (fromMe)} // aciona o efeito apenas na montagem do componente}, []) if (! balance) {return <div>…div> } Retorna ( <div> {parseFloat (formatUnits (balance, decimals)). toPrecision (4)} {symbol} div> )} Linguagem de código: JavaScript (javascript)
Vamos aumentar o zoom. Existem duas diferenças principais:
Definição de chave
A chave, usada por useSWR ([endereço, ‘balanceOf’, conta])), precisa começar com um endereço Ethereum em vez de um método. Por causa disso, o buscador pode reconhecer o que queremos alcançar e usar o ABI.
Vamos refatorar o buscador de acordo:
const fetcher = (biblioteca: Web3Provider, abi ?: any) => (… args) => {const [arg1, arg2, … params] = args // é um contrato if (isAddress (arg1)) {const address = arg1 const method = arg2 const contract = new Contract (address, abi, library.getSigner () ) return contract [method] (… params)} // é uma chamada eth const method = arg1 return library [method] (arg2, … params)} Linguagem de código: JavaScript (javascript)
Agora temos um buscador de uso geral capaz de interagir com as chamadas JSON-RPC do Ethereum. &# 128588;
Filtros de registro
O outro aspecto é como ouvir os eventos ERC20. Ether.js fornece uma maneira prática de configurar um filtro com base nos tópicos e no nome do evento. Mais informações sobre o que é um tópico podem ser encontradas no Solidity docs.
contrato const = novo Contrato (endereço, ERC20ABI, library.getSigner ()) const fromMe = contrato.filters.Transfer (conta, nulo) Linguagem de código: JavaScript (javascript)
Depois de criar uma instância de contrato com a ABI, você pode passar o filtro para a instância de biblioteca.
Aviso:
Você pode ficar tentado a usar o valor do evento ERC20 diretamente para aumentar ou diminuir o saldo.
Esteja ciente do dragão. Ao configurar o buscador, você passou um clojure como retorno de chamada para a função on, que continha o valor de saldo no momento.
Isso poderia ser corrigido usando um useRef, mas por uma questão de simplicidade, vamos revalidar o cache para garantir que os saldos estejam atualizados: mutate (undefined, true)
Agora temos todas as peças necessárias. A última parte é um pouco de cola.
Eu configurei algumas constantes para ter uma boa maneira de mapear meu componente TokenBalance para uma lista de tokens dependendo da rede em que estamos trabalhando:
export const Networks = {MainNet: 1, Rinkeby: 4, Ropsten: 3, Kovan: 42,} export interface IERC20 {símbolo: string endereço: string decimais: número nome: string} export const TOKENS_BY_NETWORK: {[chave: número]: IERC20 []} = {[Networks.Rinkeby]: [{endereço: "0x5592EC0cfb4dbc12D3aB100b257153436a1f0FEa", símbolo: "DAI", nome: "Dai", decimais: 18,}, {endereço: "0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85", símbolo: "MKR", nome: "criador", decimais: 18,},],} Linguagem de código: JavaScript (javascript)
Uma vez que temos as constantes, é fácil mapear os tokens configurados para meu componente:
export const TokenList = ({chainId}) => { Retorna ( <> {TOKENS_BY_NETWORK [chainId] .map ((token) => ( <Chave TokenBalance = {token.address} {… token} /> ))})} Linguagem de código: JavaScript (javascript)
Tudo pronto! Agora temos uma carteira Ethereum que carrega saldos de ether e token. E se o usuário enviar ou receber fundos, a IU da carteira é atualizada.
Aqui está o que fizemos até agora: GIT passo 4
Reestruturação
Vamos mover cada componente em um arquivo separado e tornar o buscador globalmente disponível usando o provedor SWRConfig.
<SWRConfig value = {{fetcher: fetcher (biblioteca, ERC20ABI)}}> <EthBalance /> <TokenList chainId = {chainId} /> <SWRConfig />Linguagem de código: HTML, XML (xml)
Com SWRConfig podemos configurar algumas opções como sempre disponíveis, para que possamos ter um uso mais conveniente de SWR.
const {data: balance, mutate} = useSWR ([address, ‘balanceOf’, account]) Linguagem de código: JavaScript (javascript)
Aqui está tudo após a refatoração: Etapa 5 do GIT
Embrulhar
SWR e Ether.js são duas boas bibliotecas para trabalhar se você deseja otimizar sua estratégia de busca de dados com Ethereum dapp.
Principais vantagens
- Abordagem declarativa
- Dados sempre atualizados por meio de soquetes da web ou opções de SWR
- Evite reinventar a roda para gerenciamento de estado com contexto React personalizado
Se você usa vários contratos inteligentes em seu dapp e gostou deste tutorial, generalizei o buscador web3 em um pequeno utilitário: swr-eth (Estrelas são apreciadas &# 128123;)
E, finalmente, aqui está o repositório GIT completo: (https://github.com/aboutlo/swr-eth-tutorial).
Obtenha mais tutoriais Ethereum direto na sua caixa de entrada
Inscreva-se em nosso boletim informativo para obter os últimos cursos de desenvolvedor Ethereum, ferramentas, dicas profissionais e muito mais. Se inscrever InfuraMetaMaskNewsletterSubscreva nossa newsletter para obter as últimas notícias da Ethereum, soluções empresariais, recursos para desenvolvedores e muito mais. Endereço de e-mailConteúdo exclusivoWebinar
Como construir um produto blockchain de sucesso
Webinar
Como configurar e executar um nó Ethereum
Webinar
Como construir sua própria API Ethereum
Webinar
Como criar um token social
Webinar
Usando ferramentas de segurança no desenvolvimento de contrato inteligente
Webinar