Story of the Cocoa Monster
Detailed demo and explanation: Cocoa Monster - Private Prediction Market (Midnight x MLH) | End to End Demo
Live deployment split:
- Staging:
https://staging.cocoa.monster - Production:
https://cocoa.monster

Inspiration 💡
Prediction markets are useful because they turn collective belief into a price. The problem is that most on-chain markets also turn every trader's behavior into public metadata: addresses, balances, timing, and portfolio history.
Cocoa Monster started from a simple question: can a prediction market keep public prices and settlement verifiable while removing the public per-user position book? Midnight is a strong fit for that question because it lets us combine normal ledger state with private witnesses and zero-knowledge proofs.
What it does 🤔
Cocoa Monster is a Midnight prediction market prototype with:
- YES/NO markets and multi-option markets with up to 8 option markets under one question.
- CPMM pricing for each option, using
reserveYes * reserveNo = k. - Wallet-local private position ownership using commitments and nonces.
- ZK redemption using nullifiers so a winning position can be claimed once without exposing a public account balance.
- Two resolution paths: a trusted oracle secret for direct settlement, and an optimistic oracle flow with propose, dispute, and finalize.
- A shared
MarketFactoryregistry so markets can be discovered without a backend. - Optional signed IPFS comments through Pinata, using a discussion identity separate from the wallet.
Important boundary: the current prototype keeps AMM reserves, trade inputs, stake totals, payouts, and resolution state public for auditability. The private layer is focused on ownership, witness state, and redemption. Fully shielded trade execution is future work.
How we built it ⚙️
The contracts are written in Compact for Midnight.
contract/src/cocoa.compactimplements trading, commitments, option state, close, oracle resolution, redemption, and nullifiers.contract/src/factory.compactimplements the on-chain market registry.contract/src/api.tswraps Midnight SDK calls, joins deployed contracts, rotates nonces, stores owned positions, computes oracle commitments, and manages wallet-scoped private state.contract/src/quote.tsmirrors the CPMM quote math in TypeScript so the UI can pass conservative quotes into the contract.
The frontend is a React + Vite app.
- Lace wallet connection is handled through the Midnight connector API.
- Public market state is read and streamed from the Midnight indexer.
- Private witness state is stored through Midnight's
levelPrivateStateProvider, scoped per wallet and per contract. - The UI includes market discovery, market creation, trading, claiming, oracle controls, live charts, and optional discussion.

Deployment is a static nginx SPA on Kubernetes using the Helm chart in charts/cocoa-monster/. Staging and production use the same image and chart; the split is handled by per-environment values.
Challenges we ran into 😤
The hardest part was being honest about what should be private and what must remain public. AMM reserves need to move publicly for prices to be verifiable, but user ownership does not need to be represented as a public balance table.
We also hit a ZK arithmetic issue around pro-rata payouts. Division is awkward inside circuits, so the client computes the payout and the circuit verifies it with multiplication bounds:
payout * winningStake <= stake * volume
(payout + 1) * winningStake > stake * volume
That proves the payout is exactly the floor-divided share without making the circuit do expensive division.
Another challenge was Midnight SDK private state. Under the SDK 4.x model, private state is scoped per contract address. Users can move between many markets, so the API wrapper has to set the contract context before witness reads and writes, rotate nonces safely, and preserve each market's owned positions.
Market discovery was also non-trivial. There is no backend indexer that we own, so we built a MarketFactory contract and merged that shared registry with browser-local storage to keep the UI responsive when the public indexer lags.
Accomplishments that we're proud of ✨
- A working Compact market contract with CPMM trading, multi-option support, oracle settlement, commitments, nullifiers, and pro-rata redemption.
- A browser flow that can deploy markets, trade, resolve outcomes, and claim payouts through Midnight providers.
- A real no-backend architecture: static UI, wallet, Midnight indexer, proof server, contracts, and optional IPFS comments.
- A clear privacy model that does not overclaim. Cocoa Monster protects wallet-local ownership and redemption links today, and leaves fully shielded trading as the next step.
- A polished UI with live charts, market creation, oracle controls, market discovery, wallet connection, and discussion.
What we learned 🙌
Proper sleep is very important! 😛
Well, a lot of things, both summed up in technical & non-technical sides. Well, ZK apps are not just normal dapps with encryption added. They need a different mental model: public ledger state for shared facts, private witness state for user-owned facts, and circuits that bridge the two. We also learned that good privacy UX has to be precise. If reserves, payouts, or recipient addresses are public, the app should say that. Users can trust the product more when it tells the truth about the boundary.
On the engineering side, we learned to design product logic around algebraic checks. The best circuit is often not the one that directly copies application code, but the one that proves the same result with simpler constraints.
What's next for Cocoa Monster 🚀
- Fully shielded trade execution so side and amount are hidden at the transaction level.
- Shielded collateral and shielded liquidity provider positions.
- Real dispute arbitration for the optimistic oracle path.
- Better analytics and historical indexing for market charts.
- A production-safe comment upload flow that does not expose a Pinata token in the client.
- More composability, so other Midnight apps can create, resolve, or consume private prediction markets.
Built With
- ipfs
- javascript
- midnight
- nix
- pinata
- typescript
- vite
- zero-knowledge




Log in or sign up for Devpost to join the conversation.