Claude Opus 4.6

Tool: Claude Opus 4.6 (Anthropic) Type: 3-round AI audit (Systematic → Economic → Triage) Contracts reviewed: evently.sol v5.4 · EventlyProfiles.sol v1.3 · EventlyMarketsV3.sol (LMSR b=200 + CLOB bids/asks) Chain: MegaETH (Chain ID 4326) — Solidity ^0.8.20 Dependencies: OpenZeppelin ERC-1155 / SafeERC20, PRBMath SD59x18 Date: March 2026 Status: Complete — v3.2 post-audit fixes verified


Summary

Full audit of EventlyMarketsV3 covering the LMSR AMM, CLOB order book (bids & asks), ERC-1155 position system, dispute mechanism, and collateral/fee flows. All previously identified vulnerabilities (F-02, A-03, F-DS-M02, R2-2, L-01) confirmed patched. Four additional findings (GROK-M01, GPT-R3-3, GROK-L02, QWEN-I03) identified and resolved in v3.2. No production blockers remain.

Severity
Count

Critical

0

High

0

Medium

1

Low

4

Informational

1

Total

6


Round 1 — Systematic Vulnerability Scan

Reentrancy

All external state-mutating functions use nonReentrant mutex (_locked). CEI (Checks-Effects-Interactions) pattern is correctly followed throughout:

  • placeOrder: USDm/shares transferred in at top; all state updates before external calls (seller transfers, share transfers, AMM mint)

  • sellToAMM: quantities and balance decremented, shares burned, _collectFees called — all before usdm.safeTransfer

  • redeemWinnings: shares burned, balances decremented before transfer

  • claimCancelRefund: pre-burn snapshot, then burn, then transfer — F-02 fix correctly implemented

  • cancelOrder: order deactivated, amounts zeroed before safeTransfer / _safeTransferFrom

  • disputeMarket: state set before transfer

  • settleDispute: state fully updated before usdm.safeTransfer to disputer

  • resolveMarket, cancelMarket, slashMarket: now protected by nonReentrant (L-01 fix) ✅

No reentrancy vulnerabilities found.

Access Control

  • onlyAdmin: correctly restricts withdrawTreasury, transferAdmin, settleDispute, slashMarket, setResolverPoolAddress, setFees, burnLosingShares

  • onlyWhitelisted: correctly gates placeOrder, sellToAMM, disputeMarket, createMarket when whitelistEnabled = true

  • transferAdmin: zero-address check present ✅

  • resolveMarket: checks msg.sender == m.creator and timing bounds ✅

  • claimCreatorFees: requires Finalized status — implicitly blocks Disputed (R2-2 fix) ✅

L-02 (Low): Single admin key is a centralization risk. Admin can call slashMarket, drain treasury, override dispute outcomes, and trigger burnLosingShares. Mitigated by planned Gnosis Safe multisig post-raise. Acknowledged design tradeoff.

Integer Arithmetic

  • Solidity ^0.8.20 built-in overflow protection ✅

  • LMSR math uses PRBMath SD59x18 — audited fixed-point library ✅

  • Fee calculations: (amount * BPS) / 10000 — correct order, no precision loss beyond 1 wei per operation ✅

  • redeemWinnings payout: payout = shares (1 share = 1e18 = 1 USDm) — no rounding, exact ✅

  • claimCancelRefund pro-rata: (poolBalance * userTotal) / allTotal — standard floor division, last redeemer may receive 1 wei less. Acceptable.

L-03 (Low): In quoteBuy, the binary search upper bound is maxQ = 26000e18 (~26,600 USDm worth of shares per tx). Derived from the SD59x18 exp overflow limit at q/b ≈ 133. Documented in NatSpec.

Token Handling

  • SafeERC20 used for all USDm transfers ✅

  • ERC-1155 shares: minted on AMM buy, burned on sell/redeem, optionSupply tracking mirrors ERC-1155 supply correctly

  • CLOB fills: shares transferred from contract escrow (ASK) or minted (AMM BUY) to buyer

L-04 (Low): optionSupply[marketId][opt] is manually maintained alongside _mint/_burn. Peer ERC-1155 transfers via safeTransferFrom do not update optionSupply — it tracks total minted supply, not per-wallet balances. The math in redeemWinnings (uses balanceOf) and claimCancelRefund (uses optionSupply) remains correct by design. Acknowledged.

CLOB Order Book

The CLOB replaces the previous P2P sell-only book with full bids and asks:

  • Price-time priority: orders sorted by price first (ascending for asks, descending for bids), FIFO within same price using placedAt timestamp

  • Immediate matching: placeOrder matches against the opposite book before creating a resting order

  • Dead entry cleanup (F-DS-M02 fix): _cleanBook called immediately on cancelOrder, preventing permanent DoS of a book

  • Book cap: MAX_ORDERS_PER_BOOK = 200 per (market, option, side) pair — griefing cap maintained ✅

  • AMM fallback: unmatched BUY budget routed to LMSR AMM — ensures liquidity always available

No CLOB-specific vulnerabilities found.

LMSR Math Verification

The cost function C(q) = b × ln(Σ exp(q[i]/b)) is correctly implemented in _lmsrCost. Verified properties:

  • Initial price symmetry: At quantities = [0,0,...,0], all options have price 1/n ✅

  • Price sum invariant: Σ P(i) = 1 always ✅

  • Monotonicity: Buying option i increases P(i), decreases P(j≠i) ✅

  • Solvency: poolBalance + subsidyDeposited ≥ total winning shares — proven by LMSR cost function bound ✅


Round 2 — Economic Attack Analysis

LMSR Solvency

Proof: poolBalance + subsidyDeposited = C(q_final) ≥ quantities[winning_opt] = total winning shares minted. redeemWinnings draws from poolBalance first, then subsidyDeposited. SOLVENT by mathematical guarantee.

F-02 Fix Verification

claimCancelRefund snapshots allTotal before burning. Multiple sequential claimers cannot extract more than poolBalance. ✅

F-DS-M02 Fix Verification

cancelOrder now calls _cleanBook immediately after marking the order inactive. Dead entries are removed on the same transaction. An attacker who fills a book with 200 orders and cancels them all will see the book length drop to 0 — no permanent DoS possible. ✅

R2-2 Fix Verification

claimCreatorFees requires m.status == MarketStatus.Finalized. While a market is Disputed, its status is Disputed — not Finalized — so the require reverts. No front-run path exists: the creator cannot claim fees during the dispute window. ✅

Fee Accounting

_collectFees correctly splits into creator, treasury, and resolver cuts. Admin markets redirect creator cut to treasury. Creator fees locked until finalization. On slash or lost dispute: creatorAccruedFees zeroed to treasury. ✅

M-01 (Medium) — Multi-ledger accounting: In sellToAMM, poolBalance -= grossUsdm before _collectFees returns netUsdm. Fees (grossUsdm - netUsdm) are credited to treasury/resolver/creator balances but not deducted separately from poolBalance — this is correct since poolBalance is reduced by gross. The implicit invariant poolBalance + treasuryBalance + resolverPoolBalance + Σ creatorAccruedFees = total USDm held should be validated by formal invariant tests. No discrepancy found.

Auto-Burn Economic Model

burnLosingShares is callable by admin only, 24h after finalization. The BURN_DELAY matches DISPUTE_WINDOW, so winning and losing determination is always final before the burn. The keeper-driven model is appropriate for MegaETH's environment. ✅


Round 3 — Adversarial Triage

Can funds be stolen?

  • withdrawTreasury: onlyAdmin, draws from treasuryBalance only ✅

  • redeemWinnings: burns shares before transfer, subsidy drawn only for shortfall ✅

  • claimCancelRefund: pre-burn snapshot prevents over-extraction ✅

  • claimCreatorFees: onlyCreator, requires Finalized, one-time flag ✅

  • cancelOrder: returns only escrowed funds to msg.sender == o.maker

  • settleDispute: slash mechanism intended; only activates on dishonest resolution ✅

  • burnLosingShares: onlyAdmin, burns worthless losing shares only — no USDm movement ✅

Verdict: No direct fund theft path found.

Can the contract be bricked?

  • transferAdmin(address(0)): blocked ✅

  • _cancelAllOrders in resolveMarket/cancelMarket/slashMarket: max 200 asks + 200 bids × 4 options = 1,600 iterations. Safe on MegaETH. L-05 (Low): On L1-like environments this could hit gas limits; noted for any future multi-chain deployment.

  • LMSR exp overflow: quoteBuy cap at 26,000e18. ✅

  • CLOB dead entries: cleaned immediately on cancel — no permanent DoS possible (F-DS-M02 fix). ✅

Verdict: No bricking path found.

ERC-1155 Transfer Hook

No _beforeTokenTransfer override. Shares are freely transferable. redeemWinnings uses balanceOf (actual balance) so peer transfers don't break redemption. optionSupply tracks total minted, not per-wallet — by design. ✅

I-01 (Info): Empty ERC-1155 URI — no metadata for position tokens. URI to be added pre-deployment.


Findings Table

ID
Severity
Contract
Title
Status

M-01

Medium

EventlyMarketsV3

Implicit multi-ledger accounting — recommend formal invariant test

Acknowledged — recommend test coverage

L-02

Low

EventlyMarketsV3

Single admin key — centralization risk

Acknowledged — Gnosis Safe post-raise

L-03

Low

EventlyMarketsV3

quoteBuy max capacity ~26,600 USDm/tx

Acknowledged — documented in NatSpec

L-04

Low

EventlyMarketsV3

optionSupply not updated on peer ERC-1155 transfers

Acknowledged — by design, math correct

L-05

Low

EventlyMarketsV3

_cancelAllOrders gas bound on L1-like chains

Acknowledged — safe on MegaETH

I-01

Info

EventlyMarketsV3

Empty ERC-1155 URI

In resolution — URI added pre-deployment


Confirmed Fixed

ID
Title
Verification

F-02

claimCancelRefund insolvency — double-counted burned shares

Resolved. Pre-burn snapshot in claimCancelRefund. ✅

A-03

Order book griefing — no MAX_ORDERS cap

Resolved. MAX_ORDERS_PER_BOOK = 200 per side per (market, option). ✅

F-DS-M02

Dead CLOB entries permanently block new orders

Resolved. _cleanBook called in cancelOrder — dead entries removed immediately. ✅

R2-2

Creator front-runs settleDispute to claim fees before slash

Resolved. claimCreatorFees requires Finalized status — Disputed markets blocked. ✅

L-01

resolveMarket, cancelMarket, slashMarket lacked nonReentrant

Resolved. nonReentrant modifier added to all three functions. ✅

G-I01

Outdated NatSpec — CPMM reference, P2P sell-only description

Resolved. NatSpec updated to LMSR + CLOB (bids & asks). ✅


Production Blockers

None. All previously identified vulnerabilities are confirmed patched. LMSR mechanism is mathematically sound. CLOB implementation correctly handles bids and asks with price-time priority. Contract is ready for professional audit engagement.


LMSR-Specific Security Properties

Property
Verified
Notes

Initial prices = 1/n

All quantities = 0 → softmax uniform

Σ prices = 1 always

Softmax invariant

Solvency: pool + subsidy ≥ winning shares

Proven by LMSR cost function bound

1 share = 1 USDm payout (exact)

payout = shares in redeemWinnings

Subsidy exhaustion impossible

By solvency proof

quoteBuy binary search converges

Monotone cost function, bounded domain


CLOB-Specific Security Properties

Property
Verified
Notes

Price-time priority maintained

_insertSorted + placedAt timestamp

Dead entries cannot block book

_cleanBook in cancelOrder (F-DS-M02)

BUY escrow always sufficient

usdmEscrowed = qty * price / SHARE_UNIT

SELL escrow returned on cancel

Shares transferred back in cancelOrder

No double-spend via order+AMM

AMM phase uses only remaining budget after CLOB fills

Book cap enforced

MAX_ORDERS_PER_BOOK = 200 per side


Severity Scale

Severity
Definition

Critical

Direct fund loss, immediate production blocker

High

Fund loss possible under specific conditions

Medium

DoS, griefing, or logic error with economic impact

Low

Edge case, minor logic issue, or gas concern

Informational

Code quality, missing feature, or best practice

Status options: Fixed · Resolved · In resolution · Acknowledged · By design

Last updated

Was this helpful?