Ethereum для новичков: как работает сеть, транзакции и почему есть комиссии
12-12-2025, 21:58
Авторизуйтесь или зарегистрируйтесь, чтобы оценивать материалы, создавать записи и писать комментарии.
Авторизоваться© 2026 24k.ru. Все материалы носят исключительно информационный характер и не являются индивидуальной инвестиционной рекомендацией (ФЗ-39 «О рынке ценных бумаг»). Криптовалюты не являются законным средством платежа в РФ (ФЗ-259). Используя сайт, вы соглашаетесь с нашей Политикой конфиденциальности и использованием cookie.
Storage layout в Solidity — это правило, по которому компилятор раскладывает переменные контракта по 32-байтным storage-слотам Ethereum. Если вы используете прокси-архитектуру (апгрейдируемые контракты), storage layout — главный источник катастроф: вы обновляете логику (implementation), но хранилище остаётся прежним. Если новая версия ожидает “другую раскладку”, вы получаете storage collision — ситуация, когда одна переменная начинает читать/писать в слот другой переменной. Итог может быть любым: от “сломались балансы” до “пользователь стал админом”.
Связанные страницы: storage layout (детально), памятка/чек-лист storage layout, риски DELEGATECALL и прокси.
Коротко: в апгрейдируемых контрактах нельзя “просто добавить/удалить/переупорядочить переменные”. Апгрейд безопасен, если новая версия совместима со старой раскладкой storage, а новые переменные добавляются так, чтобы не занять уже используемые слоты.
Storage в EVM — это “постоянная память” контракта. Она адресуется слотами по 32 байта. Простая интуиция:
В прокси-паттернах (Transparent/UUPS и т.п.) прокси хранит состояние в своём storage и делегирует выполнение кода в implementation через DELEGATECALL. Это значит:
Если новая версия изменила порядок/типы/наследование так, что переменные “съехали” — код начнёт интерпретировать старые данные как новые переменные. Это и есть причина, почему апгрейды “убиваются” layout-ошибками.
Ниже — не “юридические” правила, а практический опыт того, что ломает апгрейды чаще всего.
Главный принцип: апгрейдируемый контракт — это “вечный формат данных”. Логика может меняться, но формат storage должен быть совместимым, как формат таблицы в базе данных.
Solidity умеет упаковывать переменные меньших типов в один слот, если они стоят рядом. Пример: несколько uint128 или bool/uint8 подряд могут попасть в один слот.
Это создаёт тонкую ловушку апгрейдов:
Поэтому правило “добавлять только в конец” должно применяться с учётом упаковки: добавление в конец безопаснее, чем добавление между мелкими типами, но всё равно нужно проверять layout-компиляцией.
Динамические структуры (mapping, dynamic array, bytes/string) используют хэш-слоты: “база” лежит в одном слоте, а элементы — по вычисляемым адресам (через keccak256). Это делает их устойчивее к “сдвигу” внутренних элементов при апгрейдах, но есть нюансы:
То есть “stable” — не значит “можно всё”. Это значит, что у структуры есть предсказуемая адресация, но совместимость формата данных всё равно критична.
В прокси-паттернах есть специальные слоты (часто по EIP-1967) для адреса implementation и админа. Они выбираются так, чтобы не пересекаться со “слотами переменных” вашей логики.
Практический вывод:
В upgradeable контрактах часто встречается “storage gap” — зарезервированный массив, например:
uint256[50] private __gap;
Идея простая: вы заранее оставляете “свободные слоты”, чтобы в будущих версиях можно было добавлять переменные в пределах этого запаса, не меняя layout наследования и не рискуя сдвигом в базовых контрактах.
Это особенно актуально, когда у вас сложное наследование и вы хотите безопасно добавлять поля на разных уровнях иерархии.
Важно: gap — это не “магия безопасности”, а дисциплина версионирования. Он помогает, но не отменяет проверки layout для каждой версии.
| Что сделали в новой версии | Почему это опасно | Чем заканчивается |
|---|---|---|
| Вставили новую переменную в середину | сдвиг всех последующих слотов | слом учёта балансов/прав/лимитов |
| Переупорядочили переменные “для красоты” | layout поменялся, данные читаются не так | тихая коррапция state |
| Сменили тип uint256 → uint128 | упаковка и интерпретация данных меняются | обнуления, странные значения, переполнения |
| Поменяли порядок базовых контрактов | слоты базовых полей пересобрались | массовый сдвиг layout |
| Добавили bool между мелкими типами | packing перераспределил биты | битовые “призраки” в переменных |
| Забыли про initializer | в прокси нет конструктора, нужна init-логика | перехват owner/roles |
Потому что в прокси-паттерне пользователи взаимодействуют с прокси-адресом, а логика исполняется через DELEGATECALL. Конструктор implementation выполняется только при деплое implementation, но не “инициализирует” storage прокси. Поэтому используются initializer-функции.
Переименование в коде само по себе не меняет storage layout — важны порядок, тип и место в наследовании. Но переименование часто идёт вместе с рефакторингом, который уже может поменять порядок или тип — поэтому это нужно проверять диффом layout.
Добавить новые state-переменные в конец и не трогать существующие. Это базовый паттерн совместимости, но всё равно требуются проверки layout и тесты апгрейда.
Потому что Solidity формирует layout с учётом наследования. Если вы меняете порядок базовых контрактов или добавляете новый базовый контракт “раньше”, это может сдвинуть слоты полей, объявленных в базовых классах.
Gap помогает резервировать слоты для будущих переменных, особенно в базовых upgradeable контрактах и при сложном наследовании. Теоретически можно без gap, но тогда любое расширение базовых контрактов требует ещё большей дисциплины и чаще приводит к несовместимым изменениям.
Часто это проявляется странными значениями ролей/адресов/балансов, невозможностью выполнить операции, неожиданными revert. Но самый опасный случай — “тихая коррапция”, когда всё работает, но данные постепенно становятся неверными. Поэтому важно иметь инварианты и тесты апгрейда.
Материал носит исключительно информационный характер и не является индивидуальной инвестиционной рекомендацией (ФЗ-39). Криптовалюты не являются законным средством платежа в РФ (ФЗ-259).
12-12-2025, 21:58
13-12-2025, 02:11
13-12-2025, 02:44
13-12-2025, 02:00
15-12-2025, 19:48
Анна Коваль
Комментариев нет