Solidity é utilizado para programar contratos in­te­li­gen­tes so­fis­ti­ca­dos para operar na block­chain da Ethereum. É uma linguagem de pro­gra­ma­ção que oferece uma abordagem in­te­res­sante que a di­fe­ren­cia das outras lin­gua­gens.

O que é Solidity?

Solidity é uma linguagem de pro­gra­ma­ção de alto nível para criar contratos in­te­li­gen­tes na block­chain Ethereum. Os «contratos in­te­li­gen­tes» são contratos au­to­e­xe­cu­tá­veis que au­to­ma­ti­zam a troca de ativos ou fundos entre duas ou mais partes. Este tipo de contrato não necessita de um in­ter­me­diá­rio para garantir o seu cum­pri­mento, razão pela qual são especiais.

O código-fonte do Solidity é compilado em bytecode e inserido na block­chain do Ethereum como um contrato in­te­li­gente. O contrato in­te­li­gente pode ser executado por qualquer nó da rede e o seu estado é ar­ma­ze­nado na block­chain. Aqui está um exemplo de um contrato simples que simula uma máquina de venda de NFT:

pragma Solidity 0.8.7;
contract NFTVendingMachine {
    // Declare state variables
    address public owner;
    mapping (address => uint) public nftBalance;
    // Run on deployment
    constructor() {
        owner = msg.sender;
        nftBalance[address(this)] = 100;
    }
    // Allow the owner to restock the NFT balance
    function restock(uint amount) public {
        require(msg.sender == owner, "Only the owner can restock.");
        nftBalance[address(this)] += amount;
    }
    // Allow anyone to purchase NFTs
    function purchase(uint amount) public payable {
        require(msg.value >= amount * 1 ether, "You must pay at least 1 ETH per NFT");
        require(nftBalance[address(this)] >= amount, "Not enough NFTs in stock to complete this purchase");
        nftBalance[address(this)] -= amount;
        nftBalance[msg.sender] += amount;
    }
}
solidity

Em que casos se utiliza Solidity?

O Solidity foi de­sen­vol­vido es­pe­ci­fi­ca­mente para criar apli­ca­ções des­cen­tra­li­za­das, DApps (Dis­tri­bu­ted Ap­pli­ca­ti­ons), que são exe­cu­ta­das na Ethereum Virtual Machine (EVM). Os contratos in­te­li­gen­tes são ideais para gerir ativos digitais, criar bolsas de valores des­cen­tra­li­za­das e im­ple­men­tar sistemas de votação, entre outros aspetos.

Finanças Des­cen­tra­li­za­das (DeFi)

Solidity é usado para de­sen­vol­ver apli­ca­ções DeFi, como mercados de valores des­cen­tra­li­za­dos, pla­ta­for­mas de em­prés­ti­mos e fi­nan­ci­a­men­tos, mercados de previsão e crip­to­mo­e­das. DeFi é um dos casos de uso mais populares da tec­no­lo­gia block­chain. Solidity tornou-se uma fer­ra­menta in­dis­pen­sá­vel para criar apli­ca­ções DeFi na rede Ethereum.

Tokens não fungíveis

Os tokens não fungíveis (NFT, non-fungible tokens) gozam de grande po­pu­la­ri­dade desde 2020. Os NFT são de­ter­mi­na­dos ativos digitais ar­ma­ze­na­dos na block­chain. Podem ser obras de arte digitais, re­cor­da­ções des­por­ti­vas ou ar­te­fac­tos do setor dos vi­de­o­jo­gos. O Solidity é utilizado para criar os contratos in­te­li­gen­tes que suportam os NFT.

Gestão da cadeia de abas­te­ci­mento

É possível utilizar Solidity para criar contratos in­te­li­gen­tes com o objetivo de su­per­vi­si­o­nar e gerir as cadeias de abas­te­ci­mento. Os contratos são uti­li­za­dos para au­to­ma­ti­zar di­fe­ren­tes processos da cadeia de abas­te­ci­mento. Entre eles, encontram-se o acom­pa­nha­mento dos mo­vi­men­tos de mer­ca­do­rias, a ve­ri­fi­ca­ção da au­ten­ti­ci­dade dos produtos e o pro­ces­sa­mento dos pa­ga­men­tos entre as di­fe­ren­tes partes.

Sistemas de votação

O Solidity pode ser usado para criar contratos in­te­li­gen­tes que im­ple­men­tam sistemas de votação seguros e trans­pa­ren­tes na block­chain. Os contratos podem ser usados para garantir que os votos sejam contados cor­re­ta­mente e que o processo de votação seja justo e trans­pa­rente.

Quais são as vantagens e des­van­ta­gens do Solidity?

Em geral, Solidity é uma linguagem de pro­gra­ma­ção poderosa, de­sen­vol­vida para criar contratos in­te­li­gen­tes na block­chain Ethereum. No entanto, como toda tec­no­lo­gia, Solidity oferece vantagens e des­van­ta­gens es­pe­cí­fi­cas que os pro­gra­ma­do­res devem ter em conta ao criar contratos in­te­li­gen­tes. De qualquer forma, criar contratos in­te­li­gen­tes con­fiá­veis requer um certo nível de ha­bi­li­dade e atenção.

A título de exemplo, vamos mostrar um contrato in­te­li­gente que funciona como um buraco negro: todo o Ether enviado para o contrato é ir­re­ver­si­vel­mente absorvido, uma vez que não é possível re­em­bol­sar o Ether.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.9.0;
// This contract swallows all Ether sent to it
contract Blackhole {
    event Received(address, uint);
    receive() external payable {
            emit Received(msg.sender, msg.value);
    }
}
solidity

Vantagens do Solidity

  • Fle­xi­bi­li­dade: Solidity é uma linguagem de pro­gra­ma­ção muito versátil. Pode ser usada para criar di­fe­ren­tes contratos in­te­li­gen­tes com uma grande variedade de casos de uso.
  • Segurança: Solidity foi de­sen­vol­vida pre­ci­sa­mente a pensar na segurança. A linguagem inclui ca­rac­te­rís­ti­cas como controlos de acesso, gestão de exceções e me­ca­nis­mos de falha que ajudam os pro­gra­ma­do­res a escrever contratos seguros.
  • Com­pa­ti­bi­li­dade com Ethereum: Solidity é atu­al­mente a linguagem de pro­gra­ma­ção por ex­ce­lên­cia para criar contratos in­te­li­gen­tes na block­chain Ethereum.
  • Co­mu­ni­dade sólida: Solidity conta com uma grande co­mu­ni­dade de pro­gra­ma­do­res de block­chain. Ou seja, dispõe de inúmeros recursos para aprender e resolver problemas.

Des­van­ta­gens do Solidity

  • Curva de apren­di­za­gem: Solidity tem uma curva de apren­di­za­gem re­la­ti­va­mente alta para pro­gra­ma­do­res que estão a lidar com block­chain e contratos in­te­li­gen­tes pela primeira vez.
  • Imu­ta­bi­li­dade: uma vez que um contrato in­te­li­gente é in­tro­du­zido na block­chain, ele não pode ser mo­di­fi­cado, portanto, os pro­gra­ma­do­res devem ser ex­tre­ma­mente cui­da­do­sos ao redigi-lo e testá-lo.
  • Ausência de revisão formal: Solidity não possui fer­ra­men­tas in­te­gra­das que revisam o código for­mal­mente, portanto, os de­sen­vol­ve­do­res precisam recorrer a fer­ra­men­tas externas para garantir que seus contratos estejam bem escritos.
  • Fer­ra­men­tas limitadas: o ecos­sis­tema de fer­ra­men­tas do Solidity ainda é re­la­ti­va­mente pouco de­sen­vol­vido. Portanto, pode causar problemas com IDE, es­tru­tu­ras de testes e outras fer­ra­men­tas de de­sen­vol­vi­mento.

Como é a sintaxe básica do Solidity?

Solidity é uma linguagem orientada a objetos, es­pe­ci­a­li­zada em contratos in­te­li­gen­tes e in­flu­en­ci­ada por Ja­vaS­cript, Python e C++. A sintaxe do Solidity é se­me­lhante à do Ja­vaS­cript, embora apresente algumas pe­cu­li­a­ri­da­des in­te­res­san­tes.

Variáveis em Solidity

À primeira vista, as variáveis do Solidity parecem funcionar como em outras lin­gua­gens se­me­lhan­tes. No entanto, a maior diferença está no facto de que o ambiente de execução é a Ethereum Virtual Machine (EVM). Todas as operações na EVM, bem como o ar­ma­ze­na­mento de dados, custam uma certa quan­ti­dade de «gas». Portanto, ao programar, pode ser ne­ces­sá­rio ponderar como im­ple­men­tar uma operação da forma mais eficiente possível.

Além das variáveis «normais», o Solidity reconhece cons­tan­tes que devem ser definidas na com­pi­la­ção. As cons­tan­tes requerem menos gás para serem ar­ma­ze­na­das:

// Regular variable can be declared without defining
int a;
// Constant needs to be defined at declaration
int constant b = 51;
solidity

A situação das variáveis inmutables é se­me­lhante. Estas consomem menos gás e não permitem ser mo­di­fi­ca­das após terem sido atri­buí­das um valor. Ao contrário das variáveis constantes, é possível atribuir-lhes um valor durante a execução.

Es­tru­tu­ras de controlo em Solidity

Solidity é uma linguagem de pro­gra­ma­ção im­pe­ra­tiva, portanto, suporta as ins­tru­ções de controlo habituais, por exemplo, con­di­ci­o­nais e loops. A seguir, mostramos o código para escolher o maior de dois números, a ou b:

int largerNumber = 0;
// If-else statement
if (a > b) {
    largerNumber = a;
} else {
        largerNumber = b;
}
solidity

O loop for no Solidity é se­me­lhante à sintaxe em Ja­vaS­cript ou C++:

// Loop 10 times
for (int i = 0; i < 10; i++) {
    // …
}
solidity

O loop while também segue a mesma estrutura. Com­bi­na­mos uma condição de in­ter­rup­ção com uma variável numérica para o contador:

bool continueLoop = true;
int counter = 0;
// Loop at most 10 times
while (continueLoop && counter < 10) {
    // …
    counter++;
}
solidity

Tipos simples em Solidity

Solidity é uma linguagem de tipagem estática e suporta os tipos de variáveis comuns às lin­gua­gens de pro­gra­ma­ção. Os tipos de dados mais simples que re­pre­sen­tam valores in­di­vi­du­ais são os booleanos, numéricos e strings (cadeias de ca­rac­te­res), entre outros.

Os booleanos em Solidity podem assumir o valor true ou false. Podem ser com­bi­na­dos entre si com os ope­ra­do­res booleanos co­nhe­ci­dos e uti­li­za­dos em sentenças if:

bool paymentReceived = true;
bool itemsStocked = true;
bool continueTransaction = paymentReceived && itemsStocked;
if (continueTransaction) {
    // ...
}
solidity

O Solidity suporta uma ampla gama de tipos de números. No caso dos números inteiros, distingue-se entre números com sinal (int) e sem sinal (uint) (estes últimos são sempre positivos). Além disso, o valor de um número pode ser re­pre­sen­tado em in­cre­men­tos de 8 bits, de int8 e int16 a int265:

uint8 smallNumber = 120;
int8 negativeNumber = -125;
int8 result = smallNumber + negativeNumber;
assert(result == -5)
solidity

As cadeias de ca­rac­te­res em Solidity são usadas prin­ci­pal­mente para criar mensagens de estado. A linguagem de pro­gra­ma­ção suporta aspas simples e duplas, bem como o padrão de ca­rac­te­res Unicode:

string message = 'Hello World';
string success = unicode"Transfer sent";
Solidity

Funções em Solidity

Tal como na maioria das lin­gua­gens de pro­gra­ma­ção, as funções são uma parte essencial do Solidity. As funções são definidas de forma se­me­lhante ao Ja­vaS­cript, pelo que é ne­ces­sá­rio es­pe­ci­fi­car cada tipo de argumento. Além disso, utiliza-se a palavra-chave returns para indicar o tipo de valor obtido como resposta:

// Define a function
function addNumbers(int a, int b) returns (int) {
    return a + b;
}
solidity

As chamadas para uma função são feitas como de costume:

// Call the function
int result = addNumbers(2, 3);
solidity

Cu­ri­o­sa­mente, de forma análoga aos ar­gu­men­tos com nome, os valores de retorno também podem ter nome. Neste caso, basta atribuir as variáveis cor­res­pon­den­tes no corpo da função, não sendo ne­ces­sá­rio in­tro­du­zir um retorno através de return:

function divideNumbers(int dividend, int divisor) returns (int quotient) {
    quotient = dividend / divisor;
    // No `return` necessary
}
solidity

Se­me­lhante às variáveis constant ou immutable, as funções no Solidity podem ser marcadas para não alterarem o estado. Para isso, são uti­li­za­das as palavras-chave view e pure. Uma função view não altera o estado, enquanto uma função pure garante também não ler variáveis de estado.

Contratos in­te­li­gen­tes em Solidity

Além dos tipos habituais, o Solidity possui vários tipos es­pe­ci­a­li­za­dos em contratos in­te­li­gen­tes. O tipo mais comum é address, que atribui endereços Ethereum, onde os endereços payable podem receber trans­fe­rên­cias em Ether. Para isso, os endereços payable utilizam os métodos balance() e transfer().

// Get address of this contract
address mine = address(this);
// Get payable external address
address payable other = payable(0x123);
// Transfer if balances fulfill conditions
if (other.balance < 10 && mine.balance >= 100) {
    other.transfer(10);
}
solidity

A partir do tipo address, surge o tipo contract como estrutura essencial da linguagem. Os contratos equivalem às classes nas lin­gua­gens de pro­gra­ma­ção ori­en­ta­das a objetos. Eles são res­pon­sá­veis por agrupar uma coleção de dados de estado e funções e protegê-los do mundo exterior. Os contratos contam com herança múltipla, como em Python ou C++. Os contratos nor­mal­mente começam com uma linha pragma que indica a versão permitida do Solidity, seguida da definição do contrato pro­pri­a­mente dito:

// Make sure Solidity version matches
pragma Solidity >=0.7.1 <0.9.0;
// Contract definition
contract Purchase {
    // Public state variables
    address seller;
    address buyer;
    
    // View-function
    function getSeller() external view returns (address) {
        return seller;
    }
}
solidity

Os contratos in­te­li­gen­tes podem definir dados de estado e funções. Tal como em C++ e Java, é possível definir um dos três níveis de acesso em cada caso:

  • public: é possível aceder à variável a partir do contrato com per­mis­sões de leitura e escrita. Além disso, é criada au­to­ma­ti­ca­mente uma função view como getter (cap­tu­ra­dor) para poder ler a variável a partir do exterior.
  • internal: a variável está protegida contra qualquer acesso externo. É possível acessá-la com direitos de leitura e escrita, tanto a partir do contrato quanto dos contratos herdeiros.
  • private: é se­me­lhante a internal, mas não é possível aceder à variável a partir dos contratos herdeiros.

As funções também podem ser clas­si­fi­ca­das como external. Uma função external funciona como parte da interface do contrato e é utilizada para acesso externo. A função receive é uma forma bem conhecida de receber Ether:

// Define without `function` keyword
receive() external payable {
    // Handle Ether
}
solidity

Mo­di­fi­ca­do­res em Solidity

Os mo­di­fi­ca­do­res no Solidity permitem-lhe utilizar uma sintaxe mais precisa. Os mo­di­fi­ca­do­res funcionam de forma se­me­lhante aos de­co­ra­do­res do Python: tal como os de­co­ra­do­res, os mo­di­fi­ca­do­res são uti­li­za­dos para modificar a chamada a uma função. São fre­quen­te­mente uti­li­za­dos para verificar se uma condição é cumprida antes de executar uma função:

contract Sale {
    uint price;
    address payable owner;
    modifier onlyOwner {
        // Will throw error if called by anyone other than the owner
        require(
            msg.sender == owner,
            "Only owner can call this function."
        );
        // The wrapped function's body is inserted here
        _;
    }
    
    // `onlyOwner` wraps `changePrice`
    function changePrice(uint newPrice) public onlyOwner {
        // We'll only get here if the owner called this function
        price = newPrice;
    }
}
solidity

Gestão de tran­sa­ções com Solidity

O Solidity possui uma gestão de tran­sa­ções integrada, per­mi­tindo verificar se uma trans­fe­rên­cia de Ether foi pro­ces­sada in­te­gral­mente ou não foi pro­ces­sada de todo. A linguagem utiliza a palavra-chave revert, que provoca o «roll-back» de uma transação. A palavra-chave error permite definir os seus próprios códigos de erro:

// Custom error definition
error InsufficientPayment(uint256 paid, uint256 required);
// Contract representing a sale
contract Sale {
    uint price;
    // Purchase if enough ether transferred
    function purchase() public payable {
        if (msg.value < price) {
            revert InsufficientPayment(msg.value, price);
        }
        // Complete purchase
    }
}
solidity

Outro padrão frequente é o uso da função require(), que pode ser utilizada de forma se­me­lhante à revert:

// Using `require()` function
if (!condition) revert("Error message");
// Equivalent to
require(condition, "Error message");
solidity
Ir para o menu principal