Diffusal
Math

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

LayerFormatExample
Oracle (Pyth)8 decimals with expoprice=300_000_000_000, expo=-8 → $3000
External interfaceWAD (1e18)$3000 = 3000 × 10^18
Internal math64.64 fixed-pointABDK 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 places

The value is stored as a signed 128-bit integer where the lower 64 bits represent the fractional part:

stored value=actual value×264\text{stored value} = \text{actual value} \times 2^{64}

WAD Format (1e18)

WAD is a common DeFi convention where values are scaled by 101810^{18}:

1.0 in WAD = 1e18
0.5 in WAD = 0.5e18 = 500000000000000000

Conversion Flow

Pyth int64 → int64ToWad() → WAD → toInt128() → 64.64 math → back to WAD

Input Conversion Example

Example: 50% volatility

  Oracle: 50_000_000 (8 decimals)
  WAD:    0.5e18
  64.64:  0.5 × 2^64

Conversion 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:

FunctionDescription
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 N(x)N(x) 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:

N(x)=12πex2/2N'(x) = \frac{1}{\sqrt{2\pi}} e^{-x^2/2}

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 years

Gas 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:

LibraryDocumentation
Math64x64ExtendedMath64x64Extended Contract
BlackScholesLibBlackScholesLib Contract
BlackScholesWadBlackScholesLib Contract

On this page