Introdução
Neste artigo, vamos descrever os casos de uso de um token ERC20 e explicaremos como criá-lo do zero usando o Vyper, implantando na rede de teste da Avalanche, Fuji.
Pré-requisitos
- Programação básica e experiência com console/terminal
- Uma compreensão simples de Solidity ou Vyper
Requisitos
- NodeJS
- Python e Brownie ETH, ou qualquer estrutura de contrato inteligente
- VSCode ou outro editor de código
O que é um ERC20?
Um contrato inteligente ERC20 controla tokens fungíveis, e o próprio contrato nos permite transferir, gravar e fazer muitas outras coisas interessantes com esse token específico.
A interface do contrato é muito simples e não inclui muitas informações sobre o token em si.
#Funções
totalSupply()
balanceOf(account)
transfer(to, amount)
allowance(owner, spender)
approve(spender, amount)
transferFrom(from, to, amount)
Essas seis funções fazem parte da função original definida na EIP20. Podemos estender muitas funcionalidades adicionando mais funções (mais recursos) ao nosso contrato inteligente.
Neste artigo vamos criar um contrato inteligente ERC20, que recebe AVAX e retorna WAVAX, nossa implementação encapsulada da criptomoeda.
Vamos permitir que os usuários criem tokens depositando AVAX, queimem tokens e recebam AVAX em troca.
Por que eles são importantes?
Os tokens são uma parte fundamental do espaço web3. Eles podem ser usados de várias maneiras criativas e exclusivas, como compartilhamentos em uma comunidade, valor transacional, moeda em um protocolo e muito mais. Neste tutorial, vamos criar um token que encapsula uma criptomoeda (AVAX neste caso) e esse token pode ser usado em protocolos defi, protocolos web3 e muito mais.
Configuração
Vamos usar o Brownie neste tutorial. Após instalar o Brownie, crie uma nova pasta chamada avalanche-swap
e dentro dela execute o seguinte comando:
$ brownie init
Além disso, usaremos o nó Hardhat para testar nossos contratos inteligentes, portanto, dentro da pasta do projeto, execute o seguinte comando:
$ npm install --save-dev hardhat
Contrato Inteligente
Vamos começar com o código-modelo. Criamos um arquivo chamado contracts/WAVAX.vy
, definimos a versão do Vyper como >= 0.3.7 e importamos as interfaces ERC20 e ERC20Detailed do Vyper.
# @version >=0.3.7
from vyper.interfaces import ERC20
from vyper.interfaces import ERC20Detailed
implements: ERC20
implements: ERC20Detailed
Eventos
Precisamos definir nossos eventos (events
), que são as mensagens que o contrato inteligente emite quando chamado, e as definimos conforme proposto pela EIP20.
event Transfer:
sender: indexed(address)
receiver: indexed(address)
value: uint256
event Approval:
owner: indexed(address)
spender: indexed(address)
value: uint256
Estado
Nosso contrato inteligente precisa de estado, ou de variáveis que persistirão durante toda a vida do contrato inteligente. Essas variáveis contêm informações que serão úteis para nossos usuários e para os métodos que definiremos a seguir, como balanceOf
, totalSupply
e allowance
# @dev nome do token ("Wrapped AVAX")
name: public(String[32])
# @dev símbolo ou ticker do token ("WAVAX")
symbol: public(String[32])
# @dev a quantidade de decimais que o token contém
decimals: public(uint8)
# @dev o saldo de um determinado endereço
balanceOf: public(HashMap[address, uint256])
# @dev endereços podem permitir que outros endereços gastem seus tokens
allowance: public(HashMap[address, HashMap[address, uint256]])
# @dev o número de tokens em circulação
totalSupply: public(uint256)
Como você pode ver, definimos uma variável para armazenar quantos decimais nosso token vai ter. Isso porque o Solidity, principal linguagem de programação utilizada nas blockchains EVM, não tem suporte para uso de decimais, então definimos os decimais como inteiros. Por exemplo, nós queremos usar 2 casas decimais. Para expressar o número 100,99, colocaríamos assim, 10099, sendo os últimos dois dígitos a parte decimal real.
Construtor
Após concluir o processo anterior, definimos o construtor. Aqui especificamos o nome do token, o símbolo e o número de casas decimais que esse token terá.
@external
def __init__():
self.name = "Wrapped AVAX"
self.symbol = "WAVAX"
self.decimals = 18
Métodos internos
Os métodos internos são bem simples. Precisamos de um método para cunhar nossos tokens AVAX encapsulados quando o usuário enviar a quantia que deseja, e outro para queimá-los quando quiser recuperar seus tokens.
@internal
def _mint(_to: address, _value: uint256):
assert _to != empty(address)
self.totalSupply += _value
self.balanceOf[_to] += _value
log Transfer(empty(address), _to, _value)
@internal
def _burn(_to: address, _value: uint256):
assert _to != empty(address)
self.totalSupply -= _value
self.balanceOf[_to] -= _value
send(_to, _value)
log Transfer(_to, empty(address), _value)
Esses dois são métodos-padrão nas implementações do ERC20, pois precisamos de uma maneira de cunhar os tokens e (se assim o desejarmos) queimá-los também.
Métodos externos
Agora precisamos definir os principais métodos de um token ERC20: transfer
, transferFrom
e approve
. Esses três métodos permitem que os detentores dos tokens os utilizem como desejarem.
O método transfer
é autoexplicativo e permite que o detentor do token transfira tokens para outro usuário ou um contrato inteligente.
@external
def transfer(_to : address, _value : uint256) -> bool:
self.balanceOf[msg.sender] -= _value
self.balanceOf[_to] += _value
log Transfer(msg.sender, _to, _value)
return True
O padrão do token precisa de um método approve
para habilitar o método transferFrom
que definimos a seguir. O método approve
fornece ao detentor do token uma maneira de permitir que outra conta gerencie uma quantidade definida de tokens para eles.
@external
def approve(_spender : address, _value : uint256) -> bool:
self.allowance[msg.sender][_spender] = _value
log Approval(msg.sender, _spender, _value)
return True
E o método transferFrom
dá ao usuário a capacidade de transferir tokens para outra conta em nome do titular do token ou gerenciar uma quantidade definida de tokens para eles.
@external
def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
self.balanceOf[_from] -= _value
self.balanceOf[_to] += _value
self.allowance[_from][msg.sender] -= _value
log Transfer(_from, _to, _value)
return True
Como este é um token encapsulado, precisamos de uma maneira de receber a criptomoeda que estamos tentando encapsular e cunhar uma quantidade equivalente de tokens, e também uma maneira de fazer o contrário, queimar vários tokens e receber a criptomoeda equivalente de volta.
Para o método mint
(cunhagem), o usuário envia criptomoedas com a chamada do método e recebe o mesmo valor de volta em tokens.
@external
@payable
def mint():
self._mint(msg.sender, msg.value)
E para o método burn
(queima), o usuário passa a quantidade de tokens que deseja queimar, para receber o equivalente em criptomoeda.
@external
def burn(_value: uint256):
self._burn(msg.sender, _value)
Também definiremos um método padrão. Este método será acionado se um usuário enviar AVAX diretamente para o contrato, sem chamar a função mint
. E, por sua vez, o contrato cunhará para esse usuário o equivalente em tokens.
@external
@payable
def __default__():
self._mint(msg.sender, msg.value)
E é isso. Agora temos um contrato inteligente de token, que cunha tokens encapsulados. O seu contrato deve ficar mais ou menos assim:
# @versão >=0.3.7
from vyper.interfaces import ERC20
from vyper.interfaces import ERC20Detailed
implements: ERC20
implements: ERC20Detailed
event Transfer:
sender: indexed(address)
receiver: indexed(address)
value: uint256
event Approval:
owner: indexed(address)
spender: indexed(address)
value: uint256
# @dev nome do token ("Wrapped AVAX")
name: public(String[32])
# @dev símbolo ou ticker do token ("WAVAX")
symbol: public(String[32])
# @dev a quantidade de decimais que o token contém
decimals: public(uint8)
# @dev o saldo de um determinado endereço
balanceOf: public(HashMap[address, uint256])
# @dev endereços podem permitir que outros endereços gastem seus tokens
allowance: public(HashMap[address, HashMap[address, uint256]])
# @dev o número de tokens em circulação
totalSupply: public(uint256)
@external
def __init__():
self.name = "Wrapped AVAX"
self.symbol = "WAVAX"
self.decimals = 18
@internal
def _mint(_to: address, _value: uint256):
assert _to != empty(address)
self.totalSupply += _value
self.balanceOf[_to] += _value
log Transfer(empty(address), _to, _value)
@internal
def _burn(_to: address, _value: uint256):
assert _to != empty(address)
self.totalSupply -= _value
self.balanceOf[_to] -= _value
send(_to, _value)
log Transfer(_to, empty(address), _value)
@external
def transfer(_to : address, _value : uint256) -> bool:
self.balanceOf[msg.sender] -= _value
self.balanceOf[_to] += _value
log Transfer(msg.sender, _to, _value)
return True
@external
def approve(_spender : address, _value : uint256) -> bool:
self.allowance[msg.sender][_spender] = _value
log Approval(msg.sender, _spender, _value)
return True
@external
def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
self.balanceOf[_from] -= _value
self.balanceOf[_to] += _value
self.allowance[_from][msg.sender] -= _value
log Transfer(_from, _to, _value)
return True
@external
@payable
def mint():
self._mint(msg.sender, msg.value)
@external
def burn(_value: uint256):
self._burn(msg.sender, _value)
@external
@payable
def __default__():
self._mint(msg.sender, msg.value)
Implantar usando o Brownie
Neste tutorial, vamos usar o Brownie para implantar nossos contratos inteligentes, você pode usar qualquer outro framework de contratos inteligentes se estiver familiarizado com ele. O Hardhat ou o ApeWorx têm suporte para desenvolver e implantar contratos inteligentes do Vyper.
Como estamos implantando na Avalanche, precisamos adicionar a rede principal (mainnet) da Avalanche e a rede de teste (testnet) da Avalanche, Fuji, às nossas redes suportadas. No terminal, digite o seguinte:
$ brownie networks add Avalanche avax-testnet host=https://api.avax-test.network/ext/bc/C/rpc chainid=43113 explorer=https://testnet.snowtrace.io/ name=Fuji
$ brownie networks add Avalanche avax-mainnet host=https://api.avax.network/ext/bc/C/rpc chainid=43114 explorer=https://snowtrace.io/ name=Mainnet
Depois de executarmos esses dois comandos, serão adicionadas as redes Avalanche ao Brownie.
Avalanche
├─Mainnet: avax-mainnet
└─Fuji: avax-testnet
Após a conclusão, podemos escrever o script de implantação.
Nosso script é muito simples, pois só vamos pegar nosso contrato inteligente e implantá-lo.
Nosso script verificará primeiro se estamos em alguma das redes Avalanche e, se estivermos, implantará os contratos inteligentes usando uma carteira real (com algum AVAX nela), caso contrário, assumiremos que estamos trabalhando em nossa rede local e os implantaremos lá.
Para implantar em uma blockchain ativa (Fuji ou rede principal), precisamos ter uma carteira com algum AVAX nela. Você pode obter alguns tokens AVAX de teste nesta torneira (faucet). Além disso, precisamos definir nosso arquivo brownie-config.yaml
para que o Brownie saiba onde encontrar nossa chave privada e também algumas configurações importantes.
Crie um arquivo brownie-config.yaml
na raiz do seu projeto. Ele deve se parecer com isso.
dotenv: .env
networks:
default: hardhat
wallets:
from_key: ${PRIVATE_KEY}
compiler:
vyper:
version: "0.3.7"
O arquivo de configuração espera que um arquivo .env
funcione corretamente. Crie um na raiz do seu projeto e preencha-o com isso.
PRIVATE_KEY="SUA_CHAVE_PRIVADA_AQUI"
Depois de definirmos nossa configuração, precisamos escrever nosso script de implantação. Na pasta scripts
crie um novo arquivo chamado deploy.py
. Este programa em python será responsável por implantar nossos contratos inteligentes.
O arquivo deploy.py
deve se parecer com isto:
from brownie import accounts, network, config, WAVAX
def main():
supported_networks = ["avax-mainnet", "avax-testnet"]
active_network = network.show_active()
deployer = accounts[0]
if active_network in supported_networks:
deployer = accounts.add(config["wallets"]["from_key"])
WAVAX.deploy({"from": deployer})
Para executar este script, escreva o seguinte comando em seu terminal. Isso implantará o contrato no seu nó local do Hardhat.
$ brownie run deploy
Para implantá-lo em uma rede de testes, escreva o seguinte.
$ brownie run deploy --network avax-testnet
E para implantá-lo na rede principal da Avalanche, execute o seguinte comando:
$ brownie run deploy --network avax-mainnet
E seria isso sobre o contrato inteligente. Criamos, testamos e implantamos com sucesso nosso contrato inteligente de token encapsulado.
Contrato inteligente na rede de testes (Fuji)
Artigo original publicado por Rafo AV. Traduzido por Paulinho Giovannini.
Oldest comments (0)