Weiter zum Hauptinhalt
Change page

Anatomie von Smart Contracts

Letzte Änderung: @inlak16(opens in a new tab), 14. Juni 2024

Ein Smart Contract ist ein Programm, das auf einer Adresse auf Ethereum läuft. Ein solcher Vertrag besteht aus Daten und Funktionen, die nach dem Erhalt einer Transaktion ausgeführt werden können. Hier ein Überblick darüber, was einen Smart Contract ausmacht.

Voraussetzungen

Sie sollten sich bereits mit Smart Contracts vertraut gemacht haben. Die Informationen in diesem Dokument sind für Personen gedacht, die bereits mit Programmiersprachen wie JavaScript oder Python vertraut sind.

Daten

Alle Vertragsdaten müssen einem Ort zugewiesen werden: entweder zu storage oder memory. Speicher in einem Smart Contract zu ändern ist ein kostenintensiver Prozess. Daher sollten Sie sich überlegen, wo Ihre Daten gespeichert werden sollen.

Speicher

Gleichbleibende Daten werden als Speicher oder Storage bezeichnet und über Zustandsvariablen dargestellt. Solche Daten werden dauerhaft auf der Blockchain gespeichert. Sie müssen den Typ deklarieren, damit der Contract beim Kompilieren verfolgen kann, wie viel Speicherplatz er auf der Blockchain benötigt.

1// Solidity example
2contract SimpleStorage {
3 uint storedData; // State variable
4 // ...
5}
Kopieren
1# Vyper example
2storedData: int128
Kopieren

Wenn Sie bereits Erfahrung im Programmieren in objektorientierten Sprachen haben, werden Sie wahrscheinlich mit den meisten Typen vertraut sein. Allerdings sollte address neu für Sie sein, wenn Sie noch keine Erfahrung in der Ethereum-Entwicklung haben.

Ein adress-Typ kann eine Ethereum-Adresse aufnehmen, was 20 Byte oder 160 Bit entspricht. Die Ausgabe erfolgt in hexadezimaler Schreibweise mit einem führenden 0x.

Andere Typen umfassen:

  • boolesch
  • Ganzzahl
  • Festkommazahlen
  • Byte-Arrays mit fester Größe
  • Byte-Arrays mit dynamischer Größe
  • Rationale und ganzzahlige Literale
  • Zeichenfolgenliterale
  • Hexadezimale Literale
  • Enumerationen

Weitere Erklärungen finden Sie in folgender Dokumentation:

Speicher

Werte, die nur für die Lebensdauer der Ausführung einer Vertragsfunktion gespeichert werden, werden als Memory Variables (Speichervariablen) bezeichnet. Da diese nicht dauerhaft auf der Blockchain gespeichert werden, sind sie wesentlich preiswerter.

Erfahren Sie mehr darüber, wie die EVM Daten speichert (Aufbewahrung, Speicher und Stack), in den Solidity-Dokumenten(opens in a new tab).

Umgebungsvariablen

Zusätzlich zu den Variablen, die Sie in Ihrem Vertrag definieren, gibt es einige spezielle globale Variablen. Sie werden in erster Linie verwendet, um Informationen über die Blockchain oder aktuelle Transaktion bereitzustellen.

Beispiele:

EigenschaftStatusvariableBeschreibung
block.timestampuint256Aktueller Zeitstempel der Block-Epoche
msg.senderaddressAbsender der Nachricht (aktueller Aufruf)

Funktionen

Vereinfacht gesagt können Funktionen als Antwort auf eingehende Transaktionen Informationen erhalten oder festlegen.

Es gibt zwei Arten von Functionsaufrufen:

  • internal – diese erstellen keinen EVM-Aufruf
    • Auf interne Funktionen und Zustandsvariablen kann nur intern zugegriffen werden (d. h. innerhalb des aktuellen Vertrags oder von ihm abgeleiteter Verträge).
  • external – diese erzeugen einen EVM-Aufruf
    • Externe Funktionen sind Teil der Vertragsschnittstelle. Das bedeutet, dass sie aus anderen Verträgen und über Transaktionen aufgerufen werden können. Eine externe Funktion f kann nicht intern aufgerufen werden (z. B. f() funktioniert nicht, aber this.f() funktioniert).

Sie können auch public oder private sein

  • public-Funktionen können intern aus dem Vertrag oder extern über Nachrichten aufgerufen werden.
  • private-Funktionen sind nur für den Vertrag sichtbar, in dem sie definiert sind, und nicht in abgeleiteten Verträgen.

Sowohl Funktionen als auch Statusvariablen können öffentlich oder privat gemacht werden.

Hier ist eine Funktion zum Aktualisieren einer Zustandsvariable für einen Smart Contract:

1// Solidity example
2function update_name(string value) public {
3 dapp_name = value;
4}
Kopieren
  • Der Parameter value des Typs string wird an die Funktion update_name übergeben.
  • Es wird public deklariert. Das bedeutet, dass jeder darauf zugreifen kann.
  • view wird nicht deklariert, damit eine Änderung des Vertragsstatus möglich ist.

View-Funktionen

Diese Funktionen verpflichten sich, den Zustand der Vertragsdaten nicht zu ändern. Gängige Beispiele sind "Getter"-Funktionen, mit denen Sie z. B. den Kontostand eines Benutzers abfragen können.

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

Folgende Vorgänge werden als Modifikation des Zustands angesehen:

  1. In Zustandsvariablen schreiben
  2. Ereignisse ausgeben(opens in a new tab)
  3. Weitere Verträge erstellen(opens in a new tab)
  4. selfdestruct verwenden
  5. Ether über Aufrufe senden
  6. Eine Funktion aufrufen, die nicht mit view oder pure markiert ist
  7. Low-Level-Aufrufe verwenden
  8. Inline-Assembly verwenden, die bestimmte Opcodes enthält

Konstruktorfunktionen

constructor-Funktionen werden nur einmal ausgeführt, wenn der Vertrag in die Blockchain integriert wird. In vielen klassenbasierten Programmiersprachen initialisieren diese Funktionen wie constructor oft Zustandsvariablen auf ihre angegebenen Werte.

1// Solidity example
2// Initializes the contract's data, setting the `owner`
3// to the address of the contract creator.
4constructor() public {
5 // All smart contracts rely on external transactions to trigger its functions.
6 // `msg` is a global variable that includes relevant data on the given transaction,
7 // such as the address of the sender and the ETH value included in the transaction.
8 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
9 owner = msg.sender;
10}
Alles anzeigen
Kopieren
1# Vyper example
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
Kopieren

Integrierte Funktionen

Zusätzlich zu den Variablen, die Sie in Ihrem Vertrag definieren, gibt es einige spezielle integrierte Funktionen. Das offensichtlichste Beispiel ist:

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

Diese erlauben es Smart Contracts, ETH an andere Konten zu senden.

Funktionen entwickeln

Ihre Funktion benötigt folgende Elemente:

  • Parametervariable und -typ (wenn Parameter akzeptiert werden)
  • interne/externe Deklaration
  • Deklaration von pure/view/payable
  • Gibt den Typ zurück (wenn er einen Wert zurückgibt)
1pragma solidity >=0.4.0 <=0.6.0;
2
3contract ExampleDapp {
4 string dapp_name; // Zustandsvariable
5
6 // Wird aufgerufen, wenn der Vertrag bereitgestellt wird und initialisiert den Wert
7 constructor() public {
8 dapp_name = "Meine Beispiel-Dapp";
9 }
10
11 // Funktion holen
12 function read_name() public view returns(string) {
13 return dapp_name;
14 }
15
16 // Funktion setzen
17 function update_name(string value) public {
18 dapp_name = value;
19 }
20}
Alles anzeigen
Kopieren

Ein vollständiger Smart Contract könnte so aussehen. Hier stellt die constructor-Funktion einen Anfangswert für die dapp_name -Variable bereit.

Ereignisse und Protokolle

Ereignisse ermöglichen Ihnen die Kommunikation mit Ihrem Smart Contract von Ihrem Frontend oder anderen verbundenen Anwendungen aus. Wenn eine Transaktion verifiziert wird, können Smart Contracts Events emittieren und Protokolle in die Blockchain schreiben, die das Frontend dann verarbeiten kann.

Kommentierte Beispiele

Das sind einige Beispiele in Solidity. Wenn Sie mit dem Code spielen möchten, können Sie mit ihm in Remix(opens in a new tab) interagieren.

Hallo Welt

1// Bestimmt die Version von Solidity mit semantischer Versionierung.
2// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
3pragma solidity ^0.5.10;
4
5// Defines a contract named `HelloWorld`.
6// Ein Smart contract ist eine Sammlung von Funktionen und Daten (sein Zustand).
7// Einmal in die Blockchain integriert, befindet sich ein Contract an einer bestimmten Adresse der Ethereum-Blockchain.
8// Erfahre mehr: https://solidity.readthedocs.io/de/v0.5.10/structure-of-a-contract.html
9contract HelloWorld {
10
11 // Deklariert eine Zustandsvariable `message` vom Typ `string`.
12 // Zustandsvariablen sind Variablen, deren Werte dauerhaft im Vertragsspeicher hinterlegt werden.
13 // Das Schlüsselwort `public` macht Variablen von außerhalb eines Contracts
14 // zugänglich und erzeugt eine Funktion, die andere Contracts oder Clients aufrufen können, um auf den Wert zuzugreifen.
15 string public message;
16
17 // Ähnlich wie viele Klassen-basierte objektorientierte Sprachen, ist ein Konstruktor
18 // eine spezielle Funktion, die nur bei der Vertragserstellung ausgeführt wird.
19 // Konstruktoren werden verwendet, um die Vertragsdaten zu initialisieren.
20 // Erfahre mehr: https://solidity.readthedocs.io/de/v0.5.10/contracts. tml#constructors
21 constructor(string memory initMessage) public {
22 // Akzeptiert ein String Argument `initMessage` und setzt den Wert
23 // in die `message` Speichervariable des Contracts).
24 message = initMessage;
25 }
26
27 // Eine öffentliche Funktion, die ein String-Argument akzeptiert
28 // und die Speichervariable `message` aktualisiert.
29 function update(string memory newMessage) public {
30 message = newMessage;
31 }
32}
Alles anzeigen
Kopieren

Token

1pragma solidity ^0.5.10;
2
3contract Token {
4 // Eine `Adresse` ist mit einer E-Mail-Adresse vergleichbar - sie wird verwendet, um ein Konto auf Ethereum zu identifizieren.
5 // Adressen können einen Smart Contract oder ein externes (Benutzer) Konto darstellen.
6 // Erfahre mehr: https://solidity.readthedocs.io/en/v0.5.10/types.html#address
7 address public owner;
8
9 // Ein `mapping` ist im Wesentlichen eine Hashtabellen-Datenstruktur.
10 // Dieses `mapping` weist einer Adresse (dem Token-Halter) ein nicht signiertes Integer (dem Token-Halter) zu.
11 // Erfahre mehr: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types
12 mapping (address => uint) public balances;
13
14 // Events ermöglichen die Protokollierung von Aktivitäten auf der Blockchain.
15 // Ethereum Clients können auf Events hören, um auf Änderungen des Contract-Zustands zu reagieren.
16 // Erfahre mehr: https://solidity.readthedocs.io/de/v0.5.10/contracts. tml#Events
17 event Transfer(address from, address to, uint amount);
18
19 // Initialisiert die Vertragsdaten und setzt den `owner`
20 // auf die Adresse des Contract-Erstellers.
21 constructor() public {
22 // Alle Smart Contracts benötigen externe Transaktionen, um Funktionen auszuführen.
23 // `msg` ist eine globale Variable, die relevante Daten der gegebenen Transaktion enthält,
24 // wie die Adresse des Senders und der in der Transaktion enthaltene ETH Wert.
25 // Mehr erfahren: https://solidity.readthedocs.io/de/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
26 owner = msg.sender;
27 }
28
29 // Erstellt eine Menge neuer Tokens und sendet sie an eine Adresse.
30 function mint(address receiver, uint amount) public {
31 // `require` ist eine Kontrollstruktur, die benutzt wird, um bestimmte Bedingungen zu erzwingen.
32 // Wenn eine `require` Anweisung zu `false` auswertet, wird eine Ausnahme ausgelöst,
33 // welche alle Änderungen am Status während des aktuellen Aufrufs rückgängig macht.
34 // Erfahren Sie mehr: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
35
36 // Nur der Vertragsinhaber kann diese Funktion aufrufen
37 require(msg.sender == owner, "Sie sind nicht der Besitzer.");
38
39 // Erzwingt eine maximale Menge an Token
40 require(amount < 1e60, "Maximale Ausgabe überschritten");
41
42 // Erhöht den Saldo von `Empfänger` um `Betrag`.
43 balances[receiver] += amount;
44 }
45
46 // Sendet eine Menge vorhandener Token von einem beliebigen Anrufer an eine Adresse.
47 function transfer(address receiver, uint amount) public {
48 // Der Absender muss genug Token zum Senden besitzen
49 require(amount <= balances[msg.sender], "Insufficient balance.");
50
51 // Tokensalden der beiden Adressen anpassen
52 balances[msg.sender] -= amount;
53 balances[receiver] += amount;
54
55 // Sendet das zuvor definierte Event aus
56 emit Transfer(msg.sender, receiver, amount);
57 }
58}
Alles anzeigen
Kopieren

Einzigartiges digitales Asset

1pragma solidity ^0.5.10;
2
3// Importiert Symbole aus anderen Dateien in den aktuellen Contract.
4// In diesem Fall eine Reihe von Hilfsverträgen von OpenZeppelin.
5// Erfahre mehr: 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. ol";
9import "../node_modules/@openzeppelin/contracts/introspection/ERC165.sol";
10import "../node_modules/@openzeppelin/contracts/math/SafeMath.sol";
11
12// Das Schlüsselwort `is` wird verwendet, um Funktionen und Schlüsselwörter aus externen Smart Contracts zu erben.
13// In diesem Fall erbt `CryptoPizza` von den `IERC721` und `ERC165` Contracts.
14// Erfahre mehr: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#inheritance
15contract CryptoPizza is IERC721, ERC165 {
16 // Verwendet OpenZeppelins SafeMath Bibliothek, um arithmetische Operationen sicher durchzuführen.
17 // Erfahre mehr: https://docs.openzeppelin.com/contracts/2. /api/math#SafeMath
18 using SafeMath for uint256;
19
20 // Konstante Zustandsvariablen in Solidity sind vergleichbar mit anderen Sprachen
21 // du musst jedoch voneiner Expression zuweisen, die beim Kompilieren konstant ist.
22 // Learn more: 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 // Struct types let you define your own type
28 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs
29 struct Pizza {
30 string name;
31 uint256 dna;
32 }
33
34 // Creates an empty array of Pizza structs
35 Pizza[] public pizzas;
36
37 // Mapping from pizza ID to its owner's address
38 mapping(uint256 => address) public pizzaToOwner;
39
40 // Mapping from owner's address to number of owned token
41 mapping(address => uint256) public ownerPizzaCount;
42
43 // Mapping from token ID to approved address
44 mapping(uint256 => address) pizzaApprovals;
45
46 // You can nest mappings, this example maps owner to operator approvals
47 mapping(address => mapping(address => bool)) private operatorApprovals;
48
49 // Internal function to create a random Pizza from string (name) and DNA
50 function _createPizza(string memory _name, uint256 _dna)
51 // The `internal` keyword means this function is only visible
52 // within this contract and contracts that derive this contract
53 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters
54 internal
55 // `isUnique` is a function modifier that checks if the pizza already exists
56 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers
57 isUnique(_name, _dna)
58 {
59 // Adds Pizza to array of Pizzas and get id
60 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);
61
62 // Checks that Pizza owner is the same as current user
63 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
64
65 // note that address(0) is the zero address,
66 // indicating that pizza[id] is not yet allocated to a particular user.
67
68 assert(pizzaToOwner[id] == address(0));
69
70 // Maps the Pizza to the owner
71 pizzaToOwner[id] = msg.sender;
72 ownerPizzaCount[msg.sender] = SafeMath.add(
73 ownerPizzaCount[msg.sender],
74 1
75 );
76 }
77
78 // Creates a random Pizza from string (name)
79 function createRandomPizza(string memory _name) public {
80 uint256 randDna = generateRandomDna(_name, msg.sender);
81 _createPizza(_name, randDna);
82 }
83
84 // Generates random DNA from string (name) and address of the owner (creator)
85 function generateRandomDna(string memory _str, address _owner)
86 public
87 // Functions marked as `pure` promise not to read from or modify the state
88 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions
89 pure
90 returns (uint256)
91 {
92 // Generates random uint from string (name) + address (owner)
93 uint256 rand = uint256(keccak256(abi.encodePacked(_str))) +
94 uint256(_owner);
95 rand = rand % dnaModulus;
96 return rand;
97 }
98
99 // Returns array of Pizzas found by owner
100 function getPizzasByOwner(address _owner)
101 public
102 // Functions marked as `view` promise not to modify state
103 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions
104 view
105 returns (uint256[] memory)
106 {
107 // Uses the `memory` storage location to store values only for the
108 // lifecycle of this function call.
109 // Erfahre mehr: 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 // Transferiert Pizza und deren Besitzanspruch auf eine andere Adresse
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 // Gibt ein Event aus, dass in dem importierten IERC721 Contract definiert ist
133 emit Transfer(_from, _to, _pizzaId);
134 _clearApproval(_to, _pizzaId);
135 }
136
137 /**
138 * Übergibt auf sichere Weise den Besitzanspruch von gegebener Token ID an eine andere Adresse
139 * Wenn die Zieladresse ein Contract ist, muss dieser `onERC721Received` implementieren,
140 * was bei einem sicheren Transfer aufgerufen wird und den magischen Wert zurückgibt:
141 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
142 * ansonsten, wird die Transaktion abgewiesen.
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 * Übergibt auf sichere Weise den Besitzanspruch von gegebener Token ID an eine andere Adresse
153 * Wenn die Zieladresse ein Contract ist, muss dieser `onERC721Received` implementieren,
154 * was bei einem sicheren Transfer aufgerufen wird und den magischen Wert zurückgibt:
155 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
156 * ansonsten, wird die Transaktion abgewiesen.
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 * Internal function to invoke `onERC721Received` on a target address
170 * The call is not executed if the target address is not a contract
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 // Burns a Pizza - destroys Token completely
192 // The `external` function modifier means this function is
193 // part of the contract interface and other contracts can call it
194 function burn(uint256 _pizzaId) external {
195 require(msg.sender != address(0), "Invalid address.");
196 require(_exists(_pizzaId), "Pizza does not exist.");
197 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
198
199 ownerPizzaCount[msg.sender] = SafeMath.sub(
200 ownerPizzaCount[msg.sender],
201 1
202 );
203 pizzaToOwner[_pizzaId] = address(0);
204 }
205
206 // Returns count of Pizzas by address
207 function balanceOf(address _owner) public view returns (uint256 _balance) {
208 return ownerPizzaCount[_owner];
209 }
210
211 // Returns owner of the Pizza found by id
212 function ownerOf(uint256 _pizzaId) public view returns (address _owner) {
213 address owner = pizzaToOwner[_pizzaId];
214 require(owner != address(0), "Invalid Pizza ID.");
215 return owner;
216 }
217
218 // Approves other address to transfer ownership of Pizza
219 function approve(address _to, uint256 _pizzaId) public {
220 require(msg.sender == pizzaToOwner[_pizzaId], "Must be the Pizza owner.");
221 pizzaApprovals[_pizzaId] = _to;
222 emit Approval(msg.sender, _to, _pizzaId);
223 }
224
225 // Returns approved address for specific Pizza
226 function getApproved(uint256 _pizzaId)
227 public
228 view
229 returns (address operator)
230 {
231 require(_exists(_pizzaId), "Pizza does not exist.");
232 return pizzaApprovals[_pizzaId];
233 }
234
235 /**
236 * Private function to clear current approval of a given token ID
237 * Reverts if the given address is not indeed the owner of the token
238 */
239 function _clearApproval(address owner, uint256 _pizzaId) private {
240 require(pizzaToOwner[_pizzaId] == owner, "Must be pizza owner.");
241 require(_exists(_pizzaId), "Pizza does not exist.");
242 if (pizzaApprovals[_pizzaId] != address(0)) {
243 pizzaApprovals[_pizzaId] = address(0);
244 }
245 }
246
247 /*
248 * Sets or unsets the approval of a given operator
249 * An operator is allowed to transfer all tokens of the sender on their behalf
250 */
251 function setApprovalForAll(address to, bool approved) public {
252 require(to != msg.sender, "Cannot approve own address");
253 operatorApprovals[msg.sender][to] = approved;
254 emit ApprovalForAll(msg.sender, to, approved);
255 }
256
257 // Tells whether an operator is approved by a given owner
258 function isApprovedForAll(address owner, address operator)
259 public
260 view
261 returns (bool)
262 {
263 return operatorApprovals[owner][operator];
264 }
265
266 // Takes ownership of Pizza - only for approved users
267 function takeOwnership(uint256 _pizzaId) public {
268 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
269 address owner = this.ownerOf(_pizzaId);
270 this.transferFrom(owner, msg.sender, _pizzaId);
271 }
272
273 // Checks if Pizza exists
274 function _exists(uint256 pizzaId) internal view returns (bool) {
275 address owner = pizzaToOwner[pizzaId];
276 return owner != address(0);
277 }
278
279 // Checks if address is owner or is approved to transfer Pizza
280 function _isApprovedOrOwner(address spender, uint256 pizzaId)
281 internal
282 view
283 returns (bool)
284 {
285 address owner = pizzaToOwner[pizzaId];
286 // Disable solium check because of
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 // Check if Pizza is unique and doesn't exist yet
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, "Pizza with such name already exists.");
307 _;
308 }
309
310 // Returns whether the target address is a contract
311 function isContract(address account) internal view returns (bool) {
312 uint256 size;
313 // Currently there is no better way to check if there is a contract in an address
314 // than to check the size of the code at that address.
315 // Siehe https://ethereum.stackexchange.com/a/14016/36603
316 // für weitere Informationen zur Funktionsweise.
317 // TO-DO Verifizieren Sie dies nochmals, bevor Serenity eingeführt wird
318 //, da alle Adressen dann Contracts sein werden.
319 // solium-disable-next-line security/no-inline-assembly
320 assembly {
321 size := extcodesize(account)
322 }
323 return size > 0;
324 }
325}
Alles anzeigen
Kopieren

Weiterführende Informationen

Sehen Sie sich auch die Dokumentationen zu Solidity und Vyper an, um einen umfassenderen Überblick über Smart Contracts zu erhalten:

  • Smart Contracts
  • Ethereum-Virtual Machine (EVM)
  • Verkleinern von Verträgen, um die Vertragsgröße zu begrenzen – Einige praktische Tipps zur Reduzierung der Größe Ihres Smart Contracts
  • Protokollieren von Daten aus Smart Contracts mit Ereignissen – Eine Einführung in Smart-Contract-Ereigbnisse und wie Sie diese zur Datenprotokollierung verwenden können
  • Mit anderen Verträgen aus Solidity interagieren – So können Sie einen Smart Contract aus einem bestehenden Vertrag aufbauen und mit ihm interagieren

War dieser Artikel hilfreich?