Interest rate primitives — compounding, discounting, log returns, APR↔APY conversions, and closed-form / iterative yield calculations. Built on top of DeFiMath's exp, ln, expm1, and log1p.
Contract: Rates.sol
| Function | Gas | Description |
|---|---|---|
| compoundInterest | 467 | Continuous compounding: P · e^(r·t) |
| presentValue | 519 | Discounting: FV · e^(−r·t) |
| logReturn | 600 | ln(currentPrice / previousPrice) |
| continuousToDiscrete | 508 | e^apr − 1 (APR → APY) |
| discreteToContinuous | 589 | ln(1 + apy) (APY → APR) |
| yieldToMaturity | 736 | Zero-coupon YTM (closed form) |
| internalRateOfReturn | 17k–49k | IRR via Newton-Raphson (scales with cashflow count) |
npm install defimath-lib
principal, futureValue, price — uint128, 18-decimal fixed-point (1e18 = 1.0).rate, apr, apy — annualized as 18-decimal fixed-point. APR/APY conversions take int256 (signed).timeInterval, timeToMaturity — uint32, seconds.internalRateOfReturn takes parallel cashflows[] and times[] arrays plus an initial guess for Newton-Raphson.internal pure.import "defimath-lib/contracts/finance/Rates.sol";
// Continuous compounding: how much does principal grow in 1 year at 5%?
uint256 fv = DeFiMathRates.compoundInterest(1_000e18, 0.05e18, 365 days);
// Discount a future cashflow back to today.
uint256 pv = DeFiMathRates.presentValue(fv, 0.05e18, 365 days);
// Convert continuous APR (5%) to effective APY.
int256 apy = DeFiMathRates.continuousToDiscrete(int256(0.05e18));compoundInterest and presentValue are exact inverses. Round-tripping a value through both reconstructs the original up to the underlying exp precision (~5e-14).continuousToDiscrete uses expm1; discreteToContinuous uses log1p. Safe to call with small rates without precision loss.yieldToMaturity is closed-form and cheap; internalRateOfReturn is iterative. Use YTM for zero-coupon bonds (a single ln). Use IRR for arbitrary cashflow schedules — but expect 17k–49k gas depending on cashflow count, and supply a reasonable guess to stay within the iteration budget.NoConvergenceError. The solver caps at 50 Newton-Raphson iterations.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. 66 tests across 7 function groups (compound interest, present value, log return, continuous↔discrete rate conversions, yield to maturity, internal rate of return). Each scalar function is sweep-validated against an inline JavaScript reference; internalRateOfReturn is validated against a JS Newton-Raphson IRR for 4- and 12-cashflow series with strict-equality gas regression gates.
Foundry property-fuzz layer. 15 mathematical properties × 32,000 random runs each = 480,000 random executions per CI run.
| Category | Count | What they check |
|---|---|---|
| Round-trips | 5 | PV(CI(P, r, t), r, t) ≈ P, CI(PV(FV, r, t), r, t) ≈ FV, APR↔APY round-trip (d2c(c2d(r)) ≈ r), reverse (c2d(d2c(r)) ≈ r), bond YTM round-trip (PV(F, YTM(P, F, t), t) ≈ P) |
| Monotonicity | 4 | CI ↑ in principal, PV ↑ in futureValue, PV ↓ in rate, continuousToDiscrete ↑ in apr |
| Identities | 3 | CI(P, 0, t) = P, CI(P, r, 0) = P, logReturn(p, p) = 0 |
| Output bounds | 2 | CI ≥ principal (for rate ≥ 0), PV ≤ futureValue (for rate ≥ 0) |
| Symmetries | 1 | logReturn(p1, p0) = −logReturn(p0, p1) (anti-symmetric in argument order) |
Sources: test/Rates.test.mjs · test/foundry/Rates.t.sol