Skip to content

NUTs for prediction markets - #337

Open
joemphilips wants to merge 12 commits into
cashubtc:mainfrom
joemphilips:nuts_for_prediction_markets
Open

NUTs for prediction markets#337
joemphilips wants to merge 12 commits into
cashubtc:mainfrom
joemphilips:nuts_for_prediction_markets

Conversation

@joemphilips

@joemphilips joemphilips commented Feb 7, 2026

Copy link
Copy Markdown

This is my ongoing attempt to support encoding Prediction Market Token with cashu.
My primary motivation is to

There have been previous attempts to support DLCs on Cashu, for example:
#128

The purpose of those attempts was essentially to emulate a two-party DLC transaction using eCash tokens.

This approach is completely different. Each outcome is encoded as a separate token, and the mint is agnostic to the oracles. I believe this is a more natural representation of a prediction-market security token and enables a more liquid market.

This PR contains three NUTs

  • NUT-CTF: Conditional token redemption via oracle-attested outcome keysets
  • NUT-CTF-split-merge: Conditional token split and merge for trading positions
  • NUT-CTF-numeric: Numeric outcome conditions with digit decomposition

Done self-reviewing, it is now ready for review from others. I will try to have a reference implementation ready when I have time. I may update the spec based on what I've learn during implementation.

It is possible to split CTF.md (Conditional Token Framework) into an independent PR if the maintainer wishes. Since this is quite a huge PR.

Reference implementation: cashubtc/cdk#1666

@github-project-automation github-project-automation Bot moved this to Backlog in nuts Feb 7, 2026
@joemphilips
joemphilips force-pushed the nuts_for_prediction_markets branch from 2657a60 to 3f6850a Compare February 15, 2026 14:10
@joemphilips
joemphilips marked this pull request as ready for review February 15, 2026 14:11
@joemphilips joemphilips changed the title WIP: NUTs for prediction markets NUTs for prediction markets Feb 15, 2026
@joemphilips

joemphilips commented Feb 17, 2026

Copy link
Copy Markdown
Author

Reference implementation in CDK(WIP)
and cashu-ts

@joemphilips
joemphilips force-pushed the nuts_for_prediction_markets branch 2 times, most recently from ee15247 to 63af400 Compare February 18, 2026 01:18
@lescuer97

Copy link
Copy Markdown
Contributor

@joemphilips have you taken a look at this PR: #128? its a previous standard for DLCs in cashu. you might want to take a look

@joemphilips

Copy link
Copy Markdown
Author

Hi @lescuer97, thanks for pointing it out. Yes, I've read it.

I didn't go into detail because the approach is quite different from mine. IIUC, PR #128 proposes an
ecash-native DLC framework, using the mint as a blind intermediary for two-party conditional
contracts.

My concern with that approach is that it's primarily suited for bilateral contracts (two parties
locking funds against a predefined outcome tree), whereas CTF is designed for prediction markets
where you need liquidity, composable positions, and the ability to enter/exit freely before
settlement. The two have different scopes:

  • DLC is for "Contract" - enforce a pre-agreed bilateral payout when an oracle attests to an
    outcome.
  • CTF (this PR) is for "Market" - represent outcome exposure as tradeable tokens, enabling open market
    participation.

They're not in conflict. In fact, both can share the same oracle infrastructure (DLC oracle
announcements/attestations), which could benefit the broader ecosystem. But I believe CTF is the
more appropriate primitive for prediction markets within Cashu's trust model.

@joemphilips

Copy link
Copy Markdown
Author

In case anyone's interested, I've been building a PoC application based on this protocol.

Comment thread CTF.md
[22]: 22.md
[CTF-split-merge]: CTF-split-merge.md
[CTF-numeric]: CTF-numeric.md
[NIP-88]: https://github.com/nostr-protocol/nips/pull/1681

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This link should be replaced with an actual NIP after nostr-protocol/nips#1681 gets merged.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge

Add NUT-CTF (Conditional Token Framework), NUT-CTF-split-merge (split/merge operations), and NUT-CTF-numeric (numeric outcome conditions) specs with test vectors, supplementary material, error codes, and README entries.

Squashed from 9 commits after rebasing onto upstream/main to resolve NUT-28/29 filename conflicts (upstream now uses those numbers for P2BK and Batched Mint).
@joemphilips
joemphilips force-pushed the nuts_for_prediction_markets branch from f94f19e to 618bf78 Compare April 6, 2026 09:20
@joemphilips

Copy link
Copy Markdown
Author

squashed and rebased

Comment thread CTF.md Outdated

## Terminology

- **Condition**: A question with defined outcomes, resolved by an oracle. Identified by a `condition_id`. Equivalent to "condition" in the [Gnosis Conditional Token Framework](https://docs.gnosis.io/conditionaltokens/).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Gnosis Conditional Token Framework link appears to be broken.
It currently shows a "Page Not Found" page.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. It seems like the website is not well maintained. Perhaps I should link to the contract itself (https://github.com/gnosis/conditional-tokens-contracts)

Or referencce the Polymarket's version (https://github.com/Polymarket/ctf-exchange-v2) but this one has slightly different terminology so I must be careful. Let me think

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

broken link fixed.
It seems to be referencing Polymarket version CTF is not a good idea for now since it has a much broader scope than this (off-chain CLOB matching, UMA oracle, etc.)

The docs.gnosis.io/conditionaltokens/ URL returns 404. Replace with the official readthedocs site (rendered from the same gnosis/conditional-tokens-contracts repo) and link the contracts repo alongside as a stable fallback. Add Polymarket as a real-world production example of the same model in CTF.md's Terminology section.
Silexperience210 pushed a commit to Silexperience210/hunch that referenced this pull request May 29, 2026
…SPIKE-02)

SPIKE-02 resolved by pivot: NUT-CTF (cashubtc/nuts#337) is still draft/WIP and was
the only blocker left in the Phase 2 backend. It isn't needed. A Hunch outcome token
is a vanilla NUT-11 P2PK proof locked to L = B + S_X (bettor key + oracle outcome
signature point). The spend key b + s_X exists only after the oracle attests X, so
the token is spendable iff the outcome occurred. The mint enforces only NUT-11 — no
custom NUT, builds on shipping CDK. Refund/INVALID maps to NUT-11 locktime+refund tags.

- token.rs: build_outcome_token (NUT-11 secret with lock + refund + locktime),
            redeem_spend_secret, verify_token_unlock
- 3 tests against the real oracle attestation path: YES attestation redeems the YES
  token only; wrong bettor can't redeem; secret carries lock/refund/locktime
- spikes/nut-ctf/SPIKE-02-RESOLUTION.md documents the pivot, math, safety, and the
  HIP-3 update needed

Remaining mint work (CDK issuance, LDK Lightning, DLC funding tx) is standard
engineering on stable primitives — no longer blocked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace POST /v1/ctf/split and /v1/ctf/merge with a single POST /v1/ctf/convert that performs any payoff-preserving rebalance of conditional positions within one condition at one nesting level. Split, merge, recombine, and negative-risk-style conversion all become special cases.

Core rule: for every outcome o in Omega, out(o) == in(o) - F, valid only if in(o) >= F, where F = ceil(sum input_fee_ppk / 1000). The flat fee is retained non-contingently on every outcome (real collateral the mint keeps regardless of the attested outcome), which bounds free operations.

Collateral is the reserved map key '*' (regular keyset at root, parent conditional keyset when nested). Coverage is computed from registered keyset metadata, not request map keys. Canonical collection encoding fixed (enum: announcements[0] outcome index; numeric: [HI,LO]). Partitions must have >=2 elements with no full-set conditional collection. Issuance invariant: every conditional token backed by >= its face in collateral. Adds error codes 13041/13042/13043; updates CTF.md, CTF-numeric.md, error_codes, suppl, and test vectors accordingly.

Design and post-implementation passes adversarially reviewed via Codex (verdict: sound to merge).
Remove partition registration from the CTF specs and describe condition-time keyset creation, managed default policies, root-only convert scope, and updated numeric behavior.
Add NUT-08-style blank outputs and change signatures for condition registration fees so mints retain only the authoritative required fee.
Use a distinct 13047 error for invalid registration fee change outputs and disambiguate the idempotency cross-reference.
Advertise registration fee schedules by collateral unit and default missing schedules to free registration.
Treat registration_fees entries as the allowlist for CTF collateral units and reject non-advertised units instead of defaulting them to free registration.
@MarkovianProtocol

Copy link
Copy Markdown

This is a clean design, and the oracle-agnostic split (mint stays agnostic, outcome tokens redeem against an external attestation) is the right seam.

One attestation source that fits the redemption step without adding a trusted signer: a deterministic-replay resolver. Markovian'"'"'s RESOLVE produces a verdict by replaying public data through a fixed function, so any party (the mint, either counterparty, a third agent) can recompute the same outcome and check the commitment against Bitcoin. The attestation is not "trust this signer," it is "here is the public input, the function, and the anchor, replay it yourself." That keeps the mint agnostic and removes the single-oracle trust assumption in the redemption path.

Honest scope: this only covers outcomes decidable from public data by a deterministic function, which is where DLC-style markets already sit. Anything needing private or subjective input still needs a conventional attestor.

Happy to put up a small reference adapter against whatever attestation interface NUT-CTF settles on, so an outcome token can redeem against a replayable verdict as one option alongside DLC oracles. Would that be useful for the spec at this stage?

@joemphilips

Copy link
Copy Markdown
Author

@MarkovianProtocol Thanks for the comment. NUT-CTF is intentionally oracle-agnostic at the redemption layer: any attestation source (DLC oracle, multi-sig oracle set, or a deterministic-replay resolver like yours) that can bind a verdict to a condition_id and an outcome keyset can serve as a redemption source, without the spec naming it.

I want to keep the v1 surface small, so the core spec can be reviewed; this PR is already sizable. The right place for a specific resolver like RESOLVE is a companion NUT or supplementary doc once the core is merged and there's a working adapter demonstrating the attestation→keyset mapping. I'd welcome that as a follow-up — please open a separate issue or draft so it can be evaluated on its own.

@MarkovianProtocol

Copy link
Copy Markdown

Agreed on keeping v1 oracle-agnostic and the surface small, that is the right call for review. The redemption layer staying attestation-source-neutral is exactly what lets a deterministic-replay resolver plug in without the core spec naming it.

We will do it as you suggested: once the core merges, a separate companion draft with a working adapter that demonstrates the attestation-to-keyset mapping concretely (verdict bound to condition_id, replayed from public data, mapped to the outcome keyset), so it can be evaluated on its own rather than expanding this PR. Will open it as its own issue/draft then. Thanks for pointing to the right venue.

@MarkovianProtocol

Copy link
Copy Markdown

Opened #411 as the separate draft. Scoped to making the verdict history single and timestamped. Adapter once the core and the cdk reference merge.

Closes a partition-based over-redemption gap. HI = floor(amount*hi_payout_ratio), LO = floor(amount*lo_payout_ratio), and the mint retains amount - HI - LO as dust. HI+LO <= amount always, and because floor is subaddible, splitting or merging proofs before redemption can only reduce a holder's payout, never increase it. Trades exact conservation (a full set can redeem slightly under collateral; dust is mint revenue) for partition-invariant solvency.
joemphilips added a commit to joemphilips/nuts that referenced this pull request Aug 1, 2026
Extension of both NUT-Exchange (cashubtc#410) and NUT-CTF-split-merge (cashubtc#337).

Delta document specifying only the differences from both parent NUTs.

P0 fixes from adversarial review (gpt-5.6-sol xhigh):

- corrected H_recv and refund domain separators

- explicit inherited (rules 1-7,9,11) vs replaced (rules 8,10)

- request_digest binds condition_id + parent_collection_id

- active-keyset cutoff inherited from split-merge

- coverage requirement is multi-party-only, not inherited

- removed false NUT-03 COMPLEMENTARY claim

- attestation atomicity scoped to all /v1/ctf/convert commits
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

5 participants