Se você não possui experiência alguma com Node.js/JavaScript, recomendo começar por este outro tutorial, que usa ferramentas menos profissionais e mais simples.
Se o Bitcoin foi o grande catalisador para o surgimento das criptomoedas modernas como conhecemos, a Ethereum foi a grande catalisadora para o surgimento de milhares de outras criptomoedas, NFTs e a própria Web 3.0 que vem se falando cada vez mais. Vitalik Buterin, criador da rede Ethereum e seu time, vislumbraram na tecnologia blockchain não apenas a possibilidade de criar um dinheiro virtual P2P, ou seja descentralizado e seguro, como a oportunidade de permitir a criação de “contratos inteligentes” (smart contracts). Assim, qualquer programador ao redor do mundo poderia publicar seus códigos para serem executados na rede Ethereum e com isso iniciar o surgimento de todo um ecossistema ainda maior e mais vibrante (embora ainda de menor capitalização) do que o Bitcoin original.
Em 2014, foi Gavin Wood, um dos fundadores da Ethereum, que teve a ideia de criar a linguagem Solidity, especificamente para a codificação de Smart Contracts na rede Ethereum e demais redes compatíveis (forks, como a BNB Smart Chain que surgiu anos mais tarde). Os smart contracts escritos em Solidity são publicados na rede e toda vez que acionados por clientes, rodam em um interpretador chamado EVM, a Ethereum Virtual Machine, semelhante ao que acontece com a VM do Java, caso você conheça.
A linguagem Solidity é hoje a principal tecnologia relacionada a smart contracts que você deve estudar se deseja aprender a criar aplicações que rodam na blockchain, principalmente Dex, Defi, DAOs e é claro, dapps Web3. Uma linguagem jovem, semelhante ao padrão ECMA para quem está acostumado com programação JS, mas com tipagem estática. Aliás, os contratos em si lembram muito as classes existentes em outras linguagens Orientadas à Objetos.
A minha ideia com este tutorial é lhe dar uma introdução nesta linguagem e tecnologia, lhe despertando então interesse (e capacidade) por conhecer mais sobre este universo que não pára de crescer. Acredito estar desta forma colaborando com o desenvolvimento de novos profissionais neste meio, principalmente aqueles sem grandes conhecimentos do idioma Inglês, onde você encontra muito material acerca de Solidity.
Então vamos lá!
Se preferir, você pode acompanhar ao conteúdo deste tutorial no vídeo abaixo.
#1 – Ambiente
Primeiro é importante entender que não tem uma única forma de criar os seus smart contracts com Solidity. Em outro tutorial aqui do blog eu mostrei como fazer com a ferramenta Remix, por exemplo. Eu vou mostrar aqui da forma como eu aprendi e que considero mais profissional. Mais tarde, sinta-se livre para abandonar o que ensinarei e seguir o seu próprio caminho.
Você vai precisar primeiramente do Node.js, pois ele é a base para outras ferramentas que vamos usar. Então vá até o site oficial e baixe a versão LTS para sua máquina, instalando-a até o final (marcando tudo que ela perguntar durante o processo). Também vou usar aqui o editor de código Visual Studio Code, que é gratuito e pode ser baixado no site oficial.
As instalações são bem diretas, mas se precisar de ajuda, o vídeo abaixo pode te ajudar.
Um dos motivos de eu recomendar o Visual Studio Code ao invés de outros editores é que além dele ser gratuito, leve e multiplataforma, ele tem a possibilidade de instalarmos extensões para diferentes linguagens, como no caso da extensão Solidity do Juan Blanco, que eu recomendo que instale na aba de extensões assim que estiver com o VS Code aberto a primeira vez.
Agora que você preparou o seu editor de código e o Node já está devidamente instalado também, é hora de instalarmos o Truffle. O Truffle é um toolkit, uma suíte de ferramentas para ajudar na criação de smart contracts e que vai acelerar bastante a nossa produtividade nesse tipo de desenvolvimento. Ele é desenvolvido em Node.js e por isso que precisamos ter o Node instalado antes dele.
Para instalar o Truffle vamos usar o NPM, o gerenciador de pacotes do Node. A instalação deve ser feita com permissão de administrador e será global, como indicada pelo comando abaixo.
1 2 3 |
npm install -g truffle |
Este comando vai baixar e instalar todos os arquivos do Truffle na sua máquina o que deve demorar um bocado, então não se assuste.
Quando terminar, você poderá criar o seu primeiro projeto Truffle. Para isso crie uma pasta onde quiser na sua máquina e via terminal (ou via VS Code) navegue até ela para rodar o comando de inicialização de projeto do Truffle como abaixo.
1 2 3 |
truffle unbox |
Isso irá gerar uma estrutura inicial de projeto como abaixo, um projeto genérico de smart contracts. Existem vários outros templates que o Truffle possui, este é o padrão e lhe aconselho depois a dar uma olhada no site oficial dele para ver as possibilidades.
Essa estrutura inicial é de um projeto de novo token chamado MetaCoin e você encontrará os contratos dele na pasta contracts, sendo os arquivos com extensão “.sol” (de Solidity). Este projeto não é compliance com nenhum padrão de tokens de mercado, é apenas didático mas já dá uma ideia de como é um projeto Solidity.
#2 – Criando o Hello World
Como vamos criar o nosso contrato do zero e ele vai ser bem simples, recomendo excluir todo o conteúdo da pasta contracts, deixando a mesma vazia, bem como excluir a segunda migration (deploy) na pasta migrations e todo conteúdo da pasta test.
Agora, na pasta de contracts, vamos criar o nosso arquivo HelloWorld.sol, que vai ser o nosso arquivo de contrato.
Assim que tiver criado este arquivo em branco, tem duas instruções iniciais que devemos colocar. Uma delas é a licença do seu código-fonte, que você pode incluir usando o atalho spdx e apertando Enter, que vai adicionar a licença MIT. Tendo interesse em mudar a licença para outra à sua escolhe, fique à vontade.
A segunda instrução é a versão do compilador de Solidity que será usada para compilar este contrato. Isso porque assim como funciona com a linguagem Java, C# e outras, nós escrevemos o programa (contrato neste caso) em uma linguagem e ela precisa ser compilada para uma linguagem de máquina que será interpretada pela EVM (Ethereum Virtual Machine) mais tarde. Ao longo do tempo novas versões da linguagem são lançadas com novas funcionalidades e portanto é importante especificar qual a versão que você precisa para o seu projeto.
Para iso, use o atalho pragm apertando Enter, que vai adicionar uma instrução de versão para você, que deve ser preenchida com a versão de Solidity que foi instalada junto do Truffle na sua máquina.
Ao término destas duas instruções, o seu arquivo deve estar assim (minha versão é a 0.8.17). Se você não souber a sua versão, basta digitar qualquer uma que a mensagem de erro vai te informar a versão que você possui ao passar o mouse por cima do erro.
1 2 3 4 |
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; |
Também é importante adicionar esta mesma versão no seu arquivo truffle-config.js, que fica na raiz do projeto. Você faz isso adicionando a propriedade version dentro de compilers > solc. Aproveito para recomendar que remova o conteúdo da propriedade networks, que serviria para dizer em qual rede nosso projeto vai ser executado, mas como é apenas didático para testes, vamos omitir esta informação para que ele use uma rede interna do Truffle.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
module.exports = { networks: { }, compilers: { solc: { version: "0.8.17", settings: { optimizer: { enabled: true, // Default: false runs: 200 // Default: 200 }, } } } }; |
Enquanto não fizer os ajustes acima, o VS Code vai ficar reclamando para você, então não deixe para depois.
Opcionalmente você pode documentar o seu smart contract usando o atalho nat-contract e apertando Enter, o que vai gerar o esqueleto de documentação abaixo que fica a seu critério preencher (ou não).
1 2 3 4 5 6 |
/// @title A title that should describe the contract/interface /// @author The name of the author /// @notice Explain to an end user what this does /// @dev Explain to a developer any extra details |
Abaixo destas informações todas é que vamos começar a codificar nosso primeiro contrato. Se você já conhece alguma linguagem orientada à objetos antes, vai se sentir em casa pois escrever um contrato é muito semelhante a escrever classes. Para começar, você define o seu contrato usando a palavra reservada contract e definindo o seu escopo entre chaves.
1 2 3 4 5 |
contract HelloWorld { } |
Dentro do contrato você pode colocar suas propriedades e funções, assim como faria com classes e começaremos declarando uma propriedade chamada message, apenas para guardar o conteúdo de uma mensagem.
1 2 3 4 5 |
contract HelloWorld { string public message = "Hello World!"; } |
O tipo da variável definimos como string (texto), o modificador de acesso colocamos como public (o que indica que pode ser acessado de fora do contrato), o nome da variável colocamos message (mensagem em Inglês) e atribuímos o valor entre aspas Hello World para ela, da mesma forma que faríamos em JavaScript e tantas outras linguagens de programação.
Outros tipos válidos para variáveis, dentre muitos:
- bool: true ou false;
- int: números inteiros (com variações em bits como int8, int256, etc);
- uint: inteiros positivos (sem sinal, unsigned, com variações em bits como uint8, uint256, etc);
- address: endereços na rede;
Da mesma forma, o processo de criação de uma função também é bem simples e abaixo segue um exemplo de função helloWorld.
1 2 3 4 5 6 7 8 9 |
contract HelloWorld { string public message = "Hello World!"; function helloWorld() public view returns (string memory) { return message; } } |
Neste exemplo usamos a palavra reservada function para dizer que vamos escrever uma função, damos o nome à mesma (camel case) e abrimos parênteses para definir os parâmetros, que optei aqui por não ter nenhum. Na sequência definimos o modificador de acesso (public, indicando que ela pode ser chamada fora deste contrato), o tipo de função (usei view pois vamos precisar visualizar uma informação do contrato) e a palavra reservada returns para informar o que essa informação retorna, neste caso uma única variável string memory (que explicarei mais tarde o porque do memory).
Talvez o ponto mais diferentão aqui seja a palavra reservada view, que indica que esta função vai ler o “estado” do contrato, ou seja, as suas propriedades/variáveis internas. Outra opção seria a palavra reservada pure, que indica que esta é uma função pura, ou seja, sem estado, sem usar variáveis externas. É importante ressaltar desde já que o uso de variáveis de classe ou de função impactam diretamente no custo das chamadas à este contrato, o que exploraremos futuramente aqui no blog. E por último também temos as funções payable, que permitem que alguém envie fundos (pague) para usar a função.
Além disso, outro ponto que é bacana reforçar é quanto ao modificador de acesso, public, que se não for informado vai ser private por padrão, ou seja, sem acesso externo.
O conteúdo da função em si dispensa explicações e agora que temos nosso HelloWorld codificado, é hora de testarmos ele.
Antes disso, apenas dê um pulo na pasta migrations e na única que restou, mude as referências à MetaCoin para seu contrato HellWorld, como abaixo.
1 2 3 4 5 6 7 |
const HelloWorld = artifacts.require("HelloWorld"); module.exports = function(deployer) { deployer.deploy(HelloWorld); }; |
Este arquivo é referente a deploy e não entraremos em detalhes no momento, apenas vale sinalizar que ele deve ser ajustado ou teremos erro mais tarde.
#3 – Testando o Hello World
Testes são um parte crucial do desenvolvimento de qualquer software mas Solidity coloca isso em outro patamar. Isso porque como seu contrato ficará visível na blockchain e na maioria das vezes ele envolverá dinheiro, é crucial que ele esteja em pleno funcionamento e que seja seguro. Não apenas isso, as transações na blockchain são imutáveis então não temos margem para corrigir pequenas coisinhas depois, como é comum no mundo web.
Dito isso, vá até a sua pasta de test e crie um arquivo HelloWorld.test.js nela, embora este nome seja apenas uma recomendação, pode ser o nome que quiser.
Dentro deste arquivo vamos escrever os testes unitários do nosso contrato, para isso o Truffle vai nos fornecer uma série de ferramentas que vai deixar a experiência muito próxima de usando libs de testes JS como Tape e Jest. Caso esteja familiarizado com alguma destas bibliotecas ou outras concorrentes como Mocha, Chai e Jasmine, se sentirá em casa.
Para criar a estrutura padrão dos testes carregue o seu smart contract com a instrução artifacts.require e depois crie o contrato de teste, definindo a função JS que vai executar os testes.
1 2 3 4 5 6 7 |
const HelloWorld = artifacts.require("HelloWorld"); contract('HelloWorld', function(accounts) { }); |
Dentro da function do seu contrato de testes você colocará todos os seus cenários de teste, bem como funções de preparação dos testes, como abaixo, onde configurei a função beforeEach (nativa dos testes) como sendo responsável por criar uma nova instância do nosso contrato e olá mundo antes de cada teste que fizermos, garantindo assim o isolamento entre eles.
1 2 3 4 5 6 7 |
contract('HelloWorld', function (accounts) { beforeEach(async () => { contract = await HelloWorld.new(); }) }); |
contract será uma variável presente em cada um de nossos testes e ela está recebendo uma nova instância do contrato HelloWorld com a chamada new, que seria o equivalente aos construtores das linguagens orientadas à objetos mais populares. Tudo que você quiser que rode antes de cada teste pode ser feito nesta função do beforeEach.
Agora para escrever os testes em si, você pode adicionar chamadas à função it quantas vezes quiser, logo depois da função beforeEach, como abaixo.
1 2 3 4 5 6 |
it('Hello World', async () => { const result = await contract.helloWorld(); assert(result === "Hello World!"); }) |
Neste teste de exemplo, o único que vamos precisar, nós damos um nome ao teste (Hello World), seguido da função de teste que nada mais está fazendo do que usando a instância de contract que inicializamos no beforeAll para chamar a função do nosso contrato (helloWorld, lembra?) e analisando o retorno dela através da função assert.
A função assert é quem vai dizer se nosso teste passou ou não, baseado no booleano resultante da expressão lógica que passamos à ela, no caso a comparação literal com a frase que deve estar em nosso contrato, na variável message.
Para rodar esta bateria de testes é bem simples, basta abrir o terminal e uma vez dentro da pasta do seu projeto rodar os comandos abaixo do Truffle, certificando-se de estar rodando o terminal como administrador.
1 2 3 4 |
truffle compile truffle test |
Isso vai fazer com que o Truffle baixe o compilador necessário para rodar o seu projeto, vai fazer a compilação (primeiro comando) e vai rodar os testes (segundo comando), lhe apresentando os resultados como abaixo.
Depois dessa primeira execução, pode usar apenas o comando truffle test sempre que quiser rodar os testes novamente.
E com isso finalizamos o nosso primeiro tutorial de Solidity aqui do blog. Quer aprender mais? Aprenda a fazer um CRUD neste próximo tutorial ou a fazer deploy neste aqui.
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.