Contrato inteligente "Hello World" para iniciantes - Fullstack
Este guia é para você que é iniciante em desenvolvimento de blockchain e não sabe por onde começar ou como implantar e interagir com contratos inteligentes. Nós iremos passar por 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ê irá precisar de uma conta Alchemy para completar este tutorial. Registre-se para uma conta grátis(opens in a new tab).
E claro, se você tiver alguma dúvida em qualquer momento, não hesite em entrar no Discord da Alchemy(opens in a new tab)!
Parte 1 - Criando e Implantando seu Contrato Inteligente usando Hardhat
Conectar-se à rede Ethereum
Existem muitas maneiras de fazer solicitações à cadeia de Ethereum. Para simplificar, usaremos uma conta gratuita na Alchemy, uma plataforma de desenvolvedores de blockchain e API que nos permite comunicar com a cadeia Ethereum sem termos que executar nosso próprio nó. A Alchemy também possui ferramentas de desenvolvedor para monitoração e análise. Neste tutorial, vamos aproveitá-las para entender o que está acontecendo nos bastidores da implantação do nosso contrato inteligente.
Crie o seu app e sua chave API
Assim que criar uma conta na Alchemy, você poderá gerar uma chave API criando um app. Isso nos permitirá fazer solicitações na rede de teste Goerli. Se você não estiver familiarizado com redes de teste, você pode ler o guia da Alchemy para escolher uma rede(opens in a new tab).
No painel da Alchemy, encontre o item Apps no menu suspenso na barra de navegação e selecione Criar aplicativo.
Dê ao seu app o nome “Olá, Mundo” e escreva uma breve descrição. Selecione Staging como o seu ambiente, e Goerli como a sua rede.
Observação: certifique-se de selecionar Goerli, ou este tutorial não funcionará.
Clique em Criar app. Seu app aparecerá na tabela abaixo.
Cria uma conta Ethereum
Você precisa de uma conta Ethereum para enviar e receber transações. Nós usaremos MetaMask, a carteira virtual no navegador que permite usuários gerenciarem o endereço da sua conta Ethereum.
Você pode baixar e criar uma conta MetaMask gratuitamente neste link(opens in a new tab). Quando você estiver criando uma conta, ou se já tiver uma conta, certifique-se de mudar para a “Rede de teste Goerli”, no canto superior direito (para que não estejamos lidando com dinheiro real).
Etapa 4: Adicionar ether de um faucet
Para implantar nosso contrato inteligente na rede de teste, precisaremos de alguns ETHs falsos. Para conseguir ETH da rede Goerli, vá para o Goerli faucet e entre o endereço da sua conta Goerli. Note that Goerli faucets can be a bit unreliable recently - see the test networks page for a list of options to try:
Nota: devido a tráfego de rede, isto pode demorar um pouco.
Etapa 5: Verificar seu saldo
Para garantir que o ETH está na sua carteira, vamos fazer uma chamada eth_getBalance(opens in a new tab) usando a ferramenta de composição da Alchemy(opens in a new tab). Ele mostrará a quantidade de ETH em nossa carteira. Para saber mais, confira o Breve tutorial da Alchemy sobre como usar a ferramenta de composição(opens in a new tab).
Insira o endereço da sua conta MetaMask e clique em Send Request. Você verá a resposta que se parece com o pedação de código abaixo.
1{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" }Copiar
Nota: Este resultado é em wei, não ETH. Lembre-se de que "Wei" é a menor unidade de ether.
Ufa! O nosso dinheiro falso está todo lá.
Etapa 6: Dar início a nosso projeto
Primeiro, precisamos criar uma pasta para o nosso projeto. Navegue para a sua linha de comando e entre o seguinte.
1mkdir hello-world2cd hello-world
Agora que estamos dentro da pasta do nosso projeto, vamos usar o comando npm init
para inicializar o projeto.
Se você não tem npm instalado ainda, siga essas instruções para instalar o Node.js e o npm(opens in a new tab).
Para finalidade deste tutorial, não importa como você responde às questões de inicialização. Aqui está como nós fizemos para referência:
1package name: (hello-world)2version: (1.0.0)3description: hello world smart contract4entry point: (index.js)5test command:6git repository:7keywords:8author:9license: (ISC)1011About to write to /Users/.../.../.../hello-world/package.json:1213{14 "name": "hello-world",15 "version": "1.0.0",16 "description": "hello world smart contract",17 "main": "index.js",18 "scripts": {19 "test": "echo \"Error: no test specified\" && exit 1"20 },21 "author": "",22 "license": "ISC"23}Exibir tudo
Aprove o package.json e estaremos prontos para começar!
Passo 7: Baixar Hardhat
Hardhat é um ambiente de desenvolvimento para compilar, implementar, testar e depurar seu software de Ethereum. Ele ajuda os desenvolvedores na criação de contratos inteligentes e dapps localmente antes de implantar na cadeia real.
Dentro de nosso projeto hello-world
execute:
1npm install --save-dev hardhat
Para mais detalhes, confira esta página sobre as instruções de instalação(opens in a new tab).
Etapa 8: Criar o projeto Hardhat
Dentro da pasta do nosso projeto hello-world
, rode:
1npx hardhat
Você deve então ver uma mensagem de boas-vindas e a opção de selecionar o que quer fazer. Selecione "criar uma hardhat.config.js vazia":
1888 888 888 888 8882888 888 888 888 8883888 888 888 888 88848888888888 8888b. 888d888 .d88888 88888b. 8888b. 8888885888 888 "88b 888P" d88" 888 888 "88b "88b 8886888 888 .d888888 888 888 888 888 888 .d888888 8887888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.8888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888910👷 Welcome to Hardhat v2.0.11 👷1112What do you want to do? …13Create a sample project14❯ Create an empty hardhat.config.js15QuitExibir tudo
Isto irá gerar um arquivo hardhat.config.js
no projeto. Usaremos isso mais tarde neste tutorial para especificar a configuração do nosso projeto.
Etapa 9: Adicionar as pastas do projeto
Para manter a organização do nosso projeto, vamos criar duas novas pastas. No comando de linha, navegue para o diretório raiz do nosso projeto hello-world
e digite:
1mkdir contracts2mkdir scripts
contracts/
é onde nós vamos manter o arquivo de código do contrato inteligente "hello world"scripts/
é onde nós vamos manter scripts para implantar e interagir com nosso contrato
Etapa 10: Escrever nosso contrato
Você pode estar se perguntando, quando é que nós vamos escrever código? Está na hora!
Abra o projeto hello-world no seu editor favorito. Contratos inteligentes são mais comumente escritos em Solidity, o que nós usaremos para escrever o nosso contrato inteligente.
- Navegue para a pasta
contracts
e crie um novo arquivo chamadoHelloWorld.sol
- Veja abaixo uma amostra de contrato inteligente “Hello World”, que usaremos neste tutorial. Copie o conteúdo abaixo no arquivo
HelloWorld.sol
.
Nota: Certifique-se de ler os comentários para entender o que o contrato faz.
1// Especifica a versão do Solidity usando a versão semântica.2// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma3pragma solidity >=0.7.3;45// Defines a contract named `HelloWorld`.6// Um contrato é uma coleção de funções e dados (seu estado). Uma vez implantado, um contrato reside em um endereço específico na blockchain Ethereum. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html7contract HelloWorld {89 //Emitted when update function is called10 //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen.11 event UpdatedMessages(string oldStr, string newStr);1213 // Declares a state variable `message` of type `string`.14 // Variáveis de estado são variáveis cujos valores são permanentemente armazenados no armazenamento do contrato. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value.15 string public message;1617 // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation.18 // Os construtores são usados para inicializar os dados do contrato. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors19 constructor(string memory initMessage) {2021 // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable).22 message = initMessage;23 }2425 // A public function that accepts a string argument and updates the `message` storage variable.26 function update(string memory newMessage) public {27 string memory oldMsg = message;28 message = newMessage;29 emit UpdatedMessages(oldMsg, newMessage);30 }31}Exibir tudo
Este é um contrato inteligente básico que armazena uma mensagem quando da sua criação. Ele pode ser atualizado chamando-se a função update
.
Etapa 11: Vincular as contas MetaMask e Alchemy a seu projeto
Nós já criamos uma carteira Metamask, uma conta Alchemy e já escrevemos nosso contrato inteligente. Agora é hora de vincularmos os três.
Toda transação enviada da sua carteira requer uma assinatura, usando sua chave privada única. Para fornecer esta permissão ao nosso programa, podemos armazenar seguramente nossa chave privada em um arquivo de ambiente. Nós armazenaremos também uma chave de API da Alchemy aqui.
Para saber mais sobre o envio de transações, confira este tutorial(opens in a new tab) sobre o envio de transações usando web3.
Primeiro, instale o pacote dotenv na pasta do seu projeto:
1npm install dotenv --save
Então, crie um arquivo .env
no diretório raiz do projeto. Adicione sua chave privada MetaMask e URL da API HTTP Alchemy a ele.
Seu arquivo de ambiente deve ser nomeado .env
or ele não será reconhecido como arquivo de ambiente.
Não o nomeie como process.env
ou .env-custom
ou qualquer outra coisa.
- Siga estas instruções(opens in a new tab) para exportar sua chave privada
- Veja abaixo como obter o URL da API HTTP Alchemy
Seu arquivo .env
ficará assim:
1API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"2PRIVATE_KEY = "your-metamask-private-key"
Para realmente vinculá-los a nosso código, vamos fazer referência a essas variáveis em nosso arquivo hardhat.config.js
no passo 13.
Etapa 12: Instalar o Ethers.js
Ethers.js é uma biblioteca que facilita a interação e o envio de solicitações ao Ethereum ao incorporar métodos padrões JSON-RPC(opens in a new tab) a outros métodos mais amigáveis ao usuário.
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 implantar o contrato.
No diretório do projeto, digite:
npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0"
Etapa 13: Atualizar hardhat.config.js
Até aqui, já adicionamos diversas dependências e plugins. Agora precisamos atualizar o hardhat.config.js
para que nosso projeto reconheça todos eles.
Atualize seu hardhat.config.js
para ficar assim:
1/**2 * @type import('hardhat/config').HardhatUserConfig3 */45require("dotenv").config()6require("@nomiclabs/hardhat-ethers")78const { API_URL, PRIVATE_KEY } = process.env910module.exports = {11 solidity: "0.7.3",12 defaultNetwork: "goerli",13 networks: {14 hardhat: {},15 goerli: {16 url: API_URL,17 accounts: [`0x${PRIVATE_KEY}`],18 },19 },20}Exibir tudo
Etapa 14: Compilar nosso contrato
Para ter certeza de que tudo está funcionando, vamos compilar nosso contrato. A tarefa compile
é uma das tarefas integradas do Hardhat.
Na linha de comando, execute:
npx hardhat compile
Você pode receber o aviso SPDX license identifier not provided in source file
, mas não há necessidade de se preocupar com isso. Esperemos que tudo mais esteja bem! Se não, você sempre pode enviar uma mensagem no discord Alchemy(opens in a new tab).
Etapa 15: Escrever nosso script de implantação
Agora que nosso contrato está escrito e nosso arquivo de configuração está pronto, é hora de escrever o script de implantação do contrato.
Navegue até a pasta scripts/
e crie um novo arquivo chamado deploy.js
, adicionando o seguinte conteúdo:
1async function main() {2 const HelloWorld = await ethers.getContractFactory("HelloWorld")34 // Start deployment, returning a promise that resolves to a contract object5 const hello_world = await HelloWorld.deploy("Hello World!")6 console.log("Contract deployed to address:", hello_world.address)7}89main()10 .then(() => process.exit(0))11 .catch((error) => {12 console.error(error)13 process.exit(1)14 })Exibir tudo
A Hardhat fez um trabalho incrível ao explicar o que cada uma dessas linhas de código faz em seu Tutorial sobre contratos(opens in a new tab). Adotamos aqui as explicações deles.
1const HelloWorld = await ethers.getContractFactory("HelloWorld")
Uma ContractFactory
em ethers.js é uma abstração usada para implantar novos contratos inteligentes, então, aqui, HelloWorld
representa uma fábrica(opens in a new tab) para instâncias do nosso contrato Hello World. Quando usar o plugin hardhat-ethers
ContractFactory
e Contract
, as instâncias estão conectadas ao primeiro assinante (proprietário) por padrão.
1const hello_world = await HelloWorld.deploy()
Chamar deploy()
em uma ContractFactory
, irá iniciar a implantação, e retornará uma Promise
que se resolve em um objeto Contract
. Este é o objeto que tem um método para cada uma de nossas funções de contrato inteligente.
Etapa 16: Implantar nosso contrato
Finalmente estamos prontos para implantar o nosso contrato inteligente! Navegue até a linha de comando e digite:
npx hardhat run scripts/deploy.js --network goerli
Você deverá ver algo assim:
Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570
Por favor, grave este endereço. Nós o usaremos mais tarde neste tutorial.
Se formos ao etherscan da Goerli(opens in a new tab) e procurarmos nosso endereço de contrato, devemos ser capazes de ver que ele foi implantado com sucesso. A transação ficará parecida com isto:
O endereço From
deve combinar com o endereço da sua conta MetaMask, e o endereço To
conterá Contract Creation. Se clicarmos na transação, veremos o nosso endereço de contrato no campo To
.
Parabéns! Você acaba de implantar um contrato inteligente em uma rede de teste Ethereum.
Para entender o que está acontecendo nos bastidores, vamos navegar até a guia Explorer no painel do Alchemy(opens in a new tab). Se você tem vários aplicativos Alchemy, certifique-se de filtrar por app e selecionar Hello World.
Aqui você verá um punhado de métodos JSON-RPC que Hardhat/Ethers fizeram em segundo plano para nós quando chamamos a função .deploy()
. Dois importantes métodos aqui são eth_sendRawTransaction
(opens in a new tab), que é a requisição para escrever nosso contrato na cadeia Goerli, e eth_getTransactionByHash
(opens in a new tab) que é uma requisição para ler informações sobre nossa transação, dado o hash. Para saber mais sobre o envio de transações, confira este tutorial sobre o envio de transações usando web3.
Parte 2: Interaja com o seu Contrato Inteligente
Agora que você implantou com sucesso um contrato inteligente na rede Goerli, vamos aprender como interagir com ele.
Crie um arquivo interact.js
Este é o arquivo onde nós iremos escrever nosso script de interação. Nós usaremos a biblioteca Ether.js que você instalou anteriormente na Parte1.
Dentro da pasta scripts/
crie um novo arquivo chamado interact.js
, adicionando o seguinte código:
1// interact.js23const API_KEY = process.env.API_KEY4const PRIVATE_KEY = process.env.PRIVATE_KEY5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS
Atualize seu arquivo .env
Nós usaremos novas variáveis de ambiente, portanto nós precisamos defini-las no arquivo .env
que nós criamos antes.
Nós precisaremos adicionar uma definição para a nossa API_KEY
Alchemy e o CONTRACT_ADDRESS
onde o nosso contrato inteligente foi implantado.
Seu arquivo .env
deverá se parecer com isto:
# .envAPI_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>"
Pegue a ABI do seu contrato
O do nosso contrato é a interface para interagir com o nosso contrato inteligente. O Hardhat automaticamente gera uma ABI e a salva no arquivo HelloWorld.json
. Para usar a ABI, precisaremos analisar o conteúdo adicionando as seguintes linhas de código ao nosso arquivo interact.js
:
1// interact.js2const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")
Se quiser ver a ABI, pode imprimi-la no console:
1console.log(JSON.stringify(contract.abi))
Para ver o seu ABI impresso no console, navegue até seu terminal e execute:
npx hardhat run scripts/interact.js
Criar uma instância do seu contrato
Para interagir com o nosso contrato, precisamos criar uma instância dele em nosso código. Para fazer isso com Ether.js, nós precisaremos trabalhar com três conceitos:
- Provedor — um nó fornecedor que lhe dá acesso de leitura e escrita ao blockchain
- Signatário — representa uma conta Ethereum que pode assinar transações
- Contrato — um objeto Ether.js representando um contrato específico implantado on-chain
Usaremos a ABI do contrato da etapa anterior para criar nossa instância do contrato:
1// interact.js23// Provider4const alchemyProvider = new ethers.providers.AlchemyProvider(5 (network = "goerli"),6 API_KEY7)89// Signer10const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)1112// Contract13const helloWorldContract = new ethers.Contract(14 CONTRACT_ADDRESS,15 contract.abi,16 signer17)Exibir tudo
Aprenda mais sobre Provedores, Signatários e Contratos na documentação ethers.js(opens in a new tab).
Leia a mensagem init
Lembra-se de quando implantamos nosso contrato com o initMessage = "Hello world!"
? Nós vamos agora ler a mensagem armazenada no nosso contrato inteligente e imprimi-la no console.
Em JavaScript, funções assíncronas são usadas quando interagindo com redes. Para aprender mais sobre funções assíncronas, leia este artigo(opens in a new tab).
Use o código abaixo para chamar a função message
no nosso contrato inteligente e ler a mensagem init:
1// interact.js23// ...45async function main() {6 const message = await helloWorldContract.message()7 console.log("The message is: " + message)8}9main()Exibir tudo
Depois de rodar o arquivo usando npx hardhat run scripts/interact.js
no terminal, nós devemos ver esta resposta:
1The message is: Hello world!
Parabéns! Você acabou de ler com sucesso dados de contrato inteligente do blockchain Ethereum, continue assim!
Atualize a mensagem
Ao invés de só ler a mensagem, nós podemos também atualizar a mensagem salva no nosso contrato inteligente usando a função update
! Muito bacana, não?
Para atualizar a mensagem, nós podemos chamar diretamente a função update
no nosso objeto Contract instanciado:
1// interact.js23// ...45async function main() {6 const message = await helloWorldContract.message()7 console.log("The message is: " + message)89 console.log("Updating the message...")10 const tx = await helloWorldContract.update("This is the new message.")11 await tx.wait()12}13main()Exibir tudo
Note que na linha 11, nós fazemos uma chamada para .wait()
no objeto da transação retornada. Isso garante que nosso script espere pela transação ser minerada no blockchain antes de sair da função. Se a chamada .wait()
não estiver incluída, o script pode não ver o valor da message
atualizada no contrato.
Leia a nova mensagem
Você deve ser capaz de repetir o passo anterior para ler o valor atualizado da message
. Pegue um momento e veja se você pode fazer as mudanças necessárias para imprimir o novo valor!
Se você precisar de uma dica, aqui está o que o seu arquivo interact.js
deve se parecer neste ponto:
1// interact.js23const API_KEY = process.env.API_KEY4const PRIVATE_KEY = process.env.PRIVATE_KEY5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS67const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")89// provider - Alchemy10const alchemyProvider = new ethers.providers.AlchemyProvider(11 (network = "goerli"),12 API_KEY13)1415// signer - you16const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)1718// contract instance19const helloWorldContract = new ethers.Contract(20 CONTRACT_ADDRESS,21 contract.abi,22 signer23)2425async function main() {26 const message = await helloWorldContract.message()27 console.log("The message is: " + message)2829 console.log("Updating the message...")30 const tx = await helloWorldContract.update("this is the new message")31 await tx.wait()3233 const newMessage = await helloWorldContract.message()34 console.log("The new message is: " + newMessage)35}3637main()Exibir tudo
Agora apenas rode o script e você deve ser capaz de ver a mensagem antiga, o estado atualizado, e a nova mensagem impressa no seu terminal!
npx hardhat run scripts/interact.js --network goerli
1The message is: Hello World!2Updating the message...3The new message is: This is the new message.
Enquanto estiver rodando este script, você pode perceber que o passo Updating the message...
leva um tempo para carregar antes da nova mensagem carregar. Isto é por causa do processo de mineração; se você é curioso sobre rastrear transações enquanto elas estão sendo mineradas, visite o Alchemy mempool(opens in a new tab) para ver o estado da transação. Se a transação for derrubada, também é útil checar o Goerli Etherscan(opens in a new tab) e procurar pelo hash da sua transação.
Parte 3: Publique seu Contrato Inteligente no Etherscan
Você fez todo o trabalho duro dar vida ao seu contrato inteligente; agora é hora de compartilhá-lo com o mundo!
Verificando seu contrato inteligente no Etherscan, qualquer um pode ver seu código-fonte e interagir com o seu contrato inteligente. Vamos começar!
Passo 1: Gere a Chave API na sua conta Etherscan
Uma Chave API Etherscan é necessária para verificar que você possui o contrato inteligente que você está tentando publicar.
Se você não tem uma conta Etherscan ainda, se inscreva para uma conta(opens in a new tab).
Uma vez conectado, encontre seu nome de usuário na barra de navegação, passe o mouse em cima dele, e selecione o botão My profile.
Na página do seu perfil, você deve ver uma barra de navegação lateral. Da barra de navegação lateral, selecione API Keys. Em seguida, pressione o botão "Add" para criar uma nova chave API, nomeie seu app hello-worlde pressione o botão Create New API Key.
Sua nova chave API deve aparecer na tabela de chaves API. Copie a chave API na sua área de transferência.
Agora nós precisamos adicionar a chave API Etherscan no seu arquivo .env
.
Depois de adicionar isso, seu arquivo .env
deve se parecer com isso:
1API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"2PUBLIC_KEY = "your-public-account-address"3PRIVATE_KEY = "your-private-account-address"4CONTRACT_ADDRESS = "your-contract-address"5ETHERSCAN_API_KEY = "your-etherscan-key"
Contratos inteligentes implantados pelo Hardhat
Instale o hardhat-etherscan
Publicar o seu contrato no Etherscan usando Hardhat é uma tarefa direta. Você primeiro precisa instalar o plugin hardhat-etherscan
para começar. hardhat-etherscan
verificará automaticamente o código-fonte do contrato inteligente e da ABI no Etherscan. Para adicionar isso, no diretório hello-world
rode:
1npm install --save-dev @nomiclabs/hardhat-etherscan
Uma vez instalado, inclua o seguinte comando no topo do seu hardhat.config.js
, e adicione as opções de configuração Etherscan:
1// hardhat.config.js23require("dotenv").config()4require("@nomiclabs/hardhat-ethers")5require("@nomiclabs/hardhat-etherscan")67const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env89module.exports = {10 solidity: "0.7.3",11 defaultNetwork: "goerli",12 networks: {13 hardhat: {},14 goerli: {15 url: API_URL,16 accounts: [`0x${PRIVATE_KEY}`],17 },18 },19 etherscan: {20 // Your API key for Etherscan21 // Obtain one at https://etherscan.io/22 apiKey: ETHERSCAN_API_KEY,23 },24}Exibir tudo
Verifique seu contrato inteligente no Etherscan
Certifique-se que todos os arquivos foram salvos e todas as variáveis .env
estão corretamente configuradas.
Rode a tarefa verify
, passando o endereço do contrato, e a rede onde ele foi implantado:
1npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!'
Certifique-se que DEPLOYED_CONTRACT_ADDRESS
é o endereço do seu contrato inteligente implantado na rede de teste Goerli. Além disso, o argumento final ('Hello World!'
) tem de ser o mesmo valor de string usado durante o passo de implantação na parte 1.
Se tudo der certo, você verá a seguinte mensagem no seu terminal:
1Successfully submitted source code for contract2contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address3for verification on Etherscan. Waiting for verification result...456Successfully verified contract HelloWorld on Etherscan.7https://goerli.etherscan.io/address/<contract-address>#contracts
Parabéns! O código do seu contrato inteligente está no Etherscan!
Cheque seu contrato inteligente no Etherscan!
Quando você navegar para o link fornecido no seu terminal, você deve ser capaz de ver o código do seu contrato inteligente e ABI publicados no Etherscan!
Parabéns, você conseguiu, campeão! Agora qualquer um pode chamar ou escrever no seu contrato inteligente! Nós mal conseguimos esperar o que você vai construir em seguida!
Parte 4 - Integrando seu contrato inteligente com o front-end
No fim deste tutorial você saberá como:
- Conectar uma carteira MetaMask no seu dapp
- Ler dados do seu contrato inteligente usando a API Alchemy Web3(opens in a new tab)
- Assinar transações Ethereum usando MetaMask
Para este dapp, estaremos usando React(opens in a new tab) como nosso framework de front-end; entretanto, é importante notar que nós não gastaremos muito tempo explicando seus fundamentos, já que estaremos principalmente focados em trazer funcionalidade Web3 para o nosso projeto.
Como um pré-requisito, você deve ter um nível iniciante de entendimento de React. Caso contrário, recomendamos concluir o tutorial oficial Introdução ao React(opens in a new tab).
Clonar os arquivos iniciais
Primeiro, vá até o repositório GitHub hello-world-part-four(opens in a new tab) para obter os arquivos iniciais para esse projeto e clone o repositório no seu computador local.
Abra o repositório clonado localmente. Note que ele contém duas pastas: starter-files
e completed
.
starter-files
- nós trabalharemos neste diretório, nós conectaremos a UI à nossa carteira Ethereum e o contrato inteligente que nós publicamos no Etherscan na Parte 3.completed
contém o tutorial inteiro completado e deve ser somente usado como referência se você estiver empacado.
Em seguida, abra sua cópia de starter-files
no seu editor de código favorito, e então navegue na pasta src
.
Todo o código que vamos escrever será exibido na pasta src
. Nós estaremos editando o componente HelloWorld.js
e os arquivos JavaScript util/interact.js
para dar ao seu projeto funcionalidade Web3.
Cheque os arquivos iniciais
Antes de começar a codificar, vamos explorar o que nos é fornecido nos arquivos iniciais.
Tenha seu projeto React em execução
Vamos começar executando o projeto React em nosso navegador. A beleza do React é que uma vez que nosso projeto esteja sendo executado no nosso navegador, qualquer alteração que salvarmos será atualizada ao vivo em nosso navegador.
Para fazer com que o projeto funcione, navegue até o diretório raiz da pasta starter-files
, e executenpm install
no seu terminal para instalar as dependências do projeto:
cd starter-filesnpm install
Uma vez terminada a instalação, execute npm start
em seu terminal:
npm start
Ao fazê-lo, deve abrir http://localhost:3000/(opens in a new tab) no seu navegador, onde você verá o front-end do nosso projeto. Ele deve consistir em um campo \ (um lugar para atualizar a mensagem armazenada no seu contrato inteligente), um botão “Conectar Carteira”, e um botão “Atualizar”.
Se você tentar clicar em qualquer dos botões você notará que eles não funcionam — isso porque ainda precisamos programar a funcionalidade deles.
O componente HelloWorld.js
Vamos voltar à pasta src
no nosso editor e abrir o arquivo HelloWorld.js
. É muito importante que entendamos tudo neste arquivo, pois é o principal componente do React no qual vamos trabalhar.
No começo deste arquivo você irá notar que nós temos diversas declarações importantes que são necessárias para termos nosso projeto rodando, incluindo a biblioteca React, os hooks useEffect e UseState, alguns itens do ./util/interact.js
(nós os descreveremos em mais detalhes em breve!), e o logo Alchemy.
1// HelloWorld.js23import React from "react"4import { useEffect, useState } from "react"5import {6 helloWorldContract,7 connectWallet,8 updateMessage,9 loadCurrentMessage,10 getCurrentWalletConnected,11} from "./util/interact.js"1213import alchemylogo from "./alchemylogo.svg"Exibir tudo
Em seguida, temos nossas variáveis de estado que serão atualizadas após eventos específicos.
1// HelloWorld.js23//State variables4const [walletAddress, setWallet] = useState("")5const [status, setStatus] = useState("")6const [message, setMessage] = useState("No connection to the network.")7const [newMessage, setNewMessage] = useState("")
Veja aqui o que cada uma das variáveis representa:
walletAddress
- uma string que armazena o endereço da carteira do usuáriostatus
uma string que armazena uma mensagem útil que guia o usuário em como interagir com o dappmessage
- uma string que armazena a mensagem atual no contrato inteligentenewMessage
-uma string que armazena a nova mensagem que será escrita no contrato inteligente
Depois das variáveis de estado, você verá cinco funções não implementadas: useEffect
,addSmartContractListener
, addWalletListener
, connectWalletPressed
, e onUpdatePressed
. Nós explicaremos o que elas fazem abaixo:
1// HelloWorld.js23//called only once4useEffect(async () => {5 //TODO: implement6}, [])78function addSmartContractListener() {9 //TODO: implement10}1112function addWalletListener() {13 //TODO: implement14}1516const connectWalletPressed = async () => {17 //TODO: implement18}1920const onUpdatePressed = async () => {21 //TODO: implement22}Exibir tudo
useEffect
(opens in a new tab)- isto é um hook React hook que é chamado depois que o seu componente é renderizado. Por ele ter um array vazio[]
prop passada por ele (veja linha 4), ele só será chamado na primeira renderização do componente. Aqui nós vamos carregar a mensagem atual armazenada no nosso contrato inteligente, chamar nosso contrato inteligente e listeners da carteira, e atualizar nos UI para refletir se a carteira já está conectada.addSmartContractListener
- esta função configura um listener que irá aguardar o eventoUpdatedMessages
do nosso contrato HelloWorld e atualizar nossa UI quando a mensagem é alterada em nosso contrato inteligente.addWalletListener
- esta função configura um listener que detecta mudanças no estado da carteira MetaMask do usuário, como quando o usuário desconecta sua carteira ou muda endereços.connectWalletPressed
- esta função será chamada para conectar a carteira MetaMask do usuário no nosso dapp.onUpdatePressed
- essa função será chamada quando o usuário quiser atualizar a mensagem armazenada no contrato inteligente.
Perto do final desse arquivo, temos a interface de usuário do nosso componente.
1// HelloWorld.js23//the UI of our component4return (5 <div id="container">6 <img id="logo" src={alchemylogo}></img>7 <button id="walletButton" onClick={connectWalletPressed}>8 {walletAddress.length > 0 ? (9 "Connected: " +10 String(walletAddress).substring(0, 6) +11 "..." +12 String(walletAddress).substring(38)13 ) : (14 <span>Connect Wallet</span>15 )}16 </button>1718 <h2 style={{ paddingTop: "50px" }}>Current Message:</h2>19 <p>{message}</p>2021 <h2 style={{ paddingTop: "18px" }}>New Message:</h2>2223 <div>24 <input25 type="text"26 placeholder="Update the message in your smart contract."27 onChange={(e) => setNewMessage(e.target.value)}28 value={newMessage}29 />30 <p id="status">{status}</p>3132 <button id="publishButton" onClick={onUpdatePressed}>33 Update34 </button>35 </div>36 </div>37)Exibir tudo
Se você procurar com cuidado no código, você notará quando nós usamos nossas várias variáveis de estado na nossa UI:
- Nas linhas 6 a 12, se a carteira do usuário estiver conectada (ou seja.
walletAddress.length > 0
), mostraremos uma versão truncada dawalletAddress
do usuário no botão com a ID "walletButton;", caso contrário, ele simplesmente dirá "Connect Wallet." - Na linha 17, nós mostramos a mensagem atual armazenada no contrato inteligente, que é capturada na string
message
. - Nas linhas 23-26, nós usamos um componente controlado(opens in a new tab) para atualizar nossa variável de estado
newMessage
quando a entrada no campo texto muda.
Em adição às nossas variáveis de estado, você também verá que as funções connectWalletPressed
e onUpdatePressed
são chamadas quando os botões com IDs publishButton
e walletButton
são respectivamente clicados.
Finalmente, vamos endereçar onde esse componente HelloWorld.js
será adicionado.
Se você for ao arquivo App.js
, que é o componente principal do React, que atua como um contêiner para todos os outros componentes, você verá que o nosso componente HelloWorld.js
é injetado na linha 7.
Finalmente, mas não menos importante, vamos checar mais um arquivo fornecido para você, o arquivo interact.js
.
O arquivo interact.js
Como queremos respeitar o paradigma M-V-C(opens in a new tab), queremos um arquivo separado que contém todas as nossas funções para gerenciar a lógica, dados e regras do nosso dapp, para então conseguirmos exportar essas funções para o nosso front-end (nosso componente HelloWorld.js
component).
👆🏽Esta é a exata finalidade do nosso arquivo interact.js
!
Navegue para a pasta util
no seu diretório src
, e você notará que nós incluimos um arquivo chamado interact.js
que irá conter todas as nossas interações com o contrato inteligente, funções de carteira, e variáveis.
1// interact.js23//export const helloWorldContract;45export const loadCurrentMessage = async () => {}67export const connectWallet = async () => {}89const getCurrentWalletConnected = async () => {}1011export const updateMessage = async (message) => {}Exibir tudo
Você pode notar no topo do arquivo que nós transformamos o objeto helloWorldContract
em um comentário. Mais tarde neste tutorial nós vamos descomentar este objeto e instanciar nosso contrato inteligente nesta variável, que irá então exportar no nosso componente HelloWorld.js
.
As quatro funções não implementadas depois do nosso objeto helloWorldContract
fazem o seguinte:
loadCurrentMessage
: esta função manipula a lógica de carregamento da mensagem atual armazenada no contrato inteligente. Ela fará uma chamada read para o contrato inteligente Olá, Mundo usando a API Web3 da Alchemy(opens in a new tab).connectWallet
: essa função conectará a MetaMask do usuário ao nosso dapp.getCurrentWalletConnected
- essa função irá checar se uma conta Ethereum já está conectada no nosso dapp no carregamento da página e atualização da nossa UI devidamente.updateMessage
- esta função atualizará a mensagem armazenada no contrato inteligente. Ela fará uma chamada write para o contrato inteligente Hello World, para que a carteira do usuário MetaMask tenha que assinar uma transação Ethereum para atualizar a mensagem.
Agora que você entende no que estamos trabalhando, vamos entender como ler do nosso contrato inteligente!
Passo 3: Leia do seu Contrato Inteligente
Para ler do seu contrato inteligente, você irá precisar configurar com sucesso:
- Uma conexão API com a cadeia Ethereum
- Uma instância carregada para o seu contrato inteligente
- Uma função para chamar para a sua função de contrato inteligente
- Um ouvinte para observar as atualizações quando os dados de contrato inteligente que você está lendo mudem
Isto pode parecer que são muitos passos, mas não se preocupe! Nós vamos acompanhá-lo como fazer cada um deles passo a passo! :)
Estabeleça uma conexão API com a cadeia Ethereum
Você se lembra como na Parte 2 deste tutorial usamos a nossa chave Alchemy Web3 para ler do nosso contrato inteligente(opens in a new tab)? Você também irá precisar de uma chave Alchemy Web3 em seu dapp para ler da cadeia.
Se você ainda não tem, primeiro instale Alchemy Web3(opens in a new tab) navegando até o diretório raiz do seu starter-files
e executando o seguinte em seu terminal:
1yarn add @alch/alchemy-web3
Alchemy Web3(opens in a new tab) é um invólucro em torno do Web3.js(opens in a new tab), fornecendo métodos aprimorados da API e outros benefícios cruciais para tornar a sua vida de desenvolvedor da Web3 mais fácil. Ele foi projetado para exigir uma configuração mínima, para que você possa começar a usá-la no seu aplicativo imediatamente!
Então, instale o pacote dotenv(opens in a new tab) no seu diretório do projeto, para termos um lugar seguro para armazenar nossa chave API depois de pegarmos ela.
1npm install dotenv --save
Para o nosso dapp, nós usaremos nossa chave API Websockets ao invés de nossa chave API HTTP, já que nos garante configurar um listener que detecta quando a mensagem, armazenada no contrato inteligente, muda.
Uma vez que você tem a chave API, crie um arquivo .env
no seu diretório raiz e adicione sua url Alchemy Websockets a ele. Depois disso, seu arquivo .env
deve se parecer com isso:
1REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/<key>
Agora estamos prontos para configurar nosso ponto de extremidade Web3 da Alchemy no nosso dapp! Vamos voltar para o nosso interact.js
, que é aninhado dentro da nossa pasta util
e adicionar o seguinte código no topo do arquivo:
1// interact.js23require("dotenv").config()4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")6const web3 = createAlchemyWeb3(alchemyKey)78//export const helloWorldContract;
Acima, nós primeiro importamos a chave Alchemy do nosso arquivo .env
e então passamos nosso alchemyKey
para createAlchemyWeb3
estabelecer nosso endpoint Alchemy Web3.
Com este endpoint pronto, é hora de carregar nosso contrato inteligente!
Carregando o seu contrato inteligente Hello World
Para carregar o seu contrato inteligente Hello World, você precisará do seu endereço de contrato e ABI, ambos os quais podem ser encontrados no Etherscan se você completou a Parte 3 deste tutorial.
Como obter a ABI do seu contrato no Etherscan
Se você pulou a Parte 3 deste tutorial, você pode usar o contrato Olá, Mundo 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 irá invocar, assim como garantir que a função irá retornar dados no formato que você está esperando. Uma vez que nós copiamos nosso contrato ABI, vamos salvá-lo como um arquivo JSON chamado contract-abi.json
no seu diretório src
.
O seu contract-abi.json deve ser armazenado na sua pasta src.
Armados com nosso endereço de contrato, ABI, e endpoint Alchemy Web3, nós podemos usar o método do contrato(opens in a new tab) para carregar uma instância do nosso contrato inteligente. Importe a ABI do seu contrato no arquivo interact.js
e adicione o seu endereço de contrato.
1// interact.js23const contractABI = require("../contract-abi.json")4const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"
Nós podemos agora finalmente descomentar nossa variável helloWorldContract
, e carregar o contrato inteligente usando nosso endpoint AlchemyWeb3:
1// interact.js2export const helloWorldContract = new web3.eth.Contract(3 contractABI,4 contractAddress5)
Para recapitular, as primeiras 12 linhas do seu interact.js
deve agora se parecer com isso:
1// interact.js23require("dotenv").config()4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")6const web3 = createAlchemyWeb3(alchemyKey)78const contractABI = require("../contract-abi.json")9const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"1011export const helloWorldContract = new web3.eth.Contract(12 contractABI,13 contractAddress14)Exibir tudo
Agora que nós temos nosso contrato carregado, nós podemos implementar nossa função loadCurrentMessage
!
Implementando loadCurrentMessage
no nosso arquivo interact.js
Esta função é super simples. Nós vamos fazer uma simples chamada async web3 para ler do nosso contrato. Nossa função irá retornar a mensagem armazenada no contrato inteligente:
Atualize o loadCurrentMessage
no seu arquivo interact.js
para o seguinte:
1// interact.js23export const loadCurrentMessage = async () => {4 const message = await helloWorldContract.methods.message().call()5 return message6}
Já que nós queremos exibir este contrato inteligente na nossa UI, vamos atualizar a função useEffect
no nosso componente HelloWorld.js
com o seguinte:
1// HelloWorld.js23//called only once4useEffect(async () => {5 const message = await loadCurrentMessage()6 setMessage(message)7}, [])
Note que nós somente queremos nosso loadCurrentMessage
ser chamado uma vez durante a primeira renderização do componente. Logo implementaremos addSmartContractListener
para atualizar automaticamente a interface do usuário depois que a mensagem no contrato inteligente mudar.
Antes que nós mergulhemos no nosso listener, vamos checar o que nós temos até aqui! Salve seus arquivos HelloWorld.js
e interact.js
, e então vá para http://localhost:3000/(opens in a new tab)
Você notará que a mensagem atual não diz mais "No connection to the network." Ao invés disso, ela reflete a mensagem armazenada no contrato inteligente. Ótimo!
Sua UI poderia agora refletir a mensagem armazenada no contrato inteligente
Agora falando daquele listener...
Implementar addSmartContractListener
Se você voltar para pensar no arquivo HelloWorld.sol
que escrevemos na Parte 1 desta série de tutoriais(opens in a new tab), você se lembrará que há um evento de contrato inteligente chamado UpdatedMessages
que é emitido depois da função update
do nosso contrato inteligente ser invocada (ver linhas 9 e 27):
1// HelloWorld.sol23// Specifies the version of Solidity, using semantic versioning.4// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma5pragma solidity ^0.7.3;67// Defines a contract named `HelloWorld`.8// Um contrato é uma coleção de funções e dados (seu estado). Uma vez implantado, um contrato reside em um endereço específico na blockchain Ethereum. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html9contract HelloWorld {1011 //Emitted when update function is called12 //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen.13 event UpdatedMessages(string oldStr, string newStr);1415 // Declares a state variable `message` of type `string`.16 // Variáveis de estado são variáveis cujos valores são permanentemente armazenados no armazenamento do contrato. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value.17 string public message;1819 // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation.20 // Os construtores são usados para inicializar os dados do contrato. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors21 constructor(string memory initMessage) {2223 // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable).24 message = initMessage;25 }2627 // A public function that accepts a string argument and updates the `message` storage variable.28 function update(string memory newMessage) public {29 string memory oldMsg = message;30 message = newMessage;31 emit UpdatedMessages(oldMsg, newMessage);32 }33}Exibir tudo
Eventos de contratos inteligentes são uma maneira do seu contrato comunicar que alguma coisa aconteceu (ou seja, houve um event) na blockchain no seu aplicativo de front-end, que pode “escutar” eventos específicos e tomar uma ação quando eles acontecem.
A função addSmartContractListener
escutará especificamente o evento UpdatedMessages
do nosso contrato inteligente Olá, Mundo e atualizar nossa interface do usuário para mostrar a nova mensagem.
Modifique addSmartContractListener
da seguinte maneira:
1// HelloWorld.js23function addSmartContractListener() {4 helloWorldContract.events.UpdatedMessages({}, (error, data) => {5 if (error) {6 setStatus("😥 " + error.message)7 } else {8 setMessage(data.returnValues[1])9 setNewMessage("")10 setStatus("🎉 Your message has been updated!")11 }12 })13}Exibir tudo
Vamos quebrar em partes o que acontece quando o listener detecta um evento:
- Se um erro ocorre quando o evento é emitido, ele será refletido na UI via nossa variável de estado
status
. - Caso contrário, nós usaremos o objeto
data
retornado. Odata.returnValues
é uma array indexada no zero onde o primeiro elemento da array armazena a mensagem anterior e o segundo elemento armazena o atualizado. Ao todo, em um evento bem-sucedido, iremos configurar nossa cadeia de caracteresmessage
com a mensagem atualizada, limpar a cadeia de caracteresnewMessage
e atualizar nossa variável de estadostatus
para refletir que uma nova mensagem foi publicada no nosso contrato inteligente.
Finalmente, vamos chamar nosso listener em nossa função useEffect
para que seja inicializada na primeira renderização do componente HelloWorld.js
. Tudo junto, sua função useEffect
deve se parecer com:
1// HelloWorld.js23useEffect(async () => {4 const message = await loadCurrentMessage()5 setMessage(message)6 addSmartContractListener()7}, [])
Agora que nós somos capazes de ler do nosso contrato inteligente, seria ótimo descobrir como escrever nele também! Entretanto, para escrever no nosso dapp, nós precisamos primeiro uma carteira Ethereum conectada nele.
Então, em seguida vamos configurar nossa carteira Ethereum (MetaMask) e então conectá-la ao nosso dapp!
Passo 4: Configurar sua carteira Ethereum
Para escrever qualquer coisa na cadeia Ethereum, usuários devem assinar transações usando as chaves privadas das suas carteiras virtuais. Para este tutorial, usaremos o MetaMask(opens in a new tab), uma carteira virtual no navegador usada para gerenciar o seu endereço de conta do Ethereum, pois ele torna esta assinatura de transação superfácil para o usuário final.
Se você quiser entender mais sobre como as transações no Ethereum funcionam, confira esta página na Fundação Ethereum.
Baixar MetaMask
Você pode baixar e criar uma conta MetaMask gratuitamente neste link(opens in a new tab). Ao criar uma conta, ou mesmo se você já tiver uma conta, certifique-se de mudar para "Goerli Test Network” na parte superior direita (para não lidarmos com dinheiro real).
Etapa: Adicionar Faucet ether
Para assinar a transação no blockchain Ethereum, nós precisamos de alguns Eth falsos. Para obter Eth você pode ir emFaucETH(opens in a new tab) e entrar seu endereço de conta Goerli, clicar em “Request funds”, e então selecionar “Ethereum Testnet Goerli” no menu, e finalmente clicar no botão "Request funds" novamente. Em seguida, você deve ver Eth em sua conta Metamask!
Cheque seu Saldo
Para verificar novamente que tem saldo, vamos fazer uma solicitação através da ferramenta eth_getBalance(opens in a new tab) fornecida pelo compositor da Alchemy(opens in a new tab). Ela mostrará a quantidade de Eth na sua carteira. Depois de inserir o endereço da sua conta da MetaMask e clicar em "Send Request", você verá uma resposta como esta:
1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}
NOTA: Este resultado está em wei, não em ETH. Lembre-se de que "Wei" é a menor unidade de ether. A conversão de wei para eth é: 1 eth = 10¹⁸ wei. Então, se convertemos 0xde0b6b3a7640000 para decimal, temos 1*10¹⁸ wei, que é igual a 1 eth.
Ufa! Nosso dinheiro falso está todo lá! 🤑
Passo 5: Conecte o MetaMask na sua UI
Agora que nossa carteira MetaMask está configurada, vamos conectar nosso dapp a ela!
Função connectWallet
No nosso arquivo interact.js
, vamos implementar a função connectWallet
, a qual podemos então chamar no nosso componente HelloWorld.js
.
Vamos modificar connectWallet
para o seguinte:
1// interact.js23export const connectWallet = async () => {4 if (window.ethereum) {5 try {6 const addressArray = await window.ethereum.request({7 method: "eth_requestAccounts",8 })9 const obj = {10 status: "👆🏽 Write a message in the text-field above.",11 address: addressArray[0],12 }13 return obj14 } catch (err) {15 return {16 address: "",17 status: "😥 " + err.message,18 }19 }20 } else {21 return {22 address: "",23 status: (24 <span>25 <p>26 {" "}27 🦊 <a target="_blank" href={`https://metamask.io/download.html`}>28 You must install MetaMask, a virtual Ethereum wallet, in your29 browser.30 </a>31 </p>32 </span>33 ),34 }35 }36}Exibir tudo
Então, o que esse bloco gigante de código faz exatamente?
Bem, primeiro, ele checar se a window.ethereum
está habilitada no seu navegador.
window.ethereum
é uma API global injetada pela MetaMask e outros provedores de carteira que permitem que sites solicitem contas Ethereum dos usuários. Se aprovado, ele pode ler dados dos blockchains que o usuário está conectado, e sugerir que o usuário assine mensagens e transações. Confira a documentação da MetaMask(opens in a new tab) para obter mais informações!
Se window.ethereum
não está presente, então isso significa que o MetaMask não está instalado. Isso resulta em um objeto JSON sendo retornado, onde o endereço
retornado é uma string vazia, e o status
do objeto JSX repassa que o usuário deve instalar o MetaMask.
Agora se window.ethereum
estiver presente, e é aí que as coisas ficam interessantes.
Usando um laço try/catch, nós vamos tentar conectar ao MetaMask chamandowindow.ethereum.request({ method: "eth_requestAccounts" });
(opens in a new tab). Chamando esta função o MetaMask irá abrir no navegador, onde o usuário será solicitado a conectar sua carteira ao seu dapp.
- Se o usuário escolher conectar,
method: "eth_requestAccounts"
irá retornar uma array que contém todos os endereços de contas de usuário que conectaram ao dapp. No total, nossa funçãoconnectWallet
retornará um objeto JSON que contém o primeiroaddress
desta matriz (ver linha 9) e uma mensagemstatus
que pede que o usuário escreva uma mensagem para o contrato inteligente. - Se o usuário rejeitar a conexão, então o objeto JSON vai conter uma string vazia para o
address
retornado e uma mensagem destatus
que reflete que o usuário rejeitou a conexão.
Agora que nós escrevemos esta função connectWallet
, o próximo passo é chamar ele para o nosso componente HelloWorld.js
.
Adicione a função connectWallet
ao seu componente de interface do usuário HelloWorld.js
Navegue para a função connectWalletPressed
em HelloWorld.js
, e atualize-o para o seguinte:
1// HelloWorld.js23const connectWalletPressed = async () => {4 const walletResponse = await connectWallet()5 setStatus(walletResponse.status)6 setWallet(walletResponse.address)7}
Observe como a maior parte das nossas funcionalidades está abstraída do nosso componente HelloWorld.js
do arquivo interact.js
? É assim que respeitamos o paradigma M-V-C!
Em connectWalletPressed
, simplesmente fazemos uma chamada de espera (await) para a função connectWallet
, importada, e usando sua resposta, nós atualizaremos nossas variáveis status
e walletAddress
através de seus state hooks.
Agora, vamos salvar os dois arquivos HelloWorld.js
e interact.js
e testar nossa UI até agora.
Abra seu navegador na página http://localhost:3000/(opens in a new tab) e clique no botão “Connect Wallet” na parte superior direita da página.
Se você tiver o MetaMask instalado, você será solicitado a conectar sua carteira ao seu dapp. Aceite o convite para se conectar.
Observe que o botão de carteira agora mostra que o seu endereço está conectado! Ótimo!!🔥
Em seguida, tente atualizar a página... isso é estranho. Nosso botão de carteira está nos pedindo para conectar o MetaMask, mesmo que já esteja conectado...
Entretanto, não tenha medo! Nós podemos endereçar (entendeu?) facilmente isso implementando getCurrentWalletConnected
, o qual irá checar se um endereço já está conectado no nosso dapp e atualizar nossa UI de acordo!
A função getCurrentWalletConnected
Atualize a sua função getCurrentWalletConnected
no arquivo interact.js
como mostrado abaixo:
1// interact.js23export const getCurrentWalletConnected = async () => {4 if (window.ethereum) {5 try {6 const addressArray = await window.ethereum.request({7 method: "eth_accounts",8 })9 if (addressArray.length > 0) {10 return {11 address: addressArray[0],12 status: "👆🏽 Write a message in the text-field above.",13 }14 } else {15 return {16 address: "",17 status: "🦊 Connect to MetaMask using the top right button.",18 }19 }20 } catch (err) {21 return {22 address: "",23 status: "😥 " + err.message,24 }25 }26 } else {27 return {28 address: "",29 status: (30 <span>31 <p>32 {" "}33 🦊 <a target="_blank" href={`https://metamask.io/download.html`}>34 You must install MetaMask, a virtual Ethereum wallet, in your35 browser.36 </a>37 </p>38 </span>39 ),40 }41 }42}Exibir tudo
Este código é muito similar à função connectWallet
que nós acabamos de escrever no passo anterior.
A diferença principal é 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 uma matriz que contém os endereços MetaMask atualmente conectados ao nosso dapp.
Para ver esta função em ação, vamos chamar nossa função useEffect
do nosso componente HelloWorld.js
:
1// HelloWorld.js23useEffect(async () => {4 const message = await loadCurrentMessage()5 setMessage(message)6 addSmartContractListener()78 const { address, status } = await getCurrentWalletConnected()9 setWallet(address)10 setStatus(status)11}, [])Exibir tudo
Note que nós usamos a resposta da nossa chamada a getCurrentWalletConnected
para atualizar nossa walletAddress
e nossa variável de estado status
.
Agora que você adicionou este código, tente atualizar a janela do navegador.
Ótimo!!!! O botão deve dizer que você está conectado e mostrar uma visualização do endereço de sua carteira conectada - mesmo depois de atualizar!
Implemente addWalletListener
O passo final na configuração da nossa carteira dapp é implementar o ouvinte de carteira, para que nossa interface atualize quando o estado mudar, como quando o usuário desconecta ou troca de contas.
No seu arquivo HelloWorld.js
, modifique a sua função addWalletListener
para o seguinte:
1// HelloWorld.js23function addWalletListener() {4 if (window.ethereum) {5 window.ethereum.on("accountsChanged", (accounts) => {6 if (accounts.length > 0) {7 setWallet(accounts[0])8 setStatus("👆🏽 Write a message in the text-field above.")9 } else {10 setWallet("")11 setStatus("🦊 Connect to MetaMask using the top right button.")12 }13 })14 } else {15 setStatus(16 <p>17 {" "}18 🦊 <a target="_blank" href={`https://metamask.io/download.html`}>19 You must install MetaMask, a virtual Ethereum wallet, in your browser.20 </a>21 </p>22 )23 }24}Exibir tudo
Eu aposto que você nem mesmo precisou da nossa ajuda para entender o que está acontecendo aqui neste ponto, mas por finalidade de clareza, vamos quebrá-lo em partes:
- Primeiro, nossa função verifica se o
window.ethereum
está habilitado no seu navegador (ex. MetaMask instalado).- Caso contrário, nós simplesmente configuramos a variável de estado
status
para uma JSX string que solicita o usuário instalar a MetaMask. - Se estiver habilitado, configuramos o ouvinte
window.ethereum.on("accountsChanged")
na linha 3 que houve mudança de estado na carteira MetaMask, inclusive quando o usuário conecta uma conta adicional ao dapp, troca de conta ou desconecta uma conta. Se houver pelo menos uma conta conectada, a variável de estadowalletAddress
é atualizada como a primeira conta no arrayaccounts
retornada pelo ouvinte. Caso contrário,walletAddress
é definida como uma string vazia.
- Caso contrário, nós simplesmente configuramos a variável de estado
Por último, mas não menos importante, nós devemos chamá-la em nossa função useEffect
:
1// HelloWorld.js23useEffect(async () => {4 const message = await loadCurrentMessage()5 setMessage(message)6 addSmartContractListener()78 const { address, status } = await getCurrentWalletConnected()9 setWallet(address)10 setStatus(status)1112 addWalletListener()13}, [])Exibir tudo
E é isso! Concluímos com sucesso a programação de toda a nossa carteira! Agora, a nossa última tarefa: atualizar a mensagem armazenada no nosso contrato inteligente!
Passo 6: Implemente a função updateMessage
Tudo bem, nós chegamos ao trecho caseiro! No updateMessage
do seu arquivo interact.js
, façamos o seguinte:
- Certifique-se que a mensagem que nós queremos publicar no nosso contrato inteligente é válida
- Assine nossa transação usando MetaMask
- Chame esta função do nosso componente de frontend
HelloWorld.js
Isso não vai demorar muito; vamos terminar este dapp!
Manipulação de erros de script
Naturalmente, faz sentido ter alguns tipos de gerencialmente de erros de entrada no início da função.
Queremos que nossa função retorne rapidamente. Se não houver uma extensão MetaMask instalada, não haverá carteiras conectadas (ou seja, o address
transmitido é uma cadeira de caracteres vazia) ou a message
será uma cadeira de caracteres vazia. Vamos adicionar o seguinte gerencialmente de erro em updateMessage
:
1// interact.js23export const updateMessage = async (address, message) => {4 if (!window.ethereum || address === null) {5 return {6 status:7 "💡 Connect your MetaMask wallet to update the message on the blockchain.",8 }9 }1011 if (message.trim() === "") {12 return {13 status: "❌ Your message cannot be an empty string.",14 }15 }16}Exibir tudo
Agora que ele tem o devido gerenciamento de erro de entrada, é hora de assinar a transação via MetaMask!
Assinando a nossa transação
Se você já está confortável com as transações tradicionais Web3 do Ethereum, o código que vamos escrever em seguida será bastante familiar. Abaixo, nosso código de manipulação de erro de entrada, adicione o seguinte a updateMessage
:
1// interact.js23//set up transaction parameters4const transactionParameters = {5 to: contractAddress, // Required except during contract publications.6 from: address, // must match user's active address.7 data: helloWorldContract.methods.update(message).encodeABI(),8}910//sign the transaction11try {12 const txHash = await window.ethereum.request({13 method: "eth_sendTransaction",14 params: [transactionParameters],15 })16 return {17 status: (18 <span>19 ✅{" "}20 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>21 View the status of your transaction on Etherscan!22 </a>23 <br />24 ℹ️ Once the transaction is verified by the network, the message will be25 updated automatically.26 </span>27 ),28 }29} catch (error) {30 return {31 status: "😥 " + error.message,32 }33}Exibir tudo
Vamos quebrar em partes o que está acontecendo. Primeiro, configuramos nossos parâmetros de transações, em que:
to
especificar o endereço do destinatário (nosso contrato inteligente)from
especifica o signatário da transação, a variáveladdress
que transmitimos para a nossa funçãodata
contém a chamada para o métodoupdate
do nosso contrato inteligente Olá, Mundo, recebendo nossa variável de cadeia de caracteresmessage
como entrada
Então, nós fazemos uma chamada await, window.ethereum.request
, onde nós 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 os nossos transactionParameters
.
Neste ponto, a MetaMask irá abrir no navegador e pedirá que o usuário assine ou rejeite a transação.
- Se a transação tiver sucesso, a função retornará um objeto JSON no qual a cadeia de caracteres JSX
status
pede ao usuário para verificar o Etherscan para mais informações sobre suas transações. - Se a transação falha, a função irá retornar um objeto JSON onde a string
status
retransmite a mensagem de erro.
Tudo junto, nossa função updateMessage
deve se parecer com isso:
1// interact.js23export const updateMessage = async (address, message) => {4 //input error handling5 if (!window.ethereum || address === null) {6 return {7 status:8 "💡 Connect your MetaMask wallet to update the message on the blockchain.",9 }10 }1112 if (message.trim() === "") {13 return {14 status: "❌ Your message cannot be an empty string.",15 }16 }1718 //set up transaction parameters19 const transactionParameters = {20 to: contractAddress, // Required except during contract publications.21 from: address, // must match user's active address.22 data: helloWorldContract.methods.update(message).encodeABI(),23 }2425 //sign the transaction26 try {27 const txHash = await window.ethereum.request({28 method: "eth_sendTransaction",29 params: [transactionParameters],30 })31 return {32 status: (33 <span>34 ✅{" "}35 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>36 View the status of your transaction on Etherscan!37 </a>38 <br />39 ℹ️ Once the transaction is verified by the network, the message will40 be updated automatically.41 </span>42 ),43 }44 } catch (error) {45 return {46 status: "😥 " + error.message,47 }48 }49}Exibir tudo
Por último, mas não menos importante, nós precisamos conectar nossa função updateMessage
ao componente HelloWorld.js
.
Conecte updateMessage
ao front-end 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 a nossa transação teve sucesso ou falhou:
1// HelloWorld.js23const onUpdatePressed = async () => {4 const { status } = await updateMessage(walletAddress, newMessage)5 setStatus(status)6}
É super limpo e simples. E advinhe... SEU DAPP ESTÁ COMPLETO!!!
Vá em frente e teste com o botão Update!
Faça o seu próprio dapp customizado
Ebaaaaa, você chegou até o fim deste tutorial! Para recapitular, você aprendeu como:
- Conectar a carteira MetaMask no seu projeto dapp
- Ler dados do seu contrato inteligente usando a API Alchemy Web3(opens in a new tab)
- Assinar transações Ethereum usando MetaMask
Agora você está totalmente equipado para aplicar suas habilidades deste tutorial para construir seu próprio projeto dapp customizado! Como sempre, se você tiver questões, não hesite em nos contatar para pedir ajuda noDiscord da Alchemy(opens in a new tab). 🧙♂️
Uma vez que você terminou este tutorial, nos diga como foi sua experiência ou se você tem alguma opinião, nos marcando no Twitter @alchemyplatform(opens in a new tab)!
Última edição: @wackerow(opens in a new tab), 7 de maio de 2024