Inscribe (a reference implementation)
A live Injective × Cascade dApp for permanent, re-litigatable prediction markets.
Inscribe is a live injective-888 dApp that exercises the full Injective × Cascade pattern: permanent, re-litigatable prediction markets where every claim, every piece of evidence, and every voter's justification is stored forever on Cascade.
This page is a tour of how it's built. For the message-by-message contract reference and the deploy/CLI scripts, see the Inscribe repository.
The idea
Existing prediction markets settle on ephemeral evidence and undocumented reasoning. Voters cite news articles that get edited, paywalled, or deleted, and they never publish why they voted as they did. Six months after a market closes, you cannot reconstruct what was known, who decided what, or on what basis.
Inscribe fixes this by making three artifacts permanent on Cascade, one set per market:
| Artifact | Permanent because… | What it carries |
|---|---|---|
| Market spec | its Cascade CID is the market's identity | claim, settlement criteria, parameters |
| Evidence pool | each submission is its own action_id | citations, source metadata, claim role |
| Justifications | proposer, challenger, and every voter upload reasoning before acting | verdict + cited evidence CIDs + written explanation |
Settlement is an optimistic, bonded human oracle. There is no AI, no automated truth-detection. The protocol's contribution is making every input and every reasoning step permanent and citable forever, so a market resolved today can be re-litigated by anyone years later with the full decision trail intact.
Architecture
Injective holds the state machine, the escrow, and the bonds. Cascade holds every artifact a verdict depends on. Two small Go services join the two halves so the frontend gets one fast read, and all Cascade writes go through the server-signed cascade-api path (Pattern B).
No Interchain Account is involved. Every Cascade write is a direct HTTPS call to the cascade-api, which signs on Lumera.
The three contracts
Inscribe ships as a versioned set of three independently-deployable CosmWasm 2.0 contracts. The wiring between them is per-instance, established at instantiation.
| Contract | Code ID | Role |
|---|---|---|
inscribe-market-v2 | 39449 | Per-market state machine: bet escrow, evidence registry, proposal/challenge/vote/settlement |
bond-vault | 39446 | Bond custody by (market, role, addr); locks, releases, and slashes bonds atomically |
voter-registry | 39447 | Bonded voter set; deterministic, accuracy-weighted committee sampling |
A market locks a proposer or challenger bond by emitting a CosmosMsg::Wasm to bond-vault in the same transaction (the standard CosmWasm pattern for atomic funds-plus-state changes). The full InstantiateMsg / ExecuteMsg / QueryMsg surface lives in the repo.
The services
Both Go binaries live in one module and run independently:
inscribe-indexera poll-and-mirror loop. Every ~10s it listsinscribe-market-v2instances via Injective LCD, smart-queries each market's state, mirrors it into Postgres, and eagerly resolves every referenced Cascade CID into acascade_cachetable. It is stateless across restarts: drop the tables and it refills from chain state within minutes. Polling (not websockets) is chosen because it recovers from any disconnect by simply running another tick.inscribe-apia read-only HTTP surface over the indexer's Postgres tables, plus the one write routePOST /cascade/uploadthat forwards to the cascade-api. Its/markets/{addr}/auditendpoint resolves every CID a market references and returns them inline as one JSON blob, so the audit viewer renders without 30 separate browser round-trips.
How a market moves
Every market is a finite state machine. Funds and chain-height windows gate the transitions.
- Open: bettors stake
injinto a YES or NO pool (pari-mutuel; the same address may hold both sides), and anyone may submit evidence CIDs. - Proposed: after the settlement block, anyone posts a verdict with a justification CID and a bond. If the challenge window passes uncontested,
finalize_uncontestedreleases the bond and the verdict stands. - Challenged: a counter-verdict with its own bond moves the market to a dispute.
- Voting:
request_committeesamples a committee fromvoter-registry, weighted bybond × accuracy, seeded from a block hash drawn after the challenge was filed. Each member votes with a mandatory justification CID. - Final:
tallycounts votes, slashes the losing bond to the winner, updates voter accuracy, and records the settlement. Winning bettorsredeemtheir pro-rata share; anInvalidresolution refunds everyone 1:1.
Every verdict, challenge, and vote requires a justification artifact uploaded to Cascade before the on-chain call is accepted. The contract never verifies that a citation is sound, but the citation is permanent and public, so a bad-faith actor cannot un-cite later.
Re-litigation: the property that needs permanence
Once a market resolves, the bets are paid and the verdict is on-chain but the truth question stays open forever. Anyone, including people who weren't around when the market existed, can fetch the full record by spec_cid (spec, evidence pool, every justification, the canonical settlement record), form an independent assessment, and upload a counter-argument to the same market's Cascade namespace. It doesn't change the on-chain verdict and doesn't pay out; it becomes a permanent companion record that future markets and scholarship can cite.
This is the property no other prediction-market protocol has: the markets close, but the truth conversation does not. Cascade is the substrate that makes it possible; Injective is the venue where bets pay out. It only works because the artifacts are genuinely permanent, exactly what Cascade provides and IPFS pinning does not.
What's deployed
| Component | Where | Status |
|---|---|---|
bond-vault | code_id 39446, instance inj1hk6us04mhztdyrx7znraf5422qe9gakktnkaya | Live |
voter-registry | code_id 39447, instance inj15u6gvmgxf32zvwgpqjrc9davyhxp04qa33lxlv | Live |
inscribe-market-v2 | code_id 39449, first instance inj1swye2chl5733hen3ee3uh5nfesrx9xzjunj74e | Live |
inscribe-api + inscribe-indexer (Go) | local + Vercel preview | Complete |
| Web (Next.js + Keplr) | Vercel | Complete |
Wasm checksums are byte-deterministic via cosmwasm/optimizer and verifiable with sha256sum artifacts/<crate>.wasm. The source of truth for code IDs, tx hashes, and checksums is scripts/deployments.json in the repo.
What to take from it
Inscribe is one shape of Injective × Cascade product. The reusable lessons, the contract conventions, the indexer's eager-resolve loop, the state-machine-driven frontend, and the pitfalls to design around are generalized in Build your own integration.