This document provides a high-level yet technically accurate overview of the otX.markets protocol as implemented in the v2 smart-contracts. It explains core on-chain components, roles, phases, trading flows, settlement, default handling, and fees. It is written for technically curious users, integrators, and auditors.
AccessControlV3: Central role registry.
Roles: ADMIN_ROLE, MARKET_MANAGER_ROLE.
Admins grant/revoke roles, create markets, and set module addresses.
Market Managers update market configs and transition phases.
MarketRegistryV3: Market lifecycle and configuration.
Phases: Trading → WindingDown → Settlement → Closure.
Stores per-market metadata: name, points identifier, TGE token, conversion rate, settlement window, decimals, phase timestamps.
Integrates with OrderBookV3 for a constant-time guard that prevents entering settlement while there are active open offers.
OrderBookV3: On-chain order book for Points trading with partial fills and batch fills (market orders).
Offer types: SellPoints and BuyPoints.
Full collateralization: both makers and takers lock USDC equal to value traded (points × pricePerPoint).
States: Open, Partial, Filled, Cancelled, Settled.
Tracks fills independently; one offer can service many takers.
Batch fill across multiple offers in one market for a “market order” experience.
EscrowVaultV3: Secure custody for USDC collateral and TGE tokens.
Holds real USDC and deposited TGE tokens; not notional balances.
Authorizes specific modules (OrderBook/SettlementEngine) to move funds.
Supports EIP-2612 permit and Uniswap Permit2 for gas-efficient collateral locking.
Handles fee collection and forwards fees to a treasury address.
FeeManagerV3: Centralized fee configuration and accounting.
Fee types (basis points, defaults): CancelOffer 0.5%, Settlement 2.5%, BuyToken 2.0%, DefaultOrder 0.5%.
Enforces caps and min amounts; exposes pure calculation helpers for previews.
Records aggregate fee totals and emits events for analytics.
FeeTreasuryV3: Receives USDC fees, controlled withdrawals by Admin/Market Manager.
SettlementEngineV3: Settlement, TGE claims, and default handling.
Seller-side settlement with batched trade settlement.
TGE token deposit per market and per seller.
Merkle-based buyer TGE claims during Settlement.
Default processing transfers compensation from seller’s collateral to buyer if settlement window ends without seller settlement.
OTXPresale (separate subsystem): Presale contract for OTX with Merkle allowlists and immediate treasury forwarding of USDT0. Not part of trading lifecycle, documented here for completeness.
Admins: bootstrap the system, set addresses, manage fees/treasury, and create markets.
Market Managers: drive market lifecycle (wind down, start settlement, set merkle roots, process defaults, close market). They cannot seize user funds arbitrarily; they can only execute lifecycle transitions and default resolution constrained by contract rules.
Users: place and fill offers (as maker or taker), deposit collateral via EscrowVaultV3, and later either settle trades (sellers) or claim TGE tokens (buyers) with a Merkle proof.
Collateral token: USDC (6 decimals). The protocol uses strict, real USDC custody in EscrowVaultV3.
TGE token: arbitrary ERC-20 per market, set when starting Settlement. Sellers deposit the required TGE to the vault through SettlementEngineV3.
SellPoints (maker sells points):
Maker creates offer with pointsAmount and pricePerPoint. Collateral locked = points × price.
Taker (buyer) fills partially or fully, locking their USDC equal to filled value.
In Settlement phase, seller deposits TGE and settles each fill. Buyer receives TGE (after BuyToken fee) via claim.
BuyPoints (maker buys points):
Maker creates a buy offer, locking USDC upfront.
Taker (seller) fills, locking their own collateral for the fill.
In Settlement, the maker’s locked USDC pays the seller, and seller’s collateral is unlocked upon settlement.
Market Orders (batch fill):
A buyer can fill many offers at once within the same market via fillMultipleOffers, locking total USDC for all fills.
Computes each seller’s proportional guarantee (their locked collateral attributable to the fill).
Applies DefaultOrder fee to the seller’s guarantee, then transfers the net compensation directly from the seller’s collateral to the buyer’s wallet via the vault.
Refunds the buyer’s payment for that fill.
Marks the trade as settled/defaulted to prevent double handling.
CancelOffer (0.5%): Applied on remaining collateral when a maker cancels an active offer.
Settlement (2.5%): Applied on the buyer payment routed to the seller at settlement.
BuyToken (2.0%): Applied on TGE amount when the buyer claims TGE with a Merkle proof.
DefaultOrder (0.5%): Applied on the seller guarantee when a trade is defaulted after the settlement window.
Reentrancy protection on all fund-moving entry points (vault, order book, settlement engine).
Role-gated lifecycle operations (Admin/Market Manager).
Phase checks concentrated in MarketRegistryV3 and referenced by other modules.
Constant-time guard to ensure no active offers remain before moving to Settlement.
Permit and Permit2 support for safe/efficient collateral locking without prior approvals.
Merkle proofs validate buyer eligibility and amounts for TGE claims.
Order lifecycle: OfferCreated, FillCreated, OfferCompleted, OfferCancelled, OfferSettled, batch market-order events.
Market lifecycle: MarketCreated, MarketWindDownStarted, SettlementPhaseStarted, MarketClosed.
Settlement/TGE: TradeSettled, TGEClaimed, MerkleRootSet, SettlementDefaulted.
Vault: collateral/TGE deposit/transfer events; payout settlement events.
Fees: FeeCollected, FeesWithdrawn, treasury events.
Indexer should model offers, fills, and their states; compute remaining points; and track per-market phases.
Trade IDs are ABI-encoded (offerId, fillId) and hashed on-chain for uniqueness in settlement.
For claims UX, frontends should display fee previews using FeeManagerV3.calculateFee helpers via exposed preview functions in SettlementEngineV3 and OrderBookV3.
OTXPresale is a separate presale mechanism using USDT0 and Merkle allowlists, with immediate forwarding to a treasury. It does not interact with the trading lifecycle above.
Points: off-chain program units being traded pre-TGE.
TGE: Token Generation Event token distributed at Settlement.
Guarantee/Collateral: USDC locked to secure performance and payment.
Conversion Rate: multiplier from points to TGE token units.
Network: HyperEVM Testnet (chainId 998)
Explorer: https://testnet.purrsec.com/
Addresses (from scripts/verify-addresses.ts):
AccessControlV3: 0x9845f98084C4329b6b25815E0286EAf13e86F707
FeeManagerV3: 0x5f9a1dE9e8D87F5D9779aD5f9CB89188c6ae33fC
EscrowVaultV3: 0x5aFc8db148c0Ed8Ba9a3E1A029308549a285df95
MarketRegistryV3: 0xAb0246509a217926C6fB05928B4A04F300947993
OrderBookV3: 0xC1151978495e8C43B109209b91B15d2eD4666000
SettlementEngineV3: 0x0cE6DBDDD9E8C78e8f85ED9a8DD1Cee34EDE42cf
Market (MarketRegistryV3.Market): name, pointsIdentifier, currentPhase, settlementWindow, settlementStartTime, tgeToken, conversionRate, tgeDecimals, isActive, createdAt.
Offer (OrderBookV3.Offer): marketId, maker, offerType, pointsAmount, pricePerPoint, collateralLocked, pointsFilled, pointsSettled, status, createdAt, fillCount.
Fill (OrderBookV3.Fill): offerId, taker, pointsAmount, collateralAmount, createdAt.
MarketOrder (batch fill): taker, marketId, totalPointsBought, totalCollateralSpent, arrays offerIds, amounts.
Collateral to lock: pointsAmount × pricePerPoint (USDC, 6 decimals).
TGE required (seller): fill.pointsAmount × market.conversionRate (TGE token units).
Settlement fee base: buyer’s payment amount transferred to seller per fill.
BuyToken fee base: buyer’s TGE claimAmount during Merkle claim.
Default fee base: seller’s proportional guarantee when defaulting a fill.
Fee caps: ≤ 700 bps per type; min amounts enforced; optional max.
Scenario: Maker sells 1,000 points at 0.50 USDC/point. Conversion rate is 3 TGE per point. Buyer fully fills the offer.
Offer create (SellPoints):
Maker collateral locked: 1,000 × 0.50 = 500 USDC
Offer Open
Fill by buyer (full):
Buyer collateral locked: 1,000 × 0.50 = 500 USDC
Offer Filled
Settlement phase starts (Manager sets TGE + conversion + window):
Seller deposits TGE required: 1,000 × 3 = 3,000 TGE
Seller settles trade:
Settlement fee on buyer payment (example 2.5%): 500 × 2.5% = 12.5 USDC
Net payment to seller from buyer’s collateral: 487.5 USDC
Seller’s own collateral unlocked: 500 USDC
Vault nets the transfer to seller in one go: 987.5 USDC total to seller’s wallet
Offer becomes Settled
Buyer claims TGE using Merkle proof:
BuyToken fee (example 2.0%): 3,000 × 2% = 60 TGE to treasury/collector
Net TGE to buyer: 2,940 TGE
If seller never settles before window ends:
Default fee on seller’s guarantee (0.5% on 500 USDC): 2.5 USDC to treasury
Buyer’s 500 USDC is unlocked/refunded to buyer’s wallet
Net compensation from seller’s collateral to buyer: 497.5 USDC
Trade encoding for settlement: bytes trade = abi.encode(offerId, fillId); tradeHash = keccak256(trade).
Leaf: keccak256(abi.encodePacked(buyer, tradeHash, totalEligibleAmount)).
Guards: trade must be marked settled by seller; valid proof; cumulative claimed ≤ total eligible.
Transfers: BuyToken fee to treasury/collector; net TGE to buyer, both via EscrowVaultV3.
Only authorized modules can call escrow transfers/collections.
Fees route to EscrowVaultV3.feeTreasury() if set; otherwise to FeeManagerV3.feeCollector().
Offer create/fill requires isInTradingPhase(marketId).
Start settlement requires: WindingDown, active, and OrderBookV3.activeOpenOffersCount(marketId) == 0.
Close requires: block.timestamp >= settlementStartTime + settlementWindow and phase Settlement.
Settlement engine checks market in Settlement and that tgeDecimals > 0; prevents double-settlement via settledTrades.
Markets: MarketCreated, MarketWindDownStarted, SettlementPhaseStarted, MarketClosed
Order book: OfferCreated, FillCreated, OfferCompleted, OfferCancelled, OfferPartiallyCleanedForSettlement, OfferSettled, MultipleFillsExecuted, MarketOrderCreated
Settlement: BatchTradesSettled, TradeSettled, SettlementDefaulted, MerkleRootSet, TGEFundsDeposited, TGEClaimed
Escrow: CollateralLocked, CollateralUnlocked, CompensationTransferred, PayoutSettled, TGETokenDeposited, TGETokenTransferred, ModuleAuthorized, FeeTreasurySet
Fees: FeeConfigUpdated, FeeCollected, FeesWithdrawn, FeeCollectorUpdated
Maker collateral at create: offer.pointsAmount × offer.pricePerPoint
Taker collateral at fill: pointsAmount × offer.pricePerPoint
Proportional guarantee used in settlement/defaults:
SellPoints: (fill.pointsAmount × offer.collateralLocked) / offer.pointsAmount
BuyPoints: fill.collateralAmount
Buyer payment to seller for BuyPoints fill: (fill.pointsAmount × offer.collateralLocked) / offer.pointsAmount
Settlement fee: (paymentAmount × rateBps) / 10000
BuyToken fee: (claimAmount × rateBps) / 10000
Default fee: (proportionalGuarantee × rateBps) / 10000