Solidity Math
Fixed-point arithmetic and precision handling in smart contracts
Diffusal uses 64.64 fixed-point arithmetic for high-precision option pricing calculations on-chain. This page covers the numeric formats, conversion flows, and implementation details.
Numeric Formats
Overview
| Layer | Format | Example |
|---|---|---|
| Oracle (Pyth) | 8 decimals with expo | price=300_000_000_000, expo=-8 → $3000 |
| External interface | WAD (1e18) | $3000 = 3000 × 10^18 |
| Internal math | 64.64 fixed-point | ABDK library int128 |
64.64 Fixed-Point Format
64.64 format: value × 2^64
Integer part: 64 bits (signed)
Decimal part: 64 bits
Range: ±9.2 × 10^18 with ~19 decimal placesThe value is stored as a signed 128-bit integer where the lower 64 bits represent the fractional part:
WAD Format (1e18)
WAD is a common DeFi convention where values are scaled by :
1.0 in WAD = 1e18
0.5 in WAD = 0.5e18 = 500000000000000000Conversion Flow
Pyth int64 → int64ToWad() → WAD → toInt128() → 64.64 math → back to WADInput Conversion Example
Example: 50% volatility
Oracle: 50_000_000 (8 decimals)
WAD: 0.5e18
64.64: 0.5 × 2^64Conversion Functions
// WAD (1e18) → 64.64
function wadTo64x64(uint256 wad) internal pure returns (int128) {
return int128(int256((wad << 64) / 1e18));
}
// 64.64 → WAD (1e18)
function from64x64ToWad(int128 x) internal pure returns (uint256) {
return uint256(uint128(x)) * 1e18 >> 64;
}
// 8 decimals → WAD
function int64ToWad(int64 price, int32 expo) internal pure returns (uint256) {
// Adjust for exponent and scale to 1e18
if (expo >= 0) {
return uint256(uint64(price)) * (10 ** uint32(expo)) * 1e18;
} else {
return uint256(uint64(price)) * 1e18 / (10 ** uint32(-expo));
}
}Library Structure
BlackScholesWad (external interface)
↓ converts WAD → 64.64
BlackScholesLib (core math)
↓ uses
Math64x64Extended (math operations)Math64x64Extended
Extended math functions for 64.64 fixed-point:
| Function | Description |
|---|---|
ln(x) | Natural logarithm |
exp(x) | Exponential function |
sqrt(x) | Square root |
normalCdf(x) | Cumulative normal distribution |
normalPdf(x) | Normal probability density |
BlackScholesLib
Core implementation using 64.64 fixed-point arithmetic.
struct Params64x64 {
int128 spot; // S - Current price
int128 strike; // K - Strike price
int128 rate; // r - Risk-free rate
int128 volatility; // σ - Volatility
int128 timeToExpiry; // T - Time in years
}
struct PriceResult64x64 {
int128 price; // Option price
int128 d1; // d1 value
int128 d2; // d2 value
Greeks64x64 greeks; // All Greeks
}Key functions:
function calculateD1D2(Params64x64 memory p) internal pure returns (int128 d1, int128 d2);
function priceCall(Params64x64 memory p) internal pure returns (PriceResult64x64 memory);
function pricePut(Params64x64 memory p) internal pure returns (PriceResult64x64 memory);BlackScholesWad
WAD adapter providing the external interface.
struct ParamsWad {
uint256 spot; // S in WAD
uint256 strike; // K in WAD
uint256 rate; // r in WAD (0.05e18 = 5%)
uint256 volatility; // σ in WAD (0.5e18 = 50%)
uint256 timeToExpiry; // T in WAD (years)
}
struct GreeksWad {
int256 delta; // Delta (WAD, -1e18 to 1e18)
int256 gamma; // Gamma (WAD)
int256 vega; // Vega (WAD)
int256 theta; // Theta (WAD, per year)
int256 rho; // Rho (WAD)
}
// Full pricing with all Greeks
function priceCall(ParamsWad memory p) internal pure returns (PriceResultWad memory);
function pricePut(ParamsWad memory p) internal pure returns (PriceResultWad memory);
// Gas-optimized: price only (no Greeks)
function priceCallOnly(ParamsWad memory p) internal pure returns (uint256);
function pricePutOnly(ParamsWad memory p) internal pure returns (uint256);Normal Distribution Implementation
The cumulative normal distribution is implemented using the Abramowitz and Stegun polynomial approximation:
// Abramowitz and Stegun approximation
// Accuracy: |error| < 7.5 × 10^-8
function normalCdf(int128 x) internal pure returns (int128) {
// Handle sign
bool isNegative = x < 0;
if (isNegative) x = -x;
// Polynomial coefficients
int128 t = ONE / (ONE + p * x);
// Horner's method for polynomial evaluation
int128 poly = ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t;
int128 result = ONE - normalPdf(x) * poly;
return isNegative ? ONE - result : result;
}The probability density function:
Precision Considerations
Safe Bounds
// Minimum values to avoid underflow
uint256 constant MIN_VOLATILITY_WAD = 1e15; // 0.001 (0.1%)
uint256 constant MIN_TIME_WAD = 1e14; // 0.0001 years (~52 min)
// Maximum values to avoid overflow
uint256 constant MAX_SPOT_WAD = 1e30; // Very large price
uint256 constant MAX_TIME_WAD = 10e18; // 10 yearsGas Optimization
Price-Only Functions
When Greeks are not needed, use the optimized functions:
// ~30% gas savings vs full calculation
uint256 callPrice = BlackScholesWad.priceCallOnly(params);
uint256 putPrice = BlackScholesWad.pricePutOnly(params);Batch Operations
For multiple calculations with the same parameters, compute shared values once:
// Calculate d1, d2 once
(int128 d1, int128 d2) = BlackScholesLib.calculateD1D2(params);
// Reuse for both call and put
int128 callPrice = /* use d1, d2 */;
int128 putPrice = /* use d1, d2 */;Contract Documentation
For detailed API documentation of the Solidity implementations:
| Library | Documentation |
|---|---|
| Math64x64Extended | Math64x64Extended Contract |
| BlackScholesLib | BlackScholesLib Contract |
| BlackScholesWad | BlackScholesLib Contract |
Related
- Black-Scholes Model — Option pricing formulas and Greeks
- Margin Calculations — How margin uses these primitives
- DiffusalOptionsQuoter — Public API using these libraries