WEB3DEV

Cover image for Construa e implante um aplicativo NFT Whitelist com React e Solidity
Bernardo Perelló
Bernardo Perelló

Posted on

Construa e implante um aplicativo NFT Whitelist com React e Solidity

Esta é uma tradução de Bernardo Perelló, o original em inglês de Alexandr Kumancev pode ser encontrado aqui.

Este é um caso de uso de NFT Whitelist. Saiba como interagir com um contrato inteligente Solidity implantado usando React.js e ethers.js

Image description

Trabalhar nos componentes técnicos dos NFTspode resultar em uma infinidade de recursos que devem ser implementados, e esses recursos são codificados no contrato inteligente e no site de cunhagem/roteiro do projeto.
Essencialmente, um DApp no qual os usuários podem se comunicar com o contrato inteligente por meio do frontend.
Este projeto demonstra um caso de uso básico do whitelistening.

Introdução

Neste post, criaremos um DApp completo que será implantado na Ethereum.
Solidity é o conjunto de tecnologia do nosso contrato inteligente, Alchemy é nosso RPC e Hardhat é nosso ambiente de desenvolvimento local.
Para lidar com toda a nossa funcionalidade de interação de blockchain, usaremos React.js e ethers.js no front-end.

Pre-requesitos

Tudo o que é necessário é um conhecimento básico da linguagem de programação Solidity e uma compreensão intermediária de React.js.

Vamos construir algo

O plano é construir e implantar primeiro o contrato inteligente e, posteriormente, o front-end.
Nas linhas a seguir, vamos configurar nosso ambiente Hardhat e então escrever algum código Solidity.
Por fim, financiaremos nossa carteira com tokens de teste e lançaremos nosso contrato na testnet!
Iremos em frente e receberemos nosso endereço de contrato e abi do contrato. Ele atuará como uma rota de comunicação do contrato com a blockchain.
Vamos então colocar a mão na massa.

Preparando nosso ambiente

Construiremos e implantaremos nosso contrato inteligente localmente usando o ambiente Hardhat.

  • Crie uma pasta para seu contrato inteligente na raiz ou em qualquer lugar que desejar.
  • Para configurar o ambiente, cd na pasta raiz e execute os seguintes comandos.

// Nessa ordem, rode os seguintes comandos
npm init --yes
npm install --save-dev hardhat
npx hardhat
Depois de executar o npx hardhat, você receberá algumas configurações para configurar:

  • Selecione Criar um projeto de amostra básico.
  • Pressione enter para a raiz do Projeto Hardhat já especificada.
  • Pressione enter para a pergunta se você quiser adicionar um .gitignore. Conclua a instalação do Hardhat instalando esses pacotes. Copie e cole isso no seu terminal: npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers É isso! Podemos começar a escrever algum código.

O Contrato inteligente Whitelist

Depois de instalar o Hardhat e as outras dependências necessárias, podemos ver que as pastas contracts, scripts e test incluem o contrato inteligente de amostra Greeting.sol, bem como seus scripts de implantação e teste.
Certifique-se de que todas essas pastas estejam vazias porque criaremos um novo contrato inteligente desde o início!
Crie um novo arquivo na pasta de contratos e nomeie-o como Whitelist.sol.

  • O contrato incluirá um uint public maxWhitelistedAddresses que representa o número máximo de contas que podem estar em nossa lista de permissões. Ninguém pode entrar na Whitelist se esse número for alcançado. Definiremos o valor para essa entidade com uma função construtora quando estivermos implantando.
  • O contrato incluirá um uint public numAddressesWhitelisted que representa o número atual de contas que já temos em nossa lista de permissões. Na implantação, o valor desse valor será zero e será aumentado até que o máximo seja atingido.
  • O contrato incluirá um mapping(address => bool) public whitelistedAddresses. Essa lógica define um endereço específico como true e imediatamente ele se junta à lista de permissões. Ele também será usado para garantir que nenhuma conta possa entrar na lista de permissões duas vezes!
  • Além disso, teremos uma função constructor que será usada para definir o valor do máximo de contas que podem entrar na whitelist, ou seja, maxWhitelistedAddress. O valor será definido na implantação.
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

contract Whitelist {

    // Representa o número total de contas que queremos ter em nossa lista de permissões.
   // O valor disso será definido com o construtor quando implantarmos.

    uint  public maxWhitelistedAddresses;

    // Essa lógica cria um mapeamento de endereço para booleano
   // valor padrão é false. Ele será definido como verdadeiro quando um endereço se juntar.

    mapping(address => bool) public whitelistedAddresses;

    // Esta variável acompanhará o número de endereços na lista de permissões.
     // Aumentará até que o número máximo seja alcançado.

    uint public numAddressesWhitelisted;

    // Recebe uma entrada que definirá o valor de maxWhitelistAddress
    // Proprietário colocará o valor no momento da implantação
    constructor(uint _maxWhitelistedAddresses) {
        maxWhitelistedAddresses =  _maxWhitelistedAddresses;
    }


}
Enter fullscreen mode Exit fullscreen mode

Agora que temos todos os nossos tipos, um mapeamento e um construtor, precisamos completar este contrato adicionando a função mais importante addAddressToWhitelist().
Esta função será chamada quando o usuário quiser entrar na lista de permissões:

function addAddressToWhitelist() public {
// garante que o chamador da função ainda não faça parte da lista de permissões.
require(!whitelistedAddresses[msg.sender], "Sender has already been whitelisted");
// verifique se o número máximo de endereços na lista de permissões não foi atingido, caso contrário, lance um erro.
require(numAddressesWhitelisted < maxWhitelistedAddresses, "You cannot join now. Limit has been reached");
// Define o endereço dos chamadores como verdadeiro.
// Isso o torna um endereço legível na lista de permissões
whitelistedAddresses[msg.sender] = true;
// Isso aumentará o número de endereços na lista de permissões
numAddressesWhitelisted += 1;
}
Enter fullscreen mode Exit fullscreen mode

Juntando todo o nosso contrato

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

contract Whitelist {

    // Representa o número total de contas que queremos ter em nossa lista de permissões.
    // O valor disso será definido com o construtor quando implantarmos.
    uint  public maxWhitelistedAddresses;

    // Essa lógica cria um mapeamento de endereço para boolean
    // valor padrão é false. Ele será definido como verdadeiro quando um endereço se juntar.
    mapping(address => bool) public whitelistedAddresses;

    // Esta variável acompanhará o número de endereços na lista de permissões.
    // Aumentará até que o número máximo seja alcançado.
    uint public numAddressesWhitelisted;

    // Recebe uma entrada que definirá o valor de maxWhitelistAddress
    // Proprietário colocará o valor no momento da implantação

    constructor(uint _maxWhitelistedAddresses) {
        maxWhitelistedAddresses =  _maxWhitelistedAddresses;
    } 

    function addAddressToWhitelist() public {
 // garante que o chamador da função ainda não faça parte da lista branca.

  require(!whitelistedAddresses[msg.sender], "Sender has already been whitelisted");

   // verifica se o número máximo de endereços permitidos não foi alcançado, caso contrário, gera um erro.
   require(numAddressesWhitelisted < maxWhitelistedAddresses, "You cannot join now. Limit has been reached");

   // Define o endereço dos chamadores como true.
   // Isso o torna um endereço legível na lista de permissões
   whitelistedAddresses[msg.sender] = true;

    // This will increase the number of whitelisted addresses
    numAddressesWhitelisted += 1;
  }

 }
Enter fullscreen mode Exit fullscreen mode

Configurando o script de implantação

Na pasta scripts, crie um arquivo deploy.js. É dentro desse arquivo que teremos toda nossa lógica de implantação e construtores de valores definidos.

const { ethers } = require("hardhat");

async function main() {
  /*
 Um ContractFactory em ethers.js é uma abstração usada para implantar novos contratos inteligentes,  então `ourContract` se refere a uma fábrica para instâncias do nosso contrato Whitelist.

  */
  const ourContract = await ethers.getContractFactory("Whitelist");

  // aqui implantamos o contrato
  const deployedContract = await ourContract.deploy(10);
  // 10 é o número máximo de endereços permitidos na lista de permissões, ou seja, nosso     construtor aguarda que termine de implantar

  await deployedContract.deployed();

  // imprime e registra o endereço do contrato implantado no console
  console.log(
    "Contract Address:",
    deployedContract.address
  );
}

// Chama a função main e catch se houver algum erro
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });
Enter fullscreen mode Exit fullscreen mode

Financiando nossa carteira e obtendo nosso RPC

Financiando nossa carteira

Para lançar nosso contrato inteligente, financiaremos nossa carteira Metamask com algumas moedas testnet (falsas) por motivos de desenvolvimento.

  • Visite este link para financiar sua carteira com algum ETH falso.

Image description

  • Antes de fazer a solicitação, inicie o Metamask e mude para a rede de teste Goerli (devido às alterações de protocolo do Ethereum: as redes de teste Rinkeby, Ropsten e Kovan podem não funcionar de forma confiável e serão descontinuadas em breve. Saiba mais).

Image description

Obtendo nosso RPC

Alchemy é um serviço de API blockchain que nos permitirá implantar facilmente nosso contrato inteligente!

Image description

Inscreva-se e, em seguida, vamos criar nosso aplicativo e adquirir nossas chaves de API.

  • Após a inscrição bem-sucedida, crie um aplicativo no painel.

Image description

  • Preencha alguns dados pertinentes para concluir a configuração do seu aplicativo. Selecione Ethereum e a Rede de Teste Goerli.

Image description

  • Veja os detalhes do seu aplicativo e copie seu endpoint HTTP. Em breve, usaremos esse endpoint para a implantação do contrato.

Image description

Vamos finalizar a implantação

  • Crie um arquivo .env na raiz de sua pasta de contrato inteligente. Este arquivo conterá seu endpoint HTTP Alchemy, bem como sua Chave Privada Goerli. Alterne para a rede de teste Rinkeby no Metamask e copie a chave privada.

Image description

  • Em seu arquivo .env:
    ALCHEMY_API_KEY_URL="add-your-alchemy-endpoint-url-here"
    GOERLI_PRIVATE_KEY="add-your-goerli-private-key-here"

  • Para poder importar nossas chaves para nosso arquivo de configuração final, precisamos de um pacote npm chamado dotenv.
    npm install dotenv

Configuração final antes da implantação

Navegue até o arquivo hardhat.config.js. Este arquivo será usado para configurar nossas redes e chaves de API para o processo de implantação.
Copie e cole as seguintes linhas de código no arquivo por conveniência.

require("@nomiclabs/hardhat-waffle");
require("dotenv").config({ path: ".env" });

const API_KEY = process.env.API_KEY;

const PRIVATE_KEY = process.env.PRIVATE_KEY;

module.exports = {
  solidity: "0.8.0",
  networks: {
    goerli: {
      url: API_KEY,
      accounts: [PRIVATE_KEY],
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Compilar o contrato

npx hardhat compile

Implantar o contrato

npx hardhat run scripts/deploy.js --network goerli
Pronto! Nosso contrato foi implantado com sucesso na Ethereum Rinkeby Test Network!
Anote o endereço do contrato que foi inserido no console. Será necessário no frontend para se comunicar com o contrato inteligente.
Uma nova pasta artifacts é criada após uma implantação bem-sucedida. Esta pasta contém um arquivo .json com a ABI do nosso contrato. Copie-o e salve-o, pois vamos precisar dele junto com o endereço do contrato.

O desenvolvimento de front-end da Whitelist

Agora que completamos o aspecto difícil, vamos construir nosso front. Comunicar-se diretamente com um contrato inteligente é difícil para uma pessoa comum.
É nossa responsabilidade como desenvolvedores criar uma interface interativa a partir da qual os usuários podem enviar solicitações ao contrato inteligente.
Na segunda e última parte deste tutorial, vamos criar um aplicativo React, conectá-lo à Metamask e usar ethers.js para chamar funções.
Essencialmente, nosso aplicativo aproveitará os princípios básicos do React, como gerenciar e atualizar estados e chamar transações por meio de funções. Nada muito complicado. Te darei cobertura.

Instalando React.js e ethers.js

  • Visite os documentos oficiais para instalar e configurar o React.js em seu computador.
  • Após instalar o React com sucesso, instale o ethers.js como uma dependência executando este código: npm install ethers
  • Por uma questão de simplicidade e conveniência, vou fazer todo o meu raciocínio na raiz do arquivo src. Você pode organizar a estrutura de pastas como achar melhor.

Image description

Construindo o front-end

No arquivo Whitelist.js:

import { useState } from "react";


export default function Whitelist() {

// isso gerencia o estado do número de pessoas na lista de permissões
// valor inicial é definido como `null`
 const [numofWhitelisted, setNumofWhitelisted] = useState(null);

// isso gerencia o texto mostrado no botão quando conectado ou não.
// o valor inicial é definido em `Connect Wallet`
const [connectButtonText, setConnetButtonText] = useState("Connect Wallet");

// isso gerencia o estado da conta / endereço conectado
// valor inicial é definido como `null`
const [connectedAddress, setConnectedAddress] = useState(null);

  return (
    <div>
      <div>
        <div>
          <h1>Welcome to the Whitelist Tutorial!</h1>
          <div>


// quando este botão é clicado, o Metamask deve aparecer para o usuário se conectar
// após a conexão bem-sucedida, o estado do texto deve mudar.
            <button>
              {connectButtonText}
            </button>

// após a conexão bem-sucedida, o estado do texto deve mudar
// o endereço da conta conectada deve aparecer
            <p> Connected Address : {connectedAddress} </p>
          </div>


          <div>

// quando este botão é clicado, o estado deve ser atualizado e o número da lista de permissões deve mostrar

            <button>
              Get the number of whitelisted addresses
            </button>
            {numofWhitelisted} have already joined the Whitelist
          </div>


          <div>
// quando este botão é clicado, a Metamask deve aparecer para que uma transação seja assinada
// quando assinado, o usuário será adicionado à lista de permissões

            <button>
              {joinWhiteListText}
            </button>
          </div>
        </div>
      </div>

      <footer>Made by Alex Kumancev</footer>
    </div>
  );



}
olidity: "0.8.0",
  networks: {
    goerli: {
      url: API_KEY,
      accounts: [PRIVATE_KEY],
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Agora você tem o aplicativo sem filtros, implementando todas as funções da blockchain e atualizando os estados.

import { useState } from "react";
import { ethers } from "ethers";

// salve sua ABI em um arquivo json e importe aqui
import contract_abi from "./contract_abi.json";

export default function Whitelist() {

// endereço do contrato inteligente
const contractAddress = `0x90b989349A58a20415Cb3ff440b6244cF3737e12`;

// isso gerencia o estado do número de pessoas na lista de permissões
// valor inicial é definido como `null`
const [numofWhitelisted, setNumofWhitelisted] = useState(null);

// isso gerencia o texto mostrado no botão quando conectado ou não.
// o valor inicial é definido em `Connect Wallet`
const [connectButtonText, setConnetButtonText] = useState("Connect Wallet");

// isso gerencia o estado da conta / endereço conectado
// valor inicial é definido como `null`
const [connectedAddress, setConnectedAddress] = useState(null);

 // gestão do estado do provedor, signatário e contrato
  const [provider, setProvider] = useState(null);
  const [signer, setSigner] = useState(null);
  const [contract, setContract] = useState(null);


  return (
    <div>
      <div>
        <div>
          <h1>Welcome to the Whitelist Tutorial!</h1>
          <div>
            <button onClick={connectWalletHandler}>
              {connectButtonText}
            </button>

            <p> Connected Address : {connectedAddress} </p>
          </div>


          <div>
            <button onClick={getNumWhitelistHandler}>
              Get the number of whitelisted addresses
            </button>
            {numofWhitelisted} have already joined the Whitelist
          </div>


          <div>
            <button onClick={joinWhitelistHandler}>
              {joinWhiteListText}
            </button>
          </div>
        </div>
      </div>

      <footer>Made by Alex Kumancev</footer>
    </div>
  );
Enter fullscreen mode Exit fullscreen mode

O que são Provedores e Signatários?

  • Um provedor é uma abstração de uma conexão de rede Ethereum que fornece uma interface clara e consistente para funções regulares de nós Ethereum.
  • Um Signatário em ethers é uma abstração de uma Conta Ethereum que pode ser usada para assinar mensagens e transações e enviar transações assinadas à Rede Ethereum para realizar mudanças de estado.
// a declação if-else verifica se o navegador possui Metamasks instaladas
// se instalado, ele se conectará à primeira conta/endereço

  const connectWalletHandler = () => {
    if (window.ethereum) {
      window.ethereum
        .request({ method: "eth_requestAccounts" })
        .then((result) => {

    // após a conexão, o estado de connectAddress e connectButtonText será atualizado
         setConnectedAddress(result[0]);
          setConnetButtonText("Wallet Connected!");
    // updateEthers iniciará o provedor, o signatário e a instância do contrato
          updateEthers();
        });
    } else {
      setConnectedAddress("Please Install Metamask Extension!");
    }
  };


    const updateEthers = async () => {

    // iniciar um provedor com ethers.js
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    setProvider(provider);

   // iniciar um signatário com ethers.js
    const signer = provider.getSigner();
    setSigner(signer);

   // iniciando uma nova instância de contrato com ethers.js
    const contract = new ethers.Contract(contractAddress, contract_abi, signer);

    setContract(contract);

    // registrará o provedor, o assinante e o contrato no console quando for bem-sucedido.
    console.log({provider, signer , contract})
  };
Enter fullscreen mode Exit fullscreen mode

Obtendo o número de contas na lista de permissões e ingressando na lista de permissões

const getNumWhitelistHandler = async () => {
   // chama o uint público numAddressesWhitelisted de nosso contrato diretamente
    let number = await contract.numAddressesWhitelisted.length;
    setNumofWhitelisted(number);
  };
const joinWhitelistHandler = async () => {
  // chama a função addAddressToWhitelist em nosso contrato inteligente diretamente
    const tx = await contract.addAddressToWhitelist();
    await tx.wait();
setJoinWhiteListText("Succesfully Joined!");
    await getNumWhitelistHandler();
  };
Enter fullscreen mode Exit fullscreen mode

Juntando o front-end

import { useState } from "react";
import { ethers } from "ethers";
import "./styles.css";
import contract_abi from "./contract_abi.json";

export default function Whitelist() {
  const contractAddress = `0x90b989349A58a20415Cb3ff440b6244cF3737e12`;

  const [numofWhitelisted, setNumofWhitelisted] = useState(null);
  const [connectButtonText, setConnetButtonText] = useState("Connect Wallet");

  const [connectedAddress, setConnectedAddress] = useState(null);

  // gestão do estado do provedor, signatário e contrato
  const [provider, setProvider] = useState(null);
  const [signer, setSigner] = useState(null);
  const [contract, setContract] = useState(null);

  const connectWalletHandler = () => {
    if (window.ethereum) {
      window.ethereum
        .request({ method: "eth_requestAccounts" })
        .then((result) => {
          setConnectedAddress(result[0]);
          setConnetButtonText("Wallet Connected!");
          updateEthers();
        });
    } else {
      setConnectedAddress("Please Install Metamask Extension!");
    }
  };

  const updateEthers = async () => {
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    setProvider(provider);
    console.log(provider.getCode(contractAddress));

    const signer = provider.getSigner();
    setSigner(signer);

    const contract = new ethers.Contract(contractAddress, contract_abi, signer);

    setContract(contract);
  };

  const getNumWhitelistHandler = async () => {
    let number = await contract.numAddressesWhitelisted.length;
    setNumofWhitelisted(number);
  };

  const joinWhitelistHandler = async () => {
    const tx = await contract.addAddressToWhitelist();
    await tx.wait();

    setJoinWhiteListText("Succesfully Joined!");
    await getNumWhitelistHandler();
  };

  return (
    <div>
      <div className="main">
        <div>
          <h1 className="title">Welcome to the Whitelist Tutorial!</h1>
          <div>
            <button onClick={connectWalletHandler}>

              {connectButtonText}
            </button>
            <p> Connected Address : {connectedAddress} </p>
          </div>
          <div className="description">
            <button onClick={getNumWhitelistHandler}>

              // Obter o número de endereços permitidos
            </button>
            {numofWhitelisted} have already joined the Whitelist
          </div>
          <div>
            <button onClick={joinWhitelistHandler}>

              {joinWhiteListText}
            </button>
          </div>
        </div>
      </div>

      <footer className="footer">Made by Alex Kumancev</footer>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Antes de conectar à Metamask

Image description

Depois de conectar à Metamask

Image description

Antes de você sair
Com um maior entendimento do React, você notará que existem várias práticas recomendadas, como Custom Hooks, ContextAPI e o hook useReducer que podem ser usados para tornar nosso código mais limpo e eficiente.
Quais são alguns dos recursos que você pode implementar por conta própria?
Criei um repositório para esta lição, com uma pasta separada para o contrato inteligente e o frontend. Você pode olhar para ele.

Top comments (0)