Перейти до вмісту

Solidity

Матеріал з K2 ERP Wiki

SEO title: Solidity — мова програмування для смарт-контрактів, Ethereum, EVM, DeFi, NFT і Web3 SEO description: Solidity — Wiki-стаття про мову програмування для створення смарт-контрактів у мережах Ethereum та інших EVM-сумісних блокчейнах. Розглянуто синтаксис Solidity, EVM, smart contracts, gas, ABI, Remix, Hardhat, Foundry, Truffle, OpenZeppelin, ERC-20, ERC-721, ERC-1155, DeFi, NFT, events, modifiers, inheritance, security, testing, audits, переваги, обмеження і хороші практики. SEO keywords: Solidity, мова програмування Solidity, Solidity programming language, smart contracts, смарт-контракти, Ethereum, EVM, Web3, blockchain, DeFi, NFT, ERC-20, ERC-721, ERC-1155, ABI, gas, Remix IDE, Hardhat, Foundry, Truffle, OpenZeppelin, Ethereum Virtual Machine, dApp, програмування Alternative to: ручне виконання угод без смарт-контрактів; централізована бізнес-логіка для блокчейн-активів; закриті backend-правила там, де потрібна on-chain прозорість; ручне керування токенами; централізовані NFT-рішення; кастомні блокчейн-скрипти без стандартів; неаудитована логіка DeFi; традиційні контракти для частини автоматизованих Web3-сценаріїв


Solidity — це мова програмування для створення смарт-контрактів, які виконуються у Ethereum Virtual Machine або EVM. Вона найчастіше використовується в Ethereum та інших EVM-сумісних блокчейнах для створення токенів, DeFi-протоколів, NFT, DAO, on-chain логіки, dApp і Web3-застосунків.

Solidity має синтаксис, схожий на C-подібні мови, але її модель виконання суттєво відрізняється від звичайної backend-розробки: код виконується в блокчейні, операції коштують gas, дані можуть бути публічними, а помилки в контракті можуть мати фінансові наслідки.

Основна ідея: Solidity дозволяє описувати правила, які виконуються в блокчейні автоматично, прозоро й без централізованого сервера.

Загальний опис

Solidity використовується для програмування смарт-контрактів — програм, які зберігаються в блокчейні й виконуються EVM. Контракти можуть зберігати стан, приймати транзакції, викликати інші контракти, випускати токени, вести облік балансів, керувати правами доступу й автоматизувати частину бізнес-логіки.

Solidity застосовується для:

  • ERC-20 токенів;
  • NFT;
  • DeFi-протоколів;
  • DAO;
  • governance contracts;
  • staking;
  • vesting;
  • escrow;
  • marketplaces;
  • on-chain games;
  • identity і access control;
  • bridges;
  • oracles integration;
  • dApps;
  • smart contract wallets;
  • tokenized assets.

Перевага: Solidity дає можливість створювати програмовані правила для цифрових активів і взаємодій, які виконуються в блокчейні.

Смарт-контракт

Смарт-контракт — це програма, яка розгортається в блокчейні й виконується за правилами мережі. Користувачі взаємодіють із контрактом через транзакції або read-only виклики.

Смарт-контракт може:

  • зберігати дані;
  • приймати й відправляти активи;
  • перевіряти умови;
  • виконувати розрахунки;
  • викликати інші контракти;
  • створювати події;
  • керувати ролями;
  • реалізовувати токени;
  • автоматизувати частину угод.

Важливо: смарт-контракт після розгортання може бути складно або неможливо змінити. Тому помилки в логіці потрібно знаходити до deployment.

Ethereum і EVM

Ethereum — одна з головних платформ для смарт-контрактів.

EVM або Ethereum Virtual Machine — середовище виконання, у якому працює байткод смарт-контрактів.

Solidity-код компілюється в EVM bytecode, який потім розгортається в блокчейні.

Схематично:

Solidity code
  → compiler
  → EVM bytecode
  → deployment transaction
  → smart contract on blockchain

Практична роль: Solidity — це високорівнева мова, а EVM — середовище, яке фактично виконує скомпільований контракт.

EVM-сумісні мережі

Solidity використовується не лише в Ethereum, а й у багатьох EVM-сумісних мережах.

До таких мереж можуть належати:

  • Ethereum;
  • Polygon;
  • BNB Smart Chain;
  • Avalanche C-Chain;
  • Arbitrum;
  • Optimism;
  • Base;
  • Fantom;
  • Gnosis Chain;
  • інші EVM-сумісні L1 і L2 мережі.

Практична роль: один і той самий Solidity-код часто можна адаптувати для різних EVM-сумісних блокчейнів, але умови gas, інфраструктура й ризики можуть відрізнятися.

Перша програма на Solidity

Простий приклад контракту:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract HelloWorld {
    string public message = "Hello, world!";
}

У цьому прикладі:

  • `SPDX-License-Identifier` описує ліцензію;
  • `pragma solidity ^0.8.0;` задає сумісну версію компілятора;
  • `contract HelloWorld` створює контракт;
  • `string public message` зберігає текст у стані контракту;
  • `public` автоматично створює getter.

Суть прикладу: Solidity-контракт схожий на клас, але після deployment він стає on-chain програмою з власною адресою.

Pragma

`pragma` задає версію компілятора Solidity.

Приклад:

pragma solidity ^0.8.0;

Це означає, що контракт призначений для компілятора версії 0.8.x, сумісної із зазначеним діапазоном.

Важливо: версію Solidity потрібно фіксувати уважно, бо різні версії компілятора можуть мати різну поведінку, оптимізації й правила безпеки.

SPDX License Identifier

У Solidity-файлах часто додають SPDX-ідентифікатор ліцензії.

Приклад:

// SPDX-License-Identifier: MIT

Це допомагає:

  • явно вказати ліцензію;
  • спростити аудит;
  • полегшити публікацію коду;
  • зробити контракт зрозумілішим для екосистеми.

Практична роль: SPDX-коментар — невелика, але корисна частина стандартного Solidity-файлу.

Контракти

Contract у Solidity — основна одиниця коду.

Приклад:

contract Counter {
    uint256 public value;

    function increment() public {
        value += 1;
    }
}

Контракт може містити:

  • state variables;
  • functions;
  • events;
  • modifiers;
  • structs;
  • enums;
  • mappings;
  • constructor;
  • errors;
  • inheritance;
  • interfaces;
  • libraries.

Практична роль: контракт у Solidity описує on-chain стан і правила взаємодії з цим станом.

State variables

State variables — це змінні, які зберігаються в storage контракту.

Приклад:

uint256 public totalSupply;
address public owner;
bool public paused;

State variables зберігаються в блокчейні, тому їх зміна коштує gas.

Увага: запис у storage є однією з дорожчих операцій у Solidity, тому структуру стану потрібно проектувати уважно.

Типи даних

Основні типи Solidity:

  • `bool`;
  • `uint`;
  • `uint256`;
  • `int`;
  • `address`;
  • `string`;
  • `bytes`;
  • `bytes32`;
  • `enum`;
  • `struct`;
  • arrays;
  • mappings.

Приклад:

bool public active = true;
uint256 public count = 0;
address public owner;
string public name = "Token";
bytes32 public dataHash;

Практична роль: правильний вибір типів впливає на безпеку, gas-витрати й зрозумілість контракту.

uint і int

`uint` — беззнакове ціле число.

`int` — знакове ціле число.

Найчастіше використовують `uint256`.

Приклад:

uint256 public balance;
int256 public change;

У сучасних версіях Solidity переповнення й недоповнення цілих чисел перевіряються автоматично, якщо не використано `unchecked`.

Важливо: навіть із автоматичними перевірками арифметику в фінансових контрактах потрібно тестувати дуже уважно.

Address

`address` — тип для адрес Ethereum/EVM.

Приклад:

address public owner;

constructor() {
    owner = msg.sender;
}

`address payable` може отримувати native token через transfer/call.

Приклад:

address payable public treasury;

Практична роль: address використовується для користувачів, контрактів, власників, отримувачів платежів і перевірки доступу.

msg.sender

`msg.sender` — адреса, яка викликала поточну функцію.

Приклад:

function setOwner(address newOwner) public {
    require(msg.sender == owner, "Only owner");
    owner = newOwner;
}

Увага: `msg.sender` може бути не кінцевим користувачем, а іншим контрактом. Це важливо для meta-transactions, proxies і contract calls.

msg.value

`msg.value` — кількість native token, надіслана разом із викликом функції.

Приклад:

function deposit() public payable {
    require(msg.value > 0, "No value sent");
}

Функція має бути `payable`, щоб приймати native token.

Практична роль: `msg.value` використовується для платежів, deposits, minting за оплату й escrow-логіки.

Functions

Функції описують дії, які можна виконувати з контрактом.

Приклад:

function increment() public {
    value += 1;
}

function getValue() public view returns (uint256) {
    return value;
}

Функції можуть бути:

  • `public`;
  • `external`;
  • `internal`;
  • `private`;
  • `view`;
  • `pure`;
  • `payable`.

Практична роль: функції є публічним або внутрішнім API смарт-контракту.

Visibility

Visibility визначає, звідки можна викликати функцію або змінну.

Visibility Значення
public Можна викликати ззовні й усередині контракту
external Зазвичай викликається ззовні контракту
internal Доступно в цьому контракті й контрактах-нащадках
private Доступно лише в цьому контракті

Важливо: visibility не приховує дані з блокчейну. Навіть `private` змінні можуть бути прочитані з raw storage.

View і pure

`view` означає, що функція читає стан, але не змінює його.

`pure` означає, що функція не читає й не змінює стан контракту.

Приклад:

function getValue() public view returns (uint256) {
    return value;
}

function add(uint256 a, uint256 b) public pure returns (uint256) {
    return a + b;
}

Практична роль: `view` і `pure` допомагають відрізняти read-only логіку від транзакцій, які змінюють стан.

Constructor

Constructor виконується один раз під час deployment контракту.

Приклад:

contract Owned {
    address public owner;

    constructor() {
        owner = msg.sender;
    }
}

Constructor часто використовується для:

  • встановлення owner;
  • початкової конфігурації;
  • задання адрес залежностей;
  • встановлення параметрів токена;
  • ініціалізації стану.

Практична роль: constructor задає початковий стан контракту перед його використанням.

Require, revert і assert

Solidity має кілька способів зупинити виконання.

`require` використовується для перевірки умов:

require(amount > 0, "Amount must be positive");

`revert` явно скасовує виконання:

revert("Operation failed");

`assert` зазвичай використовується для внутрішніх інваріантів:

assert(total >= balance);

Важливо: перевірки в Solidity — це частина безпеки контракту. Не можна покладатися лише на frontend.

Custom errors

Custom errors дозволяють економніше описувати помилки.

Приклад:

error NotOwner();

function onlyOwnerAction() public {
    if (msg.sender != owner) {
        revert NotOwner();
    }
}

Custom errors часто дешевші за довгі рядкові повідомлення.

Практична роль: custom errors допомагають зробити помилки структурованішими й економнішими за gas.

Events

Events дозволяють контракту записувати інформацію в logs блокчейну.

Приклад:

event Transfer(address indexed from, address indexed to, uint256 amount);

function transfer(address to, uint256 amount) public {
    emit Transfer(msg.sender, to, amount);
}

Events використовуються для:

  • frontend updates;
  • indexing;
  • analytics;
  • audit trail;
  • token transfers;
  • marketplace activity;
  • protocol monitoring.

Практична роль: events — це основний спосіб повідомляти зовнішнім системам про on-chain дії контракту.

Modifiers

Modifier дозволяє додати перевірку або спільну логіку до функцій.

Приклад:

modifier onlyOwner() {
    require(msg.sender == owner, "Only owner");
    _;
}

function pause() public onlyOwner {
    paused = true;
}

`_;` означає місце, де виконується тіло функції.

Практична роль: modifiers часто використовують для access control, pause checks і повторюваних перевірок.

Mapping

Mapping — key-value структура в Solidity.

Приклад:

mapping(address => uint256) public balances;

Використання:

balances[msg.sender] += amount;

Mapping часто використовується для:

  • балансів;
  • allowlists;
  • approvals;
  • ролей;
  • ownership;
  • станів користувачів;
  • параметрів за адресою.

Увага: mapping не можна напряму перебрати як масив. Якщо потрібна ітерація, треба окремо зберігати список ключів.

Struct

Struct дозволяє створювати власні структури даних.

Приклад:

struct User {
    string name;
    uint256 balance;
    bool active;
}

mapping(address => User) public users;

Struct використовується для:

  • користувацьких профілів;
  • позицій у DeFi;
  • заявок;
  • orders;
  • proposals;
  • metadata;
  • налаштувань.

Практична роль: struct допомагає групувати пов’язані поля в одну логічну структуру.

Enum

Enum описує набір фіксованих значень.

Приклад:

enum Status {
    Pending,
    Active,
    Closed
}

Status public status;

Enums використовуються для:

  • станів;
  • етапів процесу;
  • типів заявок;
  • статусів голосування;
  • lifecycle контракту;
  • order states.

Практична роль: enum робить стани контракту зрозумілішими, ніж набір чисел або рядків.

Arrays

Solidity підтримує масиви.

Приклад dynamic array:

uint256[] public numbers;

function addNumber(uint256 value) public {
    numbers.push(value);
}

Fixed-size array:

uint256[3] public fixedNumbers;

Важливо: великі масиви в storage можуть бути дорогими для gas, особливо якщо функції проходять по всіх елементах.

Storage, memory і calldata

Solidity має різні області зберігання даних.

Область Значення
storage Постійне on-chain сховище контракту
memory Тимчасові дані під час виконання функції
calldata Read-only дані зовнішнього виклику

Приклад:

function setName(string calldata newName) external {
    name = newName;
}

Практична роль: правильне використання storage, memory і calldata впливає на gas і коректність роботи контракту.

Inheritance

Solidity підтримує inheritance.

Приклад:

contract Ownable {
    address public owner;

    constructor() {
        owner = msg.sender;
    }
}

contract MyContract is Ownable {
    function onlyOwnerData() public view returns (address) {
        return owner;
    }
}

Inheritance використовується для:

  • повторного використання логіки;
  • access control;
  • standard contracts;
  • token contracts;
  • abstract base contracts;
  • extension patterns.

Увага: inheritance у смарт-контрактах потрібно використовувати обережно, бо складна ієрархія може ускладнити аудит.

Interfaces

Interface описує зовнішній API контракту без реалізації.

Приклад:

interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

Interfaces використовуються для:

  • виклику інших контрактів;
  • стандартів;
  • інтеграцій;
  • ABI-сумісності;
  • dependency boundaries;
  • тестування.

Практична роль: interface дозволяє контракту взаємодіяти з іншим контрактом через відомий набір функцій.

Libraries

Library — повторно використовуваний код без власного звичайного стану контракту.

Приклад:

library MathUtils {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }
}

Libraries використовуються для:

  • математичних операцій;
  • helper-функцій;
  • роботи зі структурами;
  • повторного використання без inheritance;
  • gas optimization у деяких сценаріях.

Практична роль: libraries допомагають винести спільну логіку й не дублювати код між контрактами.

ABI

ABI або Application Binary Interface описує, як зовнішні системи взаємодіють із контрактом.

ABI містить інформацію про:

  • функції;
  • аргументи;
  • типи;
  • return values;
  • events;
  • errors.

Frontend, scripts і wallets використовують ABI, щоб викликати контракт.

Практична роль: ABI є мостом між смарт-контрактом і зовнішніми застосунками.

Gas

Gas — одиниця вартості обчислень в EVM.

Gas витрачається на:

  • deployment контракту;
  • запис у storage;
  • виклики функцій;
  • цикли;
  • створення контрактів;
  • зовнішні виклики;
  • логування events;
  • обчислення.

Важливо: Solidity-розробник має думати не лише про правильність коду, а й про вартість його виконання.

Transactions і calls

Взаємодія з контрактом може бути двох основних типів.

Transaction змінює стан і потребує gas.

Call читає стан і не змінює блокчейн.

Приклад read-only функції:

function balanceOf(address account) public view returns (uint256) {
    return balances[account];
}

Приклад state-changing функції:

function deposit() public payable {
    balances[msg.sender] += msg.value;
}

Практична роль: frontend має розрізняти читання даних і транзакції, які змінюють стан і потребують підтвердження користувача.

Receive і fallback

`receive` викликається, коли контракт отримує native token без calldata.

Приклад:

receive() external payable {
}

`fallback` викликається, коли функція не знайдена або calldata не відповідає ABI.

Приклад:

fallback() external payable {
}

Увага: receive і fallback потрібно проектувати обережно, бо вони можуть несподівано приймати платежі або виклики.

ERC-20

ERC-20 — стандарт fungible token у Ethereum/EVM-екосистемі.

ERC-20 токени використовуються для:

  • utility tokens;
  • governance tokens;
  • stablecoins;
  • DeFi;
  • rewards;
  • payments;
  • staking;
  • liquidity pools.

Типові функції ERC-20:

  • `totalSupply`;
  • `balanceOf`;
  • `transfer`;
  • `approve`;
  • `allowance`;
  • `transferFrom`.

Практична роль: ERC-20 зробив токени сумісними з wallets, exchanges, DeFi-протоколами й аналітичними інструментами.

ERC-721

ERC-721 — стандарт NFT, тобто non-fungible token.

ERC-721 використовується для:

  • цифрового мистецтва;
  • collectibles;
  • game assets;
  • memberships;
  • certificates;
  • identity tokens;
  • tokenized rights;
  • унікальних активів.

Практична роль: ERC-721 дозволяє створювати унікальні токени, де кожен tokenId представляє окремий актив.

ERC-1155

ERC-1155 — multi-token standard, який може підтримувати як fungible, так і non-fungible токени в одному контракті.

ERC-1155 використовується для:

  • game items;
  • collections;
  • semi-fungible tokens;
  • batch transfers;
  • NFT-платформ;
  • asset bundles.

Практична роль: ERC-1155 зручний, коли в одному проєкті потрібно керувати багатьма типами токенів.

OpenZeppelin

OpenZeppelin — популярна бібліотека перевірених смарт-контрактів і інструментів.

OpenZeppelin часто використовують для:

  • ERC-20;
  • ERC-721;
  • ERC-1155;
  • Ownable;
  • AccessControl;
  • Pausable;
  • ReentrancyGuard;
  • upgradeable contracts;
  • governance;
  • security helpers.

Практична порада: для стандартних токенів і access control краще використовувати перевірені бібліотеки, ніж писати все з нуля.

Access control

Access control визначає, хто може виконувати певні дії.

Простий приклад owner-перевірки:

modifier onlyOwner() {
    require(msg.sender == owner, "Only owner");
    _;
}

Складніші контракти можуть використовувати ролі:

  • admin;
  • minter;
  • burner;
  • pauser;
  • operator;
  • upgrader;
  • guardian.

Критично: помилка в access control може дозволити стороннім адресам керувати коштами, mint, upgrade або змінювати критичні параметри контракту.

Ownable

Ownable — поширений патерн, де контракт має власника.

Власник може мати право:

  • змінювати параметри;
  • pause/unpause;
  • встановлювати адреси;
  • керувати ролями;
  • запускати адміністративні дії.

Важливо: owner-адреса є критичною точкою довіри. Для важливих контрактів часто використовують multisig або governance замість одного приватного ключа.

Pausable

Pausable — патерн, який дозволяє тимчасово зупинити частину функцій контракту.

Використовується для:

  • emergency stop;
  • реагування на інциденти;
  • тимчасового блокування ризикових дій;
  • контрольованого запуску;
  • upgrade або migration windows.

Увага: pause-механізм корисний для безпеки, але він також додає централізований контроль, який потрібно пояснювати користувачам.

Reentrancy

Reentrancy — один із найвідоміших класів вразливостей у смарт-контрактах. Він виникає, коли зовнішній контракт може повторно увійти у функцію до завершення попереднього виконання.

Захисні підходи:

  • checks-effects-interactions;
  • ReentrancyGuard;
  • обережність із зовнішніми викликами;
  • оновлення стану до переказу коштів;
  • ретельне тестування.

Критично: зовнішні виклики в Solidity завжди потрібно розглядати як потенційно небезпечні. Спочатку перевірки й оновлення стану, потім взаємодія з іншими контрактами.

Checks-effects-interactions

Checks-effects-interactions — патерн безпечного порядку дій.

Загальна логіка:

1. Checks: перевірити умови
2. Effects: оновити стан контракту
3. Interactions: викликати зовнішні контракти або відправити кошти

Практична роль: цей патерн зменшує ризик reentrancy і робить порядок виконання зрозумілішим для аудиту.

Front-running і MEV

У публічних блокчейнах транзакції можуть бути видимі до включення в блок. Це створює ризики front-running, sandwich attacks і MEV.

Ризикові сценарії:

  • DEX trades;
  • auctions;
  • liquidations;
  • NFT minting;
  • oracle updates;
  • reward claims;
  • order matching.

Важливо: Solidity-контракт не існує в ізоляції. Потрібно враховувати mempool, порядок транзакцій і економічні стимули учасників мережі.

Oracles

Oracle передає зовнішні дані в блокчейн.

Oracles використовуються для:

  • цін активів;
  • randomness;
  • погоди;
  • спортивних результатів;
  • cross-chain data;
  • proof of reserves;
  • off-chain events.

Увага: oracle є критичною залежністю. Якщо oracle дає неправильні дані, контракт може виконати неправильну логіку.

Randomness

Генерація випадковості в блокчейні складна, бо on-chain дані часто передбачувані або можуть бути впливовими для валідаторів.

Не варто використовувати прості джерела як єдину основу випадковості для цінних призів.

Критично: випадковість для лотерей, NFT minting або ігор із цінністю має використовувати спеціальні перевірені механізми, а не прості on-chain значення.

Upgradeable contracts

Смарт-контракти зазвичай immutable, але існують upgradeable patterns через proxy.

Proxy-підхід дозволяє:

  • зберегти адресу;
  • оновити implementation;
  • виправити помилки;
  • додати функції;
  • розділити storage і logic.

Але він додає складність:

  • storage layout;
  • admin access;
  • upgrade risks;
  • initializer замість constructor;
  • audit complexity;
  • довіру до upgrader.

Важливо: upgradeability дає гнучкість, але зменшує простоту й може створити додаткові ризики централізованого контролю.

Proxy

Proxy — контракт, який приймає виклики й делегує їх implementation-контракту.

Поширені підходи:

  • transparent proxy;
  • UUPS proxy;
  • beacon proxy;
  • minimal proxy clones.

Практична роль: proxy використовується, коли потрібно оновлювати логіку контракту без зміни адреси, з якою взаємодіють користувачі.

Remix IDE

Remix IDE — браузерне середовище для написання, компіляції, тестування й розгортання Solidity-контрактів.

Remix корисний для:

  • навчання;
  • швидких експериментів;
  • прототипів;
  • перевірки синтаксису;
  • простого deployment;
  • взаємодії з контрактами;
  • debugging у навчальних сценаріях.

Практична роль: Remix — зручний стартовий інструмент для вивчення Solidity й швидкої перевірки контрактів.

Hardhat

Hardhat — популярне середовище розробки для Ethereum/Solidity.

Hardhat використовується для:

  • компіляції;
  • тестування;
  • local blockchain;
  • deployment scripts;
  • debugging;
  • plugins;
  • TypeScript/JavaScript tooling;
  • integration tests;
  • mainnet forking.

Практична роль: Hardhat добре підходить для командної Solidity-розробки з тестами, deployment scripts і JavaScript/TypeScript-екосистемою.

Foundry

Foundry — швидкий toolkit для Solidity-розробки.

Foundry зазвичай включає:

  • Forge для тестування;
  • Cast для взаємодії з мережами;
  • Anvil для локального node;
  • Solidity-based tests;
  • fuzz testing;
  • scripting.

Практична роль: Foundry популярний серед Solidity-розробників, які хочуть писати тести й scripts безпосередньо в Solidity.

Truffle

Truffle — один із ранніх framework-ів для Ethereum-розробки.

Він використовувався для:

  • компіляції;
  • migrations;
  • deployment;
  • тестування;
  • contract artifacts;
  • JavaScript-based workflow.

Історична роль: Truffle був важливим інструментом ранньої Solidity-екосистеми, хоча в нових проєктах часто обирають Hardhat або Foundry.

Testing

Тестування Solidity-контрактів є критично важливим.

Потрібно тестувати:

  • access control;
  • arithmetic;
  • edge cases;
  • revert conditions;
  • events;
  • token transfers;
  • upgrade behavior;
  • permissions;
  • paused states;
  • external calls;
  • oracle scenarios;
  • attacks simulation;
  • gas usage.

Критично: у смарт-контрактах тестування — не формальність. Помилка може призвести до втрати коштів або блокування активів.

Fuzz testing

Fuzz testing перевіряє контракт на багатьох згенерованих вхідних даних.

Fuzz testing корисний для:

  • арифметики;
  • invariants;
  • edge cases;
  • DeFi-логіки;
  • access rules;
  • unexpected inputs;
  • state transitions.

Практична роль: fuzz testing допомагає знаходити помилки, які складно передбачити через ручні test cases.

Invariant testing

Invariant testing перевіряє властивості, які мають залишатися істинними завжди.

Приклади інваріантів:

  • сума балансів не перевищує total supply;
  • користувач не може зняти більше, ніж має;
  • paused контракт не виконує заборонені дії;
  • резерви не стають від’ємними;
  • governance rules не порушуються.

Практична роль: invariant testing особливо корисний для DeFi, токенів і складних протоколів.

Static analysis

Static analysis допомагає знайти потенційні проблеми без виконання контракту.

Інструменти можуть виявляти:

  • reentrancy risks;
  • unchecked calls;
  • access control issues;
  • shadowing;
  • dangerous patterns;
  • unused variables;
  • gas inefficiencies;
  • suspicious external calls.

Важливо: static analysis корисний, але не замінює тести, review, аудит і розуміння бізнес-логіки.

Audit

Smart contract audit — це незалежна перевірка коду, архітектури, безпеки й економічної логіки контракту.

Аудит зазвичай перевіряє:

  • access control;
  • reentrancy;
  • oracle assumptions;
  • token logic;
  • upgradeability;
  • storage layout;
  • arithmetic;
  • edge cases;
  • governance;
  • admin powers;
  • economic attacks;
  • integration risks.

Критично: аудит не гарантує повної безпеки, але для важливих контрактів без аудиту запускати production-логіку з коштами небезпечно.

DeFi

DeFi або decentralized finance — одна з головних сфер використання Solidity.

Solidity використовується для:

  • DEX;
  • lending;
  • borrowing;
  • liquidity pools;
  • staking;
  • yield farming;
  • derivatives;
  • vaults;
  • stablecoins;
  • liquidations;
  • governance tokens.

Важливо: DeFi-контракти мають не лише технічні, а й економічні ризики: liquidity, oracle manipulation, MEV, governance attacks і systemic dependencies.

NFT

NFT — non-fungible token, тобто унікальний токен.

Solidity використовується для NFT-контрактів, які можуть містити:

  • ownership;
  • minting;
  • transfers;
  • approvals;
  • metadata;
  • royalties;
  • allowlists;
  • reveal logic;
  • marketplace integration.

Практична роль: Solidity дозволяє створювати NFT-колекції, marketplaces і on-chain правила володіння цифровими активами.

DAO

DAO або decentralized autonomous organization використовує смарт-контракти для governance.

DAO-контракти можуть реалізовувати:

  • proposals;
  • voting;
  • quorum;
  • timelocks;
  • token-based governance;
  • treasury management;
  • execution rules;
  • roles;
  • delegates.

Практична роль: Solidity може автоматизувати правила голосування й виконання рішень у decentralized governance.

dApp

dApp — decentralized application, який зазвичай складається з:

  • frontend;
  • wallet connection;
  • smart contracts;
  • ABI;
  • RPC provider;
  • indexing service;
  • off-chain backend у деяких випадках;
  • storage для metadata;
  • monitoring.

Практична роль: Solidity відповідає за on-chain логіку dApp, але повний застосунок зазвичай має ще frontend, індексацію й off-chain інфраструктуру.

Wallets

Користувачі взаємодіють із Solidity-контрактами через wallets.

Wallet може:

  • підписувати транзакції;
  • показувати дані контракту;
  • взаємодіяти з dApp;
  • керувати accounts;
  • підтверджувати gas;
  • підписувати повідомлення;
  • підключатися до мереж.

Увага: контракт має бути безпечним навіть тоді, коли frontend або wallet показує користувачу неповну інформацію.

Indexing

Дані блокчейну часто індексують окремі сервіси.

Індексація потрібна для:

  • швидкого пошуку;
  • історії подій;
  • dashboards;
  • аналітики;
  • NFT metadata;
  • user portfolios;
  • marketplace listings;
  • protocol metrics.

Events контракту часто є основою для індексації.

Практична роль: smart contract зберігає on-chain стан, а індексатор робить ці дані зручними для frontend і аналітики.

Solidity і JavaScript

Solidity часто використовується разом із JavaScript або TypeScript.

Критерій Solidity JavaScript / TypeScript
Основна роль On-chain smart contracts Frontend, scripts, tests, deployment
Середовище EVM Browser, Node.js
Дані On-chain state Off-chain UI, integration, tooling
Вартість виконання Gas Залежить від runtime
Типові інструменти solc, Hardhat, Foundry ethers.js, viem, web3.js, scripts

Висновок: Solidity описує on-chain правила, а JavaScript/TypeScript часто керує frontend, тестами й deployment workflow.

Solidity і Vyper

Vyper — інша мова для EVM-смарт-контрактів.

Критерій Solidity Vyper
Синтаксис C/JavaScript-подібний Python-подібний
Популярність Найпоширеніша EVM-мова Нішевіша
Можливості Багато feature, inheritance, libraries Більш обмежений дизайн
Фокус Гнучкість і екосистема Простота й auditability

Висновок: Solidity має більшу екосистему, а Vyper робить ставку на простіший і більш обмежений дизайн для частини сценаріїв.

Solidity і Rust

Rust використовується в інших блокчейн-екосистемах і деяких smart contract платформах.

Критерій Solidity Rust
Основна ніша EVM smart contracts Systems programming, Solana, WASM smart contracts, backend
Типізація Статична Статична з ownership model
Runtime EVM Native/WASM/інший runtime залежно від платформи
Екосистема Ethereum/EVM Ширша systems і blockchain екосистема
Складність Спеціалізована для EVM Складніша, але універсальніша

Висновок: Solidity — головний вибір для EVM, а Rust часто використовується там, де потрібна інша blockchain runtime або системна продуктивність.

Solidity і Python

Python часто використовується для scripts, testing, analytics і blockchain automation, але не є основною мовою EVM-контрактів.

Критерій Solidity Python
Основна роль Смарт-контракти Скрипти, аналітика, backend, testing tools
Виконання EVM Python runtime
Вартість операцій Gas Звичайні обчислення off-chain
Безпека On-chain ризики й незворотність Залежить від застосунку

Висновок: Python може бути корисним навколо Web3-проєкту, але on-chain логіка EVM зазвичай пишеться на Solidity.

Переваги Solidity

Основні переваги Solidity:

  • головна мова Ethereum/EVM;
  • велика екосистема;
  • підтримка ERC-стандартів;
  • OpenZeppelin;
  • сумісність із wallets і dApps;
  • багато tooling;
  • Hardhat;
  • Foundry;
  • Remix;
  • широка спільнота;
  • можливість створювати токени;
  • DeFi і NFT ecosystem;
  • ABI-сумісність;
  • підтримка багатьох EVM-мереж.

Головна перевага: Solidity є стандартним інструментом для створення смарт-контрактів у найбільшій EVM-екосистемі.

Обмеження Solidity

Solidity має обмеження.

Можливі проблеми:

  • високі security-ризики;
  • gas-вартість;
  • складність upgradeability;
  • публічність даних;
  • immutable deployment;
  • складність тестування економічних сценаріїв;
  • залежність від oracle;
  • MEV і front-running;
  • складність DeFi-інтеграцій;
  • потреба в аудиті;
  • складність роботи з великими даними;
  • обмеження EVM;
  • ризик втрати коштів через помилки.

Помилка: писати Solidity як звичайний backend. Смарт-контракт виконується в іншому середовищі, де помилки дорожчі, дані публічні, а зміни часто незворотні.

Коли варто використовувати Solidity

Solidity варто використовувати для:

  • Ethereum smart contracts;
  • EVM-сумісних мереж;
  • ERC-20 токенів;
  • NFT;
  • DeFi-протоколів;
  • DAO;
  • staking;
  • vesting;
  • escrow;
  • on-chain governance;
  • tokenized assets;
  • dApps;
  • smart contract wallets;
  • blockchain-based automation.

Практична порада: Solidity доречна тоді, коли логіка справді має виконуватися on-chain, а не просто бути частиною звичайного backend.

Коли Solidity може бути невдалим вибором

Solidity може бути не найкращим вибором для:

  • звичайних web-застосунків без blockchain-потреби;
  • приватних бізнес-даних;
  • великих обсягів даних;
  • частих обчислень із високою вартістю gas;
  • задач, які краще виконувати off-chain;
  • систем, де потрібна повна конфіденційність;
  • проєктів без бюджету на тестування й аудит;
  • логіки, яку потрібно часто змінювати.

Важливо: не всю бізнес-логіку потрібно переносити в блокчейн. On-chain має бути лише те, що справді потребує прозорості, довіри, токенізації або децентралізованого виконання.

Безпека Solidity

Безпека є центральною темою Solidity-розробки.

Потрібно контролювати:

  • reentrancy;
  • access control;
  • integer logic;
  • oracle manipulation;
  • front-running;
  • MEV;
  • upgradeability risks;
  • storage collision;
  • unchecked external calls;
  • signature replay;
  • authorization bugs;
  • emergency pause;
  • governance attacks;
  • dependency risks;
  • economic exploits.

Критично: Solidity-контракт із коштами потрібно проектувати як фінансову систему: з threat model, тестами, аудитом, monitoring і планом реагування.

Приватність даних

Дані в публічному блокчейні зазвичай доступні для перегляду.

Навіть якщо змінна позначена як `private`, її значення може бути прочитане з storage.

Не варто зберігати on-chain:

  • паролі;
  • приватні ключі;
  • секретні токени;
  • персональні дані без потреби;
  • комерційні секрети;
  • незашифровану приватну інформацію;
  • приховані random seed;
  • конфіденційні документи.

Критично: `private` у Solidity означає обмеження доступу з інших контрактів, але не конфіденційність даних у блокчейні.

Хороші практики Solidity

Рекомендовано:

  • використовувати перевірені бібліотеки;
  • фіксувати версію компілятора;
  • писати тести;
  • використовувати fuzz і invariant testing;
  • перевіряти access control;
  • мінімізувати storage writes;
  • уникати зайвих циклів по великих масивах;
  • використовувати events;
  • документувати припущення;
  • перевіряти зовнішні виклики;
  • не зберігати секрети on-chain;
  • застосовувати checks-effects-interactions;
  • використовувати ReentrancyGuard там, де потрібно;
  • проводити аудит для важливих контрактів;
  • робити deployment checklist.

Головне правило: хороший Solidity-код має бути простим, протестованим, аудитованим і написаним із розумінням on-chain ризиків.

Типові помилки початківців

Поширені помилки:

  • думати, що `private` приховує дані;
  • не тестувати edge cases;
  • не перевіряти access control;
  • писати власний ERC-20 з нуля без потреби;
  • ігнорувати reentrancy;
  • робити великі цикли по storage;
  • не враховувати gas;
  • покладатися лише на frontend-перевірки;
  • неправильно використовувати `tx.origin`;
  • не розуміти proxy storage layout;
  • не перевіряти return values;
  • довіряти oracle без перевірки;
  • не робити аудит важливого контракту;
  • зберігати secrets у контракті.

Небезпека: помилка в Solidity може бути не просто bug, а прямий фінансовий ризик для користувачів і протоколу.

Приклади задач на Solidity

Простий лічильник

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Counter {
    uint256 public value;

    function increment() public {
        value += 1;
    }

    function decrement() public {
        require(value > 0, "Value is zero");
        value -= 1;
    }
}

Контракт із власником

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract OwnableExample {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner");
        _;
    }

    function changeOwner(address newOwner) public onlyOwner {
        require(newOwner != address(0), "Zero address");
        owner = newOwner;
    }
}

Подія

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract EventExample {
    event MessageChanged(address indexed user, string message);

    string public message;

    function setMessage(string calldata newMessage) external {
        message = newMessage;
        emit MessageChanged(msg.sender, newMessage);
    }
}

Mapping балансів

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract BalanceBook {
    mapping(address => uint256) public balances;

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

    function getBalance(address account) external view returns (uint256) {
        return balances[account];
    }
}

Struct і enum

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Orders {
    enum Status {
        Created,
        Paid,
        Cancelled
    }

    struct Order {
        address buyer;
        uint256 amount;
        Status status;
    }

    mapping(uint256 => Order) public orders;
    uint256 public nextOrderId;

    function createOrder(uint256 amount) external {
        orders[nextOrderId] = Order({
            buyer: msg.sender,
            amount: amount,
            status: Status.Created
        });

        nextOrderId += 1;
    }
}

Підказка: у Solidity-прикладах важливо дивитися не лише на синтаксис, а й на access control, storage, gas, external calls і можливі edge cases.

Джерела

  • Офіційна документація Solidity.
  • Ethereum Developer Documentation.
  • Ethereum Yellow Paper.
  • OpenZeppelin Contracts Documentation.
  • Hardhat Documentation.
  • Foundry Book.
  • Remix IDE Documentation.
  • Ethers.js Documentation.
  • Viem Documentation.
  • ERC Standards.
  • ConsenSys Smart Contract Best Practices.
  • Матеріали щодо EVM, gas, ABI, DeFi security, NFT standards, proxy patterns і smart contract audits.

Висновок

Solidity — це головна мова програмування для створення смарт-контрактів у Ethereum та EVM-сумісних мережах. Вона використовується для токенів, NFT, DeFi, DAO, staking, escrow, governance, dApps і різних on-chain механізмів.

Solidity має велику екосистему, стандарти ERC, бібліотеки OpenZeppelin, інструменти Hardhat, Foundry, Remix і широку підтримку в Web3-інфраструктурі. Водночас вона потребує високої дисципліни: помилки в контрактах можуть бути незворотними, дані зазвичай публічні, операції коштують gas, а безпека залежить від тестів, review, аудитів і правильної архітектури.

Головна думка: Solidity — це мова для on-chain правил і цифрових активів. Її сила в програмованій довірі, але відповідальність розробника значно вища, ніж у звичайній backend-розробці.

Див. також

Тематичні мітки