Русский
English
RU
Сетка BlockFirst
Картинка BlockFirst
Ссылка
Иконка копировать
Иконка скопированно

рекомендации по Solidity для экономии газа

В этой статье подробно рассматриваются различные техники и рекомендации для разработки смарт-контрактов на языке Solidity, которые помогают снизить расход газа при выполнении транзакций и уменьшить общий размер байт-кода контрактов. Авторы объясняют, как грамотно писать код, чтобы повысить его эффективность.

10.06.2025
Перевод
Начинающий уровень
Автор BlockFirst
Поделиться
Иконка поделиться
Ссылка
Иконка копировать
Иконка скопировано
ARTICLE BELOW
Картинка
Превью BlockFirst

function setBoolean(
uint256 _packedBools,
uint256 _boolNumber,
bool _value
) public view returns(uint256) {
if (_value)
return _packedBools | uint256(1) << _boolNumber;
else
return _packedBools & ~(uint256(1) << _boolNumber);
}

require(balance >= amount);
//This check is redundant because the safemath subtract function used below already includes this check.balance = balance.sub(amount);

require(balance >= amount, "Insufficient balance"); //good
require(balance >= amount, "To whomsoever it may concern. I am writing this error message to let you know that the amount you are trying to transfer is unfortunately more than your current balance. Perhaps you made a typo or you are just trying to be a hacker boi. In any case, this transaction is going to revert. Please try again with a lower amount. Warm regards, EVM"; //bad

uint256 hello = 0; //bad, expensive
uint256 world; //good, cheap

(hello, world) = (world, hello)

Превью BlockFirst

Большинство общих принципов хорошего программирования и оптимизации применимы и к Solidity, но есть и свои особенности, как те, что были описаны выше, которые делают оптимизацию Solidity-кода более сложной (но интересной). По мере работы с Solidity вы будете узнавать всё больше трюков. Тем не менее, сколько бы трюков вы ни использовали, при создании сложного кода вы всё равно можете столкнуться с ограничением размера байткода в 24 КБ. Вы можете разделять свои контракты на несколько, используя прокси или другие приёмы, но лимит всё равно остаётся проблемой. Если вы хотите, чтобы это ограничение убрали, оставьте свой отзыв на этой странице GitHub Issue.

Заключительные мысли

Если нужно развернуть несколько копий одного и того же контракта, рассмотрите возможность развертывания одного основного контракта с реализацией и нескольких прокси-контрактов, которые делегируют свою логику основному контракту. Это позволит контрактам использовать одинаковую логику, но хранить разные данные.

Использование прокси-паттернов для массового развертывания

Внутри смарт-контракта вызов внутренних (internal) функций дешевле, чем вызов публичных. При вызове публичной функции все параметры копируются в память и передаются функции заново. При вызове внутренней функции передаются ссылки на параметры, без повторного копирования в память. Это экономит газ, особенно если параметры большие.

Вызов внутренних функций дешевле

Обычно хорошей практикой считается использовать небольшие отдельные функции, выполняющие одну задачу. Однако в Solidity использование множества маленьких функций стоит дороже по газу и требует больше байткода. Сложные большие функции могут усложнять тестирование и аудит, поэтому я не советую использовать их повсеместно, но если вы хотите максимально оптимизировать свои контракты, это может помочь.

Использование меньшего количества функций может быть полезным

Помимо возможности включать и выключать оптимизатор, компилятор Solidity (solc) позволяет настраивать количество проходов оптимизатора с помощью параметра runs. Этот параметр не означает, сколько раз оптимизатор будет работать, а указывает, сколько раз вы ожидаете вызывать функции в этом смарт-контракте. Если контракт предназначен для одноразового использования (например, для вестинга или блокировки токенов), можно установить runs в 1 — компилятор создаст минимально возможный байткод, но вызовы функций могут стоить немного дороже по газу. Если же контракт будет использоваться часто (например, токен ERC20), рекомендуется задать большое значение runs, например, 1337 — байткод получится немного больше, но вызовы функций, таких как transfer, будут дешевле.

Правильно используйте оптимизатор

Хранение данных через события значительно дешевле, чем через переменные. Однако данные из событий нельзя использовать внутри самого блокчейна. Кроме того, ведётся работа по удалению старых событий, поэтому в будущем, возможно, придётся запускать собственные узлы для доступа к данным из старых событий. Использовать события таким образом можно считать не совсем этичным, но кто я такой, чтобы судить. Я никому не скажу, если вы тоже — :)

Используйте события для хранения данных, которые не нужны на блокчейне

Solidity предоставляет достаточно редкую возможность обмена значениями переменных в одной строке. Используйте это вместо временных переменных, XOR-операций или арифметических функций для обмена значениями. Ниже приведён пример, показывающий, как поменять значения переменных:

Используйте однострочные обмены значениями

Нет необходимости проверять одно и то же условие снова и снова в разных вариантах. Самые частые избыточные проверки связаны с использованием библиотеки SafeMath. Эта библиотека сама проверяет переполнения и недополнения, поэтому вам не нужно делать эти проверки вручную.

Избегайте повторяющихся проверок

Мне удалось уменьшить размер одного из моих контрактов с 23.95 КБ до 11.9 КБ с помощью этого трюка. Вы можете посмотреть простой «магический» коммит здесь. Обратите внимание на контракт DataStore.sol.

Внутренние функции (internal), напротив, не встраиваются, а вызываются как отдельные функции. Это означает, что их выполнение чуть дороже во время работы, но они значительно уменьшают избыточный байткод при развертывании. Внутренние функции также помогают избежать страшной ошибки «Stack too deep», поскольку переменные, созданные во внутренней функции, не используют ограниченный стек оригинальной функции, в то время как переменные, созданные в модификаторах, разделяют этот же стек.

Когда вы вызываете публичную функцию из библиотеки, байткод этой функции не включается в ваш контракт. Это позволяет помещать сложную логику в библиотеки и при этом сохранять размер контракта небольшим. Учтите, что вызовы библиотек требуют газа и требуют некоторого байткода. Вызовы библиотек осуществляются через delegatecall, что означает, что библиотеки имеют доступ к тем же данным и тем же правам, что и контракт. Поэтому использование библиотек нецелесообразно для простых задач. Ещё важно помнить, что компилятор Solidity (solc) встраивает (inline) внутренние функции библиотек. Встраивание имеет свои преимущества, но занимает место в байткоде.троки функции acceptableRoot.

Используйте библиотеки для экономии байткода

Используйте короткие строки с причинами ошибок
Вы можете (и должны) добавлять строки с объяснением ошибок вместе с операторами require, чтобы было проще понять, почему вызов контракта откатился. Однако такие строки занимают место в байткоде после развертывания. Каждая строка причины занимает минимум 32 байта, поэтому убедитесь, что ваша строка умещается в 32 байта, иначе это станет дороже.

Используйте короткие строки с причинами ошибок

Если переменная не установлена или не инициализирована, ей автоматически присваивается значение по умолчанию (0, false, 0x0 и т.д. в зависимости от типа данных). Если вы явно инициализируете переменную значением по умолчанию, вы просто тратите газ зря.

Нет необходимости инициализировать переменные значениями по умолчанию

С помощью этой техники вы можете хранить 256 булевых значений в одном слоте хранилища. Если же упаковывать булевы значения обычным способом (например, в структуре), то в одном слоте поместится максимум 32 булевых значения. Используйте этот метод только в тех случаях, когда нужно хранить больше 32 булевых значений.

Чтобы установить или сбросить булево значение, используйте:

В Solidity булевы типы (bool) на самом деле реализованы как uint8, то есть занимают 8 бит памяти. Булевое значение может принимать только два значения: true или false. Значит, для хранения булева значения достаточно всего одного бита. Можно упаковать 256 булевых значений в одно слово (256 бит). Самый простой способ — взять переменную типа uint256 и использовать все её 256 бит для представления отдельных булевых значений. Чтобы получить отдельное булево значение из uint256, используется следующая функция:

Булевы значения занимают 8 бит, хотя нужно всего 1 бит

Когда вы добавляете модификатор функции, код этой функции вставляется внутрь модификатора на место символа _. Это можно понимать как «встраивание» (inline) модификаторов функций. В обычных языках программирования встраивание небольшого кода обычно более эффективно и не имеет серьёзных недостатков, но Solidity — это не обычный язык. В Solidity максимальный размер контракта ограничен 24 КБ согласно EIP 170. Если один и тот же код встраивается многократно, это увеличивает общий размер, и лимит может быть легко превышен.

Модификаторы функций могут быть неэффективными

Solidity — это особый язык с множеством мелких особенностей. Многие вещи в Solidity работают иначе, чем в большинстве других языков, поскольку Solidity создан для работы на EVM с её ограниченным набором функций. Несколько месяцев назад я написал пост в блоге с десятью советами по экономии газа в Solidity, и он получил отличный отклик. С тех пор я собрал ещё больше советов и трюков, которыми хочу поделиться с вами. Вот они:

function getBoolean(uint256 _packedBools, uint256 _boolNumber)
public view returns(bool)
{
uint256 flag = (_packedBools >> _boolNumber) & uint256(1);
return (flag == 1 ? true : false);
}

(Перевод)
Виталий Дорожко
Автор
Поделиться
Иконка поделиться
Ссылка
Иконка копировать
Иконка скопированно
Назад в блог
Кнопка назад
Оригинал статьи
кнопка вперед
Поделиться
Иконка поделиться
Ссылка
Иконка копировать
Иконка скопировано
Назад в блог
Кнопка назад
Оригинал статьи
кнопка вперед
сетка BlockFirst
сетка BlockFirst
сетка BlockFirst

Для запросов от пользователей

hello@blockfirst.io

Icon mail

Для бизнес запросов

business@blockfirst.io

Icon mail

Телеграм для быстрых ответов

Icon mail

компания

Сообщество

медиа

Подписываясь на рассылку, вы можете быть уверены, что мы не будем спамить Вам :)

Новости. Скидки. Анонсы

В начало
© 2025-2026 BlockFirst. Все права защищены.
Сетка BlockFirst
hello@blockfirst.io
Для коммерческих предложений
Компания
Телеграм для быстрых ответов
Кнопка копировать
Скопировано