Skip to content

amusingaxl/typed-math

Repository files navigation

Typed Math

Typed Math is a modern, typed spiritual successor to DS-Math for MakerDAO-style fixed-point units. It keeps DS-Math's unsigned wad/ray half-up formulas while using Solidity user-defined value types, operator overloads, and explicit conversion helpers to make unit mistakes harder to write.

Units

Type Scale Meaning Type docs
Wad 10**18 unsigned user-facing quantity docs/types/Wad.md
WadFactor 10**18 unsigned dimensionless multiplier or ratio docs/types/WadFactor.md
Ray 10**27 unsigned rate, index, or price factor docs/types/Ray.md
Rad 10**45 unsigned internal accounting quantity docs/types/Rad.md

Same scale does not imply same dimension. Wad is an amount; WadFactor is a multiplier such as a fee, penalty, percentage, auction parameter, or ratio.

Installation

Import the facade. It exposes the UDVTs, constants, errors, TypedMath namespace, global operators, and member-style helpers:

import {Rad, Ray, TypedMath, Wad, WadFactor} from "typed-math/src/TypedMath.sol";

TypedMath.sol is intentionally the package's core catch-all export facade. The implementation is split internally by type, but consumers should import from the facade so operator bindings, member helpers, constants, errors, and namespaced functions stay available from one stable path.

The source is split by type:

Source file Responsibility
src/TypedMath.sol canonical facade and namespaced TypedMath helpers
src/TypedMathDelta.sol opt-in signed delta facade
src/internal/TypedMathTypes.sol value types, constants, errors, global using bindings
src/internal/TypedMathOperators.sol free-function operator shims required by Solidity
src/internal/WadMath.sol Wad quantity helpers
src/internal/WadFactorMath.sol dimensionless wad-scale factor helpers
src/internal/RayMath.sol ray helpers
src/internal/RadMath.sol rad and rad-producing cross-unit helpers
src/internal/TypedMathCore.sol shared integer division helpers
src/internal/*Delta*.sol signed delta types and helpers

Basic Usage

Wrap raw scaled integers at system boundaries. Keep business logic typed:

Wad amount = Wad.wrap(2e18);
WadFactor fee = TypedMath.bpsToWadFactor(50); // 0.50%

Wad charged = amount.wmul(fee); // raw 0.01e18
uint256 raw = Wad.unwrap(charged);

Same-unit addition, subtraction, and comparisons use Solidity operators:

Wad total = Wad.wrap(1e18) + Wad.wrap(2e18);
Wad diff = Wad.wrap(3e18) - Wad.wrap(1e18);
bool ok = Wad.wrap(1e18) < Wad.wrap(2e18);

Namespaced and member-style helpers are both available:

Wad a = TypedMath.wmul(Wad.wrap(2e18), TypedMath.toWadFactor(3));
Wad b = Wad.wrap(2e18).wmul(TypedMath.toWadFactor(3));

WadFactor ratio = Wad.wrap(150e18).wdiv(Wad.wrap(100e18));
Ray compounded = Ray.wrap(1.01e27).rmul(Ray.wrap(1.02e27));
Rad debt = Wad.wrap(100e18).mul(Ray.wrap(1.05e27));

wdiv(Wad, Wad) forms a WadFactor ratio between quantities, while wdiv(Wad, WadFactor) divides a quantity by a factor and returns a Wad. They are intentionally different dimensions even though both use wad-scale arithmetic. If the denominator raw values are the same, the numeric quotient is the same; the distinction is the business meaning of the intermediate value. Use explicit intermediate variables when that intent matters.

Exact conversions use to* names, truncating conversions use trunc*, and raw amount/factor reinterpretation uses as*:

Wad exact = Ray.wrap(2e27).toWad();
Wad truncated = Ray.wrap(2e27 + 1).truncWad();
WadFactor factor = Wad.wrap(1e18).asWadFactor();
Wad amount = factor.asWad();

Signed Deltas

The core API is unsigned. Vat-style signed changes are exposed through the separate delta facade:

import {Rad, Ray, Wad} from "typed-math/src/TypedMath.sol";
import {RadDelta, RayDelta, TypedMathDelta, WadDelta, WadDeltaMath} from "typed-math/src/TypedMathDelta.sol";

using WadDeltaMath for Wad;

Wad ink = Wad.wrap(5e18).addDelta(WadDelta.wrap(-1e18));
RadDelta dtab = TypedMathDelta.mul(Ray.wrap(2e27), WadDelta.wrap(3e18));

WadDelta, RayDelta, and RadDelta are signed changes applied to unsigned state. They are not full signed fixed-point quantities and are not DS-Math semantics.

Rounding

Typed Math uses three explicit rounding families:

Name Meaning
wmul, wdiv, rmul, rdiv DS-Math-compatible half-up formulas
*Down, / operators for WadFactor and Ray floor/truncation toward zero
*Up division helpers ceil/toward positive infinity

For this unsigned-only API, Down is equivalent to floor/truncation toward zero, and Up is equivalent to ceil/toward positive infinity.

wdivDownMod and rdivDownMod return raw uint256 residues of the scaled numerator. They pair with wdivDown, rdivDown, or /, not with half-up wdiv or rdiv, and the residue is not a normalized fixed-point value.

Documentation

Development

Run the test suite:

forge test -vvv

Run the audit-oriented checks:

forge test --fuzz-runs 10000
TYPED_MATH_ORACLE_SEED=typed-math-oracle TYPED_MATH_ORACLE_CASES=64 forge test --match-contract TypedMathOracleTest -vvv
halmos
halmos --config halmos.strict.toml

Format Solidity:

forge fmt

About

A modern, typed spiritual successor to DS-Math for MakerDAO-style fixed-point units.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages