Criando nosso primeiro Token ERC20

porMatheusem04/06/2022

Nesse artigo iremos aprender a criar nosso primeiro Token ERC20 e uma aplicação que irá interagir com nosso token.

Token ERC20

Qualquer contrato que siga o padrão ERC20 é considerado um token ERC20.

Os tokens ERC20 fornecem funcionalidades para

  • transferir tokens
  • permitir que outros transfiram tokens em nome do titular do token

Um exemplo de interface para o token ERC20

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
// Link do arquivo padrão da interface IERC20
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}

Exemplo do contrato do token ERC20

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "./IERC20.sol";
// Criação do contrato do token PRATICA
contract ERC20 is IERC20 {
uint public totalSupply;
mapping(address => uint) public balanceOf;
mapping(address => mapping(address => uint)) public allowance;
string public name = "Solidity na Prática";
string public symbol = "PRATICA";
uint8 public decimals = 18;
// Função responsável por transferir os tokens da carteira
// que chamou a função para a carteira recipient
function transfer(address recipient, uint amount) external returns (bool) {
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
}
// Função responsável por aprovar que o contrato interaja com
// o saldo da sua carteira
function approve(address spender, uint amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
// Função responsável por transferir tokens de uma carteira sender
// para a carteira recipient
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool) {
allowance[sender][msg.sender] -= amount;
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
return true;
}
// Função responsável por mintar/criar mais tokens PRATICA
// e enviar para a carteira de quem chamou a função
function mint(uint amount) external {
balanceOf[msg.sender] += amount;
totalSupply += amount;
emit Transfer(address(0), msg.sender, amount);
}
// Função responsável por queimar o token
function burn(uint amount) external {
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Transfer(msg.sender, address(0), amount);
}
}

Criando seu próprio token ERC20

Para facilitar e padronizar a criação de tokens ERC20, você pode utilizar a ferramenta OpenZeppelin, repositório do OpenZeppelin no GitHub com vários exemplos de contratos ERC20 e ERC721. Você pode ler a documentação completa sobre tokens ERC20 aqui.

Através do OpenZeppelin, você cria seu token de forma automatizada passando apenas alguns parâmetros como

  • name - nome do token
  • symbol - símbolo do seu token
  • premint - total de tokens que serão criados inicialmente

Entre outras opções, acesse o site da OpenZeppelin e confira a ferramenta completa.

Abaixo, um simples exemplo de token ERC20

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/ERC20.sol";
// Para nomear seu contrato, altere MyToken para o nome desejado
contract MyToken is ERC20 {
// Criaremos 1000 tokens
uint tokenAmount = 1000;
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
// Mint 1000 tokens para carteira msg.sender
// Semelhante como
// 1 dollar = 100 cents
// 1 token = 1 * (10 ** decimals)
_mint(msg.sender, tokenAmount * 10**uint(decimals()));
// Se você quizesse por exemplo, criar 1 milhão de tokens
// Bastaria trocar o valor do tokenAmount para 1000000
}
}

Contrato para trocar tokens (Swap de Tokens)

Aqui está um exemplo de contrato TokenSwap, onde podemos trocar um token ERC20 por outro, onde poderíamos aplicar por exemplo, em uma dapp de jogo NFT, onde o jogador poderá trocar um token X pelo token Y do jogo, exemplo realizar a troca do token BNB da BSC pelo nosso token PRATICA.

Este contrato trocará tokens chamando

transferFrom(address sender, address recipient, uint256 amount)

que realizará a transferência da quantidade passada pelo parâmetro amount do token PRATICA do endereço sender para o endereço recipient.

Para que a função transferFrom tenha sucesso, o endereço sender deve

  • ter saldo amount maior do que está enviando na transação, contabilizando os valores de taxa de gas
  • autorizar o TokenSwap a interagir e retirar a quantidade de amount do token de sua carteira através da função approve

antes de TokenSwap chamar a função transferFrom

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
// Importa a interface IERC20 através do repositório GitHub do OpenZeppelin
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/IERC20.sol";
/*
Como funciona a troca de tokens
1. Alice tem 100 tokens da AliceCoin, que é um token ERC20.
2. Bob tem 100 tokens da BobCoin, que também é um token ERC20.
3. Alice e Bob querem trocar 10 AliceCoin por 20 BobCoin.
4. Alice ou Bob implantam o TokenSwap
5. Alice aprova o TokenSwap para retirar 10 tokens da AliceCoin
6. Bob aprova o TokenSwap para retirar 20 tokens do BobCoin
7. Alice ou Bob chama TokenSwap.swap()
8. Alice e Bob negociaram tokens com sucesso.
*/
contract TokenSwap {
IERC20 public token1;
address public owner1;
uint public amount1;
IERC20 public token2;
address public owner2;
uint public amount2;
// Construtor do TokenSwap
constructor(
address _token1,
address _owner1,
uint _amount1,
address _token2,
address _owner2,
uint _amount2
) {
token1 = IERC20(_token1);
owner1 = _owner1;
amount1 = _amount1;
token2 = IERC20(_token2);
owner2 = _owner2;
amount2 = _amount2;
}
// Realiza as validações necessárias para que a troca seja realizada
// com sucesso
function swap() public {
require(msg.sender == owner1 || msg.sender == owner2, "Não autorizado");
require(
token1.allowance(owner1, address(this)) >= amount1,
"Token 1 - permissão muito baixa"
);
require(
token2.allowance(owner2, address(this)) >= amount2,
"Token 2 - permissão muito baixa"
);
_safeTransferFrom(token1, owner1, owner2, amount1);
_safeTransferFrom(token2, owner2, owner1, amount2);
}
// Realiza a transferência segura do endereço sender para
// o endereço recipient na quantidade de amount
function _safeTransferFrom(
IERC20 token,
address sender,
address recipient,
uint amount
) private {
bool sent = token.transferFrom(sender, recipient, amount);
require(sent, "Falha na transferência do token");
}
}

Testar no Remix