Como criar uma nova criptomoeda usando Solidity, TypeScript e HardHat

Cripto

Como criar uma nova criptomoeda usando Solidity, TypeScript e HardHat

Luiz Duarte
Escrito por Luiz Duarte em 10/11/2023
Junte-se a mais de 34 mil devs

Entre para minha lista e receba conteúdos exclusivos e com prioridade

Recentemente ensinei como criar uma nova criptomoeda, ou melhor, um novo token, no padrão da rede Ethereum (ERC-20), o mais usado mundialmente. Naquele momento, usamos a ferramenta Remix e fizemos todo o processo até o deploy e a configuração do token na nossa carteira MetaMask. Recomendo fortemente que faça esse outro tutorial primeiro antes desse aqui, pois hoje vamos aprender novamente a criar novos tokens ERC-20, mas usando ferramentas mais profissionais como o HardHat e OpenZeppelin.

Outro ponto importante é que este não deve ser o seu primeiro tutorial envolvendo a linguagem Solidity ou mesmo o toolkit HardHat. Se esse for o seu caso, recomendo começar por este outro aqui.

#1 – Criando o Projeto

O primeiro passo é você criar um novo projeto HardHat. Para isso, crie uma pasta na sua máquina, que eu vou chamar de token-erc20-hardhat. Abra o terminal e navegue até ela, executando os comandos para criação do projeto HardHat de exemplo (selecione a opção TypeScript e o resto deixe default).

Com o projeto criado, limpe as pastas contracts, scripts e test.

Na pasta contracts, crie somente um contrato nela, cujo nome do arquivo vai ser o nome da sua cripto, no meu caso: LuizCoin. Se você fez meu outro tutorial de token ERC-20, pode inclusive copiar e colar o código-fonte de lá para ganhar tempo, mas a abordagem que vou propor aqui é diferente e mais profissional pois vamos usar contratos da OpenZeppelin.

A OpenZeppelin é uma empresa conhecida mundialmente na área de cripto e blockchain por oferecer produtos e serviços na área de segurança de aplicações decentralizadas. Como alguns códigos Solidity são muito frequentes, como os códigos de padrões ERC, e é muito fácil de você deixar brechas em smart contracts para atacantes, eles resolvem os dois problemas fornecendo bibliotecas de contratos open-source já testados e auditados extensivamente para você usar, além de serviços de auditoria requisitados por grandes players do mercado.

Assim, vamos instalar a biblioteca de contratos deles em nosso projeto e você verá os ganhos que ela vai nos trazer logo mais.

Repare que estou usando a versão 0.8.20 ou superior no meu contrato, você deve usar a versão que possuir instalada na sua máquina, a partir da que informei acima. E por fim, vamos deixar a estrutura de nossos testes preparada, criando na pasta test um arquivo LuizCoin.test.ts e deixando a estrutura abaixo nele.

Aqui estamos carregando as importações necessárias, definindo uma suíte de testes (describe), criando a constante que define quantas casas decimais teremos na nossa moeda e criando a função de deploy fixture, que serve para o contrato ser provisionado apenas uma vez e limpo a cada teste, além de termos acesso às carteiras de teste.

Agora na pasta scripts vamos criar um arquivo deploy.ts colocando o código necessário para fazer o deploy do seu contrato apenas.

Agora temos o setup do nosso projeto de novo token pronto, é hora de programarmos ele!

Curso Node.js e MongoDB

#2 – Contrato do Token

Agora vamos nos concentrar no arquivo LuizCoin.sol, que é a implementação do nosso token ERC20. Como vamos usar a biblioteca de contratos da OpenZeppelin, vamos começar importando a mesma, logo no topo do arquivo e dizendo que nosso contrato vai herdar todas as características do contrato ERC20.sol existente na biblioteca do OpenZeppelin. Fazemos isso com a keyword ‘is’ ao lado do nome do contrato novo e antes do nome do contrato-pai, como abaixo.

Com esta implementação agora nossa LuizCoin tem acesso a todas características e funções compartilhadas pelo contrato ERC20.sol, acelerando bastante o nosso desenvolvimento e reduzindo drasticamente a nossa chance de deixar brechas em nosso código pois ele será mais de customização do que de implementação.

Por exemplo, para definir o nome e symbol do nosso token, basta chamarmos o constructor da superclasse/classe-pai junto ao nosso constructor, como abaixo, passando os seus parâmetros.

Aqui estou dizendo que o constructor de LuizCoin, quando chamado, deve invocar o constructor de ERC20 passando no primeiro argumento o nome da moeda e no segundo o symbol dela.

Também já realizei a implementação do código personalizado do constructor de LuizCoin, que vai fazer o minting, ou seja, a cunhagem/criação das moedas e a sua imediata transferência para uma carteira à sua escolha. Essa função _mint é herdade de ERC20 também e espera o endereço da carteira de destino e a quantidade de tokens a serem cunhados.

No exemplo, estou dizendo que a carteira do criador do contrato (lembrando que o constructor é chamado no deploy do contrato, então msg.sender é quem criou o mesmo na blockchain neste momento) vai receber 1000 tokens, o que será 0 nosso supply inicial. Eu multiplico a quantidade por 10 elevado na potência 18 porque quero que meu token tenha 18 casas decimais, como a maioria dos tokens ERC20 aliás, mas você pode alterar como quiser.

Com isso você já tem um token ERC20 funcional e se fizer deploy dele vai funcionar normalmente, como manda a especificação e tenho a certeza que você achou que seria mais complicado, certo?

Dá pra complicar, mas a base é essa aí mesmo.

Antes de pensar em complicar, vamos testar para ver se funciona mesmo.

#3 – Testes do Contrato

É aqui onde você vai escrever a maior quantidade de código e é onde também se mostrará necessário você ter feito o outro tutorial de token ERC20 onde escrevemos todo o contrato do zero, pois ali construímos o conhecimento de como tudo funciona, permitindo que a gente consiga saber como testar um token também.

Agora abra o arquivo LuizCoin.test.ts na pasta test e vamos escrever nosso primeiro teste, que irá verificar se o saldo total do token foi transferido para a carteira de quem fez o deploy do contrato (vulgo admin/owner).

Começamos o teste pegando o saldo do contrato e comparamos ele com o big number do total supply que é 1000 multiplicado por 10 na potência 18. Repare como uso o novo tipo do TypeScript para big numbers, suportado pela versão mais recente da biblioteca EthersJS. Para isso, basta adicionar o sufixo ‘n’ após o número. Isso é necessário pois os operadores tradicionais do JS não suportam big numbers. No final das contas a asserção é simples, com uso de .to.equal.

Eu não vou ficar citando, mas a cada teste codificado, o ideal é rodar a bateria toda com o comando ‘npx hardhat test’ a ver se estão passando, ok? Outra alternativa é usar o comando ‘npx hardhat coverage’ que traz um relatório de cobertura de testes.

O teste seguinte é mais simples, para verificar se o nome do token foi definido corretamente.

Nada de especial aqui, então vamos avançar para o próximo que é igualmente simples: verificar se o symbol foi definido corretamente.

E também outro bem simples que verifica se os decimals estão corretos.

E agora sim, finalmente entramos em um teste mais complicado: transfer. Aqui teremos dois cenários: um de sucesso e um de fracasso. No primeiro cenário, sucesso, vamos fazer uma transferência de 1 token para outra das carteiras disponíveis nos testes. No entanto, antes de fazermos esta transferência, sugiro pegar os saldos do from e do to a fim de podermos comparar se a transferência deu certo depois.

A chamada da função transfer é bem direta e simples e ela é nativamente feita a partir (from) da conta owner que é quem faz o deploy do mesmo. Como é uma transferência direta e de um valor pequeno (que cabe no saldo), ela será aceita normalmente e como resultado os saldos estarão ajustados após a mesma, o que podemos conferir pedindo novamente o saldo e comparando nos expects.

Agora vamos fazer o cenário de fracasso. Para isso, vou fazer o mesmo teste, mas tentando transferir mais do que a conta owner possui. Isso deve dar um erro, então vamos capturar o erro e fazer o expect em cima dele com o to.be.revertedWithCustomError.

Repare que o big number que defini é acima do total supply em 1 token, já causando o erro de transferência inválida por fundos insuficientes. Isso que nem é considerado nos testes as taxas de gás, caso contrário eu nem precisaria ter usado 1001. Já o nome do CustomError eu peguei diretamente do contrato ERC20.sol da OpenZeppelin.

Avançando nos testes, agora vamos fazer o teste da função approve, que serve para dar permissão a outra carteira transferir uma quantidade x de nosso fundos. Para podermos fazer o “antes e depois”, temos de pegar o allowance que nada mais é do que a quantidade permitida em transferências delegadas.

Nada muito diferente aqui se você já entende como funciona a função approve e a função allowance, então vamos avançar para a próxima, que é a função transferFrom, que permite transferências delegadas.

Aqui temos um teste bem semelhante ao transfer, porém com mais passos e mais verificações. No “antes e depois” eu quero ver se o allowance depois da transferência estará igual a antes da mesma, já que ele será “queimado”. Além disso, pego as variáveis de saldo antes e depois também para ver se a transferência de fato aconteceu. Agora sobre a transferência em si, precisamos primeiro aprovar ela e depois executá-la chamando o transferFrom, sem esquecer de primeiro usar a função connect para alterar a instância de conexão com o contrato, já que por padrão ele vem conectado com a conta owner. Neste exemplo, a carteira otherAccount está transferindo para si saldo da carteira owner conforme permissão prévia.

E para finalizar, vamos escrever o teste de falha em uma transferência delegada, o que é bem simples de fazer bastando não dar um approve antes do transferFrom.

Como essa tentativa de transferência irá disparar erro, vamos capturá-lo e fazer nossa asserção em cima do mesmo.

Agora experimente rodar a bateria de testes com ‘npxhardhat test’ e espero que você tenha o meso resultado que eu abaixo.

E com isso finalizamos mais este tutorial. Se quiser aprender como fazer deploy desta moeda em blockchain local, recomendo aprender a usar o Ganache neste tutorial ou a HardHat Network neste outro. Agora se quiser colocar em uma blockchain pública, recomendo fazer este tutorial aqui.

Um abraço e sucesso!

Olá, tudo bem?

O que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *