Vai al contenuto principale
Change page

Anatomia dei contratti intelligenti

Ultima modifica: @Herbie_23(opens in a new tab), 14 giugno 2024

Un contratto intelligente è un programma eseguito a un indirizzo su Ethereum. È composto di dati e funzioni che entrano in esecuzione appena si riceve una transazione. Ecco una panoramica di cosa compone un contratto intelligente.

Prerequisiti

Prima, assicurati di aver letto a riguardo dei contratti intelligenti. Questa pagina presuppone che si conoscano i linguaggi di programmazione come JavaScript o Python.

Dati

Tutti i dati del contratto devono essere assegnati a una posizione: storage oppure memory. Modificare l'archiviazione in un contratto intelligente è dispendioso, devi quindi considerare dove dovrebbero risiedere i tuoi dati.

Storage

I dati persistenti sono detti storage (o spazio di archiviazione) e sono rappresentati da variabili di stato. Questi valori sono memorizzati permanentemente nella blockchain. È necessario dichiarare il tipo così che il contratto possa tenere traccia di quanto storage è necessario sulla blockchain quando viene compilato.

1// Esempio in Solidity
2contract SimpleStorage {
3 uint storedData; // Variabile di stato
4 // ...
5}
Copia
1# Esempio in Vyper
2storedData: int128
Copia

Se hai già programmato con linguaggi orientati agli oggetti, è probabile che tu abbia famigliarità con la maggior parte dei tipi, ma address potrebbe non essere noto se non hai mai sviluppato per Ethereum.

Un tipo address può contenere un indirizzo Ethereum che equivale a 20 byte o 160 bit. Restituisce una notazione esadecimale preceduta da 0x.

Altri tipi includono:

  • booleano
  • numero intero
  • numeri a virgola fissa
  • array di byte a dimensione fissa
  • array di byte di dimensioni dinamiche
  • letterali interi e razionali
  • stringhe
  • letterali esadecimali
  • enumerazioni

Per ulteriori spiegazioni, consulta la documentazione:

Memory

I valori che vengono memorizzati solo per la durata di esecuzione di una funzione di contratto sono detti variabili di memoria. Dal momento che non sono memorizzati in modo permanente sulla blockchain, sono molto più economici da usare.

Scopri di più su come l'EVM memorizza i dati (Archiviazione, Memoria e lo Stack), nella documentazione di Solidity(opens in a new tab).

Variabili d'ambiente

Oltre alle variabili che vengono definite nel contratto, sono presenti alcune variabili globali speciali. Vengono utilizzate principalmente per fornire informazioni sulla blockchain o sulla transazione corrente.

Esempi:

ProprietàVariabile di statoDescrizione
block.timestampuint256Data/ora dell'epoca del blocco corrente
msg.senderaddressMittente del messaggio (chiamata corrente)

Funzioni

In termini estremamente semplici, le funzioni possono ottenere informazioni o impostarle in risposta alle transazioni in arrivo.

Ci sono due tipi di chiamata di funzione:

  • internal – non creano una chiamata all'EVM
    • Le funzioni interne e le variabili di stato sono accessibili solo internamente (ovvero dall'interno del contratto corrente o dei contratti derivanti da esso).
  • external – creano una chiamata all'EVM
    • Le funzioni esterne fanno parte dell'interfaccia del contratto, quindi possono essere chiamate da altri contratti e tramite transazioni. Una funzione esterna f non può essere chiamata internamente (quindi f() non funziona, ma this.f() funziona).

Possono anche essere public o private

  • Le funzioni public possono essere chiamate direttamente dall'interno del contratto o dall'esterno tramite messaggi
  • Le funzioni private sono visibili solo per il contratto in cui sono definite e non da contratti derivati

Sia le funzioni che le variabili di stato possono essere rese pubbliche o private

Questa è una funzione per aggiornare una variabile di stato su un contratto:

1// Esempio in Solidity
2function update_name(string value) public {
3 dapp_name = value;
4}
Copia
  • Il parametro value di tipo string viene passato alla funzione: update_name
  • È dichiarato public e quindi chiunque può accedervi
  • Non è dichiarato view, quindi può modificare lo stato del contratto

Funzioni view

Queste funzioni promettono di non modificare lo stato dei dati del contratto. Tra gli esempi più comuni vi sono le funzioni "getter": puoi usarle ad esempio per ricevere un saldo dell'utente.

1// Esempio in Solidity
2function balanceOf(address _owner) public view returns (uint256 _balance) {
3 return ownerPizzaCount[_owner];
4}
Copia
1dappName: public(string)
2
3@view
4@public
5def readName() -> string:
6 return dappName
Copia

Ecco cosa è considerato modifica dello stato:

  1. Scrittura su variabili di stato.
  2. Emissione di eventi(opens in a new tab).
  3. Creazione di altri contratti(opens in a new tab).
  4. Uso di selfdestruct.
  5. Invio di ether tramite chiamate.
  6. Chiamata di qualsiasi funzione non contrassegnata con view o pure.
  7. Utilizzo di chiamate di basso livello.
  8. Utilizzo di assembly inline contenente determinati opcode.

Funzioni constructor

Quando il contratto viene distribuito per la prima volta, le funzioni constructor sono eseguite solo una volta. Come accade per constructor in molti linguaggi di programmazione basati su classi, queste funzioni spesso inizializzano le variabili di stato ai valori specificati.

1// Esempi in Solidity
2// Inizializza i dati del contratto, impostando `owner`
3// sull'indirizzo del creatore del contratto.
4constructor() public {
5 // Tutti gli Smart Contract si basano su transazioni esterne per attivare le proprie funzioni.
6 // `msg` è una variabile globale che include dati sulla transazione specificata,
7 // come indirizzo del mittente e valore degli ETH inclusi nella transazione.
8 // Per saperne di più: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
9 owner = msg.sender;
10}
Mostra tutto
Copia
1# Esempio in Vyper
2
3@external
4def __init__(_beneficiary: address, _bidding_time: uint256):
5 self.beneficiary = _beneficiary
6 self.auctionStart = block.timestamp
7 self.auctionEnd = self.auctionStart + _bidding_time
Copia

Funzioni integrate

Oltre alle variabili che vengono definite nel contratto, sono presenti alcune funzioni speciali integrate. L'esempio più evidente è:

  • address.send() – Solidity
  • send(address) – Vyper

Queste, consentono ai contratti di inviare ETH agli altri conti.

Scrivere funzioni

Una funzione ha bisogno di:

  • variabile e tipo di parametro (se accetta parametri)
  • dichiarazione interna/esterna
  • dichiarazione pure/view/payable
  • tipo di valore restituito (se restituisce un valore)
1pragma solidity >=0.4.0 <=0.6.0;
2
3contract ExampleDapp {
4 string dapp_name; // state variable
5
6 // Called when the contract is deployed and initializes the value
7 constructor() public {
8 dapp_name = "My Example dapp";
9 }
10
11 // Get Function
12 function read_name() public view returns(string) {
13 return dapp_name;
14 }
15
16 // Set Function
17 function update_name(string value) public {
18 dapp_name = value;
19 }
20}
Mostra tutto
Copia

Un contratto completo potrebbe avere questa forma. Qui la funzione constructor fornisce un valore iniziale per la variabile dapp_name.

Eventi e log

Gli eventi ti consentono di comunicare con il tuo contratto intelligente dal tuo frontend o da altre applicazioni che prevedono un'iscrizione. Quando una transazione è minata, i contratti intelligenti possono emettere eventi e scrivere registri alla blockchain, che il frontend può poi elaborare.

Esempi commentati

Questi sono alcuni esempi scritti in Solidity. Se vuoi sperimentare con il codice, puoi interagire con questi esempi in Remix(opens in a new tab).

Hello world

1// Specifica la versione di Solidity, utilizzando il controllo delle versioni semantico.
2// Per saperne di più: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
3pragma solidity ^0.5.10;
4
5// Definisce un contratto chiamato `HelloWorld`.
6// Un contratto è una raccolta di funzioni e dati (il suo stato).
7// Una volta distribuito, un contratto risiede in un indirizzo specifico della blockchain Ethereum.
8// Per saperne di più: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
9contract HelloWorld {
10
11 // Dichiara una variabile di stato `message` di tipo `string`.
12 // Le variabili di stato sono variabili con valori memorizzati in modo permanente nello spazio di archiviazione (storage) del contratto.
13 // La parola chiave `public` rende le variabili accessibili dall'esterno di un contratto
14 // e crea una funzione che altri contratti o client possono chiamare per accedere al valore.
15 string public message;
16
17 // Analogamente a molti linguaggi di programmazione basati su classi, un costruttore è
18 // una funzione speciale che viene eseguita solo al momento della creazione del contratto.
19 // I costruttori sono utilizzati per inizializzare i dati del contratto.
20 // Maggiori informazioni: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
21 constructor(string memory initMessage) public {
22 // Accetta un argomento di tipo string `initMessage` e imposta il valore
23 // nella variabile di archiviazione `message` del contratto).
24 message = initMessage;
25 }
26
27 // Funzione pubblica che accetta un argomento string
28 // e aggiorna la variabile di archiviazione `message`.
29 function update(string memory newMessage) public {
30 message = newMessage;
31 }
32}
Mostra tutto
Copia

Token

1pragma solidity ^0.5.10;
2
3contract Token {
4 // Un 'address' è paragonabile a un indirizzo email. Viene usato per identificare un account su Ethereum.
5 // Gli indirizzi possono rappresentare uno Smart Contract o un account esterno (utente).
6 // Per saperne di più: https://solidity.readthedocs.io/en/v0.5.10/types.html#address
7 address public owner;
8
9 // Un `mapping` è essenzialmente una struttura dati di tipo tabella hash.
10 // Questo `mapping` assegna un numero intero senza segno (il saldo del token) a un indirizzo (il proprietario del token).
11 // Per saperne di più: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types
12 mapping (address => uint) public balances;
13
14 // Gli eventi consentono di registrare le attività sulla blockchain.
15 // I client Ethereum possono attendere gli eventi per reagire alle modifiche di stato del contratto.
16 // Ulteriori informazioni: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#events
17 event Transfer(address from, address to, uint amount);
18
19 // Inizializza i dati del contratto, impostando `owner`
20 // sull'indirizzo del creatore del contratto.
21 constructor() public {
22 // Tutti gli Smart Contract si basano su transazioni esterne per attivare le proprie funzioni.
23 // `msg` è una variabile globale che include dati relativi alla transazione specificata,
24 // come l'indirizzo del mittente e il valore in ETH incluso nella transazione.
25 // Per saperne di più: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
26 owner = msg.sender;
27 }
28
29 // Crea una quantità di nuovi token e li invia a un indirizzo.
30 function mint(address receiver, uint amount) public {
31 // `require` è una struttura di controllo utilizzata per implementare determinate condizioni.
32 // Se un'istruzione `require` restituisce `false`, viene attivata un'eccezione,
33 // che ripristina tutte le modifiche apportate allo stato durante la chiamata corrente.
34 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
35
36 // Only the contract owner can call this function
37 require(msg.sender == owner, "You are not the owner.");
38
39 // Enforces a maximum amount of tokens
40 require(amount < 1e60, "Maximum issuance exceeded");
41
42 // Increases the balance of `receiver` by `amount`
43 balances[receiver] += amount;
44 }
45
46 // Sends an amount of existing tokens from any caller to an address.
47 function transfer(address receiver, uint amount) public {
48 // Il mittente deve avere abbastanza token da inviare
49 require(amount <= balances[msg.sender], "Insufficient balance.");
50
51 // Modifica i saldi di token dei due indirizzi
52 balances[msg.sender] -= amount;
53 balances[receiver] += amount;
54
55 // Emette l'evento definito in precedenza
56 emit Transfer(msg.sender, receiver, amount);
57 }
58}
Mostra tutto
Copia

Risorsa digitale univoca

1pragma solidity ^0.5.10;
2
3// Importa simboli da altri file nel contratto corrente.
4// In questo caso, una serie di contratti di supporto da OpenZeppelin.
5// Per saperne di più: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#importing-other-source-files
6
7import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol";
8import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
9import "../node_modules/@openzeppelin/contracts/introspection/ERC165.sol";
10import "../node_modules/@openzeppelin/contracts/math/SafeMath.sol";
11
12// La parola chiave `is` viene utilizzata per ereditare funzioni e parole chiave da contratti esterni.
13// In questo caso, `CryptoPizza` eredita dai contratti `IERC721` e `ERC165`.
14// Per saperne di più: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#inheritance
15contract CryptoPizza is IERC721, ERC165 {
16 // Utilizza la libreria SafeMath di OpenZeppelin per eseguire operazioni aritmetiche in modo sicuro.
17 // Ulteriori informazioni: https://docs.openzeppelin.com/contracts/2. /api/math#SafeMath
18 using SafeMath for uint256;
19
20 // Le variabili di stato costanti in Solidity sono simili ad altri linguaggi
21 // ma devono essere assegnate da un'espressione che è costante al momento della compilazione.
22 // Scopri di più: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constant-state-variables
23 uint256 constant dnaDigits = 10;
24 uint256 constant dnaModulus = 10 ** dnaDigits;
25 bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
26
27 // I tipi di struttura ti fanno definire il tuo tipo
28 // Scopri di più: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs
29 struct Pizza {
30 string name;
31 uint256 dna;
32 }
33
34 // Crea un insieme vuoto di strutture di Pizza
35 Pizza[] public pizzas;
36
37 // Mappatura dall'ID della pizza all'indirizzo del suo proprietario
38 mapping(uint256 => address) public pizzaToOwner;
39
40 // Mappatura dall'indirizzo del proprietario al numero di token posseduti
41 mapping(address => uint256) public ownerPizzaCount;
42
43 // Mappatura dall'ID del token all'indirizzo approvato
44 mapping(uint256 => address) pizzaApprovals;
45
46 // Puoi nidificare le mappature, questo esempio mappa le approvazioni da proprietario a operatore
47 mapping(address => mapping(address => bool)) private operatorApprovals;
48
49 // Funzione interna per creare una Pizza casuale dalla stringa (nome) e dal DNA
50 function _createPizza(string memory _name, uint256 _dna)
51 // La parola chiave `internal` significa che questa funzione è visibile solo
52 // tra questo contratto e i contratti derivati da esso
53 // Scopri di più: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters
54 internal
55 // `isUnique` è un modificatore della funzione che verifica se la pizza esiste già
56 // Scopri di più: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers
57 isUnique(_name, _dna)
58 {
59 // Aggiunge la Pizza all'insieme di Pizze e ottiene l'id
60 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);
61
62 // Verifica che il proprietario della Pizza sia l'utente corrente
63 // Scopri di più: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
64
65 // nota che address(0) è l'indirizzo zero,
66 // indicando che pizza[id] non è ancora allocato a un utente in particolare.
67
68 assert(pizzaToOwner[id] == address(0));
69
70 // Mappa la Pizza al proprietario
71 pizzaToOwner[id] = msg.sender;
72 ownerPizzaCount[msg.sender] = SafeMath.add(
73 ownerPizzaCount[msg.sender],
74 1
75 );
76 }
77
78 // Crea una Pizza casuale dalla stringa (nome)
79 function createRandomPizza(string memory _name) public {
80 uint256 randDna = generateRandomDna(_name, msg.sender);
81 _createPizza(_name, randDna);
82 }
83
84 // Genera DNA casuale dalla stringa (nome) e dall'indirizzo del proprietario (creatore)
85 function generateRandomDna(string memory _str, address _owner)
86 public
87 // Le funzioni contrassegnate come `pure` promettono di non modificare lo stato o non leggere da esso
88 // Scopri di più: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions
89 pure
90 returns (uint256)
91 {
92 // Genera uint casuale dalla stringa (nome) + indirizzo (proprietario)
93 uint256 rand = uint256(keccak256(abi.encodePacked(_str))) +
94 uint256(_owner);
95 rand = rand % dnaModulus;
96 return rand;
97 }
98
99 // Restituisce l'insieme di Pizze trovate dal proprietario
100 function getPizzasByOwner(address _owner)
101 public
102 // Le funzioni contrassegnate come `view` promettono di non modificare lo stato
103 // Scopri di più: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions
104 view
105 returns (uint256[] memory)
106 {
107 // Usa la posizione d'archiviazione `memory` per memorizzare i valori solo per la durata
108 // di questa chiamata alla funzione.
109 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/introduction-to-smart-contracts.html#storage-memory-and-the-stack
110 uint256[] memory result = new uint256[](ownerPizzaCount[_owner]);
111 uint256 counter = 0;
112 for (uint256 i = 0; i < pizzas.length; i++) {
113 if (pizzaToOwner[i] == _owner) {
114 result[counter] = i;
115 counter++;
116 }
117 }
118 return result;
119 }
120
121 // Transfers Pizza and ownership to other address
122 function transferFrom(address _from, address _to, uint256 _pizzaId) public {
123 require(_from != address(0) && _to != address(0), "Invalid address.");
124 require(_exists(_pizzaId), "Pizza does not exist.");
125 require(_from != _to, "Cannot transfer to the same address.");
126 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
127
128 ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);
129 ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);
130 pizzaToOwner[_pizzaId] = _to;
131
132 // Emits event defined in the imported IERC721 contract
133 emit Transfer(_from, _to, _pizzaId);
134 _clearApproval(_to, _pizzaId);
135 }
136
137 /**
138 * Safely transfers the ownership of a given token ID to another address
139 * If the target address is a contract, it must implement `onERC721Received`,
140 * which is called upon a safe transfer, and return the magic value
141 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
142 * otherwise, the transfer is reverted.
143 */
144 function safeTransferFrom(address from, address to, uint256 pizzaId)
145 public
146 {
147 // solium-disable-next-line arg-overflow
148 this.safeTransferFrom(from, to, pizzaId, "");
149 }
150
151 /**
152 * Safely transfers the ownership of a given token ID to another address
153 * If the target address is a contract, it must implement `onERC721Received`,
154 * which is called upon a safe transfer, and return the magic value
155 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
156 * otherwise, the transfer is reverted.
157 */
158 function safeTransferFrom(
159 address from,
160 address to,
161 uint256 pizzaId,
162 bytes memory _data
163 ) public {
164 this.transferFrom(from, to, pizzaId);
165 require(_checkOnERC721Received(from, to, pizzaId, _data), "Must implement onERC721Received.");
166 }
167
168 /**
169 * Funzione interna per invocare `onERC721Received` su un dato indirizzo
170 * La chiamata non è eseguita se l'indirizzo di destinazione non è un contratto
171 */
172 function _checkOnERC721Received(
173 address from,
174 address to,
175 uint256 pizzaId,
176 bytes memory _data
177 ) internal returns (bool) {
178 if (!isContract(to)) {
179 return true;
180 }
181
182 bytes4 retval = IERC721Receiver(to).onERC721Received(
183 msg.sender,
184 from,
185 pizzaId,
186 _data
187 );
188 return (retval == _ERC721_RECEIVED);
189 }
190
191 // Brucia una Pizza - distrugge completamente il Token
192 // Il modificatore della funzione `external` significa che questa funzione fa
193 // parte dell'interfaccia del contratto e che gli altri contratti possono chiamarla
194 function burn(uint256 _pizzaId) external {
195 require(msg.sender != address(0), "Indirizzo non valido.");
196 require(_exists(_pizzaId), "La Pizza non esiste.");
197 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Indirizzo non approvato.");
198
199 ownerPizzaCount[msg.sender] = SafeMath.sub(
200 ownerPizzaCount[msg.sender],
201 1
202 );
203 pizzaToOwner[_pizzaId] = address(0);
204 }
205
206 // Restituisce il numero di Pizze per indirizzo
207 function balanceOf(address _owner) public view returns (uint256 _balance) {
208 return ownerPizzaCount[_owner];
209 }
210
211 // Restituisce il proprietario della Pizza trovato per id
212 function ownerOf(uint256 _pizzaId) public view returns (address _owner) {
213 address owner = pizzaToOwner[_pizzaId];
214 require(owner != address(0), "ID della Pizza non valido.");
215 return owner;
216 }
217
218 // Approva altri indirizzi per trasferire la proprietà della Pizza
219 function approve(address _to, uint256 _pizzaId) public {
220 require(msg.sender == pizzaToOwner[_pizzaId], "Dev'essere il proprietario della Pizza.");
221 pizzaApprovals[_pizzaId] = _to;
222 emit Approval(msg.sender, _to, _pizzaId);
223 }
224
225 // Restituisce l'indirizzo approvato per la Pizza specifica
226 function getApproved(uint256 _pizzaId)
227 public
228 view
229 returns (address operator)
230 {
231 require(_exists(_pizzaId), "La Pizza non esiste.");
232 return pizzaApprovals[_pizzaId];
233 }
234
235 /**
236 * La funzione privata per cancellare l'approvazione corrente dell'ID di un dato token
237 * Si ripristina se l'indirizzo dato non è il proprietario del token
238 */
239 function _clearApproval(address owner, uint256 _pizzaId) private {
240 require(pizzaToOwner[_pizzaId] == owner, "Dev'essere il proprietario della pizza.");
241 require(_exists(_pizzaId), "La Pizza non esiste.");
242 if (pizzaApprovals[_pizzaId] != address(0)) {
243 pizzaApprovals[_pizzaId] = address(0);
244 }
245 }
246
247 /*
248 * Imposta o rimuove l'approvazione di un dato operatore
249 * Un operatore può trasferire tutti i token del mittente per conto suo
250 */
251 function setApprovalForAll(address to, bool approved) public {
252 require(to != msg.sender, "Impossibile approvare il proprio indirizzo");
253 operatorApprovals[msg.sender][to] = approved;
254 emit ApprovalForAll(msg.sender, to, approved);
255 }
256
257 // Dice se un operatore è approvato da un dato proprietario
258 function isApprovedForAll(address owner, address operator)
259 public
260 view
261 returns (bool)
262 {
263 return operatorApprovals[owner][operator];
264 }
265
266 // Prende proprietà della Pizza - solo per gli utenti approvati
267 function takeOwnership(uint256 _pizzaId) public {
268 require(_isApprovedOrOwner(msg.sender, _pizzaId), "L'indirizzo non è approvato.");
269 address owner = this.ownerOf(_pizzaId);
270 this.transferFrom(owner, msg.sender, _pizzaId);
271 }
272
273 // Verifica se la Pizza esiste
274 function _exists(uint256 pizzaId) internal view returns (bool) {
275 address owner = pizzaToOwner[pizzaId];
276 return owner != address(0);
277 }
278
279 // Verifica se l'indirizzo è il proprietario o è approvato per trasferire la Pizza
280 function _isApprovedOrOwner(address spender, uint256 pizzaId)
281 internal
282 view
283 returns (bool)
284 {
285 address owner = pizzaToOwner[pizzaId];
286 // Disabilita il controllo di solium a causa di
287 // https://github.com/duaraghav8/Solium/issues/175
288 // solium-disable-next-line operator-whitespace
289 return (spender == owner ||
290 this.getApproved(pizzaId) == spender ||
291 this.isApprovedForAll(owner, spender));
292 }
293
294 // Verifica se la Pizza è univoca e non esiste ancora
295 modifier isUnique(string memory _name, uint256 _dna) {
296 bool result = true;
297 for (uint256 i = 0; i < pizzas.length; i++) {
298 if (
299 keccak256(abi.encodePacked(pizzas[i].name)) ==
300 keccak256(abi.encodePacked(_name)) &&
301 pizzas[i].dna == _dna
302 ) {
303 result = false;
304 }
305 }
306 require(result, "Una Pizza con quel nome esiste già.");
307 _;
308 }
309
310 // Restituisce se l'indirizzo di destinazione è un contratto
311 function isContract(address account) internal view returns (bool) {
312 uint256 size;
313 // Correntemente non c'è modo migliore di verificare se esiste un contratto in un indirizzo
314 // se non controllare la dimensione del codice a quell'indirizzo.
315 // Visita https://ethereum.stackexchange.com/a/14016/36603
316 // per maggiori dettagli sul funzionamento.
317 // TODO Controllare questo codice nuovamente prima del rilascio di Serenity, perché a quel punto
318 // tutti gli indirizzi saranno contratti.
319 // solium-disable-next-line security/no-inline-assembly
320 assembly {
321 size := extcodesize(account)
322 }
323 return size > 0;
324 }
325}
Mostra tutto
Copia

Letture consigliate

Dai un'occhiata alla documentazione di Solidity e Vyper per una panoramica più complessa dei contratti intelligenti:

  • Contratti intelligenti
  • Macchina virtuale Ethereum
  • Ridimensionare i contratti per contrastare il limite di dimensioni del contratto: Alcuni consigli pratici per ridurre le dimensioni del tuo contratto intelligente.
  • Registrare dati dai contratti intelligenti con gli eventi: Un'introduzione agli eventi dei contratti intelligenti e a come puoi usarli per registrare i dati.
  • Interagire con gli altri contratti da Solidity: Come distribuire un contratto intelligente da un contratto esistente e interagirvi.

Questo articolo è stato utile?