Architecture
OddMaki's smart contract architecture — EIP-2535 Diamond proxy, facets, and modular upgrade mechanism.
OddMaki is deployed as a single EIP-2535 Diamond proxy on Base. All protocol functionality — venue management, market creation, orderbook, matching, resolution, and vault custody — lives in separate facets that share state through isolated storage libraries.
Diamond Proxy (EIP-2535)
The Diamond pattern splits a monolithic contract into multiple implementation contracts (facets) behind a single proxy address. When a call arrives, the fallback() function looks up which facet implements the called selector, then delegatecalls into it. All facets share the same storage space.
Why EIP-2535:
- 65+ external functions across 12 business domains — far beyond the 24KB contract size limit
- Individual facets can be upgraded without touching unrelated logic
- New features can be added by deploying a single new facet
Facets
The protocol deploys 15 facets: 3 core Diamond infrastructure + 12 business logic.
| Facet | Domain | Key Functions |
|---|---|---|
| DiamondCutFacet | Upgrades | diamondCut() |
| DiamondLoupeFacet | Introspection | facets(), facetAddress() |
| OwnershipFacet | Ownership | transferOwnership(), owner() |
| VenueFacet | Venue config | createVenue(), updateVenueFees(), pauseVenue() |
| MarketsFacet | Market creation | createMarket(), addMarket(), addPlaceholderMarkets() |
| MarketGroupFacet | Market groups | createMarketGroup(), activateMarketGroup() |
| LimitOrdersFacet | Limit orders | placeOrder(), cancelOrder() |
| MatchingFacet | Order matching | matchOrders() |
| MarketOrdersFacet | Market orders | placeMarketOrder() (FOK/FAK) |
| OrderBookFacet | Read-only queries | getTopOfBook(), getMarkPrice() |
| VaultFacet | Token custody | splitPosition(), mergePositions() |
| ResolutionFacet | UMA resolution | assertMarketOutcome(), settleAssertion(), reportResolution() |
| NegRiskFacet | Position conversion | convertPositions() |
| ProtocolFacet | Protocol config | setProtocolTreasury(), setUmaOracle() |
| ERC1155ReceiverFacet | Token receiving | onERC1155Received() |
Upgrade Mechanism
The Diamond owner calls diamondCut() with an array of FacetCut structs:
| Action | What It Does |
|---|---|
| Add | Map new function selectors to a new facet |
| Replace | Point existing selectors to an updated facet |
| Remove | Delete selector mappings (facetAddress must be address(0)) |
An optional initialization hook (_init + _calldata) runs after applying cuts — useful for one-time migration logic.
Storage Model
All state uses namespaced storage — each domain gets its own struct at a unique keccak256 slot. This prevents collision between facets.
| Storage Library | Domain |
|---|---|
LibVenueStorage | Venue configs, ID counter |
LibMarketRegistryStorage | Market lifecycle, ID counter |
LibMarketTradingStorage | Position IDs, volume, active flag |
LibMarketOracleStorage | CTF condition, UMA params |
LibOrderStorage | Order structs, ID counter |
LibOrderBookStorage | Tick levels, top of book |
LibFillStorage | Fill records |
LibMarketGroupStorage | Group data |
LibResolutionStorage | UMA assertion data |
LibNegRiskStorage | Wrapped collateral mappings |
External Dependencies
| Dependency | Purpose |
|---|---|
| Gnosis CTF | ERC-1155 outcome tokens — split, merge, redeem positions |
| UMA Optimistic Oracle V3 | Decentralized dispute resolution for market outcomes |
| OpenZeppelin | ReentrancyGuard, IERC20, IERC1155 |
Security Model
| Tier | Who | Can Do |
|---|---|---|
| Diamond Owner | Protocol admin | Upgrade facets, set treasury/oracle/CTF |
| Venue Operator | msg.sender of createVenue | Update venue config, pause/unpause (immutable role) |
| External Access Control | Optional per-venue contracts | Gate trading and market creation |
| Participants | Anyone | Place orders, match, assert outcomes |
There is no global protocol pause. Pausing is scoped to individual venues.
What's Next
- Orderbook → — how the on-chain CLOB works
- Token Mechanics → — outcome tokens and the CTF