Weiter zum Hauptinhalt
Change page

Anatomie von Smart Contracts

Seite zuletzt aktualisiert: 14. Februar 2026

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

Stellen Sie sicher, dass Sie sich zuerst über Smart Contracts informiert 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 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-Beispiel
2contract SimpleStorage {
3 uint storedData; // Zustandsvariable
4 // ...
5}
1# Vyper example
2storedData: int128

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

Ein address-Typ kann eine Ethereum-Adresse aufnehmen, was 20 Bytes oder 160 Bits 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
  • Enums

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 in den Solidity-Dokumentenopens in a new tab mehr darüber, wie die EVM Daten speichert (Storage, Memory und der Stack).

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 erzeugen keinen EVM-Aufruf
    • Auf interne Funktionen und Zustandsvariablen kann nur intern zugegriffen werden (d. h. aus dem aktuellen Vertrag oder von ihm abgeleiteten Verträgen)
  • 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 (d. h. f() funktioniert nicht, aber this.f() funktioniert).

Sie können auch public oder private sein

  • public-Funktionen können intern aus dem Vertrag heraus 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}
  • Der Parameter value vom Typ string wird an die Funktion übergeben: update_name
  • Sie ist als public deklariert, was bedeutet, dass jeder darauf zugreifen kann
  • Sie ist nicht als view deklariert, sodass sie den Vertragszustand ändern kann

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}
1dappName: public(string)
2
3@view
4@public
5def readName() -> string:
6 return dappName

Folgende Vorgänge werden als Modifikation des Zustands angesehen:

  1. In Zustandsvariablen schreiben
  2. Auslösen von Ereignissenopens in a new tab.
  3. Erstellen anderer Verträgeopens in a new tab.
  4. Verwenden von selfdestruct.
  5. Ether über Aufrufe senden
  6. Aufrufen von Funktionen, die nicht als view oder pure markiert sind.
  7. Low-Level-Aufrufe verwenden
  8. Inline-Assembly verwenden, die bestimmte Opcodes enthält

Konstruktor-Funktionen

constructor-Funktionen werden nur einmal ausgeführt, wenn der Vertrag zum ersten Mal bereitgestellt wird. Ähnlich wie der constructor in vielen klassenbasierten Programmiersprachen initialisieren diese Funktionen oft Zustandsvariablen mit ihren angegebenen Werten.

1// Solidity-Beispiel
2// Initialisiert die Vertragsdaten und setzt den `owner`
3// auf die Adresse des Vertragserstellers.
4constructor() public {
5 // Alle Smart Contracts sind auf externe Transaktionen angewiesen, um ihre Funktionen auszulösen.
6 // `msg` ist eine globale Variable, die relevante Daten über die jeweilige Transaktion enthält,
7 // wie z. B. die Adresse des Absenders und den in der Transaktion enthaltenen ETH-Wert.
8 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
9 owner = msg.sender;
10}
Alles anzeigen
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

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.

Schreiben von Funktionen

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 // Get-Funktion
12 function read_name() public view returns(string) {
13 return dapp_name;
14 }
15
16 // Set-Funktion
17 function update_name(string value) public {
18 dapp_name = value;
19 }
20}
Alles anzeigen

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

Ereignisse und Protokolle

Ereignisse ermöglichen es Ihrem Smart Contract, mit Ihrem Frontend oder anderen abonnierenden Anwendungen zu kommunizieren. Sobald eine Transaktion validiert und einem Block hinzugefügt wurde, können Smart Contracts Ereignisse auslösen und Informationen protokollieren, die das Frontend dann verarbeiten und nutzen kann.

Kommentierte Beispiele

Das sind einige Beispiele in Solidity. Wenn Sie mit dem Code experimentieren möchten, können Sie in Remixopens in a new tab mit ihnen interagieren.

Hallo Welt

1// Gibt die Version von Solidity an und verwendet die semantische Versionierung.
2// Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
3pragma solidity ^0.5.10;
4
5// Definiert einen Vertrag mit dem Namen `HelloWorld`.
6// Ein Vertrag ist eine Sammlung von Funktionen und Daten (seinem Zustand).
7// Nach der Bereitstellung befindet sich ein Vertrag an einer bestimmten Adresse auf der Ethereum-Blockchain.
8// Mehr erfahren: https://solidity.readthedocs.io/en/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 gespeichert werden.
13 // Das Schlüsselwort `public` macht Variablen von außerhalb eines Vertrags zugänglich
14 // und erstellt eine Funktion, die andere Verträge oder Clients aufrufen können, um auf den Wert zuzugreifen.
15 string public message;
16
17 // Ähnlich wie in vielen klassenbasierten objektorientierten Sprachen ist ein Konstruktor
18 // eine spezielle Funktion, die nur bei der Erstellung des Vertrags ausgeführt wird.
19 // Konstruktoren werden verwendet, um die Daten des Vertrags zu initialisieren.
20 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
21 constructor(string memory initMessage) public {
22 // Akzeptiert ein Zeichenfolgen-Argument `initMessage` und setzt den Wert
23 // in die `message`-Speichervariable des Vertrags).
24 message = initMessage;
25 }
26
27 // Eine öffentliche Funktion, die ein Zeichenfolgen-Argument akzeptiert
28 // und die `message`-Speichervariable aktualisiert.
29 function update(string memory newMessage) public {
30 message = newMessage;
31 }
32}
Alles anzeigen

Token

1pragma solidity ^0.5.10;
2
3contract Token {
4 // Eine `address` 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 externe (Benutzer-)Konten darstellen.
6 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/types.html#address
7 address public owner;
8
9 // Ein `mapping` ist im Wesentlichen eine Hash-Tabellen-Datenstruktur.
10 // Dieses `mapping` weist einer Adresse (dem Token-Inhaber) eine vorzeichenlose Ganzzahl (das Token-Guthaben) zu.
11 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types
12 mapping (address => uint) public balances;
13
14 // Ereignisse ermöglichen die Protokollierung von Aktivitäten auf der Blockchain.
15 // Ethereum-Clients können auf Ereignisse lauschen, um auf Änderungen des Vertragszustands zu reagieren.
16 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#events
17 event Transfer(address from, address to, uint amount);
18
19 // Initialisiert die Vertragsdaten und setzt den `owner`
20 // auf die Adresse des Vertragserstellers.
21 constructor() public {
22 // Alle Smart Contracts sind auf externe Transaktionen angewiesen, um ihre Funktionen auszulösen.
23 // `msg` ist eine globale Variable, die relevante Daten über die jeweilige Transaktion enthält,
24 // wie z. B. die Adresse des Absenders und den in der Transaktion enthaltenen ETH-Wert.
25 // Mehr erfahren: https://solidity.readthedocs.io/en/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 zur Durchsetzung bestimmter Bedingungen verwendet wird.
32 // Wenn eine `require`-Anweisung zu `false` ausgewertet wird, wird eine Ausnahme ausgelöst,
33 // die alle während des aktuellen Aufrufs am Zustand vorgenommenen Änderungen rückgängig macht.
34 // Mehr erfahren: 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 Inhaber.");
38
39 // Erzwingt eine maximale Menge an Tokens
40 require(amount < 1e60, "Maximale Emission überschritten");
41
42 // Erhöht das Guthaben von `receiver` um `amount`
43 balances[receiver] += amount;
44 }
45
46 // Sendet eine Menge bestehender Tokens von einem beliebigen Aufrufer an eine Adresse.
47 function transfer(address receiver, uint amount) public {
48 // Der Absender muss über genügend Tokens verfügen, um sie zu senden
49 require(amount <= balances[msg.sender], "Ungenügendes Guthaben.");
50
51 // Passt die Token-Guthaben der beiden Adressen an
52 balances[msg.sender] -= amount;
53 balances[receiver] += amount;
54
55 // Löst das zuvor definierte Ereignis aus
56 emit Transfer(msg.sender, receiver, amount);
57 }
58}
Alles anzeigen

Einzigartiger digitaler Vermögenswert

1pragma solidity ^0.5.10;
2
3// Importiert Symbole aus anderen Dateien in den aktuellen Vertrag.
4// In diesem Fall eine Reihe von Hilfsverträgen von OpenZeppelin.
5// Mehr erfahren: 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// Das Schlüsselwort `is` wird verwendet, um Funktionen und Schlüsselwörter von externen Verträgen zu erben.
13// In diesem Fall erbt `CryptoPizza` von den Verträgen `IERC721` und `ERC165`.
14// Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#inheritance
15contract CryptoPizza is IERC721, ERC165 {
16 // Verwendet die SafeMath-Bibliothek von OpenZeppelin, um arithmetische Operationen sicher durchzuführen.
17 // Mehr erfahren: https://docs.openzeppelin.com/contracts/2.x/api/math#SafeMath
18 using SafeMath for uint256;
19
20 // Konstante Zustandsvariablen in Solidity sind ähnlich wie in anderen Sprachen,
21 // aber Sie müssen sie aus einem Ausdruck zuweisen, der zur Kompilierungszeit konstant ist.
22 // Mehr erfahren: 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 // Mit Struct-Typen können Sie Ihren eigenen Typ definieren
28 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs
29 struct Pizza {
30 string name;
31 uint256 dna;
32 }
33
34 // Erstellt ein leeres Array von Pizza-Structs
35 Pizza[] public pizzas;
36
37 // Mapping von der Pizza-ID zur Adresse ihres Besitzers
38 mapping(uint256 => address) public pizzaToOwner;
39
40 // Mapping von der Adresse des Besitzers zur Anzahl der besessenen Tokens
41 mapping(address => uint256) public ownerPizzaCount;
42
43 // Mapping von der Token-ID zur genehmigten Adresse
44 mapping(uint256 => address) pizzaApprovals;
45
46 // Sie können Mappings verschachteln, dieses Beispiel bildet Besitzer auf Betreibergenehmigungen ab
47 mapping(address => mapping(address => bool)) private operatorApprovals;
48
49 // Interne Funktion zum Erstellen einer zufälligen Pizza aus einer Zeichenfolge (Name) und DNA
50 function _createPizza(string memory _name, uint256 _dna)
51 // Das Schlüsselwort `internal` bedeutet, dass diese Funktion nur
52 // innerhalb dieses Vertrags und von Verträgen, die von diesem Vertrag abgeleitet sind, sichtbar ist
53 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters
54 internal
55 // `isUnique` ist ein Funktionsmodifikator, der prüft, ob die Pizza bereits existiert
56 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers
57 isUnique(_name, _dna)
58 {
59 // Fügt Pizza zum Array von Pizzen hinzu und erhält die ID
60 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);
61
62 // Überprüft, ob der Pizzabesitzer mit dem aktuellen Benutzer identisch ist
63 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
64
65 // beachten Sie, dass address(0) die Null-Adresse ist,
66 // was anzeigt, dass pizza[id] noch keinem bestimmten Benutzer zugewiesen ist.
67
68 assert(pizzaToOwner[id] == address(0));
69
70 // Ordnet die Pizza dem Besitzer zu
71 pizzaToOwner[id] = msg.sender;
72 ownerPizzaCount[msg.sender] = SafeMath.add(
73 ownerPizzaCount[msg.sender],
74 1
75 );
76 }
77
78 // Erstellt eine zufällige Pizza aus einer Zeichenfolge (Name)
79 function createRandomPizza(string memory _name) public {
80 uint256 randDna = generateRandomDna(_name, msg.sender);
81 _createPizza(_name, randDna);
82 }
83
84 // Erzeugt eine zufällige DNA aus einer Zeichenfolge (Name) und der Adresse des Besitzers (Erstellers)
85 function generateRandomDna(string memory _str, address _owner)
86 public
87 // Funktionen, die als `pure` markiert sind, versprechen, den Zustand weder zu lesen noch zu verändern
88 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions
89 pure
90 returns (uint256)
91 {
92 // Erzeugt eine zufällige uint aus einer Zeichenfolge (Name) + Adresse (Besitzer)
93 uint256 rand = uint256(keccak256(abi.encodePacked(_str))) +
94 uint256(_owner);
95 rand = rand % dnaModulus;
96 return rand;
97 }
98
99 // Gibt ein Array von Pizzen zurück, die nach Besitzer gefunden wurden
100 function getPizzasByOwner(address _owner)
101 public
102 // Funktionen, die als `view` markiert sind, versprechen, den Zustand nicht zu verändern
103 // Mehr erfahren: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions
104 view
105 returns (uint256[] memory)
106 {
107 // Verwendet den Speicherort `memory`, um Werte nur für den
108 // Lebenszyklus dieses Funktionsaufrufs zu speichern.
109 // Mehr erfahren: 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 // Überträgt Pizza und Besitz an eine andere Adresse
122 function transferFrom(address _from, address _to, uint256 _pizzaId) public {
123 require(_from != address(0) && _to != address(0), "Ungültige Adresse.");
124 require(_exists(_pizzaId), "Pizza existiert nicht.");
125 require(_from != _to, "Kann nicht an dieselbe Adresse übertragen werden.");
126 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Adresse ist nicht genehmigt.");
127
128 ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);
129 ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);
130 pizzaToOwner[_pizzaId] = _to;
131
132 // Löst ein Ereignis aus, das im importierten IERC721-Vertrag definiert ist
133 emit Transfer(_from, _to, _pizzaId);
134 _clearApproval(_to, _pizzaId);
135 }
136
137 /**
138 * Überträgt den Besitz einer bestimmten Token-ID sicher an eine andere Adresse
139 * Wenn die Zieladresse ein Vertrag ist, muss sie `onERC721Received` implementieren,
140 * was bei einer sicheren Übertragung aufgerufen wird, und den magischen Wert
141 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` zurückgeben;
142 * andernfalls wird die Übertragung rückgängig gemacht.
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 * Überträgt den Besitz einer bestimmten Token-ID sicher an eine andere Adresse
153 * Wenn die Zieladresse ein Vertrag ist, muss sie `onERC721Received` implementieren,
154 * was bei einer sicheren Übertragung aufgerufen wird, und den magischen Wert
155 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` zurückgeben;
156 * andernfalls wird die Übertragung rückgängig gemacht.
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), "Muss onERC721Received implementieren.");
166 }
167
168 /**
169 * Interne Funktion zum Aufrufen von `onERC721Received` auf einer Zieladresse
170 * Der Aufruf wird nicht ausgeführt, wenn die Zieladresse kein Vertrag ist
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 // Verbrennt eine Pizza – zerstört den Token vollständig
192 // Der Funktionsmodifikator `external` bedeutet, dass diese Funktion
193 // Teil der Vertragsschnittstelle ist und andere Verträge sie aufrufen können
194 function burn(uint256 _pizzaId) external {
195 require(msg.sender != address(0), "Ungültige Adresse.");
196 require(_exists(_pizzaId), "Pizza existiert nicht.");
197 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Adresse ist nicht genehmigt.");
198
199 ownerPizzaCount[msg.sender] = SafeMath.sub(
200 ownerPizzaCount[msg.sender],
201 1
202 );
203 pizzaToOwner[_pizzaId] = address(0);
204 }
205
206 // Gibt die Anzahl der Pizzen nach Adresse zurück
207 function balanceOf(address _owner) public view returns (uint256 _balance) {
208 return ownerPizzaCount[_owner];
209 }
210
211 // Gibt den Besitzer der Pizza zurück, die nach ID gefunden wurde
212 function ownerOf(uint256 _pizzaId) public view returns (address _owner) {
213 address owner = pizzaToOwner[_pizzaId];
214 require(owner != address(0), "Ungültige Pizza-ID.");
215 return owner;
216 }
217
218 // Genehmigt eine andere Adresse, um den Besitz der Pizza zu übertragen
219 function approve(address _to, uint256 _pizzaId) public {
220 require(msg.sender == pizzaToOwner[_pizzaId], "Muss der Pizzabesitzer sein.");
221 pizzaApprovals[_pizzaId] = _to;
222 emit Approval(msg.sender, _to, _pizzaId);
223 }
224
225 // Gibt die genehmigte Adresse für eine bestimmte Pizza zurück
226 function getApproved(uint256 _pizzaId)
227 public
228 view
229 returns (address operator)
230 {
231 require(_exists(_pizzaId), "Pizza existiert nicht.");
232 return pizzaApprovals[_pizzaId];
233 }
234
235 /**
236 * Private Funktion, um die aktuelle Genehmigung für eine bestimmte Token-ID zu löschen
237 * Macht die Aktion rückgängig, wenn die angegebene Adresse nicht tatsächlich der Besitzer des Tokens ist
238 */
239 function _clearApproval(address owner, uint256 _pizzaId) private {
240 require(pizzaToOwner[_pizzaId] == owner, "Muss Pizzabesitzer sein.");
241 require(_exists(_pizzaId), "Pizza existiert nicht.");
242 if (pizzaApprovals[_pizzaId] != address(0)) {
243 pizzaApprovals[_pizzaId] = address(0);
244 }
245 }
246
247 /*
248 * Setzt oder hebt die Genehmigung eines bestimmten Betreibers auf
249 * Ein Betreiber darf alle Tokens des Absenders in dessen Namen übertragen
250 */
251 function setApprovalForAll(address to, bool approved) public {
252 require(to != msg.sender, "Eigene Adresse kann nicht genehmigt werden");
253 operatorApprovals[msg.sender][to] = approved;
254 emit ApprovalForAll(msg.sender, to, approved);
255 }
256
257 // Gibt an, ob ein Betreiber von einem bestimmten Besitzer genehmigt ist
258 function isApprovedForAll(address owner, address operator)
259 public
260 view
261 returns (bool)
262 {
263 return operatorApprovals[owner][operator];
264 }
265
266 // Übernimmt den Besitz der Pizza – nur für genehmigte Benutzer
267 function takeOwnership(uint256 _pizzaId) public {
268 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Adresse ist nicht genehmigt.");
269 address owner = this.ownerOf(_pizzaId);
270 this.transferFrom(owner, msg.sender, _pizzaId);
271 }
272
273 // Prüft, ob Pizza existiert
274 function _exists(uint256 pizzaId) internal view returns (bool) {
275 address owner = pizzaToOwner[pizzaId];
276 return owner != address(0);
277 }
278
279 // Prüft, ob die Adresse der Besitzer ist oder für die Übertragung der Pizza genehmigt ist
280 function _isApprovedOrOwner(address spender, uint256 pizzaId)
281 internal
282 view
283 returns (bool)
284 {
285 address owner = pizzaToOwner[pizzaId];
286 // Deaktiviere die Solium-Prüfung wegen
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 // Prüfen, ob die Pizza einzigartig ist und noch nicht existiert
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 mit diesem Namen existiert bereits.");
307 _;
308 }
309
310 // Gibt zurück, ob die Zieladresse ein Vertrag ist
311 function isContract(address account) internal view returns (bool) {
312 uint256 size;
313 // Derzeit gibt es keine bessere Möglichkeit zu prüfen, ob es einen Vertrag an einer Adresse gibt,
314 // als die Größe des Codes an dieser Adresse zu prüfen.
315 // Siehe https://ethereum.stackexchange.com/a/14016/36603
316 // für weitere Details zur Funktionsweise.
317 // TODO Dies vor der Serenity-Version erneut prüfen, da dann alle Adressen
318 // Verträge 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

Weiterführende Lektüre

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

War dieser Artikel hilfreich?