Solidity se uporablja za obli­ko­va­nje kom­ple­ksnih pametnih pogodb, za­sno­va­nih za delovanje na blok verigi Ethereum. Ta jezik pred­sta­vlja edin­stve­ne stra­te­gi­je, ki ga ločujejo od drugih pro­gram­skih jezikov.

Kaj je Solidity?

Solidity je vi­so­ko­rav­ni pro­gram­ski jezik za ustvar­ja­nje pametnih pogodb na blok verigi Ethereum. Pametne pogodbe so samodejno iz­vr­šlji­ve pogodbe, ki av­to­ma­ti­zi­ra­jo izmenjavo sredstev med strankami. Posebnost teh pogodb je, da za za­go­to­vi­tev skla­dno­sti s pametno pogodbo niso potrebni po­sre­dni­ki.

Izvorna koda Solidity se prevede v bajtno kodo in se kot pametna pogodba razporedi na blok verigi Ethereum. Ko je to storjeno, lahko pametno pogodbo izvede kateri koli vozlišče v omrežju, stanje pa se shrani na blok verigi. Pri­ka­zu­je­mo primer preproste pogodbe, ki modelira avtomat za prodajo 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

Za katere apli­ka­ci­je je Solidity primeren?

Solidity je posebej zasnovan za ustvar­ja­nje raz­pr­še­nih aplikacij ali DApps, ki delujejo na vir­tu­al­nem stroju Ethereum (EVM). Pametne pogodbe so primerne za upra­vlja­nje di­gi­tal­nih sredstev, ustvar­ja­nje de­cen­tra­li­zi­ra­nih borz in izvajanje gla­so­val­nih sistemov, med drugim.

De­cen­tra­li­zi­ra­ne finance (DeFi)

Solidity se uporablja za razvoj aplikacij DeFi, kot so de­cen­tra­li­zi­ra­ne borze, platforme za kredite in posojila, trgi napovedi in krip­to­va­lu­te. DeFi je postal eden najbolj pri­lju­blje­nih primerov uporabe teh­no­lo­gi­je block­cha­in. V tem procesu je Solidity postal ne­po­gre­šlji­vo orodje za razvoj aplikacij DeFi v omrežju Ethereum.

Ne­za­men­lji­vi žetoni

Ne­za­men­lji­vi žetoni (NFT) so od leta 2020 zelo pri­lju­blje­ni. NFT-ji so edin­stve­na digitalna sredstva, shranjena v verigi blokov. Lahko so digitalna umetniška dela, športni spominki ali artefakti iz igral­ni­ške in­du­stri­je. Solidity se uporablja za ustvar­ja­nje pametnih pogodb, ki poganjajo NFT-je.

Upra­vlja­nje dobavne verige

Solidity se lahko uporablja za ustvar­ja­nje pametnih pogodb za spre­mlja­nje in upra­vlja­nje dobavnih verig. Pogodbe se upo­ra­blja­jo za av­to­ma­ti­za­ci­jo različnih procesov v dobavni verigi. Ti vklju­ču­je­jo sledenje gibanja blaga, pre­ver­ja­nje pri­stno­sti izdelkov in obdelavo plačil med strankami.

Sistemi uskla­je­va­nja

Solidity se lahko uporabi za ustvar­ja­nje pametnih pogodb, ki izvajajo varne in pregledne sisteme gla­so­va­nja na blok verigi. Pogodbe se lahko uporabijo za za­go­to­vi­tev, da se glasovi pravilno pre­šte­je­jo in da je gla­so­val­ni postopek pošten in pregleden.

Kakšne so prednosti in slabosti Solidity?

Solidity je sicer zmogljiv jezik za ustvar­ja­nje pametnih pogodb na blok verigi Ethereum, vendar ima svoje spe­ci­fič­ne prednosti in slabosti, ki jih morajo raz­vi­jal­ci upo­šte­va­ti pri razvoju pametnih pogodb. Kljub temu pa ustvar­ja­nje varnih pametnih pogodb zahteva določeno raven uspo­so­blje­no­sti in pre­vi­dno­sti.

Za po­na­zo­ri­tev je spodaj prikazan pametni pogodbeni dokument, ki deluje kot črna luknja. Vsak Ether, poslan v pogodbeni dokument, se trajno porabi, brez možnosti za ponovno pri­do­bi­tev Etherja ali izplačilo:

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

Prednosti trdnosti

  • Pri­la­go­dlji­vost: Solidity je vse­stran­ski jezik. Uporablja se lahko za razvoj različnih pametnih pogodb z raz­lič­ni­mi primeri uporabe.
  • Varnost: Solidity je bil ustvarjen z osre­do­to­če­no­stjo na varnost, vključuje zna­čil­no­sti, kot so nadzor dostopa, obrav­na­va­nje izjem in mehanizmi za odpoved, ki raz­vi­jal­cem pomagajo pri obli­ko­va­nju varnih pogodb.
  • Zdru­žlji­vost z Ethe­re­u­mom: Trenutno je Solidity najbolj pri­lju­bljen jezik za ustvar­ja­nje pametnih pogodb na blok verigi Ethereum.
  • Izrazita skupnost: Velika skupnost raz­vi­jal­cev blokovnih verig dela s Solidity, kar omogoča številne vire za učenje in reševanje problemov.

Po­manj­klji­vo­sti Solidity

  • Učna krivulja: Za raz­vi­jal­ce, ki so novi na področju razvoja blokovnih verig in pametnih pogodb, je Solidity relativno zahteven za učenje.
  • Ne­spre­men­lji­vost: Ko je pametna pogodba enkrat raz­po­re­je­na v block­cha­inu, je ni mogoče več spre­mi­nja­ti. Iz tega sledi, da morajo raz­vi­jal­ci biti pri pisanju in te­sti­ra­nju izredno previdni.
  • Po­manj­ka­nje formalne ve­ri­fi­ka­ci­je: Solidity nima vgrajenih orodij za formalno pre­gle­do­va­nje kode. Zaradi tega morajo raz­vi­jal­ci upo­ra­blja­ti zunanja orodja, da za­go­to­vi­jo točnost svojih pogodb.
  • Omejena orodja: Ekosistem orodij Solidity je še v začetni fazi, kar lahko povzroči težave z in­te­gri­ra­ni­mi ra­zvoj­ni­mi okolji (IDE), okviri za te­sti­ra­nje in drugimi ra­zvoj­ni­mi orodji.

Kakšna je osnovna sintaksa Solidity?

Solidity je objektno usmerjen pro­gram­ski jezik, zasnovan za pametne pogodbe. Navdih črpa iz Ja­va­Script, Python in C++. Sintaksa jezika je podobna Ja­va­Script, čeprav z ne­ka­te­ri­mi za­ni­mi­vi­mi po­seb­nost­mi.

Spre­men­ljiv­ke v Solidity

Na prvi pogled se obdelava spre­men­ljivk v Solidity zdi podobna obdelavi v drugih pro­gram­skih jezikih. Vendar pa obstaja pomembna razlika, ki izhaja iz dejstva, da kot izvedbeno okolje služi Ethereum Virtual Machine (EVM). Vse operacije na EVM, vključno s shra­nje­va­njem podatkov, pov­zro­ča­jo določene stroške „plina“. Zato je med pro­gra­mi­ra­njem treba pre­teh­ta­ti učin­ko­vi­tost operacije in določiti, kako jo je mogoče izvesti čim bolj učin­ko­vi­to.

Poleg rednih spre­men­ljivk ima Solidity tudi konstante, ki jih je treba de­fi­ni­ra­ti med kom­pi­li­ra­njem. Konstante zahtevajo manj plina za shra­nje­va­nje:

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

Enako velja za immutable spre­men­ljivk, saj zahtevajo manj plina in jih po dodelitvi ni mogoče spre­me­ni­ti. Za razliko od constant spre­men­ljivk je dodelitev ne­spre­men­lji­vih spre­men­ljivk mogoča med iz­va­ja­njem.

Kontrolne izjave v Solidity

Kot im­pe­ra­tiv­ni pro­gram­ski jezik Solidity podpira znane kontrolne izjave, kot so veje in zanke. Pri­ka­zu­je­mo kodo za izbiro večjega od dveh številk, a in b:

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

Zankafor v Solidity ustreza sintaksi, znani iz Ja­va­Script ali C++:

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

Tudi zanka while deluje kot običajno. Kom­bi­ni­ra­mo pogoj za zaključek s številčno spre­men­ljiv­ko števca:

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

Preprosti tipi v Solidity

Solidity je statično tipiziran jezik in podpira tipe, ki so pogosti v pro­gram­skih jezikih. Preprosti tipi, ki pred­sta­vlja­jo posamezne vrednosti, vklju­ču­je­jo logične vrednosti, številke in nize.

Booleani v So­li­di­ty­ju pre­sli­ku­je­jo vrednosti true in false. Lahko se povežejo z znanimi boo­le­an­ski­mi ope­ra­ci­ja­mi in uporabijo v if izjavah:

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

Solidity podpira široko paleto nu­me­rič­nih tipov. Ce­lo­šte­vil­ska števila se lahko raz­li­ku­je­jo med števili s pred­zna­kom (int) in števili brez predznaka (uint), pri čemer so slednja lahko samo pozitivna. Poleg tega se lahko obseg števila določi v korakih po 8 bitov, od int8 prek int16 do int265:

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

Nize se v Solidity upo­ra­blja­jo predvsem za ustvar­ja­nje statusnih sporočil. Jezik podpira enojne in dvojne na­re­ko­va­je ter Unicode znake:

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

Funkcije v Solidity

Funkcije so temeljni vidik Solidity, kot v večini pro­gram­skih jezikov. Opre­de­li­tev funkcije je podobna Ja­va­Scrip­tu, kjer morajo biti tipi ar­gu­men­tov izrecno navedeni. Poleg tega se za ozna­če­va­nje return tipov vrednosti uporablja ključna beseda returns.

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

Klic funkcije se izvede kot običajno:

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

Zanimivo je , da se podobno kot imenovani argumenti lahko imenujejo tudi vrnjene vrednosti v Solidity. V tem primeru zadostuje dodelitev ustreznih spre­men­ljivk v telesu funkcije, ek­s­pli­ci­tna vrnitev prek return pa ni potrebna:

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

Podobno kot pri spre­men­ljiv­kah constant ali immutable se lahko funkcije v Solidity označijo kot funkcije, ki ne spre­mi­nja­jo stanja. Za ta namen se upo­ra­blja­ta ključni besedi view in pure. Funkcija view ne spreminja stanja, medtem ko funkcija pure dodatno za­go­ta­vlja, da ne bere spre­men­ljivk stanja.

Pametne pogodbe v Solidity

Poleg stan­dar­dnih tipov Solidity pozna tudi nekaj tipov, spe­ci­a­li­zi­ra­nih za pametne pogodbe. Osnovni tip je address in mapira naslove Ethereum. Naslovi, ki so payable, lahko prejemajo prenose v Etherju. Za ta namen naslovi payable za­go­ta­vlja­jo metode balance() in 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

Na podlagi tipa address obstaja tip contract kot osrednji jezikovni konstrukt. Pogodbe približno ustrezajo razredom v objektno usmer­je­nih pro­gram­skih jezikih. Pogodbe tako zdru­žu­je­jo podatke o stanju in funkcije ter jih ščitijo pred zunanjim svetom. Pogodbe podpirajo večkratno dedovanje, kot je znano iz Python ali C++.

Pogodbe se običajno začnejo s pragma vrstico, ki določa dovoljeno različico Solidity, ki ji sledi dejanska de­fi­ni­ci­ja:

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

Pametne pogodbe lahko opre­de­lju­je­jo stanje podatkov in funkcije. Kot je znano iz C++ in Java, je v vsakem primeru mogoče opre­de­li­ti eno od treh ravni dostopa:

  • public: Do spre­men­ljiv­ke je mogoče dostopati z branjem in pisanjem iz pogodbe. Poleg tega se samodejno generira funkcija view kot getter za zunanji dostop za branje.
  • internal: Spre­men­ljiv­ka je zaščitena pred zunanjim dostopom. Dostop za branje in pisanje je mogoč iz pogodbe kot tudi iz pogodb, ki jo po­de­du­je­jo.
  • private: kot internal, vendar ni dostopa iz na­sle­dnjih pogodb

Funkcije lahko nadalje opišemo kot external. Funkcija external deluje kot del po­god­be­ne­ga vmesnika in se uporablja za zunanji dostop. Funkcija receive za spre­je­ma­nje etra je dobro znan primer:

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

Mo­di­fi­ka­tor­ji v Solidity

Solidity ima zanimivo jezikovno kon­struk­ci­jo v obliki mo­di­fi­ka­tor­jev, ki so podobni de­ko­ra­tor­jem v Pythonu. Podobno kot v Pythonu se mo­di­fi­ka­tor­ji v Solidity upo­ra­blja­jo za spre­mi­nja­nje klicanja funkcije. Pogosto se upo­ra­blja­jo za za­go­to­vi­tev, da je pred izvedbo funkcije izpolnjen določen pogoj:

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

Upra­vlja­nje tran­sak­cij s Solidity

Solidity ima vgrajeno upra­vlja­nje tran­sak­cij. To se lahko uporabi za za­go­to­vi­tev, da je prenos etherja bodisi popolnoma poravnan bodisi sploh ni poravnan. Jezik razume ključno besedo revert, ki sproži »roll-back« tran­sak­ci­je. S ključno besedo error lahko opre­de­li­te svoje lastne kodekse napak:

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

Drugi pogosto srečani vzorec je uporaba funkcije require(). To se lahko uporabi analogno kot revert:

// Using `require()` function
if (!condition) revert("Error message");
// Equivalent to
require(condition, "Error message");
solidity
Go to Main Menu