Config + ledger
.env as OO_CONFIG_ID / VOTE_LEDGER_ID.
Proposal
Lifecycle
propose<COLLATERAL>
outcome \in {1,2}, coin::value(bond) >= min_bond_micro, market.resolution_kind == RESOLUTION_OPTIMISTIC. Emits ProposalCreated.
dispute<COLLATERAL>
state == OPEN, clock.timestamp_ms < challenge_deadline_ms, coin::value(bond) >= proposer_bond.value. State → DISPUTED, vote_deadline_ms set to now + vote_window_ms. Emits ProposalDisputed.
cast_vote
ResolverNFT with matching weight (v1 stub. The on-chain check is a TODO). Updates yes_weight / no_weight. Emits VoteCast.
finalize_auto<COLLATERAL> / finalize_vote<COLLATERAL>
state == OPEN, clock.timestamp_ms >= challenge_deadline_ms. Returns the proposer’s bond back and a zero coin for the unused disputer-bond slot; state → FINALIZED_AUTO. Returns the proposer address so the caller can transfer::public_transfer the bond back in the same PTB.
finalize_vote is the post-dispute path: asserts state == DISPUTED, clock.timestamp_ms >= vote_deadline_ms. The winning side takes both bonds. The losing-side proposer/disputer’s bond is forfeited to the winner.
Frontend integration
OOPanel on EventMarketDetail exposes propose / dispute / finalize buttons with a live countdown to the deadline. State pill transitions animate via AnimatePresence. See Optimistic Oracle.
Tests
tests/oo_resolution_tests.move. State + outcome constants are distinct (placeholder; full propose-dispute-finalize harness lands post-republish).