В этой статье вы найдете несколько советов по сокращению потребления газа в ваших смарт-контрактах.
Очень важно понимать, как это сделать, потому что, если выполнение функции в вашем смарт-контракте будет стоить слишком много денег, меньшее количество пользователей захотят запустить ваше Dapp.
Важно знать, что некоторая информация была взята из нескольких ресурсов, которые будут упомянуты в конце.
Совет №1. Упакуйте свои переменные
Solidity хранит данные в 256-битных слотах памяти. Переменные размером менее 256 бит будут храниться в одном слоте. Данные, которые не помещаются в один слот, распределяются по нескольким слотам.
Каждый слот для хранения стоит газа, упаковка переменных поможет вам оптимизировать использование газа за счет сокращения количество слотов, необходимых для нашего контракта.
Пример:
Компилятор Solidity может помещать 2 uint128
в одни и те же 256-битные слоты памяти, только если они объявлены рядом друг с другом. В противном случае каждая uint128
переменная занимает отдельный uint256
только для себя, тратя 128 бит на каждый слот (как и в первом контракте):
Совет № 2: Включите оптимизатор твердости
Когда вы компилируете смарт-контракты Solidity, вы можете указать флаг оптимизации, чтобы сообщить компилятору Solidity о необходимости создания высокооптимизированного байт-кода. Этот байт-код потребляет меньше газа, чем если бы вы не использовали флаг оптимизации.
Вы можете включить оптимизацию с помощью этого параметра в вашем truffle-config.js
файле:
Совет № 3. Удалите ненужные переменные
В Ethereum вы получаете возмещение за газ за освобождение места для хранения.
Удаление переменного возмещения 15 000 за газ, но не более половины стоимости транзакции за газ. Удаление с помощью ключевого слова delete
эквивалентно присвоению начального значения для типа данных, например 0
для целых чисел.
Совет №4: вычисляйте известную ценность вне сети
Если вы знаете, какие данные нужно хешировать, и нет необходимости тратить больше вычислительной мощности для хеширования с использованием keccak256
, вы в конечном итоге потребляете
2-кратное количество газа.
Совет № 5: не сжимайте переменные
Это означает, что если вы используете uint8, EVM должен сначала преобразовать его в uint256, чтобы работать с ним, и преобразование требует дополнительных затрат! Вы можете задаться вопросом, о чем думали разработчики? Почему тогда они создали переменные меньшего размера? Ответ кроется в упаковке. В принципе, вы можете упаковать несколько небольших переменных в один слот, но если вы определяете единственную переменную и не можете ее упаковать, оптимально использовать uint256, а не uint8.
Совет № 6: используйте события
Данные, к которым не требуется доступ в сети, могут быть сохранены в событиях для экономии газа.
Совет № 7: используйте сборку
Когда вы компилируете смарт-контракт Solidity, он преобразуется в серию кодов операций EVM (виртуальная машина Ethereum).
На ассемблере вы пишете код, очень близкий к уровню кода операции. Написать код на таком низком уровне не очень просто, но преимущество в том, что вы можете вручную оптимизировать код операции и в некоторых случаях превзойти байт-код Solidity.
Совет № 8: используйте библиотеки
Если у вас есть несколько контрактов, которые используют одни и те же функции, вы можете извлечь эти общие функции в единую библиотеку, а затем развернуть эту библиотеку только один раз, и все ваши контракты будут указывать на эту библиотеку для выполнения общих функций.
Совет № 9: минимизируйте данные в цепочке
Чем меньше вы вставляете в цепочку, тем меньше затраты на газ.
Когда вы разрабатываете Dapp, вам не нужно помещать 100% ваших данных в блокчейн, обычно у вас есть часть системы (ненужные данные (метаданные и т. д.)) на централизованном сервере.
Совет № 10: избегайте манипулирования данными хранилища
Выполнение операций с памятью или вызовом данных, аналогичных памяти, всегда дешевле хранения.
Следующий код надежности поможет вам понять разницу между плохим кодом и лучше оптимизированным кодом.
Во втором контракте перед запуском цикла for мы присваиваем значение данных хранилища d переменной _d, чтобы избежать доступа к хранилищу при каждой итерации.
Совет № 11: используйте eth-gas-reporter
Этот репортер отображает изменения потребления газа для каждой функции в вашем смарт-контракте.
Совет № 12: используйте правила короткого замыкания в своих интересах
При использовании логической дизъюнкции (||), логической конъюнкции (&&) убедитесь, что ваши функции упорядочены правильно для оптимального использования газа. В логическом дизъюнкции (ИЛИ), если первая функция принимает значение «истина», вторая не будет выполняться и, следовательно, сэкономит вам газ. В логической дизъюнкции (И), если первая функция оценивается как ложь, следующая функция не оценивается. Следовательно, вы должны соответствующим образом упорядочить свои функции в своем твердотельном коде, чтобы уменьшить вероятность необходимости оценивать вторую функцию.
Совет № 13: используйте ERC1167 для многократного развертывания одного и того же контракта
Контракт с минимальным прокси-сервером EIP1167 - это стандартизированный и эффективный с точки зрения газа способ развертывания группы клонов контракта с завода. EIP1167 не только минимизирует длину, но и является буквально «минимальным» прокси-сервером, который ничего не делает, кроме проксирования. Это сводит к минимуму доверие. В отличие от других модернизируемых прокси-контрактов, которые полагаются на честность своего администратора (который может изменить реализацию), адрес в EIP1167 жестко запрограммирован в байт-коде и остается неизменным.
Совет №14: избегайте присвоения значений, которые вы никогда не будете использовать
Каждое присвоение переменной в Solidity стоит газа. При инициализации переменных мы часто теряем газ, присваивая значения по умолчанию, которые никогда не будут использоваться.
uint256 value;
дешевле uint256 value = 0;
.
Совет № 15: используйте сопоставления вместо массивов
Solidity - это первый язык, в котором сопоставления обходятся дешевле, чем массивы.
В большинстве случаев будет лучше использовать mapping
вместо массива из-за более дешевых операций.
Совет №16: ограничьте длину строки в требованиях
Если мы добавляем строки сообщений к операторам require, мы можем удешевить их, ограничив длину строки 32 байтами.
Совет № 17: массивы фиксированного размера дешевле динамических
Если мы знаем, какой длины должен быть массив, мы указываем фиксированный размер:
uint256[12] monthlyTransfers;
Это же правило применяется к строкам. Переменная string
или bytes
имеет динамический размер; мы должны использовать byte32
, если наша строка достаточно короткая, чтобы уместиться.
Если нам абсолютно необходим динамический массив, лучше всего структурировать наши функции так, чтобы они были аддитивными, а не вычитающими. Расширение массива требует постоянных затрат газа, тогда как усечение массива требует линейного расхода газа.
Свяжитесь со мной в LinkedIn, Github, и Twitter.
Конец статьи