DiffusalOptionsQuoter
Read-only Black-Scholes option pricing with Greeks calculation
The DiffusalOptionsQuoter contract provides indicative option pricing using the Black-Scholes model. It's a read-only contract—all functions are view-only and don't modify state. The quoter calculates theoretical option prices and Greeks (delta, gamma, vega, theta, rho) using market data from the unified oracle.
Overview
The quoter serves two primary purposes:
| Purpose | Description |
|---|---|
| Price Discovery | Calculate theoretical Black-Scholes prices for any option |
| Risk Metrics | Compute Greeks for portfolio risk management |
All prices returned are indicative only. Actual trade execution happens through the OrderBook or RFQ contracts at market-determined prices.
API Variants
| Variant | Greeks | Use Case |
|---|---|---|
getOptionQuote | No | Gas-optimized price-only queries |
getOptionQuoteWithGreeks | Yes | Full pricing with risk metrics |
*ByName | Either | Human-readable pair names (e.g., "ETH-USD") |
*Batch | Either | Multiple quotes in one call |
Key Concepts
Black-Scholes Model
The quoter implements the standard Black-Scholes formula:
Where:
Greeks
| Greek | Symbol | Measures | Range |
|---|---|---|---|
| Delta | Δ | Price sensitivity to spot | -1 to 1 |
| Gamma | Γ | Delta sensitivity to spot | ≥ 0 |
| Vega | ν | Price sensitivity to volatility | ≥ 0 |
| Theta | Θ | Time decay (per year) | Usually < 0 |
| Rho | ρ | Price sensitivity to interest rate | Varies |
Precision
All inputs and outputs use WAD precision (1e18 = 1.0):
| Value | WAD Representation |
|---|---|
| $3,000 spot | 3000e18 |
| 50% volatility | 0.5e18 = 500000000000000000 |
| 0.25 delta | 0.25e18 = 250000000000000000 |
Storage & State
The quoter has minimal storage—just owner and oracle reference:
/// @custom:storage-location erc7201:diffusal.storage.DiffusalOptionsQuoter
struct DiffusalOptionsQuoterStorage {
address owner;
address oracle; // Unified DiffusalOracle
}Structs
OptionRequest
Parameters for pricing an option:
struct OptionRequest {
bytes32 pairId; // Trading pair identifier
uint256 strike; // Strike price (WAD)
uint256 expiry; // Expiry timestamp (unix seconds)
bool isCall; // true = call, false = put
}OptionRequestByName
Same as above but with human-readable pair name:
struct OptionRequestByName {
string pairName; // e.g., "ETH-USD"
uint256 strike;
uint256 expiry;
bool isCall;
}OptionQuote
Gas-optimized quote (price only):
struct OptionQuote {
uint256 price; // Black-Scholes price (WAD)
}OptionQuoteWithGreeks
Full quote with all Greeks:
struct OptionQuoteWithGreeks {
uint256 price; // Black-Scholes price (WAD)
int256 delta; // Delta (-1e18 to 1e18)
int256 gamma; // Gamma (WAD)
int256 vega; // Vega (WAD)
int256 theta; // Theta per year (WAD)
int256 rho; // Rho (WAD)
}OracleQuote
Raw oracle data used for pricing:
struct OracleQuote {
int64 price; // Spot price (8 decimals)
int64 volatility; // Implied volatility (8 decimals)
int64 premium; // Premium rate (8 decimals)
int64 riskFreeRate; // Risk-free rate (8 decimals)
int32 priceExpo; // Price exponent
uint256 pricePublishTime; // Price timestamp
}View Functions
Option Pricing
getOptionQuote
Returns gas-optimized price without Greeks.
function getOptionQuote(OptionRequest calldata request) external view returns (OptionQuote memory quote)Example:
OptionRequest memory req = OptionRequest({
pairId: keccak256(bytes("ETH-USD")),
strike: 3000e18, // $3000 strike
expiry: block.timestamp + 7 days,
isCall: true
});
OptionQuote memory quote = quoter.getOptionQuote(req);
// quote.price = theoretical call price in WADgetOptionQuoteByName
Same as above but uses human-readable pair name.
function getOptionQuoteByName(OptionRequestByName calldata request) external view returns (OptionQuote memory quote)Example:
OptionRequestByName memory req = OptionRequestByName({
pairName: "ETH-USD",
strike: 3000e18,
expiry: block.timestamp + 7 days,
isCall: true
});
OptionQuote memory quote = quoter.getOptionQuoteByName(req);getOptionQuoteBatch
Returns multiple quotes in a single call.
function getOptionQuoteBatch(OptionRequest[] calldata requests) external view returns (OptionQuote[] memory quotes)getOptionQuoteWithGreeks
Returns full pricing with all Greeks.
function getOptionQuoteWithGreeks(OptionRequest calldata request) external view returns (OptionQuoteWithGreeks memory quote)Example:
OptionQuoteWithGreeks memory quote = quoter.getOptionQuoteWithGreeks(req);
// quote.price = theoretical price
// quote.delta = 0.65e18 means 65% delta
// quote.gamma = change in delta per $1 spot move
// quote.vega = price change per 1% vol increase
// quote.theta = daily decay = quote.theta / 365
// quote.rho = price change per 1% rate increasegetOptionQuoteWithGreeksByName
Full pricing with Greeks using pair name.
function getOptionQuoteWithGreeksByName(OptionRequestByName calldata request) external view returns (OptionQuoteWithGreeks memory quote)getOptionQuoteWithGreeksBatch
Batch full pricing with Greeks.
function getOptionQuoteWithGreeksBatch(OptionRequest[] calldata requests) external view returns (OptionQuoteWithGreeks[] memory quotes)Oracle Data
getOracleQuote
Returns raw oracle data for a trading pair.
function getOracleQuote(bytes32 pairId) external view returns (OracleQuote memory quote)getOracleQuoteByName
Oracle data using human-readable pair name.
function getOracleQuoteByName(string calldata pairName) external view returns (OracleQuote memory quote)getOracleQuoteNoOlderThan
Oracle data with staleness check.
function getOracleQuoteNoOlderThan(bytes32 pairId, uint256 maxPriceAge) external view returns (OracleQuote memory quote)Reverts: StalePrice if price is older than maxPriceAge seconds.
getVolatility
Returns only the volatility for a pair.
function getVolatility(bytes32 pairId) external view returns (int64 volatility)getPrice
Returns only the spot price components.
function getPrice(bytes32 pairId) external view returns (int64 price, int32 expo, uint256 publishTime)getPremium
Returns only the premium rate.
function getPremium(bytes32 pairId) external view returns (int64 premium)Utility
getPairId
Generates a pair ID from asset symbols.
function getPairId(string calldata base, string calldata quote) external pure returns (bytes32 pairId)Example:
bytes32 pairId = quoter.getPairId("ETH", "USD");
// Returns keccak256("ETH-USD")Owner Functions
setOracle
Updates the unified oracle address.
function setOracle(address _oracle) external onlyOwnerEmits: DiffusalOracleUpdated, PriceOracleUpdated
transferOwnership
Transfers contract ownership.
function transferOwnership(address newOwner) external onlyOwnerEmits: OwnershipTransferred
Events
| Event | Parameters | Description |
|---|---|---|
OwnershipTransferred | previousOwner, newOwner | Ownership changed |
DiffusalOracleUpdated | oldOracle, newOracle | Oracle address updated |
PriceOracleUpdated | oldOracle, newOracle | Oracle address updated |
Integration Points
Depends On
| Contract | Purpose |
|---|---|
| DiffusalOracle | Spot price, volatility, premium, risk-free rate |
| BlackScholesWad | Black-Scholes pricing library |
Used By
| Contract | Purpose |
|---|---|
| DiffusalCollateralVault | Mark price for unrealized PnL |
| DiffusalLiquidationEngine | Mark price for liquidation |
| Frontend / Off-chain | Price discovery, portfolio analytics |
Gas Considerations
| Function | Approx. Gas | Notes |
|---|---|---|
getOptionQuote | ~50k | Price only, no Greeks |
getOptionQuoteWithGreeks | ~120k | Full Greeks calculation |
normalCdf (internal) | ~20k | Most expensive operation |
Optimization tips:
- Use
getOptionQuoteinstead ofgetOptionQuoteWithGreekswhen you don't need Greeks - Use batch functions to share oracle calls across multiple quotes
- Cache pair IDs instead of using
*ByNamevariants repeatedly
Security Considerations
Read-Only Design
The quoter is intentionally stateless (aside from owner/oracle config). All pricing functions are view-only, so:
- No reentrancy risks from pricing calls
- No state manipulation possible
- Safe to call from any context
Price Staleness
The quoter doesn't enforce price staleness by default. Use getOracleQuoteNoOlderThan when freshness matters:
// Enforce 60-second freshness
OracleQuote memory quote = quoter.getOracleQuoteNoOlderThan(pairId, 60);Indicative Prices Only
Important: Quotes from this contract are theoretical Black-Scholes prices, not execution prices. Actual trade prices are determined by:
- Order book market makers (bid/ask spread)
- RFQ quotes from the Main Market Maker
The theoretical price serves as a reference, but expect execution prices to differ based on market conditions.
Precision Boundaries
The quoter handles precision conversions internally:
Oracle (8 decimals) → WAD (18 decimals) → 64.64 fixed-point → WAD outputEdge cases to be aware of:
- Very small time-to-expiry values (near expiration)
- Extreme spot/strike ratios (deep ITM/OTM)
- Very low or very high volatility values
Code Reference
Source: packages/contracts/src/DiffusalOptionsQuoter.sol
Interface: packages/contracts/src/interfaces/IDiffusalOptionsQuoter.sol
Testnet: View on MonadVision
Internal Pricing Flow
// 1. Build Black-Scholes parameters
BlackScholesWad.ParamsWad memory params = _buildBsParams(pairId, strike, expiry);
// 2. Calculate price (with or without Greeks)
uint256 price = BlackScholesWad.priceOnly(params, isCall);
// or
BlackScholesWad.PriceResultWad memory result = BlackScholesWad.priceCall(params);Time to Expiry Calculation
function _calculateTimeToExpiry(uint256 expiry) internal view returns (uint256) {
if (expiry <= block.timestamp) revert Errors.OptionExpired();
uint256 timeRemaining = expiry - block.timestamp;
// Convert seconds to years as WAD
return (timeRemaining * Constants.WAD) / Constants.SECONDS_PER_YEAR;
}Related
- DiffusalOracle — Source of pricing inputs
- BlackScholesLib — Underlying pricing library
- Black-Scholes Math — Formula derivation and examples
- Protocol Design — Theoretical vs market pricing