ÍNDICE
- Pré-requisito
- Configuração e instalação do projeto
- Construindo e implantando CoffeeContract na Blockchain
- Configurar cliente Frontend React
- Conclusão
- Referências
Blockchain é tipicamente uma das indústrias de programação mais bem pagas, com desenvolvedores ganhando entre $ 150.000 e $ 175.000 por ano em média como funcionários assalariados. Parece interessante, certo?
Para saber mais sobre um roteiro de desenvolvedor e como começar, confira aqui.
Neste artigo, construiremos um dAPP Mini Buymeacoffee usando Solidity, Contrato Inteligente Ethereum, ReactJs e CSS Tailwind. Será uma plataforma onde qualquer pessoa na internet poderá aprender um pouco sobre nós e nos enviar dinheiro para comprar café + uma mensagem, com as informações sendo mantidas na blockchain por meio de um contrato inteligente Ethereum. (Um contrato inteligente é essencialmente um código que permanece na blockchain e pode ser lido e escrito; veremos isso com mais detalhes posteriormente).
Criaremos o contrato inteligente e o implantaremos. Também construiremos um site que permitirá que as pessoas conectem suas carteiras e se envolvam com nosso contrato inteligente.
Aqui está um link para a demonstração ao vivo e os repositórios do GitHub Frontend e Backend/Contrato Inteligente
Pré-requisito
- Vamos garantir que temos o Node/NPM instalado em nosso PC. Se você não tiver, vá até aqui para obter um guia
Configuração e instalação do projeto
Em seguida, vamos para o terminal. Precisamos entrar no diretório cd,
com o qual desejamos trabalhar e, em seguida, executar os seguintes comandos:
mkdir mini-buymeacoffee-be
cd mini-buymeacoffee-be
npm init -y
npm install --save-dev hardhat
Agora, devemos ter um hardhat. Vamos iniciar um projeto de exemplo executando o comando abaixo:
npx hardhat
Vamos com a opção de criar um projeto de amostra. Aceite todos os pedidos. A instalação de hardhat-waffle
e hardhat-ethers
é necessária para o projeto de amostra. Estas são algumas das outras coisas que usaremos mais tarde.
Caso não tenha feito isso automaticamente, instalaremos estes outros requisitos.
npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers
Para ter certeza de que tudo está funcionando, execute:
npx hardhat test
Veremos um resultado de teste aprovado em nosso console.
Agora é possível excluir sample-test.js
da pasta de teste. Exclua sample-script.js
do diretório de scripts também. Depois disso, vá para contratos e exclua Greeter.sol
.
As pastas em si não devem ser excluídas!
Em seguida, no diretório de contratos, criaremos um arquivo chamado CoffeePortal.sol. Ao usar o Hardhat, o layout do arquivo é realmente crucial, então preste atenção! Vamos começar com a estrutura básica de cada contrato.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract CoffeePortal {
constructor() payable {
console.log("Yee! Contrato Inteligente");
}
}
Para construir e implantar nosso contrato inteligente, navegue até a pasta scripts
, crie um novo arquivo chamado run.js
,e atualize-o com o seguinte trecho de código:
const main = async () => {
// Isto irá realmente compilar nosso contrato e gerar os arquivos necessários que precisamos para trabalhar com nosso contrato sob o diretório de documentos..
const coffeeContractFactory = await hre.ethers.getContractFactory('CoffeePortal');
const coffeeContract = await coffeeContractFactory.deploy();
await coffeeContract.deployed(); // Vamos esperar até que nosso contrato seja oficialmente implantado em nossa blockchain local! Nosso constructor funciona quando nós realmente o implantamos.
console.log("Contrato Coffee implantado para:", coffeeContract.endereço);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
Vamos executá-lo!
npx hardhat run scripts/run.js
Você deve ver algo semelhante ao que temos abaixo:
Temos um contrato inteligente em funcionamento. Vamos implantá-lo em uma rede, disponibilizando-o para todos em todo o mundo.
Na pasta scripts
, crie um arquivo chamado deploy.js
. Aqui está o código para isso. Parece semelhante ao run.js.
const main = async () => {
const [deployer] = await hre.ethers.getSigners();
const accountBalance = await deployer.getBalance();
console.log("Implantação de contratos com conta: ", implantador.endereço);
console.log("Saldo da conta: ", accountBalance.toString());
const Token = await hre.ethers.getContractFactory("CoffeePortal");
const portal = await Token.deploy();
await portal.deployed();
console.log("endereço CoffeePortal: ", endereço.portal);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}
};
runMain();
Agora com o comando abaixo vamos implantar localmente usando a rede localhost para teste:
npx hardhat run scripts/deploy.js --network localhost
Devemos ter algo semelhante ao que temos abaixo:
Construindo e implantando CoffeeContract na Blockchain
Agora tudo está pronto, incluindo o script de teste e o arquivo 'deploy.js'. Com o trecho de código a seguir, atualizaremos os arquivos Smart Contract, run.js e deploy.js:
contracts/CoffeePortal.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract CoffeePortal {
uint256 totalCoffee;
address payable public owner;
/*
* Um pouco de magia, Google que eventos estão em Solidity!
*/
event NovoCafe(
address indexed from,
uint256 timestamp,
string message,
string name
);
constructor() payable {
console.log("Yee! Contrato Inteligente");
// usuário que está chamando este endereço de função
owner = payable(msg.sender);
}
/*
* Eu criei aqui uma estrutura chamada Coffee.
* Uma estrutura é basicamente um datatype personalizado onde podemos personalizar o que queremos manter dentro dela.
*/
struct Coffee {
address giver; // O endereço do usuário que me compra um café.
string message; // A mensagem que o usuário enviou.
string name; // O nome do usuário que me compra um café.
uint256 timestamp; // O carimbo da hora em que o usuário me compra um café.
}
/*
* Eu declaro a variável café que me permite armazenar uma série de estruturas.
* Isto é o que me permite segurar todo o café que alguém me envia!
*/
Coffee[] coffee;
/*
* Eu adicionei uma função getAllCoffee que retornará o array struct, coffee, para nós.
* Isso facilitará a recuperação do café em nosso site!
*/
function getAllCoffee() public view returns (Coffee[] memory) {
return coffee;
}
// Obter todo o café comprado
function getTotalCoffee() public view returns (uint256) {
// Opcional: Adicione esta linha caso queira ver o contrato imprimir o valor!
// Também o imprimiremos em run.js.
console.log("Temos %d total de café recebido ", totalCoffee);
return totalCoffee;
}
/*
* Você vai notar que eu mudei um pouco a função buyCoffee também aqui e
* agora requer uma string chamado _message. Esta é a mensagem de nosso usuário
* nos envia pelo front-end!
*/
function buyCoffee(
string memory _message,
string memory _name,
uint256 _payAmount
) public payable {
uint256 cost = 0.001 ether;
require(_payAmount <= cost, "Insuficiente Ether fornecido");
totalCoffee += 1;
console.log("%s acabou de enviar um café!", msg.sender);
/*
* É aqui que eu realmente armazeno os dados do café na matriz.
*/
coffee.push(Coffee(msg.sender, _message, _name, block.timestamp));
(bool success, ) = owner.call{value: _payAmount}("");
require(success, "Falha ao enviar dinheiro");
emit NewCoffee(msg.sender, block.timestamp, _message, _name);
}
}
Atualizar scripts/run.js
const main = async () => {
const coffeeContractFactory = await hre.ethers.getContractFactory(
"CoffeePortal"
);
const coffeeContract = await coffeeContractFactory.deploy({
value: hre.ethers.utils.parseEther("0.1"),
});
await coffeeContract.deployed();
console.log("Contrato Coffee implantado para:", coffeeContract.address);
/*
* Obter saldo do contrato
*/
let contractBalance = await hre.ethers.provider.getBalance(
coffeeContract.address
);
console.log(
"Saldo do contrato:",
hre.ethers.utils.formatEther(contractBalance)
);
/*
* Vamos tentar comprar um café
*/
const coffeeTxn = await coffeeContract.buyCoffee(
"Isto é café #1",
"idris",
ethers.utils.parseEther("0.001")
);
await coffeeTxn.wait();
/*
* Obtenha o saldo do contrato para ver o que aconteceu!
*/
contractBalance = await hre.ethers.provider.getBalance(
coffeeContract.address
);
console.log(
"Saldo do contrato:",
hre.ethers.utils.formatEther(contractBalance)
);
let allCoffee = await coffeeContract.getAllCoffee();
console.log(allCoffee);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.log(error);
process.exit(1);
}
};
runMain();
Atualizar scripts/deploy.js
const main = async () => {
const [deployer] = await hre.ethers.getSigners();
const accountBalance = await deployer.getBalance();
console.log("Implantação de contratos com conta: ", deployer.address);
console.log("Saldo da conta: ", accountBalance.toString());
const Token = await hre.ethers.getContractFactory("CoffeePortal");
const portal = await Token.deploy({
value: hre.ethers.utils.parseEther("0.1"),
});
await portal.deployed();
console.log("Endereço CoffeePortal: ", endereço.portal);
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}
};
runMain();
Agora é hora de começar a trabalhar, implantando na blockchain real.
Precisaremos nos inscrever em uma conta Alchemy antes de podermos implantar na blockchain.
A Alchemy basicamente nos permite transmitir nossa transação de criação de contrato para que os mineradores possam pegá-la o mais rápido possível. A transação é então transmitida para a blockchain como uma transação legítima depois de minerada. Depois disso, a cópia da blockchain de todos é atualizada.
Criaremos um aplicativo conforme mostrado abaixo após a inscrição. Lembre-se de alterar a seleção de rede para Rinkeby porque é onde estaremos implantando.
Nós a mudamos da rede principal para a Rinkeby por um motivo. Porque é dinheiro real, e não vale a pena desperdiçar! Começaremos com uma "testnet", que é um clone de "mainnet", mas utiliza dinheiro fictício para que possamos experimentar o quanto quisermos. No entanto, é crucial observar que as redes de teste são operadas por mineradores genuínos e são projetadas para simular condições do mundo real.
Depois, precisaremos pegar nossas chaves como mostrado abaixo e armazená-las para uso posterior:
Precisaremos de algum ETH falso em nossa conta de teste e teremos que solicitar alguns da rede. Este ETH falso só pode ser usado nesta rede de testes. Rinkeby pode obter algum ETH falso usando uma faucet.
Nome | Link | Quantia | Tempo |
MyCrypto | app.mycrypto.com/faucet | 0,01 | Nenhum |
Buildspace | buildspace-faucet.vercel.app | 0,025 | 1d |
Ethily | ethily.io/rinkeby-faucet | 0,2 | 1s |
Rinkeby oficial | faucet.rinkeby.io | 3 / 7,5 / 18,75 | 8h / 1d / 3d |
Tabela por Buildspace
Nosso arquivo hardhat.config.js precisa ser atualizado. Isso pode ser encontrado no diretório raiz do projeto de contrato inteligente.
require("@nomiclabs/hardhat-waffle");
require("dotenv").config();
// Esta é uma tarefa de exemplo do Hardhat. Para aprender a criar o seu acesse https://hardhat.org/guides/create-task.html
task("contas", "Imprime a lista de contas", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(endereço.conta);
}
});
// Você precisa exportar um objeto para configurar sua configuração
// Acesse https://hardhat.org/config/ para saber mais
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.4",
networks: {
rinkeby: {
url: process.env.STAGING_ALCHEMY_KEY,
accounts: [process.env.PRIVATE_KEY],
},
},
};
Se observarmos o trecho de código acima, podemos ver que algumas chaves foram lidas do arquivo .env
, assim como a importação no topo de require("dotenv").config()
, o que implica que precisaremos instalar o pacote dotenv
e também criar um arquivo .env
usando o comando abaixo:
npm install -D dotenv
touch .env
Dentro do arquivo .env
, adicione as seguintes chaves:
STAGING_ALCHEMY_KEY= // Adicione aqui a chave que copiamos do painel de controle da Alchemy
PRIVATE_KEY= // Adicione aqui sua chave privada de conta
Para obter a chave privada da nossa conta é fácil, confira este post.
Agora podemos executar o comando para implantar nosso contrato em uma rede blockchain real
npx hardhat run scripts/deploy.js --network rinkeby
Veja como deve ficar nossa saída:
Oba! Acabamos de implantar nosso contrato.
Configurar cliente Frontend React
É hora de colocar nosso site no ar! Nosso contrato inteligente está concluído, mas precisamos descobrir como nosso front-end pode interagir com ele o mais rápido possível!
Para criar um novo projeto, usamos o comando npx create-next-app -e with-tailwindcss
para criar o cimbramento de um novo projeto em um diretório de nossa escolha.
Este comando cria um projeto Next.js com o TailwindCSS configurado. O TailwindCSS é um framework CSS utilitário, repleto de classes para nos ajudar a estilizar nossa página da web.
Para instalar as dependências, usamos os comandos:
cd <project name>
npm install ethers react-toastify
Depois que o aplicativo for criado e as dependências instaladas, veremos uma mensagem com instruções para navegar até nosso site e executá-lo localmente. Fazemos isso com o comando.
npm run dev
O Next.js iniciará um ambiente de desenvolvimento de atualização acessível, por padrão em http://localhost:3000
Precisamos conectar nossa carteira a blockchain para que nosso site se comunique com ele. Nosso site terá permissão para chamar contratos inteligentes em nosso nome depois de conectarmos nossa carteira ao nosso site. Lembre-se, é o mesmo que fazer login em um site.
Todo o nosso trabalho será feito em index.js, que pode ser encontrado em pages
.
import React, { useEffect, useState } from "react";
import { ToastContainer, toast } from "react-toastify";
import { ethers } from "ethers";
import "react-toastify/dist/ReactToastify.css";
import Head from "next/head";
export default function Home() {
/**
* Crie uma variável aqui que contenha o endereço do contrato após a implantação!
*/
const contractAddress = "";
/**
* Crie uma variável aqui que faça referência ao conteúdo abi!
*/
const contractABI = abi.abi;
/*
* Apenas uma variável de estado que usamos para armazenar a carteira pública do nosso usuário.
*/
const [currentAccount, setCurrentAccount] = useState("");
const [message, setMessage] = useState("");
const [name, setName] = useState("");
/*
* Todas as propriedades do estado para armazenar todo o café
*/
const [allCoffee, setAllCoffee] = useState([]);
const checkIfWalletIsConnected = async () => {
try {
const { ethereum } = window;
/*
* Verificar se estamos autorizados a acessar a carteira do usuário
*/
const accounts = await ethereum.request({ method: "eth_accounts" });
if (accounts.length !== 0) {
const account = accounts[0];
setCurrentAccount(account);
toast.success("🦄 A carteira está conectada", {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
} else {
toast.warn("Certifique-se de ter a MetaMask conectada", {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}
} catch (error) {
toast.error(`${error.message}`, {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}
};
/**
* Implemente seu método connectWallet aqui
*/
const connectWallet = async () => {
try {
const { ethereum } = window;
if (!ethereum) {
toast.warn("Certifique-se de ter a MetaMask conectada", {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
return;
}
const accounts = await ethereum.request({
method: "eth_requestAccounts",
});
setCurrentAccount(accounts[0]);
} catch (error) {
console.log(error);
}
};
const buyCoffee = async () => {
try {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const coffeePortalContract = new ethers.Contract(
contractAddress,
contractABI,
signer
);
let count = await coffeePortalContract.getTotalCoffee();
console.log("Contagem total de café recuperado...", count.toNumber());
/*
* Execute o coffee real do seu contrato inteligente
*/
const coffeeTxn = await coffeePortalContract.buyCoffee(
message ? message : "Desfrute do seu café",
name ? name : "Anonimo",
ethers.utils.parseEther("0.001"),
{
gasLimit: 300000,
}
);
console.log("Minerando...", coffeeTxn.hash);
toast.info("Envio de fundo para café...", {
position: "top-left",
autoClose: 18050,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
await coffeeTxn.wait();
console.log("Minerado -- ", coffeeTxn.hash);
count = await coffeePortalContract.getTotalCoffee();
console.log("Contagem total de café recuperado...", count.toNumber());
setMessage("");
setName("");
toast.success("Café comprado!", {
position: "top-left",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
} else {
console.log("Objeto Ethereum não existe!");
}
} catch (error) {
toast.error(`${error.message}`, {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
}
};
/*
* Crie um método que obtenha todo o café de seu contrato
*/
const getAllCoffee = async () => {
try {
const { ethereum } = window;
if (ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const coffeePortalContract = new ethers.Contract(
contractAddress,
contractABI,
signer
);
/*
* Chame o método getAllCoffee do seu contrato inteligente
*/
const coffees = await coffeePortalContract.getAllCoffee();
/*
* Só precisamos de endereço, carimbo de data/hora, nome e mensagem em nossa interface, então vamos
* escolher esses
*/
const coffeeCleaned = coffees.map((coffee) => {
return {
address: coffee.giver,
timestamp: new Date(coffee.timestamp * 1000),
message: coffee.message,
name: coffee.name,
};
});
/*
* Armazenar nossos dados em React State
*/
setAllCoffee(coffeeCleaned);
} else {
console.log("Objeto Ethereum não existe!");
}
} catch (error) {
console.log(error);
}
};
/*
* Isso executa nossa função quando a página é carregada.
*/
useEffect(() => {
let coffeePortalContract;
getAllCoffee();
checkIfWalletIsConnected();
const onNewCoffee = (de, carimbo de data/hora, mensagem, nome) => {
console.log("Novocafé", de, carimbo da hora, mensagem, nome);
setAllCoffee((prevState) => [
...prevState,
{
address: from,
timestamp: new Date(timestamp * 1000),
message: message,
name: name,
},
]);
};
if (window.ethereum) {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
coffeePortalContract = new ethers.Contract(
contractAddress,
contractABI,
signer
);
coffeePortalContract.on("NovoCafe", emNovoCafe);
}
return () => {
if (coffeePortalContract) {
coffeePortalContract.off("NovoCafe", emNovoCafe);
}
};
}, []);
const handleOnMessageChange = (event) => {
const { value } = event.target;
setMessage(value);
};
const handleOnNameChange = (event) => {
const { value } = event.target;
setName(value);
};
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
<Head>
<title>Mini me compre um café</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className="flex flex-col items-center justify-center w-full flex-1 px-20 text-center">
<h1 className="text-6xl font-bold text-blue-600 mb-6">
Me compre um café
</h1>
{/*
* Se houver currentAccount renderize este formulário, senão renderize um botão para conectar a carteira
*/}
{currentAccount ? (
<div className="w-full max-w-xs sticky top-3 z-50 ">
<form className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="name"
>
Name
</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="name"
type="text"
placeholder="Name"
onChange={handleOnNameChange}
required
/>
</div>
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="message"
>
Envie uma mensagem ao criador
</label>
<textarea
className="form-textarea mt-1 block w-full shadow appearance-none py-2 px-3 border rounded text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
rows="3"
placeholder="Message"
id="message"
onChange={handleOnMessageChange}
required
></textarea>
</div>
<div className="flex items-left justify-between">
<button
className="bg-blue-500 hover:bg-blue-700 text-center text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
type="button"
onClick={buyCoffee}
>
Support $5
</button>
</div>
</form>
</div>
) : (
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-3 rounded-full mt-3"
onClick={connectWallet}
>
Conecte sua carteira
</button>
)}
{allCoffee.map((coffee, index) => {
return (
<div className="border-l-2 mt-10" key={index}>
<div className="transform transition cursor-pointer hover:-translate-y-2 ml-10 relative flex items-center px-6 py-4 bg-blue-800 text-white rounded mb-10 flex-col md:flex-row space-y-4 md:space-y-0">
{/* <!-- Não seguir a linha vertical esquerda --> */}
<div className="w-5 h-5 bg-blue-600 absolute -left-10 transform -translate-x-2/4 rounded-full z-10 mt-2 md:mt-0"></div>
{/* <!-- Linha que liga a caixa com a linha vertical --> */}
<div className="w-10 h-1 bg-green-300 absolute -left-10 z-0"></div>
{/* <!-- Conteúdo que aparece na caixa --> */}
<div className="flex-auto">
<h1 className="text-md">Supporter: {coffee.name}</h1>
<h1 className="text-md">Message: {coffee.message}</h1>
<h3>Address: {coffee.address}</h3>
<h1 className="text-md font-bold">
TimeStamp: {coffee.timestamp.toString()}
</h1>
</div>
</div>
</div>
);
})}
</main>
<ToastContainer
position="top-right"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
/>
</div>
);
}
Precisamos importar abi e atualizar nosso contractAddress no exemplo de código acima. Vamos começar criando uma pasta chamada utils
, usando o comando a seguir para criar um arquivo chamado CoffeePortal.json
dentro da pasta utils
.
mkdir utils
touch CoffeePortal.json
Em seguida, precisaremos do endereço do contrato e do conteúdo que será atualizado no arquivo CoffeePortal.json
. Qual é a melhor maneira de obtê-lo?
Vamos voltar ao projeto do contrato inteligente em que trabalhamos antes, navegue para artifacts/contracts/coffeePortal.json
e copie todo o conteúdo dentro dele, bem como nosso endereço de contrato, que foi exibido em nosso terminal quando implantamos nosso contrato na blockchain.
Atualizaremos o arquivo CoffeePortal.json
com o que copiamos e também atualizaremos o index.js conforme mostrado abaixo:
// ...
import Head from "next/head";
// Importar abi
import abi from "../utils/CoffeePortal.json";
export default function Home() {
/**
* Crie aqui uma variável que possua o endereço do contrato depois de você implantar!
*/
const contractAddress = ""; // Adicione aqui o endereço do contrato
// ...
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
[...]
</div>
);
}
É hora de testar nosso aplicativo, devemos ter algo semelhante ao que temos abaixo quando visitamos http://localhost:3000
Em seguida, clicar no botão connect wallet deve nos redirecionar para a interface do usuário da MetaMask, onde concederemos acesso ao site, conforme mostrado abaixo:
Em seguida, nossa interface do usuário deve ser semelhante ao que é mostrado abaixo:
Agora podemos apoiar, comprando um café no valor de $ 5 e também fornecendo nosso nome e qualquer mensagem preferida ao proprietário.
Próximo,
Concluído...
Outro incentivo para café:
Se observarmos, a página será atualizada em tempo real assim que uma transação for concluída; este é o resultado do evento que apresentamos ao nosso contrato inteligente, e o React, é claro, atualiza sem esforço a interface do usuário.
Conclusão
Neste artigo, aprendemos como construir um dAPP mini buymeacoffee usando Solididy, contrato inteligente Ethereum, React e Tailwind CSS.
Referências
Eu adoraria me conectar com você no Twitter | LinkedIn | GitHub | Portfolio
Vejo você no meu próximo artigo do blog. Fique bem!!!
Artigo escrito por Olubisi Idris Ayinde e traduzido por Marcelo Panegali. A versão original pode ser encontrada aqui.
Top comments (0)