Salta el contingut
Logo esquerra

SESSIÓ 2 - UD2.2: FUNCIONS I MODIFICADORS - Solidity

Setmana 2 (27 abril - 3 maig) - 2 hores


FITXA TÈCNICA

Dada Valor
Unitat UD2 - Smart Contracts
Tema Funcions, visibilitat i modificadors
Durada 2 hores
Nivell Inicial-Intermedi
Eines Remix IDE, MetaMask
Requisits Sessió 1 completada

OBJECTIUS D'APRENENTATGE

Al finalitzar aquesta sessió, seràs capaç de:

  1. ✅ Diferenciar i utilitzar correctament els modificadors de visibilitat (public, private, internal, external)
  2. ✅ Implementar modificadors d'estat (view, pure, payable)
  3. ✅ Crear i utilitzar modificadors personalitzats
  4. ✅ Implementar control d'accés amb onlyOwner
  5. ✅ Utilitzar constructors amb paràmetres
  6. ✅ Emetre i consultar events
  7. ✅ Aplicar les millores al contracte de calculadora

TEMPORITZACIÓ DE LA SESSIÓ

Temps Activitat Metodologia
0-10 min Revisió sessió anterior Q&A + dubtes
10-30 min Teoria: Visibilitat i modificadors Exposició + exemples
30-100 min Pràctica guiada: Gestió d'usuaris Codificació conjunta
100-120 min Exercici: Calculadora amb seguretat Pràctica individual

MATERIAL TEÒRIC

1. Modificadors de Visibilitat

Els modificadors de visibilitat determinen des d'on es pot cridar una funció o accedir a una variable.

Taula Comparativa:

Visibilitat Contracte Derivats Extern Ús Típic
public Funcions principals, getters
private Lògica interna sensible
internal Funcions per herència
external Funcions per altres contracts

Exemple Pràctic:

contract VisibilitatExemple {

    uint256 public valorPublic;      // Getter automàtic
    uint256 private valorPrivate;    // Només aquest contracte
    uint256 internal valorInternal;  // Aquest contracte + fills

    // PUBLIC: Tothom pot cridar
    function funcioPublica() public pure returns (string memory) {
        return "Public - Tothom pot cridar";
    }

    // PRIVATE: Només aquest contracte
    function funcioPrivate() private pure returns (string memory) {
        return "Private - Només intern";
    }

    // INTERNAL: Aquest contracte i derivats
    function funcioInternal() internal pure returns (string memory) {
        return "Internal - Contracte i fills";
    }

    // EXTERNAL: Només des de fora (més eficient per arrays)
    function funcioExterna() external pure returns (string memory) {
        return "External - Només crida externa";
    }

    // Funció que crida funcions internes
    function cridarInterna() public pure returns (string memory) {
        return funcioInternal();  // ✅ Funciona
    }

    // Aquesta NO funcionaria:
    // function cridarExterna() public pure returns (string memory) {
    //     return funcioExterna();  // ❌ Error: external function
    // }
}

Nota important sobre external:

  • Més eficient en gas per a paràmetres grans (arrays, strings)
  • No es pot cridar des del mateix contracte directament
  • Cal usar this.funcioExterna() si es vol cridar internament

2. Modificadors d'Estat (State Mutability)

Indiquen com la funció interactua amb l'estat de la blockchain.

Modificador Llegeix Estat Modifica Estat Rep ETH Gas Exemple
view Gratis getBalance()
pure Gratis calculate(a, b)
payable Paga deposit()
(cap) Paga transfer()

Exemples Detallats:

contract ModificadorsEstat {

    uint256 public valor = 100;
    mapping(address => uint256) public saldos;

    // VIEW: Llegeix estat, no modifica
    function obtenirValor() public view returns (uint256) {
        return valor;  // ✅ Llegeix variable d'estat
    }

    // PURE: No llegeix ni modifica estat
    function sumar(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b;  // ✅ Només opera amb paràmetres
    }

    // PAYABLE: Pot rebre Ether
    function dipositar() public payable {
        require(msg.value > 0, "Ha d'enviar Ether");
        saldos[msg.sender] += msg.value;
    }

    // SENSE MODIFICADOR: Pot modificar estat
    function actualitzarValor(uint256 nouValor) public {
        valor = nouValor;  // ✅ Modifica variable d'estat
    }

    // Error comú:
    // function incorrecte() public view returns (uint256) {
    //     valor = 200;  // ❌ Error: view function cannot modify state
    //     return valor;
    // }
}

3. Constructors amb Paràmetres

El constructor s'executa una sola vegada quan es desplega el contracte.

contract ContracteAmbConstructor {

    address public propietari;
    string public nom;
    uint256 public dataCreacio;
    uint256 public valorInicial;

    // Constructor amb múltiples paràmetres
    constructor(string memory _nom, uint256 _valorInicial) {
        propietari = msg.sender;           // Qui desplega
        nom = _nom;                         // Paràmetre
        dataCreacio = block.timestamp;     // Timestamp actual
        valorInicial = _valorInicial;      // Paràmetre
    }

    // Aquest constructor NO es pot cridar després del desplegament
    // function constructor(string memory _nom) public {  // ❌ Error!
    //     nom = _nom;
    // }
}

Quan es desplega:

Paràmetres: _nom = "El Meu Contracte", _valorInicial = 1000

Resultat:
- propietari: 0xYourAddress
- nom: "El Meu Contracte"
- dataCreacio: 1714000000 (timestamp)
- valorInicial: 1000


4. Modificadors Personalitzats

Els modificadors permeten reutilitzar codi de validació i control d'accés.

Sintaxi Bàsica:

modifier nomModificador() {
    // Codi abans de la funció
    require(condicio, "Missatge d'error");

    _;  // Executa la funció original

    // Codi després de la funció (opcional)
}

Exemples Pràctics:

contract ModificadorsPersonalitzats {

    address public propietari;
    bool public actiu = true;
    uint256 public comptador;
    mapping(address => bool) public usuarisAutoritzats;

    constructor() {
        propietari = msg.sender;
        usuarisAutoritzats[msg.sender] = true;
    }

    // 1. Control d'accés bàsic
    modifier nomesPropietari() {
        require(msg.sender == propietari, "No es el propietari");
        _;
    }

    // 2. Contracte actiu
    modifier contracteActiu() {
        require(actiu, "Contracte no actiu");
        _;
    }

    // 3. Usuari autoritzat
    modifier usuariAutoritzat() {
        require(usuarisAutoritzats[msg.sender], "Usuari no autoritzat");
        _;
    }

    // 4. Límit de valor
    modifier valorMaxim(uint256 maxim) {
        require(comptador + 1 <= maxim, "Limit assolit");
        _;
    }

    // 5. Validació de paràmetres
    parametreValid(string memory text) {
        require(bytes(text).length > 0, "Text buit");
        require(bytes(text).length <= 100, "Text massa llarg");
        _;
    }

    // Ús de modificadors
    function destruir() public nomesPropietari {
        selfdestruct(payable(propietari));
    }

    function afegirDada() 
        public 
        contracteActiu 
        usuariAutoritzat 
        valorMaxim(100)
    {
        comptador++;
    }

    function desactivar() public nomesPropietari {
        actiu = false;
    }

    function afegirUsuari(address nouUsuari) public nomesPropietari {
        usuarisAutoritzats[nouUsuari] = true;
    }

    // Múltiples modificadors en una funció
    function operacioCritica(string memory dada) 
        public 
        nomesPropietari 
        contracteActiu 
        parametreValid(dada)
    {
        // Lògica crítica
        comptador++;
    }
}

Important:

  • Els modificadors s'executen en ordre d'esquerra a dreta
  • _ indica on s'executa la funció original
  • Es poden combinar múltiples modificadors

5. Events

Els events permeten registrar informació a la blockchain que es pot consultar des de fora del contracte.

contract EventsExemple {

    address public propietari;
    uint256 public comptador;

    // Declaració d'events
    event ContracteCreat(address indexed propietari, uint256 timestamp);
    event ComptadorActualitzat(uint256 anticValor, uint256 nouValor);
    event PropietariCanviat(address indexed antic, address indexed nou);

    constructor() {
        propietari = msg.sender;
        emit ContracteCreat(msg.sender, block.timestamp);
    }

    function incrementar() public {
        uint256 antic = comptador;
        comptador++;

        emit ComptadorActualitzat(antic, comptador);
    }

    function canviarPropietari(address nouPropietari) public {
        require(msg.sender == propietari, "No es propietari");

        address antic = propietari;
        propietari = nouPropietari;

        emit PropietariCanviat(antic, nouPropietari);
    }
}

Indexed:

  • Fins a 3 paràmetres poden ser indexed
  • Permeten filtrar events eficientment
  • Més costós en gas

Consulta d'events:

// Des de JavaScript (Ethers.js)
contract.on("ComptadorActualitzat", (antic, nou, event) => {
    console.log(`Comptador: ${antic}${nou}`);
    console.log(`Bloc: ${event.blockNumber}`);
});

PRÀCTICA GUIADA PAS A PAS

EXERCICI 1: Contracte de Gestió d'Usuaris

Objectiu: Crear un sistema de gestió d'usuaris amb control d'accés.

Pas 1: Crear el Fitxer

  1. A Remix IDE, crea un nou fitxer: 02_GestioUsuaris.sol
  2. Copia l'estructura bàsica:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract GestioUsuaris {

    // COMPLETAREM AQUEST CONTRACTE JUNTS

}

Pas 2: Afegir Variables d'Estat

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

contract GestioUsuaris {

    // Estructura per a usuari
    struct Usuari {
        uint256 id;
        string nom;
        string email;
        uint256 edat;
        bool actiu;
        uint256 dataRegistre;
    }

    // Variables d'estat
    address public propietari;
    uint256 public totalUsuaris;
    uint256 public totalUsuarisActius;

    // Mappings
    mapping(uint256 => Usuari) public usuaris;      // id → Usuari
    mapping(address => uint256) public adresaId;   // address → id
    mapping(address => bool) public administradors; // address → bool

    // Events
    event UsuariRegistrat(uint256 indexed userId, address indexed adreca, string nom);
    event UsuariActualitzat(uint256 indexed userId, string nom, bool actiu);
    event UsuariEliminat(uint256 indexed userId);
    event AdministradorAfegit(address indexed admin);

    // ... continuarem
}

Pas 3: Constructor i Modificadors

contract GestioUsuaris {

    // ... (variables anteriors)

    constructor() {
        propietari = msg.sender;
        administradors[msg.sender] = true;
    }

    // Modificador: Només propietari
    modifier nomesPropietari() {
        require(msg.sender == propietari, "No es el propietari");
        _;
    }

    // Modificador: Només administrador
    modifier nomesAdministrador() {
        require(administradors[msg.sender], "No es administrador");
        _;
    }

    // Modificador: Usuari ha d'existir
    modifier usuariExisteix(uint256 userId) {
        require(userId > 0 && userId <= totalUsuaris, "Usuari no existeix");
        _;
    }

    // Modificador: Contracte actiu
    modifier contracteActiu() {
        require(totalUsuaris < 1000, "Limit maxim d'usuaris assolit");
        _;
    }
}

Pas 4: Funcions de Registre

contract GestioUsuaris {

    // ... (codi anterior)

    // Funció per registrar nou usuari
    function registrarUsuari(
        string memory _nom,
        string memory _email,
        uint256 _edat
    ) 
        public 
        contracteActiu 
        returns (uint256)
    {
        // Validacions
        require(bytes(_nom).length > 0, "Nom buit");
        require(bytes(_email).length > 0, "Email buit");
        require(_edat >= 18, "Ha de ser major d'edat");
        require(adresaId[msg.sender] == 0, unicode"Ja estàs registrat");

        // Crear nou usuari
        totalUsuaris++;
        uint256 nouId = totalUsuaris;

        usuaris[nouId] = Usuari({
            id: nouId,
            nom: _nom,
            email: _email,
            edat: _edat,
            actiu: true,
            dataRegistre: block.timestamp
        });

        // Mapejar adreça a ID
        adresaId[msg.sender] = nouId;
        totalUsuarisActius++;

        // Emitir event
        emit UsuariRegistrat(nouId, msg.sender, _nom);

        return nouId;
    }

    // Funció per obtenir les meves dades
    function obtenirElMeuUsuari() 
        public 
        view 
        returns (
            uint256 id,
            string memory nom,
            string memory email,
            uint256 edat,
            bool actiu,
            uint256 dataRegistre
        )
    {
        uint256 userId = adresaId[msg.sender];
        require(userId > 0, unicode"No estàs registrat");

        Usuari memory user = usuaris[userId];
        return (
            user.id,
            user.nom,
            user.email,
            user.edat,
            user.actiu,
            user.dataRegistre
        );
    }

    // Funció per obtenir usuari per ID (només admin)
    function obtenirUsuariPerId(uint256 userId)
        public
        view
        usuariExisteix(userId)
        returns (
            uint256 id,
            string memory nom,
            string memory email,
            uint256 edat,
            bool actiu
        )
    {
        Usuari memory user = usuaris[userId];
        return (
            user.id,
            user.nom,
            user.email,
            user.edat,
            user.actiu
        );
    }
}

Pas 5: Funcions d'Actualització i Eliminació

contract GestioUsuaris {

    // ... (codi anterior)

    // Actualitzar les meves dades
    function actualitzarPerfil(
        string memory _nom,
        string memory _email,
        uint256 _edat
    ) public {
        uint256 userId = adresaId[msg.sender];
        require(userId > 0, unicode"No estàs registrat");

        require(bytes(_nom).length > 0, "Nom buit");
        require(bytes(_email).length > 0, "Email buit");
        require(_edat >= 18, "Ha de ser major d'edat");

        Usuari storage user = usuaris[userId];
        user.nom = _nom;
        user.email = _email;
        user.edat = _edat;

        emit UsuariActualitzat(userId, _nom, user.actiu);
    }

    // Desactivar compte (usuari)
    function desactivarCompte() public {
        uint256 userId = adresaId[msg.sender];
        require(userId > 0, unicode"No estàs registrat");

        Usuari storage user = usuaris[userId];
        require(user.actiu, unicode"Ja està desactivat");

        user.actiu = false;
        totalUsuarisActius--;

        emit UsuariActualitzat(userId, user.nom, false);
    }

    // Reactivar compte (usuari)
    function reactivarCompte() public {
        uint256 userId = adresaId[msg.sender];
        require(userId > 0, unicode"No estàs registrat");

        Usuari storage user = usuaris[userId];
        require(!user.actiu, unicode"Ja està actiu");

        user.actiu = true;
        totalUsuarisActius++;

        emit UsuariActualitzat(userId, user.nom, true);
    }

    // Eliminar usuari (només admin)
    function eliminarUsuari(uint256 userId)
        public
        nomesAdministrador
        usuariExisteix(userId)
    {
        Usuari storage user = usuaris[userId];
        require(user.actiu, "Usuari ja inactiu");

        user.actiu = false;
        totalUsuarisActius--;

        // Opcional: eliminar mapeig
        // delete usuaris[userId];

        emit UsuariEliminat(userId);
    }

    // Afegir administrador (només propietari)
    function afegirAdministrador(address nouAdmin)
        public
        nomesPropietari
    {
        require(nouAdmin != address(0), "Adreça invalida");
        require(!administradors[nouAdmin], "Ja es administrador");

        administradors[nouAdmin] = true;
        emit AdministradorAfegit(nouAdmin);
    }

    // Estadístiques
    function obtenirEstadistiques()
        public
        view
        returns (
            uint256 total,
            uint256 actius,
            uint256 inactius
        )
    {
        return (
            totalUsuaris,
            totalUsuarisActius,
            totalUsuaris - totalUsuarisActius
        );
    }
}

Pas 6: Provar el Contracte

Seqüència de proves:

  1. Desplegar contracte

    Environment: Remix VM (Cancun)
    Account: Account 1 (propietari)
    

  2. Registrar primer usuari (Account 2)

    Funció: registrarUsuari
    Paràmetres:
    - _nom: "Anna Martinez"
    - _email: "anna@email.com"
    - _edat: 28
    
    Resultat esperat: userId = 1
    

  3. Consultar dades

    Funció: obtenirElMeuUsuari (des de Account 2)
    Resultat: 
    - id: 1
    - nom: "Anna Martinez"
    - email: "anna@email.com"
    - edat: 28
    - actiu: true
    

  4. Registrar segon usuari (Account 3)

    Paràmetres:
    - _nom: "Bernat Lopez"
    - _email: "bernat@email.com"
    - _edat: 35
    
    Resultat: userId = 2
    

  5. Consultar estadístiques

    Funció: obtenirEstadistiques
    Resultat:
    - total: 2
    - actius: 2
    - inactius: 0
    

  6. Actualitzar perfil (Account 2)

    Funció: actualitzarPerfil
    Paràmetres:
    - _nom: "Anna Martinez Garcia"
    - _email: "anna.m@email.com"
    - _edat: 29
    

  7. Desactivar compte (Account 2)

    Funció: desactivarCompte
    Resultat: actiu = false
    

  8. Consultar estadístiques de nou

    Resultat:
    - total: 2
    - actius: 1
    - inactius: 1
    

  9. Afegir administrador (Account 1 - propietari)

    Funció: afegirAdministrador
    Paràmetre: Account 3 (bernat@email.com)
    

  10. Eliminar usuari (Account 3 - ara admin)

    Funció: eliminarUsuari
    Paràmetre: userId = 2
    Resultat: usuari 2 eliminat
    


EXERCICI PROPOSAT: CALCULADORA AMB CONTROL D'ACCÉS

Enunciat:

Millora el contracte CalculadoraBasica de la sessió 1 afegint:

Requisits obligatoris:

  1. Control d'accés:

    • Afegir variable propietari (address)
    • Constructor que guardi msg.sender com a propietari
    • Modificador nomesPropietari
  2. Límits d'operacions:

    • Límit màxim de valor: 1,000,000
    • Modificador valorDinsLimit(uint256 valor) que validi
    • Missatge: "Valor fora de limit"
  3. Historial millorat:

    • Estructura Operacio:
      struct Operacio {
          uint256 id;
          address usuari;
          string tipus;  // "suma", "resta", "multiplicacio"
          uint256 operand1;
          uint256 operand2;
          uint256 resultat;
          uint256 timestamp;
      }
      
    • Array d'operacions realitzades
    • Mapping per consultar operacions per usuari
  4. Events:

    • OperacioRealitzada(uint256 indexed operacioId, address indexed usuari, string tipus, uint256 resultat)
    • LimitAssolit(address indexed usuari)
  5. Funcions noves:

    • obtenirHistorialUsuari() → retorna nombre d'operacions de qui crida
    • obtenirOperacio(uint256 id) → retorna detalls d'una operació
    • resetejarComptador() → només propietari, reseteja historial
    • canviarLimit(uint256 nouLimit) → només propietari

Template per començar:

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

contract CalculadoraSegura {

    uint256 public ultimResultat;
    uint256 public totalOperacions;
    address public propietari;
    uint256 public limitMaxim;

    struct Operacio {
        uint256 id;
        address usuari;
        string tipus;
        uint256 operand1;
        uint256 operand2;
        uint256 resultat;
        uint256 timestamp;
    }

    Operacio[] public historial;
    mapping(address => uint256[]) public operacionsPerUsuari;

    event OperacioRealitzada(
        uint256 indexed operacioId,
        address indexed usuari,
        string tipus,
        uint256 resultat
    );

    event LimitAssolit(address indexed usuari);

    constructor() {
        propietari = msg.sender;
        limitMaxim = 1000000;
    }

    // COMPLETA ELS MODIFICADORS I FUNCIONS

}

Solució:

Fes clic per veure la solució proposada
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract CalculadoraSegura {

    uint256 public ultimResultat;
    uint256 public totalOperacions;
    address public propietari;
    uint256 public limitMaxim;

    struct Operacio {
      uint256 id;
      address usuari;
      string tipus;
      uint256 operand1;
      uint256 operand2;
      uint256 resultat;
      uint256 timestamp;
    }

    Operacio[] public historial;
    mapping(address => uint256[]) public operacionsPerUsuari;

    event OperacioRealitzada(
      uint256 indexed operacioId,
      address indexed usuari,
      string tipus,
      uint256 resultat
    );

    event LimitAssolit(address indexed usuari);

    constructor() {
      propietari = msg.sender;
      limitMaxim = 1000000;
    }

    modifier nomesPropietari() {
      require(msg.sender == propietari, "No es el propietari");
      _;
    }

    modifier valorDinsLimit(uint256 valor) {
      require(valor <= limitMaxim, "Valor fora de limit");
      _;
    }

    function sumar(uint256 a, uint256 b) 
      public 
      valorDinsLimit(a + b)
      returns (uint256) 
    {
      ultimResultat = a + b;
      totalOperacions++;

      _guardarOperacio("suma", a, b, ultimResultat);

      return ultimResultat;
    }

    function restar(uint256 a, uint256 b) 
      public 
      valorDinsLimit(a)
      returns (uint256) 
    {
      require(a = b, "No es permeten resultats negatius");
      ultimResultat = a - b;
      totalOperacions++;

      _guardarOperacio("resta", a, b, ultimResultat);

      return ultimResultat;
    }

    function multiplicar(uint256 a, uint256 b) 
      public 
      valorDinsLimit(a * b)
      returns (uint256) 
    {
      ultimResultat = a * b;
      totalOperacions++;

      _guardarOperacio("multiplicacio", a, b, ultimResultat);

      return ultimResultat;
    }

    function _guardarOperacio(
      string memory tipus,
      uint256 operand1,
      uint256 operand2,
      uint256 resultat)
    internal {
        uint256 operacioId = historial.length + 1;

        historial.push(Operacio({
            id: operacioId,
            usuari: msg.sender,
            tipus: tipus,
            operand1: operand1,
            operand2: operand2,
            resultat: resultat,
            timestamp: block.timestamp
        }));

        operacionsPerUsuari[msg.sender].push(operacioId - 1);

        emit OperacioRealitzada(operacioId, msg.sender, tipus, resultat);

        // Verificar si ha superat límit d'operacions
        if (totalOperacions  100) {
            emit LimitAssolit(msg.sender);
        }
    }

    function obtenirHistorialUsuari() public view returns (uint256) {
      return operacionsPerUsuari[msg.sender].length;
    }

    function obtenirOperacio(uint256 id) 
        public 
        view 
        returns (
          uint256 operacioId,
          address usuari,
          string memory tipus,
          uint256 operand1,
          uint256 operand2,
          uint256 resultat,
          uint256 timestamp
        )
    {
        require(id  0 && id <= historial.length, "Operacio no existeix");
        Operacio memory op = historial[id - 1];

        return (
          op.id,
          op.usuari,
          op.tipus,
          op.operand1,
          op.operand2,
          op.resultat,
          op.timestamp
        );
    }

    function resetejarComptador() public nomesPropietari {
        delete historial;
        totalOperacions = 0;
        ultimResultat = 0;
    }

    function canviarLimit(uint256 nouLimit) 
        public 
        nomesPropietari 
    {
        require(nouLimit  0, "Limit invalid");
        limitMaxim = nouLimit;
    }

    function obtenirUltimResultat() public view returns (uint256) {
        return ultimResultat;
    }

    function obtenirTotalOperacions() public view returns (uint256) {
        return totalOperacions;
    }
  }

Proves recomanades:

sumar(100, 200) → 300
obtenirHistorialUsuari() → 1
multiplicar(50, 10) → 500
obtenirOperacio(1) → detalls de la suma
restar(1000, 300) → 700
obtenirTotalOperacions() → 3
sumar(500000, 600000) → Error: "Valor fora de limit"
canviarLimit(2000000) → només propietari
sumar(500000, 600000) → 1100000 (ara funciona)
resetejarComptador() → només propietari
obtenirTotalOperacions() → 0


EXERCICI EXTRA (Opcional)

Sistema de Votació amb Modificadors:

Crea un contracte que: - Tingui modificadors per: votacioActiva, noHaVotat, usuariRegistrat - Permeti registrar usuaris (només admin) - Permeti iniciar/tancar votació (només admin) - Permeti votar (usuaris registrats, votació activa, un cop) - Registri events de cada vot - Tingui límit de temps

MATERIALS DE SUPORT

Cheat Sheet de Modificadors:

// VISIBILITAT
public     Tothom (contracte, derivats, extern)
private    Només aquest contracte
internal   Contracte + contractes derivats
external   Només crida externa (més eficient)

// ESTAT
view       Llegeix estat, no modifica (gratis)
pure       No llegeix ni modifica (gratis)
payable    Pot rebre Ether
(cap)      Pot modificar estat (paga gas)

// MODIFICADORS PERSONALITZATS
modifier nomesPropietari() {
    require(msg.sender == propietari, "Error");
    _;
}

modifier valorMaxim(uint256 maxim) {
    require(valor <= maxim, "Error");
    _;
}

// ÚS
function funcio() public nomesPropietari valorMaxim(100) {
    // Codi
}

Errors Comuns i Solucions:

Error Causa Solució
Function declared view but modifies state Funció view intenta modificar Canvia a funció normal o elimina la modificació
Modifier definition expected Sintaxi incorrecta de modifier Assegura't d'usar modifier keyword i _
External function call failed Crida a external des de dins Usa this.funcio() o canvia visibilitat
Invalid modifier Modifier no definit Defineix el modifier abans d'usar-lo
Constructor defined incorrectly Constructor amb tipus de retorn Elimina returns del constructor

Bones Pràctiques:

Fes: - Usa private per defecte i public només quan calgui - Implementa modificadors per reutilitzar validacions - Emet events per a totes les accions importants - Usa indexed en events per a filtres comuns - Valida sempre les entrades amb require

No facis: - No facis tot public per comoditat - No repeteixis codi de validació (usa modifiers) - No oblidis els events - No confiïs en dades externes sense validar - No modifiquis estat en funcions view

Enllaços Útils:

  • Solidity Modifiers: https://docs.soliditylang.org/en/latest/structure-of-a-contract.html#function-modifiers
  • Visibilitat: https://solidity-by-example.org/visibility/
  • Events: https://solidity-by-example.org/events/
  • OpenZeppelin Access: https://docs.openzeppelin.com/contracts/4.x/access-control

QÜESTIONARI DE REPÀS

Respon abans de la propera sessió:

  1. Quina diferència hi ha entre private i internal?
  2. Per què external és més eficient per a arrays grans?
  3. Què passa si una funció view intenta modificar una variable?
  4. Què fa exactament _ dins d'un modifier?
  5. Com es poden combinar múltiples modifiers en una funció?
  6. Per a què serveix indexed en un event?
  7. Quin modificador d'estat necessites per rebre Ether?
  8. Com cridaries una funció external des del mateix contracte?
  9. Quina és la diferència entre un constructor amb i sense paràmetres?
  10. Per què és important emetre events?

Solucions:

Fes clic per veure la solució proposada
  1. private només el contracte actual, internal també els contractes derivats
  2. Perquè no copia les dades a memòria, les llegeix directament de calldata
  3. Donarà error de compilació: "Function declared view but modifies state"
  4. Indica on s'executarà la funció original que utilitza el modifier
  5. Separant-los amb espai: function f() public modifier1 modifier2 { }
  6. Permet filtrar events eficientment des de fora del contracte (màxim 3)
  7. payable
  8. Amb this.nomFuncio() (crida externa)
  9. Amb paràmetres permet inicialitzar variables al desplegar; sense paràmetres usa valors per defecte
  10. Perquè permeten rastrejar accions, actualitzar UI en temps real, i són essencials per a DApps

PREPARACIÓ PER LA SESSIÓ 3

Abans de la propera classe:

✅ Completa l'exercici de la calculadora amb seguretat
✅ Prova tots els modificadors al contracte de gestió d'usuaris
✅ Respon el qüestionari de repàs
✅ Porta dubtes sobre visibilitat i modifiers

Material a revisar: - Modificadors de visibilitat - Creació de modifiers personalitzats - Events i com consultar-los

Pròxima sessió: Estructures de dades avançades (structs, mappings, arrays)


CONSELLS

Per dominar els modificadors:

  1. Practica la reutilització:

    • Si reps codi, probablement necessites un modifier
    • Exemple: múltiples funcions amb require(msg.sender == propietari) → crea nomesPropietari
  2. Entén l'ordre d'execució:

    function f() public modifierA modifierB {
        // 1. Codi de modifierA (abans de _)
        // 2. Codi de modifierB (abans de _)
        // 3. Codi de f()
        // 4. Codi de modifierB (després de _)
        // 5. Codi de modifierA (després de _)
    }
    

  3. Depura amb events:

    • Afegeix events als modifiers per veure quan s'executen
    • Exemple: event ModifierExecutat(string modifierName);

Per evitar errors comuns:

  • ✅ Sempre valida les entrades
  • ✅ Usa indexed en events que filtraràs sovint
  • ✅ Prova tots els camins d'execució
  • ✅ Revisa els permisos de cada funció

✅ CHECKLIST FINAL DE LA SESSIÓ 2:

  • Entenc la diferència entre public, private, internal, external
  • Puc crear modificadors personalitzats
  • Sé quan usar view, pure o payable
  • He completat el contracte de gestió d'usuaris
  • He millorat la calculadora amb control d'accés
  • He emès events correctament
  • He respost el qüestionari de repàs