Math
Everything here is checked, like the rest of Lithe's arithmetic: a result that doesn't fit, or a division by zero, panics.
Math
std/math/math.lithe.
| Function | |
|---|---|
min(a, b), max(a, b) | for uint or int |
clamp(x, low, high) | for uint or int; raises MathInvalidRange, or MathInvalidSignedRange for int, when low > high |
average(a, b) | without overflow in a + b; a uint mean rounds down, an int one toward zero |
ceilDiv(a, b) | a / b rounded up |
mulDivUp(a, b, d) | a * b / d rounded up, with a 512-bit product |
sqrt(a), cbrt(a) | the integer square or cube root, rounded down |
log2(a), log10(a), log256(a) | the base-2, base-10, or base-256 logarithm, rounded down; 0 for 0 |
log2Up(a), log10Up(a), log256Up(a) | the same, rounded up, as OpenZeppelin's Rounding.Ceil |
abs(x) | the magnitude of an int, as a uint, so abs(Int.min) is 2^255 |
dist(a, b) | |a - b| for uint or int, as a uint |
gcd(a, b) | the greatest common divisor; gcd(0, 0) is 0 |
saturatingAdd(a, b), saturatingMul(a, b) | the result, or Uint.max where it would overflow |
saturatingSub(a, b) | a - b, or 0 where it would underflow, as Solady's zeroFloorSub |
lerp(a, b, t, t0, t1) | for uint or int: t's point between t0 and t1, from a to b, as Solady's lerp. t is clamped to the range, t0 may be above t1, and the step rounds toward a |
a * b / d rounded down is the built-in Uint.mulDiv. Its product is kept in 512 bits, so Uint.mulDiv(amount, price, 1e18) doesn't overflow where amount * price would.
SafeCast
std/math/safe_cast.lithe. Every integer conversion is checked: uint(x) and uint128(x) revert with Panic(0x11) when the value doesn't fit. SafeCast.toUint(x) and SafeCast.toInt(x) check the same way but raise OpenZeppelin's SafeCastOverflowedIntToUint or SafeCastOverflowedUintToInt.
FixedPoint
std/math/fixed_point.lithe. A Wad is a number with 18 decimals, its own type so that a plain uint can't be mixed in by mistake, and a Swad is a signed one. FixedPoint requires Math, so a contract that uses it uses both.
import "std/math/fixed_point";
import "std/math/math";
contract Fees is {
use Math;
use FixedPoint;
storage is record {
rate : Wad;
feeBps : uint;
}
(* 0.3% as a rate, and 25 basis points. *)
deploy() is {
storage.rate := Wad(3_000_000_000_000_000);
storage.feeBps := 25;
}
external view function interest(const principal : uint) : uint is
FixedPoint.mulWad(principal, storage.rate)
external view function fee(const amount : uint) : uint is
FixedPoint.applyBps(amount, storage.feeBps)
}| Function | |
|---|---|
one(), scale() | 1e18 as a Wad, and as a uint |
toWad(n), fromWad(x) | whole numbers in and out, rounding down |
wadMul(a, b), wadDiv(a, b) | products and quotients of two Wads, rounding down |
wadMulUp(a, b), wadDivUp(a, b) | the same, rounding up |
mulWad(amount, factor), mulWadUp(amount, factor) | an amount scaled by a Wad, rounding down or up |
divWad(amount, factor), divWadUp(amount, factor) | an amount divided by a Wad, rounding down or up |
applyBps(amount, bps) | bps basis points of amount |
sMulWad(a, b), sDivWad(a, b) | products and quotients of two Swads, rounding toward zero. The product a * b, or a * 1e18, must fit an int |
expWad(x) | e ** x for a Swad; 0 below -41.45, ExpOverflow from 135.31 |
lnWad(x) | the natural logarithm of a Swad; LnWadUndefined unless x > 0 |
powWad(x, y) | x ** y as expWad(lnWad(x) * y), for Swads |
sqrtWad(x), cbrtWad(x) | the square or cube root of a Wad, rounded down |
rpow(x, n, base) | x ** n in a fixed point with base for one, each product rounded to nearest; 0 ** 0 is base. Panics when base is 0 and n > 1 |
The unsigned products and quotients keep a 512-bit intermediate, so they succeed wherever the result fits, past where Solady's mulWad and divWad revert. The rest are Solady's FixedPointMathLib, constant for constant, and give its results bit for bit: expWad, lnWad, and powWad are approximations, and they raise Solady's ExpOverflow and LnWadUndefined. Where Solady raises SMulWadFailed, SDivWadFailed, or RPowOverflow, the overflow panics with Panic(0x11), and a division by zero with Panic(0x12), like the rest of Lithe's arithmetic.
import "std/math/fixed_point";
import "std/math/math";
contract Curve is {
use Math;
use FixedPoint;
storage is record {
rate : Swad;
}
(* Continuous compounding: `principal * e ** (rate * years)`. *)
external view function grown(const principal : uint; const years : Swad) : uint
raises ExpOverflow
is FixedPoint.mulWad(principal, Wad(uint(int(FixedPoint.expWad(FixedPoint.sMulWad(storage.rate, years))))))
(* Per-second interest in ray, 27 decimals, compounded over `seconds`. *)
external pure function compound(const perSecond : uint; const seconds : uint) : uint is
FixedPoint.rpow(perSecond, seconds, 10 ** 27)
}
