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.
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 beforeusdm.safeTransferredeemWinnings: shares burned, balances decremented before transferclaimCancelRefund: pre-burn snapshot, then burn, then transfer — F-02 fix correctly implementedcancelOrder: order deactivated, amounts zeroed beforesafeTransfer/_safeTransferFromdisputeMarket: state set before transfersettleDispute: state fully updated beforeusdm.safeTransferto disputerresolveMarket,cancelMarket,slashMarket: now protected bynonReentrant(L-01 fix) ✅
No reentrancy vulnerabilities found.
Access Control
onlyAdmin: correctly restrictswithdrawTreasury,transferAdmin,settleDispute,slashMarket,setResolverPoolAddress,setFees,burnLosingSharesonlyWhitelisted: correctly gatesplaceOrder,sellToAMM,disputeMarket,createMarketwhenwhitelistEnabled = truetransferAdmin: zero-address check present ✅resolveMarket: checksmsg.sender == m.creatorand timing bounds ✅claimCreatorFees: requiresFinalizedstatus — implicitly blocksDisputed(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 ✅redeemWinningspayout:payout = shares(1 share = 1e18 = 1 USDm) — no rounding, exact ✅claimCancelRefundpro-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
SafeERC20used for all USDm transfers ✅ERC-1155 shares: minted on AMM buy, burned on sell/redeem,
optionSupplytracking mirrors ERC-1155 supply correctlyCLOB 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
placedAttimestampImmediate matching:
placeOrdermatches against the opposite book before creating a resting orderDead entry cleanup (F-DS-M02 fix):
_cleanBookcalled immediately oncancelOrder, preventing permanent DoS of a bookBook cap:
MAX_ORDERS_PER_BOOK = 200per (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) = 1always ✅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 fromtreasuryBalanceonly ✅redeemWinnings: burns shares before transfer, subsidy drawn only for shortfall ✅claimCancelRefund: pre-burn snapshot prevents over-extraction ✅claimCreatorFees:onlyCreator, requiresFinalized, one-time flag ✅cancelOrder: returns only escrowed funds tomsg.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 ✅_cancelAllOrdersinresolveMarket/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
expoverflow:quoteBuycap 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
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
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
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
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
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?

