Pular para o conteúdo principal

Contrato inteligente Hello World para iniciantes - Fullstack

Solidity
Hardhat
Alchemy
contratos inteligentes
implantação
explorador de blocos
frontend
transações
framework
Iniciante
nstrike2
25 de outubro de 2021
46 minutos de leitura

Este guia é para você se você é novo no desenvolvimento de blockchain e não sabe por onde começar ou como implantar e interagir com contratos inteligentes. Vamos percorrer a criação e implantação de um contrato inteligente simples na rede de teste Goerli usando MetaMask (opens in a new tab), Solidity (opens in a new tab), Hardhat (opens in a new tab) e Alchemy (opens in a new tab).

Você precisará de uma conta no Alchemy para concluir este tutorial. Inscreva-se para obter uma conta gratuita (opens in a new tab).

Se você tiver dúvidas em qualquer momento, sinta-se à vontade para entrar em contato no Discord do Alchemy (opens in a new tab)!

Parte 1 - Criar e implantar seu contrato inteligente usando Hardhat

Conectar-se à rede Ethereum

Existem muitas maneiras de fazer solicitações à cadeia Ethereum. Por simplicidade, usaremos uma conta gratuita no Alchemy, uma plataforma de desenvolvedores de blockchain e API que nos permite nos comunicar com a cadeia Ethereum sem executar um nó nós mesmos. O Alchemy também possui ferramentas de desenvolvedor para monitoramento e análise; aproveitaremos essas ferramentas neste tutorial para entender o que está acontecendo internamente na implantação do nosso contrato inteligente.

Criar seu aplicativo e chave de API

Depois de criar uma conta no Alchemy, você pode gerar uma chave de API criando um aplicativo. Isso permitirá que você faça solicitações à rede de teste Goerli. Se você não estiver familiarizado com redes de teste, pode ler o guia do Alchemy sobre como escolher uma rede (opens in a new tab).

No painel do Alchemy, encontre o menu suspenso Apps na barra de navegação e clique em Create App (Criar aplicativo).

Hello world create app

Dê ao seu aplicativo o nome 'Hello World' e escreva uma breve descrição. Selecione Staging como seu ambiente e Goerli como sua rede.

create app view hello world

Nota: certifique-se de selecionar Goerli, ou este tutorial não funcionará.

Clique em Create app (Criar aplicativo). Seu aplicativo aparecerá na tabela abaixo.

Criar uma conta Ethereum

Você precisa de uma conta Ethereum para enviar e receber transações. Usaremos a MetaMask, uma carteira virtual no navegador que permite aos usuários gerenciar o endereço de sua conta Ethereum.

Você pode baixar e criar uma conta na MetaMask gratuitamente aqui (opens in a new tab). Ao criar uma conta, ou se você já tiver uma, certifique-se de mudar para a "Goerli Test Network" (Rede de Teste Goerli) no canto superior direito (para não lidarmos com dinheiro real).

Passo 4: Adicionar ether de um Faucet

Para implantar seu contrato inteligente na rede de teste, você precisará de algum ETH falso. Para obter ETH na rede Goerli, vá a um faucet Goerli e insira o endereço da sua conta Goerli. Observe que os faucets Goerli podem estar um pouco instáveis recentemente - consulte a página de redes de teste para obter uma lista de opções para tentar:

Nota: devido ao congestionamento da rede, isso pode demorar um pouco. ``

Passo 5: Verificar seu saldo

Para verificar novamente se o ETH está na sua carteira, vamos fazer uma solicitação eth_getBalance (opens in a new tab) usando a ferramenta sandbox do Alchemy (opens in a new tab). Isso retornará a quantidade de ETH em nossa carteira. Para saber mais, confira o breve tutorial do Alchemy sobre como usar a ferramenta composer (opens in a new tab).

Insira o endereço da sua conta MetaMask e clique em Send Request. Você verá uma resposta parecida com o trecho de código abaixo.

{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" }

Nota: Este resultado está em wei, não em ETH. Wei é usado como a menor denominação de ether.

Ufa! Nosso dinheiro falso está todo lá.

Passo 6: Inicializar nosso projeto

Primeiro, precisaremos criar uma pasta para o nosso projeto. Navegue até a sua linha de comando e insira o seguinte.

mkdir hello-world
cd hello-world

Agora que estamos dentro da pasta do nosso projeto, usaremos npm init para inicializar o projeto.

Se você ainda não tem o npm instalado, siga as instruções de instalação do Node.js (opens in a new tab) para instalar o Node.js e o npm.

Para o propósito deste tutorial, não importa como você responde às perguntas de inicialização. Aqui está como fizemos para referência:

Aprove o package.json e estamos prontos para continuar!

Passo 7: Baixar o Hardhat

O Hardhat é um ambiente de desenvolvimento para compilar, implantar, testar e depurar seu software Ethereum. Ele ajuda os desenvolvedores a criar contratos inteligentes e aplicativos descentralizados (dapps) localmente antes de implantá-los na cadeia ativa.

Dentro do nosso projeto hello-world, execute:

npm install --save-dev hardhat

Confira esta página para obter mais detalhes sobre as instruções de instalação (opens in a new tab).

Passo 8: Criar o projeto Hardhat

Dentro da pasta do nosso projeto hello-world, execute:

npx hardhat

Você deverá ver uma mensagem de boas-vindas e a opção de selecionar o que deseja fazer. Selecione "create an empty hardhat.config.js":

Isso gerará um arquivo hardhat.config.js no projeto. Usaremos isso mais adiante no tutorial para especificar a configuração do nosso projeto.

Passo 9: Adicionar pastas do projeto

Para manter o projeto organizado, vamos criar duas novas pastas. Na linha de comando, navegue até o diretório raiz do seu projeto hello-world e digite:

mkdir contracts
mkdir scripts
  • contracts/ é onde manteremos o arquivo de código do nosso contrato inteligente hello world
  • scripts/ é onde manteremos os scripts para implantar e interagir com nosso contrato

Passo 10: Escrever nosso contrato

Você deve estar se perguntando: quando vamos escrever código? Chegou a hora!

Abra o projeto hello-world no seu editor favorito. Os contratos inteligentes são mais comumente escritos em Solidity, que usaremos para escrever nosso contrato inteligente.‌

  1. Navegue até a pasta contracts e crie um novo arquivo chamado HelloWorld.sol
  2. Abaixo está um exemplo de contrato inteligente Hello World que usaremos para este tutorial. Copie o conteúdo abaixo para o arquivo HelloWorld.sol.

Nota: Certifique-se de ler os comentários para entender o que este contrato faz.

Este é um contrato inteligente básico que armazena uma mensagem no momento da criação. Ele pode ser atualizado chamando a função update.

Passo 11: Conectar a MetaMask e o Alchemy ao seu projeto

Criamos uma carteira MetaMask, uma conta no Alchemy e escrevemos nosso contrato inteligente, agora é hora de conectar os três.

Toda transação enviada da sua carteira requer uma assinatura usando sua chave privada exclusiva. Para fornecer essa permissão ao nosso programa, podemos armazenar nossa chave privada com segurança em um arquivo de ambiente. Também armazenaremos uma chave de API para o Alchemy aqui.

Para saber mais sobre o envio de transações, confira este tutorial sobre como enviar transações usando a Web3.

Primeiro, instale o pacote dotenv no diretório do seu projeto:

npm install dotenv --save

Em seguida, crie um arquivo .env no diretório raiz do projeto. Adicione sua chave privada da MetaMask e a URL da API HTTP do Alchemy a ele.

Seu arquivo de ambiente deve ser nomeado .env ou não será reconhecido como um arquivo de ambiente.

Não o nomeie como process.env ou .env-custom ou qualquer outra coisa.

Animated walkthrough of getting an Alchemy API key

Seu .env deve ficar assim:

API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
PRIVATE_KEY = "your-metamask-private-key"

Para realmente conectá-los ao nosso código, faremos referência a essas variáveis em nosso arquivo hardhat.config.js no passo 13.

Passo 12: Instalar o Ethers.js

O Ethers.js é uma biblioteca que facilita a interação e a realização de solicitações ao Ethereum, envolvendo métodos JSON-RPC padrão com métodos mais amigáveis.

O Hardhat nos permite integrar plugins (opens in a new tab) para ferramentas adicionais e funcionalidade estendida. Aproveitaremos o plugin Ethers (opens in a new tab) para a implantação do contrato.

No diretório do seu projeto, digite:

npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0"

Passo 13: Atualizar o hardhat.config.js

Adicionamos várias dependências e plugins até agora, agora precisamos atualizar o hardhat.config.js para que nosso projeto saiba sobre todos eles.

Atualize seu hardhat.config.js para ficar assim:

Passo 14: Compilar nosso contrato

Para garantir que tudo esteja funcionando até agora, vamos compilar nosso contrato. A tarefa compile é uma das tarefas integradas do Hardhat.

Na linha de comando, execute:

npx hardhat compile

Você pode receber um aviso sobre SPDX license identifier not provided in source file, mas não precisa se preocupar com isso — esperançosamente, todo o resto parece bom! Se não, você sempre pode enviar uma mensagem no Discord do Alchemy (opens in a new tab).

Passo 15: Escrever nosso script de implantação

Agora que nosso contrato está escrito e nosso arquivo de configuração está pronto, é hora de escrever nosso script de implantação de contrato.

Navegue até a pasta scripts/ e crie um novo arquivo chamado deploy.js, adicionando o seguinte conteúdo a ele:

O Hardhat faz um trabalho incrível ao explicar o que cada uma dessas linhas de código faz em seu tutorial de Contratos (opens in a new tab), adotamos as explicações deles aqui.

const HelloWorld = await ethers.getContractFactory("HelloWorld")

Um ContractFactory no ethers.js é uma abstração usada para implantar novos contratos inteligentes, então HelloWorld aqui é uma fábrica (factory) (opens in a new tab) para instâncias do nosso contrato hello world. Ao usar o plugin hardhat-ethers, ContractFactory e Contract, as instâncias são conectadas ao primeiro signatário (proprietário) por padrão.

const hello_world = await HelloWorld.deploy()

Chamar deploy() em um ContractFactory iniciará a implantação e retornará uma Promise que é resolvida em um objeto Contract. Este é o objeto que possui um método para cada uma das funções do nosso contrato inteligente.

Passo 16: Implantar nosso contrato

Finalmente estamos prontos para implantar nosso contrato inteligente! Navegue até a linha de comando e execute:

npx hardhat run scripts/deploy.js --network goerli

Você deverá ver algo como:

Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570

Por favor, salve este endereço. Nós o usaremos mais adiante no tutorial.

Se formos ao Etherscan da Goerli (opens in a new tab) e pesquisarmos o endereço do nosso contrato, deveremos ver que ele foi implantado com sucesso. A transação será parecida com esta:

O endereço From deve corresponder ao endereço da sua conta MetaMask e o endereço To dirá Contract Creation (Criação de Contrato). Se clicarmos na transação, veremos o endereço do nosso contrato no campo To.

Parabéns! Você acabou de implantar um contrato inteligente em uma rede de teste Ethereum.

Para entender o que está acontecendo internamente, vamos navegar até a guia Explorer no nosso painel do Alchemy (opens in a new tab). Se você tiver vários aplicativos no Alchemy, certifique-se de filtrar por aplicativo e selecionar Hello World.

Aqui você verá alguns métodos JSON-RPC que o Hardhat/Ethers fez internamente para nós quando chamamos a função .deploy(). Dois métodos importantes aqui são eth_sendRawTransaction (opens in a new tab), que é a solicitação para gravar nosso contrato na cadeia Goerli, e eth_getTransactionByHash (opens in a new tab), que é uma solicitação para ler informações sobre nossa transação dado o hash. Para saber mais sobre o envio de transações, confira nosso tutorial sobre como enviar transações usando a Web3.

Parte 2: Interaja com seu contrato inteligente

Agora que implantamos com sucesso um contrato inteligente na rede Goerli, vamos aprender como interagir com ele.

Crie um arquivo interact.js

Este é o arquivo onde escreveremos nosso script de interação. Usaremos a biblioteca Ethers.js que você instalou anteriormente na Parte 1.

Dentro da pasta scripts/, crie um novo arquivo chamado interact.js e adicione o seguinte código:

// interact.js

const API_KEY = process.env.API_KEY
const PRIVATE_KEY = process.env.PRIVATE_KEY
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS

Atualize seu arquivo .env

Usaremos novas variáveis de ambiente, então precisamos defini-las no arquivo .env que criamos anteriormente.

Precisaremos adicionar uma definição para nossa API_KEY do Alchemy e o CONTRACT_ADDRESS onde seu contrato inteligente foi implantado.

Seu arquivo .env deve ficar parecido com isto:

# .env

API_URL = "https://eth-goerli.alchemyapi.io/v2/<your-api-key>"
API_KEY = "<your-api-key>"
PRIVATE_KEY = "<your-metamask-private-key>"
CONTRACT_ADDRESS = "0x<your contract address>"

Obtenha a ABI do seu contrato

A do nosso contrato é a interface para interagir com nosso contrato inteligente. O Hardhat gera automaticamente uma ABI e a salva em HelloWorld.json. Para usar a ABI, precisaremos analisar o conteúdo adicionando as seguintes linhas de código ao nosso arquivo interact.js:

// interact.js
const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")

Se você quiser ver a ABI, pode imprimi-la no seu console:

console.log(JSON.stringify(contract.abi))

Para ver sua ABI impressa no console, navegue até o seu terminal e execute:

npx hardhat run scripts/interact.js

Crie uma instância do seu contrato

Para interagir com nosso contrato, precisamos criar uma instância do contrato em nosso código. Para fazer isso com o Ethers.js, precisaremos trabalhar com três conceitos:

  1. Provedor (Provedor) - um provedor de nó que lhe dá acesso de leitura e gravação à blockchain
  2. Signatário (Signatário) - representa uma conta Ethereum que pode assinar transações
  3. Contract (Contrato) - um objeto Ethers.js representando um contrato específico implantado na cadeia (onchain)

Usaremos a ABI do contrato da etapa anterior para criar nossa instância do contrato:

Saiba mais sobre Providers, Signers e Contracts na documentação do ethers.js (opens in a new tab).

Leia a mensagem de inicialização

Lembra quando implantamos nosso contrato com a initMessage = "Hello world!"? Agora vamos ler essa mensagem armazenada em nosso contrato inteligente e imprimi-la no console.

Em JavaScript, funções assíncronas são usadas ao interagir com redes. Para saber mais sobre funções assíncronas, leia este artigo no Medium (opens in a new tab).

Use o código abaixo para chamar a função message em nosso contrato inteligente e ler a mensagem de inicialização:

Após executar o arquivo usando npx hardhat run scripts/interact.js no terminal, devemos ver esta resposta:

The message is: Hello world!

Parabéns! Você acabou de ler com sucesso os dados do contrato inteligente da blockchain do Ethereum, muito bem!

Atualize a mensagem

Em vez de apenas ler a mensagem, também podemos atualizar a mensagem salva em nosso contrato inteligente usando a função update! Muito legal, não é?

Para atualizar a mensagem, podemos chamar diretamente a função update em nosso objeto Contract instanciado:

Observe que na linha 11, fazemos uma chamada para .wait() no objeto de transação retornado. Isso garante que nosso script aguarde a transação ser minerada na blockchain antes de sair da função. Se a chamada .wait() não for incluída, o script pode não ver o valor atualizado de message no contrato.

Leia a nova mensagem

Você deve ser capaz de repetir a etapa anterior para ler o valor atualizado de message. Reserve um momento e veja se você consegue fazer as alterações necessárias para imprimir esse novo valor!

Se precisar de uma dica, veja como seu arquivo interact.js deve ficar neste ponto:

Agora é só executar o script e você deverá ver a mensagem antiga, o status de atualização e a nova mensagem impressos no seu terminal!

npx hardhat run scripts/interact.js --network goerli

The message is: Hello World!
Updating the message...
The new message is: This is the new message.

Ao executar esse script, você pode notar que a etapa Updating the message... demora um pouco para carregar antes que a nova mensagem seja carregada. Isso se deve ao processo de mineração; se você estiver curioso sobre o rastreamento de transações enquanto elas estão sendo mineradas, visite o mempool do Alchemy (opens in a new tab) para ver o status de uma transação. Se a transação for descartada, também é útil verificar o Etherscan da Goerli (opens in a new tab) e pesquisar pelo hash da sua transação.

Parte 3: Publique seu contrato inteligente no Etherscan

Você fez todo o trabalho duro de dar vida ao seu contrato inteligente; agora é hora de compartilhá-lo com o mundo!

Ao verificar seu contrato inteligente no Etherscan, qualquer pessoa pode visualizar seu código-fonte e interagir com seu contrato inteligente. Vamos começar!

Passo 1: Gere uma chave de API na sua conta do Etherscan

Uma chave de API do Etherscan é necessária para verificar se você é o proprietário do contrato inteligente que está tentando publicar.

Se você ainda não tem uma conta no Etherscan, inscreva-se para criar uma conta (opens in a new tab).

Após fazer o login, encontre seu nome de usuário na barra de navegação, passe o mouse sobre ele e selecione o botão My profile.

Na sua página de perfil, você deve ver uma barra de navegação lateral. Na barra de navegação lateral, selecione API Keys. Em seguida, pressione o botão "Add" para criar uma nova chave de API, nomeie seu aplicativo como hello-world e pressione o botão Create New API Key.

Sua nova chave de API deve aparecer na tabela de chaves de API. Copie a chave de API para a sua área de transferência.

Em seguida, precisamos adicionar a chave de API do Etherscan ao nosso arquivo .env.

Depois de adicioná-la, seu arquivo .env deve ficar assim:

API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
PUBLIC_KEY = "your-public-account-address"
PRIVATE_KEY = "your-private-account-address"
CONTRACT_ADDRESS = "your-contract-address"
ETHERSCAN_API_KEY = "your-etherscan-key"

Contratos inteligentes implantados com Hardhat

Instale o hardhat-etherscan

Publicar seu contrato no Etherscan usando o Hardhat é simples. Primeiro, você precisará instalar o plugin hardhat-etherscan para começar. O hardhat-etherscan verificará automaticamente o código-fonte e a ABI do contrato inteligente no Etherscan. Para adicionar isso, no diretório hello-world, execute:

npm install --save-dev @nomiclabs/hardhat-etherscan

Uma vez instalado, inclua a seguinte instrução no topo do seu hardhat.config.js e adicione as opções de configuração do Etherscan:

Verifique seu contrato inteligente no Etherscan

Certifique-se de que todos os arquivos estejam salvos e todas as variáveis .env estejam configuradas corretamente.

Execute a tarefa verify, passando o endereço do contrato e a rede onde ele foi implantado:

npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!'

Certifique-se de que DEPLOYED_CONTRACT_ADDRESS seja o endereço do seu contrato inteligente implantado na rede de teste Goerli. Além disso, o argumento final ('Hello World!') deve ser o mesmo valor de string usado durante a etapa de implantação na parte 1.

Se tudo correr bem, você verá a seguinte mensagem no seu terminal:

Successfully submitted source code for contract
contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address
for verification on Etherscan. Waiting for verification result...


Successfully verified contract HelloWorld on Etherscan.
https://goerli.etherscan.io/address/<contract-address>#contracts

Parabéns! O código do seu contrato inteligente está no Etherscan!

Confira seu contrato inteligente no Etherscan!

Ao navegar para o link fornecido no seu terminal, você deverá ser capaz de ver o código do seu contrato inteligente e a ABI publicados no Etherscan!

Uhuuu - você conseguiu, campeão! Agora qualquer pessoa pode chamar ou escrever no seu contrato inteligente! Mal podemos esperar para ver o que você vai construir a seguir!

Parte 4 - Integrando seu contrato inteligente com o frontend

Ao final deste tutorial, você saberá como:

  • Conectar uma carteira MetaMask ao seu dapp
  • Ler dados do seu contrato inteligente usando a API Alchemy Web3 (opens in a new tab)
  • Assinar transações Ethereum usando o MetaMask

Para este dapp, usaremos o React (opens in a new tab) como nosso framework de frontend; no entanto, é importante notar que não passaremos muito tempo detalhando seus fundamentos, pois focaremos principalmente em trazer a funcionalidade Web3 para o nosso projeto.

Como pré-requisito, você deve ter um entendimento de nível iniciante de React. Caso contrário, recomendamos concluir o tutorial oficial de Introdução ao React (opens in a new tab).

Clonar os arquivos iniciais

Primeiro, acesse o repositório do GitHub hello-world-part-four (opens in a new tab) para obter os arquivos iniciais deste projeto e clone este repositório em sua máquina local.

Abra o repositório clonado localmente. Observe que ele contém duas pastas: starter-files e completed.

  • starter-files- trabalharemos neste diretório, conectaremos a interface do usuário à sua carteira Ethereum e ao contrato inteligente que publicamos no Etherscan na Parte 3.
  • completed contém todo o tutorial concluído e deve ser usado apenas como referência se você ficar travado.

Em seguida, abra sua cópia de starter-files no seu editor de código favorito e navegue até a pasta src.

Todo o código que escreveremos ficará na pasta src. Editaremos o componente HelloWorld.js e os arquivos JavaScript util/interact.js para dar funcionalidade Web3 ao nosso projeto.

Conferir os arquivos iniciais

Antes de começarmos a programar, vamos explorar o que nos é fornecido nos arquivos iniciais.

Executar seu projeto React

Vamos começar executando o projeto React em nosso navegador. A beleza do React é que, uma vez que nosso projeto esteja rodando no navegador, quaisquer alterações que salvarmos serão atualizadas ao vivo no navegador.

Para executar o projeto, navegue até o diretório raiz da pasta starter-files e execute npm install no seu terminal para instalar as dependências do projeto:

cd starter-files
npm install

Assim que a instalação terminar, execute npm start no seu terminal:

npm start

Fazer isso deve abrir http://localhost:3000/ (opens in a new tab) no seu navegador, onde você verá o frontend do nosso projeto. Ele deve consistir em um campo (um lugar para atualizar a mensagem armazenada no seu contrato inteligente), um botão "Connect Wallet" (Conectar Carteira) e um botão "Update" (Atualizar).

Se você tentar clicar em qualquer um dos botões, notará que eles não funcionam — isso ocorre porque ainda precisamos programar a funcionalidade deles.

O componente HelloWorld.js

Vamos voltar para a pasta src no nosso editor e abrir o arquivo HelloWorld.js. É super importante que entendamos tudo neste arquivo, pois é o principal componente React no qual trabalharemos.

No topo deste arquivo, você notará que temos várias declarações de importação que são necessárias para fazer nosso projeto rodar, incluindo a biblioteca React, os hooks useEffect e useState, alguns itens do ./util/interact.js (vamos descrevê-los com mais detalhes em breve!) e o logotipo da Alchemy.

Em seguida, temos nossas variáveis de estado que atualizaremos após eventos específicos.

// HelloWorld.js

//Variáveis de estado
const [walletAddress, setWallet] = useState("")
const [status, setStatus] = useState("")
const [message, setMessage] = useState("No connection to the network.")
const [newMessage, setNewMessage] = useState("")

Aqui está o que cada uma das variáveis representa:

  • walletAddress - uma string que armazena o endereço da carteira do usuário
  • status - uma string que armazena uma mensagem útil que orienta o usuário sobre como interagir com o dapp
  • message - uma string que armazena a mensagem atual no contrato inteligente
  • newMessage - uma string que armazena a nova mensagem que será gravada no contrato inteligente

Após as variáveis de estado, você verá cinco funções não implementadas: useEffect ,addSmartContractListener, addWalletListener , connectWalletPressed e onUpdatePressed. Explicaremos o que elas fazem abaixo:

  • useEffect (opens in a new tab) - este é um hook do React que é chamado após a renderização do seu componente. Como ele tem uma propriedade de array vazio [] passada para ele (veja a linha 4), ele só será chamado na primeira renderização do componente. Aqui carregaremos a mensagem atual armazenada em nosso contrato inteligente, chamaremos nossos ouvintes (listeners) de contrato inteligente e carteira, e atualizaremos nossa interface do usuário para refletir se uma carteira já está conectada.
  • addSmartContractListener - esta função configura um ouvinte que observará o evento UpdatedMessages do nosso contrato HelloWorld e atualizará nossa interface do usuário quando a mensagem for alterada em nosso contrato inteligente.
  • addWalletListener - esta função configura um ouvinte que detecta alterações no estado da carteira MetaMask do usuário, como quando o usuário desconecta sua carteira ou troca de endereços.
  • connectWalletPressed - esta função será chamada para conectar a carteira MetaMask do usuário ao nosso dapp.
  • onUpdatePressed - esta função será chamada quando o usuário quiser atualizar a mensagem armazenada no contrato inteligente.

Perto do final deste arquivo, temos a interface do usuário do nosso componente.

Se você analisar este código com cuidado, notará onde usamos nossas várias variáveis de estado em nossa interface do usuário:

  • Nas linhas 6-12, se a carteira do usuário estiver conectada (ou seja, walletAddress.length > 0), exibimos uma versão truncada do walletAddress do usuário no botão com o ID "walletButton"; caso contrário, ele simplesmente diz "Connect Wallet".
  • Na linha 17, exibimos a mensagem atual armazenada no contrato inteligente, que é capturada na string message.
  • Nas linhas 23-26, usamos um componente controlado (opens in a new tab) para atualizar nossa variável de estado newMessage quando a entrada no campo de texto muda.

Além de nossas variáveis de estado, você também verá que as funções connectWalletPressed e onUpdatePressed são chamadas quando os botões com os IDs publishButton e walletButton são clicados, respectivamente.

Por fim, vamos abordar onde este componente HelloWorld.js é adicionado.

Se você for ao arquivo App.js, que é o componente principal no React que atua como um contêiner para todos os outros componentes, verá que nosso componente HelloWorld.js é injetado na linha 7.

Por último, mas não menos importante, vamos conferir mais um arquivo fornecido para você, o arquivo interact.js.

O arquivo interact.js

Como queremos seguir o paradigma M-V-C (opens in a new tab), precisaremos de um arquivo separado que contenha todas as nossas funções para gerenciar a lógica, os dados e as regras do nosso dapp, e então poder exportar essas funções para o nosso frontend (nosso componente HelloWorld.js).

👆🏽Este é o propósito exato do nosso arquivo interact.js!

Navegue até a pasta util no seu diretório src e você notará que incluímos um arquivo chamado interact.js que conterá todas as nossas funções e variáveis de interação com o contrato inteligente e a carteira.

Você notará no topo do arquivo que comentamos o objeto helloWorldContract. Mais adiante neste tutorial, vamos descomentar este objeto e instanciar nosso contrato inteligente nesta variável, que então exportaremos para o nosso componente HelloWorld.js.

As quatro funções não implementadas após o nosso objeto helloWorldContract fazem o seguinte:

  • loadCurrentMessage - esta função lida com a lógica de carregar a mensagem atual armazenada no contrato inteligente. Ela fará uma chamada de leitura para o contrato inteligente Hello World usando a API Alchemy Web3 (opens in a new tab).
  • connectWallet - esta função conectará o MetaMask do usuário ao nosso dapp.
  • getCurrentWalletConnected - esta função verificará se uma conta Ethereum já está conectada ao nosso dapp no carregamento da página e atualizará nossa interface do usuário de acordo.
  • updateMessage - esta função atualizará a mensagem armazenada no contrato inteligente. Ela fará uma chamada de gravação para o contrato inteligente Hello World, então a carteira MetaMask do usuário terá que assinar uma transação Ethereum para atualizar a mensagem.

Agora que entendemos com o que estamos trabalhando, vamos descobrir como ler do nosso contrato inteligente!

Passo 3: Ler do seu contrato inteligente

Para ler do seu contrato inteligente, você precisará configurar com sucesso:

  • Uma conexão de API com a cadeia Ethereum
  • Uma instância carregada do seu contrato inteligente
  • Uma função para chamar a função do seu contrato inteligente
  • Um ouvinte para observar atualizações quando os dados que você está lendo do contrato inteligente mudarem

Isso pode parecer muitos passos, mas não se preocupe! Vamos orientá-lo sobre como fazer cada um deles passo a passo! :)

Estabelecer uma conexão de API com a cadeia Ethereum

Então, lembra como na Parte 2 deste tutorial, usamos nossa chave Alchemy Web3 para ler do nosso contrato inteligente? Você também precisará de uma chave Alchemy Web3 no seu aplicativo descentralizado (dapp) para ler da cadeia.

Se você ainda não a tem, primeiro instale o Alchemy Web3 (opens in a new tab) navegando até o diretório raiz dos seus starter-files e executando o seguinte no seu terminal:

npm install @alch/alchemy-web3

O Alchemy Web3 (opens in a new tab) é um wrapper em torno do Web3.js (opens in a new tab), fornecendo métodos de API aprimorados e outros benefícios cruciais para facilitar sua vida como desenvolvedor Web3. Ele foi projetado para exigir configuração mínima, para que você possa começar a usá-lo em seu aplicativo imediatamente!

Em seguida, instale o pacote dotenv (opens in a new tab) no diretório do seu projeto, para que tenhamos um local seguro para armazenar nossa chave de API depois de buscá-la.

npm install dotenv --save

Para o nosso dapp, usaremos nossa chave de API de Websockets em vez da nossa chave de API HTTP, pois isso nos permitirá configurar um ouvinte que detecta quando a mensagem armazenada no contrato inteligente muda.

Assim que você tiver sua chave de API, crie um arquivo .env no seu diretório raiz e adicione a URL de Websockets do Alchemy a ele. Depois disso, seu arquivo .env deve ficar assim:

REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/<key>

Agora, estamos prontos para configurar nosso endpoint Alchemy Web3 no nosso dapp! Vamos voltar ao nosso interact.js, que está aninhado dentro da nossa pasta util, e adicionar o seguinte código no topo do arquivo:

// interact.js

require("dotenv").config()
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(alchemyKey)

//export const helloWorldContract;

Acima, primeiro importamos a chave do Alchemy do nosso arquivo .env e depois passamos nossa alchemyKey para createAlchemyWeb3 para estabelecer nosso endpoint Alchemy Web3.

Com este endpoint pronto, é hora de carregar nosso contrato inteligente!

Carregando seu contrato inteligente Hello World

Para carregar seu contrato inteligente Hello World, você precisará do endereço do contrato e da ABI, ambos os quais podem ser encontrados no Etherscan se você concluiu a Parte 3 deste tutorial.

Como obter a ABI do seu contrato no Etherscan

Se você pulou a Parte 3 deste tutorial, pode usar o contrato HelloWorld com o endereço 0x6f3f635A9762B47954229Ea479b4541eAF402A6A (opens in a new tab). Sua ABI pode ser encontrada aqui (opens in a new tab).

A ABI de um contrato é necessária para especificar qual função um contrato invocará, bem como garantir que a função retornará dados no formato que você espera. Depois de copiarmos a ABI do nosso contrato, vamos salvá-la como um arquivo JSON chamado contract-abi.json no seu diretório src.

Seu contract-abi.json deve ser armazenado na sua pasta src.

Armados com o endereço do nosso contrato, a ABI e o endpoint Alchemy Web3, podemos usar o método contract (opens in a new tab) para carregar uma instância do nosso contrato inteligente. Importe a ABI do seu contrato para o arquivo interact.js e adicione o endereço do seu contrato.

// interact.js

const contractABI = require("../contract-abi.json")
const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"

Agora podemos finalmente descomentar nossa variável helloWorldContract e carregar o contrato inteligente usando nosso endpoint AlchemyWeb3:

// interact.js
export const helloWorldContract = new web3.eth.Contract(
  contractABI,
  contractAddress
)

Para recapitular, as primeiras 12 linhas do seu interact.js agora devem ficar assim:

Agora que temos nosso contrato carregado, podemos implementar nossa função loadCurrentMessage!

Implementando loadCurrentMessage no seu arquivo interact.js

Esta função é super simples. Faremos uma chamada web3 assíncrona simples para ler do nosso contrato. Nossa função retornará a mensagem armazenada no contrato inteligente:

Atualize o loadCurrentMessage no seu arquivo interact.js para o seguinte:

// interact.js

export const loadCurrentMessage = async () => {
  const message = await helloWorldContract.methods.message().call()
  return message
}

Como queremos exibir este contrato inteligente em nossa interface do usuário, vamos atualizar a função useEffect em nosso componente HelloWorld.js para o seguinte:

// HelloWorld.js

//chamada apenas uma vez
useEffect(async () => {
  const message = await loadCurrentMessage()
  setMessage(message)
}, [])

Observe que queremos que nosso loadCurrentMessage seja chamado apenas uma vez durante a primeira renderização do componente. Em breve implementaremos addSmartContractListener para atualizar automaticamente a interface do usuário após a alteração da mensagem no contrato inteligente.

Antes de mergulharmos em nosso ouvinte, vamos conferir o que temos até agora! Salve seus arquivos HelloWorld.js e interact.js e, em seguida, acesse http://localhost:3000/ (opens in a new tab)

Você notará que a mensagem atual não diz mais "No connection to the network" (Sem conexão com a rede). Em vez disso, ela reflete a mensagem armazenada no contrato inteligente. Irado!

Sua interface do usuário agora deve refletir a mensagem armazenada no contrato inteligente

Agora, falando sobre esse ouvinte...

Implementar addSmartContractListener

Se você se lembrar do arquivo HelloWorld.sol que escrevemos na Parte 1 desta série de tutoriais, lembrará que há um evento de contrato inteligente chamado UpdatedMessages que é emitido após a função update do nosso contrato inteligente ser invocada (veja as linhas 9 e 27):

Os eventos de contrato inteligente são uma maneira de o seu contrato comunicar que algo aconteceu (ou seja, houve um evento) na blockchain para o seu aplicativo front-end, que pode estar 'ouvindo' eventos específicos e tomar medidas quando eles acontecerem.

A função addSmartContractListener ouvirá especificamente o evento UpdatedMessages do nosso contrato inteligente Hello World e atualizará nossa interface do usuário para exibir a nova mensagem.

Modifique addSmartContractListener para o seguinte:

Vamos detalhar o que acontece quando o ouvinte detecta um evento:

  • Se ocorrer um erro quando o evento for emitido, ele será refletido na interface do usuário por meio da nossa variável de estado status.
  • Caso contrário, usaremos o objeto data retornado. O data.returnValues é um array indexado em zero onde o primeiro elemento no array armazena a mensagem anterior e o segundo elemento armazena a atualizada. Ao todo, em um evento bem-sucedido, definiremos nossa string message para a mensagem atualizada, limparemos a string newMessage e atualizaremos nossa variável de estado status para refletir que uma nova mensagem foi publicada em nosso contrato inteligente.

Por fim, vamos chamar nosso ouvinte em nossa função useEffect para que ele seja inicializado na primeira renderização do componente HelloWorld.js. Ao todo, sua função useEffect deve ficar assim:

// HelloWorld.js

useEffect(async () => {
  const message = await loadCurrentMessage()
  setMessage(message)
  addSmartContractListener()
}, [])

Agora que somos capazes de ler do nosso contrato inteligente, seria ótimo descobrir como gravar nele também! No entanto, para gravar em nosso dapp, primeiro devemos ter uma carteira Ethereum conectada a ele.

Então, a seguir, abordaremos a configuração da nossa carteira Ethereum (MetaMask) e, em seguida, a conectaremos ao nosso dapp!

Passo 4: Configurar sua carteira Ethereum

Para gravar qualquer coisa na cadeia Ethereum, os usuários devem assinar transações usando as chaves privadas de sua carteira virtual. Para este tutorial, usaremos o MetaMask (opens in a new tab), uma carteira virtual no navegador usada para gerenciar o endereço da sua conta Ethereum, pois torna essa assinatura de transação super fácil para o usuário final.

Se você quiser entender mais sobre como as transações no Ethereum funcionam, confira esta página da fundação Ethereum.

Baixar o MetaMask

Você pode baixar e criar uma conta MetaMask gratuitamente aqui (opens in a new tab). Ao criar uma conta, ou se você já tiver uma, certifique-se de mudar para a “Goerli Test Network” no canto superior direito (para que não estejamos lidando com dinheiro real).

Adicionar ether de um Faucet

Para assinar uma transação na blockchain Ethereum, precisaremos de algum ETH falso. Para obter ETH, você pode ir ao FaucETH (opens in a new tab) e inserir o endereço da sua conta Goerli, clicar em “Request funds” (Solicitar fundos), selecionar “Ethereum Testnet Goerli” no menu suspenso e, finalmente, clicar no botão “Request funds” novamente. Você deve ver ETH na sua conta MetaMask logo depois!

Verifique seu saldo

Para verificar se o nosso saldo está lá, vamos fazer uma solicitação eth_getBalance (opens in a new tab) usando a ferramenta sandbox do Alchemy (opens in a new tab). Isso retornará a quantidade de ETH na nossa carteira. Depois de inserir o endereço da sua conta MetaMask e clicar em “Send Request”, você deverá ver uma resposta como esta:

{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}

NOTA: Este resultado está em wei, não em ETH. Wei é usado como a menor denominação de ether. A conversão de wei para ETH é: 1 ETH = 10¹⁸ wei. Portanto, se convertermos 0xde0b6b3a7640000 para decimal, obteremos 1*10¹⁸, o que equivale a 1 ETH.

Ufa! Nosso dinheiro falso está todo lá! 🤑

Passo 5: Conectar o MetaMask à sua interface do usuário

Agora que nossa carteira MetaMask está configurada, vamos conectar nosso dapp a ela!

A função connectWallet

No nosso arquivo interact.js, vamos implementar a função connectWallet, que podemos então chamar no nosso componente HelloWorld.js.

Vamos modificar connectWallet para o seguinte:

Então, o que esse bloco gigante de código faz exatamente?

Bem, primeiro, ele verifica se o window.ethereum está ativado no seu navegador.

O window.ethereum é uma API global injetada pelo MetaMask e outros provedores de carteira que permite que sites solicitem as contas Ethereum dos usuários. Se aprovado, ele pode ler dados das blockchains às quais o usuário está conectado e sugerir que o usuário assine mensagens e transações. Confira a documentação do MetaMask (opens in a new tab) para mais informações!

Se o window.ethereum não estiver presente, isso significa que o MetaMask não está instalado. Isso resulta no retorno de um objeto JSON, onde o address retornado é uma string vazia, e o objeto JSX status transmite que o usuário deve instalar o MetaMask.

Agora, se o window.ethereum estiver presente, é aí que as coisas ficam interessantes.

Usando um loop try/catch, tentaremos nos conectar ao MetaMask chamando window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). Chamar esta função abrirá o MetaMask no navegador, por meio do qual o usuário será solicitado a conectar sua carteira ao seu dapp.

  • Se o usuário optar por se conectar, method: "eth_requestAccounts" retornará um array que contém todos os endereços de conta do usuário que se conectaram ao dapp. Ao todo, nossa função connectWallet retornará um objeto JSON que contém o primeiro address neste array (veja a linha 9) e uma mensagem status que solicita ao usuário que escreva uma mensagem para o contrato inteligente.
  • Se o usuário rejeitar a conexão, o objeto JSON conterá uma string vazia para o address retornado e uma mensagem status que reflete que o usuário rejeitou a conexão.

Agora que escrevemos esta função connectWallet, o próximo passo é chamá-la em nosso componente HelloWorld.js.

Adicionar a função connectWallet ao seu Componente de Interface do Usuário HelloWorld.js

Navegue até a função connectWalletPressed em HelloWorld.js e atualize-a para o seguinte:

// HelloWorld.js

const connectWalletPressed = async () => {
  const walletResponse = await connectWallet()
  setStatus(walletResponse.status)
  setWallet(walletResponse.address)
}

Notou como a maior parte da nossa funcionalidade é abstraída do nosso componente HelloWorld.js a partir do arquivo interact.js? Isso é para estarmos em conformidade com o paradigma M-V-C!

Em connectWalletPressed, simplesmente fazemos uma chamada await para nossa função connectWallet importada e, usando sua resposta, atualizamos nossas variáveis status e walletAddress por meio de seus hooks de estado.

Agora, vamos salvar ambos os arquivos (HelloWorld.js e interact.js) e testar nossa interface do usuário até agora.

Abra seu navegador na página http://localhost:3000/ (opens in a new tab) e pressione o botão "Connect Wallet" no canto superior direito da página.

Se você tiver o MetaMask instalado, será solicitado a conectar sua carteira ao seu dapp. Aceite o convite para se conectar.

Você deve ver que o botão da carteira agora reflete que seu endereço está conectado! Isso aê 🔥

Em seguida, tente atualizar a página... isso é estranho. Nosso botão de carteira está nos solicitando a conectar o MetaMask, mesmo que ele já esteja conectado...

No entanto, não tenha medo! Podemos facilmente resolver isso implementando getCurrentWalletConnected, que verificará se um endereço já está conectado ao nosso dapp e atualizará nossa interface do usuário de acordo!

A função getCurrentWalletConnected

Atualize sua função getCurrentWalletConnected no arquivo interact.js para o seguinte:

Este código é muito semelhante à função connectWallet que acabamos de escrever no passo anterior.

A principal diferença é que, em vez de chamar o método eth_requestAccounts, que abre o MetaMask para o usuário conectar sua carteira, aqui chamamos o método eth_accounts, que simplesmente retorna um array contendo os endereços do MetaMask atualmente conectados ao nosso dapp.

Para ver esta função em ação, vamos chamá-la em nossa função useEffect do nosso componente HelloWorld.js:

Observe que usamos a resposta da nossa chamada para getCurrentWalletConnected para atualizar nossas variáveis de estado walletAddress e status.

Agora que você adicionou este código, vamos tentar atualizar a janela do nosso navegador.

Maneiro! O botão deve dizer que você está conectado e mostrar uma prévia do endereço da sua carteira conectada - mesmo depois de atualizar!

Implementar addWalletListener

O passo final na configuração da carteira do nosso dapp é implementar o ouvinte da carteira para que nossa interface do usuário seja atualizada quando o estado da nossa carteira mudar, como quando o usuário se desconecta ou troca de contas.

No seu arquivo HelloWorld.js, modifique sua função addWalletListener para o seguinte:

Aposto que você nem precisa da nossa ajuda para entender o que está acontecendo aqui neste ponto, mas por uma questão de rigor, vamos detalhar rapidamente:

  • Primeiro, nossa função verifica se o window.ethereum está ativado (ou seja, o MetaMask está instalado).
    • Se não estiver, simplesmente definimos nossa variável de estado status para uma string JSX que solicita ao usuário que instale o MetaMask.
    • Se estiver ativado, configuramos o ouvinte window.ethereum.on("accountsChanged") na linha 3 que ouve as alterações de estado na carteira MetaMask, que incluem quando o usuário conecta uma conta adicional ao dapp, troca de contas ou desconecta uma conta. Se houver pelo menos uma conta conectada, a variável de estado walletAddress é atualizada como a primeira conta no array accounts retornado pelo ouvinte. Caso contrário, walletAddress é definido como uma string vazia.

Por último, mas não menos importante, devemos chamá-lo em nossa função useEffect:

E é isso! Concluímos com sucesso a programação de toda a funcionalidade da nossa carteira! Agora vamos para a nossa última tarefa: atualizar a mensagem armazenada em nosso contrato inteligente!

Passo 6: Implementar a função updateMessage

Muito bem pessoal, chegamos à reta final! No updateMessage do seu arquivo interact.js, faremos o seguinte:

  1. Certificar-se de que a mensagem que desejamos publicar em nosso contrato inteligente é válida
  2. Assinar nossa transação usando o MetaMask
  3. Chamar esta função do nosso componente de frontend HelloWorld.js

Isso não vai demorar muito; vamos terminar este dapp!

Tratamento de erros de entrada

Naturalmente, faz sentido ter algum tipo de tratamento de erros de entrada no início da função.

Queremos que nossa função retorne antecipadamente se não houver extensão do MetaMask instalada, se não houver carteira conectada (ou seja, o address passado é uma string vazia) ou se o message for uma string vazia. Vamos adicionar o seguinte tratamento de erros a updateMessage:

Agora que ele tem o tratamento adequado de erros de entrada, é hora de assinar a transação via MetaMask!

Assinando nossa transação

Se você já está confortável com as transações tradicionais do Ethereum na Web3, o código que escreveremos a seguir será muito familiar. Abaixo do seu código de tratamento de erros de entrada, adicione o seguinte a updateMessage:

Vamos detalhar o que está acontecendo. Primeiro, configuramos nossos parâmetros de transações, onde:

  • to especifica o endereço do destinatário (nosso contrato inteligente)
  • from especifica o signatário da transação, a variável address que passamos para nossa função
  • data contém a chamada para o método update do nosso contrato inteligente Hello World, recebendo nossa variável de string message como entrada

Em seguida, fazemos uma chamada await, window.ethereum.request, onde pedimos ao MetaMask para assinar a transação. Observe que, nas linhas 11 e 12, estamos especificando nosso método eth, eth_sendTransaction e passando nosso transactionParameters.

Neste ponto, o MetaMask será aberto no navegador e solicitará ao usuário que assine ou rejeite a transação.

  • Se a transação for bem-sucedida, a função retornará um objeto JSON onde a string JSX status solicita ao usuário que verifique o Etherscan para obter mais informações sobre sua transação.
  • Se a transação falhar, a função retornará um objeto JSON onde a string status transmite a mensagem de erro.

Ao todo, nossa função updateMessage deve ficar assim:

Por último, mas não menos importante, precisamos conectar nossa função updateMessage ao nosso componente HelloWorld.js.

Conectar updateMessage ao frontend HelloWorld.js

Nossa função onUpdatePressed deve fazer uma chamada await para a função updateMessage importada e modificar a variável de estado status para refletir se nossa transação foi bem-sucedida ou falhou:

// HelloWorld.js

const onUpdatePressed = async () => {
  const { status } = await updateMessage(walletAddress, newMessage)
  setStatus(status)
}

É super limpo e simples. E adivinha... SEU DAPP ESTÁ COMPLETO!!!

Vá em frente e teste o botão Update!

Crie seu próprio dapp personalizado

Uhuuu, você chegou ao final do tutorial! Para recapitular, você aprendeu como:

  • Conectar uma carteira MetaMask ao seu projeto de dapp
  • Ler dados do seu contrato inteligente usando a API Alchemy Web3 (opens in a new tab)
  • Assinar transações Ethereum usando o MetaMask

Agora você está totalmente equipado para aplicar as habilidades deste tutorial para construir seu próprio projeto de dapp personalizado! Como sempre, se você tiver alguma dúvida, não hesite em nos contatar para obter ajuda no Discord do Alchemy (opens in a new tab). 🧙‍♂️

Depois de concluir este tutorial, conte-nos como foi sua experiência ou se você tem algum feedback nos marcando no Twitter @alchemyplatform (opens in a new tab)!