Przejdź do głównej zawartości
Change page

Anatomia inteligentnych kontraktów

Ostatnia edycja: @Beas(opens in a new tab), 19 listopada 2023

Inteligentny kontrakt to program, który działa pod adresem Ethereum. Składają się z danych i funkcji, które można wykonać po otrzymaniu transakcji. Oto przegląd tego, co stanowi inteligentny kontrakt.

Warunki wstępne

Upewnij się, że najpierw przeczytałeś o inteligentnych kontraktach. Ten dokument zakłada, że znasz już języki programowania, takie jak JavaScript lub Python.

Dane

Wszelkie dane kontraktu muszą być przypisane do lokalizacji: do storage lub memory. Modyfikacja pamięci masowej w inteligentnym kontrakcie jest kosztowna, więc musisz zastanowić się, gdzie powinny znajdować się Twoje dane.

Pamięć

Trwałe dane są nazywane pamięcią masową i są reprezentowane przez zmienne stanu. Te wartości są przechowywane na stałe w blockchain. Musisz zadeklarować typ, aby kontrakt mógł śledzić, ile pamięci w blockchainie potrzebuje podczas kompilacji.

1// Przykład Solidity
2contract SimpleStorage {
3 uint storedData; // Zmienna stanu
4 // ...
5}
Kopiuj
1# Przykład Vyper
2storedData: int128
Kopiuj

Jeśli programowałeś już w językach obiektowych, prawdopodobnie znasz większość typów. Jednak address powinien być dla Ciebie nowy, jeśli dopiero zaczynasz programować w Ethereum.

Typ address może zawierać adres Ethereum, który odpowiada 20 bajtom lub 160 bitom. Jest zwracany w zapisach szesnastkowych z wiodącym 0x.

Inne typy:

  • boolean
  • liczba całkowita
  • fixed point numbers
  • fixed-size byte arrays
  • dynamically-sized byte arrays
  • Rational and integer literals
  • String literals
  • Hexadecimal literals
  • Enums

Aby uzyskać więcej wyjaśnień, zapoznaj się z dokumentami:

Pamięć

Wartości przechowywane tylko przez cały okres wykonywania funkcji kontraktowej nazywane są zmiennymi pamięci. Ponieważ nie są one przechowywane na stałe w blockchain, są znacznie tańsze w użyciu.

Dowiedz się więcej o tym, jak EVM przechowuje dane (magazyn, pamięć i stos) w Dokumenty Solidity(opens in a new tab).

Zmienne środowiskowe

Oprócz zmiennych, które definiujesz w kontrakcie, istnieją pewne specjalne zmienne globalne. Są one wykorzystywane głównie do dostarczania informacji na temat łańcucha bloków lub bieżącej transakcji.

Przykłady:

PropZmienna stanuOpis
block.timestampuint256Aktualny blok — znacznik czasu epoki
msg.senderaddressNadawca wiadomości (bieżące wywołanie)

Funkcje

W najbardziej uproszczonym ujęciu, funkcje mogą pobierać informacje lub ustawiać informacje w odpowiedzi na przychodzące transakcje.

Istnieją dwa rodzaje wywołań funkcji:

  • internal – nie tworzą one wywołania EVM
    • Do funkcji i zmiennych stanu internal można uzyskać dostęp wyłącznie wewnętrznie (tzn. z bieżącego kontraktu lub pochodzących od niego kontraktów)
  • external – tworzą one wywołanie EVM
    • Funkcje zewnętrzne są częścią interfejsu kontraktu, co oznacza, że mogą być wywoływane z innych kontraktów oraz poprzez transakcje. Funkcja zewnętrzna f nie może być wywołana wewnętrznie (tj. f() nie działa, ale this.f() działa).

Mogą być także public lub private

  • Funkcje public mogą być wywoływane wewnętrznie w ramach kontraktu lub zewnętrznie za pośrednictwem wiadomości
  • Funkcje private są widoczne tylko dla kontraktu, w którym są zdefiniowane, a nie w kontraktach zależnych

Zarówno funkcje, jak i zmienne stanu mogą być publiczne lub prywatne

Oto funkcja aktualizacji zmiennej stanu w kontrakcie:

1// Przykład Solidity
2function update_name(string value) public {
3 dapp_name = value;
4}
Kopiuj
  • Parametr value typu string jest przekazywany do funkcji: update_name
  • Jest zadeklarowany jako public, co oznacza, że każdy może uzyskać do niego dostęp
  • Nie jest zadeklarowany view, więc może modyfikować stan kontraktu

Funkcje view

Funkcje te obiecują nie zmieniać stanu danych kontraktu. Typowe przykłady to funkcje „getter”, które można wykorzystać na przykład do uzyskania salda użytkownika.

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

Co jest uważane za modyfikację stanu:

  1. Zapis do zmiennych stanu.
  2. Emisja zdarzeń(opens in a new tab).
  3. Tworzenie innych kontraktów(opens in a new tab).
  4. Używanie selfdestruct.
  5. Wysyłanie etheru za pomocą wywołań.
  6. Wywołanie dowolnej funkcji nieoznaczonej view lub pure.
  7. Używanie wywołań niskiego poziomu.
  8. Korzystanie z asemblera wbudowanego, który zawiera określone kody operacji.

Funkcje constructor

konstruktor funkcje są wykonywane tylko raz w momencie pierwszego wdrożenia kontraktu. Podobnie jak konstruktor w wielu językach programowania opartych na klasie, funkcje te często inicjują zmienne stanu do ich określonych wartości.

1// Przykład Solidity
2// Inicjuje dane umowy, ustawia `właściciela`
3// na adres twórcy kontraktu.
4constructor() public {
5 // Wszystkie inteligentne kontrakty opierają się na transakcjach zewnętrznych, aby wyzwolić swoje funkcje.
6 // `msg` to zmienna globalna zawierająca odpowiednie dane dotyczące danej transakcji,
7 // takie jak adres nadawcy i wartość ETH zawarta w transakcji.
8 // Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
9 owner = msg.sender;
10}
Pokaż wszystko
Kopiuj
1# Przykład 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
Kopiuj

Wbudowane funkcje

Oprócz zmiennych i funkcji, które definiujesz w kontrakcie, istnieje kilka specjalnych wbudowanych funkcji. Najbardziej oczywistym przykładem jest:

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

Pozwalają one na wysyłanie ETH do innych kont.

Pisanie funkcji

Twoja funkcja wymaga:

  • zmiennej i typu parametru (jeżeli akceptuje parametry)
  • deklaracji wewnętrznej/zewnętrznej
  • deklaracji pure/view/payable
  • typu zwrotów (jeśli zwraca wartość)
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}
Pokaż wszystko
Kopiuj

Pełny kontrakt może wyglądać w ten sposób. Tutaj funkcja constructor zapewnia początkową wartość zmiennej dapp_name.

Zdarzenia i dzienniki

Zdarzenia pozwalają Ci komunikować się z inteligentnym kontraktem z Twojego frontendu lub innych aplikacji subskrybujących. Gdy transakcja zostanie wykopana, inteligentne kontrakty mogą emitować zdarzenia i zapisywać do blockchainu dzienniki, które frontend może następnie przetworzyć.

Przykłady z komentarzami

Są to niektóre przykłady napisane w Solidity. Jeśli chcesz pobawić się kodem, możesz wchodzić z nimi w interakcję w Remix(opens in a new tab).

Witaj świecie

1// Określa wersję Solidity przy użyciu wersji semantycznej.
2// Więcej informacji: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
3pragma solidity ^0.5.10;
4
5// Definiuje kontrakt o nazwie `HelloWorld`.
6// Kontrakt jest zbiorem funkcji i danych (jego stanu).
7// Po wdrożeniu kontrakt znajduje się pod określonym adresem w blockchainie Ethereum.
8// Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
9contract HelloWorld {
10
11 // Deklaruje zmienną stanu `message` typu `string`.
12 // zmienne stanu to zmienne, których wartości są stale przechowywane w pamięci kontraktów.
13 // Słowo kluczowe `public` udostępnia zmienne spoza kontraktu
14 // i tworzy funkcję, którą inne kontrakty lub klienci mogą wywołać, aby uzyskać dostęp do tej wartości.
15 ciąg wiadomości publicznych;
16
17 // Podobne do wielu języków obiektowych opartych na klasie, konstruktorem jest
18 // specjalna funkcja, która jest wykonywana tylko w momencie tworzenia kontraktu.
19 // Konstruktory są używane do inicjowania danych kontraktu.
20 // Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
21 constructor(string memory initMessage) public {
22 //Akceptuje argument ciągu `initMessage` i ustawia wartość
23 // na zmienną pamięci kontraktu `message`).
24 wiadomość = initMessage;
25 }
26
27 // funkcja publiczna, która akceptuje argument ciągu
28 // i aktualizuje zmienną pamięci `message`.
29 function update(string memory newMessage) public {
30 message = newMessage;
31 }
32}
Pokaż wszystko
Kopiuj

Token

1pragma solidity ^0.5.10;
2
3contract Token {
4 // Adres porównywalny z adresem e-mail - jest używany do indentyfikacji konta w Ethereum.
5 // Adresy mogą reprezentować inteligentne kontrakty lub konta zewnętrzne (użytkowników).
6 // Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/types.html#address
7 address public owner;
8
9 // Mapowanie jest zasadniczo strukturą danych o postaci tablicy skrótów.
10 // To mapowanie przypisuje niepodpisaną liczbę całkowitą (saldo tokena) do adresu (posiadacza tokenu).
11 // Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types
12 mapowanie (adres => uint) publiczne saldo;
13
14 // Wydarzenia pozwalają na rejestrowanie aktywności w blockchain.
15 // Klienci Ethereum mogą słuchać zdarzeń, aby reagować na zmiany stanu kontraktu.
16 // Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/contracty. tml#events
17 Transferu zdarzeń (adres od, adres do kwoty uint);
18
19 // inicjuje dane umowy, ustawienie `właściciela`
20 // na adres twórcy kontraktu.
21 constructor() public {
22 // Wszystkie inteligentne kontrakty opierają się na transakcjach zewnętrznych, aby wyzwolić swoje funkcje.
23 // `msg` to zmienna globalna zawierająca odpowiednie dane dotyczące danej transakcji,
24 // takie jak adres nadawcy i wartość ETH zawarta w transakcji.
25 // Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties
26 owner = msg.sender;
27 }
28
29 // Tworzy liczbę nowych tokenów i wysyła je na adres.
30 function mint(address receiver, uint amount) public {
31 // `require` jest strukturą kontroli używaną do wymuszania pewnych warunków.
32 // Jeśli wyrażenie `require` oceni na `false`, wyzwalany jest wyjątek,
33 // który cofa wszystkie zmiany w stanie podczas bieżącego wywołąnia.
34 // Więcej informacji: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
35
36 // Tylko właściciel kontraktu może wywołać tę funkcję
37 require(msg.sender == owner, "You are not the owner.");
38
39 // Wymusza maksymalną kwotę tokenów
40 require(amount < 1e60, "Maximum issuance exceeded");
41
42 // Zwiększa saldo `receiver` o `amount`
43 balances[receiver] += amount;
44 }
45
46 // Wysyła kwotę istniejących tokenów od dowolnego wywołującego na adres.
47 function transfer(address receiver, uint amount) public {
48 // Nadawca musi mieć wystarczającą ilość tokenów, aby wysłać
49 require(amount <= balances[msg.sender], "Insufficient balance.");
50
51 // Dostosowuje salda tokenów z dwóch adresów
52 balances[msg.sender] -= amount;
53 balances[receiver] += amount;
54
55 // Emituje wydarzenie zdefiniowane wcześniej
56 emit Transfer(msg.sender, receiver, amount);
57 }
58}
Pokaż wszystko
Kopiuj

Unikalne zasoby cyfrowe

1pragma solidity ^0.5.10;
2
3// Imports symbols from other files into the current contract.
4// In this case, a series of helper contracts from OpenZeppelin.
5
6
7// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#importing-other-source-files
8
9import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol";
10import "../node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
11import "../node_modules/@openzeppelin/contracts/introspection/ERC165.sol";
12import "../node_modules/@openzeppelin/contracts/math/SafeMath.sol";
13
14// The `is` keyword is used to inherit functions and keywords from external contracts.
15// In this case, `CryptoPizza` inherits from the `IERC721` and `ERC165` contracts.
16
17
18// Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#inheritance
19contract CryptoPizza is IERC721, ERC165 {
20 // Uses OpenZeppelin's SafeMath library to perform arithmetic operations safely.
21 // Learn more: https://docs.openzeppelin.com/contracts/2.x/api/math#SafeMath
22 using SafeMath for uint256;
23
24 // Constant state variables in Solidity are similar to other languages
25 // but you must assign from an expression which is constant at compile time.
26
27
28 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constant-state-variables
29 uint256 constant dnaDigits = 10;
30 uint256 constant dnaModulus = 10 ** dnaDigits;
31 bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
32
33 // Struct types let you define your own type
34 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs
35 struct Pizza {
36 string name;
37 uint256 dna;
38 }
39
40 // Creates an empty array of Pizza structs
41 Pizza[] public pizzas;
42
43 // Mapping from pizza ID to its owner's address
44 mapping(uint256 => address) public pizzaToOwner;
45
46 // Mapping from owner's address to number of owned token
47 mapping(address => uint256) public ownerPizzaCount;
48
49 // Mapping from token ID to approved address
50 mapping(uint256 => address) pizzaApprovals;
51
52 // You can nest mappings, this example maps owner to operator approvals
53 mapping(address => mapping(address => bool)) private operatorApprovals;
54
55 // Internal function to create a random Pizza from string (name) and DNA
56 function _createPizza(string memory _name, uint256 _dna)
57 // The `internal` keyword means this function is only visible
58 // within this contract and contracts that derive this contract
59 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters
60 internal
61 // `isUnique` is a function modifier that checks if the pizza already exists
62 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers
63 isUnique(_name, _dna)
64 {
65 // Adds Pizza to array of Pizzas and get id
66 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);
67
68 // Checks that Pizza owner is the same as current user
69 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions
70 assert(pizzaToOwner[id] == address(0));
71
72 // Maps the Pizza to the owner
73 pizzaToOwner[id] = msg.sender;
74 ownerPizzaCount[msg.sender] = SafeMath.add(
75 ownerPizzaCount[msg.sender],
76 1
77 );
78 }
79
80 // Creates a random Pizza from string (name)
81 function createRandomPizza(string memory _name) public {
82 uint256 randDna = generateRandomDna(_name, msg.sender);
83 _createPizza(_name, randDna);
84 }
85
86 // Generates random DNA from string (name) and address of the owner (creator)
87 function generateRandomDna(string memory _str, address _owner)
88 public
89 // Functions marked as `pure` promise not to read from or modify the state
90 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions
91 pure
92 returns (uint256)
93 {
94 // Generates random uint from string (name) + address (owner)
95 uint256 rand = uint256(keccak256(abi.encodePacked(_str))) +
96 uint256(_owner);
97 rand = rand % dnaModulus;
98 return rand;
99 }
100
101 // Returns array of Pizzas found by owner
102 function getPizzasByOwner(address _owner)
103 public
104 // Functions marked as `view` promise not to modify state
105 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions
106 view
107 returns (uint256[] memory)
108 {
109 // Uses the `memory` storage location to store values only for the
110 // lifecycle of this function call.
111 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/introduction-to-smart-contracts.html#storage-memory-and-the-stack
112 uint256[] memory result = new uint256[](ownerPizzaCount[_owner]);
113 uint256 counter = 0;
114 for (uint256 i = 0; i < pizzas.length; i++) {
115 if (pizzaToOwner[i] == _owner) {
116 result[counter] = i;
117 counter++;
118 }
119 }
120 return result;
121 }
122
123 // Transfers Pizza and ownership to other address
124 function transferFrom(address _from, address _to, uint256 _pizzaId) public {
125 require(_from != address(0) && _to != address(0), "Invalid address.");
126 require(_exists(_pizzaId), "Pizza does not exist.");
127 require(_from != _to, "Cannot transfer to the same address.");
128 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
129
130 ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);
131 ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);
132 pizzaToOwner[_pizzaId] = _to;
133
134 // Emits event defined in the imported IERC721 contract
135 emit Transfer(_from, _to, _pizzaId);
136 _clearApproval(_to, _pizzaId);
137 }
138
139 /**
140 * Safely transfers the ownership of a given token ID to another address
141 * If the target address is a contract, it must implement `onERC721Received`,
142 * which is called upon a safe transfer, and return the magic value
143 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
144 * otherwise, the transfer is reverted.
145 */
146 function safeTransferFrom(address from, address to, uint256 pizzaId)
147 public
148 {
149 // solium-disable-next-line arg-overflow
150 this.safeTransferFrom(from, to, pizzaId, "");
151 }
152
153 /**
154 * Safely transfers the ownership of a given token ID to another address
155 * If the target address is a contract, it must implement `onERC721Received`,
156 * which is called upon a safe transfer, and return the magic value
157 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;
158 * otherwise, the transfer is reverted.
159 */
160 function safeTransferFrom(
161 address from,
162 address to,
163 uint256 pizzaId,
164 bytes memory _data
165 ) public {
166 this.transferFrom(from, to, pizzaId);
167 require(_checkOnERC721Received(from, to, pizzaId, _data), "Must implmement onERC721Received.");
168 }
169
170 /**
171 * Internal function to invoke `onERC721Received` on a target address
172 * The call is not executed if the target address is not a contract
173 */
174 function _checkOnERC721Received(
175 address from,
176 address to,
177 uint256 pizzaId,
178 bytes memory _data
179 ) internal returns (bool) {
180 if (!isContract(to)) {
181 return true;
182 }
183
184 bytes4 retval = IERC721Receiver(to).onERC721Received(
185 msg.sender,
186 from,
187 pizzaId,
188 _data
189 );
190 return (retval == _ERC721_RECEIVED);
191 }
192
193 // Burns a Pizza - destroys Token completely
194 // The `external` function modifier means this function is
195 // part of the contract interface and other contracts can call it
196 function burn(uint256 _pizzaId) external {
197 require(msg.sender != address(0), "Invalid address.");
198 require(_exists(_pizzaId), "Pizza does not exist.");
199 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
200
201 ownerPizzaCount[msg.sender] = SafeMath.sub(
202 ownerPizzaCount[msg.sender],
203 1
204 );
205 pizzaToOwner[_pizzaId] = address(0);
206 }
207
208 // Returns count of Pizzas by address
209 function balanceOf(address _owner) public view returns (uint256 _balance) {
210 return ownerPizzaCount[_owner];
211 }
212
213 // Returns owner of the Pizza found by id
214 function ownerOf(uint256 _pizzaId) public view returns (address _owner) {
215 address owner = pizzaToOwner[_pizzaId];
216 require(owner != address(0), "Invalid Pizza ID.");
217 return owner;
218 }
219
220 // Approves other address to transfer ownership of Pizza
221 function approve(address _to, uint256 _pizzaId) public {
222 require(msg.sender == pizzaToOwner[_pizzaId], "Must be the Pizza owner.");
223 pizzaApprovals[_pizzaId] = _to;
224 emit Approval(msg.sender, _to, _pizzaId);
225 }
226
227 // Returns approved address for specific Pizza
228 function getApproved(uint256 _pizzaId)
229 public
230 view
231 returns (address operator)
232 {
233 require(_exists(_pizzaId), "Pizza does not exist.");
234 return pizzaApprovals[_pizzaId];
235 }
236
237 /**
238 * Private function to clear current approval of a given token ID
239 * Reverts if the given address is not indeed the owner of the token
240 */
241 function _clearApproval(address owner, uint256 _pizzaId) private {
242 require(pizzaToOwner[_pizzaId] == owner, "Must be pizza owner.");
243 require(_exists(_pizzaId), "Pizza does not exist.");
244 if (pizzaApprovals[_pizzaId] != address(0)) {
245 pizzaApprovals[_pizzaId] = address(0);
246 }
247 }
248
249 /*
250 * Sets or unsets the approval of a given operator
251 * An operator is allowed to transfer all tokens of the sender on their behalf
252 */
253 function setApprovalForAll(address to, bool approved) public {
254 require(to != msg.sender, "Cannot approve own address");
255 operatorApprovals[msg.sender][to] = approved;
256 emit ApprovalForAll(msg.sender, to, approved);
257 }
258
259 // Tells whether an operator is approved by a given owner
260 function isApprovedForAll(address owner, address operator)
261 public
262 view
263 returns (bool)
264 {
265 return operatorApprovals[owner][operator];
266 }
267
268 // Takes ownership of Pizza - only for approved users
269 function takeOwnership(uint256 _pizzaId) public {
270 require(_isApprovedOrOwner(msg.sender, _pizzaId), "Address is not approved.");
271 address owner = this.ownerOf(_pizzaId);
272 this.transferFrom(owner, msg.sender, _pizzaId);
273 }
274
275 // Checks if Pizza exists
276 function _exists(uint256 pizzaId) internal view returns (bool) {
277 address owner = pizzaToOwner[pizzaId];
278 return owner != address(0);
279 }
280
281 // Checks if address is owner or is approved to transfer Pizza
282 function _isApprovedOrOwner(address spender, uint256 pizzaId)
283 internal
284 view
285 returns (bool)
286 {
287 address owner = pizzaToOwner[pizzaId];
288 // Disable solium check because of
289 // https://github.com/duaraghav8/Solium/issues/175
290 // solium-disable-next-line operator-whitespace
291 return (spender == owner ||
292 this.getApproved(pizzaId) == spender ||
293 this.isApprovedForAll(owner, spender));
294 }
295
296 // Check if Pizza is unique and doesn't exist yet
297 modifier isUnique(string memory _name, uint256 _dna) {
298 bool result = true;
299 for (uint256 i = 0; i < pizzas.length; i++) {
300 if (
301 keccak256(abi.encodePacked(pizzas[i].name)) ==
302 keccak256(abi.encodePacked(_name)) &&
303 pizzas[i].dna == _dna
304 ) {
305 result = false;
306 }
307 }
308 require(result, "Pizza with such name already exists.");
309 _;
310 }
311
312 // Returns whether the target address is a contract
313 function isContract(address account) internal view returns (bool) {
314 uint256 size;
315 // Currently there is no better way to check if there is a contract in an address
316 // than to check the size of the code at that address.
317 // See https://ethereum.stackexchange.com/a/14016/36603
318 // for more details about how this works.
319 // TODO Check this again before the Serenity release, because all addresses will be
320 // contracts then.
321
322
323 // solium-disable-next-line security/no-inline-assembly
324 assembly {
325 size := extcodesize(account)
326 }
327 return size > 0;
328 }
329}
Pokaż wszystko
Kopiuj

Dodatkowo przeczytaj

Sprawdź dokumentację Solidity i Vyper, aby uzyskać pełniejszy przegląd inteligentnych kontraktów:

Czy ten artykuł był pomocny?