WEB3DEV

Cover image for Crie uma Crowdsale para um token de padrão ERC-20 usando Truffle, e faça o Deploy com Infura
Lorenzo Battistela
Lorenzo Battistela

Posted on

Crie uma Crowdsale para um token de padrão ERC-20 usando Truffle, e faça o Deploy com Infura

Um tutorial web3 com as mãos na massa.

O Metaverso está sendo uma grande buzzword (popularidade, muito falada) nos últimos tempos. Não é coincidência esse pico de popularidade acontecer junto com a mudança de nome do Facebook para Meta, e as intenções públicas da empresa de explorar esse espaço.

Para não ficar atrás, outra gigante da tecnologia, a Microsoft, também deixou público o seu desejo de fazer avanços imediatos no Metaverso. De tudo que vimos, é bem óbvio que estamos em um estágio inicial de uma corrida multibilionária entre as grandes empresas de tecnologia, que querem deixar uma grande pegada na próxima tecnologia a crescer.

Junto com o Metaverso, NFT é outro tópico popular que capturou a curiosidade de muita gente. Muitos casos de celebridades tirando suas fotos pessoais das redes sociais e substituindo-as com fotos da coleção dos Bored Apes tiveram um caminho longo para as pessoas criarem consciência sobre esse fenômeno, que é uma abreviação de Tokens Não Fungíveis.

O interesse já agitado aumenta ainda mais quando descobrem o preço dos Apes. A agitação ao redor das NFTs é uma boa discussão, mas para manter a premissa desse artigo, vou levar você até o processo de criar uma crowd sale para um token ERC-20.

O que é crowd sale?

Uma crowd sale é uma oferta e venda pública de uma criptomoeda ou ativo digital relativamente novos.

Assim, podemos parafrasear crowd sale como as ofertas iniciais para o público com a promessa de fazê-los os pioneiros daquele criptoativo.

Tokens ERC-20 são tokens fungíveis que são construídos utilizando o smart contract ERC-20. Esse contrato é utilizado pela maioria das altcoins que existem. Como mencionei anteriormente, são tokens fungíveis que entram em contraste com as NFTs, renomadas por serem não fungíveis.

A maior diferença desses dois lados é o fato de que usam dois padrões ERC diferentes. Tokens fungíveis são substituíveis e trocáveis, ao contrário dos não fungíveis, que são únicos.

O primeiro passo que tomei para começar esse projeto foi instalar a biblioteca truffle globalmente no meu sistema, usando o comando NPM.

Precisei de uma Truffle Box com um frontend já existente, então decidi usar Truffle React Box. Essa escolha foi influenciada pela minha experiência criando códigos client-side com o framework Javascript.

Existem algumas diferenças entre o ReactJs na Web2 e na Web3, mas a sua estrutura e fundamentos são os mesmos. Um recurso que diferencia a versão Web3 do ReactJs é a possibilidade de chamar smart contracts e suas funções.

npm install truffle -g

npx truffle unbox react
Enter fullscreen mode Exit fullscreen mode

Uma Truffle React Box deve ser configurada automaticamente dessa maneira:

Image description

O arquivo truffle-config.js é um arquivo importante que contém as configurações necessárias que habilitam a compilação e implantação dos smart contracts.

Especificamos a versão solidity exata que será usada na compilação dos nossos smart contracts e também os detalhes da rede Ethereum que estamos tentando nos conectar.

Todos estamos cientes de que fazer a implantação de um smart contract custa dinheiro real, por isso é muito importante que testes sejam feitos nas redes de teste da Ethereum em um nó Ethereum privado.

Assim, podemos ter certeza que fizemos todos os ajustes necessários antes de fazer o deploy para a rede principal, que exige dinheiro real para cobrir as taxas de gás. Abaixo está anexado um exemplo do nosso arquivo truffle-config.js:

const path = require("path"); 

module.exports = { 
  // Veja http://truffleframework.com/docs/advanced/configuration  
  // para customizar as configurações do Truffle 
  contracts_build_directory: path.join(__dirname, "client/src/contracts"), 
  networks: { 
    develop: { 
      port: 7545, 
      host: "127.0.0.1", 
      network_Id: 5777 
    } 
  }, 
  compilers: { 
    solc: { 
      version: "0.6.2" 
    } 
  } 
};
Enter fullscreen mode Exit fullscreen mode

Como mencionei anteriormente, estaremos criando tokens ERC-20 para nosso projeto de crowd sale. ERC é uma abreviação para Ethereum Requesting for Comments.

Criar um token ERC-20 do zero pode ser uma tarefa exaustiva e complicada, e devemos agradecer às pessoas boas da Openzeppelin que providenciam contratos padrão para trabalharmos, que deixam nosso trabalho bem mais fácil.

Esses smart contracts são atualizados regularmente e melhorados para assegurar que suas capacidades estão atualizadas. Tudo que precisamos fazer é importar o contrato ERC que queremos no diretório do nosso projeto e podemos começar a criar nosso token ERC-20.

npm install @openzeppelin/contracts
Enter fullscreen mode Exit fullscreen mode

Versões mais velhas dos smart contracts Openzeppelin tinham o contrato de crowd sale, mas com o tempo, o arquivo acabou defasado. O uso desse contrato nos dá o luxo de focar mais em uma lógica extra por trás da crowd sale sabendo que o contrato padrão já está pronto.

Assim, para tirar vantagem da disponibilidade da existência de smart contracts de Crowd sale, usaremos uma versão mais velha do contrato Openzeppelin.

Para instalar versões específicas de um pacote npm, apenas adicionamos a versão no final do comando de instalação.

npm install @openzeppelin/[email protected]
Enter fullscreen mode Exit fullscreen mode
pragma solidity ^0.6.0; 

import "@openzeppelin/contracts/GSN/Context.sol"; 
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 
import "@openzeppelin/contracts/math/SafeMath.sol"; 
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; 
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; 
 \
/** \
 * @título Crowdsale</code>
 * @dev Crowdsale é um contrato base para gerenciar uma crowdsale de                                 * tokens, permitindo a compra dos tokens com Ether pelos investidores.
* Esse contrato implementa essa funcionalidade em sua forma mais fundamental, e pode ser estendida para prover adicionais:* funcionalidade e/ou comportamento customizado</code>
* As interfaces externas representam interfaces básicas para compra de t * oken* A arquitetura base para crowdsales. A intenção não é fazer modificaçõ * es ou overrides.
* A interface interna conforma a extensível e modificável superfície das * crowdsales. Faça o override de métodos para adicionar funcionalidades * Considere usar `super` onde for apropriado para concatenar comportamen * tos. 
*
contract Crowdsale is Context, ReentrancyGuard { 
    using SafeMath for uint256; 
    using SafeERC20 for IERC20; 

    // O token sendo vendido 
    IERC20 private _token; 

    // Endereço onde os fundos são coletados
    address payable private _wallet; 

    // Quantas unidades de token um comprador obtém por wei.
    // A taxa é a conversão entre wei e a menor e indivisível unidade de token. 
    // Então, se você estiver usando uma taxa de 1 com um token ERC20Detailed com 3 decimais chamado TOK
    //  1 wei te dará uma unidade, ou 0.001 TOK.
   uint256 private _rate; 

    // Quantidade de wei arrecadada 
    uint256 private _weiRaised; 

    /** 
     *  Evento para logar a compra do token
     * @param comprador que pagou pelos tokens
     * @param beneficiário que coletou os tokens 
     * @param valor pago  em wei 
     * @param quantidade de tokens comprados 
     */
    event TokensPurchased(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount); 

    /** 
     * @param unidades de token que um comprador compra por wei
     * @dev A taxa é a conversão entre wei e a menor e indivisível unidade de token. 
    // Então, se você estiver usando uma taxa de 1 com um token ERC20Detailed com 3 decimais chamado TOK 
    //  1 wei te dará uma unidade, ou 0.001 TOK. 
     * @param wallet - endereço para o qual os tokens arrecadados irão 
     * @param token - endereço do token sendo vendido</code>
     */ 
    constructor (uint256 rate, address payable wallet, IERC20 token) public { 
        require(rate > 0, "Crowdsale: rate is 0"); 
        require(wallet != address(0), "Crowdsale: wallet is the zero address"); 
        require(address(token) != address(0), "Crowdsale: token is the zero address"); 

        _rate = rate; 
        _wallet = wallet; 
        _token = token; 
    } 

    /** 
     * @dev fallback function ***NÃO SOBRESCREVA*** 
     * Note que outros contratos transferiram fundos com uma base de gas 
     * estipulada de 2300, que não é o suficiente para chamar buyTokens.        * Considere chamar buyTokens diretamente na compra dos tokens de um * *contrato. 
     */ 
    receive () external payable { 
        buyTokens(_msgSender()); 
    } 

    /** 
     * @return o token sendo vendido.
    */ 
    function token() public view returns (IERC20) { 
        return _token; 
    } 

    /** 
     * @return o endereço onde os fundos estão sendo coletados. 
     */ 
    function wallet() public view returns (address payable) { 
        return _wallet; 
    } 

    /** 
     * @return o número de unidades vendidas por wei 
     */ 
    function rate() public view returns (uint256) { 
        return _rate; 
    } 

    /** 
     * @retorna quantia de wei arrecadada. 
     */ 
    function weiRaised() public view returns (uint256) { 
        return _weiRaised; 
    } 
    /** 
     * @dev compra de token low level ***NÃO SOBRESCREVA*** 
     * Essa função tem uma guarda de não reentrância, por isso não deve ser chamada por outra função nonReentrant .
     * @param beneficiary Recipiente da compra do token 
     */ 
    function buyTokens(address beneficiary) public nonReentrant payable { 
        uint256 weiAmount = msg.value; 
        _preValidatePurchase(beneficiary, weiAmount); 

        // calcula a quantidade de tokens para ser criada 
        uint256 tokens = _getTokenAmount(weiAmount); 

        // atualiza o estado  
        _weiRaised = _weiRaised.add(weiAmount); 

        _processPurchase(beneficiary, tokens); 
        emit TokensPurchased(_msgSender(), beneficiary, weiAmount, tokens); 

        _updatePurchasingState(beneficiary, weiAmount); 

        _forwardFunds(); 
        _postValidatePurchase(beneficiary, weiAmount); 
    } 

    /** 
     * @dev Validação de uma compra. Use os require statements para reverter o estado quando as condições não batem. 
     * Use `super` nos contratos que herdam de Crowdsale para extender suas validações.
     * Exemplo de CappedCrowdsale.sol's método _preValidatePurchase: 
     *     super._preValidatePurchase(beneficiary, weiAmount); 
     *     require(weiRaised().add(weiAmount) &lt;= cap); 
     * @param beneficiary Endereço performando a compra de token 
     * @param weiAmount Valor em wei envolvido na compra 
     */ 
    function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view virtual{ 
        require(beneficiary != address(0), "Crowdsale: beneficiary is the zero address"); 
        require(weiAmount != 0, "Crowdsale: weiAmount is 0"); 
        this; // silenciar o aviso de mutabilidade de estado sem gerar bytecode - see https://github.com/ethereum/solidity/issues/2691 
    } 

    /** 
     * @dev Validação de uma compra efetuada. Observe o estado e use os revert statements para reverter o estado quando as condições não batem.  
     * @param beneficiary Endereço performando a compra 
     * @param weiAmount Valor em wei envolvido na compra 
     */ 
    function _postValidatePurchase(address beneficiary, uint256 weiAmount) internal view virtual{ 
        // solhint-disable-previous-line no-empty-blocks 
    } 

    /** 
     * @dev Fonte dos tokens. Sobrescreva esse método para modificar a maneira na qual essa crowdsale pega e manda seus tokens. 
     * @param beneficiary Endereço performando a compra 
     * @param tokenAmount Número de tokens a serem emitidos 
     */ 
    function _deliverTokens(address beneficiary, uint256 tokenAmount) internal virtual{ 
        _token.safeTransfer(beneficiary, tokenAmount); 
    } 

    /** 
     * @dev Executado quando uma compra foi validade e está pronta para ser executada. Não necessariamente emite/envia tokens. 
     * @param beneficiary Endereço recebendo os tokens. 
     * @param tokenAmount Número de tokens a serem comprados
     */ 
    function _processPurchase(address beneficiary, uint256 tokenAmount) internal virtual{ 
        _deliverTokens(beneficiary, tokenAmount); 
    } 

    /** 
     * @dev Sobrescreva para extensões que exijam um estado interno para checar a validade (contribuições atuais de usuário, etc)  
     * @param beneficiary Endereço recebendo os tokens 
     * @param weiAmount Valor em wei envolvido na compra 
     */ 
    function _updatePurchasingState(address beneficiary, uint256 weiAmount) internal virtual{ 
        // solhint-disable-previous-line no-empty-blocks 
    } 

    /** 
     * @dev Sobrescreva para extender a maneira em que o ether é convertido em tokens.
     * @param weiAmount Valor em wei para ser convertido em tokens 
     * @return Número de tokens que podem ser comprados com o _weiAmount específico.
     */ 
    function _getTokenAmount(uint256 weiAmount) internal view returns (uint256) { 
        return weiAmount.mul(_rate); 
    } 

    /** 
     * @dev Determina como o ETH é armazenado nas compras.</code>
   */ 
    function _forwardFunds() internal { 
        _wallet.transfer(msg.value); 
    } 
}
Enter fullscreen mode Exit fullscreen mode

O truffle react box vem com arquivos pré existentes no contrato e diretórios de teste.

Esses arquivos não estão relacionados com o nosso projeto, por isso eles serão deletados.

Precisaremos criar nosso contrato de token que vai interagir com o contrato já importado do Openzeppelin para criar nosso token ERC-20.

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract GLDToken is ERC20 {
    constructor(uint256 initialSupply) ERC20("Gold", "GLD") {
        _mint(msg.sender, initialSupply);
    }
}
Enter fullscreen mode Exit fullscreen mode

Acima está um template a partir do Openzeppelin para criar um contrato de token que interage e herda os contratos padrão ERC-20.

Em solidity, podemos acessar funções de contratos externos usando hereditariedade. Fazemos isso usando a palavra chave "is". Assim, podemos modificar o contrato acima para criar nosso token. Podemos adicionar o nome de token de nossa preferência, seu símbolo e o número total de tokens disponíveis.

pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract GFDToken is ERC20 {
    constructor(uint256 initialSupply) public ERC20("Gryffindor", "GFD") {
        _mint(msg.sender, initialSupply);
    }
}
Enter fullscreen mode Exit fullscreen mode

Uma diferença notável entre o meu código de contrato de token e o modelo original do Openzeppelin é a versão do solidity que usei.

Isso é necessário pois ter acesso ao contrato de Crowd sale significa que preciso usar uma versão mais nova do Openzeppelin. É necessário garantir que todos os arquivos estão rodando a mesma versão do Solidity.

Como mencionei anteriormente, especificamos a versão do solidity no nosso arquivo truffle-config.js, portanto, é primordial que cada arquivo de solidity em nosso projeto esteja rodando na mesma versão. Esse é um erro que muitas pessoas encontram quando constroem seus projetos descentralizados.

O arquivo Crowdsale.sol sozinho não tem todo o necessário para habilitar nossa venda de tokens, então, para completar isso é necessária a criação de outro arquivo para definir a lógica e reforçar mais condições além da compra de tokens.

A beleza dos smart contracts é como ele fornece um caminho para desenvolvedores e criadores definirem e aplicarem regras que não podem ser adulteradas. Um ótimo exemplo de uma regra que consegui definir o funcionamento nesse projeto é a demanda de que cada comprador do token ERC-20 precisa passar por um processo de verificação de algum tipo.

Para fazer isso, escrevi um smart contract KYC que assegurou que apenas o endereço que passou pelo processo de verificação pode tomar parte da crowd sale para o meu token.

pragma solidity ^0.6.0;

import "@openzeppelin/contracts/access/Ownable.sol";

contract KycContract is Ownable{
    mapping (address => bool) public approved;

    function setKycCompleted(address _addr) public onlyOwner {
        approved[_addr] = true;
    }

    function removeKycRevoked(address _addr) public onlyOwner {
        approved[_addr] = false;
    }

    function kycStatus(address _addr) public view returns(bool) {
        return approved[_addr];
    }
}
Enter fullscreen mode Exit fullscreen mode

No contrato KycContract, herdamos o contrato Ownable. Esse contrato é providenciado pelo Openzeppelin e contém funções que garantem que só o dono do contrato pode chamar as funções desejadas. Essas funções são conhecidas como modifiers (modificadores) e seu dever é assegurar que algumas condições são cumpridas antes da execução de algumas funções. Quando estivermos prontos com o contrato KycContract, podemos dar outro passo e importar nosso contrato TokenSale para assegurar que as condições do KYC foram cumpridas antes da compra de um token ser executada.

pragma solidity ^0.6.0;

import "./Crowdsale.sol";
import "./KycContract.sol";

contract MyTokenSale is Crowdsale {

    KycContract kyc;

    constructor(
        uint256 rate,    // rate in TKNbits
        address payable wallet,
        IERC20 token,
        KycContract _kyc
    )
        Crowdsale(rate, wallet, token)
        public
    {
        kyc = _kyc;
    }

    function _preValidatePurchase(address beneficiary, uint256 weiAmount) internal view override{
        super._preValidatePurchase(beneficiary, weiAmount);
        require(kyc.kycStatus(msg.sender), "KYC is not completed, purchase is prohibited");
    }
}
Enter fullscreen mode Exit fullscreen mode

Nesse ponto, terminamos os nossos smart contracts, e precisamos passar para a próxima junção que é garantir que nossos arquivos de migração estejam perfeitamente configurados.

Em observação, você achará um sub-diretório Migrations , que possui dois arquivos: 1_initial_migration.js e 2_deploy_contract.js . Nós não precisamos fazer alterações no primeiro, já que vem configurado automaticamente, mas vamos precisar mudar o segundo para acomodar nossos arquivos solidity e certificar a implantação dos contratos.

var GLDToken = artifacts.require("./GLDToken.sol");
var MyTokenSale = artifacts.require("./MyTokenSale.sol");
var KycContract = artifacts.require("./KycContract.sol");

module.exports = async function(deployer) {
  var addr = await web3.eth.getAccounts();
  await deployer.deploy(GLDToken, 1000000);
  await deployer.deploy(KycContract);
  await deployer.deploy(MyTokenSale, 1, addr[0], GLDToken.address, KycContract.address);
  var instance = await GLDToken.deployed();
  await instance.transfer(MyTokenSale.address, 1000000);
};
Enter fullscreen mode Exit fullscreen mode

Depois que estivermos prontos com arquivos de migração, nossa próxima parada é muito importante. O conceito de transações Web3 cobrando dinheiro real por gas não é novidade, por isso é muito importante que os códigos que estamos escrevendo sejam verificados e inspecionados com cuidado antes de colocarmos eles na rede principal.

Para garantir a eficiência e precisão dos nossos smart contracts, escrevemos testes unitários para ter certeza que eles funcionam como deveriam sob certas condições. Testar em solidity também é feito com bibliotecas como Chai e Mocha.

npm install chai chai-as-promised chai-bn
Enter fullscreen mode Exit fullscreen mode

Precisamos escrever testes para nosso smart contract de token ERC-20 e de venda de tokens. Isso é para garantir que ambos estão funcionando da maneira esperada antes de fazermos a implantação na rede Ethereum. Depois, rodamos o truffle test no terminal.

var Token = artifacts.require("../contracts/GLDToken.sol");

var chai = require("./chaisetup");
var BN = web3.utils.BN;

var expect = chai.expect;

contract("Token test", async(accounts) => {
    var [deployerAccount, recipient, anotherAccount] = accounts;

    beforeEach(async() => {
        this.myToken = await Token.new(1000000);
    });

    it("all tokens should be in the deployer account", async() => {
        var instance = await this.myToken;
        var totalSupply = await instance.totalSupply();

        return expect(instance.balanceOf(deployerAccount)).to.eventually.be.a.bignumber.equal(totalSupply);
    });

    it("should be possible to send tokens between accounts", async() => {
        var sendToken = 1;
        var instance = await this.myToken;
        var totalSupply = await instance.totalSupply();
        expect(instance.balanceOf(deployerAccount)).to.eventually.be.a.bignumber.equal(totalSupply);
        expect(instance.transfer(recipient, sendToken)).to.eventually.be.fulfilled;
        expect(instance.balanceOf(deployerAccount)).to.eventually.be.a.bignumber.equal(totalSupply.sub(new BN(sendToken)));
        return expect(instance.balanceOf(recipient)).to.eventually.be.a.bignumber.equal(new BN(sendToken));
    });

    it("should not be possible to more tokens than available", async() => {
        var instance = await this.myToken;
        var totalSupply = await instance.totalSupply();
        var Balance = await instance.balanceOf(deployerAccount);
        expect(instance.transfer(recipient, new BN(Balance + 1))).to.eventually.be.rejected;
        return expect(instance.balanceOf(deployerAccount)).to.eventually.be.a.bignumber.equal(Balance);
    });
})

GLDToken.test.js hosted with ❤ by GitHub
Enter fullscreen mode Exit fullscreen mode
var Token = artifacts.require("../contracts/GLDToken.sol");
var TokenSale = artifacts.require("../contracts/MyTokenSale.sol");

var chai = require("./chaisetup");
var BN = web3.utils.BN;
var expect = chai.expect;

contract("TokenSale Test", async(accounts) => {
    var [deployerAccount, recipient, anotherAccount] = accounts;

    it("should be no tokens in the deployerAccount", async() => {
        var instance = await Token.deployed();

        return expect(instance.balanceOf(deployerAccount)).to.eventually.be.a.bignumber.equal(new BN(0));
    });

    it("all tokens should be in the Tokensale address", async() => {
        var instance = await Token.deployed();
        var Balance = await instance.balanceOf(TokenSale.address);
        var totalSupply = await Token.totalSupply();

        return expect(Balance).to.eventually.be.a.bignumber.equal(totalSupply);
    });

    it("should be able to purchase tokens", async() => {
        var TokenInstance = await Token.deployed();
        var TokenSaleInstance = await TokenSale.deployed();
        var PreviousBalance = await TokenInstance.balanceOf(deployerAccount);
        expect(TokenSaleInstance.sendTransaction({from: deployerAccount, value: web3.utils.toWei("1", "wei")})).to.be.fulfilled;
        return expect(TokenInstance.balanceOf(deployerAccount)).to.eventually.be.a.bignumber.equal(PreviousBalance.sub(new BN(1)));
    })
})

MyTokenSale.test.js hosted with ❤ by GitHub
Enter fullscreen mode Exit fullscreen mode

Nossos testes rodaram com sucesso, e assim é hora de cruzar a linha e fazer os ajustes necessários do código React para nosso frontend.

O Truffle React Box vem junto com códigos React pré-existentes que temos que alterar para podermos nos conectar com nossos smart contracts para ter uma aplicação descentralizada funcional.

Antes de me aprofundar, é importante destacar que depois que os nossos smart contracts forem compilados e migrados com sucesso, eles são armazenados como artefatos no lado do cliente dos nossos projetos.

Por isso, ao invés de importar os contratos diretamente nos nossos arquivos ReactJs, preferimos importar os artefatos. Esses artefatos contém o ABI do contrato, que nos permite acessar as funções do contrato nos nossos arquivos React. ABI é um acrônimo para Application Binary Interface. (Interface Binária da Aplicação).

import React, { Component } from "react";
import GLDToken from "./contracts/GLDToken.json";
import MyTokenSale from "./contracts/MyTokenSale.json";
import KycContract from "./contracts/KycContract.json";
import getWeb3 from "./getWeb3";

import "./App.css";

class App extends Component {
  state = { loaded: false, kycAddress: "0x12434", tokenSaleAddress: null, userTokens: 0};

  componentDidMount = async () => {
    try {
      // Pega o provider da rede e a instância web3.
      this.web3 = await getWeb3();

      // Usa web3 para conseguir as contas dos usuários.
      this.accounts = await this.web3.eth.getAccounts();

      // Pega a instância do contrato
      this.networkId = await this.web3.eth.net.getId();

      this.TokenInstance = new this.web3.eth.Contract(
        GLDToken.abi,
        GLDToken.networks[this.networkId] && GLDToken.networks[this.networkId].address
      );

      this.TokenSaleInstance = new this.web3.eth.Contract(
        MyTokenSale.abi,
        MyTokenSale.networks[this.networkId] && MyTokenSale.networks[this.networkId].address
      );

      this.KycContractInstance = new this.web3.eth.Contract(
        KycContract.abi,
        KycContract.networks[this.networkId] && KycContract.networks[this.networkId].address
      );

      // Configura web3, contas e o contrato para o estado, e  procede com um exemplo de interação com o método dos contratos.
      this.listenToTokenTransfer();
      this.setState({ loaded: true, tokenSaleAddress: MyTokenSale.networks[this.networkId].address}, this.updateUserTokens);
    } catch (error) {
      // Pega quaisquer erro para as operações acima.
      alert(
        `Failed to load web3, accounts, or contract. Check console for details.`,
      );
      console.error(error);
    }
  };

  updateUserTokens = async() => {
    let userTokens = await this.TokenInstance.methods.balanceOf(this.accounts[0]).call();
    this.setState({userTokens: userTokens});
  }

  listenToTokenTransfer = () => {
    this.TokenInstance.events.Transfer({to: this.accounts[0]}).on("data", this.updateUserTokens);
  }

  handleMoreTokensPurchase = async() => {
    await this.TokenSaleInstance.methods.buyTokens(this.accounts[0]).send({from: this.accounts[0], value: this.web3.utils.toWei("1", "wei")})
;  }

  handleInputChange = (event) => {
    const target = event.target; 
    const value = target.type === "checkbox" ? target.checked : target.value
    const name = target.name;
    this.setState({
      [name]: value
    });
  };

  handleKycWhitelisting = async() => {
    await this.KycContractInstance.methods.setKycCompleted(this.state.kycAddress).send({from: this.accounts[0]});
    alert("KYC for "+this.state.kycAddress+" is completed");
  }

  render() {
    if (!this.state.loaded) {
      return <div>Loading Web3, accounts, and contract...</div>;
    }
    return (
      <div className="App">
        <h1>Gryffindor(GFD) Token Sale</h1>
        <p>Get your tokens today</p>
        <h2>KYC Whitelisting</h2>
        <p>
          From the historic tales of Godric, the unmatched prowess of Albus, to the clever charm of Hermione. This house has attained glory unsurpassed and has always provided a light to vanquish the darkness.
          There is no greater than honour than an official symbol of association with this noble house.
        </p>
        <p>
          Address to allow: <input type="text" name="kycAddress" value={this.state.kycAddress} onChange={this.handleInputChange} />
          <button type="button" onClick={this.handleKycWhitelisting}>Add To Whitelist</button>
        </p>
        <h2>Buy Tokens</h2>
        <p>If you want to buy tokens, send wei to this address: {this.state.tokenSaleAddress} </p>
        <p>¥ou currently have {this.state.userTokens} GFD</p>
        <button type="button" onClick={this.handleMoreTokensPurchase}>Buy More GFD Tokens</button>
      </div>
    );
  }
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Estamos prontos para fazer a implantação do nosso app descentralizado para a rede principal Ethereum e todo o ecossistema blockchain, que vem com algumas especificidades.

Entretanto, uma ferramenta do ecossistema que se destaca é uma variedade de aplicativos e software à nossa disposição que deixa cada passo relativamente mais fácil. O assunto implantação é onde o Infura entra.

O que é o Infura? Citando os criadores: “A Infura fornece as ferramentas e a infraestrutura que permitem aos desenvolvedores levar facilmente seu aplicativo blockchain de testes para implantação dimensionada — com acesso simples e confiável ao Ethereum e ao IPFS.”

Para começar com o Infura, é necessário criar uma conta. Depois do registro, somos redirecionados a um dashboard onde podemos criar um novo projeto.

Image description

O Ethereum está selecionado como nosso tipo de produto, pois estamos criando um produto Ethereum. ETH2 é outra opção no dropdown mas ele está no seu estágio inicial.

O ETH2 é uma atualização da versão atual da blockchain Ethereum que visa batalhar o maior problema na plataforma, que são as taxas altas de gas. Ele também vai marcar a transição de Proof Of Work para o mecanismo de consenso Proof of Stake.

Depois de criarmos nosso novo projeto, teremos um ID de Projeto único e uma Chave de Projeto que é exigida para fazer conexões com os endpoints Infura. O Infura nos dá a opção de escolher nosso endpoint, que pode ser na rede principal ou de teste.

As redes de teste disponíveis mais novas são Ropsten, Goerli, Kovan e Rinkeby. Como mencionei várias vezes, fazer a implantação na rede principal custa dinheiro real, por isso, vamos fazer a implantação na rede de testes para garantir que o projeto rode corretamente antes de fazer essa implantação na rede principal.

Para fazer a implantação na rede de testes, vamos precisar dos tokens de rede de testes ao invés de ether, e podemos pegá-los em sites de faucet sem custo:

Ropsten: https://faucet.ropsten.be/

Goerli: https://goerli-faucet.slock.it/

Rinkeby: https://faucet.rinkeby.io/

Com o ID do projeto e chave providenciado pelo Infura, voltamos para nosso arquivo truffle-config.js onde podemos fazer algumas modificações que vão permitir que o projeto se conecte ao endpoint Infura.

const path = require("path");
require('dotenv').config({ path: './.env'});
const HDWalletProvider = require("@truffle/hdwallet-provider");
const AccountIndex = 0;

module.exports = {
  // Veja <http://truffleframework.com/docs/advanced/configuration>
  // para customizar sua configuração Truffle.
  contracts_build_directory: path.join(__dirname, "client/src/contracts"),
  networks: {
    develop: {
      port: 7545,
      host: "127.0.0.1",
      network_id: 5777
    },
    ganache_local: {
      provider: function() {
        return new HDWalletProvider(process.env.MNEMONIC, "http://127.0.0.1:7545", AccountIndex)
      },
      network_id: 5777
    },
    goerli_infura: {
      provider: function() {
        return new HDWalletProvider(process.env.MNEMONIC, "https://goerli.infura.io/v3/fee8917ab09e4e409ada6f602b288672", AccountIndex)
      },
      network_id: 5
    },
    ropsten_infura: {
      provider: function() {
        return new HDWalletProvider(process.env.MNEMONIC, "https://ropsten.infura.io/v3/fee8917ab09e4e409ada6f602b288672", AccountIndex)
      },
      network_id: 3
    }
  },
  compilers: {
    solc: {
      version: "0.6.2"
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

É isso!

Nesse ponto, fizemos todo o necessário para fazer a implantação do nosso projeto na a blockchain Ethereum.

Fazer a implantação de um contrato e interagir com uma blockchain sabendo que é uma tecnologia em desenvolvimento que vai mudar o mundo é um sentimento muito bom.

Nós apenas arranhamos a superfície de Blockchain/Web, e é incrível o que já conseguimos atingir. A maioria dos tokens por aí, tanto fungíveis quanto não fungíveis foram criados usando os mesmos padrões e passos que nós passamos, e quando levamos em consideração o impacto que eles tiveram, podemos entender a oportunidade que temos para impactar o mundo com algumas linhas de código.

Para ver a base de código completa desse projeto, aqui está o link no Github.

Esse artigo foi traduzido por Lorenzo Battistela, e foi escrito originalmente por Samuel Okafor. O artigo pode ser encontrado em inglês aqui.

Top comments (0)