Skip to content
LogoLogo

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)
}