f in x
Solidity da zero: Variabili, Funzioni, Eventi e Pattern di Sicurezza — Scrivi Smart Contract che non Perdono Soldi
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Trend emergenti e tecnologie

Solidity da zero: Variabili, Funzioni, Eventi e Pattern di Sicurezza — Scrivi Smart Contract che non Perdono Soldi

[2026-06-21] Author: Ing. Calogero Bono

Hai mai visto uno smart contract andare in fumo per una semplice rientranza? Noi sì. Un cliente aveva un pool DeFi con un bug di reentrancy: in pochi secondi perso il 40% del liquidity pool. Era un contratto scritto da un team 'esperto', ma senza pattern di sicurezza. Ecco perché abbiamo deciso di scrivere questa guida. Non vogliamo che tu perda soldi (e credibilità).

Questa guida parte da zero: variabili, funzioni, eventi. Poi entra nei pattern di sicurezza che ogni sviluppatore Solidity deve conoscere. Non è un corso di teoria — è quello che noi di Meteora Web abbiamo applicato in progetti reali, dopo anni di contabilità e gestione ERP: ogni riga di codice ha un costo, ogni bug può bruciare capitale. Parliamo di roba concreta.

Come si dichiarano variabili, funzioni ed eventi in Solidity?

Variabili di stato e tipi base

In Solidity le variabili di stato sono memorizzate sulla blockchain. Ogni scrittura costa gas. Ecco un esempio minimo:

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

contract Base {
    // variabile di stato pubblica – genera automaticamente un getter
    uint256 public count;
    // variabile privata – accessibile solo all'interno del contratto
    address private owner;
    // variabile immutabile – impostata una sola volta al deploy
    uint256 public immutable MAX_SUPPLY = 1000;
}

Attenzione: usa uint256 invece di uint per chiarezza. Evita var implicito (deprecato). I tipi address e address payable sono diversi: solo il secondo può ricevere ether.

Sponsored Protocol

Funzioni: visibilità e modificatori

Le funzioni in Solidity hanno quattro livelli di visibilità: public, internal, external, private. Attenzione a public vs external: external costa meno gas per chiamate esterne perché i parametri non vengono copiati in memoria.

contract Funzioni {
    uint256 public data;

    // funzione esterna – chiamabile solo dall'esterno
    function setData(uint256 _newData) external {
        data = _newData;
    }

    // funzione pubblica – chiamabile anche internamente
    function getData() public view returns (uint256) {
        return data;
    }

    // funzione interna – solo contratti derivati
    function _internalHelper() internal pure returns (string memory) {
        return "solo figli";
    }
}

Modificatori di funzione: view (non modifica stato), pure (non legge né scrive stato), payable (può ricevere ether). Usali sempre per documentare e ottimizzare il gas.

Eventi: tracciare le modifiche sulla chain

Gli eventi permettono di loggare azioni in modo efficiente (più economico delle variabili). I client (frontend, DApp) li ascoltano per reagire.

contract Events {
    event Transfer(address indexed from, address indexed to, uint256 value);

    function transfer(address _to, uint256 _value) external {
        // logica di trasferimento
        emit Transfer(msg.sender, _to, _value);
    }
}

I parametri indexed (massimo 3) permettono di filtrare gli eventi. Gli eventi non sono accessibili on-chain, ma sono fondamentali per l'auditing.

Sponsored Protocol

Quali sono i pattern di sicurezza indispensabili per uno smart contract?

Dopo anni a gestire vulnerabilità (anche in progetti che ci sono arrivati per riparazioni), abbiamo tre pattern che consideriamo obbligatori.

1. Checks-Effects-Interactions (CEI)

È il pattern numero uno: prima controlli le condizioni (checks), poi modifichi lo stato (effects), infine interagisci con contratti esterni (interactions). Previene il reentrancy.

contract CEIPattern {
    mapping(address => uint256) public balances;

    function withdraw() external {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "no balance");
        // effects
        balances[msg.sender] = 0;
        // interactions
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "transfer failed");
    }
}

Vedi? Abbiamo azzerato il saldo prima di inviare ether. Se il mittente è un contratto malevolo e richiama withdraw nel suo receive, il saldo è già zero. Non può rientrare.

2. Guard Check con modificatori personalizzati

Ripetere require(owner == msg.sender) ovunque è noioso e rischioso. Crea modificatori (modifier) riutilizzabili.

contract GuardCheck {
    address public owner;

    modifier onlyOwner() {
        require(owner == msg.sender, "not owner");
        _;
    }

    modifier nonZeroAddress(address _addr) {
        require(_addr != address(0), "zero address");
        _;
    }

    function setOwner(address _newOwner) external onlyOwner nonZeroAddress(_newOwner) {
        owner = _newOwner;
    }
}

Regola operativa: usa _ per rappresentare il corpo della funzione. I modificatori si concatenano: vengono eseguiti in ordine di dichiarazione.

Sponsored Protocol

3. Pausabilità e emergency stop

Quando un bug viene scoperto, devi poter fermare il contratto. Il pattern più diffuso è ereditare da OpenZeppelin's Pausable.

import "@openzeppelin/contracts/security/Pausable.sol";

contract MyContract is Pausable {
    function doSomething() external whenNotPaused {
        // logica
    }

    function pause() external onlyOwner {
        _pause();
    }

    function unpause() external onlyOwner {
        _unpause();
    }
}

Usa whenNotPaused per le funzioni critiche. Il proprietario può mettere in pausa il contratto in emergenza. Ma attenzione: l'owner diventa un punto di centralizzazione. Valuta se usare un multisig per la pausa.

Come prevenire attacchi comuni come il reentrancy o l'overflow?

Reentrancy: oltre al pattern CEI

Il reentrancy classico è risolto da CEI, ma esistono varianti come il reentrancy cross-funzione. Usa un blocco di reentrancy (mutex) per sicurezza extra:

Sponsored Protocol

contract ReentrancyGuard {
    bool private locked;

    modifier noReentrant() {
        require(!locked, "reentrant call");
        locked = true;
        _;
        locked = false;
    }

    function withdraw() external noReentrant {
        // logica
    }
}

OpenZeppelin fornisce ReentrancyGuard già testato. Noi lo consigliamo sempre su contratti che gestiscono fondi.

Overflow e underflow

Prima di Solidity 0.8, gli overflow causavano wraparound. Ora di default il compilatore controlla gli overflow e reverta. Ma attenzione: in blocchi unchecked i controlli sono disattivati (utili per risparmiare gas).

function safeMath() external pure {
    // in Solidity 0.8+ questo reverta se overflow
    uint256 a = type(uint256).max;
    uint256 b = 1;
    // revert: a + b overflow
    // uint256 c = a + b;

    // unchecked permette overflow ma attenzione
    unchecked {
        uint256 c = a + b; // c = 0 (wraparound)
    }
}

Regola: usa unchecked solo dove sei sicuro che l'overflow non possa accadere (es. incrementi che sai non supereranno un limite). Altrimenti lascia i controlli automatici.

Access control: non solo owner

Il pattern onlyOwner è semplice, ma spesso insufficiente. Per ruoli più granulari usa OpenZeppelin's AccessControl.

import "@openzeppelin/contracts/access/AccessControl.sol";

contract RoleBased is AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER");

    constructor() {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);
    }

    function mint() external onlyRole(MINTER_ROLE) {
        // logica
    }
}

Questo permette di delegare la gestione a più account, revocare ruoli, e avere un audit trail on-chain dei permessi. Molto più robusto di un singolo owner.

Sponsored Protocol

Cosa fare adesso

Non fermarti alla lettura. Ecco tre azioni concrete:

  1. Scrivi un contratto test su Remix o Hardhat. Implementa i tre pattern (CEI, modificatori, pausa) e verifica con un attacco di reentrancy simulato. Il nostro consiglio: usa il tutorial ufficiale di Hardhat per ambienti locali.
  2. Integra OpenZeppelin Contracts nel tuo progetto. Non reinventare la ruota: usa le librerie ufficiali per ReentrancyGuard, AccessControl, Pausable, SafeERC20 (per token). Riduci il rischio di bug a zero.
  3. Esegui un audit preventivo prima del deploy. Noi, di Meteora Web, abbiamo visto contratti con vulnerabilità che un semplice static analyzer (Slither) avrebbe trovato in 10 secondi. Fallo eseguire su ogni contratto.

Ricorda: ogni variabile non inizializzata, ogni call non verificato, ogni owner senza multisig è un potenziale incidente. Tratta il codice come denaro — perché su blockchain lo è davvero.

Se vuoi approfondire, leggi la nostra Pillar su Blockchain e Web3.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // WEB AGENCY

Costruiamo la presenza digitale che la tua azienda merita.

Siti web, social, pubblicità online, e-commerce e hosting performante: ingegnerizzati con metodo da ingegneri informatici a Sciacca, per tutta Italia.

> MW_JOURNAL

> READ_ALL()