New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Saturation Arithmetic Operations #5029
base: master
Are you sure you want to change the base?
Support Saturation Arithmetic Operations #5029
Conversation
🦋 Changeset detectedLatest commit: d513400 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
contracts/utils/math/Math.sol
Outdated
// equivalent to: a >= b ? a - b : 0 | ||
return (a - b) * SafeCast.toUint(a > b); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could also do.
// equivalent to: a >= b ? a - b : 0 | |
return (a - b) * SafeCast.toUint(a > b); | |
return ternary(a > b, a - b, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's actually a good point, because the ternary operator looks like:
b ^ ((a ^ b) * SafeCast.toUint(condition))
Whoever if a
or b
is zero, it can be simplified to:
// if `b` is zero, then `conditon ? a : 0` is equivalent to:
SafeCast.toUint(condition) * a;
// if `a` is zero, then `conditon ? 0 : b` is equivalent to:
SafeCast.toUint(!condition) * b;
So it works like an and
operator, I really wish the compiler could do this kind of optimization for us, I was wondering if creating a new operator would be confusing, like:
// if `condition` is true, returns `a`, otherwise return zero.
function and(bool condition, uint256 a) internal pure returns (uint256) {
unchecked {
return a * SafeCast.toUint(condition));
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise I can refactor it for use the ternary
at a little gas cost but increased code readability.
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.The equivalent code using
Math.try*
is less readable and less efficient.