Уявіть: ваш смарт-контракт у продакшні, в ньому сотні тисяч USDT ліквідності. У коді знайдено вразливість — reentrancy. Потрібно оновити логіку, але адресу контракту міняти не можна — користувачі та інтеграції прив'язані до неї. Єдиний вихід — proxy-паттерн upgradeable контрактів. Ми реалізували такі рішення для 30+ проектів на Ethereum, Polygon та Arbitrum. Наш досвід гарантує, що ви уникнете типових помилок — storage collision, неправильної ініціалізації та втрати права апгрейду.
Чому storage collision — головна загроза proxy?
Класичний proxy працює через DELEGATECALL: proxy-контракт викликає implementation, але виконує код в контексті сховища proxy. Storage в EVM — це масив із 2²⁵⁶ слотів по 32 байти. Якщо proxy зберігає адресу implementation у слоті 0, а implementation зберігає, наприклад, owner у слоті 0, виникає storage collision: owner в implementation перезаписує адресу implementation в proxy. Атакуючий, здатний модифікувати owner в implementation, отримує контроль над proxy.
EIP-1967 вирішує це радикально: зберігає адресу implementation у псевдовипадковому слоті, обчисленому як keccak256("eip1967.proxy.implementation") - 1. Ймовірність колізії з користувацькими змінними implementation — астрономічно мала. OpenZeppelin ERC1967Proxy реалізує саме цей стандарт.
Який паттерн проксі обрати для вашого проекту?
Transparent Proxy (TUP). Класика OpenZeppelin. Два типи викликаючих: admin (керує upgrade) та користувачі (викликають логіку). Admin не може викликати функції implementation — тільки апгрейдити. Overhead на кожен виклик — одне додаткове читання storage для перевірки msg.sender.
UUPS (EIP-1822). Логіка апгрейду перенесена в сам implementation-контракт. Proxy став тоншим — менше gas на виклик. Але тут критична пастка: якщо задеплоїти новий implementation без функції upgradeTo, контракт назавжди втратить можливість апгрейду. OpenZeppelin UUPSUpgradeable додає перевірку _authorizeUpgrade — це єдиний захист. UUPS дозволяє економити до 30% газу порівняно з Transparent, що може скоротити витрати на тисячі доларів щомісяця.
Beacon Proxy. Один beacon-контракт зберігає адресу implementation. Безліч proxy-контрактів дивляться в цей beacon. Один апгрейд beacon'а — оновлюються всі proxy одночасно. Ідеально для фабрик (factory pattern), де потрібно створювати багато однакових контрактів (наприклад, пули в AMM). Beacon Proxy перевершує UUPS у гнучкості для фабрик більш ніж у 2 рази при масовому деплої.
| Паттерн | Gas на виклик | Гнучкість | Ризики |
|---|---|---|---|
| Transparent | +2100 gas (SLOAD) | Висока | Storage collision при неправильному layout |
| UUPS | Мінімальний | Висока | Втрата upgradability при помилці |
| Beacon | Середній | Максимальна для фабрик | Одна точка відмови (beacon) |
Для масштабних проектів з високим транзакційним навантаженням економія на газі при використанні UUPS замість Transparent може досягати $5,000 на місяць, що робить цей паттерн оптимальним для високонавантажених DeFi-протоколів.
Чому ініціалізація замість конструктора критична?
constructor() в Solidity виконується один раз при деплої. При proxy-паттерні implementation деплоїться окремо — його конструктор виконується в контексті implementation, а не proxy. Всі змінні, встановлені в конструкторі, залишаються в implementation і недоступні через proxy.
Рішення: замінити конструктор на функцію initialize() з модифікатором initializer з OpenZeppelin. Вона викликається один раз через proxy і записує дані в storage proxy.
Типова помилка — забути викликати _disableInitializers() в конструкторі implementation. Без цього атакуючий може викликати initialize() напряму на implementation (не через proxy) і стати його owner. Це не впливає на proxy напряму, але відкриває вектори для атаки через DELEGATECALL.
| Підхід | Виконання контексту | Безпека | Використання |
|---|---|---|---|
| constructor | Implementation | Низька (недоступний через proxy) | Тільки для immutable-змінних |
| initialize | Proxy | Висока (модифікатор initializer) | Upgradeable контракти |
Як ми це робимо: стек та інструменти
Ми використовуємо сучасний стек: Foundry для розробки та тестування, OpenZeppelin Upgrades Plugin для перевірки storage layout, OpenZeppelin Upgrades для безпечної реалізації. Версії Solidity 0.8.x, підтримка всіх L2 (Arbitrum, Optimism, Base).
Для розгортання використовуємо мультисиг Gnosis Safe. Жодних приватних ключів у скриптах. Всі апгрейди проходять через TimelockController із затримкою 3 дні.
Що входить в роботу
- Аудит поточного storage layout та виявлення ризиків.
- Вибір оптимального паттерну (Transparent/UUPS/Beacon) з обґрунтуванням.
- Реалізація контракту з initialize() та тестами (Foundry/Hardhat).
- Оцінка потенційної економії газу (до 30%, що еквівалентно $5,000 на місяць для середнього проекту).
- Підготовка скриптів деплою через Safe Transaction Builder.
- Верифікація контрактів на Etherscan.
- Документація з апгрейду та навчання команди.
- 2-тижнева підтримка після розгортання.
Процес роботи
- Аналіз. Вивчаємо поточний контракт (або вимоги до нового), storage layout, бажані функції.
- Проектування. Обираємо паттерн, проектуємо структуру storage з урахуванням можливих майбутніх змін.
- Реалізація. Пишемо код з initialize(), тести на форку mainnet.
- Тестування. Проводимо газ-профілювання, перевіряємо відсутність storage collision через OpenZeppelin Upgrades Plugin.
- Деплой. Розгортаємо implementation та proxy через мультисиг. Викликаємо initialize().
- Підтримка. Після деплою надаємо скрипти для апгрейду та моніторинг.
Чек-лист перед деплоєм upgradeable-контракту
-
_disableInitializers()викликаний в конструкторі implementation -
initialize()захищений модифікаторомinitializer - Storage layout перевірено через OpenZeppelin Upgrades Plugin (validate)
- ProxyAdmin owner — мультисиг, не EOA
- Timelock налаштовано для production
- Новий implementation верифіковано на Etherscan до передачі права апгрейду
- Тест: форк mainnet, апгрейд, перевірка storage
Терміни та контакти
Реалізація proxy-паттерну для нового контракту — 2-3 робочі дні. Міграція існуючого непроксі-контракту на upgradeable-архітектуру (зі збереженням даних через migration script) — від 3 до 7 днів залежно від складності storage. Вартість розраховується індивідуально — зв'яжіться з нами для оцінки вашого проекту; ми запропонуємо оптимальне рішення під ключ. Економія на газі при виборі UUPS може досягати 30%, що в перерахунку на популярні контракти становить до $5,000 на місяць. Отримайте консультацію щодо вашого storage layout та вибору паттерну.
Proxy pattern — концептуальна основа.







