defimath-compare

August 5, 2026 · View on GitHub

Gas and precision benchmarks comparing DeFiMath (source) against other Solidity math and options-pricing libraries.

This repository is not published to npm. It exists to produce the comparison tables in DeFiMath's README and to detect regressions when DeFiMath is changed.

What gets compared

DeFiMath is benchmarked against:

CategoryLibraries
MathPRBMath, ABDK, Solady, SolStat
Black-ScholesDerivexyz, Premia, Party1983, Dopex
Black-76— (no other on-chain implementations found)
BinaryHaptic
Rates— (no other on-chain implementations found)
Statistics— (no other on-chain implementations found)

Each function is measured for:

  • Average gas (over a typical input range)
  • Maximum absolute or relative error vs. a JavaScript reference (black-scholes, greeks, math-erf)

Benchmarked library versions

Every competitor is (being) pinned to an exact version so the numbers below are reproducible and we always know precisely what we measured against. Pinned packages are exact (no ^) in package.json + package-lock.json, and their adapters import them from node_modules instead of a vendored copy.

LibraryVersionSourceHow it's pinned
Solady0.1.26vectorized/soladynpm solady (exact)
PRBMath4.1.2PaulRBerg/prb-mathnpm @prb/math (exact)
ABDK3.2.0abdk-consulting/abdk-libraries-soliditynpm abdk-libraries-solidity (exact)
SolStatv1.0.0-betaprimitivefinance/solstatvendored — Foundry remappings block a clean npm link; version recorded in PROVENANCE
Derivexyz (Lyra)vendoredlyra-finance/lyra-protocolextracted copy — PROVENANCE
PremiavendoredPremian-Labs/premia-contractsextracted copy — PROVENANCE
Party1983vendoredpartylikeits1983/black_scholes_solidityextracted copy — PROVENANCE
Dopexvendoredupstream unconfirmed (rebranded Stryke)extracted copy — PROVENANCE
Hapticvendoredupstream unconfirmedextracted copy — PROVENANCE

The ABDK row above is the standalone ABDKMathQuad math benchmark. The Premia and Dopex extracted models bundle their own vendored ABDK sub-copy — that's part of their rows, not the pinned one above, and gets addressed when those adapters are migrated.

Protocol code extracted from a larger monorepo (Derivexyz, Premia, Dopex, Haptic, Party) can't be cleanly linked — it's single files lifted out of Foundry monorepos with rewritten imports — so it stays vendored, but each carries a PROVENANCE.md recording its upstream project, repo, files, licenses, and modifications. The full strategy and status are tracked in the external-library pinning runbook in defimath-manage.

Running

npm install
npx hardhat test

Each test prints a table like:

Metric         DeFiMath  Derivexyz  Premia  Party1983   Dopex
Max abs error   7.7e-13    6.8e-13  1.7e-1     3.8e+1
Avg gas            2582      13360   20623      35963   88969
  ✔ call

Results — Math primitives

Gas (average over input sweep)

FunctionDeFiMathPRBMathABDKQuadSoladySolStat
exp2892,8155,857372
ln3906,91412,826518
log24066,84212,445
log104068,643
pow7619,943976
sqrt197974808384
cbrt340550
stdNormCDF6183,103
erf6492,876

Max relative error

FunctionDeFiMathPRBMathABDKQuadSoladySolStat
exp1.9e-141.9e-141.9e-141.9e-14
ln2.8e-162.8e-162.8e-162.8e-16
log23.6e-163.6e-163.6e-16
log102.2e-162.2e-16
pow6.1e-166.1e-166.1e-16
sqrt4.8e-194.8e-194.8e-194.8e-19
cbrt2.2e-162.2e-16
stdNormCDF1.4e-153.2e-8
erf2.1e-165.7e-8

All rows are relative error (fraction, not %). stdNormCDF and erf are measured on x ∈ [0.5, 10] — away from erf's zero at x = 0, where relative error would be ill-defined (both functions are bounded, in [0, 1] and [−1, 1]). Bold entries mark the best (lowest) value in each row; ties bold all leaders. Grids and parameters match the test sources in test/Math.test.mjs; reproduce with npx hardhat test test/Math.test.mjs. Full module reference: DeFiMath math primitives documentation.

expm1 and log1p are not benchmarked here: their domain of interest is near the root (|result| < 1), where the correct error metric is absolute, not the relative error this table reports. Their gas and precision are documented in defimath and at defimath.com/docs/math.

Results — European options (Black-Scholes + Greeks)

Gas (average over spot × strike × time × vol × rate sweep)

FunctionDeFiMathDerivexyzPremiaParty1983Dopex
call2,58213,36020,62335,96388,969
put2,59213,36320,79136,14088,301
delta1,6618,62124,960
gamma1,433
theta3,101
vega1,3737,490

Max absolute error (option price at $1,000 spot, unit Greeks)

FunctionDeFiMathDerivexyzPremiaParty1983Dopex
call7.7e-136.8e-131.7e-13.8e1
put6.5e-136.5e-131.7e-19.9e1
delta6.7e-166.7e-169.2e-1
gamma3.9e-18
theta1.8e-15
vega8.9e-161.1e-15

Max relative error (values that exceed 1)

Option prices — and theta / vega for longer-dated options — exceed 1, so relative error is the scale-invariant view there. (delta / gamma stay ≤ 1 and are compared by absolute error above.)

FunctionDeFiMathDerivexyzPremiaParty1983Dopex
call1.4e-131.5e-137.6e-31.0e0
put4.9e-144.7e-147.9e-31.0e0
theta1.2e-15
vega7.2e-167.3e-16

Dashes indicate the library doesn't implement that function. Dopex returns prices in a different scale and is benchmarked on gas only. Grids and parameters match test/BlackScholes.test.mjs; reproduce with npx hardhat test test/BlackScholes.test.mjs. Full module reference: DeFiMath Black-Scholes options pricing and Greeks documentation.

Results — Black-76 (options on a future)

No on-chain Black-76 implementation was found to benchmark against, so there is nothing to compare here. Black-76 prices European options on a forward/futures underlying and is exactly e^(−r·τ) · Black-Scholes(spot = F, rate = 0), so its gas and precision track the Black-Scholes results above. Full module reference: DeFiMath Black-76 documentation.

Results — Binary (cash-or-nothing) options

Gas (average over spot × strike × time × vol × rate sweep)

FunctionDeFiMathHaptic
call1,91316,218
put1,91816,221
delta1,717
gamma1,859
theta3,161
vega1,805

Max absolute error (unit payout)

FunctionDeFiMathHaptic
call5.6e-165.6e-16
put5.6e-165.6e-16
delta1.5e-17
gamma1.0e-18
theta4.9e-17
vega1.8e-17

Haptic is the only on-chain binary-options implementation we found. It doesn't ship greeks, so delta / gamma / theta / vega rows show DeFiMath only. DeFiMath wins gas on call / put by ~8.5×; price precision now ties Haptic at 5.6e-16, both well below ulp at unit payout. Precision is measured against a true-math Black-Scholes reference (Math.log / Math.exp / math-erf), independent of any DeFiMath algorithm. Grids match test/BinaryOptions.test.mjs; reproduce with npx hardhat test test/BinaryOptions.test.mjs. Full module reference: DeFiMath binary (cash-or-nothing) options documentation.

Results — Interest & rates

We found no other on-chain interest-rate libraries to benchmark against, so this section reports DeFiMath measurements only.

FunctionAvg gasMax rel error
compoundInterest4252.7e-16
presentValue4772.6e-15
logReturn5917.1e-16
continuousToDiscrete3751.6e-14
discreteToContinuous5745.1e-16
yieldToMaturity7362.7e-14
internalRateOfReturn12,7919.3e-16

internalRateOfReturn gas scales with cashflow count; the figure above averages over scenarios of 2–5 cashflows. Grids and parameters match test/Rates.test.mjs; reproduce with npx hardhat test test/Rates.test.mjs. Full module reference: DeFiMath interest rates and yield math documentation.

Results — Statistics

We found no other on-chain statistics libraries to benchmark against, so this section reports DeFiMath measurements only. Array-input functions are measured on 30-element inputs; their gas scales with input length.

FunctionAvg gasMax rel error
geometricMean2841.2e-16
mean6,9801.7e-16
stdDev15,2524.2e-16
weightedAverage15,6872.8e-16
historicalVolatility26,0401.6e-14
sharpeRatio26,1782.2e-14
maxDrawdown15,1919.9e-16
valueAtRisk36,7521.9e-14
conditionalValueAtRisk32,9172.5e-14

valueAtRisk precision is measured against simple-statistics' quantile; all others against direct JavaScript reference implementations. Grids and parameters match test/Statistics.test.mjs; reproduce with npx hardhat test test/Statistics.test.mjs. Full module reference: DeFiMath statistics and risk metrics documentation.

Layout

contracts/
  wrappers/    Thin Solidity contracts that import DeFiMath via npm and expose
               *MG variants returning gas used. Equivalent to the wrappers in
               defimath itself, but the import path is "defimath-lib/contracts/...".
  adapters/    Each comparison library lives here under its own subdirectory,
               with a small adapter contract that conforms to a common interface.
test/
  Math.test.mjs       Compare suite for the DeFiMath math library.
  BlackScholes.test.mjs    Compare suite for European option pricing + Greeks + IV.
  BinaryOptions.test.mjs     Compare suite for cash-or-nothing binary options.
  Rates.test.mjs      Measure suite for interest-rate functions (DeFiMath-only).
  Statistics.test.mjs      Measure suite for statistics functions (DeFiMath-only).
  Common.test.mjs     Shared helpers (tokens, time constants, etc.).

Precision references are independent of DeFiMath's algorithms: black-scholes / greeks / math-erf (npm), simple-statistics (npm), and inline true-math implementations (Math.log / Math.exp / Math.sqrt).

How it works

The BlackScholesWrapper, MathWrapper, etc. contracts in contracts/wrappers/ import DeFiMath from npm:

import "defimath-lib/contracts/derivatives/BlackScholes.sol";

contract BlackScholesWrapper {
    function getCallOptionPriceMG(...) external view returns (uint256 price, uint256 gasUsed) {
        uint256 startGas = gasleft();
        price = BlackScholes.getCallOptionPrice(...);
        gasUsed = startGas - gasleft();
    }
    // ...
}

Because DeFiMath functions are internal libraries, Solc inlines them into the wrapper at compile time — generating the same bytecode the wrapper-in-defimath would. Gas measurements are therefore directly comparable to a "real" deployment of DeFiMath that calls these library functions.

Adapters in contracts/adapters/ follow the same shape: they wrap each third-party library's API behind a common interface, so the test files can call them uniformly.

Local development against unreleased DeFiMath

By default package.json pins defimath-lib to its published version. When iterating on DeFiMath itself and you want changes to surface here without a publish cycle, use npm link:

# in the defimath repo
cd ~/path/to/defimath && npm link

# in this repo
cd ~/path/to/defimath-compare && npm link defimath-lib

This replaces node_modules/defimath-lib with a symlink to your local checkout. Edits in DeFiMath's contracts/ are picked up by the next npx hardhat compile. Revert with:

npm unlink defimath-lib && npm install

Compiler settings

Solidity version, viaIR, and optimizer runs are pinned to the same values as in defimath's hardhat.config.js. If you change either side, change both — gas numbers depend on the compiler settings, not just the library code.

Credits

This project would not be possible without the open-source libraries it benchmarks against. Thanks to the authors and maintainers of:

License

MIT