Докладніше про розумні контракти
Останні оновлення сторінки: 23 лютого 2026 р.
Розумний контракт - це програма, що працює за адресою в Ethereum. Вони складаються з даних та функцій, які можуть виконуватися після отримання транзакції. Ось огляд того, що формує розумний контракт.
Передумови
Переконайтеся, що ви спочатку прочитали про смарт-контракти. Цей документ передбачає, що ви вже знайомі з такими мовами програмування, як JavaScript чи Python.
Дані
Будь-які дані контракту мають бути призначені до розташування: або в сховище (storage), або в пам’ять (memory). Варто змінити зберігання в розумному контракті, тож вам слід подумати, де повинні міститися ваші дані.
Сховище
Постійні дані називають сховищем та представлені змінними стану. Ці значення постійно зберігаються в блокчейні. Вам слід оголосити тип таким чином, щоб контракт міг відстежувати, скільки пам’яті в блокчейні йому потрібно під час компіляції.
1// Приклад на Solidity2contract SimpleStorage {3 uint storedData; // Змінна стану4 // ...5}1# Приклад на Vyper2storedData: int128Якщо ви вже запрограмували об'єктно-орієнтовані мови, ймовірно, ви знайомі з більшістю типів. Однак тип address буде для вас новим, якщо ви новачок у розробці на Ethereum.
Тип address може містити адресу Ethereum, що дорівнює 20 байтам або 160 бітам. Він повертається у шістнадцятковій системі запису з провідним 0x.
До інших типів належать:
- логічний
- цілий
- числа фіксованих точок
- байтові масиви фіксованого розміру
- масиви байтів динамічного розміру
- раціональні та цілочисельні літерали
- рядкові літерали
- шістнадцяткові літерали
- перелічення
Для додаткового пояснення зверніться до документів:
Пам’ять
Значення, які зберігаються лише протягом усього терміну виконання функції контракту, називаються змінними пам’яті. Оскільки вони не зберігаються у блокчейні назавжди, їх використання набагато дешевше.
Дізнайтеся більше про те, як EVM зберігає дані (сховище, пам’ять і стек) у документації Solidity (opens in a new tab).
Змінні середовища
На додаток до змінних, які ви виділяєте у вашому контракті, є деякі особливі глобальні змінні. Насамперед, вони використовуються для надання інформації про блокчейн чи поточну транзакцію.
Приклади:
| Властивість | Змінна стану | Опис |
|---|---|---|
block.timestamp | uint256 | Поточна часова позначка блоку |
msg.sender | address | Відправник повідомлення (поточний дзвінок) |
Функції
Простіше кажучи, функції можуть отримувати інформацію або задавати інформацію у відповідь на вхідні транзакції.
Існує два типу виклику функції:
internal– вони не створюють виклик EVM- Внутрішні функції та змінні стану можуть бути доступні лише всередині (тобто з поточного контракту або контрактів, що є його похідними).
external– вони створюють виклик EVM- Зовнішні функції є частиною інтерфейс договору, що означає, що їх можна викликати з інших договорів і за допомогою транзакцій. Зовнішню функцію
fне можна викликати всередині (тобто викликf()не спрацює, аthis.f()спрацює).
- Зовнішні функції є частиною інтерфейс договору, що означає, що їх можна викликати з інших договорів і за допомогою транзакцій. Зовнішню функцію
Вони також можуть бути public або private
- Функції
publicможна викликати зсередини контракту або ззовні через повідомлення. - Функції
privateвидимі лише для контракту, у якому вони визначені, і не видимі для похідних контрактів.
Обидві функції і змінні стану можуть бути публічними чи приватними
Ось функція оновлення змінної стану за договором:
1// Приклад на Solidity2function update_name(string value) public {3 dapp_name = value;4}- Параметр
valueтипуstringпередається у функціюupdate_name. - Її оголошено як
public, тобто до неї може отримати доступ будь-хто. - Її не оголошено як
view, тому вона може змінювати стан контракту.
Функції View
Ці функції обіцяють не змінювати стан даних договору. Поширеними прикладами є функції "getter" – наприклад, ви можете використовувати її, щоб отримати баланс користувача.
1// Приклад на Solidity2function balanceOf(address _owner) public view returns (uint256 _balance) {3 return ownerPizzaCount[_owner];4}1dappName: public(string)23@view4@public5def readName() -> string:6 return dappNameЩо вважається змінами стану:
- Запис до змінних стану.
- Генерація подій (opens in a new tab).
- Створення інших контрактів (opens in a new tab).
- Використання
selfdestruct. - Надсилання через дзвінки.
- Виклик будь-якої функції, не позначеної як
viewабоpure. - Використання дзвінків низького рівня.
- Використовуючи вбудовану збірку, що містить деякі коди.
Функції-конструктори
Функції constructor виконуються лише один раз, під час першого розгортання контракту. Подібно до constructor у багатьох об’єктно-орієнтованих мовах програмування, ці функції часто ініціалізують змінні стану, присвоюючи їм зазначені значення.
1// Приклад на Solidity2// Ініціалізує дані контракту, встановлюючи `owner`3// як адресу автора контракту.4constructor() public {5 // Усі смарт-контракти покладаються на зовнішні транзакції для запуску своїх функцій.6 // `msg` — це глобальна змінна, яка містить відповідні дані про дану транзакцію,7 // наприклад адресу відправника та суму ETH, включену до транзакції.8 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties9 owner = msg.sender;10}Показати все1# Приклад на Vyper23@external4def __init__(_beneficiary: address, _bidding_time: uint256):5 self.beneficiary = _beneficiary6 self.auctionStart = block.timestamp7 self.auctionEnd = self.auctionStart + _bidding_timeВбудовані функції
На додаток до змінних та функцій, які ви визначаєте у вашому договорі, є ще деякі спеціальні вбудовані функції. Найбільш наочний приклад:
address.send()– Soliditysend(address)– Vyper
Вони дозволяють договорам відправляти ETH на інші облікові записи.
Написання функцій
Потреби функцій:
- змінна параметра та тип (якщо він приймає параметри)
- оголошення внутрішнього/зовнішньої
- оголошення чистий/перегляд/платний
- тип повернення (якщо повертає значення)
1pragma solidity >=0.4.0 <=0.6.0;23contract ExampleDapp {4 string dapp_name; // змінна стану56 // Викликається під час розгортання контракту та ініціалізує значення7 constructor() public {8 dapp_name = "My Example dapp";9 }1011 // Функція Get12 function read_name() public view returns(string) {13 return dapp_name;14 }1516 // Функція Set17 function update_name(string value) public {18 dapp_name = value;19 }20}Показати всеПовний контракт може виглядати приблизно так. Тут функція constructor надає початкове значення для змінної dapp_name.
Події та журнали
Події дають змогу вашому смарт-контракту обмінюватися даними з інтерфейсом або іншими підписаними застосунками. Після того, як транзакція буде підтверджена та додана до блоку, смарт-контракти можуть генерувати події та реєструвати інформацію, яку фронтенд може потім обробити та використати.
Приклади з коментарями
Деякі приклади написані в Solidity. Якщо ви хочете поекспериментувати з кодом, ви можете взаємодіяти з ним у Remix (opens in a new tab).
Привіт, світе
1// Вказує версію Solidity, використовуючи семантичне версіонування.2// Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma3pragma solidity ^0.5.10;45// Оголошує контракт під назвою `HelloWorld`.6// Контракт — це набір функцій і даних (його стан).7// Після розгортання контракт знаходиться за певною адресою в блокчейні Ethereum.8// Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html9contract HelloWorld {1011 // Оголошує змінну стану `message` типу `string`.12 // Змінні стану — це змінні, значення яких постійно зберігаються у сховищі контракту.13 // Ключове слово `public` робить змінні доступними ззовні контракту14 // і створює функцію, яку інші контракти або клієнти можуть викликати для доступу до значення.15 string public message;1617 // Подібно до багатьох об'єктно-орієнтованих мов, конструктор — це18 // спеціальна функція, яка виконується лише під час створення контракту.19 // Конструктори використовуються для ініціалізації даних контракту.20 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors21 constructor(string memory initMessage) public {22 // Приймає рядковий аргумент `initMessage` і встановлює значення23 // у змінну `message` у сховищі контракту).24 message = initMessage;25 }2627 // Публічна функція, яка приймає рядковий аргумент28 // і оновлює змінну `message` у сховищі.29 function update(string memory newMessage) public {30 message = newMessage;31 }32}Показати всеТокен
1pragma solidity ^0.5.10;23contract Token {4 // `address` можна порівняти з адресою електронної пошти — він використовується для ідентифікації облікового запису в Ethereum.5 // Адреси можуть представляти смарт-контракт або зовнішні (користувацькі) облікові записи.6 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/types.html#address7 address public owner;89 // `mapping` по суті є структурою даних хеш-таблиці.10 // Це `mapping` присвоює беззнакове ціле число (баланс токенів) адресі (власнику токенів).11 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/types.html#mapping-types12 mapping (address => uint) public balances;1314 // Події дозволяють реєструвати активність у блокчейні.15 // Клієнти Ethereum можуть прослуховувати події, щоб реагувати на зміни стану контракту.16 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#events17 event Transfer(address from, address to, uint amount);1819 // Ініціалізує дані контракту, встановлюючи `owner`20 // як адресу автора контракту.21 constructor() public {22 // Усі смарт-контракти покладаються на зовнішні транзакції для запуску своїх функцій.23 // `msg` — це глобальна змінна, яка містить відповідні дані про дану транзакцію,24 // наприклад адресу відправника та суму ETH, включену до транзакції.25 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/units-and-global-variables.html#block-and-transaction-properties26 owner = msg.sender;27 }2829 // Створює певну кількість нових токенів і надсилає їх на адресу.30 function mint(address receiver, uint amount) public {31 // `require` — це керуюча конструкція, яка використовується для забезпечення виконання певних умов.32 // Якщо вираз `require` має значення `false`, виникає виняток,33 // який скасовує всі зміни, внесені до стану під час поточного виклику.34 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions3536 // Лише власник контракту може викликати цю функцію37 require(msg.sender == owner, "You are not the owner.");3839 // Встановлює максимальну кількість токенів40 require(amount < 1e60, "Maximum issuance exceeded");4142 // Збільшує баланс `receiver` на `amount`43 balances[receiver] += amount;44 }4546 // Надсилає певну кількість існуючих токенів від будь-якого викликаючого на адресу.47 function transfer(address receiver, uint amount) public {48 // Відправник повинен мати достатньо токенів для надсилання49 require(amount <= balances[msg.sender], "Insufficient balance.");5051 // Коригує баланси токенів двох адрес52 balances[msg.sender] -= amount;53 balances[receiver] += amount;5455 // Генерує подію, визначену раніше56 emit Transfer(msg.sender, receiver, amount);57 }58}Показати всеУнікальний цифровий актив
1pragma solidity ^0.5.10;23// Імпортує символи з інших файлів у поточний контракт.4// У цьому випадку це серія допоміжних контрактів від OpenZeppelin.5// Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#importing-other-source-files67import "../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";1112// Ключове слово `is` використовується для успадкування функцій і ключових слів від зовнішніх контрактів.13// У цьому випадку `CryptoPizza` успадковує контракти `IERC721` та `ERC165`.14// Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#inheritance15contract CryptoPizza is IERC721, ERC165 {16 // Використовує бібліотеку SafeMath від OpenZeppelin для безпечного виконання арифметичних операцій.17 // Дізнайтеся більше: https://docs.openzeppelin.com/contracts/2.x/api/math#SafeMath18 using SafeMath for uint256;1920 // Константи-змінні стану в Solidity схожі на інші мови,21 // але ви повинні присвоювати їм значення з виразу, який є константою на етапі компіляції.22 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constant-state-variables23 uint256 constant dnaDigits = 10;24 uint256 constant dnaModulus = 10 ** dnaDigits;25 bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;2627 // Типи Struct дозволяють вам визначати власний тип28 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/types.html#structs29 struct Pizza {30 string name;31 uint256 dna;32 }3334 // Створює порожній масив структур Pizza35 Pizza[] public pizzas;3637 // Відображення ідентифікатора піци на адресу її власника38 mapping(uint256 => address) public pizzaToOwner;3940 // Відображення адреси власника на кількість токенів у власності41 mapping(address => uint256) public ownerPizzaCount;4243 // Відображення ідентифікатора токена на затверджену адресу44 mapping(uint256 => address) pizzaApprovals;4546 // Ви можете вкладати відображення, цей приклад відображає власника на затвердження оператора47 mapping(address => mapping(address => bool)) private operatorApprovals;4849 // Внутрішня функція для створення випадкової піци з рядка (назва) та ДНК50 function _createPizza(string memory _name, uint256 _dna)51 // Ключове слово `internal` означає, що ця функція видима лише52 // в межах цього контракту та контрактів, що походять від нього53 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#visibility-and-getters54 internal55 // `isUnique` — це модифікатор функції, який перевіряє, чи піца вже існує56 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html#function-modifiers57 isUnique(_name, _dna)58 {59 // Додає піцу до масиву піц і отримує id60 uint256 id = SafeMath.sub(pizzas.push(Pizza(_name, _dna)), 1);6162 // Перевіряє, чи власник піци такий самий, як і поточний користувач63 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/control-structures.html#error-handling-assert-require-revert-and-exceptions6465 // зауважте, що address(0) — це нульова адреса,66 // що вказує на те, що pizza[id] ще не призначена конкретному користувачеві.6768 assert(pizzaToOwner[id] == address(0));6970 // Відображає піцу на власника71 pizzaToOwner[id] = msg.sender;72 ownerPizzaCount[msg.sender] = SafeMath.add(73 ownerPizzaCount[msg.sender],74 175 );76 }7778 // Створює випадкову піцу з рядка (назва)79 function createRandomPizza(string memory _name) public {80 uint256 randDna = generateRandomDna(_name, msg.sender);81 _createPizza(_name, randDna);82 }8384 // Генерує випадкову ДНК з рядка (назва) та адреси власника (автора)85 function generateRandomDna(string memory _str, address _owner)86 public87 // Функції, позначені як `pure`, обіцяють не читати та не змінювати стан88 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#pure-functions89 pure90 returns (uint256)91 {92 // Генерує випадковий uint з рядка (назва) + адреси (власник)93 uint256 rand = uint256(keccak256(abi.encodePacked(_str))) +94 uint256(_owner);95 rand = rand % dnaModulus;96 return rand;97 }9899 // Повертає масив піц, знайдених за власником100 function getPizzasByOwner(address _owner)101 public102 // Функції, позначені як `view`, обіцяють не змінювати стан103 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#view-functions104 view105 returns (uint256[] memory)106 {107 // Використовує місце зберігання `memory` для збереження значень лише на108 // час життєвого циклу цього виклику функції.109 // Дізнайтеся більше: https://solidity.readthedocs.io/en/v0.5.10/introduction-to-smart-contracts.html#storage-memory-and-the-stack110 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 }120121 // Передає піцу та право власності на іншу адресу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.");127128 ownerPizzaCount[_to] = SafeMath.add(ownerPizzaCount[_to], 1);129 ownerPizzaCount[_from] = SafeMath.sub(ownerPizzaCount[_from], 1);130 pizzaToOwner[_pizzaId] = _to;131132 // Генерує подію, визначену в імпортованому контракті IERC721133 emit Transfer(_from, _to, _pizzaId);134 _clearApproval(_to, _pizzaId);135 }136137 /**138 * Безпечно передає право власності на даний ідентифікатор токена на іншу адресу139 * Якщо цільова адреса є контрактом, вона повинна реалізовувати `onERC721Received`,140 * яка викликається під час безпечної передачі, і повертати магічне значення141 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;142 * інакше передача скасовується.143 */144 function safeTransferFrom(address from, address to, uint256 pizzaId)145 public146 {147 // solium-disable-next-line arg-overflow148 this.safeTransferFrom(from, to, pizzaId, "");149 }150151 /**152 * Безпечно передає право власності на даний ідентифікатор токена на іншу адресу153 * Якщо цільова адреса є контрактом, вона повинна реалізовувати `onERC721Received`,154 * яка викликається під час безпечної передачі, і повертати магічне значення155 * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`;156 * інакше передача скасовується.157 */158 function safeTransferFrom(159 address from,160 address to,161 uint256 pizzaId,162 bytes memory _data163 ) public {164 this.transferFrom(from, to, pizzaId);165 require(_checkOnERC721Received(from, to, pizzaId, _data), "Must implement onERC721Received.");166 }167168 /**169 * Внутрішня функція для виклику `onERC721Received` на цільовій адресі170 * Виклик не виконується, якщо цільова адреса не є контрактом171 */172 function _checkOnERC721Received(173 address from,174 address to,175 uint256 pizzaId,176 bytes memory _data177 ) internal returns (bool) {178 if (!isContract(to)) {179 return true;180 }181182 bytes4 retval = IERC721Receiver(to).onERC721Received(183 msg.sender,184 from,185 pizzaId,186 _data187 );188 return (retval == _ERC721_RECEIVED);189 }190191 // Спалює піцу — повністю знищує токен192 // Модифікатор функції `external` означає, що ця функція є193 // частиною інтерфейсу контракту, і інші контракти можуть її викликати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.");198199 ownerPizzaCount[msg.sender] = SafeMath.sub(200 ownerPizzaCount[msg.sender],201 1202 );203 pizzaToOwner[_pizzaId] = address(0);204 }205206 // Повертає кількість піц за адресою207 function balanceOf(address _owner) public view returns (uint256 _balance) {208 return ownerPizzaCount[_owner];209 }210211 // Повертає власника піци, знайденого за id212 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 }217218 // Дозволяє іншій адресі передавати право власності на піцу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 }224225 // Повертає затверджену адресу для конкретної піци226 function getApproved(uint256 _pizzaId)227 public228 view229 returns (address operator)230 {231 require(_exists(_pizzaId), "Pizza does not exist.");232 return pizzaApprovals[_pizzaId];233 }234235 /**236 * Приватна функція для скасування поточного затвердження для даного ідентифікатора токена237 * Скасовується, якщо вказана адреса насправді не є власником токена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 }246247 /*248 * Встановлює або скасовує затвердження для даного оператора249 * Оператору дозволено передавати всі токени відправника від його імені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 }256257 // Повідомляє, чи затверджений оператор даним власником258 function isApprovedForAll(address owner, address operator)259 public260 view261 returns (bool)262 {263 return operatorApprovals[owner][operator];264 }265266 // Приймає право власності на піцу — тільки для затверджених користувачів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 }272273 // Перевіряє, чи існує піца274 function _exists(uint256 pizzaId) internal view returns (bool) {275 address owner = pizzaToOwner[pizzaId];276 return owner != address(0);277 }278279 // Перевіряє, чи є адреса власником, чи затверджена для передачі піци280 function _isApprovedOrOwner(address spender, uint256 pizzaId)281 internal282 view283 returns (bool)284 {285 address owner = pizzaToOwner[pizzaId];286 // Вимкнути перевірку solium через287 // https://github.com/duaraghav8/Solium/issues/175288 // solium-disable-next-line operator-whitespace289 return (spender == owner ||290 this.getApproved(pizzaId) == spender ||291 this.isApprovedForAll(owner, spender));292 }293294 // Перевіряє, чи піца є унікальною та ще не існує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 == _dna302 ) {303 result = false;304 }305 }306 require(result, "Pizza with such name already exists.");307 _;308 }309310 // Повертає, чи є цільова адреса контрактом311 function isContract(address account) internal view returns (bool) {312 uint256 size;313 // Наразі немає кращого способу перевірити, чи є контракт за адресою,314 // ніж перевірити розмір коду за цією адресою.315 // Дивіться https://ethereum.stackexchange.com/a/14016/36603316 // для отримання більш детальної інформації про те, як це працює.317 // TODO Перевірити це ще раз перед випуском Serenity, оскільки тоді всі адреси будуть318 // контрактами.319 // solium-disable-next-line security/no-inline-assembly320 assembly {321 size := extcodesize(account)322 }323 return size > 0;324 }325}Показати всеДля подальшого читання
Перевірте документацію Solidity і Vyper's для більш повного огляду смарт-контрактів:
Пов'язані теми
Пов'язані посібники
- Зменшення розміру контрактів для боротьби з обмеженням розміру контракту – Декілька практичних порад щодо зменшення розміру вашого смарт-контракту.
- Запис даних зі смарт-контрактів за допомогою подій – Вступ до подій у смарт-контрактах і як їх можна використовувати для запису даних.
- Взаємодія з іншими контрактами з Solidity – Як розгорнути смарт-контракт з існуючого контракту та взаємодіяти з ним.