WEB3DEV

Junior Marques
Junior Marques

Posted on • Atualizado em

Como criar um contrato ERC20 com testes

CRIANDO PROJETO COM HARDHAT
1 - Vamos criar um projeto usando o Hardhat, abra o terminal e use o comando mkdir meu-erc20 para criar uma pastar e cd meu-erc20 para entrar nela.

2 Execute o camando npx hardhat para criar o projeto. Selecione Create an advanced sample project that uses TypeScript (agora é esperar a instalação). Com a instalação finalizada, foi criado um projeto com tudo oque precisamos para trabalhar no nosso contrato.

3 - Na pasta contracts ira ficar os nossos contratos Solidity e na pasta test os nossos testes (inclusive ele já cria um contrato simples e os testes).

4 - No arquivo package.json, em "scripts": {}, iremos criar dois comandos, "compile": "hardhat compile" para compilar os contratos e test": "hardhat test" para rodar os nossos testes.

`"scripts": {
     "compile": "hardhat compile",
     "test": "hardhat test"
 }`
Enter fullscreen mode Exit fullscreen mode

5 - Agora com o terminal aberto na pasta do projeto, podemos usar o comando npm run compile para compilar e o npm run test para rodar os testes.

CRIANDO CONTRATO ERC20

1 - Iremos usar a bliblioteca do OpenZeppelin para criar nosso contrato, no terminal use o comando npm install @openzeppelin/contracts para instalar a bliblioteca. Agora está tudo pronto para colocarmos a mão na massa.

2 - Na pasta contracts crie um novo contrato, iremos criar com o nome MeuContrato.sol. Coloque o seguinte código.

// SPDX-License-Identifier: MIT

// Versão do solidity
pragma solidity ^0.8.0;

// Importa o contrato ERC20PresetFixedSupply do openzeppelin
import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol";

contract MeuContrato is ERC20PresetFixedSupply {
    // Cria o construtor e define nome, símbolo, total supply e dono dos tokens
    // 1000000000 * 10**decimals() é o calculo para define a quantidade de 1 bilhão de tokens
    constructor() ERC20PresetFixedSupply("MeuContrato", "MCT", 1000000000 * 10**decimals(), _msgSender()) {}
} 
Enter fullscreen mode Exit fullscreen mode

3 - Na pasta tests crie um arquivo com nome MeuContrato.test.ts. Mas antes disso rode o comando npm run compile para compilar o código que acabamos de fazer. Coloque o seguinte código.

import { expect } from "chai";
import { ethers } from "hardhat";
import {SignerWithAddress} from "@nomiclabs/hardhat-ethers/signers";
import {MeuContrato, MeuContrato__factory} from "../typechain";

describe("MeuContrato", function () {
    let owner: SignerWithAddress;
    let address1: SignerWithAddress;
    let address2: SignerWithAddress;
    let contract: MeuContrato;

    before(async function () {
        [owner, address1, address2] = await ethers.getSigners();
    });

    beforeEach(async function () {
        let contractFactory = <MeuContrato__factory>await ethers.getContractFactory("MeuContrato");
        contract = await contractFactory.deploy();
        contract = await contract.deployed();
    });

    // Faz o teste e verifica se nome o símbolo e o total de tokens é igual ao oque colocamos no código do contrato
    it("Verifica o nome, símbolo e tatal suplay", async function () {
        // verifica o nome do contrato
        expect((await contract.name())).to.equal("MeuContrato");
        // verifica o símbolo do contrato
        expect((await contract.symbol())).to.equal("MCT");
        // verifica a quantidade de tokens
        expect( ethers.utils.formatEther(await contract.balanceOf(owner.address))).to.equal("1000000000.0");
    });
});
Enter fullscreen mode Exit fullscreen mode

4 - Nesse momento já temos o nosso primeiro contrato criado, agora vamos dar uma incrementada nele e adicionar a função para retirar qualquer token ERC20 que tenha sido transferido para endereço do nosso contrato.

// Função para retirar qualquer ERC20 do nosso contrato
// mas apenas o Owner (dono do contrato pode usar essa função)
// por conta do modificador onlyOwner()
function withdrawERC20(
    address tokenAddress,
    address to,
    uint256 amount
) external virtual onlyOwner() {
    // Verifica se é um ERC20
    require(tokenAddress.isContract(), "ERC20 token address must be a contract");

    IERC20 tokenContract = IERC20(tokenAddress);

    // Verifica se a quantidade de tokens é igual ou maior que a quantidade a ser transferida
    require(
        tokenContract.balanceOf(address(this)) >= amount,
        "You are trying to withdraw more funds than available"
    );

    // Verifica se a transferência foi feita com sucesso
    require(tokenContract.transfer(to, amount), "Fail on transfer");
}
Enter fullscreen mode Exit fullscreen mode

5 - Agora vamos fazer nossos testes. Novamente no arquivo MeuContrato.test.ts adicione o seguinte codigo abaixo do teste anterior.

it("Faz transferência de tokens", async function () {
  // transfere tokens para o endereço1
  // essa função transfer é do contrato ERC20PresetFixedSupply que herda do ERC20
  await contract.transfer(address1.address, 2);

  // Conecta o Address1 ao contrato
  contract = contract.connect(address1);

  // transfere tokens para o endereço do contrato
  await contract.transfer(contract.address, 2);

  // Conecta o dono do contrato
  contract = contract.connect(owner);

  // Retira tokens do endereço do contrato
  await contract.withdrawERC20(contract.address, owner.address, 2);

  expect(ethers.utils.formatEther(await contract.balanceOf(contract.address))).to.equal("0.0");
});
Enter fullscreen mode Exit fullscreen mode

6 - Vamos adicionar uma função chamada withdraw que transfere todo Ether que tem no endereço do nosso contrato para o dono do contrato.

// Função que transfere ether do contrato para o Owner, o dono do contrato
// mas apenas o Owner (dono do contrato pode usar essa função)
// por conta do modificador onlyOwner()
function withdraw() onlyOwner public {
    // Pega toda a quantidade ether disponível
    uint256 balance = address(this).balance;
    console.log(balance);
    // Transfere a quantidade recuperada para o owner
    Address.sendValue(payable(msg.sender), balance);
} 
Enter fullscreen mode Exit fullscreen mode

7 - Na última linha do teste anterior vamos adicionar o seguinte.

await contract.withdraw();
Enter fullscreen mode Exit fullscreen mode

8 - Por fim, finalizamos o nosso contrato. Agora é só usar os comandos npm run compile para compilar os contratos e npm run test para rodar os testes. Fique de olho pois esse é só o primeiro capítulo da nossa série.

Latest comments (0)