Solidità - Guida rapida

Solidity è un linguaggio di programmazione di alto livello orientato al contratto per l'implementazione di contratti intelligenti. Solidity è fortemente influenzato da C ++, Python e JavaScript ed è stato progettato per colpire la Ethereum Virtual Machine (EVM).

La solidità è tipizzata staticamente, supporta l'ereditarietà, le librerie e il linguaggio di programmazione di tipi complessi definiti dall'utente.

Puoi utilizzare Solidity per creare contratti per usi come votazioni, crowdfunding, aste alla cieca e portafogli multi-firma.

Cos'è Ethereum?

Ethereum è un ie. piattaforma blockchain che esegue contratti intelligenti, ovvero applicazioni che vengono eseguite esattamente come programmato senza alcuna possibilità di downtime, censura, frode o interferenza di terze parti.

La macchina virtuale Ethereum (EVM)

La macchina virtuale Ethereum, nota anche come EVM, è l'ambiente di runtime per gli smart contract in Ethereum. La macchina virtuale Ethereum si concentra sulla fornitura di sicurezza e sull'esecuzione di codice non attendibile da parte dei computer di tutto il mondo.

L'EVM si è specializzato nella prevenzione degli attacchi Denial-of-service e garantisce che i programmi non abbiano accesso al reciproco stato, assicurando che la comunicazione possa essere stabilita senza potenziali interferenze.

La macchina virtuale Ethereum è stata progettata per fungere da ambiente di runtime per contratti intelligenti basati su Ethereum.

Cos'è lo Smart Contract?

Uno smart contract è un protocollo informatico destinato a facilitare, verificare o imporre digitalmente la negoziazione o l'esecuzione di un contratto. I contratti intelligenti consentono l'esecuzione di transazioni credibili senza terze parti. Queste transazioni sono tracciabili e irreversibili.

Il concetto di contratti intelligenti è stato proposto per la prima volta da Nick Szabo nel 1994. Szabo è uno studioso di diritto e crittografo noto per aver gettato le basi per la valuta digitale.

Va bene se non capisci Smart Contract in questo momento, entreremo più in dettaglio in seguito.

Questo capitolo spiega come possiamo configurare il compilatore Solidity sulla macchina CentOS. Se non si dispone di una macchina Linux, è possibile utilizzare il nostro compilatore online per piccoli contratti e per apprendere rapidamente Solidity.

Metodo 1: npm / Node.js

Questo è il modo più veloce per installare il compilatore Solidity sulla tua macchina CentoS. Abbiamo i seguenti passaggi per installare Solidity Compiler:

Installa Node.js

Per prima cosa assicurati di avere node.js disponibile sulla tua macchina CentOS. Se non è disponibile, installalo utilizzando i seguenti comandi:

# First install epel-release
$sudo yum install epel-release

# Now install nodejs
$sudo yum install nodejs

# Next install npm (Nodejs Package Manager )
$sudo yum install npm

# Finally verify installation
$npm --version

Se tutto è stato installato, vedrai un output simile a questo:

3.10.10

Installa solc

Dopo aver installato il gestore di pacchetti Node.js, puoi procedere con l'installazione del compilatore Solidity come di seguito:

$sudonpm install -g solc

Il comando precedente installerà il programma solcjs e lo renderà disponibile a livello globale attraverso il sistema. Ora puoi testare il tuo compilatore Solidity emettendo il seguente comando:

$solcjs-version

Se tutto va bene, verrà stampato qualcosa come segue:

0.5.2+commit.1df8f40c.Emscripten.clang

Ora sei pronto per usare solcjs che ha meno funzionalità del compilatore Solidity standard ma ti darà un buon punto di partenza.

Metodo 2 - Immagine Docker

Puoi estrarre un'immagine Docker e iniziare a usarla per iniziare con la programmazione Solidity. Di seguito sono riportati i semplici passaggi. Di seguito è riportato il comando per estrarre un'immagine Solidity Docker.

$docker pull ethereum/solc:stable

Una volta scaricata un'immagine docker, possiamo verificarla utilizzando il seguente comando.

$docker run ethereum/solc:stable-version

Questo stamperà qualcosa come segue:

$ docker run ethereum/solc:stable -version

solc, the solidity compiler commandlineinterfaceVersion: 0.5.2+commit.1df8f40c.Linux.g++

Metodo 3: installazione di pacchetti binari

Se sei disposto a installare il compilatore completo sulla tua macchina Linux, controlla il sito Web ufficiale Installazione del compilatore Solidity.

Un file sorgente Solidity può contenere un numero qualsiasi di definizioni di contratto, direttive di importazione e direttive pragma.

Cominciamo con un semplice file sorgente di Solidity. Di seguito è riportato un esempio di un file Solidity:

pragma solidity >=0.4.0 <0.6.0;
contract SimpleStorage {
   uint storedData;
   function set(uint x) public {
      storedData = x;
   }
   function get() public view returns (uint) {
      return storedData;
   }
}

Pragma

La prima riga è una direttiva pragma che dice che il codice sorgente è scritto per Solidity versione 0.4.0 o qualsiasi altra cosa più recente che non interrompe la funzionalità fino alla versione 0.6.0, ma non inclusa.

Una direttiva pragma è sempre locale in un file sorgente e se importi un altro file, il pragma da quel file non si applicherà automaticamente al file di importazione.

Quindi un pragma per un file che non verrà compilato prima della versione 0.4.0 e non funzionerà nemmeno su un compilatore a partire dalla versione 0.5.0 sarà scritto come segue:

pragma solidity ^0.4.0;

Qui la seconda condizione viene aggiunta utilizzando ^.

Contrarre

Un contratto Solidity è una raccolta di codice (le sue funzioni) e dati (il suo stato) che risiede in un indirizzo specifico su Ethereumblockchain.

La riga uintstoredData dichiara una variabile di stato chiamata storedData di tipo uint e le funzioni set e get possono essere utilizzate per modificare o recuperare il valore della variabile.

Importazione di file

Sebbene l'esempio sopra non abbia un'istruzione di importazione, Solidity supporta le istruzioni di importazione che sono molto simili a quelle disponibili in JavaScript.

La seguente istruzione importa tutti i simboli globali da "filename".

import "filename";

L'esempio seguente crea un nuovo simbolo globale symbolName i cui membri sono tutti i simboli globali da "filename".

import * as symbolName from "filename";

Per importare un file x dalla stessa directory del file corrente, utilizzare import "./x" come x ;. Se usi import "x" as x; invece, è possibile fare riferimento a un file diverso in una "directory di inclusione" globale.

Parole chiave riservate

Di seguito sono riportate le parole chiave riservate in Solidity:

astratto dopo alias applicare
auto Astuccio catturare copia di
predefinito definire finale immutabile
attrezzi in in linea permettere
macro incontro mutevole nullo
di oltrepassare parziale promettere
riferimento trasferibile sigillato taglia di
statico supporti interruttore provare
typedef tipo di deselezionato

Stiamo usando Remix IDE per compilare ed eseguire la nostra base di Solidity Code.

Step 1 - Copia il codice fornito nella sezione Codice IDE Remix.

Esempio

pragma solidity ^0.5.0;
contract SolidityTest {
   constructor() public{
   }
   function getResult() public view returns(uint){
      uint a = 1;
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Step 2 - Nella scheda Compila, fare clic su Start to Compile pulsante.

Step 3 - Nella scheda Esegui, fare clic su Deploy pulsante.

Step 4 - Nella scheda Esegui, seleziona SolidityTest at 0x... nel menu a discesa.

Step 5 - Fare clic su getResult Pulsante per visualizzare il risultato.

Produzione

0: uint256: 3

Solidity supporta commenti in stile C e C ++, quindi -

  • Qualsiasi testo compreso tra // e la fine di una riga viene trattato come un commento e viene ignorato da Solidity Compiler.

  • Qualsiasi testo compreso tra i caratteri / * e * / viene considerato come un commento. Questo può estendersi su più righe.

Esempio

L'esempio seguente mostra come utilizzare i commenti in Solidity.

function getResult() public view returns(uint){
   // This is a comment. It is similar to comments in C++

   /*
      * This is a multi-line comment in solidity
      * It is very similar to comments in C Programming
   */
   uint a = 1;
   uint b = 2;
   uint result = a + b;
   return result;
}

Durante la scrittura di programmi in qualsiasi lingua, è necessario utilizzare varie variabili per memorizzare varie informazioni. Le variabili non sono altro che posizioni di memoria riservate per memorizzare i valori. Ciò significa che quando crei una variabile riservi dello spazio in memoria.

Potresti voler memorizzare informazioni di vari tipi di dati come carattere, carattere largo, intero, virgola mobile, doppia virgola mobile, booleano ecc. In base al tipo di dati di una variabile, il sistema operativo alloca la memoria e decide cosa può essere memorizzato nel memoria riservata.

Tipi di valore

Solidity offre al programmatore un ricco assortimento di tipi di dati integrati e definiti dall'utente. La tabella seguente elenca sette tipi di dati C ++ di base:

genere Parola chiave Valori
Booleano bool vero falso
Numero intero int / uint Numeri interi con e senza segno di varie dimensioni.
Numero intero da int8 a int256 Int con segno da 8 bit a 256 bit. int256 è uguale a int.
Numero intero da uint8 a uint256 Int senza segno da 8 bit a 256 bit. uint256 è uguale a uint.
Numeri in virgola fissa fisso / non fisso Numeri in virgola fissa firmati e non firmati di varie dimensioni.
Numeri in virgola fissa fisso / non fisso Numeri in virgola fissa firmati e non firmati di varie dimensioni.
Numeri in virgola fissa fixedMxN Numero in virgola fissa con segno dove M rappresenta il numero di bit presi per tipo e N rappresenta i punti decimali. M deve essere divisibile per 8 e va da 8 a 256. N può essere compreso tra 0 e 80. fixed è uguale a fixed128x18.
Numeri in virgola fissa ufixedMxN Numero in virgola fissa senza segno dove M rappresenta il numero di bit presi per tipo e N rappresenta i punti decimali. M dovrebbe essere divisibile per 8 e va da 8 a 256. N può essere compreso tra 0 e 80. ufixed è uguale a ufixed128x18.

indirizzo

address contiene il valore di 20 byte che rappresenta la dimensione di un indirizzo Ethereum. Un indirizzo può essere utilizzato per ottenere il saldo utilizzando il metodo .balance e può essere utilizzato per trasferire il saldo a un altro indirizzo utilizzando il metodo .transfer.

address x = 0x212;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);

La solidità supporta tre tipi di variabili.

  • State Variables - Variabili i cui valori sono memorizzati in modo permanente in una memoria di contratti.

  • Local Variables - Variabili i cui valori sono presenti fino all'esecuzione della funzione.

  • Global Variables - Esistono variabili speciali nello spazio dei nomi globale utilizzato per ottenere informazioni sulla blockchain.

La solidità è un linguaggio tipizzato staticamente, il che significa che il tipo di variabile locale o di stato deve essere specificato durante la dichiarazione. Ogni variabile dichiarata ha sempre un valore predefinito basato sul suo tipo. Non esiste il concetto di "undefined" o "null".

Variabile di stato

Variabili i cui valori sono memorizzati in modo permanente in una memoria di contratti.

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData;      // State variable
   constructor() public {
      storedData = 10;   // Using State variable
   }
}

Variabile locale

Variabili i cui valori sono disponibili solo all'interno di una funzione in cui è definita. I parametri della funzione sono sempre locali rispetto a quella funzione.

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData; // State variable
   constructor() public {
      storedData = 10;   
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return result; //access the local variable
   }
}

Esempio

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData; // State variable
   constructor() public {
      storedData = 10;   
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return storedData; //access the state variable
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Produzione

0: uint256: 10

Variabili globali

Queste sono variabili speciali che esistono nello spazio di lavoro globale e forniscono informazioni sulla blockchain e sulle proprietà delle transazioni.

Nome ritorna
blockhash (uint blockNumber) restituisce (bytes32) Hash del blocco specificato: funziona solo per i 256 blocchi più recenti, esclusi quelli correnti
block.coinbase (indirizzo da pagare) Indirizzo attuale del block miner
block.difficulty (uint) Attuale difficoltà di blocco
block.gaslimit (uint) Limite gas blocco corrente
block.number (uint) Numero di blocco corrente
block.timestamp (uint) Timestamp del blocco corrente espresso in secondi dall'epoca unix
gasleft () restituisce (uint256) Gas rimanente
msg.data (byte calldata) Calldata completa
msg.sender (indirizzo da pagare) Mittente del messaggio (chiamante corrente)
msg.sig (bytes4) Primi quattro byte del calldata (identificatore di funzione)
msg.value (uint) Numero di wei inviati con il messaggio
ora (uint) Timestamp del blocco corrente
tx.gasprice (uint) Prezzo del gas della transazione
tx.origin (indirizzo da pagare) Mittente della transazione

Nomi variabili di solidità

Durante la denominazione delle variabili in Solidity, tieni a mente le seguenti regole.

  • Non utilizzare nessuna delle parole chiave riservate di Solidity come nome di variabile. Queste parole chiave sono menzionate nella sezione successiva. Ad esempio, i nomi di variabili break o booleani non sono validi.

  • I nomi delle variabili di solidità non devono iniziare con un numero (0-9). Devono iniziare con una lettera o un trattino basso. Ad esempio, 123test è un nome di variabile non valido ma _123test è uno valido.

  • I nomi delle variabili di solidità fanno distinzione tra maiuscole e minuscole. Ad esempio, Nome e nome sono due variabili diverse.

L'ambito delle variabili locali è limitato alla funzione in cui sono definite, ma le variabili di stato possono avere tre tipi di ambiti.

  • Public- È possibile accedere alle variabili di stato pubblico sia internamente che tramite messaggi. Per una variabile di stato pubblica, viene generata una funzione getter automatica.

  • Internal - È possibile accedere alle variabili di stato interne solo internamente dal contratto corrente o dal contratto derivante da esso senza utilizzarlo.

  • Private - Le variabili di stato privato sono accessibili solo internamente dal contratto corrente, esse sono definite non nel contratto derivato da esso.

Esempio

pragma solidity ^0.5.0;
contract C {
   uint public data = 30;
   uint internal iData= 10;
   
   function x() public returns (uint) {
      data = 3; // internal access
      return data;
   }
}
contract Caller {
   C c = new C();
   function f() public view returns (uint) {
      return c.data(); //external access
   }
}
contract D is C {
   function y() public returns (uint) {
      iData = 3; // internal access
      return iData;
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return storedData; //access the state variable
   }
}

Cos'è un operatore?

Prendiamo una semplice espressione 4 + 5 is equal to 9. Qui vengono chiamati 4 e 5operands e "+" è chiamato operator. Solidity supporta i seguenti tipi di operatori.

  • Operatori aritmetici
  • Operatori di confronto
  • Operatori logici (o relazionali)
  • Operatori di assegnazione
  • Operatori condizionali (o ternari)

Diamo uno sguardo a tutti gli operatori uno per uno.

Operatori aritmetici

Solidity supporta i seguenti operatori aritmetici:

Supponiamo che la variabile A contenga 10 e la variabile B ne contenga 20, quindi -

Mostra esempio

Sr.No. Operatore e descrizione
1

+ (Addition)

Aggiunge due operandi

Ex: A + B darà 30

2

- (Subtraction)

Sottrae il secondo operando dal primo

Ex: A - B darà -10

3

* (Multiplication)

Moltiplica entrambi gli operandi

Ex: A * B darà 200

4

/ (Division)

Dividi il numeratore per il denominatore

Ex: B / A darà 2

5

% (Modulus)

Restituisce il resto di una divisione intera

Ex: B% A darà 0

6

++ (Increment)

Aumenta un valore intero di uno

Ex: A ++ darà 11

7

-- (Decrement)

Diminuisce un valore intero di uno

Ex: A-- darà 9

Operatori di confronto

Solidity supporta i seguenti operatori di confronto:

Supponiamo che la variabile A contenga 10 e la variabile B ne contenga 20, quindi -

Mostra esempio

Sr.No. Operatore e descrizione
1

= = (Equal)

Controlla se il valore di due operandi è uguale o meno, in caso affermativo, la condizione diventa vera.

Ex: (A == B) non è vero.

2

!= (Not Equal)

Controlla se il valore di due operandi è uguale o meno, se i valori non sono uguali, la condizione diventa vera.

Ex: (A! = B) è vero.

3

> (Greater than)

Controlla se il valore dell'operando sinistro è maggiore del valore dell'operando destro, in caso affermativo la condizione diventa vera.

Ex: (A> B) non è vero.

4

< (Less than)

Controlla se il valore dell'operando sinistro è inferiore al valore dell'operando destro, in caso affermativo la condizione diventa vera.

Ex: (A <B) è vero.

5

>= (Greater than or Equal to)

Controlla se il valore dell'operando sinistro è maggiore o uguale al valore dell'operando destro, in caso affermativo, la condizione diventa vera.

Ex: (A> = B) non è vero.

6

<= (Less than or Equal to)

Controlla se il valore dell'operando sinistro è minore o uguale al valore dell'operando destro, se sì, la condizione diventa vera.

Ex: (A <= B) è vero.

Operatori logici

Solidity supporta i seguenti operatori logici:

Supponiamo che la variabile A contenga 10 e la variabile B ne contenga 20, quindi -

Mostra esempio

Sr.No. Operatore e descrizione
1

&& (Logical AND)

Se entrambi gli operandi sono diversi da zero, la condizione diventa vera.

Ex: (A && B) è vero.

2

|| (Logical OR)

Se uno dei due operandi è diverso da zero, la condizione diventa vera.

Ex: (A || B) è vero.

3

! (Logical NOT)

Inverte lo stato logico del suo operando. Se una condizione è vera, l'operatore NOT logico la renderà falsa.

Ex:! (A && B) è falso.

Operatori bit per bit

Solidity supporta i seguenti operatori bit per bit:

Supponiamo che la variabile A contenga 2 e la variabile B contenga 3, quindi -

Mostra esempio

Sr.No. Operatore e descrizione
1

& (Bitwise AND)

Esegue un'operazione booleana AND su ogni bit dei suoi argomenti interi.

Ex: (A e B) è 2.

2

| (BitWise OR)

Esegue un'operazione booleana OR su ogni bit dei suoi argomenti interi.

Ex: (A | B) è 3.

3

^ (Bitwise XOR)

Esegue un'operazione booleana OR esclusivo su ogni bit dei suoi argomenti interi. OR esclusivo significa che uno degli operandi uno è vero o l'operando due è vero, ma non entrambi.

Ex: (A ^ B) è 1.

4

~ (Bitwise Not)

È un operatore unario e opera invertendo tutti i bit nell'operando.

Ex: (~ B) è -4.

5

<< (Left Shift)

Sposta tutti i bit nel suo primo operando a sinistra del numero di posizioni specificato nel secondo operando. I nuovi bit vengono riempiti con zeri. Spostare un valore a sinistra di una posizione equivale a moltiplicarlo per 2, spostare due posizioni equivale a moltiplicarlo per 4 e così via.

Ex: (A << 1) è 4.

6

>> (Right Shift)

Operatore binario di spostamento a destra. Il valore dell'operando sinistro viene spostato a destra del numero di bit specificato dall'operando destro.

Ex: (A >> 1) è 1.

7

>>> (Right shift with Zero)

Questo operatore è proprio come l'operatore >>, tranne per il fatto che i bit spostati a sinistra sono sempre zero.

Ex: (A >>> 1) è 1.

Operatori di assegnazione

Solidity supporta i seguenti operatori di assegnazione:

Mostra esempio

Sr.No. Operatore e descrizione
1

= (Simple Assignment )

Assegna i valori dall'operando del lato destro all'operando del lato sinistro

Ex: C = A + B assegnerà il valore di A + B a C

2

+= (Add and Assignment)

Aggiunge l'operando destro all'operando sinistro e assegna il risultato all'operando sinistro.

Ex: C + = A è equivalente a C = C + A

3

−= (Subtract and Assignment)

Sottrae l'operando destro dall'operando sinistro e assegna il risultato all'operando sinistro.

Ex: C - = A è equivalente a C = C - A

4

*= (Multiply and Assignment)

Moltiplica l'operando destro con l'operando sinistro e assegna il risultato all'operando sinistro.

Ex: C * = A è equivalente a C = C * A

5

/= (Divide and Assignment)

Divide l'operando sinistro con l'operando destro e assegna il risultato all'operando sinistro.

Ex: C / = A è equivalente a C = C / A

6

%= (Modules and Assignment)

Richiede il modulo utilizzando due operandi e assegna il risultato all'operando sinistro.

Ex: C% = A è equivalente a C = C% A

Note - La stessa logica si applica agli operatori Bitwise, quindi diventeranno come << =, >> =, >> =, & =, | = e ^ =.

Operatore condizionale (? :)

L'operatore condizionale valuta prima un'espressione per un valore vero o falso e quindi esegue una delle due istruzioni fornite a seconda del risultato della valutazione.

Mostra esempio

Sr.No. Operatore e descrizione
1

? : (Conditional )

Se la condizione è vera? Quindi valore X: altrimenti valore Y

Durante la scrittura di un contratto, potresti incontrare una situazione in cui devi eseguire un'azione più e più volte. In tali situazioni, sarebbe necessario scrivere istruzioni di ciclo per ridurre il numero di righe.

Solidity supporta tutti i loop necessari per alleviare la pressione della programmazione.

Suor n Loop e descrizione
1

While Loop

Il ciclo più elementare in Solidity è il ciclo while che verrà discusso in questo capitolo.

2

fare ... mentre Loop

Il ciclo do ... while è simile al ciclo while tranne per il fatto che il controllo delle condizioni avviene alla fine del ciclo.

3

Per Loop

Il ciclo for è la forma più compatta di loop. Comprende le seguenti tre parti importanti.

4

Controllo del loop

Solidity fornisce il controllo completo per gestire i loop e le istruzioni switch.

Durante la scrittura di un programma, potrebbe verificarsi una situazione in cui è necessario adottarne uno da un determinato insieme di percorsi. In questi casi, è necessario utilizzare istruzioni condizionali che consentano al programma di prendere decisioni corrette ed eseguire azioni corrette.

Solidity supporta le istruzioni condizionali che vengono utilizzate per eseguire azioni diverse in base a condizioni diverse. Qui spiegheremo ilif..else dichiarazione.

Diagramma di flusso di if-else

Il seguente diagramma di flusso mostra come funziona l'istruzione if-else.

Solidity supporta le seguenti forme di if..else dichiarazione -

Suor n Dichiarazioni e descrizione
1

istruzione if

L'istruzione if è l'istruzione di controllo fondamentale che consente a Solidity di prendere decisioni ed eseguire le istruzioni in modo condizionale.

2

if ... else dichiarazione

L'istruzione "if ... else" è la forma successiva di istruzione di controllo che consente a Solidity di eseguire le istruzioni in modo più controllato.

3

if ... else if ... istruzione.

L'istruzione if ... else if ... è una forma avanzata di if ... else che consente a Solidity di prendere una decisione corretta tra diverse condizioni.

Solidity supporta String literal utilizzando sia virgolette doppie (") e virgolette singole ('). Fornisce string come tipo di dati per dichiarare una variabile di tipo String.

pragma solidity ^0.5.0;

contract SolidityTest {
   string data = "test";
}

Nell'esempio precedente, "test" è una stringa letterale e data è una variabile stringa. Il modo più preferito consiste nell'usare i tipi di byte invece di String poiché l'operazione con stringa richiede più gas rispetto all'operazione con i byte. Solidity fornisce la conversione incorporata tra byte in stringa e viceversa. In Solidity possiamo assegnare facilmente String letterale a una variabile di tipo byte32. Solidity lo considera un byte32 letterale.

pragma solidity ^0.5.0;

contract SolidityTest {
   bytes32 data = "test";
}

Caratteri di fuga

Sr.No. Carattere e descrizione
1

\n

Inizia una nuova riga.

2

\\

Barra rovesciata

3

\'

Citazione singola

4

\"

Doppia citazione

5

\b

Backspace

6

\f

Avanzamento modulo

7

\r

Ritorno in carrozza

8

\t

Tab

9

\v

Tab verticale

10

\xNN

Rappresenta il valore esadecimale e inserisce i byte appropriati.

11

\uNNNN

Rappresenta il valore Unicode e inserisce la sequenza UTF-8.

Conversione da byte a stringa

I byte possono essere convertiti in String utilizzando il costruttore string ().

bytes memory bstr = new bytes(10);
string message = string(bstr);

Esempio

Prova il codice seguente per capire come funziona la stringa in Solidity.

pragma solidity ^0.5.0;

contract SolidityTest {   
   constructor() public{       
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      
      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Produzione

0: string: 3

Array è una struttura di dati, che memorizza una raccolta sequenziale di dimensioni fisse di elementi dello stesso tipo. Un array viene utilizzato per memorizzare una raccolta di dati, ma spesso è più utile pensare a un array come una raccolta di variabili dello stesso tipo.

Invece di dichiarare singole variabili, come numero0, numero1, ... e numero99, dichiari una variabile di matrice come numeri e utilizzi numeri [0], numeri [1] e ..., numeri [99] per rappresentare variabili individuali. Un elemento specifico in un array è accessibile da un indice.

In Solidity, un array può essere di dimensione fissa in fase di compilazione o di dimensione dinamica. Per l'array di archiviazione, può anche avere diversi tipi di elementi. In caso di array di memoria, il tipo di elemento non può essere mappato e nel caso in cui debba essere utilizzato come parametro di funzione, il tipo di elemento dovrebbe essere un tipo ABI.

Tutti gli array sono costituiti da posizioni di memoria contigue. L'indirizzo più basso corrisponde al primo elemento e l'indirizzo più alto all'ultimo elemento.

Dichiarazione di array

Per dichiarare un array di dimensioni fisse in Solidity, il programmatore specifica il tipo di elementi e il numero di elementi richiesti da un array come segue:

type arrayName [ arraySize ];

Questo è chiamato array monodimensionale. IlarraySize deve essere una costante intera maggiore di zero e typepuò essere qualsiasi tipo di dati Solidity valido. Ad esempio, per dichiarare un array di 10 elementi chiamato balance di tipo uint, utilizzare questa istruzione:

uint balance[10];

Per dichiarare un array di dimensioni dinamiche in Solidity, il programmatore specifica il tipo degli elementi come segue:

type[] arrayName;

Inizializzazione degli array

È possibile inizializzare gli elementi dell'array Solidity uno per uno o utilizzando una singola istruzione come segue:

uint balance[3] = [1, 2, 3];

Il numero di valori tra parentesi graffe [] non può essere maggiore del numero di elementi che dichiariamo per l'array tra parentesi quadre []. Di seguito è riportato un esempio per assegnare un singolo elemento dell'array:

Se ometti la dimensione dell'array, viene creato un array abbastanza grande da contenere l'inizializzazione. Pertanto, se scrivi -

uint balance[] = [1, 2, 3];

Creerai esattamente lo stesso array che hai fatto nell'esempio precedente.

balance[2] = 5;

L'istruzione precedente assegna all'elemento numero 3 rd nell'array un valore di 5.

Creazione di array di memoria dinamici

Gli array di memoria dinamici vengono creati utilizzando una nuova parola chiave.

uint size = 3;
uint balance[] = new uint[](size);

Accesso agli elementi dell'array

Si accede a un elemento indicizzando il nome dell'array. Questo viene fatto inserendo l'indice dell'elemento tra parentesi quadre dopo il nome dell'array. Ad esempio:

uint salary = balance[2];

L'istruzione precedente prenderà il 3 ° elemento dall'array e assegnerà il valore alla variabile salary. Di seguito è riportato un esempio, che utilizzerà tutti i tre concetti sopra menzionati vale a dire. dichiarazione, assegnazione e accesso agli array -

Membri

  • length- length restituisce la dimensione dell'array. length può essere utilizzato per modificare la dimensione dell'array dinamico impostandolo.

  • push- push consente di aggiungere un elemento a un array di archiviazione dinamico alla fine. Restituisce la nuova lunghezza dell'array.

Esempio

Prova il codice seguente per capire come funzionano gli array in Solidity.

pragma solidity ^0.5.0;

contract test {
   function testArray() public pure{
      uint len = 7; 
      
      //dynamic array
      uint[] memory a = new uint[](7);
      
      //bytes is same as byte[]
      bytes memory b = new bytes(len);
      
      assert(a.length == 7);
      assert(b.length == len);
      
      //access array variable
      a[6] = 8;
      
      //test array variable
      assert(a[6] == 8);
      
      //static array
      uint[3] memory c = [uint(1) , 2, 3];
      assert(c.length == 3);
   }
}

Le enumerazioni limitano una variabile in modo che abbia solo uno dei pochi valori predefiniti. I valori in questo elenco enumerato sono chiamati enumerazioni.

Con l'uso di enumerazioni è possibile ridurre il numero di bug nel codice.

Ad esempio, se consideriamo una domanda per un negozio di succhi freschi, sarebbe possibile limitare le dimensioni del bicchiere a piccolo, medio e grande. Ciò garantirebbe che non consentirebbe a nessuno di ordinare dimensioni diverse da piccola, media o grande.

Esempio

Prova il codice seguente per capire come funziona l'enumerazione in Solidity.

pragma solidity ^0.5.0;

contract test {
   enum FreshJuiceSize{ SMALL, MEDIUM, LARGE }
   FreshJuiceSize choice;
   FreshJuiceSize constant defaultChoice = FreshJuiceSize.MEDIUM;

   function setLarge() public {
      choice = FreshJuiceSize.LARGE;
   }
   function getChoice() public view returns (FreshJuiceSize) {
      return choice;
   }
   function getDefaultChoice() public pure returns (uint) {
      return uint(defaultChoice);
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Primo clic setLarge Pulsante per impostare il valore su GRANDE, quindi fare clic getChoice per ottenere la scelta selezionata.

Produzione

uint8: 2

Clic getDefaultChoice Pulsante per ottenere la scelta predefinita.

Produzione

uint256: 1

I tipi di struttura vengono utilizzati per rappresentare un record. Supponi di voler tenere traccia dei tuoi libri in una biblioteca. Potresti voler monitorare i seguenti attributi di ogni libro:

  • Title
  • Author
  • Subject
  • ID libro

Definizione di una struttura

Per definire uno Struct, è necessario utilizzare il structparola chiave. La parola chiave struct definisce un nuovo tipo di dati, con più di un membro. Il formato dell'istruzione struct è il seguente:

struct struct_name { 
   type1 type_name_1;
   type2 type_name_2;
   type3 type_name_3;
}

Esempio

struct Book { 
   string title;
   string author;
   uint book_id;
}

Accesso a uno Struct e alla sua variabile

Per accedere a qualsiasi membro di una struttura, utilizziamo l'operatore di accesso ai membri (.). L'operatore di accesso al membro è codificato come un periodo tra il nome della variabile di struttura e il membro della struttura a cui si desidera accedere. Usereste la struttura per definire le variabili del tipo di struttura. L'esempio seguente mostra come utilizzare una struttura in un programma.

Esempio

Prova il codice seguente per capire come funzionano le strutture in Solidity.

pragma solidity ^0.5.0;

contract test {
   struct Book { 
      string title;
      string author;
      uint book_id;
   }
   Book book;

   function setBook() public {
      book = Book('Learn Java', 'TP', 1);
   }
   function getBookId() public view returns (uint) {
      return book.book_id;
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Primo clic setBook Pulsante per impostare il valore su GRANDE, quindi fare clic getBookId per ottenere l'ID del libro selezionato.

Produzione

uint256: 1

Il mapping è un tipo di riferimento come array e strutture. Di seguito è riportata la sintassi per dichiarare un tipo di mappatura.

mapping(_KeyType => _ValueType)

Dove

  • _KeyType- può essere qualsiasi tipo predefinito più byte e stringa. Nessun tipo di riferimento o oggetti complessi sono consentiti.

  • _ValueType - può essere di qualsiasi tipo.

Considerazioni

  • La mappatura può avere solo il tipo di storage e sono generalmente utilizzati per le variabili di stato.

  • La mappatura può essere contrassegnata come pubblica. La solidità crea automaticamente getter per questo.

Esempio

Prova il codice seguente per capire come funziona il tipo di mappatura in Solidity.

pragma solidity ^0.5.0;

contract LedgerBalance {
   mapping(address => uint) public balances;

   function updateBalance(uint newBalance) public {
      balances[msg.sender] = newBalance;
   }
}
contract Updater {
   function updateBalance() public returns (uint) {
      LedgerBalance ledgerBalance = new LedgerBalance();
      ledgerBalance.updateBalance(10);
      return ledgerBalance.balances(address(this));
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Primo clic updateBalance Pulsante per impostare il valore su 10, quindi esaminare i log che mostreranno l'output decodificato come -

Produzione

{
   "0": "uint256: 10"
}

La solidità consente la conversione implicita oltre che esplicita. Il compilatore Solidity consente la conversione implicita tra due tipi di dati, a condizione che non sia possibile alcuna conversione implicita e che non vi sia perdita di informazioni. Ad esempio, uint8 è convertibile in uint16 ma int8 è convertibile in uint256 poiché int8 può contenere un valore negativo non consentito in uint256.

Conversione esplicita

Possiamo convertire in modo esplicito un tipo di dati in un altro utilizzando la sintassi del costruttore.

int8 y = -3;
uint x = uint(y);
//Now x = 0xfffff..fd == two complement representation of -3 in 256 bit format.

La conversione al tipo più piccolo costa bit di ordine superiore.

uint32 a = 0x12345678;
uint16 b = uint16(a); // b = 0x5678

La conversione al tipo più alto aggiunge bit di riempimento a sinistra.

uint16 a = 0x1234;
uint32 b = uint32(a); // b = 0x00001234

La conversione in byte più piccoli costa dati di ordine superiore.

bytes2 a = 0x1234;
bytes1 b = bytes1(a); // b = 0x12

La conversione in byte più grandi aggiunge bit di riempimento a destra.

bytes2 a = 0x1234;
bytes4 b = bytes4(a); // b = 0x12340000

La conversione tra byte di dimensione fissa e int è possibile solo quando entrambi hanno la stessa dimensione.

bytes2 a = 0x1234;
uint32 b = uint16(a); // b = 0x00001234
uint32 c = uint32(bytes4(a)); // c = 0x12340000
uint8 d = uint8(uint16(a)); // d = 0x34
uint8 e = uint8(bytes1(a)); // e = 0x12

I numeri esadecimali possono essere assegnati a qualsiasi tipo di numero intero se non è necessario il troncamento.

uint8 a = 12; // no error
uint32 b = 1234; // no error
uint16 c = 0x123456; // error, as truncation required to 0x3456

In solidità possiamo usare wei, finney, szabo o ether come suffisso per un letterale da usare per convertire varie denominazioni basate sull'etere. L'unità più bassa è wei e 1e12 rappresenta 1 x 10 12 .

assert(1 wei == 1);
assert(1 szabo == 1e12);
assert(1 finney == 1e15);
assert(1 ether == 1e18);
assert(2 ether == 2000 fenny);

Unità di tempo

Simile alla valuta, Solidity ha unità di tempo in cui l'unità più bassa è il secondo e possiamo usare secondi, minuti, ore, giorni e settimane come suffisso per indicare il tempo.

assert(1 seconds == 1);
assert(1 minutes == 60 seconds);
assert(1 hours == 60 minutes);
assert(1 day == 24 hours);
assert(1 week == 7 days);

Le variabili speciali sono variabili disponibili a livello globale e forniscono informazioni sulla blockchain. Di seguito è riportato l'elenco delle variabili speciali:

Sr.No. Variabile speciale e descrizione
1

blockhash(uint blockNumber) returns (bytes32)

Hash del blocco specificato: funziona solo per i 256 blocchi più recenti, esclusi quelli correnti.

2

block.coinbase (address payable)

Indirizzo attuale del block miner.

3

block.difficulty (uint)

difficoltà di blocco attuale.

4

block.gaslimit (uint)

Limite gas blocco corrente.

5

block.number (uint)

Numero di blocco corrente.

6

block.timestamp

Timestamp del blocco corrente espresso in secondi dall'epoca unix.

7

gasleft() returns (uint256)

Gas rimanente.

8

msg.data (bytes calldata)

Calldata completa.

9

msg.sender (address payable)

Mittente del messaggio (chiamata in corso).

10

msg.sig (bytes4)

Primi quattro byte del calldata (cioè identificatore di funzione)

11

msg.value (uint)

Numero di wei inviati con il messaggio.

12

now (uint)

Timestamp blocco corrente (alias per block.timestamp).

13

tx.gasprice (uint)

Prezzo del gas della transazione.

14

tx.origin (address payable)

Mittente della transazione (catena di chiamate completa).

Esempio

Prova il codice seguente per vedere l'uso di msg, una variabile speciale per ottenere l'indirizzo del mittente in Solidity.

pragma solidity ^0.5.0;

contract LedgerBalance {
   mapping(address => uint) public balances;

   function updateBalance(uint newBalance) public {
      balances[msg.sender] = newBalance;
   }
}
contract Updater {
   function updateBalance() public returns (uint) {
      LedgerBalance ledgerBalance = new LedgerBalance();
      ledgerBalance.updateBalance(10);
      return ledgerBalance.balances(address(this));
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Primo clic updateBalance Pulsante per impostare il valore su 10, quindi esaminare i log che mostreranno l'output decodificato come -

Produzione

{
   "0": "uint256: 10"
}

Style Guide aiuta a mantenere il layout del codice coerente e a rendere il codice più leggibile. Di seguito sono riportate le migliori pratiche seguite durante la scrittura di contratti con Solidity.

Layout del codice

  • Indentation- Usa 4 spazi invece della tabulazione per mantenere il livello di rientro. Evita di mescolare gli spazi con le linguette.

  • Two Blank Lines Rule - Utilizzare 2 righe vuote tra due definizioni di contratto.

pragma solidity ^0.5.0;

contract LedgerBalance {
   //...
}
contract Updater {
   //...
}
  • One Blank Line Rule- Utilizzare 1 riga vuota tra due funzioni. In caso di sola dichiarazione non è necessario avere righe vuote.

pragma solidity ^0.5.0;

contract A {
   function balance() public pure;
   function account() public pure;
}
contract B is A {
   function balance() public pure {
      // ...
   }
   function account() public pure {
      // ...
   }
}
  • Maximum Line Length - Una singola riga non deve attraversare 79 caratteri in modo che i lettori possano facilmente analizzare il codice.

  • Wrapping rules- Il primo argomento deve essere in una nuova riga senza aprire parentesi. Usa un singolo rientro per argomento. Elemento terminale); dovrebbe essere l'ultimo.

function_with_a_long_name(
   longArgument1,
   longArgument2,
   longArgument3
);
variable = function_with_a_long_name(
   longArgument1,
   longArgument2,
   longArgument3
);
event multipleArguments(
   address sender,
   address recipient,
   uint256 publicKey,
   uint256 amount,
   bytes32[] options
);
MultipleArguments(
   sender,
   recipient,
   publicKey,
   amount,
   options
);
  • Source Code Encoding - La codifica UTF-8 o ASCII deve essere utilizzata preferibilmente.

  • Imports - Le istruzioni di importazione devono essere posizionate all'inizio del file subito dopo la dichiarazione pragma.

  • Order of Functions - Le funzioni dovrebbero essere raggruppate secondo la loro visibilità.

pragma solidity ^0.5.0;

contract A {
   constructor() public {
      // ...
   }
   function() external {
      // ...
   }

   // External functions
   // ...

   // External view functions
   // ...

   // External pure functions 
   // ...

   // Public functions
   // ...

   // Internal functions
   // ...

   // Private functions
   // ...
}
  • Avoid extra whitespaces - Evitare spazi bianchi immediatamente all'interno di parentesi, parentesi o parentesi graffe.

  • Control structures- Le parentesi graffe dovrebbero aprirsi sulla stessa riga della dichiarazione. Chiudere sulla propria linea mantenendo la stessa rientranza. Usa uno spazio con parentesi graffa di apertura.

pragma solidity ^0.5.0;

contract Coin {
   struct Bank {
      address owner;
      uint balance;
   }
}
if (x < 3) {
   x += 1;
} else if (x > 7) {
   x -= 1;
} else {
   x = 5;
}
if (x < 3)
   x += 1;
else
   x -= 1;
  • Function Declaration- Usa la regola sopra per le parentesi graffe. Aggiungi sempre un'etichetta di visibilità. L'etichetta di visibilità dovrebbe essere prima di qualsiasi modificatore personalizzato.

function kill() public onlyowner {
   selfdestruct(owner);
}
  • Mappings - Evita gli spazi bianchi durante la dichiarazione delle variabili di mappatura.

mapping(uint => uint) map;
mapping(address => bool) registeredAddresses;
mapping(uint => mapping(bool => Data[])) public data;
mapping(uint => mapping(uint => s)) data;
  • Variable declaration - Evita gli spazi bianchi durante la dichiarazione delle variabili di matrice.

uint[] x;  // not unit [] x;
  • String declaration - Usa virgolette doppie per dichiarare una stringa invece di virgolette singole.

str = "foo";
str = "Hamlet says, 'To be or not to be...'";

Ordine di layout

Gli elementi dovrebbero essere disposti nel seguente ordine.

  • Dichiarazioni di Pragma

  • Importa dichiarazioni

  • Interfaces

  • Libraries

  • Contracts

All'interno di interfacce, biblioteche o contratti l'ordine dovrebbe essere il seguente:

  • Dichiarazioni di tipo

  • Variabili di stato

  • Events

  • Functions

Convenzioni di denominazione

  • Il contratto e la libreria devono essere denominati utilizzando lo stile CapWords. Ad esempio, SmartContract, Owner ecc.

  • Il nome del contratto e della libreria devono corrispondere ai rispettivi nomi di file.

  • In caso di più contratti / librerie in un file, utilizzare il nome del contratto / libreria principale.

Owned.sol

pragma solidity ^0.5.0;

// Owned.sol
contract Owned {
   address public owner;
   constructor() public {
      owner = msg.sender;
   }
   modifier onlyOwner {
      //....
   }
   function transferOwnership(address newOwner) public onlyOwner {
      //...
   }
}

Congress.sol

pragma solidity ^0.5.0;

// Congress.sol
import "./Owned.sol";

contract Congress is Owned, TokenRecipient {
   //...
}
  • Nomi strutturali

    - Usa lo stile CapWords come SmartCoin.

  • Nomi degli eventi

    - Usa lo stile CapWords come Deposito, AfterTransfer.

  • Nomi delle funzioni

    - Usa lo stile mixedCase come initiateSupply.

  • Variabili locali e di stato

    - Usa lo stile mixedCase come creatorAddress, supply.

  • Costanti

    - Utilizza tutte le lettere maiuscole con trattino basso per separare parole come MAX_BLOCKS.

  • Nomi dei modificatori

    - Usa lo stile mixCase come onlyAfter.

  • Enum Names

    - Usa lo stile CapWords come TokenGroup.

Una funzione è un gruppo di codice riutilizzabile che può essere chiamato ovunque nel programma. Ciò elimina la necessità di scrivere ripetutamente lo stesso codice. Aiuta i programmatori a scrivere codici modulari. Le funzioni consentono a un programmatore di dividere un grande programma in un numero di funzioni piccole e gestibili.

Come ogni altro linguaggio di programmazione avanzato, Solidity supporta anche tutte le funzionalità necessarie per scrivere codice modulare utilizzando le funzioni. Questa sezione spiega come scrivere le proprie funzioni in Solidity.

Definizione di funzione

Prima di usare una funzione, dobbiamo definirla. Il modo più comune per definire una funzione in Solidity è usare ilfunction parola chiave, seguita da un nome di funzione univoco, un elenco di parametri (che potrebbe essere vuoto) e un blocco di istruzioni racchiuso tra parentesi graffe.

Sintassi

La sintassi di base è mostrata qui.

function function-name(parameter-list) scope returns() {
   //statements
}

Esempio

Prova il seguente esempio. Definisce una funzione chiamata getResult che non accetta parametri -

pragma solidity ^0.5.0;

contract Test {
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Chiamare una funzione

Per richiamare una funzione in un punto successivo del contratto, è sufficiente scrivere il nome di tale funzione come mostrato nel codice seguente.

Prova il codice seguente per capire come funziona la stringa in Solidity.

pragma solidity ^0.5.0;

contract SolidityTest {   
   constructor() public{       
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      
      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);//access local variable
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Produzione

0: string: 3

Parametri di funzione

Finora abbiamo visto funzioni senza parametri. Ma c'è la possibilità di passare parametri diversi durante la chiamata a una funzione. Questi parametri passati possono essere catturati all'interno della funzione e qualsiasi manipolazione può essere eseguita su quei parametri. Una funzione può accettare più parametri separati da virgola.

Esempio

Prova il seguente esempio. Abbiamo utilizzato un fileuint2strfunzione qui. Ci vuole un parametro.

pragma solidity ^0.5.0;

contract SolidityTest {   
   constructor() public{       
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      
      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);//access local variable
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Produzione

0: string: 3

La dichiarazione di ritorno

Una funzione Solidità può avere un optional returndichiarazione. Ciò è necessario se si desidera restituire un valore da una funzione. Questa istruzione dovrebbe essere l'ultima istruzione in una funzione.

Come nell'esempio precedente, stiamo usando la funzione uint2str per restituire una stringa.

In Solidity, una funzione può restituire anche più valori. Guarda l'esempio di seguito -

pragma solidity ^0.5.0;

contract Test {
   function getResult() public view returns(uint product, uint sum){
      uint a = 1; // local variable
      uint b = 2;
      product = a * b;
      sum = a + b;
  
      //alternative return statement to return 
      //multiple values
      //return(a*b, a+b);
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Produzione

0: uint256: product 2
1: uint256: sum 3

I modificatori di funzione vengono utilizzati per modificare il comportamento di una funzione. Ad esempio per aggiungere un prerequisito a una funzione.

Per prima cosa creiamo un modificatore con o senza parametro.

contract Owner {
   modifier onlyOwner {
      require(msg.sender == owner);
      _;
   }
   modifier costs(uint price) {
      if (msg.value >= price) {
         _;
      }
   }
}

Il corpo della funzione è inserito dove il simbolo speciale "_;" appare nella definizione di un modificatore. Quindi, se la condizione del modificatore è soddisfatta durante la chiamata a questa funzione, la funzione viene eseguita e in caso contrario viene generata un'eccezione.

Guarda l'esempio di seguito -

pragma solidity ^0.5.0;

contract Owner {
   address owner;
   constructor() public {
      owner = msg.sender;
   }
   modifier onlyOwner {
      require(msg.sender == owner);
      _;
   }
   modifier costs(uint price) {
      if (msg.value >= price) {
         _;
      }
   }
}
contract Register is Owner {
   mapping (address => bool) registeredAddresses;
   uint price;
   constructor(uint initialPrice) public { price = initialPrice; }
   
   function register() public payable costs(price) {
      registeredAddresses[msg.sender] = true;
   }
   function changePrice(uint _price) public onlyOwner {
      price = _price;
   }
}

Le funzioni di visualizzazione assicurano che non modificheranno lo stato. Una funzione può essere dichiarata comeview. Le seguenti istruzioni, se presenti nella funzione, sono considerate modificanti lo stato e il compilatore lancerà un avviso in questi casi.

  • Modifica delle variabili di stato.

  • Emissione di eventi.

  • Creazione di altri contratti.

  • Usare l'autodistruzione.

  • Invio di Ether tramite chiamate.

  • Chiamare qualsiasi funzione che non sia contrassegnata come view o pure.

  • Utilizzo di chiamate di basso livello.

  • Utilizzo dell'assieme in linea contenente determinati codici operativi.

Il metodo Getter sono funzioni di visualizzazione predefinite.

Vedere l'esempio di seguito utilizzando una funzione di visualizzazione.

Esempio

pragma solidity ^0.5.0;

contract Test {
   function getResult() public view returns(uint product, uint sum){
      uint a = 1; // local variable
      uint b = 2;
      product = a * b;
      sum = a + b; 
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Produzione

0: uint256: product 2
1: uint256: sum 3

Le funzioni pure assicurano che non leggano o modificano lo stato. Una funzione può essere dichiarata comepure. Le seguenti istruzioni, se presenti nella funzione, sono considerate come la lettura dello stato e il compilatore lancerà un avviso in questi casi.

  • Lettura delle variabili di stato.

  • Accesso all'indirizzo (this) .balance o <address> .balance.

  • Accedere a una qualsiasi delle variabili speciali di block, tx, msg (msg.sig e msg.data possono essere letti).

  • Chiamare qualsiasi funzione non contrassegnata come pura.

  • Utilizzo dell'assemblaggio in linea che contiene determinati codici operativi.

Le funzioni pure possono utilizzare le funzioni revert () e require () per annullare potenziali cambiamenti di stato se si verifica un errore.

Vedere l'esempio di seguito utilizzando una funzione di visualizzazione.

Esempio

pragma solidity ^0.5.0;

contract Test {
   function getResult() public pure returns(uint product, uint sum){
      uint a = 1; 
      uint b = 2;
      product = a * b;
      sum = a + b; 
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Produzione

0: uint256: product 2
1: uint256: sum 3

La funzione di fallback è una funzione speciale disponibile per un contratto. Ha le seguenti caratteristiche:

  • Viene chiamato quando una funzione inesistente viene richiamata sul contratto.

  • È necessario essere contrassegnati come esterni.

  • Non ha nome.

  • Non ha argomenti

  • Non può restituire nulla.

  • Può essere definito uno per contratto.

  • Se non contrassegnato come pagabile, genererà un'eccezione se il contratto riceve un semplice ether senza dati.

L'esempio seguente mostra il concetto di una funzione di fallback per contratto.

Esempio

pragma solidity ^0.5.0;

contract Test {
   uint public x ;
   function() external { x = 1; }    
}
contract Sink {
   function() external payable { }
}
contract Caller {
   function callTest(Test test) public returns (bool) {
      (bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
      require(success);
      // test.x is now 1

      address payable testPayable = address(uint160(address(test)));

      // Sending ether to Test contract,
      // the transfer will fail, i.e. this returns false here.
      return (testPayable.send(2 ether));
   }
   function callSink(Sink sink) public returns (bool) {
      address payable sinkPayable = address(sink);
      return (sinkPayable.send(2 ether));
   }
}

È possibile avere più definizioni per lo stesso nome di funzione nello stesso ambito. La definizione della funzione deve differire l'una dall'altra per i tipi e / o il numero di argomenti nell'elenco degli argomenti. Non è possibile eseguire l'overload di dichiarazioni di funzione che differiscono solo per il tipo restituito.

L'esempio seguente mostra il concetto di sovraccarico di una funzione in Solidity.

Esempio

pragma solidity ^0.5.0;

contract Test {
   function getSum(uint a, uint b) public pure returns(uint){      
      return a + b;
   }
   function getSum(uint a, uint b, uint c) public pure returns(uint){      
      return a + b + c;
   }
   function callSumWithTwoArguments() public pure returns(uint){
      return getSum(1,2);
   }
   function callSumWithThreeArguments() public pure returns(uint){
      return getSum(1,2,3);
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Fare prima clic sul pulsante callSumWithTwoArguments, quindi chiamare il pulsanteSumWithThreeArguments per visualizzare il risultato.

Produzione

0: uint256: 3
0: uint256: 6

Solidity fornisce anche funzioni matematiche integrate. Di seguito sono riportati metodi ampiamente utilizzati:

  • addmod(uint x, uint y, uint k) returns (uint)- calcola (x + y)% k dove l'aggiunta viene eseguita con precisione arbitraria e non va a capo a 2 256 .

  • mulmod(uint x, uint y, uint k) returns (uint)- calcola (x * y)% k dove l'aggiunta viene eseguita con precisione arbitraria e non va a capo a 2 256 .

L'esempio seguente mostra l'utilizzo di funzioni matematiche in Solidity.

Esempio

pragma solidity ^0.5.0;

contract Test {   
   function callAddMod() public pure returns(uint){
      return addmod(4, 5, 3);
   }
   function callMulMod() public pure returns(uint){
      return mulmod(4, 5, 3);
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Fare prima clic sul pulsante callAddMod e quindi sul pulsante callMulMod per vedere il risultato.

Produzione

0: uint256: 0
0: uint256: 2

Solidity fornisce anche funzioni crittografiche integrate. Di seguito sono riportati metodi importanti:

  • keccak256(bytes memory) returns (bytes32) - calcola l'hash Keccak-256 dell'input.

  • sha256(bytes memory) returns (bytes32) - calcola l'hash SHA-256 dell'input.

  • ripemd160(bytes memory) returns (bytes20) - calcola l'hash RIPEMD-160 dell'input.

  • sha256(bytes memory) returns (bytes32) - calcola l'hash SHA-256 dell'input.

  • ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)- recuperare l'indirizzo associato alla chiave pubblica dalla firma della curva ellittica o restituire zero in caso di errore. I parametri della funzione corrispondono ai valori ECDSA della firma: r - primi 32 byte della firma; s: secondi 32 byte di firma; v: 1 byte finale di firma. Questo metodo restituisce un indirizzo.

L'esempio seguente mostra l'utilizzo della funzione crittografica in Solidity.

Esempio

pragma solidity ^0.5.0;

contract Test {   
   function callKeccak256() public pure returns(bytes32 result){
      return keccak256("ABC");
   }  
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Produzione

0: bytes32: result 0xe1629b9dda060bb30c7908346f6af189c16773fa148d3366701fbaa35d54f3c8

Il modello di prelievo garantisce che non venga effettuata una chiamata di trasferimento diretto, il che rappresenta una minaccia per la sicurezza. Il seguente contratto mostra l'uso insicuro della chiamata di trasferimento per inviare ether.

pragma solidity ^0.5.0;

contract Test {
   address payable public richest;
   uint public mostSent;

   constructor() public payable {
      richest = msg.sender;
      mostSent = msg.value;
   }
   function becomeRichest() public payable returns (bool) {
      if (msg.value > mostSent) {
         // Insecure practice
         richest.transfer(msg.value);
         richest = msg.sender;
         mostSent = msg.value;
         return true;
      } else {
         return false;
      }
   }
}

Il contratto sopra può essere reso in uno stato inutilizzabile facendo sì che il più ricco sia un contratto con funzione di fallback in errore. Quando la funzione fallback fallisce, fallisce anche la funzione diventaRichest () e il contratto rimarrà bloccato per sempre. Per mitigare questo problema, possiamo utilizzare Withdrawal Pattern.

Nel modello di prelievo, reimposteremo l'importo in sospeso prima di ogni trasferimento. Garantirà che solo il contratto del chiamante fallisca.

pragma solidity ^0.5.0;

contract Test {
   address public richest;
   uint public mostSent;

   mapping (address => uint) pendingWithdrawals;

   constructor() public payable {
      richest = msg.sender;
      mostSent = msg.value;
   }
   function becomeRichest() public payable returns (bool) {
      if (msg.value > mostSent) {
         pendingWithdrawals[richest] += msg.value;
         richest = msg.sender;
         mostSent = msg.value;
         return true;
      } else {
         return false;
      }
   }
   function withdraw() public {
      uint amount = pendingWithdrawals[msg.sender];
      pendingWithdrawals[msg.sender] = 0;
      msg.sender.transfer(amount);
   }
}

L'accesso limitato a un contratto è una pratica comune. Per impostazione predefinita, uno stato del contratto è di sola lettura a meno che non sia specificato come pubblico.

Possiamo limitare chi può modificare lo stato del contratto o chiamare le funzioni di un contratto utilizzando i modificatori. Creeremo e utilizzeremo più modificatori come spiegato di seguito:

  • onlyBy - una volta utilizzato su una funzione, solo il chiamante menzionato può chiamare questa funzione.

  • onlyAfter - una volta usata su una funzione, quella funzione può essere chiamata dopo un certo periodo di tempo.

  • costs - una volta utilizzata su una funzione, il chiamante può chiamare questa funzione solo se viene fornito un determinato valore.

Esempio

pragma solidity ^0.5.0;

contract Test {
   address public owner = msg.sender;
   uint public creationTime = now;

   modifier onlyBy(address _account) {
      require(
         msg.sender == _account,
         "Sender not authorized."
      );
      _;
   }
   function changeOwner(address _newOwner) public onlyBy(owner) {
      owner = _newOwner;
   }
   modifier onlyAfter(uint _time) {
      require(
         now >= _time,
         "Function called too early."
      );
      _;
   }
   function disown() public onlyBy(owner) onlyAfter(creationTime + 6 weeks) {
      delete owner;
   }
   modifier costs(uint _amount) {
      require(
         msg.value >= _amount,
         "Not enough Ether provided."
      );
      _;
      if (msg.value > _amount)
         msg.sender.transfer(msg.value - _amount);
   }
   function forceOwnerChange(address _newOwner) public payable costs(200 ether) {
      owner = _newOwner;
      if (uint(owner) & 0 == 1) return;        
   }
}

Contract in Solidity è simile a una classe in C ++. Un contratto ha le seguenti proprietà.

  • Constructor - Una funzione speciale dichiarata con la parola chiave del costruttore che verrà eseguita una volta per contratto e viene invocata quando viene creato un contratto.

  • State Variables - Variabili per contratto per memorizzare lo stato del contratto.

  • Functions - Funzioni per contratto che possono modificare le variabili di stato per alterare lo stato di un contratto.

Quantificatori di visibilità

Di seguito sono riportati vari quantificatori di visibilità per funzioni / variabili di stato di un contratto.

  • external- Le funzioni esterne devono essere chiamate da altri contratti. Non possono essere utilizzati per chiamate interne. Per chiamare una funzione esterna all'interno del contratto è necessaria la chiamata this.function_name (). Le variabili di stato non possono essere contrassegnate come esterne.

  • public- Le funzioni / variabili pubbliche possono essere utilizzate sia esternamente che internamente. Per la variabile di stato pubblico, Solidity crea automaticamente una funzione getter.

  • internal - Le funzioni / variabili interne possono essere utilizzate solo internamente o da contratti derivati.

  • private - Le funzioni / variabili private possono essere utilizzate solo internamente e nemmeno da contratti derivati.

Esempio

pragma solidity ^0.5.0;

contract C {
   //private state variable
   uint private data;
   
   //public state variable
   uint public info;

   //constructor
   constructor() public {
      info = 10;
   }
   //private function
   function increment(uint a) private pure returns(uint) { return a + 1; }
   
   //public function
   function updateData(uint a) public { data = a; }
   function getData() public view returns(uint) { return data; }
   function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}
//External Contract
contract D {
   function readData() public returns(uint) {
      C c = new C();
      c.updateData(7);         
      return c.getData();
   }
}
//Derived Contract
contract E is C {
   uint private result;
   C private c;
   
   constructor() public {
      c = new C();
   }  
   function getComputedResult() public {      
      result = compute(3, 5); 
   }
   function getResult() public view returns(uint) { return result; }
   function getData() public view returns(uint) { return c.info(); }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application . Esegui vari metodi di contratti. Per E.getComputedResult () seguito da E.getResult () mostra -

Produzione

0: uint256: 8

L'ereditarietà è un modo per estendere la funzionalità di un contratto. Solidity supporta sia l'ereditarietà singola che multipla. Di seguito sono riportati i principali punti salienti.

  • Un contratto derivato può accedere a tutti i membri non privati ​​inclusi i metodi interni e le variabili di stato. Ma l'utilizzo di questo non è consentito.

  • L'override della funzione è consentito purché la firma della funzione rimanga la stessa. In caso di differenza dei parametri di output, la compilazione fallirà.

  • Possiamo chiamare la funzione di un super contratto usando la parola chiave super o usando il nome del super contratto.

  • In caso di ereditarietà multipla, la chiamata di funzione utilizzando super dà la preferenza alla maggior parte dei contratti derivati.

Esempio

pragma solidity ^0.5.0;

contract C {
   //private state variable
   uint private data;
   
   //public state variable
   uint public info;

   //constructor
   constructor() public {
      info = 10;
   }
   //private function
   function increment(uint a) private pure returns(uint) { return a + 1; }
   
   //public function
   function updateData(uint a) public { data = a; }
   function getData() public view returns(uint) { return data; }
   function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}
//Derived Contract
contract E is C {
   uint private result;
   C private c;
   constructor() public {
      c = new C();
   }  
   function getComputedResult() public {      
      result = compute(3, 5); 
   }
   function getResult() public view returns(uint) { return result; }
   function getData() public view returns(uint) { return c.info(); }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application . Esegui vari metodi di contratti. Per E.getComputedResult () seguito da E.getResult () mostra -

Produzione

0: uint256: 8

Constructor è una funzione speciale dichiarata utilizzando constructorparola chiave. È una funzione opzionale e viene utilizzata per inizializzare le variabili di stato di un contratto. Di seguito sono riportate le caratteristiche chiave di un costruttore.

  • Un contratto può avere un solo costruttore.

  • Un codice costruttore viene eseguito una volta quando viene creato un contratto e viene utilizzato per inizializzare lo stato del contratto.

  • Dopo l'esecuzione di un codice costruttore, il codice finale viene distribuito su blockchain. Questo codice include funzioni pubbliche e codice raggiungibile tramite funzioni pubbliche. Il codice del costruttore o qualsiasi metodo interno utilizzato solo dal costruttore non sono inclusi nel codice finale.

  • Un costruttore può essere pubblico o interno.

  • Un costruttore interno contrassegna il contratto come astratto.

  • Nel caso in cui non sia definito alcun costruttore, nel contratto è presente un costruttore predefinito.

pragma solidity ^0.5.0;

contract Test {
   constructor() public {}
}
  • Nel caso in cui il contratto base abbia un costruttore con argomenti, ogni contratto derivato deve passarli.

  • Il costruttore di base può essere inizializzato direttamente usando il modo seguente:

pragma solidity ^0.5.0;

contract Base {
   uint data;
   constructor(uint _data) public {
      data = _data;   
   }
}
contract Derived is Base (5) {
   constructor() public {}
}
  • Il costruttore di base può essere inizializzato indirettamente usando il modo seguente:

pragma solidity ^0.5.0;

contract Base {
   uint data;
   constructor(uint _data) public {
      data = _data;   
   }
}
contract Derived is Base {
   constructor(uint _info) Base(_info * _info) public {}
}
  • Non sono consentiti metodi diretti e indiretti per inizializzare il costruttore del contratto di base.

  • Se il contratto derivato non passa gli argomenti al costruttore del contratto di base, il contratto derivato diventerà astratto.

Il contratto astratto è uno che contiene almeno una funzione senza alcuna implementazione. Tale contratto viene utilizzato come contratto base. Generalmente un contratto astratto contiene funzioni sia implementate che astratte. Il contratto derivato implementerà la funzione astratta e utilizzerà le funzioni esistenti come e quando richiesto.

Nel caso in cui un contratto derivato non stia implementando la funzione astratta, questo contratto derivato verrà contrassegnato come astratto.

Esempio

Prova il codice seguente per capire come funzionano i contratti astratti in Solidity.

pragma solidity ^0.5.0;

contract Calculator {
   function getResult() public view returns(uint);
}
contract Test is Calculator {
   function getResult() public view returns(uint) {
      uint a = 1;
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Produzione

0: uint256: 3

Le interfacce sono simili ai contratti astratti e vengono create utilizzando interfaceparola chiave. Di seguito sono riportate le caratteristiche chiave di un'interfaccia.

  • L'interfaccia non può avere alcuna funzione con l'implementazione.

  • Le funzioni di un'interfaccia possono essere solo di tipo esterno.

  • L'interfaccia non può avere un costruttore.

  • L'interfaccia non può avere variabili di stato.

  • L'interfaccia può avere enum, strutture a cui è possibile accedere utilizzando la notazione punto del nome dell'interfaccia.

Esempio

Prova il codice seguente per capire come funziona l'interfaccia in Solidity.

pragma solidity ^0.5.0;

interface Calculator {
   function getResult() external view returns(uint);
}
contract Test is Calculator {
   constructor() public {}
   function getResult() external view returns(uint){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Note - Seleziona Test dal menu a discesa prima di fare clic sul pulsante di distribuzione.

Produzione

0: uint256: 3

Le biblioteche sono simili ai contratti ma sono principalmente destinate al riutilizzo. Una libreria contiene funzioni che altri contratti possono chiamare. Solidity ha alcune limitazioni sull'uso di una libreria. Di seguito sono riportate le caratteristiche chiave di una Solidity Library.

  • Le funzioni di libreria possono essere chiamate direttamente se non modificano lo stato. Ciò significa che le funzioni pure o di visualizzazione possono essere chiamate solo dall'esterno della libreria.

  • La libreria non può essere distrutta poiché si presume sia apolide.

  • Una libreria non può avere variabili di stato.

  • Una libreria non può ereditare alcun elemento.

  • Una libreria non può essere ereditata.

Esempio

Prova il codice seguente per capire come funziona una libreria in Solidity.

pragma solidity ^0.5.0;

library Search {
   function indexOf(uint[] storage self, uint value) public view returns (uint) {
      for (uint i = 0; i < self.length; i++) if (self[i] == value) return i;
      return uint(-1);
   }
}
contract Test {
   uint[] data;
   constructor() public {
      data.push(1);
      data.push(2);
      data.push(3);
      data.push(4);
      data.push(5);
   }
   function isValuePresent() external view returns(uint){
      uint value = 4;
      
      //search if value is present in the array using Library function
      uint index = Search.indexOf(data, value);
      return index;
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Note - Seleziona Test dal menu a discesa prima di fare clic sul pulsante di distribuzione.

Produzione

0: uint256: 3

Utilizzando For

La direttiva using A for B; può essere utilizzato per collegare le funzioni di libreria della libreria A a un dato tipo B. Queste funzioni utilizzeranno il tipo di chiamante come primo parametro (identificato utilizzando self).

Esempio

Prova il codice seguente per capire come funziona una libreria in Solidity.

pragma solidity ^0.5.0;

library Search {
   function indexOf(uint[] storage self, uint value) public view returns (uint) {
      for (uint i = 0; i < self.length; i++)if (self[i] == value) return i;
      return uint(-1);
   }
}
contract Test {
   using Search for uint[];
   uint[] data;
   constructor() public {
      data.push(1);
      data.push(2);
      data.push(3);
      data.push(4);
      data.push(5);
   }
   function isValuePresent() external view returns(uint){
      uint value = 4;      
      
      //Now data is representing the Library
      uint index = data.indexOf(value);
      return index;
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Note - Seleziona Test dal menu a discesa prima di fare clic sul pulsante di distribuzione.

Produzione

0: uint256: 3

Solidity fornisce un'opzione per utilizzare il linguaggio assembly per scrivere assembly inline all'interno del codice sorgente Solidity. Possiamo anche scrivere un codice assembly autonomo che verrà poi convertito in bytecode. Standalone Assembly è un linguaggio intermedio per un compilatore Solidity e converte il codice Solidity in un Assembly Standalone e quindi in byte code. È possibile utilizzare lo stesso linguaggio utilizzato in Inline Assembly per scrivere codice in un assembly autonomo.

Assemblaggio in linea

Il codice assembly inline può essere intercalato all'interno del codice di base Solidity per avere un controllo più fine su EVM e viene utilizzato soprattutto durante la scrittura delle funzioni di libreria.

Un codice assembly è scritto sotto assembly { ... } bloccare.

Esempio

Prova il codice seguente per capire come funziona una libreria in Solidity.

pragma solidity ^0.5.0;

library Sum {   
   function sumUsingInlineAssembly(uint[] memory _data) public pure returns (uint o_sum) {
      for (uint i = 0; i < _data.length; ++i) {
         assembly {
            o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
         }
      }
   }
}
contract Test {
   uint[] data;
   
   constructor() public {
      data.push(1);
      data.push(2);
      data.push(3);
      data.push(4);
      data.push(5);
   }
   function sum() external view returns(uint){      
      return Sum.sumUsingInlineAssembly(data);
   }
}

Eseguire il programma precedente utilizzando i passaggi forniti nel capitolo Solidity First Application .

Note - Seleziona Test dal menu a discesa prima di fare clic sul pulsante di distribuzione.

Produzione

0: uint256: 15

Event è un membro ereditabile di un contratto. Viene emesso un evento, memorizza gli argomenti passati nei log delle transazioni. Questi log sono archiviati su blockchain e sono accessibili utilizzando l'indirizzo del contratto fino a quando il contratto è presente sulla blockchain. Un evento generato non è accessibile dall'interno dei contratti, nemmeno quello che li ha creati ed emessi.

Un evento può essere dichiarato utilizzando la parola chiave evento.

//Declare an Event
event Deposit(address indexed _from, bytes32 indexed _id, uint _value);

//Emit an event
emit Deposit(msg.sender, _id, msg.value);

Esempio

Prova il codice seguente per capire come funziona un evento in Solidity.

Per prima cosa crea un contratto ed emetti un evento.

pragma solidity ^0.5.0;

contract Test {
   event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
   function deposit(bytes32 _id) public payable {      
      emit Deposit(msg.sender, _id, msg.value);
   }
}

Quindi accedi all'evento del contratto nel codice JavaScript.

var abi = /* abi as generated using compiler */;
var ClientReceipt = web3.eth.contract(abi);
var clientReceiptContract = ClientReceipt.at("0x1234...ab67" /* address */);

var event = clientReceiptContract.Deposit(function(error, result) {
   if (!error)console.log(result);
});

Dovrebbe stampare dettagli simili a quelli seguenti:

Produzione

{
   "returnValues": {
      "_from": "0x1111...FFFFCCCC",
      "_id": "0x50...sd5adb20",
      "_value": "0x420042"
   },
   "raw": {
      "data": "0x7f...91385",
      "topics": ["0xfd4...b4ead7", "0x7f...1a91385"]
   }
}

Solidity fornisce varie funzioni per la gestione degli errori. In genere, quando si verifica un errore, lo stato viene ripristinato al suo stato originale. Altri controlli servono per impedire l'accesso non autorizzato al codice. Di seguito sono riportati alcuni dei metodi importanti utilizzati nella gestione degli errori:

  • assert(bool condition)- Nel caso in cui la condizione non venga soddisfatta, questa chiamata al metodo causa un codice operativo non valido e tutte le modifiche apportate allo stato vengono ripristinate. Questo metodo deve essere utilizzato per errori interni.

  • require(bool condition)- Nel caso in cui la condizione non venga soddisfatta, questa chiamata al metodo torna allo stato originale. - Questo metodo deve essere utilizzato per errori negli ingressi o nei componenti esterni.

  • require(bool condition, string memory message)- Nel caso in cui la condizione non venga soddisfatta, questa chiamata al metodo torna allo stato originale. - Questo metodo deve essere utilizzato per errori negli ingressi o nei componenti esterni. Fornisce un'opzione per fornire un messaggio personalizzato.

  • revert() - Questo metodo interrompe l'esecuzione e ripristina tutte le modifiche apportate allo stato.

  • revert(string memory reason)- Questo metodo interrompe l'esecuzione e ripristina tutte le modifiche apportate allo stato. Fornisce un'opzione per fornire un messaggio personalizzato.

Esempio

Prova il codice seguente per capire come funziona la gestione degli errori in Solidity.

pragma solidity ^0.5.0;

contract Vendor {
   address public seller;
   modifier onlySeller() {
      require(
         msg.sender == seller,
         "Only seller can call this."
      );
      _;
   }
   function sell(uint amount) public payable onlySeller { 
      if (amount > msg.value / 2 ether)
         revert("Not enough Ether provided.");
      // Perform the sell operation.
   }
}

Quando viene chiamato il ripristino, restituirà i dati esadecimali come segue.

Produzione

0x08c379a0                     // Function selector for Error(string)
0x0000000000000000000000000000000000000000000000000000000000000020 // Data offset
0x000000000000000000000000000000000000000000000000000000000000001a // String length
0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // String data