openzeppelin-contracts icon indicating copy to clipboard operation
openzeppelin-contracts copied to clipboard

Support Saturation Arithmetic Operations

Open Lohann opened this issue 9 months ago • 1 comments

Description

Adds gas efficient saturating arithmetic operators, those operators are useful when both overflow and revert are not desired. This PR also optimizes and remove unnecessary branching from various Math methods.

Motivation

If you want to write some formula, but don't want neither wrapping and reverts, the only option is using the Math.try* methods, which can't be used in chain and make the code less readable.

Ex: Make sure there're at least 100_000 gas units left before calling an external contract, otherwise revert.

// “all but one 64th", reference: https://eips.ethereum.org/EIPS/eip-150
uint256 gasAvailable = gasleft().saturatingSub(5000).saturatingMul(63) / 64;
require(gasAvailable >= 100_000, "not enough gas left to call contract");
// ... call contract

The equivalent code using Math.try* is less readable and less efficient.

(bool success, uint256 gasAvailable) = gasleft().trySub(5000);
gasAvailable = Math.ternary(success, gasAvailable, 0);

// “all but one 64th", reference: https://eips.ethereum.org/EIPS/eip-150
(success, gasAvailable) = gasAvailable.tryMul(63);
gasAvailable = Math.ternary(success, gasAvailable, type(uint256).max);
gasAvailable /= 64;

require(gasAvailable >= 100_000, "not enough gas left to call contract");
// ... call contract

Lohann avatar Apr 29 '24 06:04 Lohann