-
Notifications
You must be signed in to change notification settings - Fork 46
Closed
Labels
ai-completedAI finished, needs human reviewAI finished, needs human reviewai-readyReady for AI implementationReady for AI implementationarea:apiAPI/backend changesAPI/backend changespriority:highHigh priorityHigh priorityrisk:lowLow risk - safe for AILow risk - safe for AItype:featureNew feature (creates feature/* branch)New feature (creates feature/* branch)
Description
Description
Implement the remaining four core domain slices (5-8) for the Futarchy prediction market integration, completing the on-chain event processing pipeline and market lifecycle commands. This continues the pattern established in slices 1-4 (PRs #13369, #13372, #13388, #13390).
- Slice 5 — Merge Tokens: Process
TokensMergedEVM events to record merge trades, update positions, and decrementtotal_collateral - Slice 6 — Market Resolution: Process
ProposalResolvedandMarketResolvedEVM events to transition market status toresolvedwith winner; addResolvePredictionMarketcommand for manual resolution - Slice 7 — Token Redemption: Process
TokensRedeemedEVM events to record redemption trades and update positions - Slice 8 — Market Cancellation: Add
CancelPredictionMarketcommand with auth checks (thread author or admin), validate status transitions (draft→cancelled, active→cancelled)
Technical Context
Type: feature
Category: api | workers | database
Priority: high
Risk: low
Architecture Notes
- Follow the exact same patterns from slices 3-4 (PRs Implement prediction market Slice 3: Mint Tokens projection and EVM event pipeline #13388, Implement prediction market SwapExecuted event pipeline (Slice 4) #13390): EVM event → mapper function in
chain-event-utils.ts→ event signature + registry inevm-protocols→PredictionMarketProjectionhandler → direct DB mutation - Projections, not policies: All EVM event handlers go in
PredictionMarket.projection.tswith direct Sequelize mutations — nocommand()indirection - Commands for user-initiated actions only:
ResolvePredictionMarket(Slice 6) andCancelPredictionMarket(Slice 8) are commands because they are initiated by users, not EVM events - All multi-record mutations must be wrapped in a single Sequelize transaction for consistency
- Idempotency enforced via composite PK on
PredictionMarketTrade(eth_chain_id, transaction_hash) - Event schemas for all slice 5-8 events already exist in
libs/schemas/src/events/events.schemas.ts - Feature-flag gated behind
FLAG_FUTARCHY(already configured in slice 4) - Use
DECIMAL(78,0)withSequelize.literal()for BigInt increment/decrement operations to preserve precision
Related Systems
libs/evm-protocols/src/event-registry/— event signatures and contract source registrylibs/model/src/services/evmChainEvents/chain-event-utils.ts— EVM log → domain event mapperslibs/model/src/aggregates/prediction_market/PredictionMarket.projection.ts— projection handlerslibs/model/src/aggregates/prediction_market/— aggregate commands and querieslibs/model/src/policies/PredictionMarket.policy.ts— existing policy (do not modify for projections)packages/commonwealth/server/api/predictionMarket.ts— tRPC router (needs new endpoints)
Implementation Steps
Slice 5: Merge Tokens (TokensMerged event pipeline)
- Register
TokensMergedevent signature hash ineventSignatures.ts(BinaryVault contract) - Add
TokensMergedto the BinaryVault contract source ineventRegistry.ts - Implement
predictionMarketTokensMergedMapperinchain-event-utils.tsto decode raw EVM log →PredictionMarketTokensMergedevent payload (fields:marketId,user,amount) - Add
PredictionMarketTokensMergedhandler toPredictionMarket.projection.ts:- INSERT
PredictionMarketTradewithaction: 'merge',p_amount,f_amount,collateral_returned - UPSERT
PredictionMarketPosition(decrement bothp_token_balanceandf_token_balancebyamount) - UPDATE
PredictionMarket.total_collateral(decrement by collateral returned) - All in single Sequelize transaction
- INSERT
- Write tests in
prediction-market-merge.spec.tsfollowingprediction-market-mint.spec.tspattern:- Mapper decodes raw log correctly
- Projection creates trade record
- Position balances decremented for both tokens
total_collateraldecremented on market- Idempotency: duplicate tx_hash does not create duplicate records
- Multi-user merge scenario
Slice 6: Market Resolution (ProposalResolved + MarketResolved + ResolvePredictionMarket command)
- Register
ProposalResolvedevent signature hash ineventSignatures.ts(FutarchyGovernor contract) - Register
MarketResolvedevent signature hash ineventSignatures.ts(BinaryVault contract) - Add both events to their respective contract sources in
eventRegistry.ts - Implement
predictionMarketProposalResolvedMapperinchain-event-utils.ts(fields:proposalId,marketId,winner) - Implement
predictionMarketMarketResolvedMapperinchain-event-utils.ts(fields:marketId,winner) - Add
PredictionMarketProposalResolvedhandler to projection:- UPDATE
PredictionMarketsetstatus='resolved',winner(1=PASS, 2=FAIL),resolved_at=now() - Idempotent: skip if already resolved
- UPDATE
- Add
PredictionMarketMarketResolvedhandler to projection (same logic, redundant confirmation from BinaryVault — idempotent) - Create
ResolvePredictionMarket.command.tsin aggregate directory:- Auth: thread author or community admin only
- Validates market is in
activestatus - Sets
status='resolved',winner,resolved_at - Emits
PredictionMarketResolvedevent
- Add
resolveendpoint to tRPC router inpredictionMarket.ts - Write tests in
prediction-market-resolution.spec.ts:- ProposalResolved mapper decodes correctly
- MarketResolved mapper decodes correctly
- Projection sets status=resolved, winner=1 (PASS) and winner=2 (FAIL)
- Projection sets resolved_at timestamp
- Idempotent: already-resolved market not modified
- ResolvePredictionMarket command: success for author, success for admin, rejected for non-author
- Invalid state transitions: draft→resolved rejected, already resolved rejected
Slice 7: Token Redemption (TokensRedeemed event pipeline)
- Register
TokensRedeemedevent signature hash ineventSignatures.ts(BinaryVault contract) - Add
TokensRedeemedto the BinaryVault contract source ineventRegistry.ts - Implement
predictionMarketTokensRedeemedMapperinchain-event-utils.ts(fields:marketId,user,amount,outcome) - Add
PredictionMarketTokensRedeemedhandler to projection:- INSERT
PredictionMarketTradewithaction: 'redeem',winning_token_amount,collateral_returned - UPSERT
PredictionMarketPosition(decrement winning token balance based onoutcome) - All in single Sequelize transaction
- INSERT
- Write tests in
prediction-market-redeem.spec.ts:- Mapper decodes raw log correctly (including
outcomefield) - Projection creates trade record
- Position winning token balance decremented
- Idempotency: duplicate tx_hash handled
- Redemption after PASS wins vs FAIL wins scenarios
- Mapper decodes raw log correctly (including
Slice 8: Market Cancellation (CancelPredictionMarket command)
- Create
CancelPredictionMarket.command.tsin aggregate directory:- Auth: thread author or community admin only
- Validates market is in
draftoractivestatus (notresolvedor alreadycancelled) - Sets
status='cancelled' - Emits
PredictionMarketCancelledevent
- Add
cancelendpoint to tRPC router inpredictionMarket.ts - Export new commands and queries from aggregate
index.ts - Write tests in
prediction-market-cancel.spec.ts:- Cancel from draft status succeeds
- Cancel from active status succeeds
- Cancel from resolved status rejected (invalid state transition)
- Auth: non-author non-admin rejected
- Auth: admin can cancel
Cross-cutting
- Add remaining query endpoints if not yet present:
GetPredictionMarketTrades,GetPredictionMarketPositions - Verify
pnpm -r check-typespasses - Verify
pnpm lint-branch-warningspasses - Verify all new and existing prediction market tests pass
Testing Requirements
- Unit tests: One test file per slice following existing
prediction-market-mint.spec.tsandprediction-market-swap.spec.tspatterns. Each file should test: mapper decoding, projection handler DB mutations, position balance arithmetic, idempotency, and multi-user scenarios. Command tests should cover auth checks and state machine transitions. - Integration tests: Extend
prediction-market-lifecycle.spec.tsto cover full lifecycle through resolution and redemption - Manual verification:
pnpm -r check-typesandpnpm lint-branch-warningsclean
Acceptance Criteria
-
PredictionMarketProjectionhandles all 8 domain events (4 existing from slices 1-4 + 4 new:TokensMerged,ProposalResolved,MarketResolved,TokensRedeemed) - No
command()calls inside the projection — all handlers use direct DB mutations - All mapper functions decode raw EVM logs using ABIs → typed event payloads
-
ResolvePredictionMarketcommand enforces auth (thread author or admin) and validatesactivestatus -
CancelPredictionMarketcommand enforces auth and validatesdraftoractivestatus - Both commands and new query endpoints are exposed via tRPC router behind
FLAG_FUTARCHY - All DB mutations within each projection handler wrapped in a single Sequelize transaction
- Idempotent event processing: duplicate EVM events (same tx_hash) do not create duplicate records
- Position balances correctly updated: merge decrements both tokens, redeem decrements winning token only
-
total_collateraldecremented on merge operations - Resolution sets
status='resolved',winner(1 or 2), andresolved_attimestamp -
pnpm -r check-typespasses -
pnpm lint-branch-warningspasses - All prediction market tests pass (new + existing)
Created with Claude by gent
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
ai-completedAI finished, needs human reviewAI finished, needs human reviewai-readyReady for AI implementationReady for AI implementationarea:apiAPI/backend changesAPI/backend changespriority:highHigh priorityHigh priorityrisk:lowLow risk - safe for AILow risk - safe for AItype:featureNew feature (creates feature/* branch)New feature (creates feature/* branch)