← Back to options

Documentation

Passey Options lets you create simple physical options on HyperEVM and BSC: get paid upfront to buy low or sell high, with collateral held onchain until expiry.

Intro

Passey uses physical options. A buy low position is a secured put: you commit to buy the asset at a chosen price and receive premium upfront. A sell high position is a covered call: you commit to sell the asset at a chosen price and receive premium upfront.

The app is designed for users who want to earn yield, set target entry prices, or set target exit prices without relying on an offchain custodian.

How it works

Orders are signed offchain with EIP-712 and filled onchain. When an order is filled, the option book creates a new option clone for that trade, transfers premium from buyer to seller, escrows seller collateral, and mints two receipt NFTs: an even token ID for the buyer side and an odd token ID for the seller side.

  1. Choose the market, side, strike, expiry, size, and premium.
  2. The seller posts collateral: base token for calls, quote token for puts.
  3. Premium is paid in the collateral token, quoted per base unit.
  4. The buyer can exercise during the one-hour exercise window after expiry.
  5. If the option is not exercised, anyone can trigger withdrawal and collateral returns to the seller.
  6. Transferring a receipt NFT updates the matching buyer or seller role on the option contract.

Contract addresses

Deployed option book and implementation addresses by network. Each option trade creates its own clone from the implementation contract listed below.

Solidity audit report

Review date: 2026-06-25

Preamble

This report is a focused source-level review of the option book and option clone contracts. No critical issues were identified in the signed-order, fund-flow, and receipt NFT synchronization model. After expiry and the one-hour exercise window, anyone can call withdraw(), but collateral is always sent to the stored seller rather than to msg.sender. This lets third parties, including Passey, clean up expired options and pay gas on behalf of users.

Users should still verify deployed bytecode and source on the relevant block explorer and review the contracts themselves before trading.

Scope

  • packages/foundry/contracts/PhysicalOption.sol
  • packages/foundry/contracts/PhysicalOptionBook.sol

Review focus:

  • New options respect the signed order.
  • Funds follow the intended buyer/seller exercise and withdrawal rules.
  • Receipt NFT ownership and option buyer/seller state remain consistent.

Executive summary

The system models physically settled options with a signed maker order, a factory/book that deploys option clones, and two receipt NFTs per option: an even token ID for the buyer side and an odd token ID for the seller side.

The signed-order path matches the Solidity order structure. PhysicalOptionBook verifies the maker signature, derives buyer and seller from isMakerLong, caps cumulative fills, transfers premium and collateral, initializes a clone, and mints side-specific receipt NFTs.

The main remaining risk area is rounding behavior in collateral and settlement math. That should be treated as an explicit protocol choice and covered with tests for small or non-divisible fills.

Architecture reviewed

PhysicalOptionBook verifies signed EIP-712 orders, tracks partial fills by order hash, transfers premium from buyer to seller, escrows seller collateral, creates a new option clone, and mints buyer and seller receipt NFTs. PhysicalOption stores the buyer, seller, token pair, amount, strike, expiry, premium, option type, collateral amount, and lifecycle flags for one specific option.

Collateral is base token for calls and quote token for puts. Premium is paid in the collateral token, quoted per base unit. Receipt NFT transfers update only the matching buyer or seller role based on even or odd token IDs.

Findings

Medium

Settlement and collateral math rounds down

Impact: Put collateral and call strike payments use floor division. Non-divisible amounts can settle slightly below the signed economic value. Very small put fills can round collateral to zero while premium remains nonzero.

Evidence: collateralAmount = (amount * strike) / 10**baseDecimals and strikePay = (amount * strike) / 10**baseDecimals in PhysicalOptionBook and PhysicalOption.

Recommendation: Treat rounding as an explicit protocol choice. Require exact divisibility for fills or round up where full collateralization is required, and test small or non-divisible amounts.

Low

PhysicalOption.initialize() trusts the caller to enforce order validity

Impact: The option clone validates basic parameters and pulls collateral from msg.sender, but does not verify maker signatures, fill limits, or buyer/seller derivation on its own.

Evidence: PhysicalOption records msg.sender as optionBook during initialize. Signature checks and fill caps live in PhysicalOptionBook.createOption.

Recommendation: Treat PhysicalOption as book-owned implementation logic, not a standalone option factory. Only fill options created through the canonical option book.

Security invariants

Passes

Signed orders are respected

hashOrder covers maker, tokens, option type, maker side, strike, expiry, order expiry, premium, and max fill amount. verifyOrderSignature requires the recovered signer to equal order.maker. createOption derives buyer and seller from isMakerLong and enforces maxMakerTotalAmount through amountFilled.

Passes

Funds follow exercise and withdrawal rules

Premium is paid from buyer to seller at creation. Calls are collateralized in base; puts in quote. Only the buyer can exercise during the one-hour post-expiry window. Anyone can call withdraw after the window, but collateral is sent to the seller. Put exercise sends base to seller and quote collateral to buyer; call exercise sends quote strike to seller and base collateral to buyer.

Passes

Receipt NFT ownership stays consistent with option roles

Each option mints two receipt NFTs: even token IDs for the buyer side and odd token IDs for the seller side. On transfer, only the matching side updates when the previous owner still holds that role, including when both sides are temporarily held by the same address.

Residual note: the fill amount itself is not signed. Partial fills are intentionally bounded by maxMakerTotalAmount.

Positive security checks

  • EIP-712 order type string, Solidity struct, and abi.encode field order are aligned.
  • Initialization rejects zero buyer, zero seller, zero token addresses, zero amount, zero strike, expired options, and zero premium.
  • Options cannot be initialized twice; the implementation constructor marks the template as already initialized.
  • Only the stored buyer can exercise an option.
  • Exercise is only available after expiry and before the one-hour exercise window closes.
  • Exercise and withdrawal are mutually exclusive through exercised and withdrawn state flags.
  • Put exercise transfers base from buyer to seller and quote collateral from the option to buyer.
  • Call exercise transfers quote from buyer to seller and base collateral from the option to buyer.
  • Order signatures are verified with EIP-712 before fills.
  • Order fill amounts are tracked by order hash and cannot exceed maxMakerTotalAmount.
  • Receipt NFT transfers update only the buyer or seller role that matches the token side.

Test coverage reviewed

Foundry tests cover put and call initialization, invalid initialization inputs, double initialization protection, buyer-only exercise, exercise timing, successful put and call settlement, double exercise prevention, withdrawal timing, double withdrawal prevention, clone initialization, signed order verification, EIP-712 hash correctness, signed call order creation, order expiry, premium floor checks, max fill enforcement, partial fills, receipt NFT creation, buyer and seller receipt transfer synchronization, safe transfer behavior, and cases where both roles are temporarily held by the same address.

Residual risk and limitations

This report is a source-level review of the listed contracts and tests, not a formal audit. It does not include fuzzing, invariant testing, mainnet transaction monitoring, economic stress testing, oracle analysis, or a review of offchain orderbook infrastructure. Users should verify deployed bytecode and source on the relevant block explorer before trading.

The main remaining concern is economic precision: rounding should be explicitly understood for small or non-divisible fills so market makers and users know the exact settlement behavior.