# Solidity Gas Golfing #1

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

---

Gas Golfing #1: uint8 vs uint256
--------------------------------

Below is the ReentrancyGuard.sol from OpenZeppelin

    // 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** `uint256` as against `uint8` when the only required values `1` and `2` can fit in the range of `uint8`?

The simple answer is `Gas Optimization`.

When writing smart contracts in Solidity, gas optimization is crucial. One way to optimize gas usage is by choosing the appropriate data types. Solidity supported a wide range of unsigned integers ranging between uint8 to uint256 with a gap of 8 bits. In this write-up, we will explore which of these data types is more efficient to store and use.

Storage
-------

uint8
-----

As the name suggests, uint8 is an unsigned integer that can hold a maximum value of 2⁸-1, which is 255. Therefore, uint8 should use only 1 byte of storage space.

uint256
-------

On the other hand, uint256 is an unsigned integer that can hold a maximum value of 2²⁵⁶-1, which is an incredibly large number. Therefore, uint256 requires 32 bytes of storage space.

Let’s take the below code sample

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.19;
    contract Example {
    
        uint8 smallNum = 21;  // Occupies Slot 0 of Storage
        uint256 bigNum = 12134567890; // Occupies Slot 1 of Storage
    
        // 2311 gas (Cost only applies when called by a contract)
        function readSmallNum() public view returns(uint8){
            return smallNum;
        }
    
        // 2248 gas (Cost only applies when called by a contract)
        function readBigNum() public view returns(uint256){
            return bigNum;
        }
    
        // Input 0: 0x0000000000000000000000000000000000000000000000000000000000000015
        // Input 1: 0x00000000000000000000000000000000000000000000000000000002d346cfd2
        function getValueAtSlot(uint256 slotNum) public view returns (bytes32) {
            bytes32 value;
            assembly {
                value := sload(slotNum)
            }
            return value;
        }
    }
    

In the contract storage, the variables are organized in slots of 32 bytes each irrespective of whether it was uint8 or uint256.

![](https://storage.googleapis.com/papyrus_images/ae08add88121bcf65582feca967c0c86065e245bf8ba077120fa5b0a89a6940a.webp)

Values at a given slot can be accessed by using the assembly code below:

    assembly {
         value := sload(slotNum)
    }
    

Execution
---------

The above contract was deployed with optimization turned on for 1000 runs.

*   `readBigNum` requires `2248` units of gas
    
*   `readSmallNum` requires `2311` units of gas
    

> One of the primary reasons why reading the `uint8` value is costlier than `uint256` is because when the `uint8` value is read by EVM it reads the whole slot first and performs extra masking operations, while the uint256 value is returned directly. And any extra opcode operations mean extra gas consumption.

Conclusion
----------

In conclusion, choosing the appropriate data type is essential when designing smart contracts in Solidity. Please consider the learnings from the above section to choose the appropriate data type.

Now that we know the reason for openzeppelin’’s use of `uint256` datatypes check the \*\*[_anuha_\*\*](https://github.com/sudeepjc/anuha) GitHub repo and gas-golf-1 folder sample that compare and illustrate the ReentrancyGuard.sol‘s uint8 vs uint256 versions.

Links:

[https://github.com/sudeepjc/anuha/tree/main/contracts/gas-golfing-1](https://github.com/sudeepjc/anuha/tree/main/contracts/gas-golfing-1)

[https://github.com/sudeepjc/anuha/tree/main/test/gas-golfing-1](https://github.com/sudeepjc/anuha/tree/main/test/gas-golfing-1)

---

*Originally published on [aṇuha](https://paragraph.com/@anuha/solidity-gas-golfing-1)*
