Low-level fixed-point primitives in 18-decimal format (1e18 = 1.0). All pure, gas-efficient, and validated to sub-1e-12 relative error against reference libraries.
Contract: Math.sol
| Function | Gas | Description |
|---|---|---|
| exp | 331 | Exponential function e^x |
| expm1 | 438 | e^x − 1 (precision-preserving for small x) |
| ln | 375 | Natural logarithm |
| log1p | 500 | ln(1 + x) (precision-preserving for small x) |
| log2 | 391 | Base-2 logarithm |
| log10 | 391 | Base-10 logarithm |
| pow | 748 | Power function x^a |
| sqrt | 245 | Square root |
| cbrt | 368 | Cube root |
| sqrtTime | 184 | Specialized sqrt of time in years for Black-Scholes — no input validation |
| stdNormCDF | 660 | Standard normal CDF Φ(x) |
| erf | 685 | Error function |
| mulDiv | 155 | (a · b) / d with full 512-bit intermediate precision |
| mul | 130 | (a · b) / 1e18 — fixed-point multiply with denominator baked in |
| abs | 17 | Branchless |int256| (handles int256.min cleanly) |
| min | 23 | Branchless minimum of two uint256 |
| max | 23 | Branchless maximum of two uint256 |
| clamp | 78 | Clamp x into [lo, hi] (composed max then min) |
| avg | 21 | Overflow-safe (a + b) / 2 via (a & b) + ((a ^ b) >> 1) |
npm install defimath-lib
1e18 = 1.0).exp takes int256, returns uint256; ln takes uint256, returns int256.internal pure — no storage access, no external calls.import "defimath-lib/contracts/math/Math.sol";
uint256 x = 2e18; // x = 2.0
uint256 root = DeFiMath.sqrt(x); // root ≈ 1.41421e18
int256 lnX = DeFiMath.ln(x); // lnX ≈ 0.69315e18
uint256 ePow = DeFiMath.exp(int256(x)); // ePow ≈ 7.389e18expm1 for small x. Computing ex − 1 via exp(x) - 1e18 catastrophically cancels when |x| < 0.01. expm1 uses a Taylor series in that range and preserves full 18-digit precision.log1p for small x. Same reason — log1p(x) preserves precision near zero where ln(1 + x) would lose ~15 digits to subtraction.exp underflows silently to 0. For x < −41.45e18, exp(x) returns 0 (not a revert) since the true value is below 1e-18 representational precision.ln, sqrt, cbrt, and sqrtTime emit the new CLZ opcode introduced in Osaka.sqrtTime is specialized, not general-purpose. It expects x as time in years (1e18 = 1 year) and performs no input validation. It is built for Black-Scholes option pricing — where the caller has already bounded x — and is precision-tuned for [1s, 32y]. For a general fixed-point square root use sqrt instead.Every function reverts on out-of-bounds inputs with a named error — see the per-function pages for limits and error specifics.
Hardhat correctness layer. 202 tests across 20 function groups. Each function is validated against the corresponding JavaScript reference (Math.exp, Math.log, Math.sqrt, math-erf) over ~200-sample sweeps that cover the full operational domain. Per-function precision thresholds match the table above. Limit tests pin behavior at min and max valid inputs; failure tests cover every documented revert path.
Foundry property-fuzz layer. 28 mathematical properties, each fuzzed with 32,000 random inputs per CI run (= 896,000 random executions). Counterexamples are automatically shrunk on failure.
| Category | Count | What they check |
|---|---|---|
| Round-trips | 5 | ln(exp(x)) ≈ x, exp(ln(x)) ≈ x, sqrt²(x) ≈ x, cbrt³(x) ≈ x, exp(x)·exp(-x) ≈ 1 |
| Monotonicity | 5 | exp, ln, sqrt, Φ, erf all preserve input ordering |
| Identities | 8 | expm1 = exp − 1, log1p = ln(1 + x), pow at exponents 0/1/2, mul = mulDiv(_, _, 1e18), mulDiv = a·b/d, min + max = a + b, avg = (a + b)/2 |
| Output bounds | 6 | Φ ∈ [0, 1], erf ∈ [-1, 1], pow always non-negative, min ≤ max, clamp ∈ [lo, hi], avg ∈ [min, max] |
| Symmetries | 3 | Φ(x) + Φ(-x) = 1, erf(-x) = -erf(x), abs(-x) = abs(x) |
Sources: test/Math.test.mjs · test/foundry/Math.t.sol