โ›๏ธGas Saving Technique 42: `require` vs`assert`

Introduction

Ethereum smart contracts operate in a gas-limited environment. Every operation, from simple arithmetic to interacting with the contract's storage, consumes a certain amount of gas. Given the cost associated with gas, optimizing its use is paramount. A nuanced understanding of how different statements and functions consume gas is crucial. Among such, the utilization of assert() and require() in error handling and conditional checks plays a significant role. This tutorial delves deep into these two functions, focusing on their implications concerning gas usage.

Understanding assert() and require()

assert()

  • Usage: assert() is used for internal error checking and verifying invariants in a contract.

  • Gas Consumption: When an assert() statement fails, it consumes all remaining gas in the transaction.

  • Common Scenarios: Itโ€™s typically used to check conditions that should never fail unless there's a bug in the contract.

require()

  • Usage: require() is primarily used for input validation and pre-condition checks.

  • Gas Consumption: Unlike assert(), require() refunds the remaining gas when a condition is not met.

  • Common Scenarios: Commonly used to validate user inputs and ensure that conditions are met before executing certain logic.

Optimizing Gas with require()

Since require() refunds unused gas, itโ€™s the more gas-efficient option for error handling in most cases. Developers should lean towards using require() for condition checks and validations that could fail due to external inputs or incorrect parameters. This approach not only makes debugging easier but also makes the contract more gas-efficient, as users are refunded for the unused gas in case of failure.

Example

solidityCopy codepragma solidity ^0.8.0;

contract GasOptimization {
    uint256 public storedData;

    function set(uint256 x) public {
        // Using require for input validation
        require(x > 0, "Value must be greater than 0");
        storedData = x;
    }

    function verifyInvariant() public view {
        // Using assert for internal invariant checks
        assert(storedData != 0);
    }
}

In this example, require() is used to validate the input, ensuring that the storedData is always a positive number. This is a condition that depends on external input and could commonly fail, making require() the optimal choice due to its gas refund capability.

Best Practices

  • Prefer require() for Conditions Dependent on External Inputs: Since these conditions are more likely to fail, using require() will be more gas-efficient due to its refund capability.

  • Use assert() for Internal Invariants: assert() should be used for conditions that are internal invariants, as their failure indicates a bug in the contract.

Conclusion

Understanding the gas implications of assert() and require() is fundamental for writing efficient and cost-effective Solidity code. By strategically using these functions based on the likelihood of condition failure and the nature of the checks, developers can optimize gas usage, resulting in more efficient contract execution and a better user experience.

Last updated