Laborator 3

Tematica laboratorului: Solidity - notiuni de baza

Exemplu smart contract

Redam mai jos un exemplu de smart contract cu scopul de a evidentia cateva tipuri principale de date si elemente (enumerari, modificatori, evenimente, functii, constructor), ce se pot regasi in cadrul unui contract. Majoritatea tipurilor de data in Solidity retin direct valoarea. Exista urmatoarele tipuri referinta: vectori (arrays - inclusiv tipurile speciale "string" si "bytes"), structuri si mapari (mapping).
Pentru detalii asupra API-ului Solidity se pot consulta intrarile specifice prezente in documentatia Solidity.


// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0 <=0.8.21;

//definitie contract
contract GeneralStructure {

    //variabile si constante
    uint public integerVar; // variabila de tip intreg explicit publica
    string stringVar; // variabila de tip string
    address addressVar; // variabila de tip adresa
			// descriere API: https://solidity.readthedocs.io/en/v0.8.21/types.html#address
			// descriere API: https://docs.soliditylang.org/en/v0.8.21/units-and-global-variables.html#members-of-address-types
    person structVar; // variabila de tip structura
    bool constant boolConst = true; // constanta booleana
    mapping (uint => person) persons; // variabila de tip mapping

    //declaratie enumerare - valori indexate incepand cu 0
    enum eyecolor {brown, blue, green}

    //definitie structura
    struct person {
        string name;
        uint age;
        bool isMarried;
        eyecolor eyes;
        uint[] bankAccounts; // variabila de tip vector
    }

    //declaratie modificator
    modifier onlyBy(){
        require(msg.sender == addressVar, "Nu esti cine trebuie sa fii"); // a se vedea require vs assert vs revert
        _;
    }

    //declaratie eveniment
    event ageSet(address, uint);

    //definitie constructor
    constructor() {
        addressVar = msg.sender;
        integerVar = 0;
    }

    //definitie functie
    function createPersonWithAge(uint _age) onlyBy() payable external returns (uint) {

       structVar = person("John", _age, true, eyecolor.brown, new uint[](3)); //instantiere structura

       emit ageSet(msg.sender, structVar.age);

       persons[integerVar] = structVar;
       integerVar++;

       return structVar.age;
    }

    // private
    function doPureMath(uint a) private pure returns (uint) {
        return a + 1;
    }

    // e nevoie?
    function getIntegerVar() public view returns (uint) {
        return integerVar;
    }

}

Locatii de stocare a variabilelor

Exista patru zone posibile de stocare, utilizate de catre EVM, dupa cum urmeaza:

  • storage - locatia persistenta de stocare disponibila pentru variabilele ce definesc starea contractului si accesibila functiilor din contract pentru creare de noi date sau modificari persistente (tipic, cea mai costisitoare zona)
  • memory - locatie de memorie pentru stocare nepersistenta disponibila la executia fiecarei functii din contract, de regula pentru variabilele locale functiei (costul creste odata cu dimensiunea necesara)
  • calldata - locatie speciala de memorie, nemodificabila, unde pot fi stocate argumentele functiilor externe pasate la apel (cost mai scazut de acces comparativ cu "memory")
  • stack - stiva de memorie ce este utilizata de EVM pentru pastrarea datelor in momentul in care se executa operatii asupra acestora; nu este folosita in mod explicit pentru stocare de catre programator

Modificatori de acces

Atat variabilele declarate in cadrul contractului ce definesc starea acestuia ("state variables") cat si functiile contractului pot avea urmatoarea serie de modificatori de acces programatic (pentru functii specificarea acestora este obligatorie):

  • internal - accesul programatic este posibil doar din cadrul contractului sau din contractele derivate ale acestuia
  • private - accesul programatic este posibil doar din cadrul contractului
  • public - accesul programatic este posibil de oriunde; pentru variabilele din contract declarate public, sunt generate automat intern la compilare metode de tip getter avand numele variabilei ce permit interogarea valorilor
  • external - posibil doar pentru specificarea accesului in cazul functiilor; functiile declarate astfel pot fi accesate implicit exclusiv din afara contractului (sau folosind this.nume_functie pentru mod intern, dar aceasta cauzeaza costuri crescute); diferenta fata de acces public consta in accesul la argumentele functiei de tip referinta care pentru apeluri externe se face direct pe spatiul de memorie calldata - in cazul apelurilor publice din cauza ca acestea se pot executa si intern se realizeaza mai intai o copie din spatiul calldata in memory ce creste costul de executie

Asignarea variabilelor (valoare sau referinta)

Asignarea variabilelor se face in aproape toate cazurile prin valoare: variabila ce primeste asignarea va fi independenta, cu o copie a valorii variabilei primite la asignare. Doua exceptii apar la variabilele de tip referinta (structuri, mappings si tablouri, inclusiv string si bytes) la asignarea in cadrul aceluiasi spatiu de stocare - memory la memory sau o variabila din storage la o variabila locala functiei declarata tot in storage (nu si situatia a doua variabile de stare a contractului din storage). In aceste situatii ambele variabile de tip referinta vor referi aceeasi locatie de memorie si orice modificare a unei variabile este vizibila si in cealalta. Mai multe detalii se pot regasi la aceasta referinta.

Modificatori de functii (payable)

Modificatorii de functii sunt constructii ce se pot atasa functiilor, separate de corpul acestora, pentru o lizibilitate mai buna a codului sau pentru reutilizarea usoara. In multe situatii, codul unui modificator implica verificarea unei anumite conditii. Corpul functiei la care se ataseaza modificatorul este specificat prin caracterul underscore "_;".
Un modificator predefinit este "payable". Acesta indica faptul ca functia poate accepta primirea unei balante. Daca functia nu este definita ca payable orice tranzactie care o invoca si care va incerca trimiterea de Ether va esua.

Evenimente

Evenimentele pot fi utilizate in Solidity pentru a emite anumite notificari. Acestea nu pot fi captate de catre contracte in mod direct. Principala utilizare este pentru loguri sau pentru monitorizarea de catre cod/aplicatii externe contractelor.

Variabile globale (msg)

La nivelul contractelor exista un spatiu de nume cu acces global implicit, unde variabilele definite in acest spatiu de nume se modifica in functie de contextul de executie. O descriere a diverselor functii disponibile si a variabilelor membre se regaseste la aceasta adresa.
In particular "msg" e o variabla asociata apelului functiei curente aflata in executie si include doi membri cu utilizare frecventa: msg.sender - adresa initiatorului apelului si msg.value - valoarea balantei trimise de apelant in Wei.

Exercitii

  1. 1. Redenumiti in tot contractul variabila addressVar ca admin.
  2. 2. Inlocuiti campul bankAccounts din structura person cu un camp bankAccount de tip adresa platibila si modificati tipul cheii din maparea persoanelor la tipul adresa. (Hint: Declaratia unei adrese ca platibila se face prin specificatorul address payable iar conversia la tipul platibil se face prin apelul payable(adresa de convertit).)
  3. 3. Modificati functia createPersonWithAge astfel incat sa primeasca ca parametri toate elementele structurii pentru instantierea unei noi persoane si permiteti apelarea acesteia de catre oricine in mod gratuit. Adresa contului bancar va fi setata ca fiind adresa apelantului (msg.sender).
  4. 4. Creati o functie findPerson care pe baza unei adrese sa returneze numele unei persoane si balanta contului acesteia (campul address.balance din adresa de tip uint256). Functia trebuie sa ceara o plata de minim 1000 wei de la apelant (verificabil prin msg.value). Nota: o functie poate avea mai multe valori de retur sub forma unui tuplu cuprins intre paranteze.
  5. 5. Creati o functie removePerson care sa stearga o persoana din mapare pe baza adresei care sa fie apelabila doar de admin.
© 2023 Emanuel Onica. Parts of design by W3Layouts