Solidity può essere uti­liz­za­to per pro­gram­ma­re so­fi­sti­ca­ti contratti in­tel­li­gen­ti da uti­liz­za­re sulla bloc­k­chain Ethereum. Offre alcuni approcci in­te­res­san­ti che lo di­stin­guo­no da altri linguaggi.

Cos’è Solidity?

Solidity è un lin­guag­gio di pro­gram­ma­zio­ne di alto livello per la creazione di contratti in­tel­li­gen­ti sulla bloc­k­chain Ethereum. I contratti in­tel­li­gen­ti (in inglese: “smart contracts”) sono accordi auto-esecutivi che au­to­ma­tiz­za­no lo scambio di beni tra le parti. Ciò che li rende speciali è che non sono necessari in­ter­me­dia­ri per garantire il rispetto di tale contratto.

Il codice sorgente di Solidity viene compilato in bytecode e messo a di­spo­si­zio­ne sulla bloc­k­chain Ethereum come contratto in­tel­li­gen­te. Dopodiché quest’ultimo può essere eseguito da qualsiasi nodo della rete e lo stato viene me­mo­riz­za­to sulla bloc­k­chain. Di seguito ti mostriamo un esempio di un semplice contratto che modella un di­stri­bu­to­re au­to­ma­ti­co di 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

Per quali ap­pli­ca­zio­ni viene uti­liz­za­to Solidity?

Solidity è spe­ci­fi­ca­men­te pro­get­ta­to per la creazione di ap­pli­ca­zio­ni di­stri­bui­te, DApp (“Di­stri­bu­ted Ap­pli­ca­tions”), in ese­cu­zio­ne sulla macchina virtuale di Ethereum (EVM). I contratti in­tel­li­gen­ti sono adatti, tra l’altro, alla gestione di beni digitali, alla creazione di scambi de­cen­tra­liz­za­ti e all’im­ple­men­ta­zio­ne di sistemi di voto.

Web Hosting
Diventa il n°1 della rete con il provider di hosting n°1 in Europa
  • Di­spo­ni­bi­li­tà garantita al 99,99%
  • Dominio, SSL ed e-mail inclusi
  • As­si­sten­za 24/7 in lingua italiana

Finanza de­cen­tra­liz­za­ta (DeFi)

Solidity viene uti­liz­za­to per svi­lup­pa­re ap­pli­ca­zio­ni DeFi come borse de­cen­tra­liz­za­te, piat­ta­for­me di scambio e di prestiti, mercati di pre­vi­sio­ne e crip­to­va­lu­te. La DeFi è diventata uno dei casi d’uso più popolari per la tec­no­lo­gia bloc­k­chain, rendendo Solidity uno strumento in­di­spen­sa­bi­le per costruire ap­pli­ca­zio­ni DeFi sulla rete Ethereum.

Non-Fungible Token

I Non-Fungible Token (NFT), in italiano “gettone non fungibile”, sono beni digitali unici me­mo­riz­za­ti sulla bloc­k­chain, diventati molto popolari a partire dal 2020. Possono essere opere d’arte digitali, cimeli sportivi o manufatti nei vi­deo­gio­chi. Solidity viene uti­liz­za­to per creare smart contract che ali­men­ta­no gli NFT.

Gestione della catena di ap­prov­vi­gio­na­men­to

Solidity può essere uti­liz­za­to per creare contratti in­tel­li­gen­ti per il mo­ni­to­rag­gio e la gestione delle catene di ap­prov­vi­gio­na­men­to. I contratti vengono uti­liz­za­ti per au­to­ma­tiz­za­re vari processi di questa catena. Tra questi rientrano il mo­ni­to­rag­gio del movimento delle merci, la verifica dell’au­ten­ti­ci­tà dei prodotti e l’ela­bo­ra­zio­ne dei pagamenti tra le parti.

Sistemi di voto

Grazie a Solidity si creano contratti in­tel­li­gen­ti che im­ple­men­ta­no sistemi di voto sicuri e tra­spa­ren­ti sulla bloc­k­chain. Questi ultimi possono essere uti­liz­za­ti per garantire che i voti siano contati cor­ret­ta­men­te e che il processo di voto sia equo e tra­spa­ren­te.

Quali sono i vantaggi e gli svantaggi di Solidity?

Nel complesso, Solidity è un lin­guag­gio potente per creare contratti in­tel­li­gen­ti sulla bloc­k­chain di Ethereum. Tuttavia, come ogni tec­no­lo­gia, vi sono vantaggi e svantaggi specifici di cui gli svi­lup­pa­to­ri e le svi­lup­pa­tri­ci do­vreb­be­ro essere con­sa­pe­vo­li. In ogni caso, lo sviluppo di contratti in­tel­li­gen­ti sicuri richiede un certo livello di com­pe­ten­za e at­ten­zio­ne.

A titolo di esempio, ti mostriamo uno smart contract che agisce come un buco nero: qualsiasi Ether inviato al contratto viene in­ghiot­ti­to ir­ri­me­dia­bil­men­te; non c’è quindi alcuna pos­si­bi­li­tà di versare nuo­va­men­te gli 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

Vantaggi di Solidity

  • Fles­si­bi­li­tà: Solidity è un lin­guag­gio versatile. Può essere uti­liz­za­to per svi­lup­pa­re contratti in­tel­li­gen­ti molto diversi tra loro e con una varietà di casi d’uso.
  • Sicurezza: Solidity è stato pro­get­ta­to tenendo conto della sicurezza. Il lin­guag­gio include ca­rat­te­ri­sti­che come i controlli di accesso, la gestione delle eccezioni e i mec­ca­ni­smi contro i mal­fun­zio­na­men­ti per aiutare gli svi­lup­pa­to­ri e le svi­lup­pa­tri­ci a scrivere contratti sicuri.
  • Com­pa­ti­bi­li­tà con Ethereum: Solidity è il lin­guag­gio at­tual­men­te prio­ri­ta­rio per la creazione di contratti in­tel­li­gen­ti sulla bloc­k­chain di Ethereum.
  • Community ben affermata: intorno a Solidity esiste una grande community di svi­lup­pa­to­ri e svi­lup­pa­tri­ci di bloc­k­chain. Ciò significa che esistono numerose risorse per imparare e risolvere i problemi.

Svantaggi di Solidity

  • Fase di ap­pren­di­men­to: Solidity richiede un certo tempo di ap­pren­di­men­to per gli svi­lup­pa­to­ri e le svi­lup­pa­tri­ci che si af­fac­cia­no per la prima volta allo sviluppo di bloc­k­chain e smart contract.
  • In­va­ria­bi­li­tà: dopo che un contratto in­tel­li­gen­te viene di­stri­bui­to sulla bloc­k­chain, non può essere ul­te­rior­men­te mo­di­fi­ca­to. Ne consegue che gli svi­lup­pa­to­ri e le svi­lup­pa­tri­ci devono prestare estrema at­ten­zio­ne durante la scrittura e la fase di test.
  • Verifica formale mancante: Solidity non dispone di strumenti integrati per la revisione formale del codice. Ciò significa che gli svi­lup­pa­to­ri e le svi­lup­pa­tri­ci devono affidarsi a strumenti esterni per garantire la cor­ret­tez­za dei loro contratti.
  • Strumenti limitati: l’eco­si­ste­ma di strumenti di Solidity è ancora re­la­ti­va­men­te poco svi­lup­pa­to. Ne con­se­guo­no pertanto problemi con IDE, framework di test e altri strumenti di sviluppo.

Qual è la sintassi di base di Solidity?

Solidity è un lin­guag­gio orientato agli oggetti spe­cia­liz­za­to in contratti in­tel­li­gen­ti e in­fluen­za­to da Ja­va­Script, Python e C++. La sintassi del lin­guag­gio è simile a quella di Ja­va­Script, anche se ci sono alcune in­te­res­san­ti pe­cu­lia­ri­tà.

Variabili in Solidity

A prima vista, le variabili sembrano fun­zio­na­re in Solidity allo stesso modo dei linguaggi analoghi. Tuttavia, un’im­por­tan­te dif­fe­ren­za deriva dal fatto che, come ambiente di ese­cu­zio­ne, viene uti­liz­za­ta la Ethereum Virtual Machine (EVM). Tutte le ope­ra­zio­ni sulla EVM e la me­mo­riz­za­zio­ne dei dati costano una certa quantità di “gas”. Ciò significa che, in fase di pro­gram­ma­zio­ne, può essere ne­ces­sa­rio valutare come im­ple­men­ta­re un’ope­ra­zio­ne nel modo più ef­fi­cien­te possibile.

Oltre alle variabili “normali”, Solidity conosce delle costanti che devono essere definite durante la com­pi­la­zio­ne. Queste ultime ri­chie­do­no meno energia per la me­mo­riz­za­zio­ne:

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

La si­tua­zio­ne è simile con le variabili immutable. Anche queste consumano meno energia e non per­met­to­no di essere mo­di­fi­ca­te dopo l’as­se­gna­zio­ne. A dif­fe­ren­za delle variabili constant, l’as­se­gna­zio­ne può avvenire in fase di ese­cu­zio­ne.

Di­chia­ra­zio­ni di controllo in Solidity

In qualità di lin­guag­gio di pro­gram­ma­zio­ne im­pe­ra­ti­vo, Solidity supporta le note istru­zio­ni di controllo, come rami e cicli. Ti mostriamo il codice per se­le­zio­na­re il più grande tra due numeri, a e b:

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

Il ciclo for in Solidity cor­ri­spon­de alla sintassi nota di Ja­va­Script o C++:

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

Anche il ciclo while funziona come al solito. Com­bi­nia­mo una con­di­zio­ne di ter­mi­na­zio­ne con una variabile numerica counter:

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

Solidity è un lin­guag­gio a ti­piz­za­zio­ne statica e supporta i tipi co­mu­ne­men­te presenti nei linguaggi di pro­gram­ma­zio­ne. I tipi semplici che rap­pre­sen­ta­no valori singoli includono booleani, numeri e stringhe.

I booleani in Solidity sono rap­pre­sen­ta­ti dai valori true e false. Possono essere collegati tramite i relativi operatori booleani noti e uti­liz­za­ti nelle istru­zio­ni if:

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

Solidity supporta un’ampia gamma di tipi numerici. Nel caso dei numeri interi, si può fare una di­stin­zio­ne tra numeri firmati (int) e non firmati (uint), dove questi ultimi possono essere solo positivi. Inoltre, l’in­ter­val­lo di un numero può essere definito in passi di 8 bit, da int8 passando per int16 fino a int265:

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

Le stringhe sono usate prin­ci­pal­men­te in Solidity per creare messaggi di stato. Il lin­guag­gio supporta gli apici singoli e doppi e i caratteri Unicode:

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

Funzioni in Solidity

Come nella maggior parte dei linguaggi di pro­gram­ma­zio­ne, le funzioni sono una parte centrale di Solidity. La de­fi­ni­zio­ne di una funzione ricorda Ja­va­Script, dove i tipi di argomenti devono essere spe­ci­fi­ca­ti espli­ci­ta­men­te. Inoltre, viene uti­liz­za­ta la parola chiave returns per indicare i tipi dei valori di ritorno:

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

La chiamata di una funzione viene eseguita come di consueto:

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

È in­te­res­san­te notare che ana­lo­ga­men­te agli argomenti nominati, anche i valori di ritorno possono essere nominati. In questo caso, è suf­fi­cien­te assegnare le variabili cor­ri­spon­den­ti nel corpo della funzione. Non è quindi ne­ces­sa­rio un ritorno esplicito tramite return:

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

In maniera simile alle variabili constant o immutable, in Solidity le funzioni possono essere con­tras­se­gna­te come non mutevoli. Qui si usano le parole chiave view e pure. Una funzione view non cambia lo stato, mentre pure ga­ran­ti­sce di non leggere le variabili di stato.

Contratti in­tel­li­gen­ti in Solidity

Oltre ai tipi comuni, Solidity conosce una manciata di tipologie spe­cia­liz­za­te per gli smart contract. Il tipo di base è address e mappa gli indirizzi Ethereum. Gli indirizzi che sono payable possono ricevere tra­sfe­ri­men­ti in Ether. A tal fine, gli indirizzi payable for­ni­sco­no i metodi 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

Partendo dal tipo address, esiste il tipo contract come costrutto centrale del lin­guag­gio. I contratti cor­ri­spon­do­no all’incirca alle classi dei linguaggi di pro­gram­ma­zio­ne orientati agli oggetti. Pertanto, i contratti rag­grup­pa­no i dati di stato e le funzioni, pro­teg­gen­do­li dal mondo esterno. In più, sup­por­ta­no l’ere­di­ta­rie­tà multipla, come quella co­no­sciu­ta in Python o C++.

Di solito iniziano con una riga pragma che specifica la versione di Solidity con­sen­ti­ta, seguita dalla de­fi­ni­zio­ne vera e propria:

// 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

I contratti in­tel­li­gen­ti possono definire dati e funzioni di stato. Come noto da C++ e Java, in ogni caso è possibile definire uno dei tre livelli di accesso:

  • public: la variabile è ac­ces­si­bi­le dall’interno del contratto in lettura e scrittura. Inoltre, viene au­to­ma­ti­ca­men­te creata una funzione view come getter per l’accesso in lettura dall’esterno.
  • internal: la variabile è protetta da qualsiasi accesso esterno. L’accesso in lettura e scrittura è possibile dall’interno del contratto e da quelli ereditati.
  • private: come internal, ma non c’è accesso da parte dei contratti ereditari.

Le funzioni possono anche essere definite esterne. Una funzione external agisce come parte dell’in­ter­fac­cia del contratto e viene uti­liz­za­ta per l’accesso dall’esterno. L’im­por­tan­te funzione receive per la ricezione di Ether è un esempio ben noto:

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

Mo­di­fi­ca­to­ri in Solidity

Con i mo­di­fi­ca­to­ri (“modifiers” in inglese), in Solidity esiste un in­te­res­san­te costrutto lin­gui­sti­co. La fun­zio­na­li­tà ricorda i de­co­ra­to­ri di Python; come in quel caso, i mo­di­fi­ca­to­ri sono usati per mo­di­fi­ca­re la chiamata di una funzione e per garantire che una con­di­zio­ne sia sod­di­sfat­ta prima di eseguire una funzione:

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

Gestione delle tran­sa­zio­ni con Solidity

Solidity dispone di una gestione delle tran­sa­zio­ni integrata. Ciò consente di garantire che un tra­sfe­ri­men­to di dati sia elaborato com­ple­ta­men­te o non sia elaborato affatto. Il lin­guag­gio conosce la parola chiave speciale revert, che attiva un “roll-back” di una tran­sa­zio­ne. La parola chiave error può essere usata per definire i propri codici di errore:

// 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

Un altro schema frequente è ri­con­du­ci­bi­le all’uso della funzione require(), che può essere usata in modo analogo a revert:

// Using `require()` function
if (!condition) revert("Error message");
// Equivalent to
require(condition, "Error message");
solidity
Vai al menu prin­ci­pa­le