Anatomia inteligentnych kontraktów
Ostatnia edycja: @Beas(opens in a new tab), 14 czerwca 2024
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 Solidity2contract SimpleStorage {3 uint storedData; // Zmienna stanu4 // ...5}Kopiuj
1# Przykład Vyper2storedData: int128Kopiuj
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:
Prop | Zmienna stanu | Opis |
---|---|---|
block.timestamp | uint256 | Aktualny blok — znacznik czasu epoki |
msg.sender | address | Nadawca 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, alethis.f()
działa).
- 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
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 Solidity2function update_name(string value) public {3 dapp_name = value;4}Kopiuj
- Parametr
value
typustring
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 Solidity2function balanceOf(address _owner) public view returns (uint256 _balance) {3 return ownerPizzaCount[_owner];4}Kopiuj
1dappName: public(string)23@view4@public5def readName() -> string:6 return dappNameKopiuj
Co jest uważane za modyfikację stanu:
- Zapis do zmiennych stanu.
- Emisja zdarzeń(opens in a new tab).
- Tworzenie innych kontraktów(opens in a new tab).
- Używanie
selfdestruct
. - Wysyłanie etheru za pomocą wywołań.
- Wywołanie dowolnej funkcji nieoznaczonej
view
lubpure
. - Używanie wywołań niskiego poziomu.
- 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 Solidity2// 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-properties9 owner = msg.sender;10}Pokaż wszystkoKopiuj
1# Przykład Vyper23@external4def __init__(_beneficiary: address, _bidding_time: uint256):5 self.beneficiary = _beneficiary6 self.auctionStart = block.timestamp7 self.auctionEnd = self.auctionStart + _bidding_timeKopiuj
Wbudowane funkcje
Oprócz zmiennych i funkcji, które definiujesz w kontrakcie, istnieje kilka specjalnych wbudowanych funkcji. Najbardziej oczywistym przykładem jest:
address.send()
– Soliditysend(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;23contract ExampleDapp {4 string dapp_name; // state variable56 // Called when the contract is deployed and initializes the value7 constructor() public {8 dapp_name = "My Example dapp";9 }1011 // Get Function12 function read_name() public view returns(string) {13 return dapp_name;14 }1516 // Set Function17 function update_name(string value) public {18 dapp_name = value;19 }20}Pokaż wszystkoKopiuj
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#pragma3pragma solidity ^0.5.10;45// 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.html9contract HelloWorld {1011 // 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 kontraktu14 // 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;1617 // Podobne do wielu języków obiektowych opartych na klasie, konstruktorem jest18 // 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#constructors21 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 }2627 // funkcja publiczna, która akceptuje argument ciągu28 // i aktualizuje zmienną pamięci `message`.29 function update(string memory newMessage) public {30 message = newMessage;31 }32}Pokaż wszystkoKopiuj
Token
1pragma solidity ^0.5.10;23contract 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#address7 address public owner;89 // 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-types12 mapowanie (adres => uint) publiczne saldo;1314 // 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#events17 Transferu zdarzeń (adres od, adres do kwoty uint);1819 // 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-properties26 owner = msg.sender;27 }2829 // 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-exceptions3536 // Tylko właściciel kontraktu może wywołać tę funkcję37 require(msg.sender == owner, "You are not the owner.");3839 // Wymusza maksymalną kwotę tokenów40 require(amount < 1e60, "Maximum issuance exceeded");4142 // Zwiększa saldo `receiver` o `amount`43 balances[receiver] += amount;44 }4546 // 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.");5051 // Dostosowuje salda tokenów z dwóch adresów52 balances[msg.sender] -= amount;53 balances[receiver] += amount;5455 // Emituje wydarzenie zdefiniowane wcześniej56 emit Transfer(msg.sender, receiver, amount);57 }58}Pokaż wszystkoKopiuj
Unikalne zasoby cyfrowe
1pragma solidity ^0.5.10;23// Imports symbols from other files into the current contract.4// In this case, a series of helper contracts from OpenZeppelin.567// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#importing-other-source-files89import "../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";1314// 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.161718// Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#inheritance19contract 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#SafeMath22 using SafeMath for uint256;2324 // Constant state variables in Solidity are similar to other languages25 // but you must assign from an expression which is constant at compile time.262728 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constant-state-variables29 uint256 constant dnaDigits = 10;30 uint256 constant dnaModulus = 10 ** dnaDigits;31 bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;3233 // Struct types let you define your own type34 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs35 struct Pizza {36 string name;37 uint256 dna;38 }3940 // Creates an empty array of Pizza structs41 Pizza[] public pizzas;4243 // Mapping from pizza ID to its owner's address44 mapping(uint256 => address) public pizzaToOwner;4546 // Mapping from owner's address to number of owned token47 mapping(address => uint256) public ownerPizzaCount;4849 // Mapping from token ID to approved address50 mapping(uint256 => address) pizzaApprovals;5152 // You can nest mappings, this example maps owner to operator approvals53 mapping(address => mapping(address => bool)) private operatorApprovals;5455 // Internal function to create a random Pizza from string (name) and DNA56 function _createPizza(string memory _name, uint256 _dna)57 // The `internal` keyword means this function is only visible58 // within this contract and contracts that derive this contract59 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters60 internal61 // `isUnique` is a function modifier that checks if the pizza already exists62 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers63 isUnique(_name, _dna)64 {65 // Adds Pizza to array of Pizzas and get id66 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);6768 // Checks that Pizza owner is the same as current user69 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions70 assert(pizzaToOwner[id] == address(0));7172 // Maps the Pizza to the owner73 pizzaToOwner[id] = msg.sender;74 ownerPizzaCount[msg.sender] = SafeMath.add(75 ownerPizzaCount[msg.sender],76 177 );78 }7980 // 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 }8586 // Generates random DNA from string (name) and address of the owner (creator)87 function generateRandomDna(string memory _str, address _owner)88 public89 // Functions marked as `pure` promise not to read from or modify the state90 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions91 pure92 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 }100101 // Returns array of Pizzas found by owner102 function getPizzasByOwner(address _owner)103 public104 // Functions marked as `view` promise not to modify state105 // Learn more: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions106 view107 returns (uint256[] memory)108 {109 // Uses the `memory` storage location to store values only for the110 // 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-stack112 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 }122123 // Transfers Pizza and ownership to other address124 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.");129130 ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);131 ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);132 pizzaToOwner[_pizzaId] = _to;133134 // Emits event defined in the imported IERC721 contract135 emit Transfer(_from, _to, _pizzaId);136 _clearApproval(_to, _pizzaId);137 }138139 /**140 * Safely transfers the ownership of a given token ID to another address141 * If the target address is a contract, it must implement `onERC721Received`,142 * which is called upon a safe transfer, and return the magic value143 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;144 * otherwise, the transfer is reverted.145 */146 function safeTransferFrom(address from, address to, uint256 pizzaId)147 public148 {149 // solium-disable-next-line arg-overflow150 this.safeTransferFrom(from, to, pizzaId, "");151 }152153 /**154 * Safely transfers the ownership of a given token ID to another address155 * If the target address is a contract, it must implement `onERC721Received`,156 * which is called upon a safe transfer, and return the magic value157 * `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 _data165 ) public {166 this.transferFrom(from, to, pizzaId);167 require(_checkOnERC721Received(from, to, pizzaId, _data), "Must implmement onERC721Received.");168 }169170 /**171 * Internal function to invoke `onERC721Received` on a target address172 * The call is not executed if the target address is not a contract173 */174 function _checkOnERC721Received(175 address from,176 address to,177 uint256 pizzaId,178 bytes memory _data179 ) internal returns (bool) {180 if (!isContract(to)) {181 return true;182 }183184 bytes4 retval = IERC721Receiver(to).onERC721Received(185 msg.sender,186 from,187 pizzaId,188 _data189 );190 return (retval == _ERC721_RECEIVED);191 }192193 // Burns a Pizza - destroys Token completely194 // The `external` function modifier means this function is195 // part of the contract interface and other contracts can call it196 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.");200201 ownerPizzaCount[msg.sender] = SafeMath.sub(202 ownerPizzaCount[msg.sender],203 1204 );205 pizzaToOwner[_pizzaId] = address(0);206 }207208 // Returns count of Pizzas by address209 function balanceOf(address _owner) public view returns (uint256 _balance) {210 return ownerPizzaCount[_owner];211 }212213 // Returns owner of the Pizza found by id214 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 }219220 // Approves other address to transfer ownership of Pizza221 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 }226227 // Returns approved address for specific Pizza228 function getApproved(uint256 _pizzaId)229 public230 view231 returns (address operator)232 {233 require(_exists(_pizzaId), "Pizza does not exist.");234 return pizzaApprovals[_pizzaId];235 }236237 /**238 * Private function to clear current approval of a given token ID239 * Reverts if the given address is not indeed the owner of the token240 */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 }248249 /*250 * Sets or unsets the approval of a given operator251 * An operator is allowed to transfer all tokens of the sender on their behalf252 */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 }258259 // Tells whether an operator is approved by a given owner260 function isApprovedForAll(address owner, address operator)261 public262 view263 returns (bool)264 {265 return operatorApprovals[owner][operator];266 }267268 // Takes ownership of Pizza - only for approved users269 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 }274275 // Checks if Pizza exists276 function _exists(uint256 pizzaId) internal view returns (bool) {277 address owner = pizzaToOwner[pizzaId];278 return owner != address(0);279 }280281 // Checks if address is owner or is approved to transfer Pizza282 function _isApprovedOrOwner(address spender, uint256 pizzaId)283 internal284 view285 returns (bool)286 {287 address owner = pizzaToOwner[pizzaId];288 // Disable solium check because of289 // https://github.com/duaraghav8/Solium/issues/175290 // solium-disable-next-line operator-whitespace291 return (spender == owner ||292 this.getApproved(pizzaId) == spender ||293 this.isApprovedForAll(owner, spender));294 }295296 // Check if Pizza is unique and doesn't exist yet297 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 == _dna304 ) {305 result = false;306 }307 }308 require(result, "Pizza with such name already exists.");309 _;310 }311312 // Returns whether the target address is a contract313 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 address316 // than to check the size of the code at that address.317 // See https://ethereum.stackexchange.com/a/14016/36603318 // for more details about how this works.319 // TODO Check this again before the Serenity release, because all addresses will be320 // contracts then.321322323 // solium-disable-next-line security/no-inline-assembly324 assembly {325 size := extcodesize(account)326 }327 return size > 0;328 }329}Pokaż wszystkoKopiuj
Dodatkowo przeczytaj
Sprawdź dokumentację Solidity i Vyper, aby uzyskać pełniejszy przegląd inteligentnych kontraktów:
Powiązane tematy
Powiązane samouczki
- Zmniejszenie kontraktów w celu walki z limitem wielkości kontraktu – kilka praktycznych wskazówek, jak zmniejszyć rozmiar inteligentnego kontraktu.
- Rejestrowanie danych z inteligentnych kontraktów za pomocą zdarzeń – wprowadzenie do zdarzeń inteligentnych kontraktów i jak możesz ich używać do rejestrowania danych.
- Interakcja z innymi umowami z Solidity – jak wdrożyć inteligentny kontrakt z istniejącego kontraktu i wchodzić z nim w interakcje.