Um Guia Completo para Iniciantes sobre Solidity - Parte III

Um Guia Completo para Iniciantes sobre Solidity - Parte III

Estrutura de um Smart Contract em Solidity

Faala Devs!! Avançando um pouco mais, neste artigo iremos abortar de maneira mais pratica os fundamentos iniciais do Solidity e a estrutura de um Contrato Inteligente.

Iremos juntos também aprender um pouco mais sobre o Hardhat, pois neste artigo vamos compilar e testar nosso contrato de "Hello World".

Você encontra o código fonte do projeto neste respositório sob a branch #beginners-guide-part-iii.

Estrutura do Contrato Inteligente

Um contrato, no conceito do Solidity, é um conjunto de códigos (functions) e dados (state), que residem em um específico endereço na rede blockchain Ethereum.

image.png Fig. 1 Estrutura básica de um Contrato Inteligente

A estrutura de um contrato é muito semelhante a uma classe de outra linguagem orientada a objetos como vemos a seguir, fazendo disso um motivo que torna o aprendizado de Solidity mais simples. O arquivo de código fonte do Solidity pode conter qualquer número de definições de contrato, diretivas de importação de biblioteca e diretivas de pragma.

Cada contrato pode conter declarações de Variáveis de Estado, Funções, Modificadores de Função, Eventos, Tipos de Estrutura and Tipos de Enum. Além disso, contratos podem herdar de outros contratos.

Contrato de armazenamento

Vamos iniciar por um exemplo simples. Neste contrato estamos armazenando uma informação na Blockchain do tipo uint (falaremos mais no futuro) e resgatamos o seu conteúdo. Sem problemas se você não entender tudo neste momento, iremos entrar em mais detalhes depois.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.8.1;

import "hardhat/console.sol";

contract SimpleStorage {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}

License

A primeira linha informa sob qual licença de uso esta registrada o contrato, neste caso a GPL-3.0.

DICA: A declaração da licença não é obrigatório mas é uma boa prática.

Pragma

Na linha seguinte estamos simplesmente dizendo que o código fonte foi escrito para a versão 0.4.16 do Solidity ou mais recente e não quebra sua funcionalidade (até, mas não incluindo a versão 0.8.1). Isso é para garantir que o contrato não se comporte de forma diferente com uma nova versão do compilador. A palavra chave pragma é chamada desta maneira, porque, no geral pragmas são instruções para o compilador sobre como tratar o código fonte.

Imports

Muitas vezes temos a necessidade de utilizar funções que estão em outros contratos ou mesmo fazer uso de bibliotecas externas, para atender a esta necessidade podemos realizar a importação por meio da cláusula import como visto abaixo.

Assinatura do Contrato

Em contract SimpleStorage efetivamente declaramos o inicio de um novo contrato atribuindo a ele o nome de SimpleStorage. Contratos assim como em linguagens de orientação a objetos podem estender outros contratos ou implementar interfaces. Assunto esse que não será abordado em nosso guia por hora.

Variáveis de Estado

Os tipos de dados em solidity são explícitos, ou seja nós precisamos definir através de uma palavra chave qual é o tipo da variável.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

contract SimpleStorage {
    uint storedData; //variável de estado

A linha uint storedData declara uma variável de estado chamada storedData do tipo uint (integer não assinado de 256 bits). Você pode pensar nisso como uma única coluna de um registro em um banco de dados que pode ser consultado e alterado chamando funções do código que gerenciam o banco de dados. No caso do Ethereum, ele é sempre o contrato proprietário. E neste caso, as funções set e get podem ser usadas para modificar ou recuperar o valor da variável.

Em resumo as variáveis de estado são valores armazenados permanentemente na memória de contrato.

Para acessar uma variável de estado, você não precisa do prefixo this., como é comum em outras linguagens.

Funções

As funções são unidades executáveis de código dentro de um contrato. Elas tratam de encapsular trechos de código para serem executados sempre que necessário.

function soma(uint a, uint b) external pure returns(uint) {
   return a + b;
}

No exemplo acima temos a função soma que recebe 2 parâmetros uint a, uint b e retorna um uint.

Mais para frente iremos abordar os outros itens que compõem uma função como visibilidade, acessibilidade e modificadores.

Hello World Solidity

Vamos colocar a mão na massa finalmente!!! Neste momento replicar o famoso "Hello World"em Solidity.

Se você está seguindo esta séria desde o início, neste momento já possuí o projeto beginners-guide-to-solidity. Caso não recomendo que volte acessa a Parte II e siga as instruções para criar o projeto.

Certo, para darmos início acesse a pasta raiz do projeto e crie um diretório chamado contracts e dentro dele o arquivo HelloWorld.sol conforme a seguir.

cd beginners-guide-to-solidity
mkdir contracts && cd contracts
touch HelloWorld.sol

Vamos criar o contrato HelloWorld definindo o License, Pragma e os Imports necessários.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import "hardhat/console.sol";

contract HelloWorld {

}

Agora criemos a função sayHello, ela não deve receber nenhum parâmetro mas deve retornar uma string. Já que não iremos nem escrever qualquer informação na Blockchain a função deve possuir o modificador de acesso View e como iremos acessar ela de fora do contrato o modificador de visibilidade External. Veja como fica nossa função a seguir.

function sayHello() external view returns(string memory) {
   console.log("Function sayHello foi invocada.");
   return "Hello World Solidity";
}

Sendo esta a versão final do nosso contrato HelloWorld, muito simples né!?

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;

import "hardhat/console.sol";

contract HelloWorld {

   function sayHello() external view returns(string memory) {
      console.log("Function sayHello foi invocada.");
      return "Hello World Solidity";
   }

}

Compilando o contrato inteligente

Agora que escrevemos o contrato vamos compilar e ver se tudo correu bem até aqui. ;-)

Para isso basta digitar no terminal:

npx hardhat compile

Após a compilação você tera uma mensagem semelhante a essa:

Compiled 2 Solidity files successfully

Excelente contrato compilado e pronto para uso, mas antes de fazer o delivery na rede local vamos escrever alguns testes.

Testando o contrato inteligente com Testes Unitários

Maravilha, contrato criado e compilado com sucesso, agora vamos escrever alguns testes unitários para garantir que ele realize o que foi programado para fazer, neste caso, retornar uma string com o valor Hello World Solidity.

Para isso vamos criar um novo diretório chamado test na raiz do nosso projeto e dentro dele um arquivo chamado HelloWorld.js.

mkdir test && cd test
touch HelloWorld.js

Excelente, agora copie e cole o trecho de código abaixo no arquivo HelloWorld.js, iremos falar dele em seguida.

const { expect } = require("chai");

describe("Hello World Contract", function () {

  let HelloWorldContract;
  let helloWorldContractDeployed;

  beforeEach(async function () {
    HelloWorldContract = await ethers.getContractFactory("HelloWorld");
    helloWorldContractDeployed = await HelloWorldContract.deploy();
  });

  describe("Call Function Say Hello", function () {

    it("Should be return Hello World Solidity when call the function sayHello", async function () {

      const helloWorldMessage = await helloWorldContractDeployed.sayHello();
      expect("Hello World Solidity").to.equal(helloWorldMessage);

    });

  });

});

Agora no terminal digite npx hardhat test. Ao final do teste você poderá ver uma mensagem semelhante a esta:

Hello World Contract
    Call Function Say Hello
Function sayHello foi invocada.
      ✔ Should be return Hello World Solidity when call the function sayHello (38ms)


  1 passing (734ms)

Podemos ver inclusive que na terceira linha a nossa mensagem que enviamos para o console do HardHat pode ser apresentada.

Explicando o teste

Agora que o nosso teste passou com sucesso, vamos à explicação sobre o que faz cada linha do teste.

const { expect } = require("chai");

Realizamos a importação da biblioteca de assertions.

describe("Hello World Contract", function () {

  let HelloWorldContract;
  let helloWorldContractDeployed;

Apenas definimos uma descrição melhor para o nosso grupo de testes para o contrato e definimos algumas variáveis que usamos mais a frente.

beforeEach(async function () {
   HelloWorldContract = await ethers.getContractFactory("HelloWorld");
   helloWorldContractDeployed = await HelloWorldContract.deploy();
});

Antes de cada teste realizamos o delivery do nosso contrato.

O ContractFactory no ethers.js é uma abstração utilizada para efetuar o deploy de novos contratos, e HelloWorld é a factory utilizada para instanciar o nosso contrato.

Após a chamada do deploy() nosso ContractFactory vai iniciar o deploy do nosso contrato e devolver uma Promisse que resolve o nosso contrato. Este sendo o objeto que contém as funções do nosso contrato com a qual podemos interagir.

describe("Call Function Say Hello", function ()

Define uma descrição para o nosso grupo de testes que podem vir a seguir, no nosso caso apenas um teste.

it("Should be return Hello World Solidity when call the function sayHello", async function () {

   const helloWorldMessage = await helloWorldContractDeployed.sayHello();
   expect("Hello World Solidity").to.equal(helloWorldMessage);

});

Teste propriamente dito, onde await helloWorldContractDeployed.sayHello() resolvemos a promisse com o resultado da chamada a função sayHello() dentro do nosso contrato e realizamos a assertion em expect("Hello World Solidity").to.equal(helloWorldMessage).

Conclusão

Ficamos por aqui pessoal. Espero que vocês tenham gostado deste artigo onde pudemos conhecer melhor a estrutura de um Smart Contract, bem como realizar a criação e teste unitário do nosso primeiro contrato inteligente, o famoso Hello World.

Se precisar de alguma ajuda, se tem alguma dúvida ou se tiver algum feedback, utilize o espaço para comentários e deixe um recado.

Vamos nos conectar no Twitter e LinkedIn.

👋 Obrigado por ler até o final e até o próximo artigo !!!

Did you find this article valuable?

Support Thiago (DukeFullStack) by becoming a sponsor. Any amount is appreciated!