EventlyMarketsV3.sol

Version: v3.2 (post-audit, pre-deployment) Status: Pending deployment (audit complete) Dependencies: OpenZeppelin ERC-1155, ERC-20 SafeERC20, PRBMath SD59x18


Overview

EventlyMarketsV3 is the production prediction markets contract for evently. It combines:

  • ERC-1155 tradable positions -- each trade mints/burns fungible shares per outcome

  • LMSR AMM -- Logarithmic Market Scoring Rule for automated, manipulation-resistant pricing (b=200 USDm)

  • CLOB (Central Limit Order Book) -- bids and asks with price-time priority, matched automatically on placeOrder

  • Creator economy -- 1% creator fee on all volume, claimable after finalization

The LMSR mechanism eliminates the core flaw of parimutuel systems: being correct always yields a profit, regardless of how many people chose the same option.


Key Parameters

Parameter
Value

Creator collateral

50 USDm

Dispute collateral

50 USDm

Dispute window

24 hours

Burn delay (losing shares)

24 hours after finalization

Total fee (default)

2.5% (1% creator + 1% treasury + 0.5% resolver pool)

Fee hard cap

5%

Max options per market

4

Min options per market

2

Min trade

0.001 shares (1e15)

LMSR liquidity parameter b

200 USDm (200e18)

Max orders per book

200 per (market, option, side)

1 winning share pays out

1 USDm exactly


LMSR Pricing Mechanism

LMSR (Logarithmic Market Scoring Rule) is an automated market maker where price follows a mathematically sound cost function:

  • q[i] = shares outstanding for option i

  • b = liquidity parameter (200 USDm) -- controls price sensitivity

  • Cost to buy shares of option i = C(q after) - C(q before)

Implied probability of option i:

At initialization all quantities are 0, so each option starts at 1/n probability. Prices always sum to exactly 1.

Market subsidy: To bootstrap liquidity, the creator locks b x ln(n) USDm at market creation. This subsidy guarantees solvency: poolBalance + subsidyDeposited >= total winning shares at all times.


Architecture


Market Lifecycle

  1. Created -- creator locks 50 USDm collateral + LMSR subsidy (b x ln(n))

  2. Active -- users buy/sell shares via LMSR AMM and CLOB

  3. BettingClosed -- after betting deadline, no new trades

  4. Resolved -- creator declares winning option

  5. Dispute Window (24h) -- any whitelisted user can dispute by locking 50 USDm

  6. Finalized -- after dispute window closes or admin settles dispute

  7. Redemption -- winning shareholders call redeemWinnings() for 1 USDm per share


Fee Structure

Recipient
Default
Description

Market creator

1% (creatorFeeBps = 100)

Accrues in creatorAccruedFees, claimable after finalization

Treasury

1% (treasuryFeeBps = 100)

Accumulates in treasuryBalance

Resolver pool

0.5% (resolverFeeBps = 50)

Initially = admin; redirected post-TGE to staking

Total

2.5%

Hard cap: 5% via setFees()

Admin markets (created by admin): creator cut goes to treasury instead.


Audit Fixes Applied (v3.2)

ID
Fix

F-02

claimCancelRefund -- snapshot total supply before burning; prevents insolvency on sequential claims

A-03

MAX_ORDERS_PER_BOOK = 200 per (market, option, side) -- prevents O(n) DoS griefing

F-DS-M02

_cleanBook() called immediately in cancelOrder -- dead CLOB entries removed, book never fills up with ghosts

R2-2

claimCreatorFees requires Finalized status -- blocks creator fee claim while market is Disputed

L-01

nonReentrant added to resolveMarket, cancelMarket, slashMarket

GROK-M01

subsidyDeposited included in claimCancelRefund effective pool -- subsidy recoverable by shareholders on cancel/slash

GPT-R3-3

slashMarket handles Disputed status -- dispute collateral swept to treasury, nothing stranded

GROK-L02

createdAt != 0 guard added to all view functions -- prevents silent zero returns for nonexistent markets

QWEN-I03

BettingClosed event emitted by closeBetting


Functions

Trading

  • placeOrder(marketId, optionIndex, side, quantity, pricePerShare, minFill) -- BUY or SELL limit order; CLOB matched first, remainder to AMM for BUY

  • sellToAMM(marketId, optionIndex, shares, minUsdmOut) -- instant sell to LMSR AMM at current price

  • cancelOrder(orderId) -- cancel resting order; escrowed USDm or shares returned immediately

Pricing (view)

  • getPrice(marketId, optionIndex) -- LMSR implied probability (0 to 1e18)

  • getImpliedPrices(marketId) -- array of all option probabilities

  • quoteBuy(marketId, optionIndex, usdmNet) -- shares out for a given net USDm (binary search)

  • quoteSell(marketId, optionIndex, shares) -- gross USDm out for a given share quantity

  • getQuantities(marketId) -- outstanding shares per option

  • getSubsidy(marketId) -- subsidy deposited and b parameter

  • getBidBook(marketId, optionIndex) -- resting bid order IDs

  • getAskBook(marketId, optionIndex) -- resting ask order IDs

  • getOrderInfo(orderId) -- full order struct

Market Management

  • createMarket(...) -- locks 50 USDm collateral + LMSR subsidy

  • createAdminMarket(...) -- admin pays subsidy only (no creator collateral)

  • resolveMarket(marketId, winningOption) -- creator resolves after betting deadline

  • closeBetting(marketId) -- permissionless; callable by anyone after bettingDeadline

  • disputeMarket(marketId, proposedOption) -- dispute with 50 USDm collateral

  • settleDispute(marketId, creatorWasRight, finalOption) -- admin settles

  • finalizeMarket(marketId) -- callable by anyone after dispute window closes

  • cancelMarket(marketId) -- creator, admin, or anyone after resolutionDeadline

  • slashMarket(marketId, reason) -- admin; works on Active, BettingClosed, Resolved, Disputed

Claims

  • redeemWinnings(marketId) -- burn winning shares for 1 USDm each

  • claimCancelRefund(marketId) -- pro-rata refund on cancelled/slashed markets

  • claimCreatorFees(marketId) -- creator withdraws accrued fees (post-finalization only)

  • burnLosingShares(marketId, holders[]) -- admin keeper; burns losing shares 24h after finalization

Admin

  • setFees(creatorBps, treasuryBps, resolverBps) -- adjust fee split (<=5% total)

  • setResolverPoolAddress(addr) -- redirect resolver fees

  • withdrawTreasury(to, amount) -- pull accumulated treasury fees

  • withdrawResolverPool(to, amount) -- pull accumulated resolver fees

  • transferAdmin(newAdmin) -- transfer admin role

  • addToWhitelist(wallet) / batchWhitelist(wallets[]) / removeFromWhitelist(wallet) -- whitelist management

  • setWhitelistEnabled(bool) -- toggle whitelist enforcement

  • createInviteCode(codeHash) / redeemInviteCode(code) -- invite system


Token ID Encoding

MAX_OPTIONS = 4, so each market occupies token IDs [marketId*4, marketId*4+3].


CLOB Design

Property
Value

Order types

BUY and SELL

Matching

Automatic on placeOrder

Price priority

Best price first (ascending for asks, descending for bids)

Time priority

FIFO within same price level

Dead entry cleanup

Immediately on cancelOrder (_cleanBook -- F-DS-M02)

Book cap

200 orders per (market, option, side)

Last updated

Was this helpful?