Исходный код для воспроизведения этой демонстрации доступен на 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. Для токенов, которые передаются тысячи или даже миллионы раз, эта экономия приводит к значительно более низкой стоимости для конечных пользователей. Сравнительная плата за газ для трех функций приведена ниже.