An orderbook-based options protocol on Aptos, leveraging Hyperion for price discovery, Pyth for spot oracles, and Move's resource model for position safety. Targeting European-style BTC/ETH options for MVP with 3-6 month solo development timeline.
- System Overview
- Core Components
- Move Module Architecture
- Oracle Strategy
- Hyperion Integration
- Options Pricing
- Settlement & Exercise
- Collateral & Margin
- Risk Management
- Phased Development Plan
- Technical Risks & Mitigations
┌─────────────────────────────────────────────────────────────────────┐
│ USER INTERFACE │
│ (Web App: React + Aptos Wallet Adapter) │
└─────────────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────────────▼───────────────────────────────────┐
│ PROTOCOL LAYER (Move) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │
│ │ Options │ │ Orderbook │ │ Collateral │ │ Settlement │ │
│ │ Engine │ │ Interface │ │ Vault │ │ Engine │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Oracle │ │ Risk │ │ Events │ │
│ │ Adapter │ │ Manager │ │ Emitter │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│ │
┌───────────▼───────────┐ ┌────────────▼────────────┐
│ HYPERION ORDERBOOK │ │ PYTH ORACLE │
│ (Price Discovery) │ │ (Spot + Vol Data) │
└───────────────────────┘ └─────────────────────────┘
- Safety First: Move resources prevent double-exercise, position manipulation
- Capital Efficiency: Collateral requirements based on real-time risk
- Composability: Standard interfaces for DeFi integration
- Simplicity for MVP: European options only, limited strike/expiry grid
In Scope:
- European-style call and put options
- BTC/USDC and ETH/USDC pairs
- Weekly and monthly expiries
- Cash-settled options
- Basic orderbook integration via Hyperion
- Pyth oracle for spot prices
- Simple collateral system (full collateral initially)
Out of Scope (Phase 2+):
- American-style options
- Portfolio margin
- Exotic options (barriers, binaries)
- Cross-margin between positions
- Implied volatility oracle (use historical vol initially)
Manages option series creation, position tracking, and lifecycle.
Responsibilities:
- Create option series (underlying, strike, expiry, type)
- Mint/burn option tokens
- Track open interest
- Manage exercise window
Adapter layer for Hyperion orderbook integration.
Responsibilities:
- Create markets for option series
- Route orders to Hyperion
- Handle fills and partial fills
- Manage order cancellation
Holds and manages collateral for writers (sellers).
Responsibilities:
- Accept collateral deposits
- Lock collateral against positions
- Release collateral on close/expiry
- Handle liquidations
Handles option exercise and settlement at expiry.
Responsibilities:
- Fetch settlement price from oracle
- Calculate payoffs
- Distribute funds to ITM holders
- Return collateral to OTM writers
Abstracts oracle integration for price feeds.
Responsibilities:
- Fetch spot prices (Pyth)
- Calculate historical volatility
- Provide settlement prices
- Handle oracle failures gracefully
Monitors positions and triggers liquidations.
Responsibilities:
- Calculate margin requirements
- Monitor collateral ratios
- Trigger liquidations when undercollateralized
- Manage protocol risk parameters
┌──────────────┐
│ errors │
└──────────────┘
▲
┌──────────────────┼──────────────────┐
│ │ │
┌───────┴──────┐ ┌────────┴───────┐ ┌──────┴───────┐
│ types │ │ events │ │ config │
└──────────────┘ └────────────────┘ └──────────────┘
▲ ▲ ▲
│ │ │
└──────────────────┼──────────────────┘
│
┌──────┴──────┐
│ oracle │
└─────────────┘
▲
│
┌──────────────────┼──────────────────┐
│ │ │
┌───────┴──────┐ ┌────────┴───────┐ ┌──────┴───────┐
│ collateral │ │ series │ │ margin │
└──────────────┘ └────────────────┘ └──────────────┘
▲ ▲ ▲
│ │ │
└──────────────────┼──────────────────┘
│
┌──────┴──────┐
│ options │ (main entry point)
└─────────────┘
▲
│
┌──────┴──────┐
│ orderbook │ (Hyperion integration)
└─────────────┘
sources/
├── errors.move # Error codes
├── types.move # Core type definitions
├── events.move # Event structs
├── config.move # Protocol parameters
├── oracle.move # Pyth adapter
├── collateral.move # Vault management
├── series.move # Option series management
├── margin.move # Margin calculations
├── options.move # Main protocol logic
└── orderbook.move # Hyperion integration
// types.move
module options_protocol::types {
use std::string::String;
/// Option type enum
const CALL: u8 = 1;
const PUT: u8 = 2;
/// Option style enum
const EUROPEAN: u8 = 1;
const AMERICAN: u8 = 2; // Future
/// Represents an option series (e.g., BTC-30000-CALL-2025-03-28)
struct OptionSeries has copy, drop, store {
underlying: String, // "BTC", "ETH"
quote: String, // "USDC"
strike: u64, // Strike price (6 decimals)
expiry: u64, // Unix timestamp
option_type: u8, // CALL or PUT
style: u8, // EUROPEAN or AMERICAN
}
/// Unique identifier for a series
struct SeriesId has copy, drop, store {
id: u256,
}
/// Long position (buyer)
struct LongPosition has key, store {
series_id: SeriesId,
amount: u64, // Number of contracts (8 decimals)
entry_price: u64, // Average entry premium (6 decimals)
created_at: u64,
}
/// Short position (writer/seller)
struct ShortPosition has key, store {
series_id: SeriesId,
amount: u64, // Number of contracts
collateral_locked: u64, // USDC locked
entry_price: u64, // Premium received
created_at: u64,
}
/// Settlement result
struct SettlementResult has copy, drop, store {
series_id: SeriesId,
settlement_price: u64, // Spot at expiry
payout_per_contract: u64,
is_itm: bool,
}
}// options.move
module options_protocol::options {
use std::signer;
use aptos_framework::coin::{Self, Coin};
use aptos_framework::timestamp;
use options_protocol::types::{Self, OptionSeries, SeriesId, LongPosition, ShortPosition};
use options_protocol::oracle;
use options_protocol::collateral;
use options_protocol::series;
use options_protocol::margin;
use options_protocol::events;
/// Errors
const E_SERIES_NOT_FOUND: u64 = 1;
const E_EXPIRED: u64 = 2;
const E_NOT_EXPIRED: u64 = 3;
const E_INSUFFICIENT_COLLATERAL: u64 = 4;
const E_NOT_POSITION_OWNER: u64 = 5;
const E_INVALID_AMOUNT: u64 = 6;
/// Protocol configuration stored at module address
struct ProtocolConfig has key {
admin: address,
fee_bps: u64, // Trading fee in basis points
liquidation_threshold: u64, // 80% = 8000
min_collateral_ratio: u64, // 125% = 12500
paused: bool,
}
/// Global state tracking all series
struct ProtocolState has key {
next_series_id: u256,
total_open_interest: u64,
total_volume: u64,
}
// ============ Writer Functions (Sell Options) ============
/// Write (sell) options - requires collateral
public entry fun write_options<CollateralCoin>(
writer: &signer,
series_id: SeriesId,
amount: u64,
collateral: Coin<CollateralCoin>,
) acquires ProtocolConfig, ProtocolState {
let writer_addr = signer::address_of(writer);
// Validate series exists and not expired
let series = series::get_series(series_id);
assert!(timestamp::now_seconds() < series.expiry, E_EXPIRED);
// Calculate required collateral
let spot_price = oracle::get_spot_price(series.underlying);
let required = margin::calculate_writer_collateral(
&series,
amount,
spot_price
);
assert!(coin::value(&collateral) >= required, E_INSUFFICIENT_COLLATERAL);
// Lock collateral in vault
collateral::deposit(writer_addr, series_id, collateral);
// Create short position
let position = ShortPosition {
series_id,
amount,
collateral_locked: coin::value(&collateral),
entry_price: 0, // Set when matched
created_at: timestamp::now_seconds(),
};
// Store position under writer's account
move_to(writer, position);
// Emit event
events::emit_position_opened(writer_addr, series_id, amount, false);
}
// ============ Settlement Functions ============
/// Settle position at expiry (European style)
public entry fun settle<CollateralCoin>(
holder: &signer,
position: LongPosition,
) acquires ProtocolConfig {
let holder_addr = signer::address_of(holder);
let series = series::get_series(position.series_id);
// Must be at or after expiry
assert!(timestamp::now_seconds() >= series.expiry, E_NOT_EXPIRED);
// Get settlement price
let settlement_price = oracle::get_settlement_price(
series.underlying,
series.expiry
);
// Calculate payout
let payout = calculate_payout(&series, settlement_price, position.amount);
// Consume position resource (Move guarantees single settlement)
let LongPosition { series_id: _, amount: _, entry_price: _, created_at: _ } = position;
// Transfer payout from collateral vault
if (payout > 0) {
let coins = collateral::withdraw_for_settlement<CollateralCoin>(
position.series_id,
payout
);
coin::deposit(holder_addr, coins);
}
events::emit_position_settled(holder_addr, position.series_id, payout);
}
/// Calculate option payout at settlement
fun calculate_payout(
series: &OptionSeries,
settlement_price: u64,
amount: u64
): u64 {
let strike = series.strike;
if (series.option_type == types::CALL) {
// Call: max(0, spot - strike)
if (settlement_price > strike) {
((settlement_price - strike) * amount) / PRECISION
} else {
0
}
} else {
// Put: max(0, strike - spot)
if (strike > settlement_price) {
((strike - settlement_price) * amount) / PRECISION
} else {
0
}
}
}
// ============ View Functions ============
#[view]
public fun get_series_info(series_id: SeriesId): OptionSeries {
series::get_series(series_id)
}
#[view]
public fun get_required_collateral(
series_id: SeriesId,
amount: u64
): u64 {
let series = series::get_series(series_id);
let spot_price = oracle::get_spot_price(series.underlying);
margin::calculate_writer_collateral(&series, amount, spot_price)
}
#[view]
public fun calculate_theoretical_price(
series_id: SeriesId
): u64 {
let series = series::get_series(series_id);
let spot = oracle::get_spot_price(series.underlying);
let vol = oracle::get_volatility(series.underlying);
let time_to_expiry = series.expiry - timestamp::now_seconds();
pricing::black_scholes(
spot,
series.strike,
time_to_expiry,
vol,
series.option_type
)
}
}The Gap: Implied volatility (IV) is critical for options pricing, but no oracle currently provides on-chain IV for Aptos.
Solutions (in order of feasibility for MVP):
Use realized volatility as a proxy. Simpler but less accurate.
module options_protocol::oracle {
use pyth::pyth;
use pyth::price_identifier;
/// Calculate 30-day historical volatility from Pyth price feeds
/// Store rolling window of prices, compute std dev
struct VolatilityState has key {
prices: vector<PricePoint>, // Last 30 daily prices
last_update: u64,
}
struct PricePoint has store, drop {
price: u64,
timestamp: u64,
}
/// Get current spot price from Pyth
public fun get_spot_price(underlying: String): u64 {
let price_id = get_price_id(underlying);
let price = pyth::get_price(price_id);
// Convert Pyth price to our format (6 decimals)
normalize_pyth_price(price)
}
/// Calculate historical volatility (annualized)
public fun get_volatility(underlying: String): u64 acquires VolatilityState {
let state = borrow_global<VolatilityState>(@options_protocol);
// Calculate daily returns
let returns = calculate_log_returns(&state.prices);
// Standard deviation of returns
let daily_vol = std_deviation(&returns);
// Annualize: daily_vol * sqrt(365)
(daily_vol * 191) / 10 // sqrt(365) ≈ 19.1
}
/// Get settlement price (uses Pyth with specific timestamp)
public fun get_settlement_price(underlying: String, expiry: u64): u64 {
// For settlement, we need price at exact expiry time
// Pyth provides historical prices
let price_id = get_price_id(underlying);
pyth::get_price_at_timestamp(price_id, expiry)
}
}Pros: Simple, no external dependencies beyond Pyth Cons: Historical vol ≠ implied vol, mispricing vs market
Run an off-chain service that calculates IV from Deribit/CEX data, submits on-chain.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Deribit API │────▶│ IV Calculator │────▶│ Aptos Oracle │
│ (options data) │ │ (off-chain) │ │ Contract │
└─────────────────┘ └─────────────────┘ └─────────────────┘
/// Admin-controlled IV oracle (trusted feed)
struct IVOracle has key {
admin: address,
iv_surface: Table<IVKey, u64>, // strike/expiry -> IV
last_update: u64,
}
struct IVKey has copy, drop, store {
underlying: String,
strike: u64,
expiry: u64,
}
/// Only admin can update IV (from off-chain calculation)
public entry fun update_iv(
admin: &signer,
underlying: String,
strikes: vector<u64>,
expiries: vector<u64>,
ivs: vector<u64>,
) acquires IVOracle {
let oracle = borrow_global_mut<IVOracle>(@options_protocol);
assert!(signer::address_of(admin) == oracle.admin, E_NOT_ADMIN);
// Update IV surface
let i = 0;
while (i < vector::length(&strikes)) {
let key = IVKey {
underlying,
strike: *vector::borrow(&strikes, i),
expiry: *vector::borrow(&expiries, i),
};
table::upsert(&mut oracle.iv_surface, key, *vector::borrow(&ivs, i));
i = i + 1;
};
oracle.last_update = timestamp::now_seconds();
}Chainlink is launching realized volatility feeds. When available:
- 24h, 7d, 30d rolling realized volatility
- More decentralized than custom oracle
- Still not IV, but better than nothing
Phase 1 (MVP): Historical volatility from Pyth price feeds Phase 2: Off-chain IV calculator submitting to trusted oracle Phase 3: Decentralized IV oracle (if ecosystem develops)
Hyperion is Aptos's leading DEX with orderbook functionality. We'll create options markets on Hyperion rather than building custom orderbook infrastructure.
┌─────────────────────────────────────────────────────────────┐
│ Options Protocol │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ Series │ │ Position │ │
│ │ Manager │ │ Tracker │ │
│ └──────┬──────┘ └────────▲────────┘ │
│ │ │ │
│ │ Create Market │ Record │
│ ▼ │ Position │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Hyperion Adapter │ │
│ │ - Market creation │ │
│ │ - Order routing │ │
│ │ - Fill callbacks │ │
│ └──────────────────────────┬───────────────────────────┘ │
└─────────────────────────────┼───────────────────────────────┘
│
▼
┌─────────────────┐
│ HYPERION │
│ ORDERBOOK │
│ │
│ BTC-CALL-30K │
│ ETH-PUT-2000 │
│ ... │
└─────────────────┘
For each option series, create a Hyperion market:
module options_protocol::orderbook {
use hyperion::market;
use hyperion::order;
/// Create Hyperion market for an option series
public fun create_options_market(
admin: &signer,
series_id: SeriesId,
) {
let series = series::get_series(series_id);
// Market name: "BTC-30000-CALL-20250328"
let market_name = format_market_name(&series);
// Options trade in USDC (premium currency)
// The "base" is the option contract itself
hyperion::create_market(
admin,
market_name,
@options_protocol, // Option token address
@usdc, // Quote currency
6, // Price decimals
8, // Size decimals
);
}
/// Place limit order for options
public entry fun place_order(
trader: &signer,
series_id: SeriesId,
side: u8, // BID or ASK
price: u64, // Premium in USDC (6 decimals)
size: u64, // Number of contracts (8 decimals)
) {
let market_id = get_market_id(series_id);
if (side == ASK) {
// Selling options requires collateral check
let required = margin::calculate_writer_collateral(...);
assert!(collateral::available(trader) >= required, E_INSUFFICIENT_COLLATERAL);
collateral::reserve(trader, required);
}
hyperion::place_limit_order(
trader,
market_id,
side,
price,
size,
);
}
/// Handle order fill (callback from Hyperion)
public fun on_fill(
market_id: u64,
maker: address,
taker: address,
price: u64,
size: u64,
maker_side: u8,
) {
let series_id = get_series_from_market(market_id);
// Determine buyer/seller
let (buyer, seller) = if (maker_side == ASK) {
(taker, maker)
} else {
(maker, taker)
};
// Create positions
create_long_position(buyer, series_id, size, price);
create_short_position(seller, series_id, size, price);
// Lock seller's collateral
collateral::lock(seller, series_id, size);
// Transfer premium from buyer to seller
let premium = (price * size) / PRECISION;
transfer_premium(buyer, seller, premium);
}
}If Hyperion integration proves difficult, fallback to simple on-chain orderbook:
struct OrderBook has key {
bids: vector<Order>, // Sorted by price desc
asks: vector<Order>, // Sorted by price asc
}
struct Order has store, drop {
trader: address,
price: u64,
size: u64,
timestamp: u64,
}Tradeoffs:
- Simpler to implement
- Less capital efficient (no integration with Hyperion liquidity)
- Higher gas for matching
- Recommendation: Try Hyperion first, fallback if needed
For European options, Black-Scholes provides theoretical pricing:
module options_protocol::pricing {
/// Fixed-point precision (6 decimals)
const PRECISION: u64 = 1_000_000;
/// Black-Scholes call price
/// C = S*N(d1) - K*e^(-rT)*N(d2)
///
/// Where:
/// d1 = (ln(S/K) + (r + σ²/2)T) / (σ√T)
/// d2 = d1 - σ√T
public fun black_scholes_call(
spot: u64, // Current price (6 dec)
strike: u64, // Strike price (6 dec)
time_to_expiry: u64, // Seconds until expiry
volatility: u64, // Annualized vol (6 dec, e.g., 500000 = 50%)
risk_free_rate: u64, // Annual rate (6 dec, e.g., 50000 = 5%)
): u64 {
// Convert time to years (6 decimal precision)
let t = (time_to_expiry * PRECISION) / SECONDS_PER_YEAR;
// Calculate d1 and d2
let (d1, d2) = calculate_d1_d2(spot, strike, t, volatility, risk_free_rate);
// N(d1) and N(d2) - cumulative normal distribution
let n_d1 = cumulative_normal(d1);
let n_d2 = cumulative_normal(d2);
// e^(-rT)
let discount = exp_negative((risk_free_rate * t) / PRECISION);
// C = S*N(d1) - K*e^(-rT)*N(d2)
let term1 = (spot * n_d1) / PRECISION;
let term2 = (strike * discount * n_d2) / (PRECISION * PRECISION);
if (term1 > term2) {
term1 - term2
} else {
0
}
}
/// Black-Scholes put price using put-call parity
/// P = C - S + K*e^(-rT)
public fun black_scholes_put(
spot: u64,
strike: u64,
time_to_expiry: u64,
volatility: u64,
risk_free_rate: u64,
): u64 {
let call_price = black_scholes_call(spot, strike, time_to_expiry, volatility, risk_free_rate);
let t = (time_to_expiry * PRECISION) / SECONDS_PER_YEAR;
let discount = exp_negative((risk_free_rate * t) / PRECISION);
let pv_strike = (strike * discount) / PRECISION;
// P = C - S + K*e^(-rT)
call_price + pv_strike - spot
}
/// Approximate cumulative normal distribution
/// Using Abramowitz and Stegun approximation
fun cumulative_normal(x: i64): u64 {
// Implementation using polynomial approximation
// Accurate to ~10^-7
// ...
}
/// Calculate Greeks for risk management
public fun calculate_delta(
spot: u64,
strike: u64,
time_to_expiry: u64,
volatility: u64,
option_type: u8,
): i64 {
let (d1, _) = calculate_d1_d2(spot, strike, time_to_expiry, volatility, 0);
let n_d1 = cumulative_normal(d1);
if (option_type == CALL) {
(n_d1 as i64)
} else {
(n_d1 as i64) - (PRECISION as i64)
}
}
}- Risk-Free Rate: Use USDC lending rate from Aries as proxy
- Volatility: Historical vol initially, IV oracle later
- Gas Optimization: Pre-compute common exp/log values
- Accuracy vs Gas: Fixed-point math limits precision
BEFORE EXPIRY AT/AFTER EXPIRY
┌─────────┐ ┌─────────────────┐
│ LONG │ ◄── Can trade ──────────────► │ LONG holder │
│ POSITION│ (buy/sell) │ calls settle() │
└─────────┘ └────────┬────────┘
│
┌────────▼────────┐
┌─────────┐ │ Oracle provides │
│ SHORT │ ◄── Collateral locked ──────► │ settlement price│
│ POSITION│ └────────┬────────┘
└─────────┘ │
┌────────▼────────┐
│ Calculate payout│
│ ITM: holder paid│
│ OTM: writer keeps│
│ collateral │
└────────┬────────┘
│
┌────────▼────────┐
│ Positions closed│
│ Resources burned│
└─────────────────┘
module options_protocol::settlement {
/// Settlement window: 24 hours after expiry
const SETTLEMENT_WINDOW: u64 = 86400;
/// Settle a long position
public entry fun settle_long<CollateralCoin>(
holder: &signer,
) acquires LongPosition {
let holder_addr = signer::address_of(holder);
// Move position out (consumed after this)
let position = move_from<LongPosition>(holder_addr);
let series = series::get_series(position.series_id);
// Validate timing
let now = timestamp::now_seconds();
assert!(now >= series.expiry, E_NOT_EXPIRED);
assert!(now <= series.expiry + SETTLEMENT_WINDOW, E_SETTLEMENT_EXPIRED);
// Get settlement price
let settlement_price = oracle::get_settlement_price(
series.underlying,
series.expiry
);
// Calculate payout
let payout = calculate_payout(&series, settlement_price, position.amount);
// Position resource is consumed here - cannot settle twice!
let LongPosition {
series_id: _,
amount: _,
entry_price: _,
created_at: _
} = position;
// Distribute payout
if (payout > 0) {
let coins = collateral::withdraw_for_settlement<CollateralCoin>(
series_id,
payout
);
coin::deposit(holder_addr, coins);
}
events::emit_settlement(holder_addr, series_id, settlement_price, payout);
}
/// Settle a short position (writer claims remaining collateral)
public entry fun settle_short<CollateralCoin>(
writer: &signer,
) acquires ShortPosition {
let writer_addr = signer::address_of(writer);
let position = move_from<ShortPosition>(writer_addr);
let series = series::get_series(position.series_id);
// Validate timing
assert!(timestamp::now_seconds() >= series.expiry, E_NOT_EXPIRED);
let settlement_price = oracle::get_settlement_price(
series.underlying,
series.expiry
);
// Calculate what writer owes
let liability = calculate_payout(&series, settlement_price, position.amount);
// Return excess collateral
let return_amount = if (position.collateral_locked > liability) {
position.collateral_locked - liability
} else {
0
};
// Consume position
let ShortPosition {
series_id: _,
amount: _,
collateral_locked: _,
entry_price: _,
created_at: _
} = position;
if (return_amount > 0) {
let coins = collateral::release<CollateralCoin>(writer_addr, return_amount);
coin::deposit(writer_addr, coins);
}
}
/// Auto-settle expired positions (can be called by anyone, e.g., keeper bot)
public entry fun auto_settle_batch<CollateralCoin>(
series_id: SeriesId,
holders: vector<address>,
) {
let i = 0;
while (i < vector::length(&holders)) {
let holder = *vector::borrow(&holders, i);
// Settle if position exists
if (exists<LongPosition>(holder)) {
settle_for_holder<CollateralCoin>(holder);
}
i = i + 1;
}
}
}For MVP, use simple full-collateral model:
| Position Type | Collateral Required |
|---|---|
| Short Call | max(strike, spot) * contracts |
| Short Put | strike * contracts |
| Long | Premium only (paid upfront) |
module options_protocol::margin {
/// Calculate collateral required to write options
public fun calculate_writer_collateral(
series: &OptionSeries,
amount: u64,
spot_price: u64,
): u64 {
if (series.option_type == CALL) {
// Call writer: obligated to deliver underlying at strike
// Max loss is unlimited, but practical collateral:
// max(spot, strike) * 1.25 (safety buffer)
let base = if (spot_price > series.strike) {
spot_price
} else {
series.strike
};
(base * amount * 125) / (PRECISION * 100)
} else {
// Put writer: obligated to buy at strike
// Max loss = strike (if underlying goes to 0)
(series.strike * amount * 125) / (PRECISION * 100)
}
}
/// Check if position is undercollateralized
public fun is_liquidatable(
position: &ShortPosition,
spot_price: u64,
): bool {
let series = series::get_series(position.series_id);
let required = calculate_writer_collateral(&series, position.amount, spot_price);
let threshold = (required * LIQUIDATION_THRESHOLD) / 10000; // 80%
position.collateral_locked < threshold
}
}Portfolio margin nets positions for capital efficiency:
Example:
- Long 1 BTC Call @ 30K
- Short 1 BTC Call @ 35K
= Bull Call Spread
Without portfolio margin: Full collateral for short leg
With portfolio margin: Max loss = $5K (spread width)
This requires:
- Position tracking by account
- Risk engine calculating net Greeks
- More complex liquidation logic
module options_protocol::liquidation {
/// Liquidate undercollateralized position
public entry fun liquidate<CollateralCoin>(
liquidator: &signer,
position_owner: address,
) acquires ShortPosition {
let position = borrow_global<ShortPosition>(position_owner);
let spot = oracle::get_spot_price(series::get_underlying(position.series_id));
// Verify position is liquidatable
assert!(margin::is_liquidatable(position, spot), E_NOT_LIQUIDATABLE);
// Calculate liquidation amounts
let debt = margin::calculate_writer_collateral(
&series::get_series(position.series_id),
position.amount,
spot
);
// Liquidator receives bonus (5% of collateral)
let bonus = (position.collateral_locked * 500) / 10000;
let liquidator_reward = bonus;
// Close position
let position = move_from<ShortPosition>(position_owner);
let ShortPosition {
series_id,
amount,
collateral_locked,
entry_price: _,
created_at: _
} = position;
// Pay liquidator
let reward_coins = collateral::withdraw<CollateralCoin>(
position_owner,
liquidator_reward
);
coin::deposit(signer::address_of(liquidator), reward_coins);
// Remaining collateral goes to insurance fund / covers debt
let remaining = collateral_locked - liquidator_reward;
collateral::transfer_to_insurance<CollateralCoin>(remaining);
events::emit_liquidation(position_owner, series_id, amount, liquidator_reward);
}
}/// Emergency pause
public entry fun pause(admin: &signer) acquires ProtocolConfig {
let config = borrow_global_mut<ProtocolConfig>(@options_protocol);
assert!(signer::address_of(admin) == config.admin, E_NOT_ADMIN);
config.paused = true;
}
/// Max position size per account
const MAX_POSITION_SIZE: u64 = 1000_00000000; // 1000 contracts
/// Max open interest per series
const MAX_OPEN_INTEREST: u64 = 100000_00000000; // 100K contractsGoal: Basic options that can be written, traded, and settled
Deliverables:
- Core type definitions
- Option series management
- Pyth oracle integration (spot prices)
- Historical volatility calculation
- Full-collateral margin system
- European settlement logic
- Basic frontend (write, settle)
Out of Scope:
- Orderbook integration
- Greeks display
- Advanced margin
Milestones:
- Week 2: Types, errors, config modules complete
- Week 4: Oracle adapter working with Pyth testnet
- Week 6: Write and settle functions working on testnet
- Week 8: Basic frontend deployed
Goal: Real price discovery through Hyperion
Deliverables:
- Hyperion market creation for options
- Order placement/cancellation
- Fill handling and position creation
- Collateral locking on order placement
- Premium transfers
Milestones:
- Week 10: Hyperion testnet integration
- Week 12: End-to-end trade flow working
- Week 14: Frontend orderbook UI
- Week 16: Testnet launch with community testing
Goal: Mainnet-ready protocol
Deliverables:
- Security audit
- Liquidation engine
- Keeper bot for settlements
- IV oracle (off-chain feed)
- Frontend polish
- Documentation
Milestones:
- Week 18: Audit started
- Week 20: Audit findings fixed
- Week 22: Mainnet soft launch (limited series)
- Week 24: Full mainnet launch
Phase 4: Capital Efficiency
- Portfolio margin
- Cross-collateral
- Partial collateralization
Phase 5: Product Expansion
- American options
- Weekly expiries
- More underlyings (SOL, etc.)
- Structured products
| Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|
| Pyth oracle manipulation | High | Low | Use TWAP, multiple sources |
| Hyperion integration blocked | Medium | Medium | Fallback to custom orderbook |
| IV oracle inaccuracy | Medium | High | Start with historical vol, iterate |
| Smart contract bug | Critical | Medium | Audit, formal verification |
| Low liquidity | High | High | Market maker partnerships, incentives |
| Regulatory issues | High | Medium | Focus on non-US initially |
- Internal review: Code review before each phase
- Formal verification: Use Move Prover for critical invariants
- External audit: Budget $30-50K for professional audit
- Bug bounty: Launch with Immunefi program
Unit Tests (Move)
├── types_test.move
├── pricing_test.move
├── margin_test.move
├── settlement_test.move
└── integration_test.move
Integration Tests
├── Full trade lifecycle
├── Settlement at various prices
├── Liquidation scenarios
└── Oracle failure handling
Testnet Testing
├── Community beta
├── Stress testing
└── Edge case exploration
| Resource | Link |
|---|---|
| Pyth Aptos | https://docs.pyth.network/price-feeds/contract-addresses/aptos |
| Hyperion | https://hyperion.xyz |
| Aries Markets | https://ariesmarkets.xyz |
| Aptos Explorer | https://explorer.aptoslabs.com |
| Protocol | Chain | Architecture | Study For |
|---|---|---|---|
| Derive V2 | Ethereum L2 | Hybrid orderbook | Capital efficiency |
| Aevo | OP Stack | Off-chain matching | Performance |
| Lyra V1 | Optimism | AMM | Simpler model |
| Zeta | Solana | Orderbook | Similar performance profile |
- Hyperion: Discord for integration support
- Pyth: Apply for price feed sponsorship
- Aptos Foundation: Grant application for audit funding