Высокие комиссии в сети делают оптимизацию смарт-контрактов не просто “хорошей практикой”, а прямым способом снизить стоимость транзакций для пользователей и нагрузку на протокол. Ниже — 7 техник, которые реально помогают сократить gas без потери безопасности и читаемости кода.
1. Минимизируйте запись в storage
Запись в `storage` — одна из самых дорогих операций в EVM.
- По возможности используйте `memory` и `calldata`
- Не храните данные, которые можно вычислить на лету
- Удаляйте ненужные переменные, если это оправдано логикой
Главный принцип: меньше `SSTORE` — дешевле контракт.
2. Используйте `calldata` вместо `memory` для внешних функций
Если функция принимает массивы, строки или структуры и не меняет их, `calldata` почти всегда дешевле.
Пример:
`function foo(uint[] calldata arr) external`
вместо
`function foo(uint[] memory arr) external`
Это снижает лишнее копирование данных в память ⚙️
3. Упаковывайте переменные в storage slots
Solidity хранит данные по 32 байта. Если правильно подобрать типы, несколько переменных можно разместить в одном слоте.
- `uint128` + `uint128`
- `uint64` + `uint64` + `uint128`
Но важно соблюдать порядок объявления. Неправильная компоновка может свести экономию к нулю.
4. Избегайте лишних циклов и unbounded loop
Циклы быстро делают функции дорогими, особенно если количество элементов растет со временем.
- переносить тяжелые вычисления off-chain
- разбивать операции на батчи
- использовать mappings вместо массивов там, где не нужен перебор
Это особенно критично для DeFi и NFT-контрактов 📉
5. Кэшируйте значения в локальные переменные
Если вы несколько раз обращаетесь к одной и той же переменной из `storage`, сохраните её в локальную переменную в начале функции.
Это уменьшает число чтений из хранилища (`SLOAD`) и делает код чуть эффективнее. Небольшая оптимизация, но на частых вызовах даёт заметный эффект.
6. Выбирайте правильный уровень visibility и типы функций
- `external` для функций, которые вызываются извне, часто дешевле `public`
- `view` и `pure` не снижают gas внутри транзакции напрямую, но помогают архитектурно выносить вычисления из state-changing логики
- внутренние функции иногда лучше инлайнить, если это оправдано оптимизацией компилятора
Также стоит проверять, не создаёте ли вы лишние копии данных через `public` интерфейсы 🧠
7. Используйте custom errors вместо длинных require-строк
Начиная с Solidity 0.8.4, `custom errors` обычно выгоднее по gas, чем длинные текстовые сообщения в `require`.
Вместо:
`require(x > 0, "Value must be greater than zero");`
лучше:
`error InvalidValue();`
Это особенно полезно в больших контрактах с множеством проверок.
Что важно помнить
Gas-оптимизация — это баланс. Не стоит жертвовать безопасностью и поддерживаемостью ради микровыигрыша в несколько сотен gas. Лучший подход:
- сначала безопасная архитектура
- затем профилирование
- потом точечная оптимизация самых дорогих участков кода 🔍
Для проверки используйте gas reports в Foundry или Hardhat и сравнивайте изменения до и после каждой правки.
📌 Ниже в подборке — каналы про криптовалюты, блокчейн и разработку, где регулярно разбирают смарт-контракты, DeFi и практические кейсы по оптимизации.