Overview

MarkIt is built on a minimal set of Solidity contracts. Each market is a self-contained unit — a single MarketEngine contract that holds all USDC, all state, and all logic.

Contract Architecture

LpVault (holds aggregated LP capital, owns factory)
    └── MarketFactory (deploys new markets)
            └── MarketEngine (one per market)
                    ├── owns → YES OutcomeToken (ERC-20)
                    └── owns → NO OutcomeToken (ERC-20)

LpVault

The aggregated liquidity layer. LPs deposit USDC into the vault and receive shares priced at NAV. The vault owns the MarketFactory and deploys capital into markets via createAndFundBatch(). After markets resolve, the vault collects capital + fees. LPs withdraw through a FIFO queue that processes as float becomes available. The vault tracks share price, NAV, trailing APR, and deployed capital per market.

MarketEngine

The core per-market contract. Each market is a separate MarketEngine deployment that manages the full lifecycle: LP deposits, position placement, pricing, resolution, redemption, and LP withdrawal. The MarketEngine holds all USDC for its market directly.

OutcomeToken

Standard ERC-20 tokens (18 decimals) representing YES and NO outcomes. Each market deploys its own pair. Only the parent MarketEngine can mint or burn tokens — this is enforced by a one-time engine binding set at deployment.

MarketFactory

A factory that deploys new markets (owned by LpVault). It creates a MarketEngine and its two OutcomeTokens in a single transaction, configuring them with default parameters. The factory tracks all deployed markets.

Supporting Libraries

  • WadMath — 18-decimal fixed-point math (toWad, fromWad, wadMul, wadDiv). Used internally by MarketEngine.

  • MockUSDC — A test ERC-20 with 6 decimals and public minting. Used on testnet only.

Design Principles

One contract per market. No shared state between individual markets. If one market has an issue, others are unaffected. The LpVault aggregates LP capital above the market level but each funded market holds its own USDC independently.

No external dependencies at runtime. MarketEngine does not call oracles, price feeds, or other protocols during normal operation. Resolution is operator-triggered.

Actual balance, not shadow accounting. The solvency invariant checks USDC.balanceOf(address(this)) — the real on-chain balance. This eliminates an entire class of accounting bugs.

Minimal privileged operations. Market creation and resolution flow through the LpVault (which owns the factory). Trading, redemptions, and LP withdrawals at the market level are permissionless. Vault deposits and withdrawal requests are permissionless; only vault admin functions (batch creation, resolution, emergency ops) are restricted.

Security

  • All state-changing functions are protected with reentrancy guards (nonReentrant)

  • LP withdrawals reserve USDC for unredeemed winners before calculating LP payouts

  • renounceOwnership() is disabled to prevent accidental fund lockup

  • The solvency invariant is checked after every state change using the actual USDC balance

  • The dynamic skew cap limits extreme one-sided exposure

  • Piecewise integration (16 steps) prevents single large positions from extracting value at stale prices

Tech Stack

Component
Technology

Language

Solidity 0.8.24

Framework

Foundry (forge)

Compiler

via_ir = true, EVM target: Paris

Dependencies

OpenZeppelin (ERC20, Ownable)

Chain

Base L2 (Base Sepolia for testnet)

Collateral

USDC (6 decimals)

Last updated