NUTs for prediction markets - #337
Conversation
2657a60 to
3f6850a
Compare
ee15247 to
63af400
Compare
|
@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 |
|
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 My concern with that approach is that it's primarily suited for bilateral contracts (two parties
They're not in conflict. In fact, both can share the same oracle infrastructure (DLC oracle |
94336aa to
16b650c
Compare
|
In case anyone's interested, I've been building a PoC application based on this protocol.
|
| [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 |
There was a problem hiding this comment.
This link should be replaced with an actual NIP after nostr-protocol/nips#1681 gets merged.
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).
f94f19e to
618bf78
Compare
|
squashed and rebased |
|
|
||
| ## 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/). |
There was a problem hiding this comment.
The Gnosis Conditional Token Framework link appears to be broken.
It currently shows a "Page Not Found" page.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
…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.
|
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? |
|
@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 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. |
|
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 |
5a6c744 to
01da44e
Compare
|
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.
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
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
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