# Gas Golfing #3: Zero vs NonZero Storage Values

By [aṇuha](https://paragraph.com/@anuha) · 2023-05-12

---

This is a continuation of the previous two articles on gas optimization. While it does not matter and one can start from here. There are some references to previous articles.

In Gas Golfing #1, We visited the ReentrancyGuard.sol from OpenZeppelin and discussed why a `uint256` variable is used to mark the entry and exit of functions. They could have in fact used a `bool` or a `uint8` . One of the reasons was covered in the article. We will deep dive into major reasoning in this section.

Beginning with the ReentrancyGaurd from OpenZeppelin. It is below here.

    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)
    
    pragma solidity ^0.8.0;
    /**
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * <https://blog.openzeppelin.com/reentrancy-after-istanbul/>[Reentrancy After Istanbul].
     */
    abstract contract ReentrancyGuard {
        // Booleans are more expensive than uint256 or any type that takes up a full
        // word because each write operation emits an extra SLOAD to first read the
        // slot's contents, replace the bits taken up by the boolean, and then write
        // back. This is the compiler's defense against contract upgrades and
        // pointer aliasing, and it cannot be disabled.
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        uint256 private _status;
        constructor() {
            _status = _NOT_ENTERED;
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and making it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            _nonReentrantBefore();
            _;
            _nonReentrantAfter();
        }
        function _nonReentrantBefore() private {
            // On the first call to nonReentrant, _status will be _NOT_ENTERED
            require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            // Any calls to nonReentrant after this point will fail
            _status = _ENTERED;
        }
        function _nonReentrantAfter() private {
            // By storing the original value once again, a refund is triggered (see
            // <https://eips.ethereum.org/EIPS/eip-2200>)
            _status = _NOT_ENTERED;
        }
        /**
         * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
         * `nonReentrant` function in the call stack.
         */
        function _reentrancyGuardEntered() internal view returns (bool) {
            return _status == _ENTERED;
        }
    }
    

I am interested in the below section of the code.

    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;
    uint256 private _status;
    

**Why is OpenZeppelin using _1 and 2_ integer constants and not _0 and 1_ constant integers or _false and true_ boolean constants to mark the entry and exit of execution of the code block?**

The simple answer here is again `Gas Optimization`.

The best way to understand this is by

1.  Creating a sample contract and checking the gas utilization of certain functions.
    
2.  Learning about the EVM and opcodes in a certain depth
    
3.  We will also visit certain snippets (The Uncharted Territory by most Solidity Developers) in the yellow paper.
    

Hence we will do this over multiple articles.

Zero Values
-----------

The default value of a variable is also called `Zero Value`. Examples are

    0 --> for intergers and unsigned integers
    false --> for booleans
    0x0000000000000000000000000000000000000000 --> for address
    

Non-Zero Values
---------------

Any value other than zero values are non-zero values for that variable. Examples are

    permitted numbers other than ZERO -> for integers and non-integers
    true -> for boolean
    other addresses -> for address
    

**So what is the importance of this distinction?**

> Zero Values do not occupy any storage in the blockchain. Irrespective of what storage slot or variable type is associated with a slot, the blockchain does not need to manage the zero values as they are default. At the same time, any non-zero value that is to be stored on the chain needs to be updated on all the nodes of that chain.

Let’s take the below code sample deployed with no optimization

    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.19;
    
    contract Example {
        uint256 val;
        // Input : 100 
        // Transaction Cost: 43702 gas
        // Execution Cost = 43702 - 21000 = ~22500+ gas
        // SSTORE = 20000 units
        // Input : 0 
        // Transaction Cost: 21709 gas
        // Execution Cost = 21709 - 21000 = ~709 gas? 
        // But the actual execution cost is 5398 units
        function setval(uint256 _val) external {
            val = _val;
        }
    }
    

`val` is initialized to zero on contract deployment And can be set to a desired unsigned integer using setVal.

Points to note:

1.  Setting the uint256 val to 100 consumes more than 20000 units of gas towards storing the value on the chain. This is `zero to non-zero` transition. They are costly.
    
2.  Setting the uint256 val to 0 consumes just 709 units of gas for storing the zero-value on the chain. This is `non-zero to zero` transition. They are gas-saving because the blockchain does not need to store the data on the chain. Hence Ethereum rewards the removal of value from the chain. Only 709 gas units of the sender are used while the execution cost is 5398 units.
    
3.  A total of 23000+ units of gas for set and unset of `val`.
    

Now lets the try the sample with slight changes. `val` is initialized to 1.

    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.19;
    
    contract Example {
        uint256 val = 1 ;
        // Input : 100 
        // Transaction Cost: 26602 gas
        // Execution Cost = 26602 - 21000 = 5398 gas
        // Input : 1 
        // Transaction Cost: 26602 gas
        // Execution Cost = 26602 - 21000 = 5398 gas
        function setval(uint256 _val) external {
            val = _val;
        }
    }
    

Points to note:

1.  Setting the uint256 val to 100 consumes 5398 units of gas for storing the value on the chain. This is a `non-zero to non-zero` transition. And the same for setting `val` to 1.
    
2.  A total of 10600+ units of gas for set and unset of `val`.
    

> This is why ReentrancyGuard does not use either bool or zero-values for setting and unsettling the variable values in places where the values of a variable toggle frequently.
> 
> Just pick up a token and check its transactions and most likely you will see varying gas units consumed for the same token transfer and this is most likely the reason for the varying gas units consumed.

Conclusion
----------

In conclusion, choose not only the variable type but also the initial variable values carefully.

Such excellent learning from just one contract of the OpenZeppelin. Kudos to the OZ team.

---

*Originally published on [aṇuha](https://paragraph.com/@anuha/gas-golfing-3-zero-vs-nonzero-storage-values)*
