Исходный код для воспроизведения этой демонстрации доступен на GitHub. Следите за новостями автора Alex Otsu в Твиттере, чтобы узнать больше советов, рекомендаций и мыслей о Solidity.
Относительно часто приходится проверять, является ли адрес нулевым — на самом деле реализация OpenZeppelin стандарта ERC-20 делает это дважды при каждой передаче и дважды при каждом подтверждении!
Код прост и способствует удобочитаемости. Пример ниже:
require(from != address(0), “ERC20: transfer from the zero address”);
Если адрес не нулевой, выдать ошибку «ERC20: перевод с нулевого адреса».
Это здорово, но можно улучшить двумя способами.
Во-первых, начиная с Solidity v0.8.4, Solidity поддерживает настраиваемые ошибки, которые позиционируются как удобный и экономичный способ объяснить пользователям, почему операция не удалась. Вместо того, чтобы загружать пользовательскую строку, вы можете вместо этого дать ошибке описательное имя и отобразить его вызывающей стороне, поместив его после ключевого слова revert
.
Обновленный код выглядит примерно так и экономит около 80 газа за вызов (приблизительно с 428 до 346):
error ZeroAddress(); function solidity_notZero(address toCheck) public pure returns(bool success) { if(toCheck == address(0)) revert ZeroAddress(); return true; }
Второй способ улучшить это — заменить оператор require
на Assembly. Не углубляясь в байт-код, Solidity имеет множество ограничений, которые можно удалить (с осторожностью) в целях оптимизации, особенно для простых функций, таких как проверка того, является ли адрес нулевым. Ассемблерный код, который обеспечивает тот же эффект, что и оператор require
, приведен ниже:
error ZeroAddress(); function assembly_notZero(address toCheck) public pure returns(bool success) { assembly { if iszero(toCheck) { let ptr := mload(0x40) mstore(ptr, 0xd92e233d00000000000000000000000000000000000000000000000000000000) // selector for `ZeroAddress()` revert(ptr, 0x4) } } return true; }
Первое, что мы делаем, это присваиваем переменной ptr
положение указателя свободной памяти. Затем мы сохраняем селектор для пользовательской ошибки в этом месте. Наконец, мы используем функцию revert
для возврата вызова с селектором в качестве возвращаемых данных, которые для конечного пользователя читаются как ZeroAddress()
.
Этот метод экономит дополнительно около 60 единиц газа по сравнению с оператором Solidity require
. Для токенов, которые передаются тысячи или даже миллионы раз, эта экономия приводит к значительно более низкой стоимости для конечных пользователей. Сравнительная плата за газ для трех функций приведена ниже.