Шпаргалка по Solidity
Структура контракта
Каждый файл начинается с лицензии и pragma; контракт - единица деплоя.
| Синтаксис | Значение |
|---|---|
// SPDX-License-Identifier: MIT | Комментарий с лицензией, который компилятор ожидает в строке 1 |
pragma solidity ^0.8.0; | Версия компилятора, для которой написан файл |
contract Counter { ... } | Объявить контракт (состояние + функции) |
constructor(uint start) { count = start; } | Выполняется один раз, при деплое |
import "./Token.sol"; | Импортировать другой файл |
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | Импортировать пакет библиотеки |
interface IToken { function balanceOf(address a) external view returns (uint); } | Объявить интерфейс (без тел функций) |
library Math { function min(uint a, uint b) internal pure returns (uint) { ... } } | Библиотека переиспользуемых функций |
Типы значений
У целых чисел фиксированный размер и нет дробной части; 0.8 откатывает транзакцию при переполнении.
| Тип | Что хранит |
|---|---|
uint256 / uint | Целое без знака, от 0 до 2^256 - 1 (uint - это uint256) |
uint8, uint16, ... uint128 | Целые без знака меньшего размера, с шагом 8 бит |
int256 / int | Целое со знаком (допускаются отрицательные) |
bool | true или false |
address | 20-байтовый адрес аккаунта или контракта |
address payable | Адрес, который может получать Ether через transfer/send |
bytes32, bytes1... | Байтовые массивы фиксированного размера |
enum Status { Open, Closed } | Именованный набор констант, хранится как uint8 |
1 ether, 1 gwei, 1 wei | Единицы Ether: 1 ether = 10^18 wei |
1 days, 2 hours, 30 minutes | Единицы времени, в секундах |
Ссылочные типы и расположение данных
Массивы, строки, bytes, struct и mapping живут в storage, memory или calldata.
| Синтаксис | Значение |
|---|---|
string memory name | Динамическая строка UTF-8 (временная копия) |
bytes memory data | Динамический байтовый массив |
uint[] public scores; | Динамический массив в storage |
uint[3] fixed; | Массив фиксированного размера из 3 элементов |
uint[] memory tmp = new uint[](5); | Выделить массив в memory |
scores.push(42); | Добавить элемент в конец массива в storage |
scores.pop(); | Удалить последний элемент |
scores.length | Количество элементов |
storage | Постоянное, on-chain, дорогое для записи |
memory | Временное, живёт в течение одного вызова |
calldata | Входные данные внешней функции только для чтения, самый дешёвый вариант |
Mapping и struct
Mapping - хеш-таблица без длины и без итерации; struct группирует поля.
| Синтаксис | Значение |
|---|---|
mapping(address => uint) public balances; | Хранилище ключ-значение, любой ключ существует (по умолчанию 0) |
balances[msg.sender] += 1; | Чтение/запись по ключу |
mapping(address => mapping(address => uint)) allowance; | Вложенный mapping |
struct User { string name; uint age; bool active; } | Определить struct |
User memory u = User("Ada", 36, true); | Создать struct в memory |
User memory u = User({name: "Ada", age: 36, active: true}); | Создание с именованными полями |
users[msg.sender] = u; | Сохранить struct в mapping |
users[msg.sender].age = 37; | Обновить одно поле в storage |
delete users[msg.sender]; | Сбросить к значениям по умолчанию |
Функции и видимость
Каждая функция указывает, кто может её вызвать и читает ли она состояние или пишет в него.
| Синтаксис | Значение |
|---|---|
function add(uint a, uint b) public pure returns (uint) { return a + b; } | Функция с параметрами и возвращаемым значением |
public | Вызывается откуда угодно (внутри и снаружи) |
external | Вызывается только извне контракта |
internal | Этот контракт и контракты, которые его наследуют |
private | Только этот контракт |
view | Читает состояние, никогда не пишет |
pure | Вообще не читает состояние |
payable | Может получать Ether вместе с вызовом |
returns (uint sum, bool ok) | Несколько именованных возвращаемых значений |
(uint s, bool ok) = f(); | Деструктурировать несколько возвращаемых значений |
uint public count; | Публичная переменная состояния получает автоматический геттер count() |
Модификаторы, константы и наследование
Переиспользуйте проверки через модификаторы; фиксируйте значения через constant и immutable.
| Синтаксис | Значение |
|---|---|
modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } | Определить модификатор (_ = выполнить тело функции) |
function withdraw() public onlyOwner { ... } | Применить модификатор |
uint public constant MAX = 100; | Константа времени компиляции |
address public immutable owner; | Задаётся один раз в конструкторе, затем не меняется |
contract Token is ERC20, Ownable { ... } | Наследовать от других контрактов |
function f() public virtual { ... } | Разрешить переопределение |
function f() public override { ... } | Переопределить функцию родителя |
super.f(); | Вызвать реализацию родителя |
abstract contract Base { function f() public virtual; } | Контракт с нереализованными функциями |
Управление потоком
Обычные конструкции семейства C; нет switch, а циклы стоят газа на каждой итерации.
| Синтаксис | Значение |
|---|---|
if (x > 5) { ... } else if (x > 2) { ... } else { ... } | Условные ветви |
for (uint i = 0; i < n; i++) { ... } | Цикл со счётчиком |
while (x < 10) { x++; } | Цикл, пока выполняется условие |
do { ... } while (cond); | Выполняется хотя бы один раз |
break; / continue; | Выйти из цикла / перейти к следующей итерации |
x > 0 ? a : b | Тернарное выражение |
a / b | Целочисленное деление (отбрасывает остаток) |
a % b | Остаток |
a ** 2 | Возведение в степень |
unchecked { x++; } | Пропустить проверки переполнения (экономит газ, использовать осторожно) |
Ошибки: require, revert, assert
Неудавшаяся проверка откатывает всю транзакцию и возвращает неиспользованный газ.
| Синтаксис | Значение |
|---|---|
require(amount > 0, "Amount must be positive"); | Проверить ввод или состояние; откатить с сообщением |
revert("Not allowed"); | Прервать безусловно |
error Insufficient(uint available, uint requested); | Объявить пользовательскую ошибку (дешевле строк) |
revert Insufficient(balance, amount); | Откатить с пользовательской ошибкой |
assert(total == a + b); | Проверить инвариант, который никогда не должен нарушаться |
try token.transfer(to, amt) returns (bool ok) { ... } catch { ... } | Обработать неудавшийся внешний вызов |
События
События записывают логи, на которые могут подписываться off-chain приложения; из контрактов они не читаются.
| Синтаксис | Значение |
|---|---|
event Transfer(address indexed from, address indexed to, uint value); | Объявить событие |
emit Transfer(msg.sender, to, amount); | Сгенерировать его |
indexed | Фильтруемый параметр (до 3 на событие) |
event Log(string message); | Логировать можно любой тип ABI |
Ether, адреса и глобальные переменные
Контекст транзакции берётся из msg, block и tx.
| Синтаксис | Значение |
|---|---|
msg.sender | Адрес, вызвавший эту функцию |
msg.value | Wei, отправленные с вызовом (нужен payable) |
block.timestamp | Время текущего блока (секунды с начала эпохи) |
block.number | Высота текущего блока |
tx.origin | Внешний аккаунт (EOA), инициировавший транзакцию (не используйте для авторизации) |
address(this).balance | Баланс Ether этого контракта |
payable(to).transfer(1 ether); | Отправить Ether, откат при неудаче |
(bool ok, ) = to.call{value: amt}(""); | Низкоуровневая отправка, возвращает флаг успеха |
receive() external payable {} | Выполняется при простом переводе Ether |
fallback() external payable {} | Выполняется, когда ни одна функция не подошла |
keccak256(abi.encodePacked(a, b)) | Хешировать значения |
abi.encode(x), abi.decode(data, (uint)) | Кодировать / декодировать данные ABI |
Весь синтаксис Solidity, который вам нужен, на одной странице. Эта шпаргалка по Solidity - краткий справочник по языку смарт-контрактов Ethereum и всех EVM-сетей: как объявить контракт, выбрать типы, хранить данные в mapping и struct, писать функции с правильной видимостью и изменяемостью и защищать их с помощью require, модификаторов и событий.
Здесь используется синтаксис Solidity 0.8, который по умолчанию проверяет арифметическое переполнение и работает с Remix, Hardhat и Foundry. Копируйте нужное или попробуйте вживую в плейграунде Solidity - напишите контракт, скомпилируйте его и запустите на EVM в браузере.