REDEEM TradeSide enum
value + redeem-time trade rows + GET /wallet/:index/history; verified on the losing
path (buy $20 YES → resolve NO → history shows lost −$20.00).reportPayouts / redeem (proven in
cli/src/demo.ts steps 5–6), DB Market.status + resolvedOutcomeId (currently always
OPEN / null), /wallet/:index already returns positions.There is no real auth — “admin” is just anvil account #0 by convention. A separate admin page would add a route + nav for what amounts to two buttons per market. Instead:
Fallback if this feels cluttered later: the same Resolve panel can be lifted into an
/admin page listing OPEN markets — the API work is identical either way.
POST /markets/:slug/resolve — body {outcome: "Yes" | "No", accountIndex: number}.
status === "OPEN"; accountIndex === 0 (403 otherwise —
convention-auth, same trust model as the rest of the demo API).ct.reportPayouts(questionId, outcome === "Yes" ? [1n, 0n] : [0n, 1n])
as operator (wallet 0). CTF rejects a second report (payout already set) — natural
idempotency guard.status = "RESOLVED", resolvedOutcomeId = <winning outcome id>, winning
outcome price → 1.00, losing → 0.00, plus a final PricePoint so the chart ends at
the resolution.executeTrade rejects non-OPEN markets with 400 —
currently nothing stops trading a resolved market against stale operator inventory.POST /redeem — body {accountIndex, slug}.
RESOLVED; index 1–9.ct.redeem(usdc, conditionId, [1n, 2n]) (both index sets —
losing tokens redeem for 0, so one call clears the position).status !== "OPEN" → “RESOLVED — YES/NO” badge on the market header; TradePanel
replaced by a note./resolve, then router refresh.packages/web/src/app/portfolio/page.tsx + Portfolio link in SiteNav.useWallet() context + GET /wallet/:index:
POST /redeem → refresh balances./redeem; /wallet/:index already carries the data (add
status/resolvedOutcomeId passthrough to its positions so the page can label rows).| # | Item | Verify |
|---|---|---|
| 1 | /resolve route + DB update + trade guard |
curl: resolve → 200; second resolve → 4xx; trade on resolved → 400 |
| 2 | /redeem route |
curl: winner redeems → USDC up by token count; loser → 0 |
| 3 | Wallet picker: add operator #0 | UI shows admin entry; #0 hides TradePanel |
| 4 | Market page: ResolvePanel + RESOLVED badge | resolve via UI; badge + chart end-state |
| 5 | Portfolio page + nav + Redeem buttons | buy on #1 → portfolio shows position; resolve → redeem → balance check |
End-to-end demo script (the acceptance test): wallet #1 buys YES on a market → operator #0 resolves YES on the market page → wallet #1’s portfolio shows the win → Redeem → USDC balance increases by the token count. All on the running local stack.
invalid outcome (payouts
[1,1]) is also skipped — binary YES/NO only.