WEB3DEV

Cover image for Construir um dApp Mini Buymeacoffee Usando Solidity + Contrato Inteligente Ethereum + ReactJs + TailwindCSS
Panegali
Panegali

Posted on

Construir um dApp Mini Buymeacoffee Usando Solidity + Contrato Inteligente Ethereum + ReactJs + TailwindCSS

Í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
Enter fullscreen mode Exit fullscreen mode

Agora, devemos ter um hardhat. Vamos iniciar um projeto de exemplo executando o comando abaixo:

npx hardhat
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Para ter certeza de que tudo está funcionando, execute:

npx hardhat test
Enter fullscreen mode Exit fullscreen mode

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");
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

Vamos executá-lo!

npx hardhat run scripts/run.js
Enter fullscreen mode Exit fullscreen mode

Você deve ver algo semelhante ao que temos abaixo:

1

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();
Enter fullscreen mode Exit fullscreen mode

Agora com o comando abaixo vamos implantar localmente usando a rede localhost para teste:

npx hardhat run scripts/deploy.js --network localhost
Enter fullscreen mode Exit fullscreen mode

Devemos ter algo semelhante ao que temos abaixo:

2

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
Enter fullscreen mode Exit fullscreen mode
// 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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.

3

Depois, precisaremos pegar nossas chaves como mostrado abaixo e armazená-las para uso posterior:

4

fsdf

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],
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Veja como deve ficar nossa saída:

fds

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

É hora de testar nosso aplicativo, devemos ter algo semelhante ao que temos abaixo quando visitamos http://localhost:3000

gf

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:

sa

tr

Em seguida, nossa interface do usuário deve ser semelhante ao que é mostrado abaixo:

hgfd

Agora podemos apoiar, comprando um café no valor de $ 5 e também fornecendo nosso nome e qualquer mensagem preferida ao proprietário.

9

Próximo,

345

34

Concluído...

hgfdn

Outro incentivo para café:

5454

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)