Solidity Assembly: Checking if an Address is 0 (Efficiently)

Alex Otsu
2 min readFeb 20, 2023

--

The source code to reproduce this demonstration is available in GitHub. Follow the the author Alex Otsu on Twitter for more Solidity tips, tricks, and thoughts.

It is relatively common to need to check if an address is zero — in fact, the OpenZeppelin implementation of the ERC-20 standard does so twice with every transfer and twice with every approval!

The code is straightforward and favors readability. An example is below:

require(from != address(0), “ERC20: transfer from the zero address”);

If the address is not zero, throw an error that says “ERC20: transfer from the zero address”.

This is great, but can be improved upon in two ways.

First, ever since Solidity v0.8.4, Solidity has supported custom errors, which are billed as a “a convenient and gas-efficient way to explain to users why an operation failed”. Instead of needing load in a custom string, you can instead give an error a descriptive name and display it to the caller by placing it after the revert keyword.

The updated code looks something like the following, and save around 80 gas per call (from an estimated 428 to 346):

error ZeroAddress();function solidity_notZero(address toCheck) public pure returns(bool success) {
if(toCheck == address(0)) revert ZeroAddress();
return true;
}

The second way it can be improved is by replacing the require statement with Assembly. Without digging too deep into the bytecode, Solidity has a lot of guardrails that can be removed (with care) for optimization purposes, especially for simple functionality like checking if an address is zero. Assembly code that achieves the same effect as a require statement is below:

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;
}

The first thing we are doing is assigning the location of the free memory pointer to the variable ptr. Next, we are storing the selector for the custom error at that location. Lastly, we are using the revert function to revert the call with the selector as return data, which to the end user reads ZeroAddress().

This method saves an additional ~60 gas over the Solidity require statement. For tokens that are transferred thousands or even millions of time, these savings add up to a significantly lower cost to end users. The comparative gas fees for the three functions is below.

--

--