DiffusalOptionsRFQ
On-chain settlement for EIP-712 signed RFQ quotes from Main Market Makers
The DiffusalOptionsRFQ contract enables Request-for-Quote trading between users and Main Market Makers (MMMs). Users request quotes off-chain from MMMs, who sign EIP-712 quotes that users then submit on-chain to execute trades.
Overview
RFQ trading provides an alternative to the order book, offering:
| Feature | Description |
|---|---|
| Professional Pricing | MMMs provide tight spreads and deep liquidity |
| Instant Execution | No order matching needed—quotes are pre-agreed |
| Large Orders | Better for size without moving the market |
| Partial Fills | Quotes can be filled incrementally |
RFQ vs Order Book
| Aspect | RFQ | Order Book |
|---|---|---|
| Counterparty | Main Market Maker only | Any user |
| Price Discovery | MMM quotes | Market-driven bids/asks |
| Order Creation | MMM signs quote off-chain | Maker signs order off-chain |
| Fees | RFQ fee (taker only) | Maker + taker fees |
| Best For | Large orders, professional pricing | Price discovery, liquidity provision |
Key Concepts
Trade Direction
The takerIsBuying flag determines position assignment:
takerIsBuying | Taker Gets | MMM Gets | Premium Flow |
|---|---|---|---|
true | Long (+size) | Short (-size) | Taker → MMM |
false | Short (-size) | Long (+size) | MMM → Taker |
Main Market Maker (MMM)
Only addresses registered as MMMs in the PositionManager can sign RFQ quotes:
if (!POSITION_MANAGER.isMmm(quote.mmm)) revert Errors.NotMmm();MMMs have special privileges:
- Cannot be liquidated (non-liquidatable status)
- Can sign RFQ quotes
- Can increment their nonce to invalidate all quotes
Nonce Management
Each MMM has an independent nonce:
- Quote nonce must match MMM's current nonce
incrementMmmNonce()invalidates all pending quotes- Unlike order book, only registered MMMs can increment their nonce
Storage & State
/// @custom:storage-location erc7201:diffusal.storage.DiffusalOptionsRFQ
struct DiffusalOptionsRfqStorage {
address owner;
mapping(address => uint256) mmmNonce; // MMM → current nonce
mapping(bytes32 => uint256) quoteFilled; // Quote hash → filled amount
uint256 rfqFeeBps; // RFQ fee rate
address feeRecipient; // Fee collector
address collateralVault; // Vault for premium transfers
address marginCalculator; // Margin calculator for health checks
}RFQ Quote Struct
struct RfqQuote {
address mmm; // Main Market Maker address
address taker; // Authorized taker (address(0) = any)
bytes32 seriesId; // Option series identifier
bool takerIsBuying; // true = taker buys long, false = taker buys short
uint256 price; // Premium per contract (WAD)
uint256 size; // Maximum number of contracts
uint256 nonce; // Must match MMM's current nonce
uint256 quoteExpiry; // Quote validity deadline
bytes32 pairId; // Trading pair for series validation
uint256 strike; // Strike price for series validation
uint256 optionExpiry; // Option expiry for series validation
bool isCall; // Option type for series validation
}External Functions
Quote Filling
fillRfqQuote
Fills a single RFQ quote.
function fillRfqQuote(
RFQTypes.RfqQuote calldata quote,
bytes calldata signature,
uint256 fillAmount
) external returns (RfqFillResult memory result)| Parameter | Type | Description |
|---|---|---|
quote | RfqQuote | The quote to fill |
signature | bytes | EIP-712 signature from MMM |
fillAmount | uint256 | Amount of contracts to fill |
Returns: RfqFillResult with quote hash, filled amount, premium, and fee.
Validation checks:
fillAmount > 0- Quote not expired (
block.timestamp < quoteExpiry) - Option not expired (
block.timestamp < optionExpiry) - Option has sufficient time to expiry (≥ MIN_TIME_TO_EXPIRY)
- Signer is registered MMM
- Valid signature from MMM
- Taker authorized (if
quote.taker != address(0)) - Nonce matches MMM's current nonce
- Fill amount ≤ remaining size
- Series ID matches parameters
- Post-trade: both parties pass margin health check
Emits: RfqQuoteFilled
fillRfqQuoteBatch
Fills multiple quotes in a single transaction.
function fillRfqQuoteBatch(
RFQTypes.RfqQuote[] calldata quotes,
bytes[] calldata signatures,
uint256[] calldata fillAmounts
) external returns (RfqFillResult[] memory results)Nonce Management
incrementMmmNonce
Invalidates all pending quotes for the calling MMM.
function incrementMmmNonce() externalRequirements: Caller must be a registered MMM.
Emits: MmmNonceIncremented
View Functions
getQuoteHash
Computes the EIP-712 hash of a quote.
function getQuoteHash(RFQTypes.RfqQuote calldata quote) public view returns (bytes32)getQuoteStatus
Returns the complete status of a quote.
function getQuoteStatus(RFQTypes.RfqQuote calldata quote, bytes calldata signature)
external view returns (QuoteStatus memory status)Returns:
struct QuoteStatus {
bool isValid; // Signature valid
bool isExpired; // Quote expired
bool isOptionExpired; // Option expired
uint256 filled; // Amount filled
uint256 remaining; // Amount remaining
}verifySignature
Verifies a quote signature.
function verifySignature(RFQTypes.RfqQuote calldata quote, bytes calldata signature)
public view returns (bool)quoteFilled
Returns the filled amount for a quote hash.
function quoteFilled(bytes32 quoteHash) external view returns (uint256)mmmNonce
Returns an MMM's current nonce.
function mmmNonce(address mmm) external view returns (uint256)Owner Functions
setRfqFee
Sets the RFQ fee rate.
function setRfqFee(uint256 _rfqFeeBps) externalEmits: RFQFeeUpdated
setFeeRecipient
Sets the fee recipient address.
function setFeeRecipient(address _feeRecipient) externalEmits: FeeRecipientUpdated
setCollateralVault
Sets the collateral vault address (for premium transfers).
function setCollateralVault(address _collateralVault) externalEmits: CollateralVaultUpdated
setMarginCalculator
Sets the margin calculator address (for health checks after trades).
function setMarginCalculator(address _marginCalculator) externalEmits: MarginCalculatorUpdated
transferOwnership
Transfers contract ownership.
function transferOwnership(address newOwner) externalEmits: OwnershipTransferred
Events
| Event | Parameters | Description |
|---|---|---|
RfqQuoteFilled | quoteHash, mmm, taker, seriesId, takerIsBuying, price, fillAmount, premium, feeAmount | Quote fill executed |
MmmNonceIncremented | mmm, newNonce | MMM nonce increased |
RFQFeeUpdated | oldFeeBps, newFeeBps | Fee rate changed |
FeeRecipientUpdated | oldRecipient, newRecipient | Fee recipient changed |
CollateralVaultUpdated | oldVault, newVault | Vault address changed |
MarginCalculatorUpdated | oldCalculator, newCalculator | Margin calculator changed |
OwnershipTransferred | previousOwner, newOwner | Ownership changed |
OwnershipTransferStarted | previousOwner, newOwner | Ownership transfer started |
Execution Flow
Fill Quote Sequence
┌─────────────────────────────────────────────────────────────────────────────┐
│ 1. Taker calls fillRfqQuote(quote, signature, fillAmount) │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 2. Basic Validation │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ • Check fillAmount > 0 │ │
│ │ • Check quote not expired │ │
│ │ • Check option not expired │ │
│ │ • Check sufficient time to expiry │ │
│ │ • Check signer is registered MMM │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 3. Signature & State Validation │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ • Verify EIP-712 signature from MMM │ │
│ │ • Check taker authorization (if specified) │ │
│ │ • Check nonce matches │ │
│ │ • Check fillAmount <= remaining │ │
│ │ • Verify seriesId matches parameters │ │
│ │ • Update fill state │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 4. Calculate Premium & Fee │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ • premium = (price × fillAmount) / 1e18 │ │
│ │ • rfqFee = (premium × rfqFeeBps) / 10000 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 5. Ensure Series Exists │
│ SeriesRegistry.getOrCreateSeries() │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 6. Execute Transfer │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ takerIsBuying? │ │
│ │ │ │ │
│ │ ┌──────────────┴──────────────┐ │ │
│ │ ▼ ▼ │ │
│ │ [Yes] [No] │ │
│ │ Position MMM: -fillAmount Position MMM: +fillAmount │ │
│ │ Position taker: +fillAmount Position taker: -fillAmount │ │
│ │ Premium: taker → MMM Premium: MMM → taker │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 7. Collect RFQ Fee │
│ Transfer rfqFee: taker → feeRecipient │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 8. Margin Check (via MarginCalculator) │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ • Check MMM isHealthy() │ │
│ │ • Check taker isHealthy() / isPortfolioHealthy() │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 9. Emit RfqQuoteFilled │
└─────────────────────────────────────────────────────────────────────────────┘Integration Points
Depends On
| Contract | Purpose |
|---|---|
| DiffusalOptionsPositionManager | Position updates, MMM verification |
| DiffusalOptionsSeriesRegistry | Series validation/creation |
| DiffusalCollateralVault | Premium transfers |
| DiffusalMarginCalculator | Post-trade margin health checks |
| USDC (ERC20) | Collateral token |
Used By
| Contract | Purpose |
|---|---|
| DiffusalInsuranceFund | Fee recipient |
| Off-chain MMM systems | Quote generation |
Security Considerations
MMM Authorization
Only registered MMMs can sign valid quotes:
if (!POSITION_MANAGER.isMmm(quote.mmm)) revert Errors.NotMmm();Taker Authorization
Quotes can be restricted to a specific taker:
if (quote.taker != address(0) && quote.taker != msg.sender) {
revert Errors.UnauthorizedTaker();
}Setting taker = address(0) allows anyone to fill the quote.
Series Validation
The quote includes full series parameters, which are validated against the seriesId:
bytes32 computedSeriesId = keccak256(abi.encodePacked(
quote.pairId, quote.strike, quote.optionExpiry, quote.isCall
));
if (computedSeriesId != quote.seriesId) revert Errors.InvalidSeriesId();Margin Enforcement
Both parties must pass health checks after the trade (via MarginCalculator):
IDiffusalMarginCalculator calc = IDiffusalMarginCalculator(s.marginCalculator);
if (!calc.isHealthy(quote.mmm)) revert Errors.InsufficientMargin();
if (!calc.isHealthy(msg.sender)) revert Errors.InsufficientMargin();Code Reference
Source: packages/contracts/src/DiffusalOptionsRFQ.sol
Interface: packages/contracts/src/interfaces/IDiffusalOptionsRFQ.sol
RFQ Types: packages/contracts/src/utils/RFQTypes.sol
EIP-712 Domain
bytes32 private constant EIP712_DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
string private constant NAME = "DiffusalOptionsRFQ";
string private constant VERSION = "1";Related
- RFQ Flow (Protocol) — High-level RFQ mechanics
- DiffusalOptionsOrderBook — Alternative peer-to-peer trading
- Protocol Design — MMM role and responsibilities