Tuesday, September 27, 2022
HomeWordPress DevelopmentMeu primeiro sensible contract: Criando um token ERC-20 pt.2

Meu primeiro sensible contract: Criando um token ERC-20 pt.2


Esse é o sétimo publish da série Meu primeiro sensible contract, que tem a intenção de ensinar ao longo de sete semanas alguns conceitos do solidity até construirmos um token baseado no ERC-20 com alguns testes unitários.

Nesse publish vamos terminar de implementar as funções que faltou no token ERC-20 que criamos.



Ferramentas

Nesse publish vamos utilizar o VS Code para editar o código, o Node.js para instalar e executar o código.

Vamos utilizar o mesmo projeto que criamos no publish Subindo meu primeiro sensible contract para blockchain, caso você não tenha visto clique aqui, caso você não tenha mais o código você pode pegar aqui.



CryptoCoin

Nesse publish vamos implementar as funções e eventos que faltaram no token ERC-20 que criamos no publish Criando um token ERC-20.
Vamos começar adicionando mais alguns métodos na nossa interface, a nossa interface atualmente está assim:

interface IERC20 {
  operate totalSupply() exterior view returns(uint256);
  operate balanceOf(tackle account) exterior view returns(uint256);
  operate switch(tackle to, uint256 amount) exterior returns(bool);

  occasion Approval(tackle proprietor, tackle spender, uint256 worth);
}
Enter fullscreen mode

Exit fullscreen mode

Vamos adicionar as seguintes funções:

  • allowance: Retorna o número de tokens que alguém pode transferir em nome de outro endereço.
  • approve: Outline uma quantidade de tokens que pode ser transferida em nome de outro endereço.
  • transferFrom: Transfere uma quantidade de tokens para outro endereço utilizando o mecanismo de permissão.
  • increaseAllowance: Aumenta a quantidade de tokens que pode ser transferida em nome de outro endereço.
  • decreaseAllowance: Diminui a quantidade de tokens que pode ser transferida em nome de outro endereço.

E vamos adicionar mais um evento:

  • Approval: Emite um evento quando um endereço aprova uma quantidade de tokens para outro endereço.



Organização do código

Não existe uma forma certa de estruturar o código dos nossos contratos, mas para facilitar o entendimento vamos utilizar o seguinte padrão:

Estrutura do código

Enum: Onde criamos nossos Enums, que é um tipo de dado utilizado para armazenar um conjunto de valores constantes que não pode ser modificado.
Properties: Onde criamos nossas variáveis;
Modifiers: Onde criamos nossos modificadores;
Occasions: Onde criamos nossos eventos;
Constructor: Onde criamos nosso construtor;
Public Features: Onde criamos nossas funções públicas;



Enum

Vamos criar um enum de standing onde vamos definir o estado do nosso contrato.

// Enum
enum Standing { PAUSED, ACTIVE, CANCELLED }
Enter fullscreen mode

Exit fullscreen mode



Variáveis

Vamos criar mais algumas variáveis para armazenar o endereço do dono do contrato, o estado do contrato, o valor do token e vamos criar dois mapping para verificar o saldo de um endereço e um mapping que tem outro mapping guardamos que endereço tem permissão de transferir uma quantidade de tokens em nome de outro endereço.

// Properties
string public fixed identify = "CryptoCoin";
string public fixed image = "CRC";
uint8 public fixed decimals = 18;
uint256 non-public totalsupply;

// <-- Adicionado nesse publish
tackle non-public proprietor;
Standing contractState;
uint256 valorToken;

mapping(tackle => uint256) non-public addressToBalance;
mapping(tackle => mapping (tackle => uint256)) allowed;
// -->
Enter fullscreen mode

Exit fullscreen mode



Modificadores

Vamos criar alguns modificadores para conseguirmos diferenciar nosso contrato.
O primeiro modificador que iremos criar é o isOwner, que irá verificar se o endereço que está tentando acessar o contrato é o dono do contrato.

// Modifiers
modifier isOwner() {
  require(msg.sender == proprietor , "Sender will not be proprietor!");
  _;
}
Enter fullscreen mode

Exit fullscreen mode

O segundo modificador será o isActive, que irá verificar se o contrato está ativo.

modifier isActive() {
  require(contractState == Standing.ACTIVE, "Contract will not be Energetic!");
  _;
}
Enter fullscreen mode

Exit fullscreen mode



Eventos

Vamos criar um evento chamado Mint que vamos usar mais para frente para ser emitido quando criarmos mais tokens, esse evento vai receber o endereço do dono do contrato, o saldo do endereço do dono, a quantidade de tokens que queremos criar e o complete de tokens que já existem no contrato.

// Occasions
occasion Mint(tackle proprietor, uint256 BalanceOwner, uint256 quantity, uint256 provide);
Enter fullscreen mode

Exit fullscreen mode

E vamos criar um evento chamado Burn que vamos usar mais para frente também para ser emitido quando ‘queimamos’ tokens, esse evento vai receber o endereço do dono do contrato, a quantidade de tokens que queremos queimar e complete de tokens que já existem no contrato.

occasion Burn(tackle proprietor, uint256 worth, uint256 provide);
Enter fullscreen mode

Exit fullscreen mode



Construtor

No construtor vamos passar o complete de tokens como parâmetro, definir o dono do contrato como quem realiza o deploy, o totalsupply recebendo o complete e atribuir todos os tokens inicialmente para carteira do dono do contrato e o standing inicial do contrato como ativo.

// Constructor

constructor(uint256 complete) {
  proprietor = msg.sender;
  totalsupply = complete;
  addressToBalance[msg.sender] = totalsupply;
  contractState = Standing.ACTIVE;
}
Enter fullscreen mode

Exit fullscreen mode

Agora vamos adicionar mais algumas funções para conseguirmos gerenciar o standing do nosso contrato, criar ou queimar tokens, realizar transferência em nome de um terceiro e matar nosso contrato, os funções do nosso contrato atualmente são essas:

//Public Features
operate totalSupply() public override view returns(uint256) {
  return totalsupply;
}

operate balanceOf(tackle account) public override view returns(uint256) {
  return addressToBalance[account];
}

operate switch(tackle to, uint256 amount) public override returns(bool) {
  require(addressToBalance[msg.sender] >= amount, "Inadequate Steadiness to Switch");

  addressToBalance[msg.sender] = addressToBalance[msg.sender] - amount;
  addressToBalance[to] = addressToBalance[to] + amount;

  emit Switch(msg.sender, to, amount);
  return true;
}
Enter fullscreen mode

Exit fullscreen mode

Vamos implementar as funções que criamos na nossa interface.



Quantidade restante de tokens

Vamos criar uma função pública chamada allowance que retorna o número restante de tokens que um terceiro pode transferir em nome de outro endereço. Ela espera dois parâmetros from endereço da carteira que tem permissão de realizar transferência em nome do spender.

operate allowance(tackle from, tackle spender) public override view returns (uint) {
  return allowed[from][spender];
}
Enter fullscreen mode

Exit fullscreen mode



Permissão de transferência em nome de terceiro

Vamos criar uma função pública chamada approve que permite que um terceiro transfira tokens em nome de outro endereço. Ela espera dois parâmetros: ‘spender’ endereço de quem eu quero dar permissão de realizar a transferência e quantity quantidade de tokens que eu quero dar permissão. Dentro desta função vamos chamar o evento allowed passando a carteira de quem está dando permissão, a carteira de quem está recebendo a permissão e a quantidade de tokens que está sendo permitida.
Após isso, vamos emitir o evento Approval passando a carteira de quem está dando permissão, a carteira de quem está recebendo a permissão e a quantidade de tokens que está sendo permitida.

operate approve(tackle spender, uint256 quantity) public override returns (bool) {
  allowed[msg.sender][spender] = quantity;

  emit Approval(msg.sender, spender, quantity);
  return true;
}
Enter fullscreen mode

Exit fullscreen mode



Transferência em nome de terceiro

Vamos criar uma função pública chamada transferFrom que irá movimentar tokens de uma carteira para outra. Ela espera três parâmetros sender endereço da carteira que tem permissão de realizar transferência em nome do recipient, recipient endereço da carteira que vai receber os tokens e quantity quantidade de tokens que vai ser transferida.
Dentro desta função vamos verificar se o endereço que está tentando realizar a transferência tem permissão para realizar a transferência, se o valor que está sendo transferido é maior que zero e se o endereço que está transferindo tem saldo suficiente para realizar a transferência. Após isso, vamos emitir o evento Switch passando a carteira de quem está realizando a transferência, a carteira de quem está recebendo a transferência e a quantidade de tokens que está sendo transferida.

operate transferFrom(tackle sender, tackle recipient, uint256 quantity)public isActive override returns(bool) {
  require(quantity > 0, "Tranfer worth invalid will not be zero.");
  require(quantity <= balanceOf(sender), "Inadequate Steadiness to Switch");
  require(quantity <= allowed[sender][msg.sender], "No allowed");

  addressToBalance[sender] -= quantity;
  allowed[sender][msg.sender] -= quantity;
  addressToBalance[recipient] += quantity;

  emit Switch(sender, recipient, quantity);
  return true;
}
Enter fullscreen mode

Exit fullscreen mode



Aumentar permissão de transferência em nome de terceiro

Vamos criar uma função pública chamada increaseAllowance que irá aumentar a permissão de transferência em nome de terceiro. Ela espera dois parâmetros: ‘spender’ endereço da carteira que vai receber a permissão e addedValue quantidade de tokens que vai ser adicionada à permissão.
Dentro desta função vamos verificar se o endereço que está tentando receber a permissão é um endereço válido. Após isso, vamos aumentar a permissão de transferência e emitir o evento Approval passando a carteira de quem está chamando a função, a carteira de quem está recebendo a permissão e a quantidade de tokens que foram aprovados.

operate increaseAllowance(tackle spender, uint256 addedValue) public override returns (bool){
  require(spender != tackle(0), "Invalid tackle!");

  allowed[msg.sender][spender] += addedValue;

  emit Approval(msg.sender, spender, allowed[msg.sender][spender]);
  return true;
}
Enter fullscreen mode

Exit fullscreen mode



Diminuir permissão de transferência em nome de terceiro

Vamos criar uma função pública chamada decreaseAllowance que irá diminuir a permissão de transferência em nome de terceiro. Ela espera dois parâmetros: ‘spender’ endereço da carteira que vai receber a permissão e subtractedValue quantidade de tokens que vai ser removida da permissão.
Dentro desta função vamos verificar se o endereço que está tentando receber a permissão é um endereço válido. Após isso, vamos diminuir a permissão de transferência e emitir o evento Approval passando a carteira de quem está chamando a função, a carteira de quem está recebendo a permissão e a quantidade de tokens que foram aprovados.

operate decreaseAllowance(tackle spender, uint256 subtractedValue) public override returns (bool) {
  require(spender != tackle(0), "Invalid tackle!");

  allowed[msg.sender][spender] -= subtractedValue;

  emit Approval(msg.sender, spender, allowed[msg.sender][spender]);
  return true;
}
Enter fullscreen mode

Exit fullscreen mode



Estado do contrato

Vamos criar uma função pública chamada state que irá retornar o estado do contrato. Ela não espera nenhum parâmetro e retorna o valor de contractState.

operate state() public view returns(Standing) {
  return contractState;
}
Enter fullscreen mode

Exit fullscreen mode



Mudar o estado do contrato

Vamos criar uma função chamada setState que irá mudar o estado do contrato. Ela espera um parâmetro standing que é o novo estado do contrato, como contractState é um enum então devemos passar um numérico de 0 a 2. Essa função só pode ser chamada pelo dono do contrato, por isso vamos passar o nosso modificado ìsOwner que verifica se o endereço que está chamando a função é o dono do contrato.
Dentro desta função vamos verificar se o novo estado do contrato é diferente do estado atual do contrato, se o novo estado do contrato é um estado válido. Após isso vamos definir o valor de contractState para o estado passado como parâmetro.

operate setState(uint8 standing) public isOwner {
  require(standing <= 1, "Invalid standing");

  if(standing == 0) {
    require(contractState != Standing.PAUSED, "The standing is already PAUSED");
    contractState = Standing.PAUSED;
  }else if(standing == 1){
    require(contractState != Standing.ACTIVE, "The standing is already ACTIVE");
    contractState = Standing.ACTIVE;
  }
}
Enter fullscreen mode

Exit fullscreen mode



Cunhando mais tokens

Vamos criar uma função pública chamada mint que irá cunhar mais tokens. Ela espera um parâmetro quantity que é a quantidade de tokens que vai ser cunhada. Essa função só pode ser chamada pelo dono do contrato, por isso vamos passar o nosso modificado ìsOwner que verifica se o endereço que está chamando a função é o dono do contrato.
Dentro desta função vamos verificar se a quantidade de tokens que vai ser cunhada é maior que zero, se for maior que zero vamos aumentar o complete de tokens em circulação e aumentar a quantidade de tokens do dono do contrato. Após isso, vamos emitir o evento Mint passando o endereço do dono do contrato, o saldo da carteira do dono do contrato e a quantidade de tokens existentes.

operate mint(uint256 quantity) public isActive isOwner {
  require(quantity > 0, "Invalid mint worth.");

  totalsupply += quantity;
  addressToBalance[owner] += quantity;

  emit Mint(proprietor,addressToBalance[owner], quantity, totalSupply());
}
Enter fullscreen mode

Exit fullscreen mode



Queimando tokens

Vamos criar uma função pública chamada burn que irá queimar tokens. Ela espera um parâmetro quantity que é a quantidade de tokens que vai ser queimada. Essa função só pode ser chamada pelo dono do contrato, por isso vamos passar o nosso modificado ìsOwner que verifica se o endereço que está chamando a função é o dono do contrato.
Dentro desta função vamos verificar se a quantidade de tokens que vai ser queimada é maior que zero, se for maior que zero vamos verificar se a quantidade de tokens que vai ser queimada é menor ou igual a quantidade de tokens em circulação, se for menor ou igual vamos verificar se a quantidade de tokens que vai ser queimada é menor ou igual a quantidade de tokens do dono do contrato. Após isso, vamos diminuir o complete de tokens em circulação e diminuir a quantidade de tokens do dono do contrato. Após isso vamos emitir o evento Burn passando o endereço do dono do contrato, a quantidade de tokens queimados e a quantidade de tokens existentes.

operate burn(uint256 quantity) public isActive isOwner {
  require(quantity > 0, "Invalid burn worth.");
  require(totalSupply() >= quantity, "The quantity exceeds your stability.");
  require(balanceOf(proprietor) >= quantity, "The worth exceeds the proprietor's out there quantity");

  totalsupply -= quantity;
  addressToBalance[owner] -= quantity;

  emit Burn(proprietor, quantity, totalSupply());
}
Enter fullscreen mode

Exit fullscreen mode



Matando o contrato

Vamos criar uma função pública chamada kill que irá matar o contrato. Essa função só pode ser chamada pelo dono do contrato, por isso vamos passar o nosso modificado ìsOwner que verifica se o endereço que está chamando a função é o dono do contrato.
Dentro desta função vamos mudar o estado do contrato para CANCELLED e vamos destruir o contrato e enviar todos os Ether do contrato para o dono do contrato.

operate kill() public isOwner {
  contractState = Standing.CANCELLED;
  selfdestruct(payable(proprietor));
}
Enter fullscreen mode

Exit fullscreen mode



Como ficou nosso código

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.0;

interface IERC20 {

  operate totalSupply() exterior view returns(uint256);
  operate balanceOf(tackle account) exterior view returns(uint256);
  operate switch(tackle recipient, uint256 quantity) exterior returns(bool);
  operate allowance(tackle proprietor, tackle spender) exterior view returns (uint256);
  operate approve(tackle spender, uint256 quantity) exterior returns (bool);
  operate transferFrom(tackle sender, tackle recipient, uint256 quantity) exterior returns (bool);
  operate increaseAllowance(tackle spender, uint256 addedValue) exterior  returns (bool) ;
  operate decreaseAllowance(tackle spender, uint256 subtractedValue) exterior returns (bool) ;

  occasion Switch(tackle from, tackle to, uint256 worth);
  occasion Approval(tackle proprietor, tackle spender, uint256 worth);

}

contract CryptoCoin is IERC20 {
  // Enum
  enum Standing { PAUSED, ACTIVE, CANCELLED }

  //Properties
  tackle non-public proprietor;
  string public fixed identify = "CryptoCoin";
  string public fixed image = "CRC";
  uint8 public fixed decimals = 18;
  uint256 non-public totalsupply;
  Standing contractState;
  uint256 valorToken;
  mapping(tackle => mapping (tackle => uint256)) allowed;
  mapping(tackle => uint256) non-public addressToBalance;

  // Modifiers
  modifier isOwner() {
    require(msg.sender == proprietor , "Sender will not be proprietor!");
    _;
  }

  modifier isActive() {
    require(contractState == Standing.ACTIVE, "Contract will not be Energetic!");
    _;
  }

  // Occasions
  occasion Mint(tackle proprietor, uint256 BalanceOwner, uint256 quantity, uint256 provide);
  occasion Burn(tackle proprietor, uint256 worth, uint256 provide);


  //Constructor
  constructor(uint256 complete) {
    proprietor = msg.sender;
    totalsupply = complete;
    addressToBalance[msg.sender] = totalsupply;
    contractState = Standing.ACTIVE;
  }

  //Public Features
  operate totalSupply() public override view returns(uint256) {
    return totalsupply;
  }

  operate balanceOf(tackle tokenOwner) public override view returns(uint256) {
    return addressToBalance[tokenOwner];
  }

  operate switch(tackle recipient, uint256 quantity) public isActive override returns(bool) {
    require(quantity <= addressToBalance[msg.sender], "Inadequate Steadiness to Switch");

    addressToBalance[msg.sender] -= quantity;
    addressToBalance[recipient] += quantity;

    emit Switch(msg.sender, recipient, quantity);
    return true;
  }

  operate allowance(tackle from, tackle spender) public override view returns (uint) {
    return allowed[from][spender];
  }

  operate approve(tackle spender, uint256 quantity) public override returns (bool) {
    allowed[msg.sender][spender] = quantity;

    emit Approval(msg.sender, spender, quantity);
    return true;
  }

  operate transferFrom(tackle sender, tackle recipient, uint256 quantity)public isActive override returns(bool) {
    require(quantity > 0, "Tranfer worth invalid will not be zero.");
    require(quantity <= balanceOf(sender), "Inadequate Steadiness to Switch");
    require(quantity <= allowed[sender][msg.sender], "No allowed");

    addressToBalance[sender] -= quantity;
    allowed[sender][msg.sender] -= quantity;
    addressToBalance[recipient] += quantity;

    emit Switch(sender, recipient, quantity);
    return true;
  }

  operate increaseAllowance(tackle spender, uint256 addedValue) public override returns (bool){
    require(spender != tackle(0), "Invalid tackle!");

    allowed[msg.sender][spender] += addedValue;

    emit Approval(msg.sender, spender, allowed[msg.sender][spender]);
    return true;
  }

  operate decreaseAllowance(tackle spender, uint256 subtractedValue) public override returns (bool) {
    require(spender != tackle(0), "Invalid tackle!");

    allowed[msg.sender][spender] -= subtractedValue;

    emit Approval(msg.sender, spender, allowed[msg.sender][spender]);
    return true;
  }

  operate state() public view returns(Standing) {
    return contractState;
  }

  operate setState(uint8 standing) public isOwner {
    require(standing <= 1, "Invalid standing");

    if(standing == 0) {
      require(contractState != Standing.PAUSED, "The standing is already PAUSED");
      contractState = Standing.PAUSED;
    }else if(standing == 1){
      require(contractState != Standing.ACTIVE, "The standing is already ACTIVE");
      contractState = Standing.ACTIVE;
    }
  }

  operate mint(uint256 quantity) public isActive isOwner {
    require(quantity > 0, "Invalid mint worth.");

    totalsupply += quantity;
    addressToBalance[owner] += quantity;

    emit Mint(proprietor,addressToBalance[owner], quantity, totalSupply());
  }

  operate burn(uint256 quantity) public isActive isOwner {
    require(quantity > 0, "Invalid burn worth.");
    require(totalSupply() >= quantity, "The quantity exceeds your stability.");
    require(balanceOf(proprietor) >= quantity, "The worth exceeds the proprietor's out there quantity");

    totalsupply -= quantity;
    addressToBalance[owner] -= quantity;

    emit Burn(proprietor, quantity, totalSupply());
  }

  // Kill
  operate kill() public isOwner {
    contractState = Standing.CANCELLED;
    selfdestruct(payable(proprietor));
  }
}

Enter fullscreen mode

Exit fullscreen mode



Deploy

Caso você queira entender com mais detalhes de como realizar o deploy de um sensible contract clique aqui.
Na pasta script vamos criar um arquivo chamado deploy-cryptoCoin.js onde vamos escrever nossos códigos para deployar o contrato.

No arquivo deploy-cryptoCoin.js vamos importar os arquivos do hardhat e criar nossa função assíncrona essential e capturar o retorno dos erros caso tenha algum.

const hre = require("hardhat");

async operate essential() {}

essential().catch((error) => {
  console.error(error);
  course of.exitCode = 1;
});
Enter fullscreen mode

Exit fullscreen mode

Dentro da função essential vamos nos conectar ao contrato CryptoCoin, realizar o deploy deste contrato criando mil tokens e escrever no console o endereço do contrato de token.

const hre = require("hardhat");

async operate essential() {
  const CryptoCoin = await hre.ethers.getContractFactory("CryptoCoin");
  const cryptoCoin = await CryptoCoin.deploy(1000);
  await cryptoCoin.deployed();
  console.log("Endereço do CryptoCoin", cryptoCoin.tackle);
}

essential().catch((error) => {
  console.error(error);
  course of.exitCode = 1;
});
Enter fullscreen mode

Exit fullscreen mode

Como configuramos o hardhat no publish anterior, no terminal vamos executar o seguinte comando:


npx hardhat run scripts/deploy-cryptoCoin.js --network goerli
Enter fullscreen mode

Exit fullscreen mode

Endereço dos nossos contratos

Se tudo estiver certo esse irá retornar o endereço do nosso contrato.

Copiando os endereços e entrando no Goerli Etherscan podemos ver nossos contratos na blockchain da Goerli.
Esses são os contratos que subimos nesse publish.



Conclusão

Esse foi o sétimo publish da série “Meu primeiro sensible contract”.
Se tudo deu certo, agora você tem um sensible contract que é capaz criar e queimar tokens, além de ter um sistema de pausa e cancelamento do contrato.

Se você gostou do conteúdo e te ajudou de alguma forma, deixe um like para ajudar o conteúdo a chegar para mais pessoas.
deixa um like




Hyperlink do repositório

https://github.com/viniblack/meu-primeiro-smart-contract



Vamos trocar uma ideia ?

Fique a vontade para me chamar para trocarmos uma ideia, aqui embaixo está meu contato.

https://www.linkedin.com/in/viniblack/

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments