Parlay Journey
Every step a customer goes through, with every exit the documents define. Sources: Litepaper (01), API spec (03), Data (04), custody docs (05–07) and the two outcome-clearing branches. Where the two custody branches change what the customer experiences, both are shown. Nothing here is a fee: the customer pays the maker's price P and nothing else at launch.
Overview
flowchart TD
P0["0 · Fund your account"] --> P1["1 · Browse the catalogue"]
P1 --> P2["2 · Build a parlay and request a price"]
P2 -->|no price, or quote expired| P1
P2 --> P3["3 · Commit to the quote"]
P3 --> P4{"4 · Maker last look"}
P4 -->|confirm| P5["5 · Execution: stake and liability locked"]
P4 -->|reject, timeout, market invalidated| R1(["No trade · stake released"])
P4 -->|replacement quote| P3
P5 -->|contract or funding failure| R1
R1 --> P1
P5 --> P6["6 · Legs resolve"]
P6 --> P7{"7 · Settlement"}
P7 -->|every leg wins| W(["WIN · payout P to Available"])
P7 -->|a leg loses| L(["LOSS · stake S to the maker"])
P7 -->|any leg void or unresolvable| V(["REFUND · exact S back"])
W --> P8["8 · Withdraw"]
V --> P8
L --> P1
classDef user fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E
classDef sys fill:#EEF2ED,stroke:#7D877F,color:#1C221E
classDef good fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E,stroke-width:2px
classDef bad fill:#F6E9E9,stroke:#9A3B3B,color:#1C221E
class P0,P1,P2,P3,P8 user
class P5,P6 sys
class W,V good
class L,R1 badFund your account
flowchart TD
A["Connect wallet"] --> B{"Do you have a vault yet?"}
B -->|no| C["Outcome creates your UserVault (Admin pays gas)"]
B -->|yes| D
C --> D{"Custody branch"}
D -->|A · HyperCore| E["Send spot USDC on HyperCore to your vault address"]
E --> F["Operator sees the transfer and credits it"]
F --> G(["Available = deposit"])
D -->|B · HyperEVM| H["Move USDC HyperCore to HyperEVM (one-time)"]
H --> I["Approve, then deposit into your vault (you pay gas)"]
I --> G
E -.->|sent from a different wallet| X1(["Left unclaimed - not credited"])
I -.->|raw transfer instead of deposit| X2(["Unaccounted - stranded"])
classDef user fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E
classDef good fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E,stroke-width:2px
classDef bad fill:#F6E9E9,stroke:#9A3B3B,color:#1C221E
class A,E,H,I user
class G good
class X1,X2 bad| You see | What happens underneath | Exits |
|---|---|---|
| A balance with Available / Locked (branch A also shows Withdrawing) | One immutable UserVault per person, created lazily. A: the vault address is your HyperCore account; the operator watches the ledger and calls creditDeposit only for owner → own-vault transfers. B: the vault holds ERC-20 USDC; deposit() credits the observed balance delta. | Sending from an unrelated wallet (A) or a raw token transfer (B) is not credited. Designed but not built: authorizing a bounded trading key once so you never sign per trade (doc 07). |
Browse the catalogue
flowchart LR A["Open the catalogue"] --> B["Eligible legs only: pre-event moneylines"] B --> C["Reference prices shown (informational)"] C --> D["Pick a leg"] D -.->|market suspended or closed on Outcome Market| X(["Leg disappears or is marked ineligible"]) classDef user fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E classDef bad fill:#F6E9E9,stroke:#9A3B3B,color:#1C221E class A,D user class X bad
| You see | Underneath | Exits |
|---|---|---|
| Sports, competitions, moneyline legs; a reference probability per leg | outcome-market polls Outcome Market, normalizes to Market / Leg / PriceObservation, applies the content-hashed eligibility policy, serves snapshots over REST and changes over WebSocket. Reference prices are never executable. | Lifecycle changes only on explicit Outcome Market evidence; an ineligible leg cannot be added to a parlay. |
Build a parlay and request a price
flowchart TD
A["Select 2 to 4 legs"] --> B["Enter stake S"]
B --> C{"Outcome validates"}
C -->|ineligible combination, over limits, insufficient Available| X1(["Request refused with a reason"])
C -->|ok| D["RFQ sent to the maker"]
D --> E{"Maker quotes before the deadline?"}
E -->|no| X2(["No price available right now"])
E -->|yes| F["Quote shown: payout P, expires at"]
F --> G{"You act before expiry?"}
G -->|maker re-prices first| F
G -->|expired| X3(["Quote gone - request again"])
G -->|accept| H(["Go to Commit"])
classDef user fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E
classDef bad fill:#F6E9E9,stroke:#9A3B3B,color:#1C221E
classDef good fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E,stroke-width:2px
class A,B user
class X1,X2,X3 bad
class H good| You see | Underneath | Exits |
|---|---|---|
Your legs, your stake S, then one price: total payout P (stake included) with a countdown | Outcome checks eligibility, permitted combinations, limits and funding readiness, then delivers a maker-scoped RFQ (quote_response_deadline, rfq_expires_at). The maker prices the whole ticket and submits P; Outcome derives the maker's liability L = P − S, validates P > S, and reserves maker capacity atomically so the price cannot later fail for "no capacity". Before you act the maker may quote.replace (you see the new price) or quote.cancel. | Validation failure → refused with reason. Maker silent → closed_no_quote. Quote expiry → request again. RFQs are ephemeral — there is no order book to rest on. |
Commit to the quote
flowchart TD
A["Tap Confirm on the quote"] --> B["Wallet authorization"]
B --> C{"Outcome verifies authority and funding"}
C -->|fails| X(["Not committed - nothing reserved"])
C -->|ok| D["Commitment recorded, confirm_by clock starts"]
D --> E(["Stake S reserved - cannot withdraw it now"])
classDef user fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E
classDef bad fill:#F6E9E9,stroke:#9A3B3B,color:#1C221E
classDef good fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E,stroke-width:2px
class A,B user
class X bad
class E good| You see | Underneath | Exits |
|---|---|---|
A short "waiting for the maker" state; your Available drops by S (reserved) | customer_action.recorded with effective_for_execution = true carries S, P, L, an attestation that Outcome verified your wallet authority and funding, and the Outcome-set confirm_by deadline (target 500 ms). This is a binding instruction to proceed at that price — not yet a trade. | Authorization or funding failure → the action is not effective and nothing is reserved. |
Maker last look
flowchart TD
A["Maker has until confirm_by"] --> B{"Decision"}
B -->|confirm| C(["Proceed to execution"])
B -->|reject with an approved reason| D(["No trade - stake released"])
B -->|timeout| D
B -->|market objectively invalidated| D
B -->|replacement quote, if enabled| E["New price P' shown - fresh approval needed"]
E -->|you approve| F(["Back to Commit with P'"])
E -->|you decline or it expires| D
classDef good fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E,stroke-width:2px
classDef bad fill:#F6E9E9,stroke:#9A3B3B,color:#1C221E
classDef user fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E
class C,F good
class D bad
class E user| You see | Underneath | Exits |
|---|---|---|
| Either "confirmed", "declined by the maker", or a new price to approve | Every commitment gets a maker confirm or reject; reject needs an approved reason code — "not enough capacity" is never allowed because capacity was reserved when the quote went live. maker_decision.replace (only if the environment advertises it) creates a new immutable quote, exhausts your commitment, and is bounded by replacement depth and RFQ lifetime. Outcome's recorded order decides every race. | Reject / timeout / invalidation → no trade, reservation released, you can request again. Replacement → your choice. SLA targets: ≥ 99 % decided in time, ≤ 1 % timeouts. |
Execution
flowchart TD
A["Outcome authorizes one contract operation"] --> B["TradeManager.openTrade locks S in your vault and L in the maker's vault - one transaction"]
B --> C{"Finality predicate passes?"}
C -->|yes| D(["Binding trade · Available -S, Locked +S"])
C -->|revert or definitive failure| E(["No trade - stake released, no one-sided lock possible"])
C -->|outcome unknown| F["Operator recovers from receipt and chain state - never resubmits"]
F --> C
classDef good fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E,stroke-width:2px
classDef bad fill:#F6E9E9,stroke:#9A3B3B,color:#1C221E
classDef sys fill:#EEF2ED,stroke:#7D877F,color:#1C221E
class D good
class E bad
class A,B,F sys| You see | Underneath | Exits |
|---|---|---|
The trade appears as Open: stake S moved from Available to Locked; payout P shown | execution.submission_authorized → pending → finalized → trade.created. On both branches openTrade locks the customer stake and the maker liability atomically — if either lock fails the whole transaction reverts. Submission or observation is not a trade; only the configured finality predicate is. The trade's S, P, L, rulebook and mapping versions are now immutable. | Objective invalidation before submission, loss of funding authority, security revocation or a contract failure → explicit no-trade with your stake released. An unknown outcome blocks capacity and is resolved from authoritative status, never by blind retry. |
Legs resolve
flowchart TD
A["Events happen"] --> B["Outcome Market publishes outcomes"]
B --> C["Outcome applies the recorded rulebook and mapping per leg"]
C --> D{"Evidence complete and final?"}
D -->|yes| E(["Leg result recorded: won, lost or void"])
D -->|conflicting or missing| F["Resolution suspended - you see Pending"]
F --> G{"Long-stop outer deadline reached?"}
G -->|no| D
G -->|yes| H(["Leg classified unresolvable"])
classDef good fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E,stroke-width:2px
classDef bad fill:#F6E9E9,stroke:#9A3B3B,color:#1C221E
classDef sys fill:#EEF2ED,stroke:#7D877F,color:#1C221E
class E good
class H bad
class A,B,C,F sys| You see | Underneath | Exits |
|---|---|---|
| Each leg: Pending → Won / Lost / Void. The parlay stays Open until every leg is final. No cash-out at launch. | Outcome Market evidence (determination, dispute or correction state, finality, settlement value, cancellation, payout availability) → normalized source state → canonical result under the rulebook version fixed at execution. Conflicting or unavailable evidence → resolution.suspended, never silently collapsed. A later Outcome Market corrections opens a new case; it never rewrites your trade. | Long-stop (parameters still TBD) caps how long collateral can stay locked; at the absolute outer deadline any unresolved leg becomes unresolvable. |
Settlement
flowchart TD
A{"All legs final - combined result?"} -->|every leg won| W["WIN"]
A -->|any leg lost, none void| L["LOSS"]
A -->|any leg void, cancelled, invalid or unresolvable| V["REFUND"]
W --> W1{"Custody branch"}
W1 -->|A| W2["Maker vault requests HyperCore transfer of L to your vault - shows Settling"]
W2 --> W3["Operator confirms the transfer arrived"]
W3 --> W4(["Available +P (your S released plus L received)"])
W1 -->|B| W4
L --> L1(["Locked -S, your stake now belongs to the maker"])
V --> V1(["Locked -S, Available +S · exact stake back"])
classDef good fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E,stroke-width:2px
classDef bad fill:#F6E9E9,stroke:#9A3B3B,color:#1C221E
classDef sys fill:#EEF2ED,stroke:#7D877F,color:#1C221E
class W4,V1 good
class L1 bad
class W2,W3 sys| You see | Underneath | Exits |
|---|---|---|
Win: Available rises by P. Loss: Locked drops by S, nothing comes back. Refund: exact S returns to Available. Exactly one of the three, ever. | Settlement amounts and recipients are derived from the immutable trade; the maker approves nothing and supplies nothing. A: beginSettlement → CoreWriter spotSend → operator matches the exact HyperCore ledger event → finalizeSettlement; your vault refuses the credit until the USDC has physically arrived. B: settle releases, transfers and credits in one atomic transaction. Void needs no transfer on either branch. | Win/loss and refund are mutually exclusive. Void-adjusted settlement (payout on surviving legs) is a future option, not launch behavior. |
Withdraw
flowchart TD
A["Choose an amount up to Available"] --> B["Sign once (EIP-712) - Outcome relays and pays gas"]
B --> C{"Branch"}
C -->|A · HyperCore| D["Available to Withdrawing, CoreWriter sends"]
D --> E["Operator confirms the HyperCore payment"]
E --> F(["USDC in your HyperCore wallet"])
C -->|B · HyperEVM| G["Vault transfers ERC-20 USDC immediately"]
G --> H(["USDC in your HyperEVM wallet"])
H -.->|optional| I["Move back to HyperCore"]
A -.->|amount above Available, or locked in an open trade| X(["Rejected"])
classDef user fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E
classDef good fill:#E6F1EE,stroke:#2F6F5E,color:#1C221E,stroke-width:2px
classDef bad fill:#F6E9E9,stroke:#9A3B3B,color:#1C221E
classDef sys fill:#EEF2ED,stroke:#7D877F,color:#1C221E
class A,B,I user
class F,H good
class X bad
class D,E,G sys| You see | Underneath | Exits |
|---|---|---|
| Enter amount → one wallet signature → funds arrive. You never need gas. | The signature binds owner, destination, amount, nonce and expiry (branch B also the vault) under the Outcome UserVault EIP-712 domain, so the relayer cannot change anything. A: available → withdrawing → CoreWriter → operator observes the payment → completeWithdrawal. B: available -= amount and safeTransfer in the same call. You may also call withdraw yourself and pay your own gas. | Anything above Available — including stake locked in open trades — is rejected. Nobody but you can withdraw from your vault (Admin is a relayer, not an owner). |
Invariants the customer can rely on
- You pay the maker's price
P— Outcome adds no fee, markup or rebate at launch. - Once you commit, the price cannot be edited; a different price is a new quote you must approve.
- You cannot be told "no capacity" after committing.
- Nothing is a trade until your
Sand the maker'sLare locked together and final. - Your trade's rules are frozen at execution; later Outcome Market corrections cannot change it.
- Exactly one ending: win (
+P), loss (−S), or refund (Sback). Any voided leg refunds the whole ticket. - Your locked collateral cannot stay locked past the long-stop outer deadline.
- The maker who priced your trade stays on the hook for it even if Outcome stops using that maker.
- Only you can withdraw from your vault; Outcome relays and pays gas but cannot redirect or resize a withdrawal.
Designed but not yet built (so not in the flow above)
Bounded trading/session keys (sign once for a session instead of per commit) · void-adjusted settlement on surviving legs · cash-out and secondary liquidity · same-event and in-play parlays · multiple makers competing on your RFQ · gasless deposits on branch B (permit / EIP-3009).