Разработка смарт-контрактов на Solidity

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка смарт-контрактов на Solidity
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка смарт-контрактов на Solidity

Клиент приносит контракт на аудит — 800 строк Solidity, деплой на Ethereum mainnet через неделю. На третьей странице кода обнаруживается паттерн: внешний вызов до обновления состояния, классическая reentrancy. Не теоретическая — такая же конфигурация была у The DAO в 2016-м, 60 миллионов долларов. Контракт уходит на переработку. Это стандартная ситуация, когда Solidity-разработка идёт без системного подхода к безопасности.

Где чаще всего ломается Solidity-контракт

Reentrancy — всё ещё живёт в продакшне

Несмотря на то что атака известна с 2016 года, варианты reentrancy продолжают появляться. Проблема не в незнании паттерна — большинство разработчиков знают про Checks-Effects-Interactions. Проблема в cross-function reentrancy, которую ReentrancyGuard из OpenZeppelin не покрывает по умолчанию.

Сценарий: контракт A вызывает контракт B через низкоуровневый call. B — это токен, реализующий ERC-777 с хуком tokensReceived. В момент хука у A уже списаны токены, но ETH ещё не отправлен. Функция вывода в A не заблокирована reentrancy-гардом, потому что разработчик считал, что защитил только withdraw. Итог — дренаж резервов.

Решение: nonReentrant на все публичные функции, которые меняют состояние и делают внешние вызовы. Для сложных систем — отдельный ReentrancyGuardUpgradeable с проверкой на уровне модуля, а не функции.

Storage collision в proxy-паттернах

При использовании Transparent Proxy или UUPS переменные хранятся в storage слотах по позиции объявления. Если в новой версии имплементации добавить переменную перед существующей — весь storage сдвинется. address public owner превращается в мусор, который раньше был uint256 public totalSupply.

Это не гипотетика: несколько протоколов в 2022-2023 годах обнаруживали проблему после апгрейда, когда маппинги начинали возвращать неверные значения. Спасает ERC-7201 (namespaced storage) — переменные имплементации хранятся в заранее выбранном слоте через keccak256-хэш, изолированно от proxy-переменных.

Gas griefing через unbounded loops

Функция, которая итерирует по address[] public users без ограничений, безопасна при 50 пользователях и превращается в DoS-вектор при 5000. Транзакция упирается в block gas limit и реверсируется. Если эта функция критична для протокола — griefing атакующему обходится дёшево, протоколу дорого.

Паттерн решения: pagination через offset/limit или pull-паттерн вместо push (пользователь сам забирает награды, а не контракт рассылает всем).

Как мы пишем Solidity-контракты

Стек и инструменты

Основной инструмент разработки — Foundry. Причина не в моде, а в конкретных возможностях: fuzz-тестирование прямо в тестах через vm.fuzz, fork-тесты на реальном состоянии mainnet через vm.createFork, и скорость компиляции в 4-5 раз выше Hardhat на больших проектах.

Hardhat остаётся в стеке для задач, где важна экосистема плагинов: hardhat-deploy для воспроизводимых деплоев, hardhat-gas-reporter для отчётов по газу в CI, интеграция с TypeChain.

Базовые контракты — OpenZeppelin 5.x. Не форкаем, не модифицируем внутренности. Если нужно расширение поведения — наследование и override с явным super._call().

Статический анализ: Slither на каждый PR, Mythril для символьного выполнения перед деплоем. Для fuzzing сложной логики — Echidna с property-based тестами.

Паттерны, которые используем

Diamond Pattern (EIP-2535) — для систем, где количество функций превышает лимит байткода одного контракта (24 KB). Facet-архитектура позволяет добавлять функциональность без нарушения storage. Используем редко — только там, где действительно нужно, из-за сложности аудита.

Pull payment pattern — ETH никогда не отправляется напрямую из функции протокола. Балансы накапливаются в маппинге, пользователь вызывает withdraw(). Это убирает целый класс reentrancy-векторов и устраняет проблемы с контрактами-получателями, которые реверсируют receive().

Multicall — батчинг транзакций через ERC-2771 или собственную реализацию. Снижает количество on-chain вызовов, особенно критично при высоком газе на mainnet.

Оптимизация газа

Типичные места, где газ уходит впустую:

Паттерн Проблема Решение Экономия
bool переменная отдельно Занимает полный slot (32 байта) Упаковка в struct со смежными типами 15-20k gas на деплой
storage read в loop Каждый SLOAD = 100 gas (EIP-2929) Кэш в memory-переменную перед циклом До 80% на loop
emit Event без индексации Невозможно фильтровать через The Graph indexed на ключевые поля Нет экономии газа, но критично для DX
string в storage Дорого и неэффективно bytes32 для фиксированных строк 3-5x экономия

Переупорядочение переменных под slot packing — первое, что делаем при аудите газа. Контракт с uint128 a; uint256 b; uint128 c; занимает 3 slot. Переставить в uint128 a; uint128 c; uint256 b; — 2 slot. На деплое разница 20-40k gas, на каждом SLOAD в горячих путях — ощутимо.

Процесс работы

Аналитика (1-3 дня). Разбираем архитектуру: какие роли, какие права, какие инварианты система должна соблюдать всегда. Инварианты — основа для property-based тестов в Echidna.

Проектирование (2-5 дней). Диаграмма контрактов, storage layout, интерфейсы. На этом этапе решаем вопрос апгрейдаемости: UUPS, Transparent, или immutable. Для DeFi-протоколов с ценностью >1M USD апгрейдаемость — не всегда преимущество с точки зрения доверия.

Разработка. Контракты + тесты в Foundry. Покрытие >95% по строкам, fuzz-тесты на все публичные функции с числовыми параметрами. Fork-тесты на Ethereum/Polygon mainnet для интеграций с Uniswap, Aave, Chainlink.

Внутренний аудит. Slither, Mythril, ручной review с чеклистом SWC (Smart Contract Weakness Classification). Не заменяет внешний аудит, но закрывает low/medium severity до его начала.

Деплой. Скрипты через Foundry forge script с верификацией на Etherscan/Polygonscan автоматически. Деплой сначала на testnet (Sepolia, Mumbai), затем mainnet с мультисиг через Gnosis Safe.

Ориентиры по срокам

Простой токен ERC-20 с базовыми функциями — 3-5 дней включая тесты. Стейкинг-контракт с наградами и временными локами — 1-2 недели. Полноценный DeFi-протокол с AMM-логикой или лендингом — от 6 недель. Сроки зависят от сложности логики и требований к покрытию тестами.

Стоимость рассчитывается после анализа технического задания.