Практический курс по разработке смарт-контрактов на Ethereum. Вы изучите язык Solidity, напишете ERC-20 токен и NFT с нуля, освоите паттерны безопасности и архитектуру DeFi-протоколов. Курс построен от простого к сложному — каждая тема опирается на предыдущую и включает реальный код, готовый к деплою.
Смарт-контракт нельзя починить патчем. Код опубликован, на нём лежат деньги, а ошибка становится чужой прибылью — и найдёт её не пользователь, а тот, кто целенаправленно ищет. Поэтому Solidity изучают не так, как обычный язык: безопасность здесь не заключительная глава для полноты, она определяет, как пишется каждая функция.
Двенадцать тем от простого к сложному. Сначала фундамент: как устроен блокчейн, что такое EVM, чем внешний аккаунт отличается от контракта, откуда берётся газ и почему за него платит пользователь. Затем сам язык: первый контракт, pragma, ABI и байткод, типы данных и — отдельно — области хранения storage, memory и calldata, потому что от выбора зависят и поведение, и стоимость вызова.
Дальше функции и модификаторы, payable, fallback и receive; события с индексируемыми параметрами как единственный способ отдать данные наружу дёшево; require, assert, revert и пользовательские ошибки; наследование и интерфейсы.
Практическая часть — два стандарта, которые пишутся с нуля, а потом сравниваются с реализацией OpenZeppelin: ERC-20 с механикой allowance и transferFrom и ERC-721 с метаданными и безопасной передачей, плюс ERC-1155.
Затем безопасность как отдельная тема: повторный вход, паттерн Checks-Effects-Interactions, tx.origin, переполнение и что изменилось в 0.8, опережающие транзакции. И архитектура: устройство AMM с формулой x*y=k, пулы ликвидности, стейкинг, оракулы цен, flash loans. Завершают курс оптимизация газа — упаковка переменных, кэширование storage, работа с calldata — и апгрейдируемые контракты: proxy, Transparent против UUPS, коллизии слотов хранения.
Нужен опыт программирования на любом языке. Знакомство с блокчейном не требуется.
Как работает блокчейн, EVM, типы аккаунтов, транзакции и газ — фундамент для понимания смарт-контрактов
Первый смарт-контракт, синтаксис Solidity, SPDX, pragma, структура контракта, ABI и байткод
Значимые и ссылочные типы, storage/memory/calldata, struct, mapping — как данные живут в EVM
Видимость, pure/view/payable, модификаторы доступа, constructor, fallback и receive
Events, indexed-параметры, require/assert/revert, custom errors Solidity 0.8+
Наследование, множественное наследование, интерфейсы, абстрактные контракты, virtual/override
Стандарт ERC-20, реализация с нуля, allowance/transferFrom, mint/burn, OpenZeppelin
Не взаимозаменяемые токены, tokenId, tokenURI, метаданные, safeTransferFrom, ERC-1155
Reentrancy, CEI-паттерн, tx.origin, integer overflow, front-running, ReentrancyGuard
AMM и формула x*y=k, пулы ликвидности, стейкинг-контракты, оракулы цен, flash loans
Packing переменных, calldata vs memory, кэш storage-переменных, эффективные циклы, optimizer
Proxy-паттерн, Transparent vs UUPS, столкновения слотов хранения, Initializable, OpenZeppelin Upgrades
Доступен после всех тем (0 из 12)
Доступен после зачёта
Детерминированная виртуальная машина, выполняющая байткод смарт-контрактов. Детерминированность — ключевое свойство: одинаковый код с одинаковыми входными данными всегда даёт одинаковый результат на любом узле сети, что позволяет прийти к консенсусу.
Пример
Когда вы вызываете функцию контракта, EVM выполняет байткод по одному опкоду, снимая gas за каждую инструкцию.Связанные термины
Единица измерения вычислительной работы в EVM. Каждый опкод стоит определённое число gas. Итоговая стоимость транзакции в ETH = gasUsed × (baseFee + priorityFee). gasLimit — максимально разрешённый газ для транзакции.
Пример
SSTORE (запись в storage) стоит 20,000 gas при первой записи. При газ-цене 10 gwei это 0.0002 ETH за одну запись.Связанные термины
Аккаунт Ethereum, управляемый приватным ключом (кошелёк пользователя). Не содержит кода. Только EOA может инициировать транзакции. Адрес = последние 20 байт keccak256(publicKey).
Пример
Ваш MetaMask кошелёк — это EOA. Адрес вроде 0xAbCd... принадлежит EOA.Связанные термины
Аккаунт Ethereum, созданный при деплое смарт-контракта. Содержит байткод и постоянное хранилище (storage). Не имеет приватного ключа — управляется только своим кодом. Не может самостоятельно инициировать транзакции.
Пример
Адрес токена USDC — это контрактный аккаунт. Его код исполняется при каждом вызове transfer().Связанные термины
Монотонно возрастающий счётчик транзакций аккаунта (начинается с 0). Каждая новая транзакция должна иметь nonce = предыдущий + 1. Защищает от replay-атак и определяет порядок выполнения транзакций.
Пример
Если отправить две транзакции с nonce=5 — только одна будет принята. Вторая отклонится как дубликат.Связанные термины
Минимальная единица ETH (1 ETH = 10^18 Wei). Все вычисления с ETH внутри EVM ведутся в Wei. Gwei (10^9 Wei) используется для указания gas price.
Пример
msg.value возвращает значение в Wei. 0.1 ETH = 100,000,000,000,000,000 Wei = 10^17 Wei.Связанные термины
JSON-описание публичного интерфейса смарт-контракта: имена функций, типы параметров и возвращаемых значений, события. Используется фронтендом и библиотеками (ethers.js) для правильного кодирования вызовов.
Пример
ethers.js использует ABI для превращения вызова `contract.transfer(addr, amount)` в правильно закодированные байты транзакции.Связанные термины
Скомпилированный код смарт-контракта в виде последовательности опкодов EVM. Записывается в блокчейн при деплое. Не читается человеком напрямую (для этого используется дизассемблер или Decompiler).
Пример
solc компилирует Counter.sol в байткод вроде 0x6080604052... Именно этот байткод хранится на адресе контракта.Связанные термины
Первые 4 байта keccak256-хэша сигнатуры функции. EVM использует selector для определения, какую функцию вызвать. Передаётся в начале calldata транзакции.
Пример
Selector для transfer(address,uint256): keccak256('transfer(address,uint256)')[:4] = 0xa9059cbb. Именно эти байты начинают каждую ERC-20 transfer транзакцию.Связанные термины
Многоразовый декоратор для функций в Solidity. Добавляет проверки или логику до и/или после тела функции через символ `_;`. Компилятор inline вставляет код модификатора в каждую функцию.
Пример
modifier onlyOwner() { require(msg.sender == owner); _; } — классический паттерн для ограничения доступа к административным функциям.Связанные термины
Три области данных в Solidity. Storage — постоянное хранилище в блокчейне (дорого, SLOAD/SSTORE). Memory — временная память на время вызова функции (дешевле). Calldata — read-only входные данные транзакции (самое дешёвое, только для external функций).
Пример
function process(uint256[] calldata data) external — calldata дешевле memory для read-only массивов. uint256[] memory temp = array — копирует из storage в memory.Связанные термины
Стандарт взаимозаменяемых (fungible) токенов Ethereum. Определяет обязательный интерфейс: balanceOf, transfer, approve, transferFrom, allowance, totalSupply + события Transfer и Approval.
Пример
USDC, DAI, UNI, LINK — всё это ERC-20 токены. Любой DeFi-протокол работает с ними единообразно через интерфейс IERC20.Связанные термины
Стандарт невзаимозаменяемых (non-fungible) токенов. Каждый токен идентифицируется уникальным tokenId. Функции: ownerOf, safeTransferFrom, approve, setApprovalForAll, tokenURI.
Пример
CryptoPunks, Bored Ape Yacht Club, ENS доменные имена — всё это ERC-721 NFT. tokenURI возвращает ссылку на JSON с метаданными и изображением.Связанные термины
Сколько токенов определённый адрес (spender) может потратить от имени владельца (owner). Хранится как mapping(owner => mapping(spender => amount)). Устанавливается через approve(), уменьшается при transferFrom().
Пример
approve(uniswap, 1000e18) разрешает Uniswap списывать до 1000 ваших токенов. Затем Uniswap вызывает transferFrom(you, pool, amount) в рамках разрешённой суммы.Связанные термины
Функция ERC-721, возвращающая URI к JSON-файлу с метаданными токена (name, description, image, attributes). URI может быть IPFS (рекомендуется) или HTTP. Маркетплейсы читают tokenURI для отображения NFT.
Пример
tokenURI(42) возвращает 'ipfs://QmXxx.../42.json'. JSON содержит: {name: 'Ape #42', image: 'ipfs://QmYyy.../42.png', attributes: [...]}Связанные термины
Уязвимость, при которой контракт-жертва вызывает внешний адрес (отправляет ETH) до обновления своего состояния. Злоумышленник в receive() повторно вызывает уязвимую функцию — баланс ещё не обнулён. Убила DAO в 2016 ($60M).
Пример
withdraw() отправляет ETH через call{}, затем обнуляет баланс. Злоумышленник в receive() снова вызывает withdraw() — получает двойной вывод.Связанные термины
Паттерн безопасного порядка операций: сначала проверки (require), затем изменение состояния (effects), только потом внешние вызовы (interactions). Основная защита от reentrancy-атак.
Пример
require(balance >= amount); balance -= amount; msg.sender.call{value: amount}('') — CEI порядок. Если поменять местами effects и interactions — reentrancy уязвимость.Связанные термины
Атака, при которой злоумышленник видит pending транзакцию в mempool, копирует её данные и отправляет с более высоким gasPrice — его транзакция исполняется первой. Типична для лотерей, DEX-арбитража, NFT-минтов.
Пример
Пользователь угадал ответ в лотерее и отправил транзакцию. Бот-MEV увидел ответ в mempool, скопировал и отправил с gasPrice на 1 gwei выше. Бот выиграл.Связанные термины
Механизм ограничения вызова функций только авторизованными адресами. OpenZeppelin предоставляет Ownable (один владелец) и AccessControl (ролевая модель с MINTER_ROLE, PAUSER_ROLE и т.д.).
Пример
bytes32 MINTER_ROLE = keccak256('MINTER_ROLE'); modifier onlyRole(MINTER_ROLE) — только адреса с ролью MINTER могут вызывать mint().Связанные термины
Алгоритмический маркет-мейкер — тип DEX, где цена определяется математической формулой (x*y=k) на основе резервов пула ликвидности, а не книгой ордеров. Пример: Uniswap, Curve, Balancer.
Пример
Uniswap v2 использует x*y=k. Пул с 100 ETH и 200,000 USDC: покупка 10 ETH вычисляется как (100+10)*y2=20M, даёт ~18,182 USDC по цене ~1818 USDC/ETH.Связанные термины
Смарт-контракт с резервами двух (или более) токенов, предоставленными LP (Liquidity Providers). LP вносят оба токена и получают LP-токены как долю в пуле. Доход LP — комиссии свопов минус impermanent loss.
Пример
Пул USDC/ETH на Uniswap содержит $10M USDC и 5000 ETH. LP с долей 1% получают 1% всех торговых комиссий (0.3% от каждого свопа).Связанные термины
Убыток LP по сравнению с простым хранением тех же токенов. Возникает при дивергенции цен токенов в пуле: AMM ребалансирует через арбитраж, LP получает больше дешевеющего и меньше дорожающего актива.
Пример
При росте ETH с $1000 до $4000: LP в пуле ETH/USDC имеет 0.5 ETH + 2000 USDC = $4000, тогда как HODLer имеет 1 ETH + 1000 USDC = $5000. IL = $1000.Связанные термины
Беззалоговый займ, который должен быть возвращён в той же транзакции. Если к концу транзакции займ не возвращён — вся транзакция откатывается. Используется для арбитража, рефинансирования позиций и атак.
Пример
Aave flash loan: занять $10M USDC → выполнить арбитраж → вернуть $10M USDC + 0.09% комиссия. Если арбитраж не сработал — вся транзакция revert.Связанные термины
Система для передачи внешних данных (цены активов, погода, спортивные результаты) в смарт-контракт. EVM детерминирована и не может самостоятельно обращаться к внешнему миру. Chainlink — крупнейшая сеть оракулов.
Пример
Chainlink Price Feed: контракт вызывает AggregatorV3Interface.latestRoundData() и получает актуальную цену ETH/USD, агрегированную из десятков источников.Связанные термины
Архитектурный паттерн для апгрейдируемых контрактов: proxy хранит данные в своём storage и перенаправляет все вызовы через delegatecall в implementation. Implementation содержит логику и может быть заменена без изменения адреса proxy.
Пример
USDC на Ethereum — апгрейдируемый контракт через Transparent Proxy. Circle может обновить логику токена, не изменяя его адрес и балансы пользователей.Связанные термины
Низкоуровневый вызов, при котором код вызываемого контракта выполняется в контексте вызывающего: msg.sender и address(this) остаются от вызывающего, storage читается из слотов вызывающего. Основа proxy-паттерна.
Пример
proxy.delegatecall(implementation, data): код implementation исполняется, но читает и пишет storage proxy. Поэтому данные хранятся в proxy, а логика — в implementation.Связанные термины
Базовый контракт OpenZeppelin для апгрейдируемых контрактов. Предоставляет модификатор initializer, гарантирующий единственный вызов функции initialize() — аналога constructor для proxy-контрактов.
Пример
contract MyToken is ERC20Upgradeable, OwnableUpgradeable { function initialize(address owner) public initializer { __Ownable_init(owner); } }Связанные термины
Контракт OpenZeppelin с модификатором nonReentrant. Хранит bool-флаг в storage: устанавливает locked=true перед выполнением функции и сбрасывает после. Повторный вызов видит флаг и откатывается.
Пример
contract SafeBank is ReentrancyGuard { function withdraw() external nonReentrant { ... } } — повторный вызов из receive() откатится с ReentrancyGuard: reentrant call.Связанные термины
Библиотека проверенных смарт-контрактов для Ethereum. Содержит реализации ERC-20, ERC-721, ERC-1155, AccessControl, Ownable, ReentrancyGuard, Pausable, Upgrades. Прошла многократный аудит безопасности.
Пример
import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; — вместо написания ERC-20 с нуля используют проверенную реализацию OpenZeppelin.Связанные термины
Состав курса, уровни, практика и способы проверки знаний.
Курс включает 12 тем и 88 вопросов с разбором ответа. Начать можно с первой темы курса.
Маршрут охватывает уровни Middle, Senior. Темы расположены от основы к более сложным инженерным задачам, поэтому можно начать с подходящего места и не пропускать важные зависимости.
После прохождения тем доступен зачёт по курсу «Смарт-контракты на Solidity: от основ до DeFi» — 20 случайных вопросов с порогом 80%. После зачёта открывается экзамен с развёрнутыми ответами и автоматической оценкой, приближённый к техническому собеседованию.
Да, курс полностью бесплатный: все 12 тем доступны без оплаты.