put

Black-76

Computes the price of a European put option on a future using the Black-76 model.

Avg. gas

2,565

Max abs. error

1.3e-10

when price < 1

Max rel. error

5e-12

when price ≥ 1

Signature

solidity
function put(
    uint128 future,
    uint128 strike,
    uint32  timeToExp,
    uint64  volatility,
    uint64  rate
) internal pure returns (uint256 price)

Parameters

NameTypeDescription
futureuint128Current future price in 18-decimal fixed-point format.
strikeuint128Strike price, 18-decimal fixed-point. Precision-tuned for the no-arbitrage band against the future — see Bounds.
timeToExpuint32Time to expiration in seconds. timeToExp == 0 is allowed (handled as expired).
volatilityuint64Annualized implied volatility, 18-decimal fixed-point (e.g. 60% → 6e17).
rateuint64Annualized risk-free (discount) rate, 18-decimal fixed-point. The future already embeds the cost of carry.

Returns

NameTypeDescription
priceuint256Put option price in 18-decimal fixed-point. Always ≥ 0.

Bounds

BoundValue
MIN_FUTURE1e-6 smallest allowed future price (1e12)
MAX_FUTURE1e15 largest allowed future price (1e33)
MAX_STSP_RATIO5× (strike must lie within [future/5, future·5])
MAX_EXPIRATION32 years (1,009,152,000 seconds)
MAX_RATE400% annual (4e18)

Behavior

  • Validates all five inputs against module-wide constants and reverts with a typed error on any violation.
  • Prices options on a future, not spot: d₁ carries no rate term and the whole payoff is discounted by e^(−rτ), since the future already embeds the cost of carry.
  • Fast-path on expiration: when timeToExp == 0, returns intrinsic value max(strike − future, 0) without running the pricer.
  • Symmetric counterpart of the call: same d₁/d₂ machinery with Φ(−d₁)/Φ(−d₂) in place of Φ(d₁)/Φ(d₂). By put-call parity the two prices differ by e^(−rτ)(F − K).
  • Equivalent to e^(−rτ) · BlackScholes.put(spot = F, rate = 0). Pure internal function; no external calls, no storage.

How it works

put implements the closed-form Black-76 formula for a European put on a future:

P=erT[KΦ(d2)FΦ(d1)]P = e^{-rT}\left[ K \, \Phi(-d_2) - F \, \Phi(-d_1) \right]
d1=ln(F/K)+σ22TσT,d2=d1σTd_1 = \frac{\ln(F/K) + \tfrac{\sigma^2}{2} T}{\sigma \sqrt{T}}, \qquad d_2 = d_1 - \sigma \sqrt{T}

It reuses the same d₁/d₂ machinery as the call — d₁ carries no rate term — evaluating Φ(−d₁)/Φ(−d₂) with Math.stdNormCDF and discounting the whole bracket once by e^(−rT) via Math.expPositive(rT).

The suite enforces a 5e-12 relative bound where the price is ≥ 1 and a 1.3e-10 absolute bound for the sub-$1 (deep-OTM) tail, both at future = $1,000 across a full sweep of strike, time, vol, and rate — head-to-head measurements against other libraries live in defimath-compare.

Errors

ErrorTrigger
FutureLowerBoundErrorfuture ≤ MIN_FUTURE
FutureUpperBoundErrorfuture ≥ MAX_FUTURE
StrikeLowerBoundErrorstrike · 5 < future
StrikeUpperBoundErrorfuture · 5 < strike
TimeToExpiryUpperBoundErrortimeToExp ≥ MAX_EXPIRATION
RateUpperBoundErrorrate ≥ MAX_RATE

Example

solidity
import "defimath-lib/contracts/derivatives/Black76.sol";

uint256 price = Black76.put(
    1000e18,         // future = $1,000
    950e18,          // strike = $950
    90 days,         // 90 days to expiry
    0.60e18,         // 60% annualized vol
    0.05e18          // 5% discount rate
);
// price ≈ 93.5e18  (about $93.50 per option)