Pular para o conteúdo principal

Contrato inteligente "Hello World" para iniciantes - Fullstack

solidityhardhatalchemycontratos inteligentesimplementaçãoblockexplorerfront-endtransações
Iniciante
nstrike2
25 de outubro de 2021
46 minutos de leitura minute read

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.

Criar um aplicativo Hello World

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.

criar uma visualização do app hello world

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-world
2cd 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 contract
4entry point: (index.js)
5test command:
6git repository:
7keywords:
8author:
9license: (ISC)
10
11About to write to /Users/.../.../.../hello-world/package.json:
12
13{
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 888
2888 888 888 888 888
3888 888 888 888 888
48888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
5888 888 "88b 888P" d88" 888 888 "88b "88b 888
6888 888 .d888888 888 888 888 888 888 .d888888 888
7888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
8888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
9
10👷 Welcome to Hardhat v2.0.11 👷‍
11
12What do you want to do? …
13Create a sample project
14❯ Create an empty hardhat.config.js
15Quit
Exibir 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 contracts
2mkdir 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.

  1. Navegue para a pasta contracts e crie um novo arquivo chamado HelloWorld.sol
  2. 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#pragma
3pragma solidity >=0.7.3;
4
5// 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.html
7contract HelloWorld {
8
9 //Emitted when update function is called
10 //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);
12
13 // 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;
16
17 // 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#constructors
19 constructor(string memory initMessage) {
20
21 // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable).
22 message = initMessage;
23 }
24
25 // 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.

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').HardhatUserConfig
3 */
4
5require("dotenv").config()
6require("@nomiclabs/hardhat-ethers")
7
8const { API_URL, PRIVATE_KEY } = process.env
9
10module.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")
3
4 // Start deployment, returning a promise that resolves to a contract object
5 const hello_world = await HelloWorld.deploy("Hello World!")
6 console.log("Contract deployed to address:", hello_world.address)
7}
8
9main()
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.js
2
3const API_KEY = process.env.API_KEY
4const PRIVATE_KEY = process.env.PRIVATE_KEY
5const 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:

# .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>"

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.js
2const 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:

  1. Provedor — um nó fornecedor que lhe dá acesso de leitura e escrita ao blockchain
  2. Signatário — representa uma conta Ethereum que pode assinar transações
  3. 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.js
2
3// Provider
4const alchemyProvider = new ethers.providers.AlchemyProvider(
5 (network = "goerli"),
6 API_KEY
7)
8
9// Signer
10const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
11
12// Contract
13const helloWorldContract = new ethers.Contract(
14 CONTRACT_ADDRESS,
15 contract.abi,
16 signer
17)
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.js
2
3// ...
4
5async 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.js
2
3// ...
4
5async function main() {
6 const message = await helloWorldContract.message()
7 console.log("The message is: " + message)
8
9 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.js
2
3const API_KEY = process.env.API_KEY
4const PRIVATE_KEY = process.env.PRIVATE_KEY
5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS
6
7const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")
8
9// provider - Alchemy
10const alchemyProvider = new ethers.providers.AlchemyProvider(
11 (network = "goerli"),
12 API_KEY
13)
14
15// signer - you
16const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
17
18// contract instance
19const helloWorldContract = new ethers.Contract(
20 CONTRACT_ADDRESS,
21 contract.abi,
22 signer
23)
24
25async function main() {
26 const message = await helloWorldContract.message()
27 console.log("The message is: " + message)
28
29 console.log("Updating the message...")
30 const tx = await helloWorldContract.update("this is the new message")
31 await tx.wait()
32
33 const newMessage = await helloWorldContract.message()
34 console.log("The new message is: " + newMessage)
35}
36
37main()
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.js
2
3require("dotenv").config()
4require("@nomiclabs/hardhat-ethers")
5require("@nomiclabs/hardhat-etherscan")
6
7const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env
8
9module.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 Etherscan
21 // 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 contract
2contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address
3for verification on Etherscan. Waiting for verification result...
4
5
6Successfully 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-files
npm 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.js
2
3import React from "react"
4import { useEffect, useState } from "react"
5import {
6 helloWorldContract,
7 connectWallet,
8 updateMessage,
9 loadCurrentMessage,
10 getCurrentWalletConnected,
11} from "./util/interact.js"
12
13import 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.js
2
3//State variables
4const [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ário
  • status uma string que armazena uma mensagem útil que guia o usuário em 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á 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.js
2
3//called only once
4useEffect(async () => {
5 //TODO: implement
6}, [])
7
8function addSmartContractListener() {
9 //TODO: implement
10}
11
12function addWalletListener() {
13 //TODO: implement
14}
15
16const connectWalletPressed = async () => {
17 //TODO: implement
18}
19
20const onUpdatePressed = async () => {
21 //TODO: implement
22}
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 evento UpdatedMessages 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.js
2
3//the UI of our component
4return (
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>
17
18 <h2 style={{ paddingTop: "50px" }}>Current Message:</h2>
19 <p>{message}</p>
20
21 <h2 style={{ paddingTop: "18px" }}>New Message:</h2>
22
23 <div>
24 <input
25 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>
31
32 <button id="publishButton" onClick={onUpdatePressed}>
33 Update
34 </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 da walletAddress 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.js
2
3//export const helloWorldContract;
4
5export const loadCurrentMessage = async () => {}
6
7export const connectWallet = async () => {}
8
9const getCurrentWalletConnected = async () => {}
10
11export 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.js
2
3require("dotenv").config()
4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
6const web3 = createAlchemyWeb3(alchemyKey)
7
8//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.js
2
3const 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.js
2export const helloWorldContract = new web3.eth.Contract(
3 contractABI,
4 contractAddress
5)

Para recapitular, as primeiras 12 linhas do seu interact.js deve agora se parecer com isso:

1// interact.js
2
3require("dotenv").config()
4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
6const web3 = createAlchemyWeb3(alchemyKey)
7
8const contractABI = require("../contract-abi.json")
9const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"
10
11export const helloWorldContract = new web3.eth.Contract(
12 contractABI,
13 contractAddress
14)
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.js
2
3export const loadCurrentMessage = async () => {
4 const message = await helloWorldContract.methods.message().call()
5 return message
6}

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.js
2
3//called only once
4useEffect(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.sol
2
3// Specifies the version of Solidity, using semantic versioning.
4// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
5pragma solidity ^0.7.3;
6
7// 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.html
9contract HelloWorld {
10
11 //Emitted when update function is called
12 //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);
14
15 // 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;
18
19 // 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#constructors
21 constructor(string memory initMessage) {
22
23 // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable).
24 message = initMessage;
25 }
26
27 // 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.js
2
3function 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. O data.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 caracteres message com a mensagem atualizada, limpar a cadeia de caracteres newMessage e atualizar nossa variável de estado status 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.js
2
3useEffect(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.js
2
3export 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 obj
14 } 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 your
29 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ção connectWallet retornará um objeto JSON que contém o primeiro address desta matriz (ver linha 9) e uma mensagem status 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 de status 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.js
2
3const 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.js
2
3export 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 your
35 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.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7
8 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.js
2
3function 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 estado walletAddress é atualizada como a primeira conta no array accounts retornada pelo ouvinte. Caso contrário, walletAddress é definida como uma string vazia.

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

1// HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7
8 const { address, status } = await getCurrentWalletConnected()
9 setWallet(address)
10 setStatus(status)
11
12 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:

  1. Certifique-se que a mensagem que nós queremos publicar no nosso contrato inteligente é válida
  2. Assine nossa transação usando MetaMask
  3. 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.js
2
3export 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 }
10
11 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.js
2
3//set up transaction parameters
4const 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}
9
10//sign the transaction
11try {
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 be
25 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ável address que transmitimos para a nossa função
  • data contém a chamada para o método update do nosso contrato inteligente Olá, Mundo, recebendo nossa variável de cadeia de caracteres message 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.js
2
3export const updateMessage = async (address, message) => {
4 //input error handling
5 if (!window.ethereum || address === null) {
6 return {
7 status:
8 "💡 Connect your MetaMask wallet to update the message on the blockchain.",
9 }
10 }
11
12 if (message.trim() === "") {
13 return {
14 status: "❌ Your message cannot be an empty string.",
15 }
16 }
17
18 //set up transaction parameters
19 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 }
24
25 //sign the transaction
26 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 will
40 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.js
2
3const 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: @MCreimer(opens in a new tab), 19 de janeiro de 2024

Este tutorial foi útil?