E chegamos finalmente à terceira e última parte da nossa série de tutoriais sobre programação de um novo token ERC-20 usando Solidity.
Na parte 1 você encontra a base teórica e fundamental para entender os tokens e o padrão ERC-20. Confira aqui.
Já na parte 2 nós começamos a implementação de fato, programando os estados do token e as primeiras e mais importantes funções. Confira aqui.
Nesta terceira parte vamos implementar as funções restantes e fazer o deploy na blockchain, para que possamos usar nosso novo token em uma carteira de verdade.
Vamos lá!
#1 – Transferência Delegada
Existe um conceito muito importante em token ERC-20 que é o de transferência delegada. Por padrão as transferências são sempre de alguém enviando fundos para outra carteira, ali, naquela hora, através de algum software de carteira como MetaMask. No entanto existem muitas situações em que uma transferência de fundos deve ser realizada mas o dono da carteira não está ali, no momento, para autorizar a transação. Por exemplo quando acontecem negociações em exchanges.
Assim, os tokens ERC-20 definem que o dono da carteira pode pré-autorizar outro endereço a gastar uma quantia de seus tokens como bem entender, o que chamamos de delegação de transferência ou transferência delegada. E mesmo que você talvez não tenha certeza ainda da utilidade disso, é importante deixarmos implementado no token a fim de garantir que ele seja 100% compatível com o padrão, certo?
O primeiro passo para implementar a transferência delegada é criar alguma estrutura capaz de registrar as delegações, sendo que cada delegação que uma carteira possuir deve incluir a carteira autorizada e a quantia autorizada. Assim, uma combinação de mappings pode ser interessante, como abaixo.
1 2 3 |
mapping(address => mapping(address => uint)) private _allowances; |
Mapping não é novidade aqui, certo? Mas talvez esta estruturação combinando dois níveis de mappings o seja, então vale nova explicação.
No primeiro mapping temos como chave o endereço da carteira que tem os fundos, o owner/from. Para cada carteira teremos então um novo mapping, com todos os gastadores (spenders) autorizados naquela carteira. Para cada spender, este segundo mapping irá trazer a quantia de fundos autorizados na carteira do owner, lembrando que as quantias são sempre na menor fração (wei). O diagrama abaixo ilustra essa relação mais complexa criada pelos mappings aninhados.
Assim, se eu quiser saber se o Autorizado 2 possui permissão para transferir fundos da Carteira 1, eu faria algo como abaixo (endereços ilustrativos).
1 2 3 |
if(_allowances[carteira1][autorizado2] > 0) |
Agora que temos a estrutura para registrar as aprovações vamos criar a função de aprovação, bem como o evento que será emitido a cada aprovação, como abaixo.
1 2 3 4 5 6 7 8 9 |
event Approval(address indexed owner, address indexed spender, uint value); function approve(address spender, uint value) public returns(bool){ _allowances[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } |
Na função acima, approve, que é definida no padrão, nós recebemos quem vai ser autorizado e qual a quantia pré-autorizada. Guardaremos estas informações dentro da carteira autorizadora no primeiro nível do mapping e em seguida emitimos o evento de aprovação, que também está definido na especificação, então não tem muito o que inventarmos.
E conforme define o padrão, vamos criar também a função que consulta o allowance para um determinado spender em um determinado endereço.
1 2 3 4 5 |
function allowance(address _owner, address _spender) public view returns (uint256){ return _allowances[_owner][_spender]; } |
Com estes primeiros pontos implementados, agora poderemos fazer a transferência delegada acontecer de fato.
#2 – Função transferFrom
A última função que falta implementarmos agora é a de transferência delegada em si, a transferFrom, conforme exigido no padrão ERC-20.
1 2 3 4 5 6 7 8 9 10 |
function transferFrom(address from, address to, uint value) public returns(bool){ require(balanceOf(from) >= value, "Insufficient balance"); require(allowance(from, msg.sender) >= value, "Insufficient allowance"); _balances[to] += value; _balances[from] -= value; emit Transfer(from, to, value); return true; } |
Aqui usamos uma combinação de conceitos previamente explorados, como os requires que agora são duas as condições para execução da função: enquanto que o primeiro require valida a existência de fundos na carteira do from, o segundo require valida se quem está tentando fazer a transferência delegada tem permissão para tanto.
A seguir temos o incremento e decremento de tokens, sinalizando a mudança de fundos, seguido da emissão do evento de transferência e o retorno positivo.
E caso o usuário não queira mais que o usuário x tenha permissão, como isso é feito? Temos de criar uma função de disapproval? Não é necessário, basta chamar o approval novamente passando o valor 0 no value e isso será registrado no mapping de allowances e portanto não permitirá mais transferências delegadas vindas daquele endereço.
Agora a recomendação que faço, antes de fazer um deploy final na blockchain, é de que você faça deploy na VM de teste do Remix e teste essas novas funções nela, antes de avançar.
#3 – Deploy e Testes Finais
Após realizar os seus testes finais, você pode realizar o deploy usando Remix e MetaMask seguindo este passo a passo.
Depois que tiver realizado o deploy é hora de testarmos o nosso token em uma carteira de verdade, sendo que usarei a MetaMask apontada para a Testnet, como ensino a fazer no tutorial que citei acima. Caso queira fazer um teste mais bacana, é legal você ter duas MetaMask, de repente uma no navegador e outra no smartphone, pra você ver tudo de fato funcionando. Apenas certifique-se de que as duas estão apontadas para a Testnet.
O primeiro passo do nosso teste final é que temos de importar o nosso novo token na nossa carteira MetaMask, já que ela não vai encontrar ele sozinho. Também é importante que você faça isso na carteira cujo endereço foi usado no deploy do token, a fim de que você tenha acesso ao total supply de tokens e possa começar a distribuir os mesmos.
Para importar um token é bem simples, você deve ir na mesma carteira MetaMask que fez o deploy e bem no final dos tokens que você possui terá uma opção “Importar Tokens”. A primeira informação e mais importante que você deve fornecer para que a importação aconteça é o endereço do contrato do seu token, que você obteve no Remix após o deploy que você já fez.
Via de regra, todos os demais campos se preencherão automaticamente, mas você pode preenchê-los manualmente caso isso não aconteça. Depois basta confirmar e pronto, você estará com todos os seus tokens à disposição e poderá fazer transferências para outras carteiras a partir dessa principal.
Se você quiser dar uma olhada nas informações do seu contrato na blockchain, além de fazê-lo pelo Remix na seção logo abaixo do deploy, você pode usar o site do block explorer da sua rede (Sepolia EtherScan por exemplo), sendo que o meu contrato da LuizCoin está disponível como exemplo neste link. Nesta página você consegue ver todos os detalhes do contrato e todas as transações já realizadas nele. Como é apenas um projeto didático, não espera muita atividade por lá, haha.
Espero que tenha conseguindo implementar seu token e se quiser fazer um projeto de token mais profissional, dá uma olhada nesse tutorial aqui. E se quiser aprender a reduzir o consumo de gás nos seus smart contracts, este é o artigo certo.
Até a próxima!
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.
Olá. Bom dia. Quero fazer Tokens de meus desenhos,sou Artista,desenhista de Artes a lápis retrato e abstrato.
Nesse caso o que você deve estudar é a ERC721, que serve para tokens não-fungíveis (NFTs).