From 026b9b52dee8ee491de6259fe129662c6e55187e Mon Sep 17 00:00:00 2001 From: MightyMags Date: Fri, 12 Jun 2026 08:52:38 -0400 Subject: [PATCH 01/26] copy-trade app: scaffold + Liquid Glass UI + leader discovery + copy engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert examples/copy-trade from a CLI into a full Next.js app, reusing tap-trade's account infra (flash/session/signer/enable/funds/hooks/format). New: lib/leaders.ts (fstats V2/ER leaderboard via a same-origin /api/leaders route — fstats has no CORS), lib/copy-engine.ts (port the diff → collateral- ratio sizing → replay loop into a React hook with manual-confirm + capped auto-copy + budget + $11-floor skip + kill), and a Liquid Glass design system (OLED surface, mesh glow, frosted double-bezel panels). app.tsx wires wallet → enable → session signer → leaderboard → follow a leader → live book → mirror. Typechecks + builds clean. Co-Authored-By: Claude Fable 5 --- bun.lock | 35 +- examples/copy-trade/.env.example | 27 +- examples/copy-trade/app/api/leaders/route.ts | 39 ++ examples/copy-trade/app/globals.css | 190 +++++++ examples/copy-trade/app/icon.svg | 11 + examples/copy-trade/app/layout.tsx | 36 ++ examples/copy-trade/app/page.tsx | 12 + examples/copy-trade/components/app.tsx | 520 ++++++++++++++++++ .../copy-trade/components/rolling-number.tsx | 73 +++ examples/copy-trade/components/sheet.tsx | 64 +++ examples/copy-trade/lib/copy-engine.ts | 321 +++++++++++ examples/copy-trade/lib/enable.ts | 339 ++++++++++++ examples/copy-trade/lib/flash.ts | 48 ++ examples/copy-trade/lib/format.ts | 130 +++++ examples/copy-trade/lib/funds.ts | 218 ++++++++ examples/copy-trade/lib/hooks.ts | 474 ++++++++++++++++ examples/copy-trade/lib/leaders.ts | 76 +++ examples/copy-trade/lib/session.ts | 268 +++++++++ examples/copy-trade/lib/signer.ts | 40 ++ examples/copy-trade/next-env.d.ts | 6 + examples/copy-trade/next.config.ts | 30 + examples/copy-trade/package.json | 27 +- examples/copy-trade/postcss.config.mjs | 14 + examples/copy-trade/src/index.ts | 220 -------- examples/copy-trade/tsconfig.json | 22 +- 25 files changed, 2994 insertions(+), 246 deletions(-) create mode 100644 examples/copy-trade/app/api/leaders/route.ts create mode 100644 examples/copy-trade/app/globals.css create mode 100644 examples/copy-trade/app/icon.svg create mode 100644 examples/copy-trade/app/layout.tsx create mode 100644 examples/copy-trade/app/page.tsx create mode 100644 examples/copy-trade/components/app.tsx create mode 100644 examples/copy-trade/components/rolling-number.tsx create mode 100644 examples/copy-trade/components/sheet.tsx create mode 100644 examples/copy-trade/lib/copy-engine.ts create mode 100644 examples/copy-trade/lib/enable.ts create mode 100644 examples/copy-trade/lib/flash.ts create mode 100644 examples/copy-trade/lib/format.ts create mode 100644 examples/copy-trade/lib/funds.ts create mode 100644 examples/copy-trade/lib/hooks.ts create mode 100644 examples/copy-trade/lib/leaders.ts create mode 100644 examples/copy-trade/lib/session.ts create mode 100644 examples/copy-trade/lib/signer.ts create mode 100644 examples/copy-trade/next-env.d.ts create mode 100644 examples/copy-trade/next.config.ts create mode 100644 examples/copy-trade/postcss.config.mjs delete mode 100644 examples/copy-trade/src/index.ts diff --git a/bun.lock b/bun.lock index d4251b6..3361735 100644 --- a/bun.lock +++ b/bun.lock @@ -17,15 +17,30 @@ }, }, "examples/copy-trade": { - "name": "example-copy-trade", + "name": "copy-trade", "version": "0.1.0", "dependencies": { + "@coral-xyz/anchor": "^0.30.1", + "@magicblock-labs/gum-sdk": "^3.0.10", + "@solana/spl-token": "^0.4.14", + "@solana/wallet-adapter-base": "^0.9.27", + "@solana/wallet-adapter-phantom": "^0.9.29", + "@solana/wallet-adapter-react": "^0.15.39", + "@solana/wallet-adapter-solflare": "^0.6.28", "@solana/web3.js": "^1.98.2", "flash-v2": "workspace:*", + "next": "^15.5.19", + "react": "^19.2.0", + "react-dom": "^19.2.0", }, "devDependencies": { + "@tailwindcss/postcss": "^4.3.0", + "@types/bn.js": "^5.1.5", "@types/node": "^22.10.0", - "typescript": "^5.6.0", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "tailwindcss": "^4.3.0", + "typescript": "^5.9.0", }, }, "examples/tap-trade": { @@ -506,6 +521,8 @@ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + "copy-trade": ["copy-trade@workspace:examples/copy-trade"], + "cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], @@ -570,8 +587,6 @@ "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], - "example-copy-trade": ["example-copy-trade@workspace:examples/copy-trade"], - "example-template": ["example-template@workspace:examples/_template"], "exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], @@ -912,7 +927,7 @@ "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], - "superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + "superstruct": ["superstruct@0.15.5", "", {}, "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ=="], "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], @@ -958,7 +973,7 @@ "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], - "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], "vlq": ["vlq@1.0.1", "", {}, "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w=="], @@ -992,8 +1007,6 @@ "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@coral-xyz/anchor/superstruct": ["superstruct@0.15.5", "", {}, "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ=="], - "@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@solana/accounts/@solana/codecs-core": ["@solana/codecs-core@6.9.0", "", { "dependencies": { "@solana/errors": "6.9.0" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-F2BmLecG/1nTtnjyD509NsEc254pxJKa2bpvotymv1lL1WfEn3zchcZ9SMIiLyL4G6J8b9F3OKIq2YSZho2AOQ=="], @@ -1140,12 +1153,12 @@ "@solana/wallet-adapter-base/eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + "@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + "@solflare-wallet/sdk/bs58": ["bs58@5.0.0", "", { "dependencies": { "base-x": "^4.0.0" } }, "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ=="], "@solflare-wallet/sdk/eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], - "@solflare-wallet/sdk/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg=="], @@ -1174,6 +1187,8 @@ "jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + "jayson/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "jest-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], diff --git a/examples/copy-trade/.env.example b/examples/copy-trade/.env.example index e03df22..1a1e9ec 100644 --- a/examples/copy-trade/.env.example +++ b/examples/copy-trade/.env.example @@ -1,5 +1,22 @@ -# copy-trade — LEADER is required; everything else optional (dry-run default) -LEADER= # wallet pubkey to mirror (any owner with live V2 positions) -MAX_FOLLOW_USD=100 # hard cap per mirrored trade -# RATIO=0.1 # override collateral-ratio sizing with a fixed ratio -# FOLLOWER_KEYPAIR= # + run with `-- --execute` to sign for real (MAINNET wallet!) +# copy-trade — copy to .env.local and fill in ONE thing: your RPC key. +# cp .env.example .env.local +# +# Flash Trade V2 runs on Solana MAINNET (real funds — start small). You connect a +# wallet and Enable One-Click Trading; mirrors sign with a session key, no popups. + +NEXT_PUBLIC_FLASH_NETWORK=mainnet + +# ── RPC ─────────────────────────────────────────────────────────────────────── +# Base-chain RPC (setup / deposit / withdraw) — browser-direct to a CORS-friendly +# RPC. A FREE key from helius.dev / triton.one / quicknode.com works. This URL +# ships in the browser bundle, so use a DOMAIN-RESTRICTED key for deploys. +NEXT_PUBLIC_BASE_RPC=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY_HERE +# Ephemeral Rollup RPC (trading + the live leader stream) — serves CORS, direct. +NEXT_PUBLIC_ER_RPC=https://flash.magicblock.xyz + +# The leaderboard is fetched server-side from the public fstats V2 stats API via +# /api/leaders (it has no CORS). No key needed. Override only if it ever moves: +# FSTATS_BASE=https://fstats.io/v2/api/v1 + +# Override the hosted Flash V2 API (rarely needed). +# NEXT_PUBLIC_FLASH_API_BASE=https://flashapi.trade/v2 diff --git a/examples/copy-trade/app/api/leaders/route.ts b/examples/copy-trade/app/api/leaders/route.ts new file mode 100644 index 0000000..3f01960 --- /dev/null +++ b/examples/copy-trade/app/api/leaders/route.ts @@ -0,0 +1,39 @@ +// ───────────────────────────────────────────────────────────────────────────── +// app/api/leaders/route.ts — same-origin read-only proxy for the PUBLIC fstats +// V2/ER leaderboard. WHY IT EXISTS: fstats serves leader rankings (win rate, +// PnL) for the MagicBlock ER but does NOT send CORS headers, so a browser can't +// fetch it directly. This route fetches it server-side and hands the JSON back. +// It carries NO keys and reaches NO RPC — it only relays public stats. (Live +// LEADER POSITIONS come straight from flashapi.trade over the owner WS, which +// DOES serve CORS — see lib/copy-engine.ts; this is discovery only.) +// ───────────────────────────────────────────────────────────────────────────── + +export const runtime = "nodejs"; + +const FSTATS = "https://fstats.io/v2/api/v1"; + +export async function GET(): Promise { + try { + const [lbRes, openRes] = await Promise.all([ + fetch(`${FSTATS}/leaderboards/pnl`, { cache: "no-store" }), + fetch(`${FSTATS}/positions/open`, { cache: "no-store" }), + ]); + if (!lbRes.ok) throw new Error(`fstats leaderboard ${lbRes.status}`); + const lb = (await lbRes.json()) as { leaderboard?: unknown[] }; + // positions/open is best-effort — it only enriches rows with "live now" counts + let openByOwner: Record = {}; + if (openRes.ok) { + const open = (await openRes.json()) as { positions?: Array<{ owner: string }> }; + openByOwner = (open.positions ?? []).reduce>((acc, p) => { + acc[p.owner] = (acc[p.owner] ?? 0) + 1; + return acc; + }, {}); + } + return Response.json( + { leaders: lb.leaderboard ?? [], openByOwner }, + { headers: { "Cache-Control": "public, max-age=10" } }, + ); + } catch (e) { + return Response.json({ error: (e as Error).message }, { status: 502 }); + } +} diff --git a/examples/copy-trade/app/globals.css b/examples/copy-trade/app/globals.css new file mode 100644 index 0000000..0a841f3 --- /dev/null +++ b/examples/copy-trade/app/globals.css @@ -0,0 +1,190 @@ +/* ───────────────────────────────────────────────────────────────────────────── + app/globals.css — the LIQUID GLASS design language for copy-trade. + Deep OLED surface lit by a faint mesh of emerald/teal/rose glow orbs; every + surface is frosted glass: translucent fill + backdrop blur + a hairline white + edge + an inset specular highlight that reads as a beveled top lip. Major + containers use the Double-Bezel (an outer tray cradling an inner glass plate, + concentric squircle radii). Flash brand survives in the mint LONG / rose SHORT + and the gold leaderboard accent. Motion is heavy and springy (one easing). + THE HARD PART: blur only on fixed/non-scrolling glass; transform/opacity only. + Tailwind v4. + ───────────────────────────────────────────────────────────────────────── */ + +@import "tailwindcss"; + +@theme { + /* ── surface ─────────────────────────────────────────────────────────────── */ + --color-bg: #060807; /* near-OLED, faintly green-cold black */ + --color-bg2: #0a0d0c; /* lifted base behind glass */ + --color-ink: #eef2f0; + --color-dim: #9aa6a1; + --color-faint: #5b655f; + + /* ── semantics (Flash brand) ─────────────────────────────────────────────── */ + --color-long: #34d399; /* LONG · live · profit */ + --color-short: #f43f5e; /* SHORT · loss */ + --color-gold: #fbbf24; /* leaderboard rank · win-rate halo */ + --color-info: #5b9dff; /* leader / stream accents */ + --color-accent: #ffe600; /* Flash bolt — the logo mark ONLY */ + + /* ── glass material ──────────────────────────────────────────────────────── */ + --glass-fill: rgb(255 255 255 / 0.045); + --glass-fill-2: rgb(255 255 255 / 0.075); /* raised glass (chips, active) */ + --glass-edge: rgb(255 255 255 / 0.09); /* the hairline lip */ + --glass-edge-2: rgb(255 255 255 / 0.16); /* emphasis lip */ + --glass-hi: rgb(255 255 255 / 0.14); /* specular top highlight */ + --tray: rgb(255 255 255 / 0.03); /* double-bezel outer tray */ + --tray-edge: rgb(255 255 255 / 0.06); + + --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + --font-display: var(--font-grotesk), ui-sans-serif, system-ui, sans-serif; + --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, "JetBrains Mono", Consolas, monospace; + + /* the one easing — springy, weighty (Apple-ish) */ + --ease: cubic-bezier(0.32, 0.72, 0, 1); +} + +html { background: var(--color-bg); color-scheme: dark; } + +/* The lit surface: OLED black under a fixed mesh of soft glow orbs. */ +body { + min-height: 100dvh; + background: + radial-gradient(58% 50% at 16% -4%, rgb(52 211 153 / 0.11), transparent 70%), + radial-gradient(46% 42% at 92% 8%, rgb(91 157 255 / 0.08), transparent 70%), + radial-gradient(64% 60% at 52% 116%, rgb(244 63 94 / 0.06), transparent 72%), + var(--color-bg); + background-attachment: fixed; + font-feature-settings: "tnum" 1; + overscroll-behavior: none; +} + +/* Fixed film-grain — kills banding on the glow gradients. pointer-events none. */ +body::before { + content: ""; + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + opacity: 0.025; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); +} + +::selection { background: rgb(52 211 153 / 0.22); } + +:where(button, a, [role="button"]):focus-visible { + outline: 1px solid var(--color-long); + outline-offset: 2px; +} +:where(input, textarea):focus-visible { outline: none; } + +/* ── LIQUID GLASS material ───────────────────────────────────────────────────── + .glass: a frosted plate. The inset top highlight is the bevel lip; the ambient + drop lifts it off the surface. Apply to NON-scrolling panels only (blur cost). */ +.glass { + background: var(--glass-fill); + -webkit-backdrop-filter: blur(22px) saturate(155%); + backdrop-filter: blur(22px) saturate(155%); + border: 1px solid var(--glass-edge); + box-shadow: + inset 0 1px 0 0 var(--glass-hi), + inset 0 0 0 1px rgb(255 255 255 / 0.02), + 0 18px 44px -16px rgb(0 0 0 / 0.66); +} +.glass-2 { + background: var(--glass-fill-2); + -webkit-backdrop-filter: blur(26px) saturate(160%); + backdrop-filter: blur(26px) saturate(160%); + border: 1px solid var(--glass-edge-2); + box-shadow: + inset 0 1px 0 0 var(--glass-hi), + 0 14px 34px -14px rgb(0 0 0 / 0.6); +} +/* a flat (non-blur) glass for rows inside scrolling lists — cheap, no repaint */ +.glass-flat { + background: rgb(255 255 255 / 0.035); + border: 1px solid rgb(255 255 255 / 0.06); + box-shadow: inset 0 1px 0 0 rgb(255 255 255 / 0.06); +} + +/* Specular sheen — a glancing top-left light across a glass plate. */ +.spec { position: relative; isolation: isolate; } +.spec::after { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + pointer-events: none; + background: + linear-gradient(177deg, rgb(255 255 255 / 0.10) 0%, transparent 26%), + radial-gradient(120% 80% at 0% 0%, rgb(255 255 255 / 0.06), transparent 42%); + mix-blend-mode: screen; +} + +/* Double-bezel: an outer tray cradling a glass plate, concentric radii. */ +.bezel { + padding: 5px; + border-radius: 26px; + background: var(--tray); + border: 1px solid var(--tray-edge); + box-shadow: inset 0 1px 0 0 rgb(255 255 255 / 0.05); +} +.bezel-r { border-radius: 21px; } /* the inner plate radius (26 − 5) */ + +/* tinted glow halos for semantic glass (long/short/gold leader) */ +.halo-long { box-shadow: inset 0 1px 0 0 var(--glass-hi), 0 0 0 1px rgb(52 211 153 / 0.22), 0 14px 40px -14px rgb(52 211 153 / 0.28); } +.halo-short { box-shadow: inset 0 1px 0 0 var(--glass-hi), 0 0 0 1px rgb(244 63 94 / 0.22), 0 14px 40px -14px rgb(244 63 94 / 0.28); } +.halo-gold { box-shadow: inset 0 1px 0 0 var(--glass-hi), 0 0 0 1px rgb(251 191 36 / 0.30), 0 14px 40px -14px rgb(251 191 36 / 0.26); } + +/* ── motion ──────────────────────────────────────────────────────────────── */ +.lift { transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease); will-change: transform; } +.lift:hover { transform: translateY(-2px); } +.press { transition: transform 0.18s var(--ease); } +.press:active { transform: scale(0.985); } + +/* Magnetic CTA — inner icon disc drifts diagonally on hover (button-in-button). */ +.mag .disc { transition: transform 0.45s var(--ease); } +.mag:hover .disc { transform: translate(2px, -1px) scale(1.06); } + +/* Bottom sheets slide on transform; backdrop fades on opacity. */ +.sheet-up { transition: transform 380ms var(--ease); will-change: transform; } +.backdrop-fade { transition: opacity 240ms ease; } + +/* Slot-reel digit columns (RollingNumber) — the signature numeric tick. */ +.reel-col { transition: transform 420ms var(--ease); will-change: transform; } + +/* Fill confirmation sweep — celebrates an on-chain mirror confirm (no spinners). */ +@keyframes fill-flash { + 0% { filter: brightness(1.4); box-shadow: 0 0 0 0 rgb(52 211 153 / 0); } + 30% { box-shadow: 0 0 22px 2px rgb(52 211 153 / 0.22); } + 100% { filter: brightness(1); box-shadow: 0 0 0 0 rgb(52 211 153 / 0); } +} +.fill-flash { animation: fill-flash 0.5s var(--ease); } + +/* Live breathing — the stream dot, "mirroring…" states. */ +@keyframes soft-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } +.soft-pulse { animation: soft-pulse 1.7s ease-in-out infinite; } + +/* New rows (leader events, mirrored fills) slide + fade in on state change. */ +@keyframes row-in { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } } +.row-in { animation: row-in 0.4s var(--ease); } + +/* A glass plate entering (a leader picked, a sheet) — fade-up with a blur lift. */ +@keyframes glass-in { from { opacity: 0; transform: translateY(10px); filter: blur(6px); } to { opacity: 1; transform: translateY(0); filter: blur(0); } } +.glass-in { animation: glass-in 0.55s var(--ease); } + +/* Shimmer sweep for the live/auto-copy armed state (a moving specular band). */ +@keyframes shimmer { from { transform: translateX(-130%); } to { transform: translateX(130%); } } +.shimmer { position: relative; overflow: hidden; } +.shimmer::before { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient(100deg, transparent 30%, rgb(255 255 255 / 0.10) 50%, transparent 70%); + animation: shimmer 2.6s var(--ease) infinite; +} + +@media (prefers-reduced-motion: reduce) { + .lift, .press, .mag .disc, .sheet-up, .reel-col, .fill-flash, .soft-pulse, + .row-in, .glass-in, .shimmer::before { animation: none !important; transition: none !important; } +} diff --git a/examples/copy-trade/app/icon.svg b/examples/copy-trade/app/icon.svg new file mode 100644 index 0000000..c8bd830 --- /dev/null +++ b/examples/copy-trade/app/icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/examples/copy-trade/app/layout.tsx b/examples/copy-trade/app/layout.tsx new file mode 100644 index 0000000..7a02418 --- /dev/null +++ b/examples/copy-trade/app/layout.tsx @@ -0,0 +1,36 @@ +// ───────────────────────────────────────────────────────────────────────────── +// app/layout.tsx — root shell for the Liquid Glass copy-trade app. Loads the +// Space Grotesk display face and frames everything on the lit OLED surface +// (the glow mesh + grain live in globals.css). All live logic is client-side. +// ───────────────────────────────────────────────────────────────────────────── + +import type { Metadata, Viewport } from "next"; +import { Space_Grotesk } from "next/font/google"; +import "./globals.css"; + +const grotesk = Space_Grotesk({ + subsets: ["latin"], + weight: ["600", "700"], + variable: "--font-grotesk", + display: "swap", +}); + +export const metadata: Metadata = { + title: "copy-trade — mirror the best, on the rollup", + description: + "Rank real Flash V2 leaders by win rate, follow one, and mirror their trades onto your own account with collateral-ratio sizing — manual or capped auto-copy, non-custodial. Liquid Glass UI on a MagicBlock Ephemeral Rollup.", +}; + +export const viewport: Viewport = { + themeColor: "#0b0d0c", +}; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + + ); +} diff --git a/examples/copy-trade/app/page.tsx b/examples/copy-trade/app/page.tsx new file mode 100644 index 0000000..9bf6460 --- /dev/null +++ b/examples/copy-trade/app/page.tsx @@ -0,0 +1,12 @@ +// ───────────────────────────────────────────────────────────────────────────── +// app/page.tsx — the single route. Hands straight off to the client app: +// everything (wallet, stream, taps) is browser state by design. +// THE HARD PART: none here — see components/app.tsx for the orchestration. +// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +import App from "@/components/app"; + +export default function Page() { + return ; +} diff --git a/examples/copy-trade/components/app.tsx b/examples/copy-trade/components/app.tsx new file mode 100644 index 0000000..6c1760c --- /dev/null +++ b/examples/copy-trade/components/app.tsx @@ -0,0 +1,520 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/app.tsx — the copy-trade orchestrator, Liquid Glass. +// Flow: connect wallet → Enable One-Click Trading (account + session key) → +// pick a leader off the ranked board (or paste one) → their live book streams +// in → mirror their opens/closes onto YOUR account (manual one-tap by default, +// capped auto opt-in). The follower signs every mirror with their own session +// key; the app never holds keys and never moves funds outside an explicit trade. +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom"; +import { ConnectionProvider, WalletProvider, useAnchorWallet, useWallet } from "@solana/wallet-adapter-react"; +import { SolflareWalletAdapter } from "@solana/wallet-adapter-solflare"; +import { type PositionMetrics } from "flash-v2"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { flash, explorerTx } from "@/lib/flash"; +import { computePositionView, fmtUsd, shortKey } from "@/lib/format"; +import { useBalances, useBasketBalance, useOwner, useUsdcMint } from "@/lib/hooks"; +import { loadSession, type LoadedSession } from "@/lib/session"; +import { makeSessionSigner } from "@/lib/signer"; +import { enableOneClickTrading, type EnableState } from "@/lib/enable"; +import { useLeaderboard, isLikelyPubkey, type LeaderRow } from "@/lib/leaders"; +import { useCopyEngine, type CopyConfig } from "@/lib/copy-engine"; + +// ── providers ──────────────────────────────────────────────────────────────── +export default function App() { + const wallets = useMemo(() => [new PhantomWalletAdapter(), new SolflareWalletAdapter()], []); + return ( + + + + + + ); +} + +// ── live prices (for honest mark-price PnL on both books) ────────────────────── +function usePrices(pollMs = 2000): Record { + const [prices, setPrices] = useState>({}); + useEffect(() => { + let dead = false; + const tick = async () => { + try { + const all = await flash.prices(); + if (dead) return; + const out: Record = {}; + for (const [sym, p] of Object.entries(all)) out[sym] = Number(p.priceUi); + setPrices(out); + } catch { /* keep last */ } + }; + void tick(); + const t = setInterval(tick, pollMs); + return () => { dead = true; clearInterval(t); }; + }, [pollMs]); + return prices; +} + +function AppInner() { + const { publicKey, connected } = useWallet(); + const walletCtx = useWallet(); + const anchorWallet = useAnchorWallet(); + const owner = publicKey?.toBase58() ?? null; + + const usdcMint = useUsdcMint(); + const { snapshot, loaded } = useOwner(owner); + const balances = useBalances(owner, usdcMint); + const { bal: basketBal, refresh: refreshBasket } = useBasketBalance(owner, snapshot?.basketPubkey ?? null, usdcMint); + const prices = usePrices(); + + const basketExists = Boolean(snapshot?.basketPubkey); + const inBasketUsd = basketBal?.inBasketUsd ?? 0; + // follower collateral the engine sizes against: deposited (free) + already at work + const marginInUse = Object.values(snapshot?.positionMetrics ?? {}).reduce((s, p) => s + (Number(p.collateralUsdUi) || 0), 0); + const followerCollateralUsd = inBasketUsd + marginInUse; + + // session signer (popup-free mirror signing) — present once Enabled + const [session, setSession] = useState(null); + useEffect(() => { setSession(owner ? loadSession(owner) : null); }, [owner]); + const signer = useMemo(() => { + if (!walletCtx.publicKey || !walletCtx.signTransaction || !walletCtx.signAllTransactions || !session) return null; + const sw = { publicKey: walletCtx.publicKey, signTransaction: walletCtx.signTransaction, signAllTransactions: walletCtx.signAllTransactions }; + return makeSessionSigner(sw, session, flash.network); + }, [walletCtx.publicKey, walletCtx.signTransaction, walletCtx.signAllTransactions, session]); + + // ── leader selection ──────────────────────────────────────────────────────── + const board = useLeaderboard(); + const [leader, setLeader] = useState(null); + const [paste, setPaste] = useState(""); + const leaderRow = useMemo(() => board.leaders.find((l) => l.owner === leader) ?? null, [board.leaders, leader]); + + // ── copy config ────────────────────────────────────────────────────────────── + const [cfg, setCfg] = useState({ mode: "manual", armed: false, budgetUsd: 50, maxPerTradeUsd: 15 }); + const setC = (p: Partial) => setCfg((c) => ({ ...c, ...p })); + + const engine = useCopyEngine({ leader, signer, followerCollateralUsd, config: cfg }); + + // ── enable (account setup + session key) ───────────────────────────────────── + const [enabling, setEnabling] = useState(false); + const [enableState, setEnableState] = useState(null); + const [err, setErr] = useState(null); + const runEnable = useCallback(async () => { + if (enabling || !walletCtx.publicKey || !anchorWallet || !walletCtx.signTransaction) return; + setEnabling(true); setErr(null); + try { + const res = await enableOneClickTrading({ + wallet: { publicKey: walletCtx.publicKey, signTransaction: walletCtx.signTransaction, signAllTransactions: walletCtx.signAllTransactions }, + anchorWallet, snapshot, usdcMint, + balances: { sol: balances.sol, usdc: balances.usdc }, + onStep: setEnableState, onLog: () => {}, + }); + if (res.session) setSession(res.session); + void balances.refresh(); void refreshBasket(); + } catch (e) { setErr((e as Error).message); } + finally { setEnabling(false); } + }, [enabling, walletCtx, anchorWallet, snapshot, usdcMint, balances, refreshBasket]); + + const followerPositions = Object.values(snapshot?.positionMetrics ?? {}); + const ready = basketExists && Boolean(signer); + + return ( +
+
walletCtx.select?.(walletCtx.wallets[0]?.adapter.name ?? null)} + streamStatus={leader ? engine.status : null} + /> + + {!leader ? ( + { setLeader(o); setPaste(""); }} + prices={prices} + /> + ) : ( +
+ setLeader(null)} + /> + +
+ )} +

+ Flash V2 mainnet · real funds. Copy trading carries the leader's risk — start small, watch the spread, keep your budget what you can lose. +

+
+ ); +} + +// ════════════════════════════════════════════════════════════════════════════ +// Header +// ════════════════════════════════════════════════════════════════════════════ +function Header({ connected, address, inBasketUsd, onConnect, streamStatus }: { + connected: boolean; address: string | null; inBasketUsd: number | null; onConnect: () => void; streamStatus: string | null; +}) { + return ( +
+
+ + + +
+

Copy Trade

+

Flash V2 · ephemeral rollup

+
+ {streamStatus && ( + + + {streamStatus} + + )} +
+ {connected ? ( +
+ {inBasketUsd !== null && ( + {fmtUsd(inBasketUsd)} + )} + {address ? shortKey(address) : ""} +
+ ) : ( + + )} +
+ ); +} + +// ════════════════════════════════════════════════════════════════════════════ +// Discover — the leaderboard + paste-a-leader +// ════════════════════════════════════════════════════════════════════════════ +function Discover({ board, paste, setPaste, onFollow, prices }: { + board: ReturnType; paste: string; setPaste: (s: string) => void; onFollow: (o: string) => void; prices: Record; +}) { + void prices; + return ( +
+
+ leaderboard · live ER +

Follow the proven.

+

Real Flash V2 traders, ranked by net PnL and win rate on the rollup. Pick one — or paste any wallet — and mirror their moves onto your account.

+
+ +
+ +
+ +
+ {board.loading && board.leaders.length === 0 && ( + Array.from({ length: 6 }).map((_, i) =>
) + )} + {board.error && board.leaders.length === 0 && ( +
Couldn't load the leaderboard: {board.error}
+ )} + {board.leaders.slice(0, 12).map((l) => ( + 0} openCount={board.openByOwner[l.owner] ?? 0} onFollow={() => onFollow(l.owner)} /> + ))} +
+
+ ); +} + +function LeaderCard({ l, live, openCount, onFollow }: { l: LeaderRow; live: boolean; openCount: number; onFollow: () => void }) { + const win = l.win_rate; + return ( + + ); +} + +// ════════════════════════════════════════════════════════════════════════════ +// LeaderPanel — the followed leader's live book +// ════════════════════════════════════════════════════════════════════════════ +function LeaderPanel({ leader, row, positions, prices, status, onUnfollow }: { + leader: string; row: LeaderRow | null; positions: PositionMetrics[]; prices: Record; status: string; onUnfollow: () => void; +}) { + return ( +
+
+
+ {row ? `#${row.rank}` : "•"} +
+

{shortKey(leader)}

+

{row ? `${row.win_rate.toFixed(0)}% win · ${row.num_trades} trades` : "following"}

+
+
+ +
+ +
+ leader's book + + {positions.length} open +
+ +
+ {positions.length === 0 && ( +
{status === "live" ? "leader is flat — waiting for their next move…" : "connecting to the leader stream…"}
+ )} + {positions.map((p) => )} +
+
+ ); +} + +function PositionRow({ p, mark }: { p: PositionMetrics; mark: number | null }) { + const view = computePositionView(p, mark); + const long = p.sideUi.toUpperCase() === "LONG"; + return ( +
+
+ {long ? "LONG" : "SHORT"} +
+

{p.marketSymbol}

+

{fmtUsd(p.sizeUsdUi)} · {Number(p.leverageUi || 0).toFixed(1)}× · entry {Number(p.entryPriceUi).toLocaleString()}

+
+
+
+

= 0 ? "text-long" : "text-short"}`}>{view ? `${view.pnlUsd >= 0 ? "+" : ""}${fmtUsd(view.pnlUsd)}` : "—"}

+

{view ? `${view.pnlPct >= 0 ? "+" : ""}${view.pnlPct.toFixed(1)}%` : ""}

+
+
+ ); +} + +// ════════════════════════════════════════════════════════════════════════════ +// Console — your status, controls, pending mirrors, fills, your book +// ════════════════════════════════════════════════════════════════════════════ +function Console({ ready, connected, inBasketUsd, loaded, enabling, enableState, err, onEnable, cfg, setC, engine, followerPositions, prices }: { + ready: boolean; connected: boolean; inBasketUsd: number; loaded: boolean; + enabling: boolean; enableState: EnableState | null; err: string | null; onEnable: () => void; + cfg: CopyConfig; setC: (p: Partial) => void; engine: ReturnType; + followerPositions: PositionMetrics[]; prices: Record; +}) { + // gate: connect → enable → fund → copy + if (!connected) return ; + if (!loaded) return ; + if (!ready) return ( + + ); + if (inBasketUsd < 1) return ( + + ); + + return ( +
+ + + + +
+ ); +} + +function GateCard({ title, body, action, error, pulse }: { title: string; body: string; action?: { label: string; onClick: () => void; busy?: boolean }; error?: string | null; pulse?: boolean }) { + return ( +
+

{title}

+

{body}

+ {error &&

{error}

} + {action && ( + + )} +
+ ); +} + +function Controls({ cfg, setC, spent, inBasketUsd }: { cfg: CopyConfig; setC: (p: Partial) => void; spent: number; inBasketUsd: number }) { + const auto = cfg.mode === "auto"; + return ( +
+
+
+

Copy controls

+

balance {fmtUsd(inBasketUsd)} · spent {fmtUsd(spent)} / {fmtUsd(cfg.budgetUsd)}

+
+ {/* mode toggle */} +
+ + +
+
+ +
+ setC({ budgetUsd: v })} /> + setC({ maxPerTradeUsd: v })} /> +
+ + {auto && ( + + )} + {auto && cfg.armed && ( +

mirrors execute automatically within your caps while this tab stays open. Keep the budget what you can lose.

+ )} +
+ ); +} + +function NumField({ label, value, onChange }: { label: string; value: number; onChange: (v: number) => void }) { + return ( + + ); +} + +function Pending({ engine }: { engine: ReturnType }) { + if (engine.pending.length === 0) return null; + return ( +
+ {engine.pending.map(({ event, sized }) => { + const opening = event.kind === "OPEN" || event.kind === "GROW"; + const long = event.side === "LONG"; + return ( +
+
+
+ {event.kind} + {event.side} {event.market} +
+ leader Δ{fmtUsd(event.deltaUsd)} +
+

+ {opening + ? `mirror ${fmtUsd(sized.collateralUsd ?? 0)} × ${event.leverage.toFixed(1)}× = ${fmtUsd(sized.sizeUsd)}` + : event.kind === "CLOSE" ? "close your matching position" : `trim ${fmtUsd(sized.sizeUsd)}`} + · ratio {sized.ratio.toFixed(2)} +

+
+ + +
+
+ ); + })} +
+ ); +} + +function Fills({ engine }: { engine: ReturnType }) { + if (engine.fills.length === 0) return null; + return ( +
+

mirror log

+
+ {engine.fills.map((f) => ( +
+
+ + + {f.event.kind} {f.event.side} {f.event.market} + {f.status === "skipped" && — {f.note}} + {f.status === "error" && — {f.note}} + +
+ {f.signature + ? {f.confirmMs}ms · {shortKey(f.signature)} + : {f.status}} +
+ ))} +
+
+ ); +} + +function YourBook({ positions, prices }: { positions: PositionMetrics[]; prices: Record }) { + return ( +
+
+ your mirror + + {positions.length} open +
+ {positions.length === 0 + ?

no mirrored positions yet

+ :
{positions.map((p) => )}
} +
+ ); +} diff --git a/examples/copy-trade/components/rolling-number.tsx b/examples/copy-trade/components/rolling-number.tsx new file mode 100644 index 0000000..248c09f --- /dev/null +++ b/examples/copy-trade/components/rolling-number.tsx @@ -0,0 +1,73 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/rolling-number.tsx — bloxwap's signature slot-reel price: each +// digit is a vertical 0-9 column, translateY'd to the active digit with the +// app's spring-out easing. THE HARD PART: column keys must be stable while the +// string length is constant, or React remounts and the roll never animates. +// GOTCHAS.md → (pure UI; no API gotchas) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { useEffect, useState } from "react"; + +const DIGITS = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; + +function Reel({ digit }: { digit: number }) { + return ( + + + {DIGITS.map((d) => ( + + {d} + + ))} + + + ); +} + +/** + * Renders a pre-formatted numeric string (e.g. "$64.42") with rolling digits. + * Separators ($ . , −) are static layers. Monospace + tabular so columns + * never shift horizontally; a length change remounts (acceptable jump). + */ +export default function RollingNumber({ + value, + className = "", +}: { + value: string | null; + className?: string; +}) { + // HYDRATION GUARD: render plain text until after mount — the reels' inline + // translateY styles must never participate in hydration (React #418). + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + if (value === null || !mounted) { + return {value ?? "$—"}; + } + const chars = Array.from(value); + return ( + // items-start (NOT baseline): every child is an identical h-[1em] + // leading-[1em] box, so top-aligning the boxes puts separators ($ . ,) + // and reel digits on the exact same optical baseline. Baseline alignment + // breaks here because an overflow-hidden inline-block's flex baseline is + // its box bottom, not its glyph baseline — the $ drifts. + + {chars.map((ch, i) => + /[0-9]/.test(ch) ? ( + + ) : ( + + {ch} + + ), + )} + + ); +} diff --git a/examples/copy-trade/components/sheet.tsx b/examples/copy-trade/components/sheet.tsx new file mode 100644 index 0000000..90828b6 --- /dev/null +++ b/examples/copy-trade/components/sheet.tsx @@ -0,0 +1,64 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/sheet.tsx — the one bottom sheet: hairline notch, slide-up +// transform, backdrop tap-to-close, Escape, max-h-[80dvh]. THE HARD PART: +// content stays MOUNTED while hidden (translate-y-full + pointer-events-none) +// so live state inside (fee previews, step rows) never resets mid-flow. +// GOTCHAS.md → (pure UI; no API gotchas) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { useEffect } from "react"; + +export default function Sheet({ + open, + onClose, + label, + locked = false, + children, +}: { + open: boolean; + onClose: () => void; + /** Accessible name for the dialog. */ + label: string; + /** True while mid-signing — the sheet must not be escapable then. */ + locked?: boolean; + children: React.ReactNode; +}) { + useEffect(() => { + if (!open || locked) return; + const onKey = (e: KeyboardEvent) => { + if (e.key === "Escape") onClose(); + }; + window.addEventListener("keydown", onKey); + return () => window.removeEventListener("keydown", onKey); + }, [open, locked, onClose]); + + return ( +
+
+
+
+ +
+
+ {children} +
+
+
+ ); +} diff --git a/examples/copy-trade/lib/copy-engine.ts b/examples/copy-trade/lib/copy-engine.ts new file mode 100644 index 0000000..4aa0a83 --- /dev/null +++ b/examples/copy-trade/lib/copy-engine.ts @@ -0,0 +1,321 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/copy-engine.ts — the copy loop, browser-side and non-custodial. +// +// THE HARD PARTS (all carried over from the CLI, now live in a hook): +// 1. There is NO "trades feed" — you DIFF consecutive `basket` frames of the +// leader's owner stream into events: OPEN / GROW / SHRINK / CLOSE. +// 2. You size by COLLATERAL RATIO (follower collateral ÷ leader collateral), +// never raw size — that is how a small follower copying a whale gets +// instantly liquidated. Then a hard per-trade cap and a budget ceiling. +// 3. The follower signs their OWN mirror txs with their OWN session key — the +// app never holds keys and never moves funds outside an explicit trade. +// 4. metrics frames re-price the SAME position every ~250ms; only `basket` +// frames are settlement truth, so we diff ONLY basket frames (GOTCHAS §9). +// 5. The $11-after-fees floor (GOTCHAS §16): a mirror whose collateral lands +// below the floor is SKIPPED and SAID SO — never silently up-sized. +// +// Default mode is MANUAL: every event becomes a one-tap "mirror this?" card. +// AUTO is opt-in, armed, and bounded by the budget + per-trade cap + kill switch. +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { useCallback, useEffect, useRef, useState } from "react"; +import { + subscribeOwner, + RECOMMENDED_MIN_COLLATERAL_USD, + type BasketSnapshot, + type PositionMetrics, + type TradeType, +} from "flash-v2"; +import { flash } from "./flash"; +import type { ActiveSigner } from "./signer"; + +// ── events ───────────────────────────────────────────────────────────────── +export type MirrorKind = "OPEN" | "GROW" | "SHRINK" | "CLOSE"; + +export interface MirrorEvent { + id: string; + at: number; + kind: MirrorKind; + market: string; + side: TradeType; + /** leader size change, USD notional */ + deltaUsd: number; + /** leader's current leverage (OPEN/GROW) */ + leverage: number; + leaderCollateralUsd: number; +} + +/** A position is keyed market+side (V2 allows one long + one short per market). */ +const keyOf = (p: PositionMetrics): string => `${p.marketSymbol}:${p.sideUi.toUpperCase()}`; + +/** Diff two leader snapshots into mirror events. Pure — also unit-testable. */ +export function diffPositions(prev: BasketSnapshot | undefined, next: BasketSnapshot): Omit[] { + const out: Omit[] = []; + const before = new Map(Object.values(prev?.positionMetrics ?? {}).map((p) => [keyOf(p), p])); + const after = new Map(Object.values(next.positionMetrics ?? {}).map((p) => [keyOf(p), p])); + + for (const [k, now] of after) { + const was = before.get(k); + const side = now.sideUi.toUpperCase() as TradeType; + const sizeNow = Number(now.sizeUsdUi); + const lev = Number.parseFloat(now.leverageUi) || 1; + const col = Number(now.collateralUsdUi); + if (!was) { + out.push({ kind: "OPEN", market: now.marketSymbol, side, deltaUsd: sizeNow, leverage: lev, leaderCollateralUsd: col }); + } else { + const delta = sizeNow - Number(was.sizeUsdUi); + if (Math.abs(delta) > 0.01) { + out.push({ + kind: delta > 0 ? "GROW" : "SHRINK", + market: now.marketSymbol, side, deltaUsd: Math.abs(delta), leverage: lev, leaderCollateralUsd: col, + }); + } + } + } + for (const [k, was] of before) { + if (!after.has(k)) { + out.push({ + kind: "CLOSE", market: was.marketSymbol, side: was.sideUi.toUpperCase() as TradeType, + deltaUsd: Number(was.sizeUsdUi), leverage: 1, leaderCollateralUsd: Number(was.collateralUsdUi), + }); + } + } + return out; +} + +// ── sizing ─────────────────────────────────────────────────────────────────── +export interface CopyConfig { + /** manual = surface a card per event; auto = execute when armed + in budget. */ + mode: "manual" | "auto"; + /** auto is only live while this is true (the arm toggle). */ + armed: boolean; + /** total USD the copier may ever deploy this session (budget ceiling). */ + budgetUsd: number; + /** hard cap on any single mirror's notional. */ + maxPerTradeUsd: number; + /** optional fixed ratio override; otherwise collateral ratio is used. */ + ratioOverride?: number; +} + +export interface SizedMirror { + /** follower collateral to post (USDC), null if this is a close. */ + collateralUsd: number | null; + /** mirror notional USD (size). */ + sizeUsd: number; + /** "0" full close, a USD string for partial, or null for opens. */ + closeUsdUi: string | null; + ratio: number; + /** present when the mirror can't be placed honestly — show, don't execute. */ + skip?: string; +} + +/** Size one event. Identical math for the preview card and the executed trade. */ +export function sizeMirror( + e: Pick, + cfg: CopyConfig, + followerCollateralUsd: number, + spentUsd: number, +): SizedMirror { + const ratio = + cfg.ratioOverride ?? + (e.leaderCollateralUsd > 0 && followerCollateralUsd > 0 ? followerCollateralUsd / e.leaderCollateralUsd : 0); + + if (e.kind === "CLOSE") { + return { collateralUsd: null, sizeUsd: e.deltaUsd * ratio, closeUsdUi: "0", ratio }; + } + if (e.kind === "SHRINK") { + const usd = Math.min(e.deltaUsd * ratio, cfg.maxPerTradeUsd); + return { collateralUsd: null, sizeUsd: usd, closeUsdUi: usd.toFixed(2), ratio }; + } + + // OPEN / GROW + const budgetLeft = Math.max(0, cfg.budgetUsd - spentUsd); + const sizeUsd = Math.min(e.deltaUsd * ratio, cfg.maxPerTradeUsd, budgetLeft * e.leverage); + const collateralUsd = sizeUsd / (e.leverage || 1); + if (ratio <= 0) return { collateralUsd, sizeUsd, closeUsdUi: null, ratio, skip: "no follower collateral to size against — deposit first" }; + if (budgetLeft <= 0) return { collateralUsd, sizeUsd, closeUsdUi: null, ratio, skip: "session budget reached" }; + // FLOORING collateral while keeping leader leverage would inflate the mirror + // (a $4 copy becoming $44) — too small to mirror honestly, so skip + say so. + if (collateralUsd < RECOMMENDED_MIN_COLLATERAL_USD) { + return { collateralUsd, sizeUsd, closeUsdUi: null, ratio, skip: `mirror too small ($${collateralUsd.toFixed(2)} collateral < $${RECOMMENDED_MIN_COLLATERAL_USD} floor)` }; + } + return { collateralUsd, sizeUsd, closeUsdUi: null, ratio }; +} + +// ── live fills ──────────────────────────────────────────────────────────────── +export interface MirrorFill { + id: string; + at: number; + event: MirrorEvent; + sized: SizedMirror; + status: "executing" | "done" | "skipped" | "error"; + signature?: string; + confirmMs?: number; + note?: string; +} + +export type EngineStatus = "idle" | "connecting" | "live" | "reconnecting" | "polling" | "stopped"; + +export interface CopyEngineState { + status: EngineStatus; + /** the leader's current live positions (rendered as their open book). */ + leaderPositions: PositionMetrics[]; + /** newest first — every event we surfaced (pending manual cards + history). */ + pending: { event: MirrorEvent; sized: SizedMirror }[]; + fills: MirrorFill[]; + spentUsd: number; +} + +let _seq = 0; +const uid = () => `${++_seq}`; + +/** + * The copy engine. Streams `leader`, diffs basket frames, and either queues a + * one-tap mirror (manual) or executes it with the follower's session signer + * (auto, armed, in budget). `kill()` stops the stream and clears the queue. + */ +export function useCopyEngine(args: { + leader: string | null; + signer: ActiveSigner | null; + followerCollateralUsd: number; + config: CopyConfig; +}): CopyEngineState & { + confirm: (eventId: string) => void; + dismiss: (eventId: string) => void; +} { + const { leader, signer, followerCollateralUsd, config } = args; + const [state, setState] = useState({ + status: "idle", + leaderPositions: [], + pending: [], + fills: [], + spentUsd: 0, + }); + + // refs so the stream callback always sees the latest config/signer/collateral + const cfgRef = useRef(config); + cfgRef.current = config; + const signerRef = useRef(signer); + signerRef.current = signer; + const colRef = useRef(followerCollateralUsd); + colRef.current = followerCollateralUsd; + const spentRef = useRef(0); + + // ── execute one sized mirror against the follower's account ──────────────── + const execute = useCallback(async (event: MirrorEvent, sized: SizedMirror) => { + const sg = signerRef.current; + const fillId = uid(); + if (!sg) return; + const startFill: MirrorFill = { id: fillId, at: Date.now(), event, sized, status: "executing" }; + setState((s) => ({ ...s, fills: [startFill, ...s.fills].slice(0, 60) })); + const patch = (p: Partial) => + setState((s) => ({ ...s, fills: s.fills.map((f) => (f.id === fillId ? { ...f, ...p } : f)) })); + try { + if (event.kind === "OPEN" || event.kind === "GROW") { + const built = await flash.openPosition({ + inputTokenSymbol: "USDC", outputTokenSymbol: event.market, + inputAmountUi: (sized.collateralUsd ?? 0).toFixed(2), leverage: event.leverage, + tradeType: event.side, orderType: "MARKET", owner: sg.owner, + slippagePercentage: "0.8", // wider than the leader — you fill a beat later + ...sg.tradeFields, + }); + if (!built.transactionBase64) throw new Error("no transaction from open-position"); + const { signature, confirmMs } = await sg.sendTrade(built.transactionBase64); + spentRef.current += sized.collateralUsd ?? 0; + setState((s) => ({ ...s, spentUsd: spentRef.current })); + patch({ status: "done", signature, confirmMs }); + } else { + const built = await flash.closePosition({ + marketSymbol: event.market, side: event.side, + inputUsdUi: sized.closeUsdUi ?? "0", withdrawTokenSymbol: "USDC", owner: sg.owner, + ...sg.tradeFields, + }); + if (!built.transactionBase64) throw new Error("no transaction from close-position"); + const { signature, confirmMs } = await sg.sendTrade(built.transactionBase64); + patch({ status: "done", signature, confirmMs }); + } + } catch (e) { + patch({ status: "error", note: (e as Error).message }); + } + }, []); + + // ── route an event: skip · auto-execute · or queue for manual confirm ────── + const routeEvent = useCallback((raw: Omit) => { + const event: MirrorEvent = { ...raw, id: uid(), at: Date.now() }; + const sized = sizeMirror(event, cfgRef.current, colRef.current, spentRef.current); + if (sized.skip && event.kind !== "CLOSE" && event.kind !== "SHRINK") { + const skipFill: MirrorFill = { id: uid(), at: Date.now(), event, sized, status: "skipped", note: sized.skip }; + setState((s) => ({ ...s, fills: [skipFill, ...s.fills].slice(0, 60) })); + return; + } + const cfg = cfgRef.current; + if (cfg.mode === "auto" && cfg.armed && signerRef.current) { + void execute(event, sized); + } else { + setState((s) => ({ ...s, pending: [{ event, sized }, ...s.pending].slice(0, 24) })); + } + }, [execute]); + + // ── confirm / dismiss a queued manual mirror ─────────────────────────────── + const confirm = useCallback((eventId: string) => { + setState((s) => { + const hit = s.pending.find((p) => p.event.id === eventId); + if (hit) void execute(hit.event, hit.sized); + return { ...s, pending: s.pending.filter((p) => p.event.id !== eventId) }; + }); + }, [execute]); + const dismiss = useCallback((eventId: string) => { + setState((s) => ({ ...s, pending: s.pending.filter((p) => p.event.id !== eventId) })); + }, []); + + // ── the leader stream: diff basket frames, coalescing re-entrant frames ──── + useEffect(() => { + spentRef.current = 0; + setState({ status: leader ? "connecting" : "idle", leaderPositions: [], pending: [], fills: [], spentUsd: 0 }); + if (!leader) return; + + let prev: BasketSnapshot | undefined; + let processing = false; + let queued: BasketSnapshot | undefined; + let dead = false; + + const onBasket = (snap: BasketSnapshot) => { + if (dead) return; + setState((s) => ({ ...s, leaderPositions: Object.values(snap.positionMetrics ?? {}) })); + if (processing) { queued = snap; return; } + processing = true; + let next: BasketSnapshot | undefined = snap; + while (next) { + for (const raw of diffPositions(prev, next)) routeEvent(raw); + prev = structuredClone(next); + next = queued; + queued = undefined; + } + processing = false; + }; + + const stream = subscribeOwner({ + owner: leader, + network: flash.network, + onUpdate: (snap, source) => { + if (source === "metrics") { + // refresh the rendered book (live PnL) but never diff metrics frames + if (!dead) setState((s) => ({ ...s, leaderPositions: Object.values(snap.positionMetrics ?? {}) })); + return; + } + onBasket(snap); + }, + onStatus: (st) => { + if (dead) return; + const map: Record = { open: "live", connecting: "connecting", reconnecting: "reconnecting", polling: "polling", closed: "stopped" }; + setState((s) => ({ ...s, status: map[st] ?? s.status })); + }, + }); + + return () => { dead = true; stream.close(); }; + }, [leader, routeEvent]); + + return { ...state, confirm, dismiss }; +} diff --git a/examples/copy-trade/lib/enable.ts b/examples/copy-trade/lib/enable.ts new file mode 100644 index 0000000..e1c7726 --- /dev/null +++ b/examples/copy-trade/lib/enable.ts @@ -0,0 +1,339 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/enable.ts — "Enable One-Click Trading": session + basket + ledger + +// delegate behind ONE wallet approval sheet (signAllTransactions). +// CONSENT RULE: NO USER FUNDS MOVE HERE. Enable is account setup only — the +// sole transfer is the disclosed 0.01 SOL rent top-up to the session key +// (recoverable on revoke). Deposits/withdrawals are EXPLICIT, amount-chosen, +// separately-approved actions in lib/funds.ts. Never bundle a deposit into a +// convenience flow in a production app. +// THE HARD PART: build EVERYTHING first (API txs arrive partially signed with +// their own blockhashes — never touch them), sign once, then submit in strict +// lifecycle order on the BASE chain. +// GOTCHAS.md → "The lifecycle has a strict order" +// ───────────────────────────────────────────────────────────────────────────── + +import type { PublicKey, Transaction, VersionedTransaction } from "@solana/web3.js"; +import { decodeTransaction, type BasketSnapshot } from "flash-v2"; +import { baseConnection, flash } from "./flash"; +import type { LatencyEntry } from "./hooks"; +import { + buildSessionTransaction, + loadSession, + persistSession, + type BuiltSessionTransaction, + type LoadedSession, + type SessionWallet, +} from "./session"; + +// ── public shapes ───────────────────────────────────────────────────────────── + +export type EnableStepId = "fund" | "session" | "basket" | "ledger" | "delegate"; +export type EnableStepStatus = "idle" | "active" | "done" | "skipped" | "error"; + +export interface EnableStepRow { + id: EnableStepId; + label: string; + status: EnableStepStatus; + note?: string; + ms?: number; + signature?: string; +} + +export interface EnableState { + phase: "precheck" | "building" | "signing" | "submitting" | "done" | "stopped"; + /** Live text for the action-zone pill ("approve in wallet…", "init-basket…"). */ + headline: string; + steps: EnableStepRow[]; + /** Set when the wallet can't pay setup rent. */ + fundingHint: string | null; + /** Fresh basket has no funds yet — surface the EXPLICIT Deposit affordance. + * (Funds never move during Enable; see lib/funds.ts.) */ + needsUsdc: boolean; + /** Fatal step error (already phrased for humans; calming when upstream). */ + error: string | null; + /** True when `error` is a known transient upstream-deploy situation. */ + upstream: boolean; +} + +export interface EnableResult { + ok: boolean; + /** Minted (and persisted) even when a later step failed — partial progress counts. */ + session: LoadedSession | null; + needsUsdc: boolean; +} + +/** The narrow slice of wallet-adapter's useWallet() that enable needs. */ +export interface EnableWalletCtx { + publicKey: PublicKey; + signTransaction: (tx: T) => Promise; + signAllTransactions?: ((txs: T[]) => Promise) | undefined; +} + +// ── error classification (per-step) ────────────────────────────────────────── + +const RE_ALREADY = /already in use|already exists|custom program error: 0x0\b/i; +const RE_UPSTREAM = /Access violation in heap|ProgramFailedToComplete/i; +const RE_REJECTED = /reject|declin|cancel/i; +const RE_STALE = /blockhash not found|block height exceeded|expired/i; +const RE_NO_SIGNALL = /signAllTransactions|not.{0,4}supported/i; + +export const UPSTREAM_MESSAGE = + "known upstream issue: the magic-trade program is temporarily unavailable (Flash may be redeploying) — your setup is fine; retry after the fix."; + +export function errText(e: unknown): string { + const msg = e instanceof Error ? e.message : String(e); + const logs = (e as { logs?: unknown })?.logs; + return Array.isArray(logs) ? `${msg} ${logs.join(" ")}` : msg; +} + +/** Shared per-tx error classification — also used by the explicit funds flows. */ +export function classifyTxError(e: unknown): { + message: string; + upstream: boolean; + stale: boolean; + rejected: boolean; +} { + const raw = errText(e); + if (RE_UPSTREAM.test(raw)) return { message: UPSTREAM_MESSAGE, upstream: true, stale: false, rejected: false }; + if (RE_REJECTED.test(raw)) + return { message: "approval declined in the wallet — nothing was sent", upstream: false, stale: false, rejected: true }; + if (RE_STALE.test(raw)) + return { message: "the approval took too long and the transaction expired — try again", upstream: false, stale: true, rejected: false }; + return { message: raw, upstream: false, stale: false, rejected: false }; +} + +// ── confirmation (the confirmAirdrop pattern: poll signature statuses) ─────── + +export async function submitAndConfirm( + tx: Transaction | VersionedTransaction, + timeoutMs = 45_000, +): Promise<{ signature: string; ms: number }> { + const raw = tx.serialize(); + const started = Date.now(); + const signature = await baseConnection.sendRawTransaction(raw, { maxRetries: 3 }); + for (;;) { + try { + const status = (await baseConnection.getSignatureStatuses([signature])).value[0]; + if (status) { + if (status.err) throw new Error(`on-chain error (${signature}): ${JSON.stringify(status.err)}`); + const level = status.confirmationStatus; + if (level === "confirmed" || level === "finalized") { + return { signature, ms: Date.now() - started }; + } + } + } catch (e) { + // A real on-chain error must surface; transient 429s while polling must not. + if (e instanceof Error && e.message.startsWith("on-chain error")) throw e; + } + if (Date.now() - started > timeoutMs) throw new Error(`confirmation timeout (${signature})`); + await new Promise((r) => setTimeout(r, 400)); + } +} + +// ── the flow ────────────────────────────────────────────────────────────────── + +export async function enableOneClickTrading(args: { + wallet: EnableWalletCtx; + anchorWallet: SessionWallet; + snapshot: BasketSnapshot | null; + usdcMint: string | null; + balances: { sol: number | null; usdc: number | null }; + onStep: (state: EnableState) => void; + onLog: (e: Omit) => void; +}): Promise { + const { wallet, anchorWallet, snapshot, usdcMint, balances, onStep, onLog } = args; + const owner = wallet.publicKey.toBase58(); + + // 1 ── what does this account still need? + const basketExists = Boolean(snapshot?.basketPubkey); + const freshSession = loadSession(owner); + const needs = { + session: !freshSession, + basket: !basketExists, + ledger: !basketExists, // basket unknown → ledger unknown → init it (idempotent-ish) + delegate: !basketExists, // a fresh basket must be handed to the MagicBlock validator + // NO deposit here — funds move only via the explicit Deposit flow (lib/funds.ts). + }; + + const state: EnableState = { + phase: "precheck", + headline: "checking wallet…", + steps: [], + fundingHint: null, + needsUsdc: false, + error: null, + upstream: false, + }; + const rows = new Map(); + const emit = () => { + state.steps = [...rows.values()]; + onStep({ ...state, steps: state.steps.map((s) => ({ ...s })) }); + }; + const row = (id: EnableStepId, label: string, patch: Partial) => { + rows.set(id, { ...(rows.get(id) ?? { id, label, status: "idle" }), label, ...patch }); + emit(); + }; + const stop = (error: string, upstream = false): EnableResult => { + state.phase = "stopped"; + state.error = error; + state.upstream = upstream; + state.headline = upstream ? "program unavailable upstream" : "setup stopped"; + emit(); + return { ok: false, session: mintedSession, needsUsdc: state.needsUsdc }; + }; + let mintedSession: LoadedSession | null = freshSession; + /** Freshly built session pieces (set in step 3, persisted in step 5 once its tx confirms). */ + let sessionParts: BuiltSessionTransaction | null = null; + + // 2 ── funding pre-check: compute the real SOL needed at runtime — mostly + // recoverable account rent (basket + deposit-ledger + session-token), + // plus the recoverable 0.01 SOL session top-up and a small fee buffer. + // No hardcoded gate: getMinimumBalanceForRentExemption is the source of truth. + const sol = balances.sol ?? 0; + emit(); + let rentLamports = 0; + if (needs.basket) rentLamports += await baseConnection.getMinimumBalanceForRentExemption(256); + if (needs.ledger) rentLamports += await baseConnection.getMinimumBalanceForRentExemption(852); + if (needs.session) rentLamports += await baseConnection.getMinimumBalanceForRentExemption(144); + const requiredSol = rentLamports / 1e9 + (needs.session ? 0.01 : 0) + 0.002; // top-up + fee buffer + if (sol < requiredSol) { + const need = requiredSol.toFixed(4); + state.fundingHint = + `this wallet needs ~${need} SOL for setup — mostly recoverable account rent plus a ` + + `0.01 SOL session top-up (recoverable on revoke), not fees. Fund ${owner} and tap Enable again.`; + return stop(`wallet needs ~${need} SOL for setup (recoverable rent + top-up)`); + } + // 3 ── BUILD everything first (no signatures yet). + state.phase = "building"; + state.headline = "preparing transactions…"; + emit(); + + type Planned = { id: EnableStepId; label: string; action: string; tx: Transaction | VersionedTransaction }; + const plan: Planned[] = []; + try { + if (needs.session) { + row("session", "create session key", { status: "active", note: "building…" }); + const built = await buildSessionTransaction(anchorWallet, baseConnection, { + }); + plan.push({ id: "session", label: "create session key", action: "create-session", tx: built.tx }); + // Persisted only after its tx confirms (step 5 below). + sessionParts = built; + row("session", "create session key", { status: "active", note: "ready to sign" }); + } else { + row("session", "create session key", { status: "skipped", note: "session still valid" }); + } + + const builders: Array<{ id: EnableStepId; label: string; action: string; build: () => Promise<{ transactionBase64: string }> }> = []; + if (needs.basket) builders.push({ id: "basket", label: "init-basket", action: "init-basket", build: () => flash.initBasket({ owner }) }); + else row("basket", "init-basket", { status: "skipped", note: "basket already on-chain" }); + if (needs.ledger) builders.push({ id: "ledger", label: "init-deposit-ledger", action: "init-deposit-ledger", build: () => flash.initDepositLedger({ owner }) }); + else row("ledger", "init-deposit-ledger", { status: "skipped", note: "already set up" }); + if (needs.delegate) builders.push({ id: "delegate", label: "delegate-basket", action: "delegate-basket", build: () => flash.delegateBasket({ payer: owner, owner }) }); + else row("delegate", "delegate-basket", { status: "skipped", note: "already delegated" }); + + // CONSENT RULE: no deposit in this batch — a fresh basket starts EMPTY and + // the user funds it via the explicit Deposit sheet (their amount, their + // separate approval). needsUsdc just surfaces that affordance. + if (!basketExists) state.needsUsdc = true; + + for (const b of builders) { + row(b.id, b.label, { status: "active", note: "building…" }); + const built = await b.build(); + // NEVER touch the blockhash — these arrive partially signed server-side. + plan.push({ id: b.id, label: b.label, action: b.action, tx: decodeTransaction(built.transactionBase64) }); + row(b.id, b.label, { status: "active", note: "ready to sign" }); + } + } catch (e) { + const raw = errText(e); + return stop(RE_UPSTREAM.test(raw) ? UPSTREAM_MESSAGE : raw, RE_UPSTREAM.test(raw)); + } + + if (plan.length === 0) { + state.phase = "done"; + state.headline = "already set up"; + emit(); + return { ok: true, session: mintedSession, needsUsdc: state.needsUsdc }; + } + + // 4 ── ONE approval for the whole bundle (fallback: sequential popups). + state.phase = "signing"; + state.headline = "approve in wallet…"; + for (const p of plan) row(p.id, p.label, { status: "active", note: "waiting for your approval…" }); + let signed: (Transaction | VersionedTransaction)[]; + try { + const toSign = plan.map((p) => p.tx); + const signAll = wallet.signAllTransactions; + if (typeof signAll === "function") { + try { + signed = await signAll(toSign); + } catch (e) { + const raw = errText(e); + if (RE_REJECTED.test(raw)) throw e; + if (!RE_NO_SIGNALL.test(raw)) throw e; + signed = await signSequentially(); + } + } else { + signed = await signSequentially(); + } + async function signSequentially(): Promise<(Transaction | VersionedTransaction)[]> { + const out: (Transaction | VersionedTransaction)[] = []; + for (let i = 0; i < plan.length; i++) { + const p = plan[i]; + if (!p) continue; + state.headline = `approve in wallet (${i + 1}/${plan.length})…`; + row(p.id, p.label, { status: "active", note: `approve in wallet (${i + 1}/${plan.length})…` }); + out.push(await wallet.signTransaction(p.tx)); + } + return out; + } + } catch (e) { + const raw = errText(e); + const friendly = RE_REJECTED.test(raw) ? "approval declined in the wallet — nothing was sent" : raw; + for (const p of plan) row(p.id, p.label, { status: "error", note: friendly }); + return stop(friendly); + } + + // 5 ── submit sequentially with confirmation, strict lifecycle order. + state.phase = "submitting"; + for (const p of plan) row(p.id, p.label, { status: "active", note: "queued…" }); + for (let i = 0; i < plan.length; i++) { + const p = plan[i]; + const tx = signed[i]; + if (!p || !tx) continue; + state.headline = `${p.action}…`; + row(p.id, p.label, { status: "active", note: "submitting to the base chain…" }); + try { + const { signature, ms } = await submitAndConfirm(tx); + row(p.id, p.label, { status: "done", note: undefined, ms, signature }); + onLog({ action: p.action, chain: "base", ms, signature }); + if (p.id === "session" && sessionParts) { + mintedSession = persistSession({ + sessionSigner: sessionParts.sessionSigner, + sessionToken: sessionParts.sessionToken, + authority: owner, + validUntil: sessionParts.validUntil, + }); + } + } catch (e) { + const raw = errText(e); + if (RE_ALREADY.test(raw)) { + row(p.id, p.label, { status: "done", note: "already set up" }); + continue; + } + if (RE_UPSTREAM.test(raw)) { + row(p.id, p.label, { status: "error", note: UPSTREAM_MESSAGE }); + return stop(UPSTREAM_MESSAGE, true); + } + const friendly = RE_STALE.test(raw) + ? "the approval took too long and the transaction expired — tap Enable again to rebuild" + : raw; + row(p.id, p.label, { status: "error", note: friendly }); + return stop(friendly); + } + } + + state.phase = "done"; + state.headline = "one-click trading enabled"; + emit(); + return { ok: true, session: mintedSession, needsUsdc: state.needsUsdc }; +} diff --git a/examples/copy-trade/lib/flash.ts b/examples/copy-trade/lib/flash.ts new file mode 100644 index 0000000..e2922bc --- /dev/null +++ b/examples/copy-trade/lib/flash.ts @@ -0,0 +1,48 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/flash.ts — one FlashV2Client + one base-chain Connection for the app. +// THE HARD PART: V2 spans TWO chains — trading txs go to network.erRpc, setup +// txs to network.baseRpc. Flash V2 is MAINNET — real funds. +// GOTCHAS.md → "Two chains, one flow" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +import { Connection } from "@solana/web3.js"; +import { FlashV2Client, type NetworkConfig } from "flash-v2"; + +/** NEXT_PUBLIC_ env overrides, applied only when actually set (a key whose + * value is `undefined` would otherwise stomp the preset in the spread). */ +function overridesFromEnv(): Partial { + const cfg: Partial = {}; + if (process.env.NEXT_PUBLIC_FLASH_API_BASE) cfg.apiBase = process.env.NEXT_PUBLIC_FLASH_API_BASE; + if (process.env.NEXT_PUBLIC_ER_RPC) cfg.erRpc = process.env.NEXT_PUBLIC_ER_RPC; + if (process.env.NEXT_PUBLIC_BASE_RPC) cfg.baseRpc = process.env.NEXT_PUBLIC_BASE_RPC; + return cfg; +} + +/** The one client. Reads, quotes, and every transaction builder. */ +export const flash = new FlashV2Client(overridesFromEnv()); + + +/** Base-chain connection — airdrops, balances, setup-tx submission. + * disableRetryOnRateLimit: the public RPC rate-limits; one attempt, one + * clean failure — the app owns retry UX. */ +export const baseConnection = new Connection(flash.network.baseRpc, { + commitment: "confirmed", + disableRetryOnRateLimit: true, +}); + +/** All tradeable markets (first is the default). */ +export const MARKETS = ["SOL", "BTC", "ETH"]; +/** Kept for backward compat — equals MARKETS[0]. */ +export const MARKET = MARKETS[0] as string; +export const COLLATERAL = "USDC"; + +/** Price poll cadence — 1s keeps the chart line + slot-reel digits alive. */ +export const PRICE_POLL_MS = 1000; + +/** Static, clearly-labeled comparison baseline for the latency HUD. */ +export const SOLANA_L1_TYPICAL_MS = 400; + +/** Explorer link for a signature — cluster param matches the active network. */ +export function explorerTx(signature: string): string { + return `https://explorer.solana.com/tx/${signature}`; +} diff --git a/examples/copy-trade/lib/format.ts b/examples/copy-trade/lib/format.ts new file mode 100644 index 0000000..ef221ac --- /dev/null +++ b/examples/copy-trade/lib/format.ts @@ -0,0 +1,130 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/format.ts — display formatting for keys, prices, USD, PnL, and latency. +// THE HARD PART: the API speaks UI DECIMAL STRINGS (and leverage can be the +// literal string "Infinity") — every formatter here tolerates that without +// NaN leaking into the UI. GOTCHAS.md → "Responses are V1-shaped" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +/** "FMTg…xtvj" — middle-truncate a pubkey or signature. */ +export function shortKey(key: string, n = 4): string { + if (key.length <= n * 2 + 1) return key; + return `${key.slice(0, n)}…${key.slice(-n)}`; +} + +/** Parse an API decimal string defensively. Returns null on junk/Infinity. */ +export function num(v: string | number | null | undefined): number | null { + if (v === null || v === undefined) return null; + const n = typeof v === "number" ? v : Number(v); + return Number.isFinite(n) ? n : null; +} + +/** USDC has 6 decimals on-chain — tiny but REAL values must never display as + * $0.00 (a $5 position's fee is ~$0.004). Adaptive precision: more digits as + * the magnitude shrinks, never inventing precision the chain doesn't have. */ +function usdDigits(abs: number, base: number): number { + if (abs === 0) return base; + if (abs < 0.01) return 4; + if (abs < 1) return 3; + return base; +} + +/** "$151.23" / "−$0.0042" — USD with sign folded in, 6-dec-aware. */ +export function fmtUsd(v: string | number | null | undefined, digits = 2): string { + const n = num(v); + if (n === null) return "—"; + const abs = Math.abs(n); + const d = usdDigits(abs, digits); + const sign = n < 0 ? "−" : ""; + return `${sign}$${abs.toLocaleString("en-US", { + minimumFractionDigits: d, + maximumFractionDigits: d, + })}`; +} + +/** Signed USD for PnL: "+$0.42" / "−$0.0042" (6-dec-aware for small books). */ +export function fmtPnlUsd(v: string | number | null | undefined): string { + const n = num(v); + if (n === null) return "—"; + const abs = Math.abs(n); + return `${n >= 0 ? "+" : "−"}$${abs.toFixed(usdDigits(abs, 2))}`; +} + +/** "+3.81%" / "−1.20%". */ +export function fmtPct(v: string | number | null | undefined, digits = 2): string { + const n = num(v); + if (n === null) return "—"; + return `${n >= 0 ? "+" : "−"}${Math.abs(n).toFixed(digits)}%`; +} + +/** Plain number with fixed digits, em-dash on junk. */ +export function fmtNum(v: string | number | null | undefined, digits = 2): string { + const n = num(v); + return n === null ? "—" : n.toLocaleString("en-US", { + minimumFractionDigits: digits, + maximumFractionDigits: digits, + }); +} + +/** "5.0×" — leverage; tolerates the API's literal "Infinity". */ +export function fmtLeverage(v: string | number | null | undefined): string { + const n = num(v); + return n === null ? "∞×" : `${n.toFixed(1)}×`; +} + +/** "38 ms" / "1.24 s". */ +export function fmtMs(ms: number): string { + return ms < 1000 ? `${ms} ms` : `${(ms / 1000).toFixed(2)} s`; +} + +/** Median of a number list (null when empty). */ +export function median(values: number[]): number | null { + if (values.length === 0) return null; + const sorted = [...values].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + const lo = sorted[mid - 1]; + const hi = sorted[mid]; + if (sorted.length % 2 === 0 && lo !== undefined && hi !== undefined) { + return Math.round((lo + hi) / 2); + } + return hi ?? null; +} + +// ── Flash-parity position math (client-side, like Flash's own UI) ─────────── +// Flash's UI shows mark-price PnL incl. fees, NOT the indexer's spread-through +// pnlWithFeeUsdUi (which can read wildly different on the same position). +// Compute what the product computes: +// pricePnl = (mark − entry)/entry × size × dir +// pnl = pricePnl − (exitFee + borrowFee) [raw 6-dec ÷ 1e6] +// pct = pnl / collateral × 100 +// lev = size / collateral +// liq ≈ entry × (1 ∓ collateral/size × 0.92) [≈ maintenance buffer] +export interface PositionView { + pnlUsd: number; + pnlPct: number; + leverage: number; + liqUi: number | null; +} + +export function computePositionView( + p: { + sideUi: string; + sizeUsdUi: string; + collateralUsdUi: string; + entryPriceUi: string; + exitFeeUsd: string; + borrowFeeUsd: string; + }, + markUi: number | null, +): PositionView | null { + const size = num(p.sizeUsdUi); + const coll = num(p.collateralUsdUi); + const entry = num(p.entryPriceUi); + if (size === null || coll === null || entry === null || entry <= 0 || markUi === null) return null; + const dir = p.sideUi.toUpperCase() === "LONG" ? 1 : -1; + const fees = ((num(p.exitFeeUsd) ?? 0) + (num(p.borrowFeeUsd) ?? 0)) / 1e6; + const pnlUsd = ((markUi - entry) / entry) * size * dir - fees; + const pnlPct = coll > 0 ? (pnlUsd / coll) * 100 : 0; + const leverage = coll > 0 ? size / coll : 0; + const liqUi = size > 0 ? entry * (1 - dir * (coll / size) * 0.92) : null; + return { pnlUsd, pnlPct, leverage, liqUi }; +} diff --git a/examples/copy-trade/lib/funds.ts b/examples/copy-trade/lib/funds.ts new file mode 100644 index 0000000..3cb4cbd --- /dev/null +++ b/examples/copy-trade/lib/funds.ts @@ -0,0 +1,218 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/funds.ts — EXPLICIT deposit & withdraw. THE PRODUCTION RULE THIS FILE +// EXISTS TO TEACH: user funds move ONLY on a user-initiated action with a +// user-chosen amount and its own wallet approval — never bundled into a +// convenience flow like "Enable". Withdraw is the API's two-step dance: +// request-withdrawal → execute-withdrawal (both signed by the OWNER wallet, +// both on the BASE chain; execute can be re-run alone to recover). +// GOTCHAS.md → "Funds move on consent only" · "Two chains, one flow" +// ───────────────────────────────────────────────────────────────────────────── + +import { decodeTransaction } from "flash-v2"; +import { baseConnection, flash } from "./flash"; +import { classifyTxError, submitAndConfirm, type EnableWalletCtx } from "./enable"; +import type { LatencyEntry } from "./hooks"; + +export interface FundsStep { + /** "building" → "approve" → "submitting" → "done" | "error" */ + phase: "building" | "approve" | "submitting" | "done" | "error"; + label: string; + note?: string; + signature?: string; + ms?: number; +} + +type OnStep = (step: FundsStep) => void; +type OnLog = (e: Omit) => void; + +async function signSubmit( + wallet: EnableWalletCtx, + txBase64: string, + label: string, + onStep: OnStep, + onLog: OnLog, +): Promise<{ signature: string; ms: number }> { + onStep({ phase: "approve", label, note: "approve in your wallet…" }); + // NEVER touch the blockhash — builder txs arrive partially signed server-side. + const signed = await wallet.signTransaction(decodeTransaction(txBase64)); + onStep({ phase: "submitting", label, note: "submitting to the base chain…" }); + const { signature, ms } = await submitAndConfirm(signed); + onLog({ action: label, chain: "base", ms, signature }); + return { signature, ms }; +} + +/** + * Deposit ANY pool token into the basket — the user typed `amount`, the user + * approves the single transfer. Tradable once the deposit confirms on the ER. + */ +export async function depositToken(args: { + wallet: EnableWalletCtx; + tokenMint: string; + symbol: string; + amount: string; // UI units, user-chosen + onStep: OnStep; + onLog: OnLog; +}): Promise<{ ok: boolean; error?: string }> { + const { wallet, tokenMint, symbol, amount, onStep, onLog } = args; + const owner = wallet.publicKey.toBase58(); + const label = `deposit ${amount} ${symbol}`; + try { + onStep({ phase: "building", label, note: "building the transaction…" }); + const built = await flash.depositDirect({ owner, tokenMint, amount }); + const { signature, ms } = await signSubmit(wallet, built.transactionBase64, label, onStep, onLog); + onStep({ phase: "done", label, note: "deposited — tradable once it confirms on the rollup", signature, ms }); + return { ok: true }; + } catch (e) { + const c = classifyTxError(e); + onStep({ phase: "error", label, note: c.message }); + return { ok: false, error: c.message }; + } +} + +/** USDC compat wrapper (existing callers). */ +export async function depositUsdc(args: { + wallet: EnableWalletCtx; + usdcMint: string; + amount: string; + onStep: OnStep; + onLog: OnLog; +}): Promise<{ ok: boolean; error?: string }> { + return depositToken({ wallet: args.wallet, tokenMint: args.usdcMint, symbol: "USDC", amount: args.amount, onStep: args.onStep, onLog: args.onLog }); +} + +// The receipt the execute step consumes is written when the rollup's validator +// CROSSES the settlement to base chain (~30-90s after request). Until then the +// program answers 0xbc4 AccountNotInitialized on `settlement_receipt` — a +// timing state, never an error worth showing raw logs for. +const RE_SETTLEMENT_PENDING = /settlement_receipt|AccountNotInitialized|0xbc4|3012/i; + +/** + * Withdraw USDC back to the wallet. Two explicit steps, two approvals: + * 1) request-withdrawal (queues settlement out of the rollup) + * 2) execute-withdrawal — AUTO-RETRIED while the rollup crosses the + * settlement receipt to base (~30-90s), with a countdown the sheet shows. + */ +export async function withdrawToken(args: { + wallet: EnableWalletCtx; + tokenMint: string; + symbol: string; + amount: string; // UI units, user-chosen + onStep: OnStep; + onLog: OnLog; +}): Promise<{ ok: boolean; error?: string; executePending?: boolean }> { + const { wallet, tokenMint, symbol, amount, onStep, onLog } = args; + const owner = wallet.publicKey.toBase58(); + try { + onStep({ phase: "building", label: `request withdrawal of ${amount} ${symbol}`, note: "building…" }); + const req = await flash.requestWithdrawal({ owner, tokenMint, amount }); + await signSubmit(wallet, req.transactionBase64, `request-withdrawal ${amount} ${symbol}`, onStep, onLog); + } catch (e) { + const c = classifyTxError(e); + onStep({ phase: "error", label: "request withdrawal", note: c.message }); + return { ok: false, error: c.message }; + } + // Settlement crossing: poll UNSIGNED simulations (zero popups) until the + // receipt exists on base, then ask for the ONE execute signature. + const ready = await waitForSettlementReceipt({ owner, tokenMint, onStep, maxSeconds: 120 }); + if (!ready) { + onStep({ + phase: "error", + label: "execute withdrawal", + note: "settlement is taking longer than usual — your funds are queued safely; tap \"Execute again\" in a minute.", + }); + return { ok: false, error: "settlement still crossing", executePending: true }; + } + return executeWithdrawalToken({ wallet, tokenMint, symbol, onStep, onLog }); +} + +/** USDC compat wrapper (existing callers). */ +export async function withdrawUsdc(args: { + wallet: EnableWalletCtx; + usdcMint: string; + amount: string; + onStep: OnStep; + onLog: OnLog; +}): Promise<{ ok: boolean; error?: string; executePending?: boolean }> { + return withdrawToken({ wallet: args.wallet, tokenMint: args.usdcMint, symbol: "USDC", amount: args.amount, onStep: args.onStep, onLog: args.onLog }); +} + +/** + * Poll an UNSIGNED simulation of execute-withdrawal until the settlement + * receipt exists (no wallet popups while waiting). True = ready to sign. + */ +async function waitForSettlementReceipt(args: { + owner: string; + tokenMint: string; + onStep: OnStep; + maxSeconds: number; +}): Promise { + const { owner, tokenMint, onStep, maxSeconds } = args; + const started = Date.now(); + for (;;) { + const waited = Math.round((Date.now() - started) / 1000); + if (waited > maxSeconds) return false; + try { + const exec = await flash.executeWithdrawal({ owner, tokenMint }); + const tx = decodeTransaction(exec.transactionBase64); + const sim = await baseConnection.simulateTransaction(tx, { + sigVerify: false, + replaceRecentBlockhash: true, + }); + const err = sim.value.err ? JSON.stringify(sim.value.err) + (sim.value.logs ?? []).join(" ") : ""; + if (!sim.value.err) return true; // receipt landed — execute will pass + if (!RE_SETTLEMENT_PENDING.test(err)) return true; // different state — let the real attempt surface it + } catch { /* builder/RPC hiccup — keep polling */ } + onStep({ + phase: "submitting", + label: "settlement crossing from the rollup", + note: `the rollup writes the receipt to base chain (~30–90s) — waiting ${waited}s, no action needed`, + }); + await new Promise((r) => setTimeout(r, 8000)); + } +} + +/** Step 2 alone — also the recovery path when settlement needed more time. */ +export async function executeWithdrawalToken(args: { + wallet: EnableWalletCtx; + tokenMint: string; + symbol: string; + onStep: OnStep; + onLog: OnLog; + attempt?: number; +}): Promise<{ ok: boolean; error?: string; executePending?: boolean }> { + const { wallet, tokenMint, onStep, onLog, attempt } = args; + const owner = wallet.publicKey.toBase58(); + const tag = attempt ? ` (attempt ${attempt}/4)` : ""; + try { + onStep({ phase: "building", label: "execute withdrawal", note: `building…${tag}` }); + const exec = await flash.executeWithdrawal({ owner, tokenMint }); + const { signature, ms } = await signSubmit(wallet, exec.transactionBase64, "execute-withdrawal", onStep, onLog); + onStep({ phase: "done", label: "execute withdrawal", note: "funds are back in your wallet", signature, ms }); + return { ok: true }; + } catch (e) { + const raw = e instanceof Error ? e.message : String(e); + if (RE_SETTLEMENT_PENDING.test(raw)) { + // Timing, not failure — the caller's retry loop (or the button) handles it. + onStep({ + phase: "submitting", + label: "settlement crossing from the rollup", + note: `receipt not on base chain yet${tag} — retrying shortly`, + }); + return { ok: false, error: "settlement pending", executePending: true }; + } + const c = classifyTxError(e); + onStep({ phase: "error", label: "execute withdrawal", note: c.message }); + return { ok: false, error: c.message, executePending: c.rejected ? false : true }; + } +} + +/** USDC compat wrapper (existing callers). */ +export async function executeWithdrawalStep(args: { + wallet: EnableWalletCtx; + usdcMint: string; + onStep: OnStep; + onLog: OnLog; + attempt?: number; +}): Promise<{ ok: boolean; error?: string; executePending?: boolean }> { + return executeWithdrawalToken({ wallet: args.wallet, tokenMint: args.usdcMint, symbol: "USDC", onStep: args.onStep, onLog: args.onLog, ...(args.attempt !== undefined ? { attempt: args.attempt } : {}) }); +} diff --git a/examples/copy-trade/lib/hooks.ts b/examples/copy-trade/lib/hooks.ts new file mode 100644 index 0000000..1ab0f9e --- /dev/null +++ b/examples/copy-trade/lib/hooks.ts @@ -0,0 +1,474 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/hooks.ts — live data: price poll, owner stream, balances, latency log. +// THE HARD PART: the owner WS sends TWO frame types (basket vs metrics) that +// flash-v2 merges for us; the HUD numbers are REAL confirmMs values only — +// nothing here invents latency. GOTCHAS.md → "The WS sends two frame types" +// · "WS connection limits are real" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { PublicKey } from "@solana/web3.js"; +import { + subscribeOwner, + type BasketSnapshot, + type PositionMetrics, + type PriceInfo, +} from "flash-v2"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { baseConnection, flash } from "./flash"; +import { MAGIC_TRADE_PROGRAM } from "./session"; + +/** The magic-trade program on the ACTIVE network (ledger reads filter on it). */ +const MAGIC_TRADE_PROGRAM_ID = MAGIC_TRADE_PROGRAM.toBase58(); + +// ── price ticker ───────────────────────────────────────────────────────────── + +export function usePrice(symbol: string, intervalMs = 1000): { + price: PriceInfo | null; + drift: "up" | "down" | "flat"; +} { + const [price, setPrice] = useState(null); + const [drift, setDrift] = useState<"up" | "down" | "flat">("flat"); + const last = useRef(null); + + useEffect(() => { + // Market switch: drop the previous symbol's price immediately so the + // history buffer never receives a stale cross-market tick. + setPrice(null); + setDrift("flat"); + last.current = null; + let dead = false; + const tick = async () => { + try { + const p = await flash.price(symbol); + if (dead) return; + if (last.current !== null && p.priceUi !== last.current) { + setDrift(p.priceUi > last.current ? "up" : "down"); + } + last.current = p.priceUi; + setPrice(p); + } catch { + /* keep last price; next tick retries */ + } + }; + void tick(); + const timer = setInterval(tick, intervalMs); + return () => { dead = true; clearInterval(timer); }; + }, [symbol, intervalMs]); + + return { price, drift }; +} + +/** + * The tradeable market list, LIVE from Flash's own config (`GET /v2/tokens` — + * CDN-driven and hot-reloaded server-side): every non-stable token in the + * active pool is a market. New listings appear here automatically. + * Returns null until loaded (callers keep a static fallback meanwhile). + */ +export function useMarkets(): string[] | null { + const [markets, setMarkets] = useState(null); + useEffect(() => { + let dead = false; + flash.tokens() + .then((tokens) => { + if (dead) return; + const symbols = tokens.filter((t) => !t.isStable).map((t) => t.symbol); + if (symbols.length > 0) setMarkets(symbols); + }) + .catch(() => { /* fallback list keeps the selector usable */ }); + return () => { dead = true; }; + }, []); + return markets; +} + +/** Per-market leverage limits + trade spreads, LIVE from Flash's custody config. */ +export interface MarketLimits { + /** Lowest initial leverage the program accepts (UI floor, ≥1.1 for sanity). */ + minLeverage: number; + /** Highest initial leverage the program accepts for this market. */ + maxLeverage: number; + /** Exit spread the engine prices longs through (0.10 = 10%). THE number + * that makes a fresh fill read −10% "realizable now" — show it BEFORE + * the tap, and name it in the PnL breakdown. FX pairs are 0%. */ + spreadLongPct: number; + spreadShortPct: number; +} + +// custody.pricing values are fixed-point with 4 decimals (110000 = 11x — +// Flash's tiers carry a ×1.1 buffer over the advertised 10x/50x/100x/500x). +const LEVERAGE_SCALE = 10_000; + +/** + * Live leverage bounds for a market symbol: token config → custody (by mint) + * → pricing.minInitialLeverage/maxInitialLeverage. Cached per session; null + * until loaded (callers keep a conservative fallback meanwhile). Same + * philosophy as useMarkets: Flash changes limits → the UI follows, no code. + */ +const limitsCache = new Map(); +let custodyPricing: Map | null = null; + +export function useMarketLimits(marketSymbol: string): MarketLimits | null { + const [limits, setLimits] = useState(limitsCache.get(marketSymbol) ?? null); + useEffect(() => { + const cached = limitsCache.get(marketSymbol); + setLimits(cached ?? null); + if (cached) return; + let dead = false; + void (async () => { + try { + if (!custodyPricing || custodyPricing.size === 0) { + const res = await fetch(`${flash.network.apiBase}/raw/custodies`); + const json = (await res.json()) as + | Array<{ account?: { tokenMint?: string; pricing?: { minInitialLeverage?: number; maxInitialLeverage?: number; tradeSpreadLong?: number; tradeSpreadShort?: number } } }> + | { custodies?: unknown }; + const arr = Array.isArray(json) ? json : []; + const map = new Map(); + for (const c of arr) { + const a = c.account; + // the custody's token field is `tokenMint` (NOT `mint`) + if (a?.tokenMint && a.pricing?.maxInitialLeverage) { + map.set(a.tokenMint, { + min: (a.pricing.minInitialLeverage ?? LEVERAGE_SCALE) / LEVERAGE_SCALE, + max: a.pricing.maxInitialLeverage / LEVERAGE_SCALE, + // tradeSpread is in bps×1 (1000 = 10%): ÷1e4 → fraction + spreadL: (a.pricing.tradeSpreadLong ?? 0) / 10_000, + spreadS: (a.pricing.tradeSpreadShort ?? 0) / 10_000, + }); + } + } + // only cache a USEFUL result — an empty map must not wedge "loading…" + if (map.size > 0) custodyPricing = map; + } + if (!custodyPricing) return; + const tokens = await flash.tokens(); + if (dead) return; + const mint = tokens.find((t) => t.symbol.toUpperCase() === marketSymbol.toUpperCase())?.mintKey; + const p = mint ? custodyPricing.get(mint) : undefined; + if (p) { + const out: MarketLimits = { + minLeverage: Math.max(1.1, p.min), + maxLeverage: p.max, + spreadLongPct: p.spreadL, + spreadShortPct: p.spreadS, + }; + limitsCache.set(marketSymbol, out); + setLimits(out); + } + } catch { /* fallback stays; next mount retries */ } + })(); + return () => { dead = true; }; + }, [marketSymbol]); + return limits; +} + +// ── owner snapshot stream ──────────────────────────────────────────────────── + +export type StreamStatus = "connecting" | "open" | "reconnecting" | "polling" | "closed"; + +export function useOwner(owner: string | null): { + snapshot: BasketSnapshot | null; + /** true once we have heard ANYTHING for this owner (fetch or frame). */ + loaded: boolean; + status: StreamStatus; + refresh: () => Promise; +} { + const [snapshot, setSnapshot] = useState(null); + const [loaded, setLoaded] = useState(false); + const [status, setStatus] = useState("connecting"); + + useEffect(() => { + setSnapshot(null); + setLoaded(false); + if (!owner) { setStatus("closed"); return; } + setStatus("connecting"); + let dead = false; + + // Spec move: fetch client.owner(pubkey) on load so the setup wizard can + // decide instantly; the stream takes over from the first frame onward. + flash.owner(owner) + .then((snap) => { + if (dead) return; + setSnapshot((prev) => prev ?? snap); + setLoaded(true); + }) + .catch(() => { /* stream below retries via poll fallback */ }); + + const stream = subscribeOwner({ + owner, + network: flash.network, + onUpdate: (snap) => { + if (dead) return; + setSnapshot(snap); + setLoaded(true); + }, + onStatus: (s) => { if (!dead) setStatus(s); }, + }); + return () => { dead = true; stream.close(); }; + }, [owner]); + + const refresh = useCallback(async () => { + if (!owner) return; + try { + const snap = await flash.owner(owner); + setSnapshot(snap); + setLoaded(true); + } catch { /* non-fatal */ } + }, [owner]); + + return { snapshot, loaded, status, refresh }; +} + +/** All live positions for one market symbol out of a snapshot. */ +export function positionsFor(snapshot: BasketSnapshot | null, marketSymbol: string): PositionMetrics[] { + if (!snapshot) return []; + return Object.values(snapshot.positionMetrics ?? {}).filter( + (p) => p.marketSymbol?.toUpperCase() === marketSymbol.toUpperCase(), + ); +} + +// ── wallet balances (base chain) ───────────────────────────────────────────── + +export function useBalances(owner: string | null, usdcMint: string | null): { + sol: number | null; + usdc: number | null; + refresh: () => Promise; +} { + const [sol, setSol] = useState(null); + const [usdc, setUsdc] = useState(null); + + const refresh = useCallback(async () => { + if (!owner) { setSol(null); setUsdc(null); return; } + try { + const lamports = await baseConnection.getBalance(new PublicKey(owner)); + setSol(lamports / 1e9); + } catch { /* keep last */ } + if (usdcMint) { + try { + const res = await baseConnection.getParsedTokenAccountsByOwner( + new PublicKey(owner), + { mint: new PublicKey(usdcMint) }, + ); + const total = res.value.reduce((sum, acc) => { + const ui = acc.account.data.parsed?.info?.tokenAmount?.uiAmount as number | null | undefined; + return sum + (typeof ui === "number" ? ui : 0); + }, 0); + setUsdc(total); + } catch { /* keep last */ } + } + }, [owner, usdcMint]); + + useEffect(() => { + void refresh(); + // Background safety-net poll; balance-changing actions refresh explicitly + // for instant updates. A keyed RPC handles this cadence comfortably. + const timer = setInterval(() => { void refresh(); }, 6_000); + return () => clearInterval(timer); + }, [refresh]); + + return { sol, usdc, refresh }; +} + +// ── deposited balance: the USER DEPOSIT LEDGER, read like Flash's own infra ── + +export interface BasketAsset { + mint: string; + symbol: string; + decimals: number; + /** AVAILABLE on the rollup side (ledger − debits + pending), native units. */ + amountUi: number; +} + +export interface BasketBalance { + /** USDC available — the tradable "in basket" figure (compat alias of assets). */ + inBasketUsd: number; + /** EVERY asset with rollup-side availability — nothing orphans invisibly. */ + assets: BasketAsset[]; + /** Where the ledger was found ("er" once delegated, "base" before). */ + source: "er" | "base"; +} + +// Anchor discriminator for `UserDepositLedger` = sha256("account:UserDepositLedger")[0..8] +// (hex 3363adbe15330782). Precomputed so the browser needs no crypto/bs58 deps. +const LEDGER_DISC_B58 = "9bYPoR9mRKo"; +// Zero-copy layout (validated against 60 live mainnet ledgers, all 852 bytes): +// 8 disc | 1 bump | 7 pad | 32 owner @16 | u32 count @48 | 20 × {32 mint, u64 amount} @52. +const LEDGER_OWNER_OFFSET = 16; +const LEDGER_COUNT_OFFSET = 48; +const LEDGER_ENTRIES_OFFSET = 52; +const LEDGER_ENTRY_SIZE = 40; + +/** All ledger entries (mint → raw u64 amount), or null when no ledger exists. */ +async function fetchLedgerEntries(rpcUrl: string, owner: string): Promise | null> { + const res = await fetch(rpcUrl, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", id: 1, method: "getProgramAccounts", + params: [MAGIC_TRADE_PROGRAM_ID, { + encoding: "base64", + filters: [ + { memcmp: { offset: 0, bytes: LEDGER_DISC_B58 } }, + { memcmp: { offset: LEDGER_OWNER_OFFSET, bytes: owner } }, + ], + }], + }), + }); + const json = (await res.json()) as { result?: Array<{ account: { data: [string, string] } }> }; + const acct = json.result?.[0]; + if (!acct) return null; + const buf = Uint8Array.from(atob(acct.account.data[0]), (c) => c.charCodeAt(0)); + const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); + const count = view.getUint32(LEDGER_COUNT_OFFSET, true); + const out = new Map(); + for (let i = 0; i < Math.min(count, 20); i++) { + const off = LEDGER_ENTRIES_OFFSET + i * LEDGER_ENTRY_SIZE; + const mint = new PublicKey(buf.subarray(off, off + 32)).toBase58(); + const amount = Number(view.getBigUint64(off + 32, true)); + out.set(mint, (out.get(mint) ?? 0) + amount); + } + return out; +} + + + +/** + * The owner's AVAILABLE USDC (GOTCHAS §20): + * + * available = ledger.deposits − basket.debits + basket.pendingCredits + * + * V2 is double-entry: the ledger records CUMULATIVE deposits (withdrawals do + * NOT decrement it), the basket's debits/pendingCredits record all internal + * churn, and only the three together net to the balance. A fully-withdrawn + * account nets exactly $0.00; while funded, the same math gives the true + * positive balance. All three figures MUST come from the same coherent + * source — the ER-fed store (mixing chains on a half-delegated account breaks + * the invariant; clamp at 0). + */ +export function useBasketBalance( + owner: string | null, + basketPubkey: string | null, + usdcMint: string | null, +): { bal: BasketBalance | null; refresh: () => Promise } { + const [bal, setBal] = useState(null); + const tokenMeta = useRef | null>(null); + // Monotonic request id: a newer read (poll OR an explicit post-trade refresh) + // supersedes an in-flight older one, so stale data never lands. + const reqId = useRef(0); + + const refresh = useCallback(async () => { + if (!owner || !usdcMint) { setBal(null); return; } + const id = ++reqId.current; + try { + // 1) cumulative deposits — ALL mints from the ledger (ER → base fallback) + let source: "er" | "base" = "er"; + let ledger = await fetchLedgerEntries(flash.network.erRpc, owner); + if (ledger === null) { + ledger = await fetchLedgerEntries(flash.network.baseRpc, owner); + source = "base"; + } + if (id !== reqId.current) return; + if (ledger === null) { setBal({ inBasketUsd: 0, assets: [], source: "base" }); return; } + // 2) the basket's counter-entries (server-decoded from the ER store) + const debits = new Map(); + const pending = new Map(); + if (basketPubkey) { + const raw = await flash.rawBasket(basketPubkey); + if (id !== reqId.current) return; + const acct = raw.account as { + debits?: Array<{ mint: string; amount: number | string }>; + pendingCredits?: Array<{ mint: string; amount: number | string }>; + }; + for (const r of acct.debits ?? []) debits.set(r.mint, (debits.get(r.mint) ?? 0) + Number(r.amount)); + for (const r of acct.pendingCredits ?? []) pending.set(r.mint, (pending.get(r.mint) ?? 0) + Number(r.amount)); + } + // 3) symbols/decimals from Flash's live token config (cached across reads) + if (!tokenMeta.current) { + try { + tokenMeta.current = new Map( + (await flash.tokens()).map((t) => [t.mintKey, { symbol: t.symbol, decimals: t.decimals }]), + ); + } catch { tokenMeta.current = new Map(); } + } + if (id !== reqId.current) return; + // 4) available per mint = ledger − debits + pending (clamped), native units + const mints = new Set([...ledger.keys(), ...debits.keys(), ...pending.keys()]); + const assets: BasketAsset[] = []; + for (const mint of mints) { + const meta = tokenMeta.current.get(mint) ?? { symbol: mint.slice(0, 4) + "…", decimals: mint === usdcMint ? 6 : 9 }; + const rawAvail = (ledger.get(mint) ?? 0) - (debits.get(mint) ?? 0) + (pending.get(mint) ?? 0); + const amountUi = Math.max(0, rawAvail) / 10 ** meta.decimals; + if (amountUi > 0) assets.push({ mint, symbol: meta.symbol, decimals: meta.decimals, amountUi }); + } + assets.sort((a, b) => (a.mint === usdcMint ? -1 : b.mint === usdcMint ? 1 : a.symbol.localeCompare(b.symbol))); + const usdc = assets.find((a) => a.mint === usdcMint)?.amountUi ?? 0; + setBal({ inBasketUsd: usdc, assets, source }); + } catch { /* keep last good read; next read retries */ } + }, [owner, basketPubkey, usdcMint]); + + useEffect(() => { + setBal(null); // clear on owner/basket change — don't show another owner's balance + reqId.current++; // invalidate any in-flight read + void refresh(); + const timer = setInterval(() => { void refresh(); }, 5_000); + return () => clearInterval(timer); + }, [refresh]); + + return { bal, refresh }; +} + +/** Resolve the USDC mint once via /tokens (deposits need the MINT, not "USDC"). */ +export function useUsdcMint(): string | null { + const [mint, setMint] = useState(null); + useEffect(() => { + let dead = false; + flash.tokens() + .then((tokens) => { + if (dead) return; + const usdc = tokens.find((t) => t.symbol.toUpperCase() === "USDC"); + setMint(usdc?.mintKey ?? null); + }) + .catch(() => { /* tokens() retried implicitly when wizard runs */ }); + return () => { dead = true; }; + }, []); + return mint; +} + +// ── latency log (the HUD's data) ───────────────────────────────────────────── + +export interface LatencyEntry { + id: string; + /** What was tapped, e.g. "LONG 5×", "FLATTEN", "init-basket". */ + action: string; + /** Which chain confirmed it — "er" rows are the headline numbers. */ + chain: "er" | "base"; + /** REAL wall-clock submit→confirmed milliseconds (flash-v2 confirmMs). */ + ms: number; + /** Measured send round-trip (≈ one wire trip) — splits network vs rollup. */ + sendMs?: number; + signature: string; + at: number; + /** Trade facts captured at action time (history rows render these). + * pnlUi = the WITH-FEE figure at the moment of close (≈ realized). */ + trade?: { + market: string; + side: "LONG" | "SHORT"; + entryUi: number | null; + collateralUi: number | null; + pnlUi: number | null; + }; +} + +export function useLatencyLog(): { + entries: LatencyEntry[]; + add: (entry: Omit) => void; +} { + const [entries, setEntries] = useState([]); + const add = useCallback((entry: Omit) => { + const id = typeof crypto !== "undefined" && "randomUUID" in crypto + ? crypto.randomUUID() + : `${Date.now()}-${Math.random()}`; + setEntries((prev) => [{ ...entry, id, at: Date.now() }, ...prev].slice(0, 50)); + }, []); + return { entries, add }; +} diff --git a/examples/copy-trade/lib/leaders.ts b/examples/copy-trade/lib/leaders.ts new file mode 100644 index 0000000..fd03772 --- /dev/null +++ b/examples/copy-trade/lib/leaders.ts @@ -0,0 +1,76 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/leaders.ts — leader DISCOVERY off the fstats V2/ER leaderboard (via the +// same-origin /api/leaders route). This is the "who do I copy?" half: ranked +// traders with real win rate, PnL, and volume on the Ephemeral Rollup. The +// "mirror their trades" half lives in lib/copy-engine.ts (the live owner WS). +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { useEffect, useRef, useState } from "react"; + +/** One row of the fstats V2/ER PnL leaderboard. */ +export interface LeaderRow { + owner: string; + rank: number; + win_rate: number; // 0–100 + wins: number; + losses: number; + num_trades: number; + net_pnl: number; + gross_pnl: number; + total_volume_usd: number; +} + +export interface LeaderboardState { + leaders: LeaderRow[]; + /** owner → number of positions open RIGHT NOW (so the UI can flag live leaders). */ + openByOwner: Record; + loading: boolean; + error: string | null; +} + +/** Poll the ranked leaderboard. Discovery data tolerates a slow cadence. */ +export function useLeaderboard(pollMs = 20_000): LeaderboardState { + const [state, setState] = useState({ + leaders: [], + openByOwner: {}, + loading: true, + error: null, + }); + const dead = useRef(false); + + useEffect(() => { + dead.current = false; + const load = async () => { + try { + const res = await fetch("/api/leaders"); + const j = (await res.json()) as Partial & { error?: string }; + if (dead.current) return; + if (!res.ok || j.error) throw new Error(j.error ?? `leaderboard ${res.status}`); + setState({ + leaders: j.leaders ?? [], + openByOwner: j.openByOwner ?? {}, + loading: false, + error: null, + }); + } catch (e) { + if (dead.current) return; + setState((s) => ({ ...s, loading: false, error: (e as Error).message })); + } + }; + void load(); + const t = setInterval(load, pollMs); + return () => { + dead.current = true; + clearInterval(t); + }; + }, [pollMs]); + + return state; +} + +/** Validate a pasted leader pubkey (base58, 32–44 chars) before streaming it. */ +export function isLikelyPubkey(s: string): boolean { + return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(s.trim()); +} diff --git a/examples/copy-trade/lib/session.ts b/examples/copy-trade/lib/session.ts new file mode 100644 index 0000000..63a8964 --- /dev/null +++ b/examples/copy-trade/lib/session.ts @@ -0,0 +1,268 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/session.ts — REAL session keys via @magicblock-labs/gum-sdk (v3). +// THE HARD PART: there is NO server endpoint for sessions — the CLIENT mints a +// SessionTokenV2 PDA (seeds ["session_token_v2", target, signer, authority] — +// note the _v2!). buildSessionTransaction lets lib/enable.ts bundle it into +// the ONE Enable One-Click Trading approval; trades then auto-sign. +// SESSION-KEYS.md has the full story. GOTCHAS.md → "Session keys" entry. +// ───────────────────────────────────────────────────────────────────────────── + +import { BN } from "@coral-xyz/anchor"; +import { SessionTokenManager } from "@magicblock-labs/gum-sdk"; +import { + Connection, + Keypair, + PublicKey, + type Transaction, + type VersionedTransaction, +} from "@solana/web3.js"; + +/** Gum session-keys program. */ +export const SESSION_KEYS_PROGRAM = new PublicKey("KeyspM2ssCJbqUhQ4k7sveSiY4WjnYsrXkC8oDbwde5"); + +/** + * magic-trade (Flash V2 on MagicBlock) program — the session target. + * Mainnet: FTv2RxXarPfNta45HTTMVaGvjzsGg27FXJ3hEKWBhrzV + * (verified by decoding a mainnet init-basket transaction from /v2/transaction-builder) + */ +/** magic-trade program (mainnet) — decoded from live builder txs. */ +export const MAGIC_TRADE_PROGRAM = new PublicKey("FTv2RxXarPfNta45HTTMVaGvjzsGg27FXJ3hEKWBhrzV"); + + +/** localStorage key for the persisted session (signer secret + token PDA). */ +export const SESSION_STORAGE_KEY = "tap-trade-session"; + +/** Minimal wallet shape we need (matches wallet-adapter's AnchorWallet). */ +export interface SessionWallet { + publicKey: PublicKey; + signTransaction(tx: T): Promise; + signAllTransactions(txs: T[]): Promise; +} + +export interface LoadedSession { + /** Ephemeral signer — auto-signs every trade, no popup. */ + keypair: Keypair; + /** SessionTokenV2 PDA (base58) — passed as `sessionToken` to the API. */ + token: string; + /** The real wallet that authorized the session (base58). */ + authority: string; + /** Unix seconds — the program rejects the session after this. */ + validUntil: number; +} + +interface StoredSession { + secretKey: number[]; + token: string; + authority: string; + validUntil: number; +} + +/** + * Derive the SessionTokenV2 PDA. Live-verified on-chain: 77 session tokens + * targeting magic-trade all match seeds + * ["session_token_v2", target_program, session_signer, authority]. + */ +export function deriveSessionToken( + authority: PublicKey, + sessionSigner: PublicKey +): PublicKey { + const [pda] = PublicKey.findProgramAddressSync( + [ + new TextEncoder().encode("session_token_v2"), + MAGIC_TRADE_PROGRAM.toBytes(), + sessionSigner.toBytes(), + authority.toBytes(), + ], + SESSION_KEYS_PROGRAM, + ); + return pda; +} + +/** Load the persisted session if it exists, belongs to `authority`, and has + * ≥60s of validity left. Anything else is cleared. Browser-only. */ +export function loadSession(authority?: string): LoadedSession | null { + if (typeof window === "undefined") return null; + const raw = window.localStorage.getItem(SESSION_STORAGE_KEY); + if (!raw) return null; + try { + const stored = JSON.parse(raw) as StoredSession; + const fresh = stored.validUntil > Date.now() / 1000 + 60; + const mine = !authority || stored.authority === authority; + if (!fresh || !mine) { + if (!fresh) clearSession(); + return null; + } + return { + keypair: Keypair.fromSecretKey(Uint8Array.from(stored.secretKey)), + token: stored.token, + authority: stored.authority, + validUntil: stored.validUntil, + }; + } catch { + clearSession(); + return null; + } +} + +export function clearSession(): void { + if (typeof window !== "undefined") window.localStorage.removeItem(SESSION_STORAGE_KEY); +} + +/** Poll a signature to confirmation (same approach as flash-v2/sign.ts). */ +async function confirmSignature(connection: Connection, signature: string, timeoutMs = 60_000): Promise { + const started = Date.now(); + for (;;) { + const status = (await connection.getSignatureStatuses([signature])).value[0]; + if (status) { + if (status.err) throw new Error(`on-chain error (${signature}): ${JSON.stringify(status.err)}`); + const level = status.confirmationStatus; + if (level === "confirmed" || level === "finalized") return; + } + if (Date.now() - started > timeoutMs) throw new Error(`confirmation timeout (${signature})`); + await new Promise((r) => setTimeout(r, 400)); + } +} + +/** Everything `enableOneClickTrading` needs to bundle the session into ONE + * signAllTransactions sheet: the tx (blockhash set, ephemeral key already + * partial-signed — the wallet only adds its own signature) plus the pieces + * to persist once it confirms. */ +export interface BuiltSessionTransaction { + tx: Transaction; + sessionSigner: Keypair; + /** SessionTokenV2 PDA (base58). */ + sessionToken: string; + validUntil: number; +} + +/** + * Build (don't sign-with-the-wallet, don't send) a `create_session_v2` tx: + * generate an ephemeral keypair, derive the SessionTokenV2 PDA, set blockhash + * + fee payer, and partial-sign with the ephemeral key. `topUpSol` moves SOL + * from the wallet to the session signer so trades pay their own fees. + */ +export async function buildSessionTransaction( + wallet: SessionWallet, + connection: Connection, + opts: { validHours?: number; topUpSol?: number } = {}, +): Promise { + const { validHours = 24, topUpSol = 0.01 } = opts; + const sessionSigner = Keypair.generate(); + const validUntil = Math.floor(Date.now() / 1000) + Math.floor(validHours * 3600); + const sessionToken = deriveSessionToken(wallet.publicKey, sessionSigner.publicKey); + const targetProgram = MAGIC_TRADE_PROGRAM; + + const manager = new SessionTokenManager(wallet, connection); + const tx: Transaction = await manager.program.methods + .createSessionV2(true, new BN(validUntil), new BN(Math.round(topUpSol * 1e9))) + .accountsPartial({ + sessionToken, + sessionSigner: sessionSigner.publicKey, + feePayer: wallet.publicKey, + authority: wallet.publicKey, + targetProgram, + }) + .transaction(); + + tx.feePayer = wallet.publicKey; + tx.recentBlockhash = (await connection.getLatestBlockhash("confirmed")).blockhash; + tx.partialSign(sessionSigner); // ephemeral key co-signs; wallet signs later + return { tx, sessionSigner, sessionToken: sessionToken.toBase58(), validUntil }; +} + +/** Persist a confirmed session to localStorage and return the live shape. */ +export function persistSession(args: { + sessionSigner: Keypair; + sessionToken: string; + authority: string; + validUntil: number; +}): LoadedSession { + const stored: StoredSession = { + secretKey: Array.from(args.sessionSigner.secretKey), + token: args.sessionToken, + authority: args.authority, + validUntil: args.validUntil, + }; + window.localStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(stored)); + return { + keypair: args.sessionSigner, + token: stored.token, + authority: stored.authority, + validUntil: stored.validUntil, + }; +} + +/** + * Compat wrapper (build → ONE wallet popup → send → confirm → persist). + * `enableOneClickTrading` (lib/enable.ts) uses buildSessionTransaction + * directly so the session rides the same signAllTransactions sheet as the + * basket/ledger/delegate/deposit txs. + */ +export async function createSession(opts: { + wallet: SessionWallet; + connection: Connection; + validHours?: number; + topUpSol?: number; +}): Promise<{ session: LoadedSession; signature: string; confirmMs: number }> { + const { wallet, connection, validHours, topUpSol } = opts; + const built = await buildSessionTransaction(wallet, connection, { validHours, topUpSol }); + const signed = await wallet.signTransaction(built.tx); // the ONE popup + const started = Date.now(); // measure submit→confirmed only + const signature = await connection.sendRawTransaction(signed.serialize(), { maxRetries: 3 }); + await confirmSignature(connection, signature); + const confirmMs = Date.now() - started; + const session = persistSession({ + sessionSigner: built.sessionSigner, + sessionToken: built.sessionToken, + authority: wallet.publicKey.toBase58(), + validUntil: built.validUntil, + }); + return { session, signature, confirmMs }; +} + +/** + * Revoke the session on-chain (rent + leftover top-up refund to the wallet). + * `revoke_session_v2` needs NO wallet signature — the session signer itself + * pays the fee, so even ending a session is popup-free. Always clears local + * storage, even if the on-chain revoke fails (e.g. already expired + closed). + */ +export async function revokeSession( + session: LoadedSession, + connection: Connection, +): Promise<{ signature: string; confirmMs: number } | null> { + try { + const authority = new PublicKey(session.authority); + // Provider wallet here is only a type requirement — nothing is signed by it. + const manager = new SessionTokenManager( + { + publicKey: session.keypair.publicKey, + signTransaction: async (t: T) => t, + signAllTransactions: async (t: T[]) => t, + } as SessionWallet, + connection, + ); + const tx: Transaction = await manager.program.methods + .revokeSessionV2() + .accountsPartial({ + sessionToken: new PublicKey(session.token), + // Must match tx.feePayer below — the SESSION key pays for its own + // revocation (popup-free); a mismatch fails the signer constraint. + feePayer: session.keypair.publicKey, + authority, + }) + .transaction(); + tx.feePayer = session.keypair.publicKey; + tx.recentBlockhash = (await connection.getLatestBlockhash("confirmed")).blockhash; + tx.sign(session.keypair); + const started = Date.now(); + const signature = await connection.sendRawTransaction(tx.serialize(), { maxRetries: 3 }); + await confirmSignature(connection, signature); + clearSession(); + return { signature, confirmMs: Date.now() - started }; + // ONLY a confirmed revoke clears storage — wiping on failure would orphan + // a LIVE key with no way to retry the revoke (audit find). + } catch { + return null; // revoke failed — session stays stored so you can retry; + // worst case the token expires on its own at validUntil + } +} diff --git a/examples/copy-trade/lib/signer.ts b/examples/copy-trade/lib/signer.ts new file mode 100644 index 0000000..84a53a5 --- /dev/null +++ b/examples/copy-trade/lib/signer.ts @@ -0,0 +1,40 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/signer.ts — the trading signer: the SESSION keypair signs every tap. +// THE HARD PART: routing — trading txs sign+submit to network.erRpc and NEVER +// touch the blockhash (the API pre-signed it). Setup txs are handled by +// lib/enable.ts on the base chain behind ONE wallet approval. +// GOTCHAS.md → "Two chains, one flow" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +import { signAndSend, type NetworkConfig, type SendResult } from "flash-v2"; +import type { LoadedSession, SessionWallet } from "./session"; + +/** Everything a tap needs to know about "who signs and where it goes". */ +export interface ActiveSigner { + /** Basket owner (base58) — the connected wallet. */ + owner: string; + /** Sign + submit a TRADING tx → Ephemeral Rollup. Popup-free. */ + sendTrade(transactionBase64: string): Promise; + /** Extra request fields so the API builds for the session signer. */ + tradeFields: { signer: string; sessionToken: string }; +} + +/** + * One-Click Trading signer: the wallet approved setup ONCE (lib/enable.ts); + * every TRADE auto-signs with the ephemeral session key — `signer` + + * `sessionToken` ride along in the request so the API builds for it. + */ +export function makeSessionSigner( + wallet: SessionWallet, + session: LoadedSession, + network: NetworkConfig, +): ActiveSigner { + return { + owner: wallet.publicKey.toBase58(), + sendTrade: (tx64) => signAndSend(network.erRpc, tx64, session.keypair, { skipPreflight: true }), + tradeFields: { + signer: session.keypair.publicKey.toBase58(), + sessionToken: session.token, + }, + }; +} diff --git a/examples/copy-trade/next-env.d.ts b/examples/copy-trade/next-env.d.ts new file mode 100644 index 0000000..830fb59 --- /dev/null +++ b/examples/copy-trade/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/examples/copy-trade/next.config.ts b/examples/copy-trade/next.config.ts new file mode 100644 index 0000000..6804dcf --- /dev/null +++ b/examples/copy-trade/next.config.ts @@ -0,0 +1,30 @@ +// ───────────────────────────────────────────────────────────────────────────── +// next.config.ts — Next.js wiring for a workspace that ships raw TypeScript. +// THE HARD PART: flash-v2's entry point IS src/index.ts (no build step), so it +// must be transpiled by the app; anchor/gum-sdk pull Node-ish deps that need +// browser fallbacks. GOTCHAS.md → "Two chains, one flow" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +import path from "node:path"; +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + reactStrictMode: true, + // The monorepo root (bun workspaces) — keeps Next from guessing wrong. + outputFileTracingRoot: path.join(__dirname, "../.."), + // flash-v2 is consumed straight from packages/flash-v2/src/*.ts — compile it. + transpilePackages: ["flash-v2"], + // Browsers auto-request /favicon.ico; we only ship app/icon.svg — redirect + // instead of 404ing the console on every load. + async redirects() { + return [{ source: "/favicon.ico", destination: "/icon.svg", permanent: true }]; + }, + webpack: (config) => { + // @coral-xyz/anchor (via @magicblock-labs/gum-sdk) probes Node built-ins; + // none are needed in the browser paths this app uses. + config.resolve.fallback = { ...config.resolve.fallback, fs: false, os: false, path: false }; + return config; + }, +}; + +export default nextConfig; diff --git a/examples/copy-trade/package.json b/examples/copy-trade/package.json index 0c14d5c..cdd467d 100644 --- a/examples/copy-trade/package.json +++ b/examples/copy-trade/package.json @@ -1,19 +1,36 @@ { - "name": "example-copy-trade", + "name": "copy-trade", "private": true, "version": "0.1.0", - "type": "module", + "description": "Copy trading on Flash V2 × MagicBlock: rank real leaders by win rate, follow one, and mirror their opens/closes onto your own account with collateral-ratio sizing — manual-confirm or capped auto-copy, non-custodial via session keys. Liquid Glass UI.", "scripts": { - "dev": "bun run src/index.ts", + "dev": "next dev", + "build": "next build", + "start": "next start", "typecheck": "tsc --noEmit" }, "dependencies": { + "@coral-xyz/anchor": "^0.30.1", + "@magicblock-labs/gum-sdk": "^3.0.10", + "@solana/spl-token": "^0.4.14", + "@solana/wallet-adapter-base": "^0.9.27", + "@solana/wallet-adapter-phantom": "^0.9.29", + "@solana/wallet-adapter-react": "^0.15.39", + "@solana/wallet-adapter-solflare": "^0.6.28", "@solana/web3.js": "^1.98.2", - "flash-v2": "workspace:*" + "flash-v2": "workspace:*", + "next": "^15.5.19", + "react": "^19.2.0", + "react-dom": "^19.2.0" }, "devDependencies": { + "@tailwindcss/postcss": "^4.3.0", + "@types/bn.js": "^5.1.5", "@types/node": "^22.10.0", - "typescript": "^5.6.0" + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "tailwindcss": "^4.3.0", + "typescript": "^5.9.0" }, "repository": { "type": "git", diff --git a/examples/copy-trade/postcss.config.mjs b/examples/copy-trade/postcss.config.mjs new file mode 100644 index 0000000..5e461c6 --- /dev/null +++ b/examples/copy-trade/postcss.config.mjs @@ -0,0 +1,14 @@ +// ───────────────────────────────────────────────────────────────────────────── +// postcss.config.mjs — Tailwind v4 enters through PostCSS; no tailwind.config. +// THE HARD PART: nothing — v4 reads design tokens from @theme in globals.css, +// so the entire look lives in one CSS file instead of a JS config. +// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/examples/copy-trade/src/index.ts b/examples/copy-trade/src/index.ts deleted file mode 100644 index 64bb28c..0000000 --- a/examples/copy-trade/src/index.ts +++ /dev/null @@ -1,220 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// copy-trade — mirror a leader wallet's positions, non-custodially. -// THE HARD PARTS: there is no "trades feed" — you DIFF consecutive owner -// snapshots into events; you size by COLLATERAL RATIO (never copy raw size, -// that's how followers get over-leveraged); and the follower signs their own -// transactions — nobody ever holds their keys. Dry-run by default. -// GOTCHAS.md → §9 WS frames · §16 the $11 rule · §4 the 97% close threshold -// ───────────────────────────────────────────────────────────────────────────── -// -// LEADER= bun run dev # dry-run: print mirrored intents -// LEADER= FOLLOWER_KEYPAIR=~/.config/solana/mainnet.json \ -// bun run dev -- --execute # mainnet: follower signs + submits -// -// Architecture (one screen): -// subscribeOwner(LEADER) ──▶ diff(prev, next) ──▶ events: OPEN / GROW / SHRINK / CLOSE -// events × (follower_collateral / leader_collateral) ──▶ trade-builder calls -// dry-run prints them · --execute signs with the FOLLOWER's key → ER RPC - -import { Keypair } from "@solana/web3.js"; -import { readFileSync } from "node:fs"; -import { - FlashV2Client, - subscribeOwner, - signAndSend, - RECOMMENDED_MIN_COLLATERAL_USD, - type BasketSnapshot, - type PositionMetrics, - type TradeType, -} from "flash-v2"; - -// ── Config ─────────────────────────────────────────────────────────────────── -const LEADER = process.env.LEADER ?? ""; -const EXECUTE = process.argv.includes("--execute"); -const MAX_FOLLOW_USD = Number(process.env.MAX_FOLLOW_USD ?? "100"); // hard cap per mirror -const RATIO_OVERRIDE = process.env.RATIO ? Number(process.env.RATIO) : undefined; - -if (!LEADER) { - console.log("copy-trade needs LEADER=."); - console.log("Tip: any wallet with live V2 positions works. Dry-run prints intents only."); - process.exit(1); -} - -const flash = new FlashV2Client(); - -const followerKeypair = (() => { - if (!EXECUTE) return undefined; - const p = process.env.FOLLOWER_KEYPAIR; - if (!p) throw new Error("--execute needs FOLLOWER_KEYPAIR (THROWAWAY wallet — real funds, keep it small)"); - return Keypair.fromSecretKey(Uint8Array.from(JSON.parse(readFileSync(p, "utf8")))); -})(); -const follower = followerKeypair?.publicKey.toBase58(); - -// ── 1. Diffing: snapshots → events ─────────────────────────────────────────── -// A position is keyed by market+side (V2 allows one long + one short per market). -type PosKey = string; -const keyOf = (p: PositionMetrics): PosKey => `${p.marketSymbol}:${p.sideUi.toUpperCase()}`; - -interface MirrorEvent { - kind: "OPEN" | "GROW" | "SHRINK" | "CLOSE"; - market: string; - side: TradeType; - deltaUsd: number; // size change, USD notional - leverage: number; // leader's current leverage (for OPEN/GROW) - leaderCollateralUsd: number; -} - -function diff(prev: BasketSnapshot | undefined, next: BasketSnapshot): MirrorEvent[] { - const events: MirrorEvent[] = []; - const before = new Map( - Object.values(prev?.positionMetrics ?? {}).map((p) => [keyOf(p), p]), - ); - const after = new Map( - Object.values(next.positionMetrics).map((p) => [keyOf(p), p]), - ); - - for (const [k, now] of after) { - const was = before.get(k); - const side = now.sideUi.toUpperCase() as TradeType; - const sizeNow = Number(now.sizeUsdUi); - const lev = Number.parseFloat(now.leverageUi) || 1; - const col = Number(now.collateralUsdUi); - if (!was) { - events.push({ kind: "OPEN", market: now.marketSymbol, side, deltaUsd: sizeNow, leverage: lev, leaderCollateralUsd: col }); - } else { - const deltaUsd = sizeNow - Number(was.sizeUsdUi); - if (Math.abs(deltaUsd) > 0.01) { - events.push({ - kind: deltaUsd > 0 ? "GROW" : "SHRINK", - market: now.marketSymbol, side, deltaUsd: Math.abs(deltaUsd), leverage: lev, leaderCollateralUsd: col, - }); - } - } - } - for (const [k, was] of before) { - if (!after.has(k)) { - events.push({ - kind: "CLOSE", market: was.marketSymbol, - side: was.sideUi.toUpperCase() as TradeType, - deltaUsd: Number(was.sizeUsdUi), leverage: 1, leaderCollateralUsd: Number(was.collateralUsdUi), - }); - } - } - return events; -} - -// ── 2. Sizing: collateral ratio, never raw size — ONE function, both modes ─── -async function followerCollateralUsd(): Promise { - if (!follower) return 0; - const snap = await flash.owner(follower); - // Free collateral approximation for the demo: sum of position collateral. - // A production copier would track its deposit-ledger balance too. - return Object.values(snap.positionMetrics).reduce((s, p) => s + Number(p.collateralUsdUi), 0); -} - -/** Identical sizing for dry-run and execute. Dry-run without a follower wallet - * assumes ratio 1 and SAYS SO — the printed intent must match what execute - * would do, or the dry-run is a lie. */ -function sizeFor(e: MirrorEvent, followerColUsd: number | undefined): { usd: number; label: string } { - const ratio = - RATIO_OVERRIDE ?? - (followerColUsd === undefined - ? 1 // dry-run with no follower configured — labeled as assumed below - : e.leaderCollateralUsd > 0 && followerColUsd > 0 - ? followerColUsd / e.leaderCollateralUsd - : 0); // unknown leader collateral or broke follower → mirror nothing - const usd = Math.min(e.deltaUsd * ratio, MAX_FOLLOW_USD); // hard cap — never YOLO a whale's size - const label = - RATIO_OVERRIDE !== undefined ? `ratio ${RATIO_OVERRIDE} (RATIO env)` : - followerColUsd === undefined ? "ratio 1 (ASSUMED — set FOLLOWER_KEYPAIR for real sizing)" : - `ratio ${ratio.toFixed(3)} (collateral ${followerColUsd.toFixed(2)}/${e.leaderCollateralUsd.toFixed(2)})`; - return { usd, label }; -} - -// ── 3. Replay: build → (dry-print | follower signs → ER) ───────────────────── -async function replay(e: MirrorEvent) { - const followerCol = EXECUTE ? await followerCollateralUsd() : undefined; - const { usd, label } = sizeFor(e, followerCol); - const stamp = new Date().toISOString().slice(11, 19); - - if (e.kind === "OPEN" || e.kind === "GROW") { - // GOTCHAS §16: collateral below ~$11 can't take TP/SL — but FLOORING the - // collateral while keeping leader leverage would INFLATE the position - // (e.g. a $5 mirror becoming $55). Too small to mirror honestly → skip. - const collateral = usd / e.leverage; - if (collateral < RECOMMENDED_MIN_COLLATERAL_USD) { - console.log(`[${stamp}] SKIP ${e.kind} ${e.side} ${e.market} — mirror too small ($${collateral.toFixed(2)} collateral < $${RECOMMENDED_MIN_COLLATERAL_USD} floor; raise MAX_FOLLOW_USD/RATIO or accept skipping)`); - return; - } - console.log(`[${stamp}] ${e.kind} ${e.side} ${e.market} — leader Δ$${e.deltaUsd.toFixed(2)} → mirror $${usd.toFixed(2)} (${collateral.toFixed(2)} USDC × ${e.leverage}x) · ${label}`); - if (!EXECUTE || !followerKeypair || !follower) return console.log(" dry-run: not signed (set FOLLOWER_KEYPAIR + --execute)"); - const built = await flash.openPosition({ - inputTokenSymbol: "USDC", outputTokenSymbol: e.market, - inputAmountUi: collateral.toFixed(2), leverage: e.leverage, - tradeType: e.side, orderType: "MARKET", owner: follower, slippagePercentage: "0.8", // wider than leader — you fill later - }); - if (built.transactionBase64) { - const { signature, confirmMs } = await signAndSend(flash.network.erRpc, built.transactionBase64, followerKeypair); - console.log(` ⛓ mirrored in ${confirmMs}ms → ${signature}`); - } - } else { - // SHRINK/CLOSE: close proportionally. CLOSE sends "0" = full (GOTCHAS §4/§18). - const closeUsd = e.kind === "CLOSE" ? "0" : usd.toFixed(2); - console.log(`[${stamp}] ${e.kind} ${e.side} ${e.market} — leader Δ$${e.deltaUsd.toFixed(2)} → close ${closeUsd === "0" ? "ALL" : "$" + closeUsd}`); - if (!EXECUTE || !followerKeypair || !follower) return console.log(" dry-run: not signed"); - const built = await flash.closePosition({ - marketSymbol: e.market, side: e.side, inputUsdUi: closeUsd, - withdrawTokenSymbol: "USDC", owner: follower, - }); - if (built.transactionBase64) { - const { signature, confirmMs } = await signAndSend(flash.network.erRpc, built.transactionBase64, followerKeypair); - console.log(` ⛓ mirrored in ${confirmMs}ms → ${signature}`); - } - } -} - -// ── Main: stream the leader, diff every basket frame ───────────────────────── -console.log(`copy-trade on ${flash.network.name} — leader ${LEADER}`); -console.log(EXECUTE ? `EXECUTE mode — follower ${follower} signs its own txs (mainnet)` : "DRY-RUN — printing mirror intents only"); -console.log("waiting for leader activity (diffing basket frames)…\n"); - -let prev: BasketSnapshot | undefined; - -// RE-ENTRANCY: onUpdate is NOT awaited by the stream — frames can arrive while -// a replay is mid-flight, and two concurrent replay loops would double-mirror. -// We COALESCE instead of dropping: remember only the LATEST basket frame and -// diff against it when the current loop finishes. Intermediate states collapse -// into net deltas, which is exactly what a mirror wants. -let processing = false; -let queued: BasketSnapshot | undefined; - -async function onBasketFrame(snap: BasketSnapshot) { - if (processing) { queued = snap; return; } - processing = true; - try { - let next: BasketSnapshot | undefined = snap; - while (next) { - const events = diff(prev, next); - prev = structuredClone(next); - for (const e of events) { - try { await replay(e); } catch (err) { console.log(`! mirror failed: ${(err as Error).message}`); } - } - next = queued; - queued = undefined; - } - } finally { - processing = false; - } -} - -subscribeOwner({ - owner: LEADER, - network: flash.network, - onUpdate: (snap, source) => { - // Only `basket` frames are settlement truth (GOTCHAS §8-9); `metrics` - // frames re-price the SAME position every second and would spam GROW/SHRINK. - if (source === "metrics") return; - void onBasketFrame(snap); - }, - onStatus: (s) => console.log(`(stream: ${s})`), -}); diff --git a/examples/copy-trade/tsconfig.json b/examples/copy-trade/tsconfig.json index 553828d..792d382 100644 --- a/examples/copy-trade/tsconfig.json +++ b/examples/copy-trade/tsconfig.json @@ -1,13 +1,23 @@ { "compilerOptions": { "target": "ES2022", - "lib": ["ES2022", "DOM"], - "module": "ESNext", - "moduleResolution": "Bundler", - "strict": true, + "lib": ["dom", "dom.iterable", "ES2022"], + "allowJs": true, "skipLibCheck": true, + "strict": true, + "noUncheckedIndexedAccess": true, "noEmit": true, - "allowImportingTsExtensions": true + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "allowImportingTsExtensions": true, + "plugins": [{ "name": "next" }], + "paths": { "@/*": ["./*"] } }, - "include": ["src/**/*.ts"] + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] } From f6f118f092c338fb89b9e2733b20fadb5bb66376 Mon Sep 17 00:00:00 2001 From: MightyMags Date: Fri, 12 Jun 2026 09:36:01 -0400 Subject: [PATCH 02/26] copy-trade: plain-language console, deposit/withdraw flow, leaderboard filters - Engine baselines the leader's current book on follow (copy their NEXT move, not a position they're already mid-way through); plain skip reasons - Console rewritten in stupid-short language: 'How to copy' (Ask me / Auto), 'your balance' tappable -> deposit/withdraw, 'Total budget' / 'Max per trade' with hints, 'Used $X of $Y'; activity + your-copies headers - Wired full V2 deposit/withdraw FundsSheet (two-step withdraw) into the app - Leaderboard sort (Top PnL / Win rate / Volume / Most trades) + in-a-trade filter - Labeled position rows (size / lev / entry / pnl) Co-Authored-By: Claude Opus 4.8 --- examples/copy-trade/app/globals.css | 7 + examples/copy-trade/components/app.tsx | 197 +++++++--- .../copy-trade/components/funds-sheet.tsx | 347 ++++++++++++++++++ examples/copy-trade/lib/copy-engine.ts | 19 +- 4 files changed, 506 insertions(+), 64 deletions(-) create mode 100644 examples/copy-trade/components/funds-sheet.tsx diff --git a/examples/copy-trade/app/globals.css b/examples/copy-trade/app/globals.css index 0a841f3..d61cd0a 100644 --- a/examples/copy-trade/app/globals.css +++ b/examples/copy-trade/app/globals.css @@ -36,6 +36,13 @@ --tray: rgb(255 255 255 / 0.03); /* double-bezel outer tray */ --tray-edge: rgb(255 255 255 / 0.06); + /* flat surface tokens — sheets, inputs, segmented controls (glass-flavored darks) */ + --color-panel: rgb(255 255 255 / 0.04); + --color-panel2: rgb(255 255 255 / 0.08); + --color-sheet: #0c100e; + --color-edge: rgb(255 255 255 / 0.10); + --color-edge2: rgb(255 255 255 / 0.17); + --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; --font-display: var(--font-grotesk), ui-sans-serif, system-ui, sans-serif; --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, "JetBrains Mono", Consolas, monospace; diff --git a/examples/copy-trade/components/app.tsx b/examples/copy-trade/components/app.tsx index 6c1760c..1f876e5 100644 --- a/examples/copy-trade/components/app.tsx +++ b/examples/copy-trade/components/app.tsx @@ -22,6 +22,7 @@ import { makeSessionSigner } from "@/lib/signer"; import { enableOneClickTrading, type EnableState } from "@/lib/enable"; import { useLeaderboard, isLikelyPubkey, type LeaderRow } from "@/lib/leaders"; import { useCopyEngine, type CopyConfig } from "@/lib/copy-engine"; +import FundsSheet from "@/components/funds-sheet"; // ── providers ──────────────────────────────────────────────────────────────── export default function App() { @@ -92,6 +93,7 @@ function AppInner() { // ── copy config ────────────────────────────────────────────────────────────── const [cfg, setCfg] = useState({ mode: "manual", armed: false, budgetUsd: 50, maxPerTradeUsd: 15 }); const setC = (p: Partial) => setCfg((c) => ({ ...c, ...p })); + const [fundsOpen, setFundsOpen] = useState(false); const engine = useCopyEngine({ leader, signer, followerCollateralUsd, config: cfg }); @@ -125,6 +127,7 @@ function AppInner() { address={owner} inBasketUsd={basketExists ? inBasketUsd : null} onConnect={() => walletCtx.select?.(walletCtx.wallets[0]?.adapter.name ?? null)} + onBack={leader ? () => setLeader(null) : undefined} streamStatus={leader ? engine.status : null} /> @@ -155,6 +158,7 @@ function AppInner() { enableState={enableState} err={err} onEnable={runEnable} + onFunds={() => setFundsOpen(true)} cfg={cfg} setC={setC} engine={engine} @@ -163,9 +167,24 @@ function AppInner() { />
)} -

- Flash V2 mainnet · real funds. Copy trading carries the leader's risk — start small, watch the spread, keep your budget what you can lose. +

+ Mainnet · real funds. You take the leader's risk — start small.

+ + setFundsOpen(false)} + walletCtx={walletCtx.publicKey && walletCtx.signTransaction + ? { publicKey: walletCtx.publicKey, signTransaction: walletCtx.signTransaction, signAllTransactions: walletCtx.signAllTransactions } + : null} + usdcMint={usdcMint} + walletUsdc={balances.usdc} + walletSol={balances.sol} + inBasketUsd={basketBal?.inBasketUsd ?? null} + rollupAssets={basketBal?.assets ?? null} + onLog={() => {}} + onMoved={() => { void balances.refresh(); void refreshBasket(); }} + /> ); } @@ -173,21 +192,29 @@ function AppInner() { // ════════════════════════════════════════════════════════════════════════════ // Header // ════════════════════════════════════════════════════════════════════════════ -function Header({ connected, address, inBasketUsd, onConnect, streamStatus }: { - connected: boolean; address: string | null; inBasketUsd: number | null; onConnect: () => void; streamStatus: string | null; +function Header({ connected, address, inBasketUsd, onConnect, onBack, streamStatus }: { + connected: boolean; address: string | null; inBasketUsd: number | null; onConnect: () => void; onBack?: () => void; streamStatus: string | null; }) { return ( -
+
- - - -
-

Copy Trade

-

Flash V2 · ephemeral rollup

-
+ {onBack ? ( + + ) : ( + <> + + + +

Copy Trade

+ + )} {streamStatus && ( - + {streamStatus} @@ -219,12 +246,27 @@ function Discover({ board, paste, setPaste, onFollow, prices }: { board: ReturnType; paste: string; setPaste: (s: string) => void; onFollow: (o: string) => void; prices: Record; }) { void prices; + const [sortBy, setSortBy] = useState<"pnl" | "win" | "vol" | "trades">("pnl"); + const [liveOnly, setLiveOnly] = useState(false); + const shown = useMemo(() => { + const base = liveOnly ? board.leaders.filter((l) => (board.openByOwner[l.owner] ?? 0) > 0) : board.leaders; + return [...base].sort((a, b) => + sortBy === "win" ? b.win_rate - a.win_rate + : sortBy === "vol" ? b.total_volume_usd - a.total_volume_usd + : sortBy === "trades" ? b.num_trades - a.num_trades + : b.net_pnl - a.net_pnl, + ).slice(0, 12); + }, [board.leaders, board.openByOwner, sortBy, liveOnly]); + const sorts: { k: typeof sortBy; label: string }[] = [ + { k: "pnl", label: "Top PnL" }, { k: "win", label: "Win rate" }, { k: "vol", label: "Volume" }, { k: "trades", label: "Most trades" }, + ]; + return (
-
- leaderboard · live ER -

Follow the proven.

-

Real Flash V2 traders, ranked by net PnL and win rate on the rollup. Pick one — or paste any wallet — and mirror their moves onto your account.

+
+ live · ranked +

Copy a winner.

+

Pick a leader. Mirror their trades.

@@ -233,7 +275,7 @@ function Discover({ board, paste, setPaste, onFollow, prices }: { setPaste(e.target.value.trim())} - placeholder="paste a leader wallet address to follow…" + placeholder="or paste a wallet" className="w-full bg-transparent font-mono text-[13px] text-ink outline-none placeholder:text-faint" /> {isLikelyPubkey(paste) && ( @@ -242,6 +284,22 @@ function Discover({ board, paste, setPaste, onFollow, prices }: {
+ {/* sort + live filter — rank the same 50 leaders by the metric you care about */} +
+
+ {sorts.map((s) => ( + + ))} +
+ +
+
{board.loading && board.leaders.length === 0 && ( Array.from({ length: 6 }).map((_, i) =>
) @@ -249,7 +307,10 @@ function Discover({ board, paste, setPaste, onFollow, prices }: { {board.error && board.leaders.length === 0 && (
Couldn't load the leaderboard: {board.error}
)} - {board.leaders.slice(0, 12).map((l) => ( + {!board.loading && !board.error && shown.length === 0 && ( +
No one's in a trade right now.
+ )} + {shown.map((l) => ( 0} openCount={board.openByOwner[l.owner] ?? 0} onFollow={() => onFollow(l.owner)} /> ))}
@@ -308,14 +369,14 @@ function LeaderPanel({ leader, row, positions, prices, status, onUnfollow }: {
- leader's book + their trades {positions.length} open
{positions.length === 0 && ( -
{status === "live" ? "leader is flat — waiting for their next move…" : "connecting to the leader stream…"}
+
{status === "live" ? "No open trades right now." : "Connecting…"}
)} {positions.map((p) => )}
@@ -332,10 +393,13 @@ function PositionRow({ p, mark }: { p: PositionMetrics; mark: number | null }) { {long ? "LONG" : "SHORT"}

{p.marketSymbol}

-

{fmtUsd(p.sizeUsdUi)} · {Number(p.leverageUi || 0).toFixed(1)}× · entry {Number(p.entryPriceUi).toLocaleString()}

+

+ size {fmtUsd(p.sizeUsdUi)} · lev {Number(p.leverageUi || 0).toFixed(1)}× · entry {Number(p.entryPriceUi).toLocaleString()} +

-
+
+

pnl

= 0 ? "text-long" : "text-short"}`}>{view ? `${view.pnlUsd >= 0 ? "+" : ""}${fmtUsd(view.pnlUsd)}` : "—"}

{view ? `${view.pnlPct >= 0 ? "+" : ""}${view.pnlPct.toFixed(1)}%` : ""}

@@ -346,30 +410,30 @@ function PositionRow({ p, mark }: { p: PositionMetrics; mark: number | null }) { // ════════════════════════════════════════════════════════════════════════════ // Console — your status, controls, pending mirrors, fills, your book // ════════════════════════════════════════════════════════════════════════════ -function Console({ ready, connected, inBasketUsd, loaded, enabling, enableState, err, onEnable, cfg, setC, engine, followerPositions, prices }: { +function Console({ ready, connected, inBasketUsd, loaded, enabling, enableState, err, onEnable, onFunds, cfg, setC, engine, followerPositions, prices }: { ready: boolean; connected: boolean; inBasketUsd: number; loaded: boolean; - enabling: boolean; enableState: EnableState | null; err: string | null; onEnable: () => void; + enabling: boolean; enableState: EnableState | null; err: string | null; onEnable: () => void; onFunds: () => void; cfg: CopyConfig; setC: (p: Partial) => void; engine: ReturnType; followerPositions: PositionMetrics[]; prices: Record; }) { // gate: connect → enable → fund → copy - if (!connected) return ; - if (!loaded) return ; + if (!connected) return ; + if (!loaded) return ; if (!ready) return ( ); if (inBasketUsd < 1) return ( - + ); return (
- + @@ -393,54 +457,69 @@ function GateCard({ title, body, action, error, pulse }: { title: string; body: ); } -function Controls({ cfg, setC, spent, inBasketUsd }: { cfg: CopyConfig; setC: (p: Partial) => void; spent: number; inBasketUsd: number }) { +function Controls({ cfg, setC, spent, inBasketUsd, onFunds }: { cfg: CopyConfig; setC: (p: Partial) => void; spent: number; inBasketUsd: number; onFunds: () => void }) { const auto = cfg.mode === "auto"; return (
-
-

Copy controls

-

balance {fmtUsd(inBasketUsd)} · spent {fmtUsd(spent)} / {fmtUsd(cfg.budgetUsd)}

-
- {/* mode toggle */} -
- - +

How to copy

+
+ +
+

+ {auto ? "Every trade they make copies automatically." : "We ask before each trade — you tap to confirm."} +

-
- setC({ budgetUsd: v })} /> - setC({ maxPerTradeUsd: v })} /> + {/* your money — tap to deposit / withdraw */} + + +
+ setC({ budgetUsd: v })} /> + setC({ maxPerTradeUsd: Math.max(1, v) })} />
+

Used {fmtUsd(spent)} of {fmtUsd(cfg.budgetUsd)}.

{auto && ( )} {auto && cfg.armed && ( -

mirrors execute automatically within your caps while this tab stays open. Keep the budget what you can lose.

+

Copying automatically. Keep this tab open.

)}
); } -function NumField({ label, value, onChange }: { label: string; value: number; onChange: (v: number) => void }) { +function NumField({ label, hint, value, onChange }: { label: string; hint?: string; value: number; onChange: (v: number) => void }) { return ( -
); } -function LeaderCard({ l, live, openCount, onFollow }: { l: LeaderRow; live: boolean; openCount: number; onFollow: () => void }) { +function LeaderCard({ l, live, openCount, onFollow, locked }: { l: LeaderRow; live: boolean; openCount: number; onFollow: () => void; locked?: boolean }) { const win = l.win_rate; return ( -
From 68e878a4df502d7c35aad03618631366345c2f1e Mon Sep 17 00:00:00 2001 From: MightyMags Date: Fri, 12 Jun 2026 10:19:08 -0400 Subject: [PATCH 05/26] copy-trade: combinable metric filters + big red Stop all on the hero - Reverted the extra filter row (Profitable/>=10/Win>=50). Back to the four metric chips (Top PnL / Win rate / Volume / Most trades), now MULTI-SELECT: pick several and they rank by a normalized blend (strong on all picked). The 'In a trade' toggle is independent again. Single clean row; active = glass pill. - Promoted Stop all out of the header chip to a BIG RED button on the right edge of the 'Copy a winner.' hero (shows when running or positions are open). Header keeps only the lightweight '1 copying / N open' indicator. Co-Authored-By: Claude Opus 4.8 --- examples/copy-trade/components/app.tsx | 133 ++++++++++++------------- 1 file changed, 65 insertions(+), 68 deletions(-) diff --git a/examples/copy-trade/components/app.tsx b/examples/copy-trade/components/app.tsx index 21daf01..6d2057f 100644 --- a/examples/copy-trade/components/app.tsx +++ b/examples/copy-trade/components/app.tsx @@ -177,8 +177,6 @@ function AppInner() { runningLeader={leader ? shortKey(leader) : null} copiedCount={followerPositions.length} onResume={() => setView("leader")} - onStopAll={stopAll} - stopping={stopping} /> {view === "leader" && leader ? ( @@ -216,6 +214,10 @@ function AppInner() { prices={prices} lockedTo={running ? leader : null} onResume={() => setView("leader")} + running={running} + copiedCount={followerPositions.length} + onStopAll={stopAll} + stopping={stopping} /> )}

@@ -243,9 +245,9 @@ function AppInner() { // ════════════════════════════════════════════════════════════════════════════ // Header // ════════════════════════════════════════════════════════════════════════════ -function Header({ connected, address, inBasketUsd, onConnect, onBack, streamStatus, running, runningLeader, copiedCount, onResume, onStopAll, stopping }: { +function Header({ connected, address, inBasketUsd, onConnect, onBack, streamStatus, running, runningLeader, copiedCount, onResume }: { connected: boolean; address: string | null; inBasketUsd: number | null; onConnect: () => void; onBack?: () => void; streamStatus: string | null; - running: boolean; runningLeader: string | null; copiedCount: number; onResume: () => void; onStopAll: () => void; stopping: boolean; + running: boolean; runningLeader: string | null; copiedCount: number; onResume: () => void; }) { return (

@@ -271,23 +273,16 @@ function Header({ connected, address, inBasketUsd, onConnect, onBack, streamStat {streamStatus} )} - {(running || copiedCount > 0) && ( -
- {running ? ( - - ) : ( - - {copiedCount} open - - )} - -
- )} + {running ? ( + + ) : copiedCount > 0 ? ( + + {copiedCount} open + + ) : null}
{connected ? (
@@ -311,40 +306,51 @@ function Header({ connected, address, inBasketUsd, onConnect, onBack, streamStat // ════════════════════════════════════════════════════════════════════════════ // Discover — the leaderboard + paste-a-leader // ════════════════════════════════════════════════════════════════════════════ -function Discover({ board, paste, setPaste, onFollow, prices, lockedTo, onResume }: { +function Discover({ board, paste, setPaste, onFollow, prices, lockedTo, onResume, running, copiedCount, onStopAll, stopping }: { board: ReturnType; paste: string; setPaste: (s: string) => void; onFollow: (o: string) => void; prices: Record; - lockedTo: string | null; onResume: () => void; + lockedTo: string | null; onResume: () => void; running: boolean; copiedCount: number; onStopAll: () => void; stopping: boolean; }) { void prices; - const [sortBy, setSortBy] = useState<"pnl" | "win" | "vol" | "trades">("pnl"); - const [filters, setFilters] = useState>(new Set()); - const toggle = (k: string) => setFilters((prev) => { const n = new Set(prev); if (n.has(k)) n.delete(k); else n.add(k); return n; }); - const shown = useMemo(() => { - let base = board.leaders; - if (filters.has("live")) base = base.filter((l) => (board.openByOwner[l.owner] ?? 0) > 0); - if (filters.has("profit")) base = base.filter((l) => l.net_pnl > 0); - if (filters.has("active")) base = base.filter((l) => l.num_trades >= 10); - if (filters.has("win50")) base = base.filter((l) => l.win_rate >= 50); - return [...base].sort((a, b) => - sortBy === "win" ? b.win_rate - a.win_rate - : sortBy === "vol" ? b.total_volume_usd - a.total_volume_usd - : sortBy === "trades" ? b.num_trades - a.num_trades - : b.net_pnl - a.net_pnl, - ).slice(0, 12); - }, [board.leaders, board.openByOwner, sortBy, filters]); - const sorts: { k: typeof sortBy; label: string }[] = [ + // Pick one or more metric chips — multiple combine into a normalized blend + // (e.g. Top PnL + Most trades = strong on both). "In a trade" is independent. + const [picked, setPicked] = useState>(() => new Set(["pnl"])); + const [liveOnly, setLiveOnly] = useState(false); + const toggleMetric = (k: string) => setPicked((prev) => { + const n = new Set(prev); if (n.has(k)) n.delete(k); else n.add(k); + if (n.size === 0) n.add("pnl"); // never empty — fall back to Top PnL + return n; + }); + const metrics: { k: string; label: string }[] = [ { k: "pnl", label: "Top PnL" }, { k: "win", label: "Win rate" }, { k: "vol", label: "Volume" }, { k: "trades", label: "Most trades" }, ]; - const filterDefs: { k: string; label: string }[] = [ - { k: "live", label: "In a trade" }, { k: "profit", label: "Profitable" }, { k: "active", label: "≥10 trades" }, { k: "win50", label: "Win ≥50%" }, - ]; + const shown = useMemo(() => { + const base = liveOnly ? board.leaders.filter((l) => (board.openByOwner[l.owner] ?? 0) > 0) : board.leaders; + const val = (l: LeaderRow, k: string) => k === "win" ? l.win_rate : k === "vol" ? l.total_volume_usd : k === "trades" ? l.num_trades : l.net_pnl; + // normalize each picked metric to 0..1 across the board so they're comparable, then sum + const ranges = [...picked].map((k) => { + const vals = base.map((l) => val(l, k)); + const min = Math.min(...vals); const span = (Math.max(...vals) - min) || 1; + return { k, min, span }; + }); + const score = (l: LeaderRow) => ranges.reduce((s, r) => s + (val(l, r.k) - r.min) / r.span, 0); + return [...base].sort((a, b) => score(b) - score(a)).slice(0, 12); + }, [board.leaders, board.openByOwner, picked, liveOnly]); return (
-
- live · ranked -

Copy a winner.

-

Pick a leader. Mirror their trades.

+
+
+ live · ranked +

Copy a winner.

+

Pick a leader. Mirror their trades.

+
+ {(running || copiedCount > 0) && ( + + )}
{lockedTo && ( @@ -372,32 +378,23 @@ function Discover({ board, paste, setPaste, onFollow, prices, lockedTo, onResume
- {/* SORT (pick one) + FILTERS (stack as many as you want) */} -
-
- sort - {sorts.map((s) => ( - - ))} -
-
- filter - {filterDefs.map((f) => { - const on = filters.has(f.k); + {/* metric chips — pick one or combine several — plus an independent "in a trade" */} +
+
+ {metrics.map((m) => { + const on = picked.has(m.k); return ( - ); })} - {filters.size > 0 && ( - - )}
+
From c0641a71f44b71df54d5c3b22b7aa65bd6ab9b56 Mon Sep 17 00:00:00 2001 From: MightyMags Date: Fri, 12 Jun 2026 18:17:26 -0400 Subject: [PATCH 06/26] =?UTF-8?q?predict:=20Updown=20=E2=80=94=20glass=20p?= =?UTF-8?q?rice-call=20rounds=20on=20Flash=20V2=20(5m/15m/1h=20timed=20UP/?= =?UTF-8?q?DOWN,=20session-key=20settlement)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New examples/predict workspace: timed price-call rounds where each round is a real session-signed perp — ownerless quote at review, signed response = locked numbers, observed-state settlement engine with chain-truth reconciliation and orphan adoption. Plain-words UI (zero trading jargon), glassmorphism system, live price chart, searchable market picker, sheets conditionally mounted (closed markup can't render unstyled). Reuses tap-trade's proven session/enable/funds/balance/stream lib verbatim. Co-Authored-By: Claude Fable 5 --- bun.lock | 29 + examples/predict/.env.example | 17 + examples/predict/DESIGN.md | 48 ++ examples/predict/README.md | 66 +++ examples/predict/app/globals.css | 291 +++++++++ examples/predict/app/icon.svg | 5 + examples/predict/app/layout.tsx | 38 ++ examples/predict/app/page.tsx | 12 + examples/predict/components/active-round.tsx | 88 +++ examples/predict/components/app.tsx | 555 ++++++++++++++++++ examples/predict/components/enable-sheet.tsx | 85 +++ examples/predict/components/funds-sheet.tsx | 131 +++++ examples/predict/components/history.tsx | 46 ++ examples/predict/components/market-picker.tsx | 207 +++++++ .../components/mechanics-disclosure.tsx | 57 ++ examples/predict/components/price-chart.tsx | 120 ++++ examples/predict/components/settle-card.tsx | 76 +++ examples/predict/components/stats-strip.tsx | 31 + examples/predict/components/ticket.tsx | 253 ++++++++ examples/predict/components/token-icon.tsx | 34 ++ examples/predict/components/wallet-bar.tsx | 139 +++++ examples/predict/lib/clock.tsx | 26 + examples/predict/lib/copy.ts | 73 +++ examples/predict/lib/enable.ts | 339 +++++++++++ examples/predict/lib/flash.ts | 48 ++ examples/predict/lib/format.ts | 130 ++++ examples/predict/lib/funds.ts | 218 +++++++ examples/predict/lib/hooks.ts | 474 +++++++++++++++ examples/predict/lib/payoff.ts | 142 +++++ examples/predict/lib/rounds.ts | 223 +++++++ examples/predict/lib/session.ts | 268 +++++++++ examples/predict/lib/signer.ts | 40 ++ examples/predict/lib/stream.tsx | 81 +++ examples/predict/lib/use-price-history.ts | 79 +++ examples/predict/lib/use-sheet-mount.ts | 26 + examples/predict/next-env.d.ts | 6 + examples/predict/next.config.ts | 30 + examples/predict/package.json | 34 ++ examples/predict/postcss.config.mjs | 14 + examples/predict/public/token-icons/2z.png | Bin 0 -> 1983 bytes examples/predict/public/token-icons/NOTICE | 4 + examples/predict/public/token-icons/aapl.png | Bin 0 -> 7097 bytes examples/predict/public/token-icons/ada.png | Bin 0 -> 4799 bytes examples/predict/public/token-icons/amd.png | Bin 0 -> 5665 bytes examples/predict/public/token-icons/amzn.png | Bin 0 -> 3091 bytes examples/predict/public/token-icons/asts.svg | 1 + examples/predict/public/token-icons/aud.png | Bin 0 -> 3548 bytes examples/predict/public/token-icons/bnb.png | Bin 0 -> 2320 bytes examples/predict/public/token-icons/btc.svg | 1 + examples/predict/public/token-icons/chip.svg | 38 ++ examples/predict/public/token-icons/coin.svg | 4 + .../predict/public/token-icons/copper.svg | 16 + examples/predict/public/token-icons/crcl.svg | 20 + .../predict/public/token-icons/crudeoil.png | Bin 0 -> 582655 bytes examples/predict/public/token-icons/eth.svg | 1 + examples/predict/public/token-icons/eur.png | Bin 0 -> 2156 bytes examples/predict/public/token-icons/flp.x.png | Bin 0 -> 1377743 bytes examples/predict/public/token-icons/gbp.png | Bin 0 -> 2667 bytes examples/predict/public/token-icons/hype.png | Bin 0 -> 2379 bytes examples/predict/public/token-icons/intc.svg | 1 + examples/predict/public/token-icons/iwm.png | Bin 0 -> 72601 bytes .../predict/public/token-icons/jitosol.png | Bin 0 -> 6042 bytes examples/predict/public/token-icons/jup.png | Bin 0 -> 1835 bytes examples/predict/public/token-icons/kmno.png | Bin 0 -> 3618 bytes examples/predict/public/token-icons/lit.png | Bin 0 -> 1240 bytes examples/predict/public/token-icons/lly.svg | 1 + examples/predict/public/token-icons/mega.svg | 7 + examples/predict/public/token-icons/mon.png | Bin 0 -> 3138 bytes examples/predict/public/token-icons/mstr.svg | 4 + examples/predict/public/token-icons/mu.svg | 1 + .../predict/public/token-icons/natgas.svg | 17 + examples/predict/public/token-icons/near.png | Bin 0 -> 3312 bytes examples/predict/public/token-icons/nvda.png | Bin 0 -> 10151 bytes examples/predict/public/token-icons/ondo.svg | 6 + examples/predict/public/token-icons/ore.png | Bin 0 -> 1640 bytes examples/predict/public/token-icons/pump.png | Bin 0 -> 198869 bytes examples/predict/public/token-icons/pyth.png | Bin 0 -> 40675 bytes examples/predict/public/token-icons/qcom.svg | 1 + examples/predict/public/token-icons/samo.png | Bin 0 -> 646283 bytes .../predict/public/token-icons/sflp.x.png | Bin 0 -> 1386071 bytes examples/predict/public/token-icons/sndk.svg | 5 + examples/predict/public/token-icons/sol.svg | 20 + examples/predict/public/token-icons/spr.png | Bin 0 -> 4584 bytes examples/predict/public/token-icons/spy.png | Bin 0 -> 4584 bytes examples/predict/public/token-icons/spyr.png | Bin 0 -> 4584 bytes examples/predict/public/token-icons/sui.png | Bin 0 -> 1346 bytes examples/predict/public/token-icons/tao.svg | 11 + examples/predict/public/token-icons/ton.svg | 4 + examples/predict/public/token-icons/trump.png | Bin 0 -> 10519 bytes examples/predict/public/token-icons/trx.svg | 14 + examples/predict/public/token-icons/tsla.png | Bin 0 -> 4264 bytes examples/predict/public/token-icons/tsm.svg | 1 + examples/predict/public/token-icons/txn.svg | 1 + .../predict/public/token-icons/usdcnh.png | Bin 0 -> 13903 bytes .../predict/public/token-icons/usdjpy.png | Bin 0 -> 6548 bytes examples/predict/public/token-icons/vvv.png | Bin 0 -> 1835 bytes examples/predict/public/token-icons/wif.png | Bin 0 -> 3105 bytes examples/predict/public/token-icons/wsol.png | Bin 0 -> 847 bytes examples/predict/public/token-icons/xag.png | Bin 0 -> 2667 bytes examples/predict/public/token-icons/xau.png | Bin 0 -> 1479 bytes examples/predict/public/token-icons/xaut.png | Bin 0 -> 746 bytes examples/predict/public/token-icons/xpd.svg | 21 + examples/predict/public/token-icons/xpt.svg | 17 + examples/predict/public/token-icons/xrp.png | Bin 0 -> 636 bytes examples/predict/public/token-icons/zec.png | Bin 0 -> 3727 bytes examples/predict/tsconfig.json | 23 + package.json | 3 +- 107 files changed, 4821 insertions(+), 1 deletion(-) create mode 100644 examples/predict/.env.example create mode 100644 examples/predict/DESIGN.md create mode 100644 examples/predict/README.md create mode 100644 examples/predict/app/globals.css create mode 100644 examples/predict/app/icon.svg create mode 100644 examples/predict/app/layout.tsx create mode 100644 examples/predict/app/page.tsx create mode 100644 examples/predict/components/active-round.tsx create mode 100644 examples/predict/components/app.tsx create mode 100644 examples/predict/components/enable-sheet.tsx create mode 100644 examples/predict/components/funds-sheet.tsx create mode 100644 examples/predict/components/history.tsx create mode 100644 examples/predict/components/market-picker.tsx create mode 100644 examples/predict/components/mechanics-disclosure.tsx create mode 100644 examples/predict/components/price-chart.tsx create mode 100644 examples/predict/components/settle-card.tsx create mode 100644 examples/predict/components/stats-strip.tsx create mode 100644 examples/predict/components/ticket.tsx create mode 100644 examples/predict/components/token-icon.tsx create mode 100644 examples/predict/components/wallet-bar.tsx create mode 100644 examples/predict/lib/clock.tsx create mode 100644 examples/predict/lib/copy.ts create mode 100644 examples/predict/lib/enable.ts create mode 100644 examples/predict/lib/flash.ts create mode 100644 examples/predict/lib/format.ts create mode 100644 examples/predict/lib/funds.ts create mode 100644 examples/predict/lib/hooks.ts create mode 100644 examples/predict/lib/payoff.ts create mode 100644 examples/predict/lib/rounds.ts create mode 100644 examples/predict/lib/session.ts create mode 100644 examples/predict/lib/signer.ts create mode 100644 examples/predict/lib/stream.tsx create mode 100644 examples/predict/lib/use-price-history.ts create mode 100644 examples/predict/lib/use-sheet-mount.ts create mode 100644 examples/predict/next-env.d.ts create mode 100644 examples/predict/next.config.ts create mode 100644 examples/predict/package.json create mode 100644 examples/predict/postcss.config.mjs create mode 100644 examples/predict/public/token-icons/2z.png create mode 100644 examples/predict/public/token-icons/NOTICE create mode 100644 examples/predict/public/token-icons/aapl.png create mode 100644 examples/predict/public/token-icons/ada.png create mode 100644 examples/predict/public/token-icons/amd.png create mode 100644 examples/predict/public/token-icons/amzn.png create mode 100644 examples/predict/public/token-icons/asts.svg create mode 100644 examples/predict/public/token-icons/aud.png create mode 100644 examples/predict/public/token-icons/bnb.png create mode 100644 examples/predict/public/token-icons/btc.svg create mode 100644 examples/predict/public/token-icons/chip.svg create mode 100644 examples/predict/public/token-icons/coin.svg create mode 100644 examples/predict/public/token-icons/copper.svg create mode 100644 examples/predict/public/token-icons/crcl.svg create mode 100644 examples/predict/public/token-icons/crudeoil.png create mode 100644 examples/predict/public/token-icons/eth.svg create mode 100644 examples/predict/public/token-icons/eur.png create mode 100644 examples/predict/public/token-icons/flp.x.png create mode 100644 examples/predict/public/token-icons/gbp.png create mode 100644 examples/predict/public/token-icons/hype.png create mode 100644 examples/predict/public/token-icons/intc.svg create mode 100644 examples/predict/public/token-icons/iwm.png create mode 100644 examples/predict/public/token-icons/jitosol.png create mode 100644 examples/predict/public/token-icons/jup.png create mode 100644 examples/predict/public/token-icons/kmno.png create mode 100644 examples/predict/public/token-icons/lit.png create mode 100644 examples/predict/public/token-icons/lly.svg create mode 100644 examples/predict/public/token-icons/mega.svg create mode 100644 examples/predict/public/token-icons/mon.png create mode 100644 examples/predict/public/token-icons/mstr.svg create mode 100644 examples/predict/public/token-icons/mu.svg create mode 100644 examples/predict/public/token-icons/natgas.svg create mode 100644 examples/predict/public/token-icons/near.png create mode 100644 examples/predict/public/token-icons/nvda.png create mode 100644 examples/predict/public/token-icons/ondo.svg create mode 100644 examples/predict/public/token-icons/ore.png create mode 100644 examples/predict/public/token-icons/pump.png create mode 100644 examples/predict/public/token-icons/pyth.png create mode 100644 examples/predict/public/token-icons/qcom.svg create mode 100644 examples/predict/public/token-icons/samo.png create mode 100644 examples/predict/public/token-icons/sflp.x.png create mode 100644 examples/predict/public/token-icons/sndk.svg create mode 100644 examples/predict/public/token-icons/sol.svg create mode 100644 examples/predict/public/token-icons/spr.png create mode 100644 examples/predict/public/token-icons/spy.png create mode 100644 examples/predict/public/token-icons/spyr.png create mode 100644 examples/predict/public/token-icons/sui.png create mode 100644 examples/predict/public/token-icons/tao.svg create mode 100644 examples/predict/public/token-icons/ton.svg create mode 100644 examples/predict/public/token-icons/trump.png create mode 100644 examples/predict/public/token-icons/trx.svg create mode 100644 examples/predict/public/token-icons/tsla.png create mode 100644 examples/predict/public/token-icons/tsm.svg create mode 100644 examples/predict/public/token-icons/txn.svg create mode 100644 examples/predict/public/token-icons/usdcnh.png create mode 100644 examples/predict/public/token-icons/usdjpy.png create mode 100644 examples/predict/public/token-icons/vvv.png create mode 100644 examples/predict/public/token-icons/wif.png create mode 100644 examples/predict/public/token-icons/wsol.png create mode 100644 examples/predict/public/token-icons/xag.png create mode 100644 examples/predict/public/token-icons/xau.png create mode 100644 examples/predict/public/token-icons/xaut.png create mode 100644 examples/predict/public/token-icons/xpd.svg create mode 100644 examples/predict/public/token-icons/xpt.svg create mode 100644 examples/predict/public/token-icons/xrp.png create mode 100644 examples/predict/public/token-icons/zec.png create mode 100644 examples/predict/tsconfig.json diff --git a/bun.lock b/bun.lock index 3361735..04dc369 100644 --- a/bun.lock +++ b/bun.lock @@ -43,6 +43,33 @@ "typescript": "^5.9.0", }, }, + "examples/predict": { + "name": "predict", + "version": "0.1.0", + "dependencies": { + "@coral-xyz/anchor": "^0.30.1", + "@magicblock-labs/gum-sdk": "^3.0.10", + "@solana/spl-token": "^0.4.14", + "@solana/wallet-adapter-base": "^0.9.27", + "@solana/wallet-adapter-phantom": "^0.9.29", + "@solana/wallet-adapter-react": "^0.15.39", + "@solana/wallet-adapter-solflare": "^0.6.28", + "@solana/web3.js": "^1.98.2", + "flash-v2": "workspace:*", + "next": "^15.5.19", + "react": "^19.2.0", + "react-dom": "^19.2.0", + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.3.0", + "@types/bn.js": "^5.1.5", + "@types/node": "^22.10.0", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "tailwindcss": "^4.3.0", + "typescript": "^5.9.0", + }, + }, "examples/tap-trade": { "name": "tap-trade", "version": "0.2.0", @@ -845,6 +872,8 @@ "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + "predict": ["predict@workspace:examples/predict"], + "pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], "promise": ["promise@8.3.0", "", { "dependencies": { "asap": "~2.0.6" } }, "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg=="], diff --git a/examples/predict/.env.example b/examples/predict/.env.example new file mode 100644 index 0000000..d32a3aa --- /dev/null +++ b/examples/predict/.env.example @@ -0,0 +1,17 @@ +# predict — copy to .env.local and fill in ONE thing: your RPC key. +# cp .env.example .env.local +# +# Updown runs on Solana MAINNET (real funds — every round is a real position). + +NEXT_PUBLIC_FLASH_NETWORK=mainnet + +# ── RPC ─────────────────────────────────────────────────────────────────────── +# Base-chain RPC (setup / deposit / withdraw). Use your own CORS-friendly RPC — +# a FREE key from helius.dev / triton.one / quicknode.com. This URL ships in the +# browser bundle, so use a DOMAIN-RESTRICTED key (or proxy it server-side). +NEXT_PUBLIC_BASE_RPC=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY_HERE +# Ephemeral Rollup RPC (trading) — serves CORS, called directly from the browser. +NEXT_PUBLIC_ER_RPC=https://flash.magicblock.xyz + +# Override the hosted Flash V2 API (rarely needed). +# NEXT_PUBLIC_FLASH_API_BASE=https://flashapi.trade/v2 diff --git a/examples/predict/DESIGN.md b/examples/predict/DESIGN.md new file mode 100644 index 0000000..1d6f928 --- /dev/null +++ b/examples/predict/DESIGN.md @@ -0,0 +1,48 @@ +# Updown — design direction + +**Vibe: Void Instrument, v2 — frosted.** A dark room with one light source that knows how you're doing; the instruments are sheets of glass catching that light. Not a casino, not a terminal — a kitchen timer with money on it. + +## Glass recipe (v2) + +One static ambient orb (mood-tinted teal/coral via `body[data-mood]`) gives the frost something to refract — never two (they fight). Surfaces: `.glass` = white 5% fill, `blur(12px) saturate(180%) brightness(1.08)`, 1px white/10 border, inset top rim-light `0 1px 0 white/12`, drop `0 8px 32px black/36`, radius 20px. `.glass-strong` (inputs, picker, locked-numbers) = white 7% + blur 16. **Live digits always sit on a `.film`** (~32% dark plate) so numbers never fight the frost. Blur only on static surfaces — history rows stay solid. Solid teal/coral commit buttons keep an accent glow (`.cta-glow-*`); everything else is quiet glass. No-`backdrop-filter` fallback: solid `#0d0f1a/92`. + +## Plain-words rule (v2.1) + +Zero trading vocabulary anywhere a user reads, and **no payoff chart at all** — a chart is still something to decode. "What can happen" is three one-line rows on film plates (▲ Moves your way 1% → +$0.55 · ▼ Moves against you 1% → −$0.55 · ✕ Falls 18% — round ends → −$11 max). Reading beats decoding; nothing can overlap. Timeframes say "pays ×5/×3.3/×2"; the locked card says "You get in at / Round ends early at / Backing your call". The first-run disclosure remains the one place mechanics are named honestly. + +## Tokens + +| Token | Value | Rule | +|---|---|---| +| `bg` | `#07080F` | indigo-tinted void — never pure black, never the sibling's `#0b0d0c` green-black | +| `panel / panel2 / sheet` | `#0D0F1A / #131628 / #0B0D17` | flat layers, 1px `edge #1E2338` hairlines | +| `ink / dim / faint` | `#EFF1FF / #9AA1C4 / #5A6184` | type ladder | +| `up` | `#3EE6C1` | mint-teal — UP, wins. Always paired with ▲ or words (never color-only) | +| `down` | `#FF5C87` | coral-rose — DOWN, losses. Always paired with ▼ or words | +| `warn` | `#FFC857` | amber — disclosure & warnings ONLY. Never decorative | + +Anti-patterns enforced: no AI purple/pink gradients, no glassmorphism-everywhere, no traffic-light green/red, no Inter. + +## Type + +- **Unbounded** (500/700/900) — display only: wordmark, UP/DOWN buttons, result verdicts ("CALLED IT"). +- **Sora** (400–700) — all UI copy. +- **JetBrains Mono** — every number, `tnum` everywhere a digit lives. Numbers never reflow. + +## Motion (meaning only) + +Curve: `cubic-bezier(0.32, 0.72, 0, 1)` — mass, no bounce. Press = scale 0.98. +Only four things move: the countdown bar (1s linear depletion; urgency pulse + coral in the final fifth), the price tick (brightness flash, direction-tinted), the settle card (`settle-pop`), and sheets (transform-only slide). `prefers-reduced-motion` kills all of it. + +## Signature moves + +1. **The ambient mood radial** — a fixed background glow tinted by state (`body[data-mood]`): teal when you're calling UP / winning, coral on DOWN / a loss. The room reacts; the UI stays still. +2. **Double-bezel cards** — every major card is machined: outer shell (`.bezel`, white/3% + hairline + 1.6rem radius) holding an inner core with concentric radius and inset highlight. +3. **The payoff line IS the screen** — an SVG of your actual dollars vs the move, with break-even (teal dot) and knockout (coral wall) marked. It kills the odds mental model on sight. +4. **Locked-numbers card** — commit is two-step; the confirm card restates *your* numbers ("These don't drift") in mono before anything signs. +5. **The welcome-back settle card** — a returning user's expired round settles in front of them: stake shown intact while "settling…", then the verdict in Unbounded with a receipt link. Acknowledgment gates the next round. +6. **Verb, not noun** — the word "predict" appears as something you *do*; the product never claims to *be* a prediction market (the disclosure negates it explicitly, in amber). + +## Layout + +Mobile-first 390px single column (ticket → live rounds → history). Desktop ≥1024px: asymmetric 12-col bento — ticket col-span-5, rounds column col-span-7 offset down 4rem so the grid breathes diagonally. diff --git a/examples/predict/README.md b/examples/predict/README.md new file mode 100644 index 0000000..d7c9a17 --- /dev/null +++ b/examples/predict/README.md @@ -0,0 +1,66 @@ +# predict — Updown: pick up or down. + +**Your stake is the most you can lose.** Timed UP/DOWN price rounds (5m / 15m / 1h) where every round is a *real* Flash Trade V2 perp position on Solana mainnet — opened with your session key in ~30–50 ms, settled by a full close when the clock runs out. + +## What you'll build + +A single-screen price-call game: pick a market (live from `/v2/tokens`), pick UP or DOWN, pick a timeframe, stake USDC. The ticket shows your numbers *before* you sign — payoff per 1% move, break-even, knockout level, max loss (= your stake) — locks them at commit, and settles the round from your browser at expiry. Wins land in your basket; history keeps the receipts. + +## What's tricky here + +- **It is not a prediction market** — no fixed odds, no shared pot. It's a capped-loss leveraged position wearing a round-timer. The app says this out loud (first-run disclosure) instead of letting users keep the Polymarket mental model. +- **The browser is the settlement engine.** Settlement triggers on *observed* state (owner stream + a shared 1 s clock), never a bare `setTimeout`. Three guards prevent double-closes: a `settling` status, an in-flight set, and a retry backoff. +- **Chain is the source of truth; localStorage is a cache.** Rounds reconcile against live positions by `(market, side)` — positions carry no timestamp. Positions opened elsewhere surface for *adoption*; rounds whose position vanished get marked *settled elsewhere*, never silently dropped. +- **Close the tab mid-round and nothing is lost** — the position stays on-chain. Come back and the welcome-back card settles it in front of you; acknowledgment gates the next round. +- **Your numbers can't drift.** PancakeSwap Prediction's biggest trust failure is a payout multiplier that changes after you enter. Here the entry, fees, and knockout shown on the confirm card come from the same signed response the round is built from. +- **Displayed PnL is Flash-parity client math** (`computePositionView`) — the indexer's `pnlWithFeeUsdUi` is never rendered (GOTCHAS §20). + +## How it's meant to work + +1. **Connect** (Phantom / Solflare) — mainnet warning up front. +2. **Enable one-tap rounds** — one wallet approval: session key + basket + ledger + delegate. *No funds move*; the disclosed 0.01 SOL rent top-up goes to your own session key and comes back on revoke. +3. **Deposit** — explicit, typed amount, its own approval (GOTCHAS §17 consent rule). +4. **Call it** — UP or DOWN; review the locked numbers; confirm. The position opens on the Ephemeral Rollup via your session key — no popup. +5. **Watch** — countdown bar, live mark-price PnL, settle-early any time. +6. **Settle** — at expiry the app full-closes the position (`inputUsdUi: "0"` — the 97% partial-close trap never applies). Result card shows won/lost with the on-chain receipt. +7. **Withdraw** — two approvals by design: `request-withdrawal` → `execute-withdrawal`, with unsigned-simulation polling in between (no popup burning). + +Timeframe → payout profiles: **5m → ×5, 15m → ×3.3, 1h → ×2**, clamped to the market's live caps. Break-even move is multiplier-independent (fees scale with size); early-end distance ≈ 92%/multiplier (−18% / −28% / −46%) — deliberately far away on every round length. + +## Endpoints used + +| Endpoint | Why | +|---|---| +| `GET /v2/tokens` | live market list (never hardcoded) | +| `GET /v2/prices/{symbol}` | ticket price + live PnL mark | +| `POST open-position` (no `owner`) | the free quote at the review gate | +| `POST open-position` | the round itself (session-signed → ER RPC) | +| `POST close-position` (`"0"`) | settlement — explicit FULL close | +| `GET /v2/owner/{owner}/ws` | live positions (one shared `subscribeOwner`) | +| `init-basket` / `init-deposit-ledger` / `delegate-basket` / `deposit-direct` | Enable + deposit (base chain) | +| `request-withdrawal` / `execute-withdrawal` | two-phase exit (base chain) | + +## Run it + +```bash +cp examples/predict/.env.example examples/predict/.env.local # add your RPC key +bun install +bun run predict # from the repo root (or: bun run --cwd examples/predict dev) +``` + +Open http://localhost:3000. You can browse markets, prices, and quotes with **no wallet at all** — connect + enable only when you want a real round. `bun run --cwd examples/predict typecheck` and `bun run --cwd examples/predict build` must stay green. + +## Money flows (the consent rule) + +Enable never moves funds. Deposits and withdrawals are explicit: user-typed amount, labeled source → destination (wallet vs basket), dedicated approval. Balances are computed with the deposit-ledger formula from the ER (`deposits − debits + pendingCredits`) and always labeled by where they live. Session revoke returns the rent top-up. + +## Production checklist (for operators) + +This is an open-source example. Before running it as a *service* for other people: + +- **Jurisdictions are your problem.** Comparable products geo-fence: PancakeSwap Prediction hard-blocks restricted regions (HTTP 451), dYdX/Hyperliquid block US/UK/Canada IPs and prohibit VPN circumvention in their ToU. Several countries classify prediction-style products as gambling (Singapore, Indonesia, Spain, others). Get advice, geofence accordingly, and put a Restricted-Persons clause in your terms. +- **Adopt the loss-disclosure banner.** ESMA/FCA require "X% of retail accounts lose money" for CFD-like retail products; this app ships a voluntary version in the footer. Keep it. +- **Protect your RPC key.** `NEXT_PUBLIC_BASE_RPC` ships in the browser bundle — use a domain-restricted key, or better, a server-side proxy (e.g. Helius's one-click worker). Add a fallback provider; map 429s to calm copy (already done in `lib/copy.ts`). +- **Add error monitoring** (Sentry + source maps) and uptime checks on `https://flashapi.trade/v2/health`. +- **Keeper settlement.** Client-side settlement is honest but depends on the user returning. A production service should run a keeper that closes expired rounds server-side — that requires a delegated signing design (users' session keys must never leave their browser; consider per-round TP/SL brackets via `place-tp-sl` as a chain-side backstop instead). +- **Known future work:** touch-mode rounds (TP/SL bracket = near-binary payoff, `validateTriggerPrice` + the $11 floor already enforced), shared leaderboards (needs an indexer), Mobile Wallet Adapter flows for Android/iOS. diff --git a/examples/predict/app/globals.css b/examples/predict/app/globals.css new file mode 100644 index 0000000..bfc5177 --- /dev/null +++ b/examples/predict/app/globals.css @@ -0,0 +1,291 @@ +/* ───────────────────────────────────────────────────────────────────────────── + app/globals.css — the design language, v2: minimalist glassmorphism over an + indigo void. ONE ambient mood orb gives the frost something to refract + (council: two orbs fight). Frost recipe (researched): white 5–7% fill, + blur 12px + saturate 180% + brightness 1.08, white/10 border, inset top + rim-light; live digits always sit on a darker ~30% film for legibility. + THE HARD PART: restraint — glass on a handful of static surfaces only, + never scrolling rows; motion only where it means something. TW v4. + ───────────────────────────────────────────────────────────────────────── */ + +@import "tailwindcss"; + +@theme { + --color-bg: #07080f; /* indigo-tinted void — never pure #000 */ + --color-panel: #0d0f1a; /* solid fallback surfaces (history rows) */ + --color-panel2: #131628; + --color-sheet: #0b0d17; + --color-edge: #1e2338; + --color-edge2: #2a3050; + --color-ink: #eff1ff; + --color-dim: #9aa1c4; + --color-faint: #5a6184; + --color-up: #3ee6c1; /* mint-teal — UP, wins */ + --color-up-deep: #052019; + --color-down: #ff5c87; /* coral-rose — DOWN, losses */ + --color-down-deep: #2a0612; + --color-warn: #ffc857; /* amber — disclosure & warnings ONLY */ + + --font-sans: var(--font-sora), ui-sans-serif, system-ui, sans-serif; + --font-display: var(--font-unbounded), var(--font-sora), ui-sans-serif, sans-serif; + --font-mono: var(--font-jbmono), ui-monospace, "SF Mono", Menlo, monospace; + + --ease-mass: cubic-bezier(0.32, 0.72, 0, 1); +} + +html { + background: var(--color-bg); + color-scheme: dark; +} + +body { + font-feature-settings: "tnum" 1; + overscroll-behavior: none; +} + +::selection { + background: rgb(62 230 193 / 0.25); +} + +:where(button, a, [role="button"]):focus-visible { + outline: 1.5px solid var(--color-up); + outline-offset: 2px; +} +:where(input, textarea):focus-visible { + outline: none; +} + +/* ── ONE ambient orb — the room's light. Static (no animation: digits stay + legible), mood-tinted via body[data-mood]. The glass refracts this. */ +.ambient { + position: fixed; + inset: 0; + pointer-events: none; + z-index: 0; + background: + radial-gradient(55% 42% at 28% 8%, var(--mood, rgb(86 96 160 / 0.34)) 0%, transparent 70%), + radial-gradient(120% 100% at 50% 120%, rgb(10 12 24 / 0.9) 0%, transparent 60%); + transition: opacity 1.2s var(--ease-mass); +} +body[data-mood="up"] .ambient { + --mood: rgb(62 230 193 / 0.2); +} +body[data-mood="down"] .ambient { + --mood: rgb(255 92 135 / 0.17); +} + +/* ── The glass system ────────────────────────────────────────────────────── + .glass — standard frosted card (static surfaces only) + .glass-strong — elevated frost for inputs / the picker / focal moments + .film — the darker plate that sits BEHIND live digits (legibility) + Fallback: agents without backdrop-filter get a solid panel. */ +.glass, +.glass-strong { + border: 1px solid rgb(255 255 255 / 0.1); + border-radius: 1.25rem; + background: rgb(255 255 255 / 0.05); + box-shadow: + inset 0 1px 0 rgb(255 255 255 / 0.12), + 0 8px 32px rgb(0 0 0 / 0.36); +} +.glass-strong { + background: rgb(255 255 255 / 0.07); + border-color: rgb(255 255 255 / 0.14); +} +@supports (backdrop-filter: blur(1px)) { + .glass { + backdrop-filter: blur(12px) saturate(180%) brightness(1.08); + } + .glass-strong { + backdrop-filter: blur(16px) saturate(180%) brightness(1.1); + } +} +@supports not (backdrop-filter: blur(1px)) { + .glass, + .glass-strong { + background: rgb(13 15 26 / 0.92); + } +} +.film { + background: rgb(7 8 15 / 0.32); + border-radius: 0.875rem; +} + +/* ── Press physics: every tappable scales down 2% on press. */ +.press { + transition: transform 180ms var(--ease-mass), filter 180ms var(--ease-mass), opacity 180ms var(--ease-mass), + background-color 180ms var(--ease-mass), border-color 180ms var(--ease-mass); +} +.press:active { + transform: scale(0.98); +} + +/* Accent glow behind the solid primary commit button (style guidance). */ +.cta-glow-up { + box-shadow: 0 0 28px rgb(62 230 193 / 0.28); +} +.cta-glow-down { + box-shadow: 0 0 28px rgb(255 92 135 / 0.24); +} + +/* ── Countdown — depleting bar; urgency in the final fifth. */ +.cd-bar { + transform-origin: left center; + transition: transform 1s linear, background-color 600ms var(--ease-mass); +} +@keyframes urgency-pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.55; + } +} +.cd-urgent { + animation: urgency-pulse 1s ease-in-out infinite; +} + +/* ── Settle reveal. */ +@keyframes settle-pop { + 0% { + opacity: 0; + transform: translateY(14px) scale(0.97); + } + 100% { + opacity: 1; + transform: translateY(0) scale(1); + } +} +.settle-pop { + animation: settle-pop 0.6s var(--ease-mass); +} + +/* ── Sheets mount only while open (closed markup cannot exist, so it cannot + ever render unstyled). Enter 420ms, exit 260ms (~62%), transform-only. */ +@keyframes sheet-in { + from { + transform: translateY(100%); + } + to { + transform: translateY(0); + } +} +@keyframes sheet-out { + from { + transform: translateY(0); + } + to { + transform: translateY(100%); + } +} +@keyframes backdrop-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +@keyframes backdrop-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +.sheet-in { + animation: sheet-in 420ms var(--ease-mass); + will-change: transform; +} +.sheet-out { + animation: sheet-out 260ms var(--ease-mass) forwards; + will-change: transform; +} +.backdrop-in { + animation: backdrop-in 260ms ease; +} +.backdrop-out { + animation: backdrop-out 200ms ease forwards; +} + +@keyframes row-in { + from { + opacity: 0; + transform: translateY(-4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} +.row-in { + animation: row-in 0.35s ease-out; +} + +@keyframes soft-pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.35; + } +} +.soft-pulse { + animation: soft-pulse 1.6s ease-in-out infinite; +} + +/* ── Stake slider — glass track, solid ink thumb (fat-finger friendly). */ +.stake-range { + -webkit-appearance: none; + appearance: none; + height: 6px; + border-radius: 999px; + background: rgb(255 255 255 / 0.08); + border: 1px solid rgb(255 255 255 / 0.06); + outline: none; +} +.stake-range::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 22px; + height: 22px; + border-radius: 999px; + border: none; + background: var(--color-ink); + box-shadow: 0 2px 10px rgb(0 0 0 / 0.5); + cursor: pointer; +} +.stake-range::-moz-range-thumb { + width: 22px; + height: 22px; + border-radius: 999px; + border: none; + background: var(--color-ink); + box-shadow: 0 2px 10px rgb(0 0 0 / 0.5); + cursor: pointer; +} +.stake-range:disabled { + opacity: 0.35; +} + +@media (prefers-reduced-motion: reduce) { + .cd-urgent, + .settle-pop, + .row-in, + .soft-pulse, + .sheet-in, + .backdrop-in { + animation: none; + } + .sheet-out, + .backdrop-out { + animation-duration: 1ms; + } + .press, + .cd-bar { + transition: none; + } +} diff --git a/examples/predict/app/icon.svg b/examples/predict/app/icon.svg new file mode 100644 index 0000000..641b3cc --- /dev/null +++ b/examples/predict/app/icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/examples/predict/app/layout.tsx b/examples/predict/app/layout.tsx new file mode 100644 index 0000000..927850b --- /dev/null +++ b/examples/predict/app/layout.tsx @@ -0,0 +1,38 @@ +// ───────────────────────────────────────────────────────────────────────────── +// app/layout.tsx — root shell: void canvas, three faces loaded once here — +// Unbounded (display moments), Sora (UI copy), JetBrains Mono (every number, +// tabular). THE HARD PART: numbers must never reflow — the mono face + +// "tnum" on (globals.css) keep timers and prices width-stable. +// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +import type { Metadata, Viewport } from "next"; +import { JetBrains_Mono, Sora, Unbounded } from "next/font/google"; +import "./globals.css"; + +const unbounded = Unbounded({ subsets: ["latin"], weight: ["500", "700", "900"], variable: "--font-unbounded" }); +const sora = Sora({ subsets: ["latin"], weight: ["400", "500", "600", "700"], variable: "--font-sora" }); +const jbMono = JetBrains_Mono({ subsets: ["latin"], weight: ["400", "500", "700"], variable: "--font-jbmono" }); + +export const metadata: Metadata = { + title: "Updown — pick up or down", + description: + "Pick up or down. Your stake is the most you can lose. Timed price rounds backed by real Flash Trade V2 positions on Solana mainnet.", +}; + +export const viewport: Viewport = { + themeColor: "#07080f", + width: "device-width", + initialScale: 1, +}; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + +
+ {children} + + + ); +} diff --git a/examples/predict/app/page.tsx b/examples/predict/app/page.tsx new file mode 100644 index 0000000..09e79e2 --- /dev/null +++ b/examples/predict/app/page.tsx @@ -0,0 +1,12 @@ +// ───────────────────────────────────────────────────────────────────────────── +// app/page.tsx — single route; hands straight off to the client app. All +// state is browser state by design: wallet, session key, rounds cache. +// THE HARD PART: nothing — the client boundary is components/app.tsx. +// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +import { App } from "@/components/app"; + +export default function Page() { + return ; +} diff --git a/examples/predict/components/active-round.tsx b/examples/predict/components/active-round.tsx new file mode 100644 index 0000000..e4d7325 --- /dev/null +++ b/examples/predict/components/active-round.tsx @@ -0,0 +1,88 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/active-round.tsx — a running round: countdown, live PnL, exit. +// THE HARD PART: PnL is computed CLIENT-SIDE at mark price (computePosition- +// View) — the indexer's pnlWithFee is never rendered (GOTCHAS §20). The bar +// depletes on the shared clock; urgency arrives in the final fifth; expiry +// flips copy to "settling at close" — taps land on explained, disabled UI. +// GOTCHAS.md → "The indexer's PnL is NOT the product's PnL" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { useClock } from "@/lib/clock"; +import { fmtCountdown, fmtPrice } from "@/lib/copy"; +import { computePositionView, fmtPnlUsd, fmtUsd } from "@/lib/format"; +import { usePrice } from "@/lib/hooks"; +import { timeframe, type TimeframeId } from "@/lib/payoff"; +import { positionFor, type Round } from "@/lib/rounds"; +import { useStream } from "@/lib/stream"; + +interface Props { + round: Round; + onSettleNow: (round: Round) => void; +} + +export function ActiveRound({ round, onSettleNow }: Props) { + const now = useClock(); + const { price } = usePrice(round.market, 1000); + const { snapshot } = useStream(); + + const metrics = snapshot ? positionFor(snapshot, round.market, round.side) : undefined; + const view = metrics ? computePositionView(metrics, price?.priceUi ?? null) : null; + + const settling = round.status === "settling"; + const timed = round.expiresAt !== null && round.timeframe !== null; + const totalMs = timed ? timeframe(round.timeframe as TimeframeId).ms : 0; + const remaining = timed ? Math.max(0, (round.expiresAt as number) - now) : 0; + const frac = timed && totalMs > 0 ? remaining / totalMs : 0; + const urgent = timed && frac > 0 && frac < 0.2; + const expired = timed && remaining <= 0; + + const pnl = view?.pnlUsd ?? null; + const pnlClass = pnl === null ? "text-dim" : pnl >= 0 ? "text-up" : "text-down"; + + return ( +
+
+
+ + {round.side === "LONG" ? "▲" : "▼"} {round.market} + + + {timed ? timeframe(round.timeframe as TimeframeId).label : "manual"} · {fmtUsd(round.stakeUsd)} stake + + + {pnl === null ? "—" : fmtPnlUsd(pnl)} + +
+ + {timed && ( +
+
+ + {settling ? "settling…" : expired ? "settling at close" : fmtCountdown(remaining)} + + + in at {fmtPrice(round.quote.entryPrice)} · ends early at {fmtPrice(round.quote.liqPrice)} + +
+
+
+
+
+ )} + + +
+
+ ); +} diff --git a/examples/predict/components/app.tsx b/examples/predict/components/app.tsx new file mode 100644 index 0000000..44cef28 --- /dev/null +++ b/examples/predict/components/app.tsx @@ -0,0 +1,555 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/app.tsx — the orchestrator: providers, session, rounds, and the +// settlement watcher. THE HARD PART: settlement is driven by OBSERVED state +// (snapshot + shared clock), guarded three ways against double-close — +// "settling" status, an in-flight ref, and a retry backoff. The settled PnL +// shown is the same client-side mark-price number the user watched live +// (GOTCHAS §20) — never the indexer's, never a raw-unit guess. +// GOTCHAS.md → "Two chains, one flow" · "The 97% full-close threshold" +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { ConnectionProvider, WalletProvider, useAnchorWallet, useWallet } from "@solana/wallet-adapter-react"; +import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom"; +import { SolflareWalletAdapter } from "@solana/wallet-adapter-solflare"; +import { assertNoErr } from "flash-v2"; + +import { ClockProvider, useClock } from "@/lib/clock"; +import { APP_NAME, TAGLINE, calmError } from "@/lib/copy"; +import { enableOneClickTrading, type EnableState, type EnableWalletCtx } from "@/lib/enable"; +import { flash, baseConnection } from "@/lib/flash"; +import { depositUsdc, executeWithdrawalStep, withdrawUsdc, type FundsStep } from "@/lib/funds"; +import { computePositionView } from "@/lib/format"; +import { useBalances, useBasketBalance, useLatencyLog, useMarketLimits, useMarkets, usePrice, useUsdcMint } from "@/lib/hooks"; +import { lockedQuoteFrom, timeframe, type LockedQuote, type TimeframeId } from "@/lib/payoff"; +import { + adoptOrphan, loadRounds, newRoundId, pendingAcks, positionFor, reconcile, roundStats, saveRounds, withAck, withResult, withStatus, + type Round, type Side, +} from "@/lib/rounds"; +import { loadSession, revokeSession, type LoadedSession } from "@/lib/session"; +import { makeSessionSigner } from "@/lib/signer"; +import { StreamProvider, useStream } from "@/lib/stream"; + +import { ActiveRound } from "./active-round"; +import { EnableSheet } from "./enable-sheet"; +import { FundsSheet } from "./funds-sheet"; +import { History } from "./history"; +import { MechanicsDisclosure } from "./mechanics-disclosure"; +import { PriceChart } from "./price-chart"; +import { SettleCard } from "./settle-card"; +import { StatsStrip } from "./stats-strip"; +import { Ticket, type TicketPhase } from "./ticket"; +import { WalletBar } from "./wallet-bar"; + +const DISCLOSURE_ACK_KEY = "predict-disclosure-ack"; +const SETTLE_RETRY_MS = 10_000; + +/** The ambient radial's tint — the one sanctioned document mutation. */ +const setMood = (mood: "up" | "down") => { + document.body.dataset.mood = mood; +}; + +export function App() { + const wallets = useMemo(() => [new PhantomWalletAdapter(), new SolflareWalletAdapter()], []); + return ( + + + + + + + + ); +} + +function Shell() { + const { publicKey } = useWallet(); + const owner = publicKey ? publicKey.toBase58() : null; + return ( + + + + ); +} + +function Inner({ owner }: { owner: string | null }) { + const walletCtx = useWallet(); + const anchorWallet = useAnchorWallet(); + const { snapshot, loaded } = useStream(); + const now = useClock(); + const latency = useLatencyLog(); + + // ── market + ticket state ────────────────────────────────────────────────── + const markets = useMarkets(); + const [market, setMarket] = useState("SOL"); + const [side, setSide] = useState("LONG"); + const [timeframeId, setTimeframeId] = useState("5m"); + const [stake, setStake] = useState("11"); + const { price } = usePrice(market, 1000); + const limits = useMarketLimits(market); + + const profileLev = timeframe(timeframeId).leverage; + const leverage = useMemo(() => { + if (!limits) return profileLev; + const max = limits.maxLeverage > 0 ? limits.maxLeverage : profileLev; + const min = limits.minLeverage > 0 ? limits.minLeverage : 1; + return Math.max(min, Math.min(profileLev, max)); + }, [limits, profileLev]); + + // ── balances ─────────────────────────────────────────────────────────────── + const usdcMint = useUsdcMint(); + const balances = useBalances(owner, usdcMint); + const basket = useBasketBalance(owner, snapshot?.basketPubkey ?? null, usdcMint); + const inBasketUsd = basket.bal?.inBasketUsd ?? null; + + // ── session ──────────────────────────────────────────────────────────────── + const [session, setSession] = useState(null); + useEffect(() => { + setSession(owner ? loadSession(owner) : null); + }, [owner]); + const signer = useMemo( + () => (session && anchorWallet ? makeSessionSigner(anchorWallet, session, flash.network) : null), + [session, anchorWallet], + ); + + // ── rounds (chain is truth; this is the cache) ───────────────────────────── + const [rounds, setRounds] = useState([]); + const prevSaved = useRef(null); + useEffect(() => { + const loadedRounds = owner ? loadRounds(owner) : []; + setRounds(loadedRounds); + prevSaved.current = loadedRounds; + }, [owner]); + useEffect(() => { + if (!owner) return; + if (prevSaved.current !== rounds) { + saveRounds(owner, rounds); + prevSaved.current = rounds; + } + }, [owner, rounds]); + + // ── toasts (calm copy only) ──────────────────────────────────────────────── + const [toast, setToast] = useState(null); + const toastTimer = useRef | null>(null); + const say = useCallback((msg: string) => { + setToast(msg); + if (toastTimer.current) clearTimeout(toastTimer.current); + toastTimer.current = setTimeout(() => setToast(null), 6000); + }, []); + + // ── settlement engine ────────────────────────────────────────────────────── + const inflight = useRef>(new Set()); + const retryAt = useRef>(new Map()); + + const settleRound = useCallback( + async (round: Round) => { + if (inflight.current.has(round.id)) return; + if (!signer) { + say("Your session key needs a refresh — re-enable one-tap rounds to settle."); + return; + } + const due = retryAt.current.get(round.id) ?? 0; + if (Date.now() < due) return; + inflight.current.add(round.id); + setRounds((rs) => withStatus(rs, round.id, "settling")); + try { + // The PnL the user watched is the PnL they settle with (GOTCHAS §20) — + // priced at the round's OWN market, fetched at settle time. + const metrics = snapshot ? positionFor(snapshot, round.market, round.side) : undefined; + const mark = await flash.price(round.market).then((p) => p.priceUi).catch(() => null); + const view = metrics ? computePositionView(metrics, mark ?? Number(metrics.entryPriceUi)) : null; + + const res = await flash.closePosition({ + marketSymbol: round.market, + side: round.side, + inputUsdUi: "0", // explicit FULL close — the 97% trap never applies + withdrawTokenSymbol: "USDC", + owner: signer.owner, + slippagePercentage: "1", + ...signer.tradeFields, + }); + assertNoErr("close-position", res); + if (!res.transactionBase64) throw new Error(res.err ?? "no transaction returned"); + const sent = await signer.sendTrade(res.transactionBase64); + latency.add({ action: `settle ${round.market}`, chain: "er", ms: sent.confirmMs, sendMs: sent.sendMs, signature: sent.signature }); + + const pnlUsd = view?.pnlUsd ?? 0; + setRounds((rs) => + withResult(rs, round.id, { pnlUsd, won: pnlUsd > 0, settledAt: Date.now(), signature: sent.signature }), + ); + setMood(pnlUsd > 0 ? "up" : "down"); + void basket.refresh(); + } catch (e) { + console.error("[settle]", e); + retryAt.current.set(round.id, Date.now() + SETTLE_RETRY_MS); + const gone = /Position is empty/i.test(e instanceof Error ? e.message : String(e)); + setRounds((rs) => + gone + ? rs.map((r) => (r.id === round.id ? { ...r, status: "closed-elsewhere" as const } : r)) + : withStatus(rs, round.id, "active"), + ); + say(calmError(e)); + } finally { + inflight.current.delete(round.id); + } + }, + [signer, snapshot, latency, basket, say], + ); + + // The watcher: observed state in, settle dispatches out. + const recon = useMemo(() => reconcile(rounds, snapshot, now), [rounds, snapshot, now]); + useEffect(() => { + if (!loaded) return; + if (recon.closedElsewhere.length) { + setRounds((rs) => + rs.map((r) => (recon.closedElsewhere.some((c) => c.id === r.id) ? { ...r, status: "closed-elsewhere" as const } : r)), + ); + } + for (const r of recon.toSettle) void settleRound(r); + }, [recon, loaded, settleRound]); + + // ── commit flow: review (one quote) → confirm (sign those numbers) ───────── + const [phase, setPhase] = useState("idle"); + const [review, setReview] = useState(null); + const [ticketError, setTicketError] = useState(null); + + const doReview = useCallback(async () => { + setTicketError(null); + setPhase("quoting"); + try { + const stakeNum = Number(stake); + const res = await flash.openPosition({ + inputTokenSymbol: "USDC", + outputTokenSymbol: market, + inputAmountUi: stake, + leverage, + tradeType: side, + slippagePercentage: "1", + }); + assertNoErr("open-position quote", res); + setReview(lockedQuoteFrom(res, stakeNum, leverage, side)); + setPhase("review"); + } catch (e) { + console.error("[quote]", e); + setTicketError(calmError(e)); + setPhase("idle"); + } + }, [stake, market, leverage, side]); + + const doConfirm = useCallback(async () => { + if (!signer) return; + setPhase("signing"); + setTicketError(null); + try { + const stakeNum = Number(stake); + const res = await flash.openPosition({ + inputTokenSymbol: "USDC", + outputTokenSymbol: market, + inputAmountUi: stake, + leverage, + tradeType: side, + slippagePercentage: "1", + owner: signer.owner, + ...signer.tradeFields, + }); + assertNoErr("open-position", res); + if (!res.transactionBase64) throw new Error(res.err ?? "no transaction returned"); + const sent = await signer.sendTrade(res.transactionBase64); + latency.add({ action: `${side} ${leverage}× ${market}`, chain: "er", ms: sent.confirmMs, sendMs: sent.sendMs, signature: sent.signature }); + + const tf = timeframe(timeframeId); + const round: Round = { + id: newRoundId(), + market, + side, + stakeUsd: stakeNum, + leverage, + timeframe: timeframeId, + placedAt: Date.now(), + expiresAt: Date.now() + tf.ms, + // Authoritative numbers: the SIGNED response, not the preview. + quote: lockedQuoteFrom(res, stakeNum, leverage, side), + status: "active", + }; + setRounds((rs) => [...rs, round]); + setMood(side === "LONG" ? "up" : "down"); + setPhase("idle"); + setReview(null); + void basket.refresh(); + } catch (e) { + console.error("[open]", e); + setTicketError(calmError(e)); + setPhase("review"); + } + }, [signer, stake, market, leverage, side, timeframeId, latency, basket]); + + // ── enable / revoke ──────────────────────────────────────────────────────── + const [enableOpen, setEnableOpen] = useState(false); + const [enableState, setEnableState] = useState(null); + const [busy, setBusy] = useState(false); + + const doEnable = useCallback(async () => { + if (!walletCtx.publicKey || !walletCtx.signTransaction || !anchorWallet) return; + setBusy(true); + try { + const ctx: EnableWalletCtx = { + publicKey: walletCtx.publicKey, + signTransaction: walletCtx.signTransaction, + signAllTransactions: walletCtx.signAllTransactions, + }; + const result = await enableOneClickTrading({ + wallet: ctx, + anchorWallet, + snapshot, + usdcMint, + balances: { sol: balances.sol, usdc: balances.usdc }, + onStep: setEnableState, + onLog: latency.add, + }); + if (result.session) setSession(result.session); + if (result.ok) { + setEnableOpen(false); + if (result.needsUsdc) setFundsOpen(true); + } + } catch (e) { + say(calmError(e)); + } finally { + setBusy(false); + } + }, [walletCtx, anchorWallet, snapshot, usdcMint, balances.sol, balances.usdc, latency, say]); + + const doRevoke = useCallback(async () => { + if (!session) return; + setBusy(true); + try { + await revokeSession(session, baseConnection); + setSession(null); + say("Session revoked — rent returned to your wallet."); + } catch (e) { + say(calmError(e)); + } finally { + setBusy(false); + } + }, [session, say]); + + // ── funds ────────────────────────────────────────────────────────────────── + const [fundsOpen, setFundsOpen] = useState(false); + const [fundsStep, setFundsStep] = useState(null); + const [executePending, setExecutePending] = useState(false); + + const fundsWallet = useMemo(() => { + if (!walletCtx.publicKey || !walletCtx.signTransaction) return null; + return { + publicKey: walletCtx.publicKey, + signTransaction: walletCtx.signTransaction, + signAllTransactions: walletCtx.signAllTransactions, + }; + }, [walletCtx.publicKey, walletCtx.signTransaction, walletCtx.signAllTransactions]); + + const doDeposit = useCallback( + async (amount: string) => { + const w = fundsWallet; + if (!w || !usdcMint) return; + setBusy(true); + try { + const r = await depositUsdc({ wallet: w, usdcMint, amount, onStep: setFundsStep, onLog: latency.add }); + if (!r.ok && r.error) say(r.error); + await Promise.all([balances.refresh(), basket.refresh()]); + } finally { + setBusy(false); + } + }, + [fundsWallet, usdcMint, latency, balances, basket, say], + ); + + const doWithdraw = useCallback( + async (amount: string) => { + const w = fundsWallet; + if (!w || !usdcMint) return; + setBusy(true); + try { + const r = await withdrawUsdc({ wallet: w, usdcMint, amount, onStep: setFundsStep, onLog: latency.add }); + setExecutePending(Boolean(r.executePending)); + if (!r.ok && r.error) say(r.error); + await Promise.all([balances.refresh(), basket.refresh()]); + } finally { + setBusy(false); + } + }, + [fundsWallet, usdcMint, latency, balances, basket, say], + ); + + const doExecuteWithdraw = useCallback(async () => { + const w = fundsWallet; + if (!w || !usdcMint) return; + setBusy(true); + try { + const r = await executeWithdrawalStep({ wallet: w, usdcMint, onStep: setFundsStep, onLog: latency.add }); + setExecutePending(Boolean(r.executePending)); + if (!r.ok && r.error) say(r.error); + await Promise.all([balances.refresh(), basket.refresh()]); + } finally { + setBusy(false); + } + }, [fundsWallet, usdcMint, latency, balances, basket, say]); + + // ── disclosure gate ──────────────────────────────────────────────────────── + const [disclosureAcked, setDisclosureAcked] = useState(true); // assume acked until mount check + const [disclosureOpen, setDisclosureOpen] = useState(false); + useEffect(() => { + const acked = typeof window !== "undefined" && window.localStorage.getItem(DISCLOSURE_ACK_KEY) === "1"; + setDisclosureAcked(acked); + setDisclosureOpen(!acked); + }, []); + const ackDisclosure = useCallback(() => { + window.localStorage.setItem(DISCLOSURE_ACK_KEY, "1"); + setDisclosureAcked(true); + setDisclosureOpen(false); + }, []); + + // ── derived view state ───────────────────────────────────────────────────── + const active = rounds.filter((r) => r.status === "active" || r.status === "settling"); + const acks = pendingAcks(rounds); + const stats = roundStats(rounds); + const chartRound = active.find((r) => r.market === market); + const chartEntry = chartRound ? { price: chartRound.quote.entryPrice, side: chartRound.side } : null; + const sessionActive = Boolean(session && signer); + + const blockedReason = !owner + ? "Connect a wallet to play." + : !sessionActive + ? "Enable one-tap rounds to play — one approval, no funds move." + : inBasketUsd !== null && inBasketUsd < Number(stake || 0) + ? "Deposit USDC to your basket first." + : null; + + return ( +
+ setFundsOpen(true)} + onEnable={() => setEnableOpen(true)} + onRevoke={doRevoke} + onShowDisclosure={() => setDisclosureOpen(true)} + /> + +
+ {/* ticket column */} +
+

+ Call the next move. +

+

{TAGLINE}

+ { + setSide(s); + setMood(s === "LONG" ? "up" : "down"); + }} + timeframeId={timeframeId} + onTimeframe={setTimeframeId} + stake={stake} + onStake={setStake} + maxStake={inBasketUsd} + leverage={leverage} + price={price} + blockedReason={blockedReason} + phase={phase} + review={review} + error={ticketError} + onReview={doReview} + onConfirm={doConfirm} + onCancelReview={() => { + setPhase("idle"); + setReview(null); + }} + /> +
+ + {/* live column */} +
+ + + + {recon.orphans.length > 0 && ( +
+

+ {recon.orphans.length === 1 ? "A position" : `${recon.orphans.length} positions`} on this wallet{" "} + {recon.orphans.length === 1 ? "isn't" : "aren't"} tracked here (opened elsewhere or cache cleared). +

+
+ {recon.orphans.map((o) => ( + + ))} +
+
+ )} + + {active.length > 0 ? ( +
+

+ Live rounds +

+ {active.map((r) => ( + + ))} +
+ ) : ( +
+ No live rounds. Your next call starts on the left. +
+ )} + + +
+
+ +
+

+ {APP_NAME} is a starter built on Flash Trade V2 — every round is a real position on Solana mainnet, with real money. + Most people who play short-term price games lose money. Never stake more than you can afford to lose.{" "} + +

+
+ + {/* overlays */} + {acks.length > 0 && setRounds((rs) => withAck(rs, r.id))} />} + + void doEnable()} onClose={() => setEnableOpen(false)} /> + void doDeposit(a)} + onWithdraw={(a) => void doWithdraw(a)} + onExecuteWithdraw={() => void doExecuteWithdraw()} + onClose={() => setFundsOpen(false)} + /> + + {toast && ( +
+
{toast}
+
+ )} +
+ ); +} diff --git a/examples/predict/components/enable-sheet.tsx b/examples/predict/components/enable-sheet.tsx new file mode 100644 index 0000000..3e43515 --- /dev/null +++ b/examples/predict/components/enable-sheet.tsx @@ -0,0 +1,85 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/enable-sheet.tsx — Enable One-Tap: consent screen + live steps. +// THE HARD PART: the consent rule. Enable is account setup ONLY — the single +// disclosed transfer is the 0.01 SOL rent top-up to the user's OWN session +// key, stated here at the moment of consent, returned on revoke. +// GOTCHAS.md → "Funds move on consent only" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import type { EnableState } from "@/lib/enable"; +import { ENABLE_EXPLAINER } from "@/lib/copy"; +import { useSheetMount } from "@/lib/use-sheet-mount"; + +interface Props { + open: boolean; + busy: boolean; + state: EnableState | null; + onEnable: () => void; + onClose: () => void; +} + +const dot: Record = { + idle: "bg-edge2", + active: "bg-warn soft-pulse", + done: "bg-up", + skipped: "bg-faint", + error: "bg-down", +}; + +export function EnableSheet({ open, busy, state, onEnable, onClose }: Props) { + const { mounted, closing } = useSheetMount(open); + if (!mounted) return null; + return ( +
+
+
+
+
+

{ENABLE_EXPLAINER.title}

+

{ENABLE_EXPLAINER.body}

+ +
    + {(state?.steps ?? ENABLE_EXPLAINER.steps.map((label, i) => ({ id: String(i), label, status: "idle" as const, note: undefined }))).map( + (s) => ( +
  • + + {s.label} + {s.note ? {s.note} : null} +
  • + ), + )} +
+ + {state?.fundingHint ?

{state.fundingHint}

: null} + {state?.error ?

{state.error}

: null} + +
+ + +
+
+
+
+
+ ); +} diff --git a/examples/predict/components/funds-sheet.tsx b/examples/predict/components/funds-sheet.tsx new file mode 100644 index 0000000..b2ebfcf --- /dev/null +++ b/examples/predict/components/funds-sheet.tsx @@ -0,0 +1,131 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/funds-sheet.tsx — explicit deposit & withdraw. User types the +// amount, sees where money lives, approves that ONE transfer. +// THE HARD PART: withdraw is two phases — request queues settlement off the +// rollup (~30–90s), execute completes it. The wait is polled with UNSIGNED +// simulations (lib/funds.ts), so retries never burn wallet popups. +// GOTCHAS.md → "Withdrawals settle in two phases" · §17 (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { useState } from "react"; +import type { FundsStep } from "@/lib/funds"; +import { fmtUsd } from "@/lib/format"; +import { useSheetMount } from "@/lib/use-sheet-mount"; + +interface Props { + open: boolean; + busy: boolean; + walletUsdc: number | null; + inBasketUsd: number | null; + step: FundsStep | null; + /** A requested withdrawal is waiting for its execute leg. */ + executePending: boolean; + onDeposit: (amount: string) => void; + onWithdraw: (amount: string) => void; + onExecuteWithdraw: () => void; + onClose: () => void; +} + +export function FundsSheet({ open, busy, walletUsdc, inBasketUsd, step, executePending, onDeposit, onWithdraw, onExecuteWithdraw, onClose }: Props) { + const [mode, setMode] = useState<"deposit" | "withdraw">("deposit"); + const [amount, setAmount] = useState(""); + + const { mounted, closing } = useSheetMount(open); + + const max = mode === "deposit" ? walletUsdc : inBasketUsd; + const amt = Number(amount); + const valid = Number.isFinite(amt) && amt > 0 && (max === null || amt <= max + 1e-9); + + if (!mounted) return null; + return ( +
+
+
+
+
+
+ {(["deposit", "withdraw"] as const).map((m) => ( + + ))} +
+ +
+ wallet {walletUsdc === null ? "…" : fmtUsd(walletUsdc)} + {mode === "deposit" ? "→" : "←"} + basket {inBasketUsd === null ? "…" : fmtUsd(inBasketUsd)} +
+ +
+ $ + setAmount(e.target.value.replace(/[^0-9.]/g, ""))} + disabled={busy} + aria-label={`Amount to ${mode} in USDC`} + className="w-full bg-transparent font-mono text-xl text-ink placeholder:text-faint focus:outline-none" + /> + +
+ +

+ {mode === "deposit" + ? "USDC moves from your wallet into your basket. One wallet approval." + : "Two approvals by design: request queues settlement off the rollup (~30–90s), execute completes it to your wallet."} +

+ + {executePending && ( + + )} + + {step && ( +
+ + {step.label} + {step.note ? {step.note} : null} +
+ )} + +
+ + +
+
+
+
+
+ ); +} diff --git a/examples/predict/components/history.tsx b/examples/predict/components/history.tsx new file mode 100644 index 0000000..08d21a2 --- /dev/null +++ b/examples/predict/components/history.tsx @@ -0,0 +1,46 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/history.tsx — settled rounds, newest first, each with receipt. +// THE HARD PART: nothing clever — terse rows, tabular numbers, capped at 50. +// "closed-elsewhere" rows stay honest about what we don't know. +// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { explorerTxUrl } from "@/lib/copy"; +import { fmtPnlUsd, fmtUsd } from "@/lib/format"; +import type { Round } from "@/lib/rounds"; + +export function History({ rounds }: { rounds: Round[] }) { + const done = rounds + .filter((r) => r.status === "settled" || r.status === "closed-elsewhere") + .sort((a, b) => (b.result?.settledAt ?? b.placedAt) - (a.result?.settledAt ?? a.placedAt)) + .slice(0, 50); + + if (done.length === 0) return null; + + return ( +
+

History

+
    + {done.map((r) => { + const pnl = r.result?.pnlUsd ?? null; + return ( +
  • + {r.side === "LONG" ? "▲" : "▼"} + {r.market} + {r.timeframe ?? "manual"} · {fmtUsd(r.stakeUsd)} + = 0 ? "text-up" : "text-down"}`}> + {r.status === "closed-elsewhere" ? "settled elsewhere" : pnl !== null ? fmtPnlUsd(pnl) : "—"} + + {r.result?.signature && ( + + ↗ + + )} +
  • + ); + })} +
+
+ ); +} diff --git a/examples/predict/components/market-picker.tsx b/examples/predict/components/market-picker.tsx new file mode 100644 index 0000000..88bc52a --- /dev/null +++ b/examples/predict/components/market-picker.tsx @@ -0,0 +1,207 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/market-picker.tsx — pick what you're calling. A glass button +// (icon · symbol · live price) opens a searchable sheet: 45+ markets need +// search, not a scroll row. THE HARD PART: category pills are derived from +// Pyth ticker prefixes, which is best-effort taxonomy — if less than 70% of +// tokens classify cleanly, the pills hide themselves (council: never ship a +// silently-wrong filter). Prices in the grid come from ONE bulk prices() +// call on open. GOTCHAS.md → "Deposits take a MINT, trading takes SYMBOLS" +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { useEffect, useMemo, useRef, useState } from "react"; +import type { PriceInfo, TokenInfo } from "flash-v2"; +import { fmtPrice } from "@/lib/copy"; +import { flash } from "@/lib/flash"; +import { useSheetMount } from "@/lib/use-sheet-mount"; +import { TokenIcon } from "./token-icon"; + +// Module-level caches: the token list is static per session, and the grid's +// price hints don't need to be fresher than a few seconds across re-opens. +let tokensOnce: Promise | null = null; +const cachedTokens = () => (tokensOnce ??= flash.tokens().catch((e) => ((tokensOnce = null), Promise.reject(e)))); +let pricesAt = 0; +let pricesCache: Record = {}; +const PRICES_TTL_MS = 10_000; +async function cachedPrices(): Promise> { + if (Date.now() - pricesAt < PRICES_TTL_MS) return pricesCache; + pricesCache = await flash.prices(); + pricesAt = Date.now(); + return pricesCache; +} + +interface Props { + markets: string[] | null; + market: string; + price: PriceInfo | null; + disabled?: boolean; + onSelect: (symbol: string) => void; +} + +const CATEGORY_LABELS: Record = { + Crypto: "Crypto", + Equity: "Stocks", + FX: "FX", + Metal: "Metals", + Commodity: "Commodities", + Commodities: "Commodities", + Energy: "Commodities", + Rates: "Rates", +}; + +function categoryOf(pythTicker: string | null | undefined): string | null { + if (!pythTicker) return null; + const prefix = pythTicker.split(".")[0] ?? ""; + return CATEGORY_LABELS[prefix] ?? null; +} + +export function MarketPicker({ markets, market, price, disabled, onSelect }: Props) { + const [open, setOpen] = useState(false); + const { mounted, closing } = useSheetMount(open); + const [query, setQuery] = useState(""); + const [category, setCategory] = useState("All"); + const [categories, setCategories] = useState>(new Map()); // symbol → category + const [pills, setPills] = useState([]); + const [gridPrices, setGridPrices] = useState>({}); + const searchRef = useRef(null); + + // Best-effort taxonomy from token metadata; hide pills unless ≥70% classify. + useEffect(() => { + let alive = true; + cachedTokens() + .then((tokens) => { + if (!alive) return; + const map = new Map(); + let classified = 0; + const tradeable = tokens.filter((t) => !t.isStable); + for (const t of tradeable) { + const cat = categoryOf(t.pythTicker); + if (cat) { + map.set(t.symbol, cat); + classified++; + } + } + setCategories(map); + if (tradeable.length > 0 && classified / tradeable.length >= 0.7) { + setPills(["All", ...Array.from(new Set(map.values())).sort()]); + } else { + setPills([]); + } + }) + .catch(() => setPills([])); + return () => { + alive = false; + }; + }, []); + + // One bulk price snapshot when the sheet opens (grid hints, not live ticks). + useEffect(() => { + if (!open) return; + searchRef.current?.focus(); + cachedPrices() + .then(setGridPrices) + .catch(() => {}); + }, [open]); + + const list = useMemo(() => { + const all = markets ?? []; + const q = query.trim().toUpperCase(); + return all.filter((s) => { + if (q && !s.toUpperCase().includes(q)) return false; + if (category !== "All" && pills.length > 0 && categories.get(s) !== category) return false; + return true; + }); + }, [markets, query, category, categories, pills.length]); + + const pick = (s: string) => { + onSelect(s); + setOpen(false); + setQuery(""); + setCategory("All"); + }; + + return ( + <> + + + {mounted && ( +
+
setOpen(false)} + /> +
+
+ setQuery(e.target.value)} + placeholder="Search markets…" + aria-label="Search markets" + className="film w-full px-4 py-3 font-mono text-sm text-ink placeholder:text-faint focus:outline-none" + /> + + {pills.length > 0 && ( +
+ {pills.map((p) => ( + + ))} +
+ )} + +
+ {(markets === null ? [] : list).map((s) => { + const p = gridPrices[s]; + return ( + + ); + })} + {markets === null && loading markets…} + {markets !== null && list.length === 0 && ( + nothing matches “{query}” + )} +
+
+
+
+ )} + + ); +} diff --git a/examples/predict/components/mechanics-disclosure.tsx b/examples/predict/components/mechanics-disclosure.tsx new file mode 100644 index 0000000..53e13cb --- /dev/null +++ b/examples/predict/components/mechanics-disclosure.tsx @@ -0,0 +1,57 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/mechanics-disclosure.tsx — the gate before the first round. +// THE HARD PART: this copy NEGATES the prediction-market mental model while +// honoring the verb "predict" — shown once, acknowledged explicitly, and +// reachable forever from the footer. Amber is reserved for exactly this. +// GOTCHAS.md → "Funds move on consent only" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { useState } from "react"; +import { DISCLOSURE } from "@/lib/copy"; + +interface Props { + open: boolean; + /** First-run gating mode requires the checkbox; revisits don't. */ + gate: boolean; + onAck: () => void; +} + +export function MechanicsDisclosure({ open, gate, onAck }: Props) { + const [checked, setChecked] = useState(false); + if (!open) return null; + return ( +
+
+
+

Read me first

+

{DISCLOSURE.title}

+
+ {DISCLOSURE.paragraphs.map((p, i) => ( +

{p}

+ ))} +
+ {gate ? ( + + ) : null} + +
+
+
+ ); +} diff --git a/examples/predict/components/price-chart.tsx b/examples/predict/components/price-chart.tsx new file mode 100644 index 0000000..46891ac --- /dev/null +++ b/examples/predict/components/price-chart.tsx @@ -0,0 +1,120 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/price-chart.tsx — the live pulse: where the price has been and +// where it is right now. THE HARD PART: zero extra polling — it feeds off the +// app's existing 1s price tick (usePriceHistory seeds with Pyth minute closes +// and appends live ticks). Fixed height, tabular digits, entry-line overlay +// when you have a round riding this market. Trend tints teal/coral. +// GOTCHAS.md → "Latency: never bill the rollup for the user's geography" +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { useMemo } from "react"; +import type { PriceInfo } from "flash-v2"; +import { fmtPrice } from "@/lib/copy"; +import { usePriceHistory } from "@/lib/use-price-history"; +import type { Side } from "@/lib/rounds"; + +interface Props { + symbol: string; + price: PriceInfo | null; + /** An active round on this market — draws your entry on the chart. */ + entry?: { price: number; side: Side } | null; +} + +const W = 100; +const H = 36; + +export function PriceChart({ symbol, price, entry }: Props) { + const history = usePriceHistory(price, symbol); + + const view = useMemo(() => { + if (history.length < 2) return null; + const lo = Math.min(...history); + const hi = Math.max(...history); + const pad = (hi - lo) * 0.12 || hi * 0.0005 || 1; + const min = lo - pad; + const max = hi + pad; + const xOf = (i: number) => (i / (history.length - 1)) * W; + const yOf = (p: number) => H - ((p - min) / (max - min)) * H; + + const pts = history.map((p, i) => `${xOf(i).toFixed(2)},${yOf(p).toFixed(2)}`); + const first = history[0] as number; + const last = history[history.length - 1] as number; + const changePct = first > 0 ? ((last - first) / first) * 100 : 0; + const lastX = xOf(history.length - 1); + const lastY = yOf(last); + const entryY = entry && entry.price >= min && entry.price <= max ? yOf(entry.price) : null; + + return { + line: pts.join(" "), + area: `0,${H} ${pts.join(" ")} ${W},${H}`, + up: changePct >= 0, + changePct, + lo, + hi, + lastX, + lastY, + entryY, + }; + }, [history, entry]); + + const tone = view?.up ? "var(--color-up)" : "var(--color-down)"; + + return ( +
+
+
+ {symbol} + live +
+ {view && ( + + {view.up ? "▲" : "▼"} {Math.abs(view.changePct).toFixed(2)}% + + )} +
+ + {/* the number — on its own film so it never fights the frost */} +
+ + {price ? fmtPrice(price.priceUi) : "—"} + +
+ +
+ {view ? ( + <> + + + + + + + + + + {view.entryY !== null && ( + + )} + + + {view.entryY !== null && ( + + your entry + + )} + {fmtPrice(view.hi, "compact")} + {fmtPrice(view.lo, "compact")} + + ) : ( +
+ drawing the last hour… +
+ )} +
+
+ ); +} diff --git a/examples/predict/components/settle-card.tsx b/examples/predict/components/settle-card.tsx new file mode 100644 index 0000000..ab8f373 --- /dev/null +++ b/examples/predict/components/settle-card.tsx @@ -0,0 +1,76 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/settle-card.tsx — the trust crucible: a settled round's result. +// THE HARD PART: the stake must never look stolen — a settling round shows +// custody ("being settled now"), the result lands with its receipt link, and +// acknowledgment gates the next round. Win is celebratory; loss is calm and +// exact ("your full stake, nothing more"). +// GOTCHAS.md → "err arrives inside HTTP 200" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { explorerTxUrl } from "@/lib/copy"; +import { fmtPnlUsd, fmtUsd } from "@/lib/format"; +import type { Round } from "@/lib/rounds"; + +interface Props { + round: Round; + onAck: (round: Round) => void; +} + +export function SettleCard({ round, onAck }: Props) { + const r = round.result; + const elsewhere = round.status === "closed-elsewhere"; + const won = r?.won ?? false; + + return ( +
+
+
+

+ {round.market} {round.side === "LONG" ? "▲ up" : "▼ down"} · {fmtUsd(round.stakeUsd)} stake +

+ + {elsewhere ? ( + <> +

SETTLED ELSEWHERE

+

+ This position was closed outside Updown — another device, the Flash app, or a knockout. Your balance reflects the on-chain outcome. +

+ + ) : ( + <> +

+ {won ? "CALLED IT" : "NOT THIS TIME"} +

+

{r ? fmtPnlUsd(r.pnlUsd) : ""}

+

+ {won + ? "Settled to your basket. Withdraw any time." + : `That's your full stake — nothing more. The cap is the feature.`} +

+ + )} + + {r?.signature && ( + + receipt ↗ + + )} + + +
+
+
+ ); +} diff --git a/examples/predict/components/stats-strip.tsx b/examples/predict/components/stats-strip.tsx new file mode 100644 index 0000000..fd4b194 --- /dev/null +++ b/examples/predict/components/stats-strip.tsx @@ -0,0 +1,31 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/stats-strip.tsx — your record: rounds, win rate, net PnL, streak. +// THE HARD PART: numbers from settled rounds only — live PnL never leaks in +// here (it belongs to the active round card). Tabular mono keeps it still. +// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { fmtPnlUsd } from "@/lib/format"; +import type { RoundStats } from "@/lib/rounds"; + +export function StatsStrip({ stats }: { stats: RoundStats }) { + if (stats.played === 0) return null; + const pnlClass = stats.totalPnlUsd > 0 ? "text-up" : stats.totalPnlUsd < 0 ? "text-down" : "text-dim"; + const streakLabel = + stats.streak > 1 ? `${stats.streak}× win streak` : stats.streak < -1 ? `${-stats.streak}× loss streak` : null; + return ( +
+ + {stats.wins}W · {stats.losses}L + + {stats.winRatePct.toFixed(0)}% win rate + {fmtPnlUsd(stats.totalPnlUsd)} all-time + {streakLabel && ( + 0 ? "border-up/30 text-up" : "border-down/30 text-down"}`}> + {streakLabel} + + )} +
+ ); +} diff --git a/examples/predict/components/ticket.tsx b/examples/predict/components/ticket.tsx new file mode 100644 index 0000000..af39678 --- /dev/null +++ b/examples/predict/components/ticket.tsx @@ -0,0 +1,253 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/ticket.tsx — the commit surface, v2: glass, plain words, zero +// trading vocabulary. Market via picker sheet (no scroll row); stake is a +// labeled glass field + balance slider + "use max" (no amount chips); facts +// speak in the user's dollars; ONE real quote at Review locks the numbers +// before anything signs. $11 floor enforced (the chain's, not ours). +// GOTCHAS.md → "The $11 rule" · "owner is optional exactly once" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { useMemo } from "react"; +import type { PriceInfo } from "flash-v2"; +import { RECOMMENDED_MIN_COLLATERAL_USD } from "flash-v2"; +import { fmtPrice } from "@/lib/copy"; +import { fmtUsd } from "@/lib/format"; +import { payoffFacts, TIMEFRAMES, type LockedQuote, type TimeframeId } from "@/lib/payoff"; +import type { Side } from "@/lib/rounds"; +import { MarketPicker } from "./market-picker"; + +const MIN_STAKE = RECOMMENDED_MIN_COLLATERAL_USD; // $11 — the chain's floor + +export type TicketPhase = "idle" | "quoting" | "review" | "signing"; + +interface Props { + markets: string[] | null; + market: string; + onMarket: (m: string) => void; + side: Side; + onSide: (s: Side) => void; + timeframeId: TimeframeId; + onTimeframe: (t: TimeframeId) => void; + stake: string; + onStake: (s: string) => void; + maxStake: number | null; + leverage: number; // clamped to market caps by the app + price: PriceInfo | null; + blockedReason: string | null; + phase: TicketPhase; + review: LockedQuote | null; + error: string | null; + onReview: () => void; + onConfirm: () => void; + onCancelReview: () => void; +} + +export function Ticket(props: Props) { + const { + markets, market, onMarket, side, onSide, timeframeId, onTimeframe, stake, onStake, maxStake, + leverage, price, blockedReason, phase, review, error, onReview, onConfirm, onCancelReview, + } = props; + + const stakeNum = Number(stake) || 0; + const facts = useMemo(() => payoffFacts(stakeNum, leverage), [stakeNum, leverage]); + const stakeOk = stakeNum >= MIN_STAKE && (maxStake === null || stakeNum <= maxStake + 1e-9); + const inReview = phase === "review" || phase === "signing"; + const sliderMax = maxStake !== null ? Math.max(MIN_STAKE, Math.floor(maxStake)) : null; + const stakeShown = stakeNum || MIN_STAKE; + + return ( +
+ {/* market */} + + + {/* direction — the product's two verbs */} +
+ + +
+ + {/* how long */} +

How long

+
+ {TIMEFRAMES.map((tf) => ( + + ))} +
+ + {/* your stake */} +

Your stake

+
+
+ $ + onStake(e.target.value.replace(/[^0-9.]/g, ""))} + onFocus={(e) => e.target.select()} + aria-label="Your stake in dollars" + className="w-full bg-transparent font-mono text-[28px] font-medium leading-none text-ink placeholder:text-faint focus:outline-none disabled:opacity-60" + placeholder={String(MIN_STAKE)} + /> + {maxStake !== null && ( + + )} +
+ {sliderMax !== null && sliderMax > MIN_STAKE && ( + onStake(e.target.value)} + aria-label="Slide to set your stake" + className="stake-range mt-3 w-full cursor-pointer" + /> + )} +

+ {maxStake === null ? `$${MIN_STAKE} minimum` : `Balance ${fmtUsd(maxStake)} · $${MIN_STAKE} minimum`} +

+
+ {stakeNum > 0 && stakeNum < MIN_STAKE && ( +

${MIN_STAKE} minimum — below that the blockchain rejects the round.

+ )} + {maxStake !== null && stakeNum > maxStake && ( +

That's more than your balance ({fmtUsd(maxStake)}). Deposit first.

+ )} + + {/* what can happen — three lines, nothing to decode */} +

What can happen

+
    +
  • + + Moves your way 1% + +${facts.perPctUsd.toFixed(2)} +
  • +
  • + + Moves against you 1% + −${facts.perPctUsd.toFixed(2)} +
  • +
  • + + + {side === "LONG" ? "Falls" : "Jumps"} {facts.knockoutPct.toFixed(0)}% — round ends + + −{fmtUsd(stakeShown)} max +
  • +
+ + {/* commit gate */} + {!inReview ? ( + <> + + {blockedReason &&

{blockedReason}

} + + ) : ( +
+

Your numbers — locked, they don't drift

+
+
You get in at
{fmtPrice(review?.entryPrice)}
+
Round ends early at
{fmtPrice(review?.liqPrice)}
+
Backing your call
{fmtUsd(review?.sizeUsd ?? 0)}
+
Each 1% your way
+{fmtUsd(review?.perPctUsd ?? 0)}
+
Fees, in and out (≈)
{fmtUsd((review?.entryFeeUsd ?? 0) * 2)}
+
Cost per hour held
{(review?.marginFeePctHourly ?? 0).toFixed(4)}%
+
+ {(() => { + const mark = price?.priceUi ?? null; + const entry = review?.entryPrice ?? null; + const gapPct = mark && entry ? ((entry - mark) / mark) * 100 : 0; + return Math.abs(gapPct) >= 0.25 ? ( +

+ Heads up: you'd get in {Math.abs(gapPct).toFixed(1)}% {gapPct > 0 ? "above" : "below"} the live price — that's + the venue's cut of the entry. The round starts that far {gapPct > 0 === (side === "LONG") ? "behind" : "ahead"}. +

+ ) : null; + })()} +

+ {fmtUsd(stakeNum)} says {market} {side === "LONG" ? "goes up" : "goes down"} in{" "} + {TIMEFRAMES.find((t) => t.id === timeframeId)?.label}. Worst case: −{fmtUsd(stakeNum)}. +

+
+ + +
+ {blockedReason &&

{blockedReason}

} +
+ )} + + {error &&

{error}

} +
+ ); +} diff --git a/examples/predict/components/token-icon.tsx b/examples/predict/components/token-icon.tsx new file mode 100644 index 0000000..edfa100 --- /dev/null +++ b/examples/predict/components/token-icon.tsx @@ -0,0 +1,34 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/token-icon.tsx — one token glyph, shared by ticket and picker. +// THE HARD PART: the vendored icon set mixes .png and .svg — try png, fall +// back to svg, fall back to a lettered dot. No network beyond /public. +// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { useEffect, useState } from "react"; + +export function TokenIcon({ symbol, size = 20 }: { symbol: string; size?: number }) { + const [ext, setExt] = useState<"png" | "svg" | "none">("png"); + useEffect(() => setExt("png"), [symbol]); + if (ext === "none") + return ( + + {symbol.slice(0, 1)} + + ); + return ( + // eslint-disable-next-line @next/next/no-img-element + setExt(ext === "png" ? "svg" : "none")} + /> + ); +} diff --git a/examples/predict/components/wallet-bar.tsx b/examples/predict/components/wallet-bar.tsx new file mode 100644 index 0000000..901054e --- /dev/null +++ b/examples/predict/components/wallet-bar.tsx @@ -0,0 +1,139 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/wallet-bar.tsx — wordmark, connect, balances, session controls. +// THE HARD PART: balances must be labeled by WHERE they live (wallet vs +// basket) — an unlabeled number reads as "my deposit" and that's a consent +// bug. Mainnet warning shows at the connect moment, not buried in a footer. +// GOTCHAS.md → "Your balance is the DEPOSIT LEDGER" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { useCallback, useEffect, useRef, useState } from "react"; +import { useWallet } from "@solana/wallet-adapter-react"; +import type { WalletName } from "@solana/wallet-adapter-base"; +import { CONNECT_WARNING } from "@/lib/copy"; +import { fmtUsd, shortKey } from "@/lib/format"; + +interface Props { + inBasketUsd: number | null; + walletUsdc: number | null; + sessionActive: boolean; + busy: boolean; + onFunds: () => void; + onEnable: () => void; + onRevoke: () => void; + onShowDisclosure: () => void; +} + +export function WalletBar({ inBasketUsd, walletUsdc, sessionActive, busy, onFunds, onEnable, onRevoke, onShowDisclosure }: Props) { + const { wallets, select, connect, disconnect, connected, connecting, publicKey, wallet } = useWallet(); + const [picking, setPicking] = useState(false); + const wantConnect = useRef(false); + + // select() is async-ish in adapter-land: connect on the effect after the + // wallet object lands, not in the same tick as select(). + useEffect(() => { + if (wallet && wantConnect.current && !connected && !connecting) { + wantConnect.current = false; + connect().catch(() => {}); + } + }, [wallet, connected, connecting, connect]); + + const pick = useCallback( + (name: WalletName) => { + setPicking(false); + wantConnect.current = true; + select(name); + }, + [select], + ); + + return ( +
+
+ UP + DOWN +
+ + +
+ {connected && publicKey ? ( + <> + + {sessionActive ? ( + + ) : ( + + )} + + + ) : ( +
+ + {picking && ( +
+

{CONNECT_WARNING}

+ {wallets.map((w) => ( + + ))} +
+ )} +
+ )} +
+
+ ); +} diff --git a/examples/predict/lib/clock.tsx b/examples/predict/lib/clock.tsx new file mode 100644 index 0000000..8d9c549 --- /dev/null +++ b/examples/predict/lib/clock.tsx @@ -0,0 +1,26 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/clock.tsx — one shared 1s clock for every countdown and the settlement +// watcher. THE HARD PART: N components with their own setInterval = N +// re-render storms and a reconcile effect with an unstable dependency. One +// provider ticks; everyone reads the same timestamp. +// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { createContext, useContext, useEffect, useState, type ReactNode } from "react"; + +const ClockContext = createContext(0); + +export function ClockProvider({ children }: { children: ReactNode }) { + const [now, setNow] = useState(() => Date.now()); + useEffect(() => { + const id = setInterval(() => setNow(Date.now()), 1000); + return () => clearInterval(id); + }, []); + return {children}; +} + +/** Shared wall-clock, ticking once per second. */ +export function useClock(): number { + return useContext(ClockContext); +} diff --git a/examples/predict/lib/copy.ts b/examples/predict/lib/copy.ts new file mode 100644 index 0000000..4f90507 --- /dev/null +++ b/examples/predict/lib/copy.ts @@ -0,0 +1,73 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/copy.ts — every user-facing sentence that carries risk, in one place. +// THE HARD PART: honesty without jargon. The disclosure NEGATES the +// prediction-market mental model (fixed odds) and states what this is: a +// short-term leveraged position with loss capped at your stake. Failure +// modes map to calm copy, never raw logs. GOTCHAS.md → "Withdrawals settle +// in two phases" · "err arrives inside HTTP 200" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +export const APP_NAME = "Updown"; +export const TAGLINE = "Pick up or down. Your stake is the most you can lose."; + +export const DISCLOSURE = { + title: "How Updown actually works", + paragraphs: [ + "This isn't a Polymarket-style prediction market — there are no fixed odds and no shared pot. When you call a direction, you open a real, short-term leveraged position on Flash Trade, sized by your stake.", + "Your payoff is linear: for every 1% the price moves your way, you win your stake × leverage × 1%. Move against you and you lose the same way — but never more than your stake. If the price falls past your knockout level before time runs out, the round ends early and your stake is gone.", + "Your numbers lock when you commit. The entry price, fees, and knockout level on the confirm screen are the ones you settle with — they don't drift afterward.", + "Settlement happens from this browser with your session key. If you close the tab mid-round, the position simply stays open on-chain — come back any time and settle it. Nothing expires, nothing is lost.", + ], + checkbox: "I understand each round is a real leveraged position on Solana mainnet, with real money.", +} as const; + +export const CONNECT_WARNING = "Mainnet. Real funds — every round is a real position. Start small."; + +export const ENABLE_EXPLAINER = { + title: "Enable one-tap rounds", + body: "This sets up your trading account — it never moves your money. The only transfer is a 0.01 SOL top-up to your own session key for rent, returned when you revoke.", + steps: ["Create session key (lives in this browser)", "Create basket + deposit ledger", "Delegate to the rollup"], +} as const; + +/** Calm-copy mapping for everything that can go wrong. Raw details stay in + * the console for debugging; the user sees the human line. */ +export function calmError(e: unknown): string { + const msg = e instanceof Error ? e.message : String(e); + if (/429|rate.?limit/i.test(msg)) return "Your RPC is rate-limiting. Add a free RPC key in .env.local and reload."; + if (/blockhash|expired|block height/i.test(msg)) return "That transaction went stale. Nothing was sent — try again."; + if (/reject|declin|cancel/i.test(msg)) return "You declined in the wallet. Nothing was sent."; + if (/settlement_receipt|AccountNotInitialized|0xbc4/i.test(msg)) + return "Your withdrawal is crossing from the rollup — usually 30–90 seconds. We'll keep checking."; + if (/Position is empty/i.test(msg)) return "That position is already closed — refreshing your rounds."; + if (/insufficient|not enough/i.test(msg)) return "Not enough balance for that. Lower the amount or deposit first."; + if (/session|signer/i.test(msg)) return "Your session key needs a refresh — re-enable one-tap rounds to continue."; + if (/fetch|network|ECONN|timeout/i.test(msg)) return "Network hiccup talking to the API. Check your connection and try again."; + return "Something went wrong on the venue side. Your funds are safe — try again in a moment."; +} + +export function fmtCountdown(ms: number): string { + const total = Math.max(0, Math.ceil(ms / 1000)); + const m = Math.floor(total / 60); + const s = total % 60; + if (m >= 60) { + const h = Math.floor(m / 60); + return `${h}:${String(m % 60).padStart(2, "0")}:${String(s).padStart(2, "0")}`; + } + return `${m}:${String(s).padStart(2, "0")}`; +} + +export function fmtDuration(ms: number): string { + const m = Math.round(ms / 60_000); + if (m < 60) return `${m} min`; + return `${m / 60} hour${m === 60 ? "" : "s"}`; +} + +export const explorerTxUrl = (signature: string) => `https://solscan.io/tx/${signature}`; + +/** One price formatter for every surface — kills the scattered `>=1000` + * digit branches. "main" = focal numbers; "compact" = grid/axis hints. */ +export function fmtPrice(v: number | null | undefined, mode: "main" | "compact" = "main"): string { + if (v === null || v === undefined || !Number.isFinite(v)) return "—"; + const digits = mode === "main" ? (v >= 1000 ? 2 : 4) : v >= 1000 ? 0 : 2; + return `$${v.toLocaleString("en-US", { minimumFractionDigits: digits, maximumFractionDigits: digits })}`; +} diff --git a/examples/predict/lib/enable.ts b/examples/predict/lib/enable.ts new file mode 100644 index 0000000..e1c7726 --- /dev/null +++ b/examples/predict/lib/enable.ts @@ -0,0 +1,339 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/enable.ts — "Enable One-Click Trading": session + basket + ledger + +// delegate behind ONE wallet approval sheet (signAllTransactions). +// CONSENT RULE: NO USER FUNDS MOVE HERE. Enable is account setup only — the +// sole transfer is the disclosed 0.01 SOL rent top-up to the session key +// (recoverable on revoke). Deposits/withdrawals are EXPLICIT, amount-chosen, +// separately-approved actions in lib/funds.ts. Never bundle a deposit into a +// convenience flow in a production app. +// THE HARD PART: build EVERYTHING first (API txs arrive partially signed with +// their own blockhashes — never touch them), sign once, then submit in strict +// lifecycle order on the BASE chain. +// GOTCHAS.md → "The lifecycle has a strict order" +// ───────────────────────────────────────────────────────────────────────────── + +import type { PublicKey, Transaction, VersionedTransaction } from "@solana/web3.js"; +import { decodeTransaction, type BasketSnapshot } from "flash-v2"; +import { baseConnection, flash } from "./flash"; +import type { LatencyEntry } from "./hooks"; +import { + buildSessionTransaction, + loadSession, + persistSession, + type BuiltSessionTransaction, + type LoadedSession, + type SessionWallet, +} from "./session"; + +// ── public shapes ───────────────────────────────────────────────────────────── + +export type EnableStepId = "fund" | "session" | "basket" | "ledger" | "delegate"; +export type EnableStepStatus = "idle" | "active" | "done" | "skipped" | "error"; + +export interface EnableStepRow { + id: EnableStepId; + label: string; + status: EnableStepStatus; + note?: string; + ms?: number; + signature?: string; +} + +export interface EnableState { + phase: "precheck" | "building" | "signing" | "submitting" | "done" | "stopped"; + /** Live text for the action-zone pill ("approve in wallet…", "init-basket…"). */ + headline: string; + steps: EnableStepRow[]; + /** Set when the wallet can't pay setup rent. */ + fundingHint: string | null; + /** Fresh basket has no funds yet — surface the EXPLICIT Deposit affordance. + * (Funds never move during Enable; see lib/funds.ts.) */ + needsUsdc: boolean; + /** Fatal step error (already phrased for humans; calming when upstream). */ + error: string | null; + /** True when `error` is a known transient upstream-deploy situation. */ + upstream: boolean; +} + +export interface EnableResult { + ok: boolean; + /** Minted (and persisted) even when a later step failed — partial progress counts. */ + session: LoadedSession | null; + needsUsdc: boolean; +} + +/** The narrow slice of wallet-adapter's useWallet() that enable needs. */ +export interface EnableWalletCtx { + publicKey: PublicKey; + signTransaction: (tx: T) => Promise; + signAllTransactions?: ((txs: T[]) => Promise) | undefined; +} + +// ── error classification (per-step) ────────────────────────────────────────── + +const RE_ALREADY = /already in use|already exists|custom program error: 0x0\b/i; +const RE_UPSTREAM = /Access violation in heap|ProgramFailedToComplete/i; +const RE_REJECTED = /reject|declin|cancel/i; +const RE_STALE = /blockhash not found|block height exceeded|expired/i; +const RE_NO_SIGNALL = /signAllTransactions|not.{0,4}supported/i; + +export const UPSTREAM_MESSAGE = + "known upstream issue: the magic-trade program is temporarily unavailable (Flash may be redeploying) — your setup is fine; retry after the fix."; + +export function errText(e: unknown): string { + const msg = e instanceof Error ? e.message : String(e); + const logs = (e as { logs?: unknown })?.logs; + return Array.isArray(logs) ? `${msg} ${logs.join(" ")}` : msg; +} + +/** Shared per-tx error classification — also used by the explicit funds flows. */ +export function classifyTxError(e: unknown): { + message: string; + upstream: boolean; + stale: boolean; + rejected: boolean; +} { + const raw = errText(e); + if (RE_UPSTREAM.test(raw)) return { message: UPSTREAM_MESSAGE, upstream: true, stale: false, rejected: false }; + if (RE_REJECTED.test(raw)) + return { message: "approval declined in the wallet — nothing was sent", upstream: false, stale: false, rejected: true }; + if (RE_STALE.test(raw)) + return { message: "the approval took too long and the transaction expired — try again", upstream: false, stale: true, rejected: false }; + return { message: raw, upstream: false, stale: false, rejected: false }; +} + +// ── confirmation (the confirmAirdrop pattern: poll signature statuses) ─────── + +export async function submitAndConfirm( + tx: Transaction | VersionedTransaction, + timeoutMs = 45_000, +): Promise<{ signature: string; ms: number }> { + const raw = tx.serialize(); + const started = Date.now(); + const signature = await baseConnection.sendRawTransaction(raw, { maxRetries: 3 }); + for (;;) { + try { + const status = (await baseConnection.getSignatureStatuses([signature])).value[0]; + if (status) { + if (status.err) throw new Error(`on-chain error (${signature}): ${JSON.stringify(status.err)}`); + const level = status.confirmationStatus; + if (level === "confirmed" || level === "finalized") { + return { signature, ms: Date.now() - started }; + } + } + } catch (e) { + // A real on-chain error must surface; transient 429s while polling must not. + if (e instanceof Error && e.message.startsWith("on-chain error")) throw e; + } + if (Date.now() - started > timeoutMs) throw new Error(`confirmation timeout (${signature})`); + await new Promise((r) => setTimeout(r, 400)); + } +} + +// ── the flow ────────────────────────────────────────────────────────────────── + +export async function enableOneClickTrading(args: { + wallet: EnableWalletCtx; + anchorWallet: SessionWallet; + snapshot: BasketSnapshot | null; + usdcMint: string | null; + balances: { sol: number | null; usdc: number | null }; + onStep: (state: EnableState) => void; + onLog: (e: Omit) => void; +}): Promise { + const { wallet, anchorWallet, snapshot, usdcMint, balances, onStep, onLog } = args; + const owner = wallet.publicKey.toBase58(); + + // 1 ── what does this account still need? + const basketExists = Boolean(snapshot?.basketPubkey); + const freshSession = loadSession(owner); + const needs = { + session: !freshSession, + basket: !basketExists, + ledger: !basketExists, // basket unknown → ledger unknown → init it (idempotent-ish) + delegate: !basketExists, // a fresh basket must be handed to the MagicBlock validator + // NO deposit here — funds move only via the explicit Deposit flow (lib/funds.ts). + }; + + const state: EnableState = { + phase: "precheck", + headline: "checking wallet…", + steps: [], + fundingHint: null, + needsUsdc: false, + error: null, + upstream: false, + }; + const rows = new Map(); + const emit = () => { + state.steps = [...rows.values()]; + onStep({ ...state, steps: state.steps.map((s) => ({ ...s })) }); + }; + const row = (id: EnableStepId, label: string, patch: Partial) => { + rows.set(id, { ...(rows.get(id) ?? { id, label, status: "idle" }), label, ...patch }); + emit(); + }; + const stop = (error: string, upstream = false): EnableResult => { + state.phase = "stopped"; + state.error = error; + state.upstream = upstream; + state.headline = upstream ? "program unavailable upstream" : "setup stopped"; + emit(); + return { ok: false, session: mintedSession, needsUsdc: state.needsUsdc }; + }; + let mintedSession: LoadedSession | null = freshSession; + /** Freshly built session pieces (set in step 3, persisted in step 5 once its tx confirms). */ + let sessionParts: BuiltSessionTransaction | null = null; + + // 2 ── funding pre-check: compute the real SOL needed at runtime — mostly + // recoverable account rent (basket + deposit-ledger + session-token), + // plus the recoverable 0.01 SOL session top-up and a small fee buffer. + // No hardcoded gate: getMinimumBalanceForRentExemption is the source of truth. + const sol = balances.sol ?? 0; + emit(); + let rentLamports = 0; + if (needs.basket) rentLamports += await baseConnection.getMinimumBalanceForRentExemption(256); + if (needs.ledger) rentLamports += await baseConnection.getMinimumBalanceForRentExemption(852); + if (needs.session) rentLamports += await baseConnection.getMinimumBalanceForRentExemption(144); + const requiredSol = rentLamports / 1e9 + (needs.session ? 0.01 : 0) + 0.002; // top-up + fee buffer + if (sol < requiredSol) { + const need = requiredSol.toFixed(4); + state.fundingHint = + `this wallet needs ~${need} SOL for setup — mostly recoverable account rent plus a ` + + `0.01 SOL session top-up (recoverable on revoke), not fees. Fund ${owner} and tap Enable again.`; + return stop(`wallet needs ~${need} SOL for setup (recoverable rent + top-up)`); + } + // 3 ── BUILD everything first (no signatures yet). + state.phase = "building"; + state.headline = "preparing transactions…"; + emit(); + + type Planned = { id: EnableStepId; label: string; action: string; tx: Transaction | VersionedTransaction }; + const plan: Planned[] = []; + try { + if (needs.session) { + row("session", "create session key", { status: "active", note: "building…" }); + const built = await buildSessionTransaction(anchorWallet, baseConnection, { + }); + plan.push({ id: "session", label: "create session key", action: "create-session", tx: built.tx }); + // Persisted only after its tx confirms (step 5 below). + sessionParts = built; + row("session", "create session key", { status: "active", note: "ready to sign" }); + } else { + row("session", "create session key", { status: "skipped", note: "session still valid" }); + } + + const builders: Array<{ id: EnableStepId; label: string; action: string; build: () => Promise<{ transactionBase64: string }> }> = []; + if (needs.basket) builders.push({ id: "basket", label: "init-basket", action: "init-basket", build: () => flash.initBasket({ owner }) }); + else row("basket", "init-basket", { status: "skipped", note: "basket already on-chain" }); + if (needs.ledger) builders.push({ id: "ledger", label: "init-deposit-ledger", action: "init-deposit-ledger", build: () => flash.initDepositLedger({ owner }) }); + else row("ledger", "init-deposit-ledger", { status: "skipped", note: "already set up" }); + if (needs.delegate) builders.push({ id: "delegate", label: "delegate-basket", action: "delegate-basket", build: () => flash.delegateBasket({ payer: owner, owner }) }); + else row("delegate", "delegate-basket", { status: "skipped", note: "already delegated" }); + + // CONSENT RULE: no deposit in this batch — a fresh basket starts EMPTY and + // the user funds it via the explicit Deposit sheet (their amount, their + // separate approval). needsUsdc just surfaces that affordance. + if (!basketExists) state.needsUsdc = true; + + for (const b of builders) { + row(b.id, b.label, { status: "active", note: "building…" }); + const built = await b.build(); + // NEVER touch the blockhash — these arrive partially signed server-side. + plan.push({ id: b.id, label: b.label, action: b.action, tx: decodeTransaction(built.transactionBase64) }); + row(b.id, b.label, { status: "active", note: "ready to sign" }); + } + } catch (e) { + const raw = errText(e); + return stop(RE_UPSTREAM.test(raw) ? UPSTREAM_MESSAGE : raw, RE_UPSTREAM.test(raw)); + } + + if (plan.length === 0) { + state.phase = "done"; + state.headline = "already set up"; + emit(); + return { ok: true, session: mintedSession, needsUsdc: state.needsUsdc }; + } + + // 4 ── ONE approval for the whole bundle (fallback: sequential popups). + state.phase = "signing"; + state.headline = "approve in wallet…"; + for (const p of plan) row(p.id, p.label, { status: "active", note: "waiting for your approval…" }); + let signed: (Transaction | VersionedTransaction)[]; + try { + const toSign = plan.map((p) => p.tx); + const signAll = wallet.signAllTransactions; + if (typeof signAll === "function") { + try { + signed = await signAll(toSign); + } catch (e) { + const raw = errText(e); + if (RE_REJECTED.test(raw)) throw e; + if (!RE_NO_SIGNALL.test(raw)) throw e; + signed = await signSequentially(); + } + } else { + signed = await signSequentially(); + } + async function signSequentially(): Promise<(Transaction | VersionedTransaction)[]> { + const out: (Transaction | VersionedTransaction)[] = []; + for (let i = 0; i < plan.length; i++) { + const p = plan[i]; + if (!p) continue; + state.headline = `approve in wallet (${i + 1}/${plan.length})…`; + row(p.id, p.label, { status: "active", note: `approve in wallet (${i + 1}/${plan.length})…` }); + out.push(await wallet.signTransaction(p.tx)); + } + return out; + } + } catch (e) { + const raw = errText(e); + const friendly = RE_REJECTED.test(raw) ? "approval declined in the wallet — nothing was sent" : raw; + for (const p of plan) row(p.id, p.label, { status: "error", note: friendly }); + return stop(friendly); + } + + // 5 ── submit sequentially with confirmation, strict lifecycle order. + state.phase = "submitting"; + for (const p of plan) row(p.id, p.label, { status: "active", note: "queued…" }); + for (let i = 0; i < plan.length; i++) { + const p = plan[i]; + const tx = signed[i]; + if (!p || !tx) continue; + state.headline = `${p.action}…`; + row(p.id, p.label, { status: "active", note: "submitting to the base chain…" }); + try { + const { signature, ms } = await submitAndConfirm(tx); + row(p.id, p.label, { status: "done", note: undefined, ms, signature }); + onLog({ action: p.action, chain: "base", ms, signature }); + if (p.id === "session" && sessionParts) { + mintedSession = persistSession({ + sessionSigner: sessionParts.sessionSigner, + sessionToken: sessionParts.sessionToken, + authority: owner, + validUntil: sessionParts.validUntil, + }); + } + } catch (e) { + const raw = errText(e); + if (RE_ALREADY.test(raw)) { + row(p.id, p.label, { status: "done", note: "already set up" }); + continue; + } + if (RE_UPSTREAM.test(raw)) { + row(p.id, p.label, { status: "error", note: UPSTREAM_MESSAGE }); + return stop(UPSTREAM_MESSAGE, true); + } + const friendly = RE_STALE.test(raw) + ? "the approval took too long and the transaction expired — tap Enable again to rebuild" + : raw; + row(p.id, p.label, { status: "error", note: friendly }); + return stop(friendly); + } + } + + state.phase = "done"; + state.headline = "one-click trading enabled"; + emit(); + return { ok: true, session: mintedSession, needsUsdc: state.needsUsdc }; +} diff --git a/examples/predict/lib/flash.ts b/examples/predict/lib/flash.ts new file mode 100644 index 0000000..e2922bc --- /dev/null +++ b/examples/predict/lib/flash.ts @@ -0,0 +1,48 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/flash.ts — one FlashV2Client + one base-chain Connection for the app. +// THE HARD PART: V2 spans TWO chains — trading txs go to network.erRpc, setup +// txs to network.baseRpc. Flash V2 is MAINNET — real funds. +// GOTCHAS.md → "Two chains, one flow" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +import { Connection } from "@solana/web3.js"; +import { FlashV2Client, type NetworkConfig } from "flash-v2"; + +/** NEXT_PUBLIC_ env overrides, applied only when actually set (a key whose + * value is `undefined` would otherwise stomp the preset in the spread). */ +function overridesFromEnv(): Partial { + const cfg: Partial = {}; + if (process.env.NEXT_PUBLIC_FLASH_API_BASE) cfg.apiBase = process.env.NEXT_PUBLIC_FLASH_API_BASE; + if (process.env.NEXT_PUBLIC_ER_RPC) cfg.erRpc = process.env.NEXT_PUBLIC_ER_RPC; + if (process.env.NEXT_PUBLIC_BASE_RPC) cfg.baseRpc = process.env.NEXT_PUBLIC_BASE_RPC; + return cfg; +} + +/** The one client. Reads, quotes, and every transaction builder. */ +export const flash = new FlashV2Client(overridesFromEnv()); + + +/** Base-chain connection — airdrops, balances, setup-tx submission. + * disableRetryOnRateLimit: the public RPC rate-limits; one attempt, one + * clean failure — the app owns retry UX. */ +export const baseConnection = new Connection(flash.network.baseRpc, { + commitment: "confirmed", + disableRetryOnRateLimit: true, +}); + +/** All tradeable markets (first is the default). */ +export const MARKETS = ["SOL", "BTC", "ETH"]; +/** Kept for backward compat — equals MARKETS[0]. */ +export const MARKET = MARKETS[0] as string; +export const COLLATERAL = "USDC"; + +/** Price poll cadence — 1s keeps the chart line + slot-reel digits alive. */ +export const PRICE_POLL_MS = 1000; + +/** Static, clearly-labeled comparison baseline for the latency HUD. */ +export const SOLANA_L1_TYPICAL_MS = 400; + +/** Explorer link for a signature — cluster param matches the active network. */ +export function explorerTx(signature: string): string { + return `https://explorer.solana.com/tx/${signature}`; +} diff --git a/examples/predict/lib/format.ts b/examples/predict/lib/format.ts new file mode 100644 index 0000000..ef221ac --- /dev/null +++ b/examples/predict/lib/format.ts @@ -0,0 +1,130 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/format.ts — display formatting for keys, prices, USD, PnL, and latency. +// THE HARD PART: the API speaks UI DECIMAL STRINGS (and leverage can be the +// literal string "Infinity") — every formatter here tolerates that without +// NaN leaking into the UI. GOTCHAS.md → "Responses are V1-shaped" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +/** "FMTg…xtvj" — middle-truncate a pubkey or signature. */ +export function shortKey(key: string, n = 4): string { + if (key.length <= n * 2 + 1) return key; + return `${key.slice(0, n)}…${key.slice(-n)}`; +} + +/** Parse an API decimal string defensively. Returns null on junk/Infinity. */ +export function num(v: string | number | null | undefined): number | null { + if (v === null || v === undefined) return null; + const n = typeof v === "number" ? v : Number(v); + return Number.isFinite(n) ? n : null; +} + +/** USDC has 6 decimals on-chain — tiny but REAL values must never display as + * $0.00 (a $5 position's fee is ~$0.004). Adaptive precision: more digits as + * the magnitude shrinks, never inventing precision the chain doesn't have. */ +function usdDigits(abs: number, base: number): number { + if (abs === 0) return base; + if (abs < 0.01) return 4; + if (abs < 1) return 3; + return base; +} + +/** "$151.23" / "−$0.0042" — USD with sign folded in, 6-dec-aware. */ +export function fmtUsd(v: string | number | null | undefined, digits = 2): string { + const n = num(v); + if (n === null) return "—"; + const abs = Math.abs(n); + const d = usdDigits(abs, digits); + const sign = n < 0 ? "−" : ""; + return `${sign}$${abs.toLocaleString("en-US", { + minimumFractionDigits: d, + maximumFractionDigits: d, + })}`; +} + +/** Signed USD for PnL: "+$0.42" / "−$0.0042" (6-dec-aware for small books). */ +export function fmtPnlUsd(v: string | number | null | undefined): string { + const n = num(v); + if (n === null) return "—"; + const abs = Math.abs(n); + return `${n >= 0 ? "+" : "−"}$${abs.toFixed(usdDigits(abs, 2))}`; +} + +/** "+3.81%" / "−1.20%". */ +export function fmtPct(v: string | number | null | undefined, digits = 2): string { + const n = num(v); + if (n === null) return "—"; + return `${n >= 0 ? "+" : "−"}${Math.abs(n).toFixed(digits)}%`; +} + +/** Plain number with fixed digits, em-dash on junk. */ +export function fmtNum(v: string | number | null | undefined, digits = 2): string { + const n = num(v); + return n === null ? "—" : n.toLocaleString("en-US", { + minimumFractionDigits: digits, + maximumFractionDigits: digits, + }); +} + +/** "5.0×" — leverage; tolerates the API's literal "Infinity". */ +export function fmtLeverage(v: string | number | null | undefined): string { + const n = num(v); + return n === null ? "∞×" : `${n.toFixed(1)}×`; +} + +/** "38 ms" / "1.24 s". */ +export function fmtMs(ms: number): string { + return ms < 1000 ? `${ms} ms` : `${(ms / 1000).toFixed(2)} s`; +} + +/** Median of a number list (null when empty). */ +export function median(values: number[]): number | null { + if (values.length === 0) return null; + const sorted = [...values].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + const lo = sorted[mid - 1]; + const hi = sorted[mid]; + if (sorted.length % 2 === 0 && lo !== undefined && hi !== undefined) { + return Math.round((lo + hi) / 2); + } + return hi ?? null; +} + +// ── Flash-parity position math (client-side, like Flash's own UI) ─────────── +// Flash's UI shows mark-price PnL incl. fees, NOT the indexer's spread-through +// pnlWithFeeUsdUi (which can read wildly different on the same position). +// Compute what the product computes: +// pricePnl = (mark − entry)/entry × size × dir +// pnl = pricePnl − (exitFee + borrowFee) [raw 6-dec ÷ 1e6] +// pct = pnl / collateral × 100 +// lev = size / collateral +// liq ≈ entry × (1 ∓ collateral/size × 0.92) [≈ maintenance buffer] +export interface PositionView { + pnlUsd: number; + pnlPct: number; + leverage: number; + liqUi: number | null; +} + +export function computePositionView( + p: { + sideUi: string; + sizeUsdUi: string; + collateralUsdUi: string; + entryPriceUi: string; + exitFeeUsd: string; + borrowFeeUsd: string; + }, + markUi: number | null, +): PositionView | null { + const size = num(p.sizeUsdUi); + const coll = num(p.collateralUsdUi); + const entry = num(p.entryPriceUi); + if (size === null || coll === null || entry === null || entry <= 0 || markUi === null) return null; + const dir = p.sideUi.toUpperCase() === "LONG" ? 1 : -1; + const fees = ((num(p.exitFeeUsd) ?? 0) + (num(p.borrowFeeUsd) ?? 0)) / 1e6; + const pnlUsd = ((markUi - entry) / entry) * size * dir - fees; + const pnlPct = coll > 0 ? (pnlUsd / coll) * 100 : 0; + const leverage = coll > 0 ? size / coll : 0; + const liqUi = size > 0 ? entry * (1 - dir * (coll / size) * 0.92) : null; + return { pnlUsd, pnlPct, leverage, liqUi }; +} diff --git a/examples/predict/lib/funds.ts b/examples/predict/lib/funds.ts new file mode 100644 index 0000000..3cb4cbd --- /dev/null +++ b/examples/predict/lib/funds.ts @@ -0,0 +1,218 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/funds.ts — EXPLICIT deposit & withdraw. THE PRODUCTION RULE THIS FILE +// EXISTS TO TEACH: user funds move ONLY on a user-initiated action with a +// user-chosen amount and its own wallet approval — never bundled into a +// convenience flow like "Enable". Withdraw is the API's two-step dance: +// request-withdrawal → execute-withdrawal (both signed by the OWNER wallet, +// both on the BASE chain; execute can be re-run alone to recover). +// GOTCHAS.md → "Funds move on consent only" · "Two chains, one flow" +// ───────────────────────────────────────────────────────────────────────────── + +import { decodeTransaction } from "flash-v2"; +import { baseConnection, flash } from "./flash"; +import { classifyTxError, submitAndConfirm, type EnableWalletCtx } from "./enable"; +import type { LatencyEntry } from "./hooks"; + +export interface FundsStep { + /** "building" → "approve" → "submitting" → "done" | "error" */ + phase: "building" | "approve" | "submitting" | "done" | "error"; + label: string; + note?: string; + signature?: string; + ms?: number; +} + +type OnStep = (step: FundsStep) => void; +type OnLog = (e: Omit) => void; + +async function signSubmit( + wallet: EnableWalletCtx, + txBase64: string, + label: string, + onStep: OnStep, + onLog: OnLog, +): Promise<{ signature: string; ms: number }> { + onStep({ phase: "approve", label, note: "approve in your wallet…" }); + // NEVER touch the blockhash — builder txs arrive partially signed server-side. + const signed = await wallet.signTransaction(decodeTransaction(txBase64)); + onStep({ phase: "submitting", label, note: "submitting to the base chain…" }); + const { signature, ms } = await submitAndConfirm(signed); + onLog({ action: label, chain: "base", ms, signature }); + return { signature, ms }; +} + +/** + * Deposit ANY pool token into the basket — the user typed `amount`, the user + * approves the single transfer. Tradable once the deposit confirms on the ER. + */ +export async function depositToken(args: { + wallet: EnableWalletCtx; + tokenMint: string; + symbol: string; + amount: string; // UI units, user-chosen + onStep: OnStep; + onLog: OnLog; +}): Promise<{ ok: boolean; error?: string }> { + const { wallet, tokenMint, symbol, amount, onStep, onLog } = args; + const owner = wallet.publicKey.toBase58(); + const label = `deposit ${amount} ${symbol}`; + try { + onStep({ phase: "building", label, note: "building the transaction…" }); + const built = await flash.depositDirect({ owner, tokenMint, amount }); + const { signature, ms } = await signSubmit(wallet, built.transactionBase64, label, onStep, onLog); + onStep({ phase: "done", label, note: "deposited — tradable once it confirms on the rollup", signature, ms }); + return { ok: true }; + } catch (e) { + const c = classifyTxError(e); + onStep({ phase: "error", label, note: c.message }); + return { ok: false, error: c.message }; + } +} + +/** USDC compat wrapper (existing callers). */ +export async function depositUsdc(args: { + wallet: EnableWalletCtx; + usdcMint: string; + amount: string; + onStep: OnStep; + onLog: OnLog; +}): Promise<{ ok: boolean; error?: string }> { + return depositToken({ wallet: args.wallet, tokenMint: args.usdcMint, symbol: "USDC", amount: args.amount, onStep: args.onStep, onLog: args.onLog }); +} + +// The receipt the execute step consumes is written when the rollup's validator +// CROSSES the settlement to base chain (~30-90s after request). Until then the +// program answers 0xbc4 AccountNotInitialized on `settlement_receipt` — a +// timing state, never an error worth showing raw logs for. +const RE_SETTLEMENT_PENDING = /settlement_receipt|AccountNotInitialized|0xbc4|3012/i; + +/** + * Withdraw USDC back to the wallet. Two explicit steps, two approvals: + * 1) request-withdrawal (queues settlement out of the rollup) + * 2) execute-withdrawal — AUTO-RETRIED while the rollup crosses the + * settlement receipt to base (~30-90s), with a countdown the sheet shows. + */ +export async function withdrawToken(args: { + wallet: EnableWalletCtx; + tokenMint: string; + symbol: string; + amount: string; // UI units, user-chosen + onStep: OnStep; + onLog: OnLog; +}): Promise<{ ok: boolean; error?: string; executePending?: boolean }> { + const { wallet, tokenMint, symbol, amount, onStep, onLog } = args; + const owner = wallet.publicKey.toBase58(); + try { + onStep({ phase: "building", label: `request withdrawal of ${amount} ${symbol}`, note: "building…" }); + const req = await flash.requestWithdrawal({ owner, tokenMint, amount }); + await signSubmit(wallet, req.transactionBase64, `request-withdrawal ${amount} ${symbol}`, onStep, onLog); + } catch (e) { + const c = classifyTxError(e); + onStep({ phase: "error", label: "request withdrawal", note: c.message }); + return { ok: false, error: c.message }; + } + // Settlement crossing: poll UNSIGNED simulations (zero popups) until the + // receipt exists on base, then ask for the ONE execute signature. + const ready = await waitForSettlementReceipt({ owner, tokenMint, onStep, maxSeconds: 120 }); + if (!ready) { + onStep({ + phase: "error", + label: "execute withdrawal", + note: "settlement is taking longer than usual — your funds are queued safely; tap \"Execute again\" in a minute.", + }); + return { ok: false, error: "settlement still crossing", executePending: true }; + } + return executeWithdrawalToken({ wallet, tokenMint, symbol, onStep, onLog }); +} + +/** USDC compat wrapper (existing callers). */ +export async function withdrawUsdc(args: { + wallet: EnableWalletCtx; + usdcMint: string; + amount: string; + onStep: OnStep; + onLog: OnLog; +}): Promise<{ ok: boolean; error?: string; executePending?: boolean }> { + return withdrawToken({ wallet: args.wallet, tokenMint: args.usdcMint, symbol: "USDC", amount: args.amount, onStep: args.onStep, onLog: args.onLog }); +} + +/** + * Poll an UNSIGNED simulation of execute-withdrawal until the settlement + * receipt exists (no wallet popups while waiting). True = ready to sign. + */ +async function waitForSettlementReceipt(args: { + owner: string; + tokenMint: string; + onStep: OnStep; + maxSeconds: number; +}): Promise { + const { owner, tokenMint, onStep, maxSeconds } = args; + const started = Date.now(); + for (;;) { + const waited = Math.round((Date.now() - started) / 1000); + if (waited > maxSeconds) return false; + try { + const exec = await flash.executeWithdrawal({ owner, tokenMint }); + const tx = decodeTransaction(exec.transactionBase64); + const sim = await baseConnection.simulateTransaction(tx, { + sigVerify: false, + replaceRecentBlockhash: true, + }); + const err = sim.value.err ? JSON.stringify(sim.value.err) + (sim.value.logs ?? []).join(" ") : ""; + if (!sim.value.err) return true; // receipt landed — execute will pass + if (!RE_SETTLEMENT_PENDING.test(err)) return true; // different state — let the real attempt surface it + } catch { /* builder/RPC hiccup — keep polling */ } + onStep({ + phase: "submitting", + label: "settlement crossing from the rollup", + note: `the rollup writes the receipt to base chain (~30–90s) — waiting ${waited}s, no action needed`, + }); + await new Promise((r) => setTimeout(r, 8000)); + } +} + +/** Step 2 alone — also the recovery path when settlement needed more time. */ +export async function executeWithdrawalToken(args: { + wallet: EnableWalletCtx; + tokenMint: string; + symbol: string; + onStep: OnStep; + onLog: OnLog; + attempt?: number; +}): Promise<{ ok: boolean; error?: string; executePending?: boolean }> { + const { wallet, tokenMint, onStep, onLog, attempt } = args; + const owner = wallet.publicKey.toBase58(); + const tag = attempt ? ` (attempt ${attempt}/4)` : ""; + try { + onStep({ phase: "building", label: "execute withdrawal", note: `building…${tag}` }); + const exec = await flash.executeWithdrawal({ owner, tokenMint }); + const { signature, ms } = await signSubmit(wallet, exec.transactionBase64, "execute-withdrawal", onStep, onLog); + onStep({ phase: "done", label: "execute withdrawal", note: "funds are back in your wallet", signature, ms }); + return { ok: true }; + } catch (e) { + const raw = e instanceof Error ? e.message : String(e); + if (RE_SETTLEMENT_PENDING.test(raw)) { + // Timing, not failure — the caller's retry loop (or the button) handles it. + onStep({ + phase: "submitting", + label: "settlement crossing from the rollup", + note: `receipt not on base chain yet${tag} — retrying shortly`, + }); + return { ok: false, error: "settlement pending", executePending: true }; + } + const c = classifyTxError(e); + onStep({ phase: "error", label: "execute withdrawal", note: c.message }); + return { ok: false, error: c.message, executePending: c.rejected ? false : true }; + } +} + +/** USDC compat wrapper (existing callers). */ +export async function executeWithdrawalStep(args: { + wallet: EnableWalletCtx; + usdcMint: string; + onStep: OnStep; + onLog: OnLog; + attempt?: number; +}): Promise<{ ok: boolean; error?: string; executePending?: boolean }> { + return executeWithdrawalToken({ wallet: args.wallet, tokenMint: args.usdcMint, symbol: "USDC", onStep: args.onStep, onLog: args.onLog, ...(args.attempt !== undefined ? { attempt: args.attempt } : {}) }); +} diff --git a/examples/predict/lib/hooks.ts b/examples/predict/lib/hooks.ts new file mode 100644 index 0000000..1ab0f9e --- /dev/null +++ b/examples/predict/lib/hooks.ts @@ -0,0 +1,474 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/hooks.ts — live data: price poll, owner stream, balances, latency log. +// THE HARD PART: the owner WS sends TWO frame types (basket vs metrics) that +// flash-v2 merges for us; the HUD numbers are REAL confirmMs values only — +// nothing here invents latency. GOTCHAS.md → "The WS sends two frame types" +// · "WS connection limits are real" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { PublicKey } from "@solana/web3.js"; +import { + subscribeOwner, + type BasketSnapshot, + type PositionMetrics, + type PriceInfo, +} from "flash-v2"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { baseConnection, flash } from "./flash"; +import { MAGIC_TRADE_PROGRAM } from "./session"; + +/** The magic-trade program on the ACTIVE network (ledger reads filter on it). */ +const MAGIC_TRADE_PROGRAM_ID = MAGIC_TRADE_PROGRAM.toBase58(); + +// ── price ticker ───────────────────────────────────────────────────────────── + +export function usePrice(symbol: string, intervalMs = 1000): { + price: PriceInfo | null; + drift: "up" | "down" | "flat"; +} { + const [price, setPrice] = useState(null); + const [drift, setDrift] = useState<"up" | "down" | "flat">("flat"); + const last = useRef(null); + + useEffect(() => { + // Market switch: drop the previous symbol's price immediately so the + // history buffer never receives a stale cross-market tick. + setPrice(null); + setDrift("flat"); + last.current = null; + let dead = false; + const tick = async () => { + try { + const p = await flash.price(symbol); + if (dead) return; + if (last.current !== null && p.priceUi !== last.current) { + setDrift(p.priceUi > last.current ? "up" : "down"); + } + last.current = p.priceUi; + setPrice(p); + } catch { + /* keep last price; next tick retries */ + } + }; + void tick(); + const timer = setInterval(tick, intervalMs); + return () => { dead = true; clearInterval(timer); }; + }, [symbol, intervalMs]); + + return { price, drift }; +} + +/** + * The tradeable market list, LIVE from Flash's own config (`GET /v2/tokens` — + * CDN-driven and hot-reloaded server-side): every non-stable token in the + * active pool is a market. New listings appear here automatically. + * Returns null until loaded (callers keep a static fallback meanwhile). + */ +export function useMarkets(): string[] | null { + const [markets, setMarkets] = useState(null); + useEffect(() => { + let dead = false; + flash.tokens() + .then((tokens) => { + if (dead) return; + const symbols = tokens.filter((t) => !t.isStable).map((t) => t.symbol); + if (symbols.length > 0) setMarkets(symbols); + }) + .catch(() => { /* fallback list keeps the selector usable */ }); + return () => { dead = true; }; + }, []); + return markets; +} + +/** Per-market leverage limits + trade spreads, LIVE from Flash's custody config. */ +export interface MarketLimits { + /** Lowest initial leverage the program accepts (UI floor, ≥1.1 for sanity). */ + minLeverage: number; + /** Highest initial leverage the program accepts for this market. */ + maxLeverage: number; + /** Exit spread the engine prices longs through (0.10 = 10%). THE number + * that makes a fresh fill read −10% "realizable now" — show it BEFORE + * the tap, and name it in the PnL breakdown. FX pairs are 0%. */ + spreadLongPct: number; + spreadShortPct: number; +} + +// custody.pricing values are fixed-point with 4 decimals (110000 = 11x — +// Flash's tiers carry a ×1.1 buffer over the advertised 10x/50x/100x/500x). +const LEVERAGE_SCALE = 10_000; + +/** + * Live leverage bounds for a market symbol: token config → custody (by mint) + * → pricing.minInitialLeverage/maxInitialLeverage. Cached per session; null + * until loaded (callers keep a conservative fallback meanwhile). Same + * philosophy as useMarkets: Flash changes limits → the UI follows, no code. + */ +const limitsCache = new Map(); +let custodyPricing: Map | null = null; + +export function useMarketLimits(marketSymbol: string): MarketLimits | null { + const [limits, setLimits] = useState(limitsCache.get(marketSymbol) ?? null); + useEffect(() => { + const cached = limitsCache.get(marketSymbol); + setLimits(cached ?? null); + if (cached) return; + let dead = false; + void (async () => { + try { + if (!custodyPricing || custodyPricing.size === 0) { + const res = await fetch(`${flash.network.apiBase}/raw/custodies`); + const json = (await res.json()) as + | Array<{ account?: { tokenMint?: string; pricing?: { minInitialLeverage?: number; maxInitialLeverage?: number; tradeSpreadLong?: number; tradeSpreadShort?: number } } }> + | { custodies?: unknown }; + const arr = Array.isArray(json) ? json : []; + const map = new Map(); + for (const c of arr) { + const a = c.account; + // the custody's token field is `tokenMint` (NOT `mint`) + if (a?.tokenMint && a.pricing?.maxInitialLeverage) { + map.set(a.tokenMint, { + min: (a.pricing.minInitialLeverage ?? LEVERAGE_SCALE) / LEVERAGE_SCALE, + max: a.pricing.maxInitialLeverage / LEVERAGE_SCALE, + // tradeSpread is in bps×1 (1000 = 10%): ÷1e4 → fraction + spreadL: (a.pricing.tradeSpreadLong ?? 0) / 10_000, + spreadS: (a.pricing.tradeSpreadShort ?? 0) / 10_000, + }); + } + } + // only cache a USEFUL result — an empty map must not wedge "loading…" + if (map.size > 0) custodyPricing = map; + } + if (!custodyPricing) return; + const tokens = await flash.tokens(); + if (dead) return; + const mint = tokens.find((t) => t.symbol.toUpperCase() === marketSymbol.toUpperCase())?.mintKey; + const p = mint ? custodyPricing.get(mint) : undefined; + if (p) { + const out: MarketLimits = { + minLeverage: Math.max(1.1, p.min), + maxLeverage: p.max, + spreadLongPct: p.spreadL, + spreadShortPct: p.spreadS, + }; + limitsCache.set(marketSymbol, out); + setLimits(out); + } + } catch { /* fallback stays; next mount retries */ } + })(); + return () => { dead = true; }; + }, [marketSymbol]); + return limits; +} + +// ── owner snapshot stream ──────────────────────────────────────────────────── + +export type StreamStatus = "connecting" | "open" | "reconnecting" | "polling" | "closed"; + +export function useOwner(owner: string | null): { + snapshot: BasketSnapshot | null; + /** true once we have heard ANYTHING for this owner (fetch or frame). */ + loaded: boolean; + status: StreamStatus; + refresh: () => Promise; +} { + const [snapshot, setSnapshot] = useState(null); + const [loaded, setLoaded] = useState(false); + const [status, setStatus] = useState("connecting"); + + useEffect(() => { + setSnapshot(null); + setLoaded(false); + if (!owner) { setStatus("closed"); return; } + setStatus("connecting"); + let dead = false; + + // Spec move: fetch client.owner(pubkey) on load so the setup wizard can + // decide instantly; the stream takes over from the first frame onward. + flash.owner(owner) + .then((snap) => { + if (dead) return; + setSnapshot((prev) => prev ?? snap); + setLoaded(true); + }) + .catch(() => { /* stream below retries via poll fallback */ }); + + const stream = subscribeOwner({ + owner, + network: flash.network, + onUpdate: (snap) => { + if (dead) return; + setSnapshot(snap); + setLoaded(true); + }, + onStatus: (s) => { if (!dead) setStatus(s); }, + }); + return () => { dead = true; stream.close(); }; + }, [owner]); + + const refresh = useCallback(async () => { + if (!owner) return; + try { + const snap = await flash.owner(owner); + setSnapshot(snap); + setLoaded(true); + } catch { /* non-fatal */ } + }, [owner]); + + return { snapshot, loaded, status, refresh }; +} + +/** All live positions for one market symbol out of a snapshot. */ +export function positionsFor(snapshot: BasketSnapshot | null, marketSymbol: string): PositionMetrics[] { + if (!snapshot) return []; + return Object.values(snapshot.positionMetrics ?? {}).filter( + (p) => p.marketSymbol?.toUpperCase() === marketSymbol.toUpperCase(), + ); +} + +// ── wallet balances (base chain) ───────────────────────────────────────────── + +export function useBalances(owner: string | null, usdcMint: string | null): { + sol: number | null; + usdc: number | null; + refresh: () => Promise; +} { + const [sol, setSol] = useState(null); + const [usdc, setUsdc] = useState(null); + + const refresh = useCallback(async () => { + if (!owner) { setSol(null); setUsdc(null); return; } + try { + const lamports = await baseConnection.getBalance(new PublicKey(owner)); + setSol(lamports / 1e9); + } catch { /* keep last */ } + if (usdcMint) { + try { + const res = await baseConnection.getParsedTokenAccountsByOwner( + new PublicKey(owner), + { mint: new PublicKey(usdcMint) }, + ); + const total = res.value.reduce((sum, acc) => { + const ui = acc.account.data.parsed?.info?.tokenAmount?.uiAmount as number | null | undefined; + return sum + (typeof ui === "number" ? ui : 0); + }, 0); + setUsdc(total); + } catch { /* keep last */ } + } + }, [owner, usdcMint]); + + useEffect(() => { + void refresh(); + // Background safety-net poll; balance-changing actions refresh explicitly + // for instant updates. A keyed RPC handles this cadence comfortably. + const timer = setInterval(() => { void refresh(); }, 6_000); + return () => clearInterval(timer); + }, [refresh]); + + return { sol, usdc, refresh }; +} + +// ── deposited balance: the USER DEPOSIT LEDGER, read like Flash's own infra ── + +export interface BasketAsset { + mint: string; + symbol: string; + decimals: number; + /** AVAILABLE on the rollup side (ledger − debits + pending), native units. */ + amountUi: number; +} + +export interface BasketBalance { + /** USDC available — the tradable "in basket" figure (compat alias of assets). */ + inBasketUsd: number; + /** EVERY asset with rollup-side availability — nothing orphans invisibly. */ + assets: BasketAsset[]; + /** Where the ledger was found ("er" once delegated, "base" before). */ + source: "er" | "base"; +} + +// Anchor discriminator for `UserDepositLedger` = sha256("account:UserDepositLedger")[0..8] +// (hex 3363adbe15330782). Precomputed so the browser needs no crypto/bs58 deps. +const LEDGER_DISC_B58 = "9bYPoR9mRKo"; +// Zero-copy layout (validated against 60 live mainnet ledgers, all 852 bytes): +// 8 disc | 1 bump | 7 pad | 32 owner @16 | u32 count @48 | 20 × {32 mint, u64 amount} @52. +const LEDGER_OWNER_OFFSET = 16; +const LEDGER_COUNT_OFFSET = 48; +const LEDGER_ENTRIES_OFFSET = 52; +const LEDGER_ENTRY_SIZE = 40; + +/** All ledger entries (mint → raw u64 amount), or null when no ledger exists. */ +async function fetchLedgerEntries(rpcUrl: string, owner: string): Promise | null> { + const res = await fetch(rpcUrl, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + jsonrpc: "2.0", id: 1, method: "getProgramAccounts", + params: [MAGIC_TRADE_PROGRAM_ID, { + encoding: "base64", + filters: [ + { memcmp: { offset: 0, bytes: LEDGER_DISC_B58 } }, + { memcmp: { offset: LEDGER_OWNER_OFFSET, bytes: owner } }, + ], + }], + }), + }); + const json = (await res.json()) as { result?: Array<{ account: { data: [string, string] } }> }; + const acct = json.result?.[0]; + if (!acct) return null; + const buf = Uint8Array.from(atob(acct.account.data[0]), (c) => c.charCodeAt(0)); + const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); + const count = view.getUint32(LEDGER_COUNT_OFFSET, true); + const out = new Map(); + for (let i = 0; i < Math.min(count, 20); i++) { + const off = LEDGER_ENTRIES_OFFSET + i * LEDGER_ENTRY_SIZE; + const mint = new PublicKey(buf.subarray(off, off + 32)).toBase58(); + const amount = Number(view.getBigUint64(off + 32, true)); + out.set(mint, (out.get(mint) ?? 0) + amount); + } + return out; +} + + + +/** + * The owner's AVAILABLE USDC (GOTCHAS §20): + * + * available = ledger.deposits − basket.debits + basket.pendingCredits + * + * V2 is double-entry: the ledger records CUMULATIVE deposits (withdrawals do + * NOT decrement it), the basket's debits/pendingCredits record all internal + * churn, and only the three together net to the balance. A fully-withdrawn + * account nets exactly $0.00; while funded, the same math gives the true + * positive balance. All three figures MUST come from the same coherent + * source — the ER-fed store (mixing chains on a half-delegated account breaks + * the invariant; clamp at 0). + */ +export function useBasketBalance( + owner: string | null, + basketPubkey: string | null, + usdcMint: string | null, +): { bal: BasketBalance | null; refresh: () => Promise } { + const [bal, setBal] = useState(null); + const tokenMeta = useRef | null>(null); + // Monotonic request id: a newer read (poll OR an explicit post-trade refresh) + // supersedes an in-flight older one, so stale data never lands. + const reqId = useRef(0); + + const refresh = useCallback(async () => { + if (!owner || !usdcMint) { setBal(null); return; } + const id = ++reqId.current; + try { + // 1) cumulative deposits — ALL mints from the ledger (ER → base fallback) + let source: "er" | "base" = "er"; + let ledger = await fetchLedgerEntries(flash.network.erRpc, owner); + if (ledger === null) { + ledger = await fetchLedgerEntries(flash.network.baseRpc, owner); + source = "base"; + } + if (id !== reqId.current) return; + if (ledger === null) { setBal({ inBasketUsd: 0, assets: [], source: "base" }); return; } + // 2) the basket's counter-entries (server-decoded from the ER store) + const debits = new Map(); + const pending = new Map(); + if (basketPubkey) { + const raw = await flash.rawBasket(basketPubkey); + if (id !== reqId.current) return; + const acct = raw.account as { + debits?: Array<{ mint: string; amount: number | string }>; + pendingCredits?: Array<{ mint: string; amount: number | string }>; + }; + for (const r of acct.debits ?? []) debits.set(r.mint, (debits.get(r.mint) ?? 0) + Number(r.amount)); + for (const r of acct.pendingCredits ?? []) pending.set(r.mint, (pending.get(r.mint) ?? 0) + Number(r.amount)); + } + // 3) symbols/decimals from Flash's live token config (cached across reads) + if (!tokenMeta.current) { + try { + tokenMeta.current = new Map( + (await flash.tokens()).map((t) => [t.mintKey, { symbol: t.symbol, decimals: t.decimals }]), + ); + } catch { tokenMeta.current = new Map(); } + } + if (id !== reqId.current) return; + // 4) available per mint = ledger − debits + pending (clamped), native units + const mints = new Set([...ledger.keys(), ...debits.keys(), ...pending.keys()]); + const assets: BasketAsset[] = []; + for (const mint of mints) { + const meta = tokenMeta.current.get(mint) ?? { symbol: mint.slice(0, 4) + "…", decimals: mint === usdcMint ? 6 : 9 }; + const rawAvail = (ledger.get(mint) ?? 0) - (debits.get(mint) ?? 0) + (pending.get(mint) ?? 0); + const amountUi = Math.max(0, rawAvail) / 10 ** meta.decimals; + if (amountUi > 0) assets.push({ mint, symbol: meta.symbol, decimals: meta.decimals, amountUi }); + } + assets.sort((a, b) => (a.mint === usdcMint ? -1 : b.mint === usdcMint ? 1 : a.symbol.localeCompare(b.symbol))); + const usdc = assets.find((a) => a.mint === usdcMint)?.amountUi ?? 0; + setBal({ inBasketUsd: usdc, assets, source }); + } catch { /* keep last good read; next read retries */ } + }, [owner, basketPubkey, usdcMint]); + + useEffect(() => { + setBal(null); // clear on owner/basket change — don't show another owner's balance + reqId.current++; // invalidate any in-flight read + void refresh(); + const timer = setInterval(() => { void refresh(); }, 5_000); + return () => clearInterval(timer); + }, [refresh]); + + return { bal, refresh }; +} + +/** Resolve the USDC mint once via /tokens (deposits need the MINT, not "USDC"). */ +export function useUsdcMint(): string | null { + const [mint, setMint] = useState(null); + useEffect(() => { + let dead = false; + flash.tokens() + .then((tokens) => { + if (dead) return; + const usdc = tokens.find((t) => t.symbol.toUpperCase() === "USDC"); + setMint(usdc?.mintKey ?? null); + }) + .catch(() => { /* tokens() retried implicitly when wizard runs */ }); + return () => { dead = true; }; + }, []); + return mint; +} + +// ── latency log (the HUD's data) ───────────────────────────────────────────── + +export interface LatencyEntry { + id: string; + /** What was tapped, e.g. "LONG 5×", "FLATTEN", "init-basket". */ + action: string; + /** Which chain confirmed it — "er" rows are the headline numbers. */ + chain: "er" | "base"; + /** REAL wall-clock submit→confirmed milliseconds (flash-v2 confirmMs). */ + ms: number; + /** Measured send round-trip (≈ one wire trip) — splits network vs rollup. */ + sendMs?: number; + signature: string; + at: number; + /** Trade facts captured at action time (history rows render these). + * pnlUi = the WITH-FEE figure at the moment of close (≈ realized). */ + trade?: { + market: string; + side: "LONG" | "SHORT"; + entryUi: number | null; + collateralUi: number | null; + pnlUi: number | null; + }; +} + +export function useLatencyLog(): { + entries: LatencyEntry[]; + add: (entry: Omit) => void; +} { + const [entries, setEntries] = useState([]); + const add = useCallback((entry: Omit) => { + const id = typeof crypto !== "undefined" && "randomUUID" in crypto + ? crypto.randomUUID() + : `${Date.now()}-${Math.random()}`; + setEntries((prev) => [{ ...entry, id, at: Date.now() }, ...prev].slice(0, 50)); + }, []); + return { entries, add }; +} diff --git a/examples/predict/lib/payoff.ts b/examples/predict/lib/payoff.ts new file mode 100644 index 0000000..40a120a --- /dev/null +++ b/examples/predict/lib/payoff.ts @@ -0,0 +1,142 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/payoff.ts — the honest math behind a round: pure functions, no React. +// THE HARD PART: the live payoff line is LOCAL math (never quote-per-tick — +// rate limits + jitter); ONE real quote at the commit gate overrides every +// estimate and becomes the round's locked numbers. Fees scale with size, so +// break-even move % is leverage-INDEPENDENT; liquidation distance ≈ 92%/L. +// GOTCHAS.md → "Three numbers that surprise you on your first real fill" +// ───────────────────────────────────────────────────────────────────────────── + +/** Conservative entry+exit fee estimate (fraction of SIZE) until a real quote lands. */ +export const EST_ROUNDTRIP_FEE_RATE = 0.0016; // 0.08% open + 0.08% close + +/** The protocol's liquidation buffer factor (GOTCHAS §20 display formula). */ +export const LIQ_FACTOR = 0.92; + +/** Timeframes a round can run. Leverage profiles are conservative-moderate: + * break-even move is leverage-independent, and liquidation distance (≈92%/L) + * is hardest to reach in short windows — so short rounds can carry more + * leverage without raising knockout risk. Clamped to live market caps. */ +export const TIMEFRAMES = [ + { id: "5m", label: "5 min", ms: 5 * 60_000, leverage: 5 }, + { id: "15m", label: "15 min", ms: 15 * 60_000, leverage: 3.3 }, + { id: "1h", label: "1 hour", ms: 60 * 60_000, leverage: 2 }, +] as const; + +export type TimeframeId = (typeof TIMEFRAMES)[number]["id"]; + +export function timeframe(id: TimeframeId) { + const tf = TIMEFRAMES.find((t) => t.id === id); + if (!tf) throw new Error(`unknown timeframe: ${id}`); + return tf; +} + +/** Dollars of PnL per 1% move in the chosen direction. */ +export function pnlPerPct(stakeUsd: number, leverage: number): number { + return (stakeUsd * leverage) / 100; +} + +/** Move (in %) the price must travel your way before fees are covered. + * feesUsd defaults to the local estimate when no quote exists yet. */ +export function breakEvenMovePct(stakeUsd: number, leverage: number, feesUsd?: number): number { + const size = stakeUsd * leverage; + const fees = feesUsd ?? size * EST_ROUNDTRIP_FEE_RATE; + if (size <= 0) return 0; + return (fees / size) * 100; +} + +/** Adverse move (in %) that knocks the round out early (liquidation). */ +export function knockoutMovePct(leverage: number): number { + if (leverage <= 0) return 100; + return (LIQ_FACTOR / leverage) * 100; +} + +/** Liquidation price from entry — the GOTCHAS §20 approximation. */ +export function liqPriceApprox(entry: number, leverage: number, side: "LONG" | "SHORT"): number { + const dist = LIQ_FACTOR / leverage; + return side === "LONG" ? entry * (1 - dist) : entry * (1 + dist); +} + +/** Hourly borrow drag in USD for a given size (marginFeePercentage is %/hr). */ +export function borrowDragUsd(sizeUsd: number, marginFeePctHourly: number, hours: number): number { + return sizeUsd * (marginFeePctHourly / 100) * hours; +} + +/** Everything the ticket renders live, from local math alone. */ +export interface PayoffFacts { + sizeUsd: number; + perPctUsd: number; + breakEvenPct: number; + knockoutPct: number; + estFeesUsd: number; +} + +export function payoffFacts(stakeUsd: number, leverage: number): PayoffFacts { + const sizeUsd = stakeUsd * leverage; + return { + sizeUsd, + perPctUsd: pnlPerPct(stakeUsd, leverage), + breakEvenPct: breakEvenMovePct(stakeUsd, leverage), + knockoutPct: knockoutMovePct(leverage), + estFeesUsd: sizeUsd * EST_ROUNDTRIP_FEE_RATE, + }; +} + +/** The numbers locked at commit — taken from the SIGNED open's response, never + * from a stale preview. PancakeSwap's #1 trust failure is a displayed payout + * that drifts before settle; ours cannot, because these lock here. */ +export interface LockedQuote { + entryPrice: number; + liqPrice: number; + entryFeeUsd: number; + /** hourly borrow %, straight from the API */ + marginFeePctHourly: number; + perPctUsd: number; + breakEvenPct: number; + /** EFFECTIVE size from the venue's fill math (GOTCHAS §21) — not stake×lev. */ + sizeUsd: number; +} + +/** Build the locked quote from an open-position response — the ONE place the + * API's strings become the round's numbers (review and confirm both use it). */ +export function lockedQuoteFrom( + res: { + newEntryPrice: string; + newLiquidationPrice: string; + entryFee: string; + marginFeePercentage: string; + outputAmountUi: string; + }, + stakeUsd: number, + leverage: number, + side: "LONG" | "SHORT", +): LockedQuote { + const entryFeeUsd = Number(res.entryFee) || 0; + const entryPrice = Number(res.newEntryPrice) || 0; + // The venue's actual fill: output tokens × entry — the spread reshapes it. + const filled = (Number(res.outputAmountUi) || 0) * entryPrice; + const sizeUsd = filled > 0 ? filled : stakeUsd * leverage; + // Knockout is OURS to compute (GOTCHAS §20): the builder's + // newLiquidationPrice is spread-valued and degenerates (it can land on the + // wrong side of entry). Effective leverage = filled size / stake. + const effLev = stakeUsd > 0 ? sizeUsd / stakeUsd : leverage; + return { + entryPrice, + liqPrice: liqPriceApprox(entryPrice, effLev, side), + entryFeeUsd, + marginFeePctHourly: Number(res.marginFeePercentage) || 0, + perPctUsd: sizeUsd / 100, + breakEvenPct: sizeUsd > 0 ? ((entryFeeUsd * 2) / sizeUsd) * 100 : 0, + sizeUsd, + }; +} + +/** PnL in USD at a given move % (for drawing the payoff line). */ +export function pnlAtMovePct(stakeUsd: number, leverage: number, movePct: number, feesUsd?: number): number { + const size = stakeUsd * leverage; + const fees = feesUsd ?? size * EST_ROUNDTRIP_FEE_RATE; + const gross = (size * movePct) / 100; + const net = gross - fees; + // Loss is capped at stake: the position liquidates before losing more. + return Math.max(net, -stakeUsd); +} diff --git a/examples/predict/lib/rounds.ts b/examples/predict/lib/rounds.ts new file mode 100644 index 0000000..4feccdc --- /dev/null +++ b/examples/predict/lib/rounds.ts @@ -0,0 +1,223 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/rounds.ts — rounds are a CACHE; chain positions are the truth. +// THE HARD PART: reconciliation. Positions carry NO timestamp, so the join +// key is (marketSymbol, sideUi) — valid because one position per market+side +// exists at a time. Settlement triggers on OBSERVED state (snapshot + clock), +// never a bare setTimeout; "settling" status + an in-flight set guard against +// double-close. GOTCHAS.md → "The 97% full-close threshold" (we always send +// "0" = explicit FULL close) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +import type { BasketSnapshot, PositionMetrics } from "flash-v2"; +import type { TimeframeId } from "./payoff"; +import type { LockedQuote } from "./payoff"; + +export const SCHEMA_VERSION = 1; + +/** New rounds are invisible to reconciliation for this long — the position + * needs a beat to appear in the owner stream after the ER confirm. */ +export const PROPAGATION_GRACE_MS = 5_000; + +export type Side = "LONG" | "SHORT"; + +export type RoundStatus = + | "active" // position open, clock running (or adopted: no clock) + | "settling" // close dispatched, awaiting confirm + | "settled" // we closed it; result recorded + | "closed-elsewhere"; // position vanished without our close (other device / liquidated) + +export interface RoundResult { + pnlUsd: number; + won: boolean; + settledAt: number; + /** ER tx signature of the close — the round's receipt. */ + signature?: string; +} + +export interface Round { + id: string; + market: string; // marketSymbol, e.g. "SOL" + side: Side; + stakeUsd: number; + leverage: number; + timeframe: TimeframeId | null; // null = adopted position, settle-now only + placedAt: number; + expiresAt: number | null; // null = adopted, no clock + quote: LockedQuote; + status: RoundStatus; + result?: RoundResult; + /** The user saw and dismissed the result card (ack gates new rounds). */ + acked?: boolean; +} + +interface StoredRounds { + v: number; + rounds: Round[]; +} + +const roundsKey = (owner: string) => `predict-rounds-${owner}`; + +export function loadRounds(owner: string): Round[] { + if (typeof window === "undefined") return []; + try { + const raw = window.localStorage.getItem(roundsKey(owner)); + if (!raw) return []; + const parsed = JSON.parse(raw) as StoredRounds; + if (parsed.v !== SCHEMA_VERSION || !Array.isArray(parsed.rounds)) { + window.localStorage.removeItem(roundsKey(owner)); + return []; + } + return parsed.rounds; + } catch { + return []; + } +} + +export function saveRounds(owner: string, rounds: Round[]): void { + if (typeof window === "undefined") return; + const stored: StoredRounds = { v: SCHEMA_VERSION, rounds }; + window.localStorage.setItem(roundsKey(owner), JSON.stringify(stored)); +} + +export function newRoundId(): string { + return `r_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`; +} + +/** A live position with no matching round — opened elsewhere (or a lost + * cache). Surfaced for adoption: settle-now only, no invented clock. */ +export interface Orphan { + market: string; + side: Side; + metrics: PositionMetrics; +} + +export interface ReconcileResult { + /** Expired, position still on chain → dispatch full close. */ + toSettle: Round[]; + /** Round says active, chain says gone → mark closed-elsewhere. */ + closedElsewhere: Round[]; + /** Chain position with no round → offer adoption. */ + orphans: Orphan[]; +} + +const sideOf = (p: PositionMetrics): Side => (p.sideUi.toUpperCase() === "LONG" ? "LONG" : "SHORT"); + +/** The round↔chain join: one position per market+side exists at a time. */ +export function positionFor(snapshot: BasketSnapshot, market: string, side: Side): PositionMetrics | undefined { + return Object.values(snapshot.positionMetrics).find( + (p) => p.marketSymbol === market && sideOf(p) === side && Number(p.sizeUsdUi) > 0, + ); +} + +/** Pure classification — no I/O, no status writes. Rounds inside the + * propagation grace window are left untouched in BOTH directions. */ +export function reconcile(rounds: Round[], snapshot: BasketSnapshot | null, nowMs: number): ReconcileResult { + const result: ReconcileResult = { toSettle: [], closedElsewhere: [], orphans: [] }; + if (!snapshot) return result; + + const active = rounds.filter((r) => r.status === "active"); + + for (const round of active) { + if (nowMs - round.placedAt < PROPAGATION_GRACE_MS) continue; + const pos = positionFor(snapshot, round.market, round.side); + if (!pos) { + result.closedElsewhere.push(round); + } else if (round.expiresAt !== null && nowMs >= round.expiresAt) { + result.toSettle.push(round); + } + } + + const claimed = new Set( + rounds + .filter((r) => r.status === "active" || r.status === "settling") + .map((r) => `${r.market}|${r.side}`), + ); + for (const p of Object.values(snapshot.positionMetrics)) { + if (Number(p.sizeUsdUi) <= 0) continue; + const key = `${p.marketSymbol}|${sideOf(p)}`; + if (!claimed.has(key)) result.orphans.push({ market: p.marketSymbol, side: sideOf(p), metrics: p }); + } + + return result; +} + +/** Status transitions, applied immutably. */ +export function withStatus(rounds: Round[], id: string, status: RoundStatus): Round[] { + return rounds.map((r) => (r.id === id ? { ...r, status } : r)); +} + +export function withResult(rounds: Round[], id: string, result: RoundResult): Round[] { + return rounds.map((r) => (r.id === id ? { ...r, status: "settled" as const, result } : r)); +} + +export function withAck(rounds: Round[], id: string): Round[] { + return rounds.map((r) => (r.id === id ? { ...r, acked: true } : r)); +} + +/** Results waiting to be seen — drives the SettleCard queue, oldest first. */ +export function pendingAcks(rounds: Round[]): Round[] { + return rounds + .filter((r) => (r.status === "settled" || r.status === "closed-elsewhere") && !r.acked) + .sort((a, b) => (a.result?.settledAt ?? a.placedAt) - (b.result?.settledAt ?? b.placedAt)); +} + +/** Adopt an orphan as a clockless round so it can be watched and settled. */ +export function adoptOrphan(orphan: Orphan, nowMs: number): Round { + const collateral = Number(orphan.metrics.collateralUsdUi) || 0; + const size = Number(orphan.metrics.sizeUsdUi) || 0; + const entry = Number(orphan.metrics.entryPriceUi) || 0; + const leverage = collateral > 0 ? size / collateral : 1; + return { + id: newRoundId(), + market: orphan.market, + side: orphan.side, + stakeUsd: collateral, + leverage, + timeframe: null, + placedAt: nowMs, + expiresAt: null, + quote: { + entryPrice: entry, + liqPrice: Number(orphan.metrics.liquidationPriceUi) || 0, + entryFeeUsd: 0, + marginFeePctHourly: 0, + perPctUsd: size / 100, + breakEvenPct: 0, + sizeUsd: size, + }, + status: "active", + }; +} + +export interface RoundStats { + played: number; + wins: number; + losses: number; + winRatePct: number; + totalPnlUsd: number; + streak: number; // positive = current win streak, negative = loss streak +} + +export function roundStats(rounds: Round[]): RoundStats { + const settled = rounds + .filter((r) => r.status === "settled" && r.result) + .sort((a, b) => (a.result?.settledAt ?? 0) - (b.result?.settledAt ?? 0)); + const wins = settled.filter((r) => r.result?.won).length; + const losses = settled.length - wins; + let streak = 0; + for (let i = settled.length - 1; i >= 0; i--) { + const won = settled[i]?.result?.won ?? false; + if (streak === 0) streak = won ? 1 : -1; + else if (won && streak > 0) streak++; + else if (!won && streak < 0) streak--; + else break; + } + return { + played: settled.length, + wins, + losses, + winRatePct: settled.length ? (wins / settled.length) * 100 : 0, + totalPnlUsd: settled.reduce((s, r) => s + (r.result?.pnlUsd ?? 0), 0), + streak, + }; +} diff --git a/examples/predict/lib/session.ts b/examples/predict/lib/session.ts new file mode 100644 index 0000000..ab2fa31 --- /dev/null +++ b/examples/predict/lib/session.ts @@ -0,0 +1,268 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/session.ts — REAL session keys via @magicblock-labs/gum-sdk (v3). +// THE HARD PART: there is NO server endpoint for sessions — the CLIENT mints a +// SessionTokenV2 PDA (seeds ["session_token_v2", target, signer, authority] — +// note the _v2!). buildSessionTransaction lets lib/enable.ts bundle it into +// the ONE Enable One-Click Trading approval; trades then auto-sign. +// SESSION-KEYS.md has the full story. GOTCHAS.md → "Session keys" entry. +// ───────────────────────────────────────────────────────────────────────────── + +import { BN } from "@coral-xyz/anchor"; +import { SessionTokenManager } from "@magicblock-labs/gum-sdk"; +import { + Connection, + Keypair, + PublicKey, + type Transaction, + type VersionedTransaction, +} from "@solana/web3.js"; + +/** Gum session-keys program. */ +export const SESSION_KEYS_PROGRAM = new PublicKey("KeyspM2ssCJbqUhQ4k7sveSiY4WjnYsrXkC8oDbwde5"); + +/** + * magic-trade (Flash V2 on MagicBlock) program — the session target. + * Mainnet: FTv2RxXarPfNta45HTTMVaGvjzsGg27FXJ3hEKWBhrzV + * (verified by decoding a mainnet init-basket transaction from /v2/transaction-builder) + */ +/** magic-trade program (mainnet) — decoded from live builder txs. */ +export const MAGIC_TRADE_PROGRAM = new PublicKey("FTv2RxXarPfNta45HTTMVaGvjzsGg27FXJ3hEKWBhrzV"); + + +/** localStorage key for the persisted session (signer secret + token PDA). */ +export const SESSION_STORAGE_KEY = "predict-session"; + +/** Minimal wallet shape we need (matches wallet-adapter's AnchorWallet). */ +export interface SessionWallet { + publicKey: PublicKey; + signTransaction(tx: T): Promise; + signAllTransactions(txs: T[]): Promise; +} + +export interface LoadedSession { + /** Ephemeral signer — auto-signs every trade, no popup. */ + keypair: Keypair; + /** SessionTokenV2 PDA (base58) — passed as `sessionToken` to the API. */ + token: string; + /** The real wallet that authorized the session (base58). */ + authority: string; + /** Unix seconds — the program rejects the session after this. */ + validUntil: number; +} + +interface StoredSession { + secretKey: number[]; + token: string; + authority: string; + validUntil: number; +} + +/** + * Derive the SessionTokenV2 PDA. Live-verified on-chain: 77 session tokens + * targeting magic-trade all match seeds + * ["session_token_v2", target_program, session_signer, authority]. + */ +export function deriveSessionToken( + authority: PublicKey, + sessionSigner: PublicKey +): PublicKey { + const [pda] = PublicKey.findProgramAddressSync( + [ + new TextEncoder().encode("session_token_v2"), + MAGIC_TRADE_PROGRAM.toBytes(), + sessionSigner.toBytes(), + authority.toBytes(), + ], + SESSION_KEYS_PROGRAM, + ); + return pda; +} + +/** Load the persisted session if it exists, belongs to `authority`, and has + * ≥60s of validity left. Anything else is cleared. Browser-only. */ +export function loadSession(authority?: string): LoadedSession | null { + if (typeof window === "undefined") return null; + const raw = window.localStorage.getItem(SESSION_STORAGE_KEY); + if (!raw) return null; + try { + const stored = JSON.parse(raw) as StoredSession; + const fresh = stored.validUntil > Date.now() / 1000 + 60; + const mine = !authority || stored.authority === authority; + if (!fresh || !mine) { + if (!fresh) clearSession(); + return null; + } + return { + keypair: Keypair.fromSecretKey(Uint8Array.from(stored.secretKey)), + token: stored.token, + authority: stored.authority, + validUntil: stored.validUntil, + }; + } catch { + clearSession(); + return null; + } +} + +export function clearSession(): void { + if (typeof window !== "undefined") window.localStorage.removeItem(SESSION_STORAGE_KEY); +} + +/** Poll a signature to confirmation (same approach as flash-v2/sign.ts). */ +async function confirmSignature(connection: Connection, signature: string, timeoutMs = 60_000): Promise { + const started = Date.now(); + for (;;) { + const status = (await connection.getSignatureStatuses([signature])).value[0]; + if (status) { + if (status.err) throw new Error(`on-chain error (${signature}): ${JSON.stringify(status.err)}`); + const level = status.confirmationStatus; + if (level === "confirmed" || level === "finalized") return; + } + if (Date.now() - started > timeoutMs) throw new Error(`confirmation timeout (${signature})`); + await new Promise((r) => setTimeout(r, 400)); + } +} + +/** Everything `enableOneClickTrading` needs to bundle the session into ONE + * signAllTransactions sheet: the tx (blockhash set, ephemeral key already + * partial-signed — the wallet only adds its own signature) plus the pieces + * to persist once it confirms. */ +export interface BuiltSessionTransaction { + tx: Transaction; + sessionSigner: Keypair; + /** SessionTokenV2 PDA (base58). */ + sessionToken: string; + validUntil: number; +} + +/** + * Build (don't sign-with-the-wallet, don't send) a `create_session_v2` tx: + * generate an ephemeral keypair, derive the SessionTokenV2 PDA, set blockhash + * + fee payer, and partial-sign with the ephemeral key. `topUpSol` moves SOL + * from the wallet to the session signer so trades pay their own fees. + */ +export async function buildSessionTransaction( + wallet: SessionWallet, + connection: Connection, + opts: { validHours?: number; topUpSol?: number } = {}, +): Promise { + const { validHours = 24, topUpSol = 0.01 } = opts; + const sessionSigner = Keypair.generate(); + const validUntil = Math.floor(Date.now() / 1000) + Math.floor(validHours * 3600); + const sessionToken = deriveSessionToken(wallet.publicKey, sessionSigner.publicKey); + const targetProgram = MAGIC_TRADE_PROGRAM; + + const manager = new SessionTokenManager(wallet, connection); + const tx: Transaction = await manager.program.methods + .createSessionV2(true, new BN(validUntil), new BN(Math.round(topUpSol * 1e9))) + .accountsPartial({ + sessionToken, + sessionSigner: sessionSigner.publicKey, + feePayer: wallet.publicKey, + authority: wallet.publicKey, + targetProgram, + }) + .transaction(); + + tx.feePayer = wallet.publicKey; + tx.recentBlockhash = (await connection.getLatestBlockhash("confirmed")).blockhash; + tx.partialSign(sessionSigner); // ephemeral key co-signs; wallet signs later + return { tx, sessionSigner, sessionToken: sessionToken.toBase58(), validUntil }; +} + +/** Persist a confirmed session to localStorage and return the live shape. */ +export function persistSession(args: { + sessionSigner: Keypair; + sessionToken: string; + authority: string; + validUntil: number; +}): LoadedSession { + const stored: StoredSession = { + secretKey: Array.from(args.sessionSigner.secretKey), + token: args.sessionToken, + authority: args.authority, + validUntil: args.validUntil, + }; + window.localStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(stored)); + return { + keypair: args.sessionSigner, + token: stored.token, + authority: stored.authority, + validUntil: stored.validUntil, + }; +} + +/** + * Compat wrapper (build → ONE wallet popup → send → confirm → persist). + * `enableOneClickTrading` (lib/enable.ts) uses buildSessionTransaction + * directly so the session rides the same signAllTransactions sheet as the + * basket/ledger/delegate/deposit txs. + */ +export async function createSession(opts: { + wallet: SessionWallet; + connection: Connection; + validHours?: number; + topUpSol?: number; +}): Promise<{ session: LoadedSession; signature: string; confirmMs: number }> { + const { wallet, connection, validHours, topUpSol } = opts; + const built = await buildSessionTransaction(wallet, connection, { validHours, topUpSol }); + const signed = await wallet.signTransaction(built.tx); // the ONE popup + const started = Date.now(); // measure submit→confirmed only + const signature = await connection.sendRawTransaction(signed.serialize(), { maxRetries: 3 }); + await confirmSignature(connection, signature); + const confirmMs = Date.now() - started; + const session = persistSession({ + sessionSigner: built.sessionSigner, + sessionToken: built.sessionToken, + authority: wallet.publicKey.toBase58(), + validUntil: built.validUntil, + }); + return { session, signature, confirmMs }; +} + +/** + * Revoke the session on-chain (rent + leftover top-up refund to the wallet). + * `revoke_session_v2` needs NO wallet signature — the session signer itself + * pays the fee, so even ending a session is popup-free. Always clears local + * storage, even if the on-chain revoke fails (e.g. already expired + closed). + */ +export async function revokeSession( + session: LoadedSession, + connection: Connection, +): Promise<{ signature: string; confirmMs: number } | null> { + try { + const authority = new PublicKey(session.authority); + // Provider wallet here is only a type requirement — nothing is signed by it. + const manager = new SessionTokenManager( + { + publicKey: session.keypair.publicKey, + signTransaction: async (t: T) => t, + signAllTransactions: async (t: T[]) => t, + } as SessionWallet, + connection, + ); + const tx: Transaction = await manager.program.methods + .revokeSessionV2() + .accountsPartial({ + sessionToken: new PublicKey(session.token), + // Must match tx.feePayer below — the SESSION key pays for its own + // revocation (popup-free); a mismatch fails the signer constraint. + feePayer: session.keypair.publicKey, + authority, + }) + .transaction(); + tx.feePayer = session.keypair.publicKey; + tx.recentBlockhash = (await connection.getLatestBlockhash("confirmed")).blockhash; + tx.sign(session.keypair); + const started = Date.now(); + const signature = await connection.sendRawTransaction(tx.serialize(), { maxRetries: 3 }); + await confirmSignature(connection, signature); + clearSession(); + return { signature, confirmMs: Date.now() - started }; + // ONLY a confirmed revoke clears storage — wiping on failure would orphan + // a LIVE key with no way to retry the revoke (audit find). + } catch { + return null; // revoke failed — session stays stored so you can retry; + // worst case the token expires on its own at validUntil + } +} diff --git a/examples/predict/lib/signer.ts b/examples/predict/lib/signer.ts new file mode 100644 index 0000000..84a53a5 --- /dev/null +++ b/examples/predict/lib/signer.ts @@ -0,0 +1,40 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/signer.ts — the trading signer: the SESSION keypair signs every tap. +// THE HARD PART: routing — trading txs sign+submit to network.erRpc and NEVER +// touch the blockhash (the API pre-signed it). Setup txs are handled by +// lib/enable.ts on the base chain behind ONE wallet approval. +// GOTCHAS.md → "Two chains, one flow" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +import { signAndSend, type NetworkConfig, type SendResult } from "flash-v2"; +import type { LoadedSession, SessionWallet } from "./session"; + +/** Everything a tap needs to know about "who signs and where it goes". */ +export interface ActiveSigner { + /** Basket owner (base58) — the connected wallet. */ + owner: string; + /** Sign + submit a TRADING tx → Ephemeral Rollup. Popup-free. */ + sendTrade(transactionBase64: string): Promise; + /** Extra request fields so the API builds for the session signer. */ + tradeFields: { signer: string; sessionToken: string }; +} + +/** + * One-Click Trading signer: the wallet approved setup ONCE (lib/enable.ts); + * every TRADE auto-signs with the ephemeral session key — `signer` + + * `sessionToken` ride along in the request so the API builds for it. + */ +export function makeSessionSigner( + wallet: SessionWallet, + session: LoadedSession, + network: NetworkConfig, +): ActiveSigner { + return { + owner: wallet.publicKey.toBase58(), + sendTrade: (tx64) => signAndSend(network.erRpc, tx64, session.keypair, { skipPreflight: true }), + tradeFields: { + signer: session.keypair.publicKey.toBase58(), + sessionToken: session.token, + }, + }; +} diff --git a/examples/predict/lib/stream.tsx b/examples/predict/lib/stream.tsx new file mode 100644 index 0000000..4662012 --- /dev/null +++ b/examples/predict/lib/stream.tsx @@ -0,0 +1,81 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/stream.tsx — ONE owner stream for the whole app, shared via context. +// THE HARD PART: the WS caps at 5 connections per owner (429) — per-component +// subscriptions are how you hit it. One subscribeOwner lives here; everything +// reads through useStream(). flash-v2 merges basket+metrics frames and falls +// back to polling for us. GOTCHAS.md → "WS connection limits are real" · +// "The WS sends two frame types" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { createContext, useContext, useEffect, useMemo, useRef, useState, type ReactNode } from "react"; +import { subscribeOwner, type BasketSnapshot, type OwnerStream } from "flash-v2"; +import { flash } from "./flash"; + +export type StreamStatus = "connecting" | "open" | "reconnecting" | "polling" | "closed"; + +export interface StreamCtx { + snapshot: BasketSnapshot | null; + loaded: boolean; + status: StreamStatus; + refresh(): Promise; +} + +const StreamContext = createContext({ + snapshot: null, + loaded: false, + status: "closed", + refresh: async () => {}, +}); + +export function StreamProvider({ owner, children }: { owner: string | null; children: ReactNode }) { + const [snapshot, setSnapshot] = useState(null); + const [loaded, setLoaded] = useState(false); + const [status, setStatus] = useState("closed"); + const streamRef = useRef(null); + + useEffect(() => { + setSnapshot(null); + setLoaded(false); + if (!owner) { + setStatus("closed"); + return; + } + setStatus("connecting"); + const stream = subscribeOwner({ + owner, + network: flash.network, + onUpdate: (snap) => { + setSnapshot(snap); + setLoaded(true); + }, + onStatus: (s) => setStatus(s), + }); + streamRef.current = stream; + return () => { + stream.close(); + streamRef.current = null; + }; + }, [owner]); + + const value = useMemo( + () => ({ + snapshot, + loaded, + status, + refresh: async () => { + if (!owner) return; + const snap = await flash.owner(owner); + setSnapshot(snap); + setLoaded(true); + }, + }), + [snapshot, loaded, status, owner], + ); + + return {children}; +} + +export function useStream(): StreamCtx { + return useContext(StreamContext); +} diff --git a/examples/predict/lib/use-price-history.ts b/examples/predict/lib/use-price-history.ts new file mode 100644 index 0000000..fe51689 --- /dev/null +++ b/examples/predict/lib/use-price-history.ts @@ -0,0 +1,79 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/use-price-history.ts — client-side ring buffer of price ticks feeding +// the canvas chart. THE HARD PART: pushing on every POLL (not every CHANGE) — +// usePrice emits a fresh PriceInfo object per tick, so depending on object +// identity makes a flat price still drift the line rightward. +// GOTCHAS.md → (client-side only; no API gotchas) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import type { PriceInfo } from "flash-v2"; +import { useEffect, useState } from "react"; +import { flash } from "./flash"; + +/** + * Seed the chart with REAL history on market switch — Pyth's public + * Benchmarks API (TradingView shim) serves minute closes for every feed the + * app trades, keyed by the SAME `pythTicker` Flash's token config carries. + * One fetch per switch; the live 1s poll keeps appending after the seed, so + * the line has shape immediately instead of a flat creep. + */ +async function fetchSeed(market: string, points: number): Promise { + try { + const tokens = await flash.tokens(); + const ticker = tokens.find((t) => t.symbol.toUpperCase() === market.toUpperCase())?.pythTicker; + if (!ticker) return null; + const to = Math.floor(Date.now() / 1000); + const from = to - points * 60; + const url = + `https://benchmarks.pyth.network/v1/shims/tradingview/history` + + `?symbol=${encodeURIComponent(ticker)}&resolution=1&from=${from}&to=${to}`; + const res = await fetch(url); + if (!res.ok) return null; + const j = (await res.json()) as { s?: string; c?: number[] }; + if (j.s !== "ok" || !Array.isArray(j.c) || j.c.length < 2) return null; + return j.c.filter((v) => Number.isFinite(v)); + } catch { + return null; + } +} + +/** How many ticks the chart keeps (~4 min at the 1s poll). */ +export const PRICE_HISTORY_CAP = 240; + +/** + * Append `price.priceUi` once per poll tick (object identity = one tick), + * keep the last `cap` points. Returns the same array reference between ticks + * so consumers can use it as a stable dependency. + * `resetKey` (the market symbol) WIPES the buffer on change — mixing two + * markets' prices in one buffer draws a deformed cross-scale line. + */ +export function usePriceHistory(price: PriceInfo | null, resetKey = "", cap = PRICE_HISTORY_CAP): number[] { + const [points, setPoints] = useState([]); + + useEffect(() => { + setPoints([]); // new market, fresh chart… + if (!resetKey) return; + let dead = false; + // …then seed it with real Pyth minute-closes (last ~60 min) so the line + // is born with shape. Live ticks append after; if the seed loses the race + // to the first live points, keep the live ones (never clobber freshness). + void fetchSeed(resetKey, 60).then((seed) => { + if (dead || !seed) return; + setPoints((prev) => (prev.length > 5 ? prev : [...seed.slice(-cap + 1), ...prev])); + }); + return () => { dead = true; }; + }, [resetKey, cap]); + + useEffect(() => { + if (!price || !Number.isFinite(price.priceUi)) return; + setPoints((prev) => { + const next = prev.length >= cap ? prev.slice(prev.length - cap + 1) : prev.slice(); + next.push(price.priceUi); + return next; + }); + }, [price, cap]); + + return points; +} diff --git a/examples/predict/lib/use-sheet-mount.ts b/examples/predict/lib/use-sheet-mount.ts new file mode 100644 index 0000000..52146df --- /dev/null +++ b/examples/predict/lib/use-sheet-mount.ts @@ -0,0 +1,26 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/use-sheet-mount.ts — sheets exist in the DOM only while open (or for +// the 260ms exit slide). THE HARD PART: the always-mounted translate-offscreen +// pattern renders the sheet's full markup inline whenever CSS fails to load — +// seen in the wild as a token grid dumped at the page bottom. Unmounted +// markup cannot fail. GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── +"use client"; + +import { useEffect, useState } from "react"; + +export function useSheetMount(open: boolean, exitMs = 260): { mounted: boolean; closing: boolean } { + // `open || kept` mounts in the SAME render open flips true — effects that + // focus the sheet's contents (deps: [open]) find their refs populated. + const [kept, setKept] = useState(open); + useEffect(() => { + if (open) { + setKept(true); + return; + } + if (!kept) return; + const t = setTimeout(() => setKept(false), exitMs); + return () => clearTimeout(t); + }, [open, kept, exitMs]); + return { mounted: open || kept, closing: !open && kept }; +} diff --git a/examples/predict/next-env.d.ts b/examples/predict/next-env.d.ts new file mode 100644 index 0000000..830fb59 --- /dev/null +++ b/examples/predict/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/examples/predict/next.config.ts b/examples/predict/next.config.ts new file mode 100644 index 0000000..6804dcf --- /dev/null +++ b/examples/predict/next.config.ts @@ -0,0 +1,30 @@ +// ───────────────────────────────────────────────────────────────────────────── +// next.config.ts — Next.js wiring for a workspace that ships raw TypeScript. +// THE HARD PART: flash-v2's entry point IS src/index.ts (no build step), so it +// must be transpiled by the app; anchor/gum-sdk pull Node-ish deps that need +// browser fallbacks. GOTCHAS.md → "Two chains, one flow" (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +import path from "node:path"; +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + reactStrictMode: true, + // The monorepo root (bun workspaces) — keeps Next from guessing wrong. + outputFileTracingRoot: path.join(__dirname, "../.."), + // flash-v2 is consumed straight from packages/flash-v2/src/*.ts — compile it. + transpilePackages: ["flash-v2"], + // Browsers auto-request /favicon.ico; we only ship app/icon.svg — redirect + // instead of 404ing the console on every load. + async redirects() { + return [{ source: "/favicon.ico", destination: "/icon.svg", permanent: true }]; + }, + webpack: (config) => { + // @coral-xyz/anchor (via @magicblock-labs/gum-sdk) probes Node built-ins; + // none are needed in the browser paths this app uses. + config.resolve.fallback = { ...config.resolve.fallback, fs: false, os: false, path: false }; + return config; + }, +}; + +export default nextConfig; diff --git a/examples/predict/package.json b/examples/predict/package.json new file mode 100644 index 0000000..db7bbf1 --- /dev/null +++ b/examples/predict/package.json @@ -0,0 +1,34 @@ +{ + "name": "predict", + "private": true, + "version": "0.1.0", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@coral-xyz/anchor": "^0.30.1", + "@magicblock-labs/gum-sdk": "^3.0.10", + "@solana/spl-token": "^0.4.14", + "@solana/wallet-adapter-base": "^0.9.27", + "@solana/wallet-adapter-phantom": "^0.9.29", + "@solana/wallet-adapter-react": "^0.15.39", + "@solana/wallet-adapter-solflare": "^0.6.28", + "@solana/web3.js": "^1.98.2", + "flash-v2": "workspace:*", + "next": "^15.5.19", + "react": "^19.2.0", + "react-dom": "^19.2.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.3.0", + "@types/bn.js": "^5.1.5", + "@types/node": "^22.10.0", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "tailwindcss": "^4.3.0", + "typescript": "^5.9.0" + } +} diff --git a/examples/predict/postcss.config.mjs b/examples/predict/postcss.config.mjs new file mode 100644 index 0000000..5e461c6 --- /dev/null +++ b/examples/predict/postcss.config.mjs @@ -0,0 +1,14 @@ +// ───────────────────────────────────────────────────────────────────────────── +// postcss.config.mjs — Tailwind v4 enters through PostCSS; no tailwind.config. +// THE HARD PART: nothing — v4 reads design tokens from @theme in globals.css, +// so the entire look lives in one CSS file instead of a JS config. +// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// ───────────────────────────────────────────────────────────────────────────── + +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/examples/predict/public/token-icons/2z.png b/examples/predict/public/token-icons/2z.png new file mode 100644 index 0000000000000000000000000000000000000000..817036170ab78dc5045fe4878a942b33f6fa76b2 GIT binary patch literal 1983 zcma)7X;9O55d8-cB4?%uC~{S%+76242Dyr0qjJfa!2&6k0VE^=Lj+B@f?8V@+FBI| zhvhIrI0Q^U0s&M+C?Q-S+$4rG5F&&qBmqk6*zt?Vhn?U4_QRWbZ+GARa@G^7s-&X? z0D$W0Q?5P$0FvK|ufPgh6}i=%tgUBH%&9;O04Qtx8z3M%XWv#4gznmZ)jGSq+tjj`~)mmbNpMMSE=;Ev2jo75}o=A&a_d`D;I9f^^UjlH| z5cm@O9as*sO=?=YD1{{TcFqw%IDQF9|3F4tTWKS!C$fJI6JxaCI~X#Cq=WkiAlghG zPR-vM7hW^;myQio!B8Fqb_PyBQXul5OmAFXa2afP?ofU&1Sg;r7WPCYJ7;v|IY>Ec zq8W96oN>+2LYO!*G?ZI=r#*~vJhRR5s%Q2-U8H{fk%uW=mlj6@gDr%-yKM%^vIOj# zQ4ABi&XeKIa|xVRJ7U)(70(b`4hWk<5U_!$M_TB>vP5`Y(=0pEB0IpEH#IdCOQ}5w z#=Pm9%a|W6##s8PkH$X8OALED%MLTM4brnPO+Z)?Z~>Lsgpyb(9w zU*c;S#s6rc5%LRvkWzt7AP$O-r{mx-!-Tm9m)JJr| z%0A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 literal 0 HcmV?d00001 diff --git a/examples/predict/public/token-icons/amzn.png b/examples/predict/public/token-icons/amzn.png new file mode 100644 index 0000000000000000000000000000000000000000..70933dbaebb0816491c8457a5c32f7dd4e22b8cc GIT binary patch literal 3091 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 \ No newline at end of file diff --git a/examples/predict/public/token-icons/aud.png b/examples/predict/public/token-icons/aud.png new file mode 100644 index 0000000000000000000000000000000000000000..d6433b126d40ef223f58afe2c96166945c96f60b GIT binary patch literal 3548 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 \ No newline at end of file diff --git a/examples/predict/public/token-icons/chip.svg b/examples/predict/public/token-icons/chip.svg new file mode 100644 index 0000000..8abd78f --- /dev/null +++ b/examples/predict/public/token-icons/chip.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/predict/public/token-icons/coin.svg b/examples/predict/public/token-icons/coin.svg new file mode 100644 index 0000000..c3b9a85 --- /dev/null +++ b/examples/predict/public/token-icons/coin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/examples/predict/public/token-icons/copper.svg b/examples/predict/public/token-icons/copper.svg new file mode 100644 index 0000000..f3fedf2 --- /dev/null +++ b/examples/predict/public/token-icons/copper.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/predict/public/token-icons/crcl.svg b/examples/predict/public/token-icons/crcl.svg new file mode 100644 index 0000000..5bfef5c --- /dev/null +++ b/examples/predict/public/token-icons/crcl.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/examples/predict/public/token-icons/crudeoil.png b/examples/predict/public/token-icons/crudeoil.png new file mode 100644 index 0000000000000000000000000000000000000000..aad1850989af655a7f2413cab89aff87a472d8cb GIT binary patch literal 582655 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 z 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

Q9t&wDINC7 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

_8xesAM=Se#{o`lH<<9sN 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 z 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

$^n)DVbB=f7=1g-0?F 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⁢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ΠaB_I5CH?j#z~Q!rd;ptl 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%0AKe%@Kw(H0=Ao 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 g{y%A8@7KOCMhE \ No newline at end of file diff --git a/examples/predict/public/token-icons/eur.png b/examples/predict/public/token-icons/eur.png new file mode 100644 index 0000000000000000000000000000000000000000..2140c6233947f2ad623f9191472732f09707ca98 GIT binary patch literal 2156 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 z%0D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

@@ -99,6 +110,15 @@ export function Discover({ onOpen }: { onOpen?: (token: string, price: number, t Mainnet · real funds. Odds are set by a formula on a real leveraged position — not a shared-pot market with discovered prices. You can never lose more than your stake.

+ + {selected && ( + setSelected(null)} + /> + )}
); } diff --git a/examples/predict/components/market-detail.tsx b/examples/predict/components/market-detail.tsx new file mode 100644 index 0000000..9163a6b --- /dev/null +++ b/examples/predict/components/market-detail.tsx @@ -0,0 +1,171 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/market-detail.tsx — tap a card → this overlay. The full prediction +// surface for one token+timeframe: +// • Above / Below direction toggle (each side is a real, separate bet — odds +// don't sum to 100¢; the gap is the honest house edge, like a book's vig). +// • The STRIKE LADDER: a row per strike, each a YES bet at its own odds. +// • MULTI-OUTCOME BUCKETS: "where does it land?", normalized to ~100%. +// • The BUY TICKET: stake → shares · to-win · max-loss (= stake), locked math. +// Every number is from lib/markets.ts. Real on-chain execution wires in phase 5 +// (needs the wallet/enable/session orchestrator) — here the ticket is a preview. +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { useMemo, useState } from "react"; +import { + bucketMarket, + cents, + profitUsd, + strikeLadder, + toWinUsd, + type Direction, + type PricedMarket, +} from "@/lib/markets"; +import { timeframe, type TimeframeId } from "@/lib/payoff"; +import { fmtPrice } from "@/lib/copy"; +import { TokenIcon } from "./token-icon"; + +const MIN_STAKE = 11; // the $11-after-fees floor (RECOMMENDED_MIN_COLLATERAL_USD) + +const fmtUsd = (n: number) => `$${n.toFixed(2)}`; +const fmtStrike = (n: number) => (n >= 1000 ? n.toLocaleString(undefined, { maximumFractionDigits: 0 }) : n.toFixed(n >= 1 ? 2 : 4)); + +/** Five bucket edges around the live price (±0.5% / ±1.5%) → six outcomes. */ +function defaultEdges(price: number): number[] { + return [0.985, 0.995, 1.005, 1.015].map((m) => price * m); +} + +export function MarketDetail({ + token, + price, + timeframe: tf, + onClose, + onConfirm, +}: { + token: string; + price: number; + timeframe: TimeframeId; + onClose: () => void; + onConfirm?: (m: PricedMarket, stakeUsd: number) => void; +}) { + const [dir, setDir] = useState("ABOVE"); + const ladder = useMemo(() => strikeLadder({ token, entry: price, direction: dir, timeframe: tf }), [token, price, dir, tf]); + const buckets = useMemo(() => bucketMarket({ token, entry: price, timeframe: tf, edges: defaultEdges(price) }), [token, price, tf]); + + const [picked, setPicked] = useState(null); + const active = picked && picked.direction === dir ? picked : ladder[Math.floor(ladder.length / 2)] ?? null; + const [stake, setStake] = useState("25"); + + const stakeUsd = Math.max(0, Number(stake.replace(/[^0-9.]/g, "")) || 0); + const tooSmall = stakeUsd > 0 && stakeUsd < MIN_STAKE; + const yesCents = active ? cents(active.prob) : 0; + + return ( +
+ +
+ + {/* direction toggle — each side is its own real bet */} +
+ + +
+ + {/* the strike ladder — pick your odds by picking a strike */} +
+

{dir === "ABOVE" ? "climbs above" : "drops below"} — pick a strike

+ {ladder.map((m) => { + const on = active === m; + const c = cents(m.prob); + return ( + + ); + })} +
+ + {/* multi-outcome buckets — where does it land? */} +
+

where does it land?

+ {buckets.buckets.map((b) => ( +
+ {b.label} +
+
+
+ {Math.round(b.prob * 100)}% +
+ ))} +
+ + {/* the buy ticket */} + {active && ( +
+
+

+ {token} {dir === "ABOVE" ? "above" : "below"} {fmtStrike(active.strike)} +

+ {yesCents}¢ +
+ + + + {/* the honest numbers — locked from the same math the trade uses */} +
+ + + +
+ + +

+ Odds are formula-set on a real capped-loss position. You can never lose more than your stake. +

+
+ )} +
+
+ ); +} + +function Stat({ label, value, tone }: { label: string; value: string; tone: "up" | "down" }) { + return ( +
+

{label}

+

{value}

+
+ ); +} From a6af920484e95e3fa797a336f8391004f2858d6f Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 04:44:16 -0400 Subject: [PATCH 10/26] predict v2 phase 5: real bet execution (wallet/session/signer wired) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - market-detail.tsx: the ticket now PLACES a real bet — openPosition with the bundled takeProfit at the strike (YES win) + the liquidation knockout (lose stake), via the same owner+tradeFields+sendTrade path app.tsx proves. Status (placing/done/error) + receipt; gated on a signer ('Connect & enable to bet'). - markets-app.tsx: /markets shell reusing the proven wiring (providers -> owner stream -> loadSession -> makeSessionSigner -> enableOneClickTrading). Minimal wallet bar: connect / enable one-tap / basket balance. canBet = basket + signer + >=$1 deposited. - discover.tsx threads signer/canBet/onNeedWallet/onPlaced to the detail. typecheck + build green (/markets 292 kB w/ wallet adapters). Phase 5 of 6. NOT yet through the mandatory adversarial review (subagents rate-limited) — do not trade real funds on it until reviewed. Remaining: portfolio of open bets + the funds sheet on /markets (5b), then disclosure + polish (6). Co-Authored-By: Claude Opus 4.8 --- examples/predict/app/markets/page.tsx | 15 +- examples/predict/components/discover.tsx | 19 ++- examples/predict/components/market-detail.tsx | 66 +++++++- examples/predict/components/markets-app.tsx | 151 ++++++++++++++++++ 4 files changed, 234 insertions(+), 17 deletions(-) create mode 100644 examples/predict/components/markets-app.tsx diff --git a/examples/predict/app/markets/page.tsx b/examples/predict/app/markets/page.tsx index e10f016..5fa9b4c 100644 --- a/examples/predict/app/markets/page.tsx +++ b/examples/predict/app/markets/page.tsx @@ -1,12 +1,9 @@ -// app/markets/page.tsx — the prediction-market browser (phase 2/3 preview). -// Public + wallet-free: browse live markets, see the YES/NO odds. Tapping a card -// will open the detail + buy ticket (phase 4). Visit /markets. -import { Discover } from "@/components/discover"; +// app/markets/page.tsx — the prediction-market app (phase 5). +// Browse markets wallet-free; connect + enable only when you back one. Real bets +// open a capped-loss Flash V2 perp with a bundled take-profit (the YES win). +// Visit /markets. +import { MarketsApp } from "@/components/markets-app"; export default function MarketsPage() { - return ( -
- -
- ); + return ; } diff --git a/examples/predict/components/discover.tsx b/examples/predict/components/discover.tsx index 1874ffd..b90436a 100644 --- a/examples/predict/components/discover.tsx +++ b/examples/predict/components/discover.tsx @@ -10,10 +10,23 @@ import { useMemo, useState } from "react"; import { TIMEFRAMES, type TimeframeId } from "@/lib/payoff"; import { usePredictMarkets } from "@/lib/use-markets"; +import type { ActiveSigner } from "@/lib/signer"; import { MarketCard } from "./market-card"; import { MarketDetail } from "./market-detail"; -export function Discover({ onOpen }: { onOpen?: (token: string, price: number, tf: TimeframeId) => void }) { +export function Discover({ + signer, + canBet, + onNeedWallet, + onPlaced, + onOpen, +}: { + signer: ActiveSigner | null; + canBet: boolean; + onNeedWallet?: () => void; + onPlaced?: () => void; + onOpen?: (token: string, price: number, tf: TimeframeId) => void; +}) { const { rows, categories, loading, error } = usePredictMarkets(); const [cat, setCat] = useState("All"); const [tf, setTf] = useState("1h"); @@ -116,7 +129,11 @@ export function Discover({ onOpen }: { onOpen?: (token: string, price: number, t token={selected.token} price={selected.price} timeframe={selected.tf} + signer={signer} + canBet={canBet} onClose={() => setSelected(null)} + onNeedWallet={onNeedWallet} + onPlaced={onPlaced} /> )}
diff --git a/examples/predict/components/market-detail.tsx b/examples/predict/components/market-detail.tsx index 9163a6b..6dcfd4b 100644 --- a/examples/predict/components/market-detail.tsx +++ b/examples/predict/components/market-detail.tsx @@ -12,7 +12,8 @@ "use client"; -import { useMemo, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; +import { assertNoErr } from "flash-v2"; import { bucketMarket, cents, @@ -24,6 +25,8 @@ import { } from "@/lib/markets"; import { timeframe, type TimeframeId } from "@/lib/payoff"; import { fmtPrice } from "@/lib/copy"; +import { flash } from "@/lib/flash"; +import type { ActiveSigner } from "@/lib/signer"; import { TokenIcon } from "./token-icon"; const MIN_STAKE = 11; // the $11-after-fees floor (RECOMMENDED_MIN_COLLATERAL_USD) @@ -40,14 +43,20 @@ export function MarketDetail({ token, price, timeframe: tf, + signer, + canBet, onClose, - onConfirm, + onNeedWallet, + onPlaced, }: { token: string; price: number; timeframe: TimeframeId; + signer: ActiveSigner | null; + canBet: boolean; onClose: () => void; - onConfirm?: (m: PricedMarket, stakeUsd: number) => void; + onNeedWallet?: () => void; + onPlaced?: () => void; }) { const [dir, setDir] = useState("ABOVE"); const ladder = useMemo(() => strikeLadder({ token, entry: price, direction: dir, timeframe: tf }), [token, price, dir, tf]); @@ -61,6 +70,40 @@ export function MarketDetail({ const tooSmall = stakeUsd > 0 && stakeUsd < MIN_STAKE; const yesCents = active ? cents(active.prob) : 0; + const [placing, setPlacing] = useState(false); + const [result, setResult] = useState<{ ok: boolean; msg: string } | null>(null); + + // ── place a real bet: a capped-loss perp with the bundled take-profit AT the + // strike (the win boundary); the liquidation is the knockout (lose your stake). + // Same openPosition→sendTrade path the existing Updown ticket proves (app.tsx). + const place = useCallback(async () => { + if (!active || !signer) return; + setPlacing(true); + setResult(null); + try { + const res = await flash.openPosition({ + inputTokenSymbol: "USDC", + outputTokenSymbol: active.token, + inputAmountUi: stakeUsd.toFixed(2), + leverage: Number(active.construction.leverage.toFixed(4)), + tradeType: active.construction.side, + slippagePercentage: "1", + takeProfit: active.construction.takeProfitPrice.toFixed(4), + owner: signer.owner, + ...signer.tradeFields, + }); + assertNoErr("open-position", res); + if (!res.transactionBase64) throw new Error(res.err ?? "no transaction returned"); + const sent = await signer.sendTrade(res.transactionBase64); + setResult({ ok: true, msg: `Bet placed · ${sent.signature.slice(0, 8)}…` }); + onPlaced?.(); + } catch (e) { + setResult({ ok: false, msg: (e as Error).message }); + } finally { + setPlacing(false); + } + }, [active, signer, stakeUsd, onPlaced]); + return (
+ {result && ( +

{result.msg}

+ )}

Odds are formula-set on a real capped-loss position. You can never lose more than your stake.

diff --git a/examples/predict/components/markets-app.tsx b/examples/predict/components/markets-app.tsx new file mode 100644 index 0000000..f5af8ba --- /dev/null +++ b/examples/predict/components/markets-app.tsx @@ -0,0 +1,151 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/markets-app.tsx — the prediction-market app shell at /markets. +// Reuses the SAME proven wiring as the Updown orchestrator (providers → owner +// stream → session → signer → enable) so a connected + enabled user places REAL +// bets through MarketDetail. Browsing stays wallet-free; the wallet is asked for +// only at "back this". Deposits live on the main app for now (phase 5b: bring +// the funds sheet here + a portfolio of open bets). +// +// ⚠️ Real-funds path — built + compiles, but NOT yet through the mandatory +// adversarial review (subagents rate-limited). Don't trade real funds until it is. +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { useCallback, useEffect, useMemo, useState } from "react"; +import { ConnectionProvider, WalletProvider, useAnchorWallet, useWallet } from "@solana/wallet-adapter-react"; +import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom"; +import { SolflareWalletAdapter } from "@solana/wallet-adapter-solflare"; +import { enableOneClickTrading, type EnableWalletCtx } from "@/lib/enable"; +import { flash } from "@/lib/flash"; +import { shortKey } from "@/lib/format"; +import { useBalances, useBasketBalance, useUsdcMint } from "@/lib/hooks"; +import { loadSession, type LoadedSession } from "@/lib/session"; +import { makeSessionSigner } from "@/lib/signer"; +import { StreamProvider, useStream } from "@/lib/stream"; +import { Discover } from "./discover"; + +export function MarketsApp() { + const wallets = useMemo(() => [new PhantomWalletAdapter(), new SolflareWalletAdapter()], []); + return ( + + + + + + ); +} + +function Shell() { + const { publicKey } = useWallet(); + const owner = publicKey ? publicKey.toBase58() : null; + return ( + + + + ); +} + +function Inner({ owner }: { owner: string | null }) { + const walletCtx = useWallet(); + const anchorWallet = useAnchorWallet(); + const { snapshot } = useStream(); + const usdcMint = useUsdcMint(); + const balances = useBalances(owner, usdcMint); + const basket = useBasketBalance(owner, snapshot?.basketPubkey ?? null, usdcMint); + const inBasketUsd = basket.bal?.inBasketUsd ?? null; + + const [session, setSession] = useState(null); + useEffect(() => { + setSession(owner ? loadSession(owner) : null); + }, [owner]); + const signer = useMemo( + () => (session && anchorWallet ? makeSessionSigner(anchorWallet, session, flash.network) : null), + [session, anchorWallet], + ); + + const basketExists = Boolean(snapshot?.basketPubkey); + const canBet = basketExists && Boolean(signer) && (inBasketUsd ?? 0) >= 1; + + const [enabling, setEnabling] = useState(false); + const [note, setNote] = useState(null); + + const connect = useCallback(() => { + walletCtx.select?.(walletCtx.wallets[0]?.adapter.name ?? null); + }, [walletCtx]); + + const enable = useCallback(async () => { + if (!walletCtx.publicKey || !walletCtx.signTransaction || !anchorWallet) { + connect(); + return; + } + setEnabling(true); + setNote(null); + try { + const ctx: EnableWalletCtx = { + publicKey: walletCtx.publicKey, + signTransaction: walletCtx.signTransaction, + signAllTransactions: walletCtx.signAllTransactions, + }; + const result = await enableOneClickTrading({ + wallet: ctx, + anchorWallet, + snapshot, + usdcMint, + balances: { sol: balances.sol, usdc: balances.usdc }, + onStep: () => {}, + onLog: () => {}, + }); + if (result.session) setSession(result.session); + if (!result.ok) { + setNote(result.needsUsdc ? "Account ready — deposit USDC on the main app, then bet." : "Couldn't finish enabling — check your wallet."); + } else if (result.needsUsdc) { + setNote("Deposit USDC on the main app to start betting."); + } + void balances.refresh(); + void basket.refresh(); + } catch (e) { + setNote((e as Error).message); + } finally { + setEnabling(false); + } + }, [walletCtx, anchorWallet, snapshot, usdcMint, balances, basket, connect]); + + const onNeedWallet = useCallback(() => { + if (!owner) connect(); + else void enable(); + }, [owner, connect, enable]); + + return ( +
+
+
+ P + predict +
+ {owner ? ( +
+ {canBet ? ( + ${(inBasketUsd ?? 0).toFixed(2)} + ) : ( + + )} + {shortKey(owner)} +
+ ) : ( + + )} +
+ {note &&

{note}

} + void basket.refresh()} /> +
+ ); +} From 7d6a104853708f8858083842ce017c01485e07ec Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 09:15:40 -0400 Subject: [PATCH 11/26] predict v2 phase 5b: portfolio of open bets (read-only) + card polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - components/portfolio.tsx: 'your open bets' — each open Flash V2 position is a live bet (LONG = Above call, SHORT = Below), live value via computePositionView (Flash-parity mark math, GOTCHAS §20), marks from one polled bulk /v2/prices. READ-ONLY: no trade is placed here, zero funds risk. - markets-app.tsx: renders the portfolio above the grid when connected. - market-card.tsx: subtle hover lift (premium touch). typecheck (all workspaces) + build green (/markets 6.58 kB). Co-Authored-By: Claude Opus 4.8 --- examples/predict/components/market-card.tsx | 2 +- examples/predict/components/markets-app.tsx | 3 + examples/predict/components/portfolio.tsx | 89 +++++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 examples/predict/components/portfolio.tsx diff --git a/examples/predict/components/market-card.tsx b/examples/predict/components/market-card.tsx index 7c89ed3..905f785 100644 --- a/examples/predict/components/market-card.tsx +++ b/examples/predict/components/market-card.tsx @@ -39,7 +39,7 @@ export function MarketCard({ return (
{note &&

{note}

} + {owner && } void basket.refresh()} /> ); diff --git a/examples/predict/components/portfolio.tsx b/examples/predict/components/portfolio.tsx new file mode 100644 index 0000000..bcaca14 --- /dev/null +++ b/examples/predict/components/portfolio.tsx @@ -0,0 +1,89 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/portfolio.tsx — "your open bets" (READ-ONLY, zero funds risk). +// Each open Flash V2 position IS a live bet: a LONG = an "Above" call, a SHORT = +// a "Below" call. Live value is the Flash-parity client mark-price math +// (computePositionView, GOTCHAS §20) — never the indexer's pnl field. Marks come +// from one polled bulk /v2/prices. No trade is ever placed here. +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { useEffect, useState } from "react"; +import type { PositionMetrics, PriceInfo } from "flash-v2"; +import { computePositionView } from "@/lib/format"; +import { flash } from "@/lib/flash"; +import { fmtPrice } from "@/lib/copy"; +import { TokenIcon } from "./token-icon"; + +const fmtUsd = (n: number) => `${n < 0 ? "−" : ""}$${Math.abs(n).toFixed(2)}`; + +export function Portfolio({ positions }: { positions: PositionMetrics[] }) { + const [prices, setPrices] = useState>({}); + + useEffect(() => { + let alive = true; + let timer: ReturnType | undefined; + const tick = async () => { + try { + const p = await flash.prices(); + if (alive) setPrices(p); + } catch { + /* keep last marks */ + } + if (alive) timer = setTimeout(() => void tick(), 3000); + }; + void tick(); + return () => { + alive = false; + if (timer) clearTimeout(timer); + }; + }, []); + + if (positions.length === 0) return null; + + return ( +
+
+ your open bets + + {positions.length} +
+ +
+ {positions.map((p) => { + const long = p.sideUi.toUpperCase() === "LONG"; + const mark = prices[p.marketSymbol]?.priceUi ?? null; + const view = computePositionView(p, mark); + const pnl = view?.pnlUsd ?? null; + const pct = view?.pnlPct ?? null; + const stake = Number(p.collateralUsdUi) || 0; + const winning = (pnl ?? 0) >= 0; + return ( +
+
+ +
+

+ {p.marketSymbol}{" "} + {long ? "▲ Above" : "Below ▼"} +

+

+ stake {fmtUsd(stake)} · entry {fmtPrice(Number(p.entryPriceUi) || 0)} +

+
+
+
+

+ {pnl === null ? "—" : `${pnl >= 0 ? "+" : ""}${fmtUsd(pnl)}`} +

+

+ {pct === null ? "" : `${pct >= 0 ? "+" : ""}${pct.toFixed(1)}%`} +

+
+
+ ); + })} +
+
+ ); +} From 80a2db5aa673da71db17fd6c0e82c5e508a46d70 Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 09:26:42 -0400 Subject: [PATCH 12/26] predict v2: float the card odds (each market gets its own cents) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The card was solving for a fixed 45c target (moving the strike to hit it), so every card showed YES 45c / NO 55c. Now it fixes a round-number strike a believable distance above the live price and lets the odds FLOAT from priceMarket — SOL above $80 (+8%) prices longer than BTC above $70k (+4.7%), etc. Real, per-market odds. Card-only display change; the money path is unchanged. typecheck green. Co-Authored-By: Claude Opus 4.8 --- examples/predict/components/market-card.tsx | 28 +++++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/examples/predict/components/market-card.tsx b/examples/predict/components/market-card.tsx index 905f785..3816f2a 100644 --- a/examples/predict/components/market-card.tsx +++ b/examples/predict/components/market-card.tsx @@ -9,7 +9,7 @@ "use client"; import { useMemo } from "react"; -import { cents, marketForTargetProb } from "@/lib/markets"; +import { cents, priceMarket } from "@/lib/markets"; import { timeframe, type TimeframeId } from "@/lib/payoff"; import { fmtPrice } from "@/lib/copy"; import { TokenIcon } from "./token-icon"; @@ -18,6 +18,23 @@ function fmtStrike(n: number): string { return n >= 1000 ? n.toLocaleString(undefined, { maximumFractionDigits: 0 }) : n.toFixed(n >= 1 ? 2 : 4); } +/** The NO-boundary band per timeframe (sets the knockout → the leverage). */ +const KO_BAND: Record = { "5m": 0.012, "15m": 0.025, "1h": 0.05 }; + +/** Round a price UP to a human "nice" number a believable bit above. The distance + * varies per token, so the odds FLOAT — each market gets its own cents — instead + * of every card showing the same solved-for probability. */ +function niceStrikeAbove(price: number): number { + if (price <= 0) return 0; + const min = price * 1.02; + const mag = Math.pow(10, Math.floor(Math.log10(min))); + for (const s of [1, 1.25, 1.5, 2, 2.5, 3, 4, 5, 6, 7, 7.5, 8, 9, 10]) { + const cand = s * mag; + if (cand >= min) return Number(cand.toPrecision(4)); + } + return Number((10 * mag).toPrecision(4)); +} + export function MarketCard({ token, price, @@ -29,10 +46,11 @@ export function MarketCard({ timeframe: TimeframeId; onOpen?: () => void; }) { - const m = useMemo( - () => marketForTargetProb({ token, direction: "ABOVE", entry: price, timeframe: tf, targetProb: 0.45 }), - [token, price, tf], - ); + const m = useMemo(() => { + const strike = niceStrikeAbove(price); + const knockoutPrice = price * (1 - KO_BAND[tf]); + return priceMarket({ token, direction: "ABOVE", entry: price, strike, knockoutPrice, timeframe: tf }); + }, [token, price, tf]); const yes = cents(m.prob); const no = 100 - yes; From 45f4803140329aed02fce634b6933e798bee086e Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 09:46:54 -0400 Subject: [PATCH 13/26] predict v2.1: lock the spread-aware Path A redesign (corrected construction) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 3-reviewer money-path audit found the v2 engine funds-loss-grade broken: modeled cost as 0.16% fee, ignored the real 5-10% Flash spread -> bundled TP fired as a ~100% loss on SOL (live pnlPercentage -100), leverage unclamped (100x already liquidated), nothing reconciled to the signed fill. Decision: keep the TRUE fixed-odds binary (Path A), corrected for spread: - everything measured from fillEntry = oracle*(1±s), strike = fillEntry*(1±t), knockout = fillEntry*(1∓0.92/L); R = L*(t-fee), q = 1/(1+R) -> honest odds. - new hard constraint: knockout must sit beyond the spread -> L_max = min(custody.maxLeverage, 0.92/(s*1.5)). SOL (s=0.10) => L<=~6x. - floor is correct (bundled TP needs >$10 after fees; bare-open is ~$5). - full fix list (every CRITICAL/HIGH from the audit) + order in the spec. Design locked; engine rewrite + wiring + reconcile + settlement port is the next focused session, re-reviewed before the 'don't trade' banner comes off. Co-Authored-By: Claude Opus 4.8 --- examples/predict/SPEC-PREDICT-V2.md | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/examples/predict/SPEC-PREDICT-V2.md b/examples/predict/SPEC-PREDICT-V2.md index 04ee541..421dda8 100644 --- a/examples/predict/SPEC-PREDICT-V2.md +++ b/examples/predict/SPEC-PREDICT-V2.md @@ -71,6 +71,48 @@ Keep the existing "Void Instrument / frosted glass" base (`globals.css`, `DESIGN 5. **Portfolio + settlement** — shares with live value, history, the welcome-back settle adapted to win/lose framing. 6. **Disclosure + polish** — the honest "how odds are set" disclosure; the premium pass; `typecheck` + `build` green; adversarial review. +## v2.1 REDESIGN — Path A, spread-aware (supersedes the engine math above) + +The 3-reviewer money-path audit (2026-06-15) found the v2 engine **funds-loss-grade broken**: it modeled cost as a 0.16% fee and **ignored the real 5–10% Flash trade spread**, so the bundled take-profit landed *inside* the spread and fired as a ~100% loss on SOL (live `pnlPercentage: −100`), leverage was never clamped (SOL opened at 100× already-liquidated), and nothing was reconciled to the signed fill. Decision: **keep the true fixed-odds binary (Path A), corrected for the spread.** `$11` floor is correct here — a bundled TP needs `> $10` collateral after fees (`guards.ts`: `MIN_COLLATERAL_USD_AFTER_FEES=10`, `RECOMMENDED_MIN_COLLATERAL_USD=11`). (A bare open's floor is ~$5, but our win = a bundled TP, so $11 stands.) + +### The corrected construction (the entry is the FILL, not the oracle) + +A LONG fills at `fillEntry = oracle·(1+s)`; a SHORT at `oracle·(1−s)`, where `s = tradeSpread` (live, per market, from `useMarketLimits` — SOL≈0.10, BTC/ETH≈0.05). **Everything is measured from `fillEntry`, never the oracle:** + +``` +fillEntry = oracle·(1 ± s) # + for ABOVE/LONG, − for BELOW/SHORT +strike = fillEntry·(1 ± t) # the TP, set BEYOND the fill by t (the win) +knockout = fillEntry·(1 ∓ 0.92/L) # liquidation (the loss) +R = L·(t − fee) q = 1/(1+R) # t is the move FROM THE FILL; q honest +to-win = stake/q max-loss = stake +``` + +Because the strike is `fillEntry·(1+t)`, on SOL it sits `≈ s+t ≈ 12%+` above the oracle — honest (the bet truly needs a big move), and **long-shot-only on high-spread tokens, exactly as accepted.** + +### The new hard constraint: the knockout must sit BEYOND the spread + +At high L, `koDist = 0.92/L` can be smaller than `s` → the position opens already past liquidation (CRIT-2). So clamp: + +``` +L_max = min( custody.maxLeverage , 0.92 / (s · KO_MARGIN) ) # KO_MARGIN ≈ 1.5 +``` + +SOL (s=0.10) ⇒ `L ≤ 0.92/0.15 ≈ 6×`. High-spread markets are naturally low-leverage (correct). The ladder must only offer odds achievable within `L_max` at a believable strike; deeper long-shots simply aren't available on high-spread tokens (show them honestly or omit). + +### The fix list (every CRITICAL/HIGH from the audit), in order + +1. **Engine spread-aware** (`lib/markets.ts`): `priceMarket`/`marketForTargetProb`/`strikeLadder`/`bucketMarket` take `oracle` + `spread` + `maxLeverage`; compute from `fillEntry`; clamp `L` by the constraint above; the `Market` carries `oracle` (display) and `entry`=`fillEntry`. **Fixes CRIT-1, CRIT-2, HIGH-4.** +2. **Wire live spread + caps**: card/detail call `useMarketLimits(token)` → pass `tradeSpread` + `maxLeverage` into the engine. No market is built before limits load (gate the ticket). +3. **Reconcile to the signed fill** (`place()`): after `openPosition`, build `lockedQuoteFrom(res,…)`, render the REAL entry/liq/TP/fee, and **block the send / show the result** if `takeProfitQuote.profitUsdUi ≤ 0` or liq is on the wrong side. Re-quote at the commit gate (two-step like `app.tsx doReview→doConfirm`). **Fixes CRIT-3, MEDIUM (stale price).** +4. **Validate the TP**: call `guards.validateTriggerPrice({side, kind:"tp", price:strike, markPrice:fillEntry})` before sending; refuse on `!ok` (avoids on-chain `6057`). +5. **Settlement + expiry** (port `lib/rounds.ts`): record a `Round{…expiresAt}` on a confirmed send; run the reconcile/settle watcher; close at expiry by mark (`inputUsdUi:"0"`) for the between-barrier case. Render a **/markets-specific** disclosure (don't inherit Updown's). **Fixes HIGH-5.** +6. **Gates + errors**: `canBet` floor → `MIN_STAKE` (11); add `stake > balance` block; double-submit guard (in-flight set keyed by owner|market|side, re-check live balance); route `place()`/`enable()` errors through `calmError`; surface the real `enable` step error. **Fixes C3, H1, H2, M1.** +7. **Buckets**: rebuild as bounded (double-barrier / call-spread) outcomes, or relabel honestly as "chance price *reaches* this zone" and drop the fake normalization. **Fixes HIGH-6.** +8. **Slippage**: set `slippagePercentage` ≥ the spread (or omit and let the spread price it) — `"1"` is below a 5–10% spread. **Fixes MEDIUM-7.** +9. Guard degenerate inputs (`oracle ≤ 0`, `t ≤ s+fee`). **Fixes LOW-8.** + +**Re-run the 3-agent adversarial review before flipping the "don't trade real funds" banner.** + ## Verification bar (every phase) `bun run --cwd examples/predict typecheck` and `build` must stay green. Mainnet, real funds — no shortcuts. Adversarial review before anything is called done. From 06d739082594c7abf7a11fed3fd63d7c11f10019 Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 10:19:25 -0400 Subject: [PATCH 14/26] predict v2.1: spread-aware engine + live-spread wiring + slippage (fixes #1,#2,#8,#9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Engine (lib/markets.ts) rewritten to measure from the SIGNED FILL, not the oracle — kills the funds-loss bug where a take-profit computed off the oracle landed inside the 5-10% Flash spread and fired as a ~100% loss: fillEntry = oracle*(1±s); strike = fillEntry*(1±t); knockout = fillEntry*(1∓0.92/L) R = L*(t−fee); q = 1/(1+R) Leverage clamped at the chokepoint priceMarket so koDist=0.92/L always clears the spread (L ≤ 0.92/(s·1.5); SOL ⇒ ~6x, never 100x already-liquidated). Ladder spans by STRIKE once leverage pins at the cap, so odds float per token (answers 'why are all the markets the same odds'). Degenerate inputs never NaN/throw (#9). Slippage derived from the spread, never a hardcoded 1% (#8). Wiring (#2): one shared custody+tokens parse path (deduped in-flight) feeds both useMarketLimits (detail) and a new useAllMarketLimits (grid) — the fund-critical spread is derived in exactly one place. Card + detail gate on live limits and NEVER render spread-blind odds. Pure engine verified (all asserts pass); full typecheck green. The money-path reconcile (#3), trigger validation (#4), settlement (#5), gates (#6) and bucket redesign (#7) are the remaining steps; 'don't trade real funds' banner stays up until the 3-agent re-review. Co-Authored-By: Claude Opus 4.8 --- examples/predict/components/discover.tsx | 3 + examples/predict/components/market-card.tsx | 95 +++--- examples/predict/components/market-detail.tsx | 207 +++++++----- examples/predict/lib/hooks.ts | 147 ++++++--- examples/predict/lib/markets.ts | 294 +++++++++++++----- 5 files changed, 508 insertions(+), 238 deletions(-) diff --git a/examples/predict/components/discover.tsx b/examples/predict/components/discover.tsx index b90436a..27b03ab 100644 --- a/examples/predict/components/discover.tsx +++ b/examples/predict/components/discover.tsx @@ -10,6 +10,7 @@ import { useMemo, useState } from "react"; import { TIMEFRAMES, type TimeframeId } from "@/lib/payoff"; import { usePredictMarkets } from "@/lib/use-markets"; +import { useAllMarketLimits } from "@/lib/hooks"; import type { ActiveSigner } from "@/lib/signer"; import { MarketCard } from "./market-card"; import { MarketDetail } from "./market-detail"; @@ -28,6 +29,7 @@ export function Discover({ onOpen?: (token: string, price: number, tf: TimeframeId) => void; }) { const { rows, categories, loading, error } = usePredictMarkets(); + const limitsBySymbol = useAllMarketLimits(useMemo(() => rows.map((r) => r.token), [rows])); const [cat, setCat] = useState("All"); const [tf, setTf] = useState("1h"); const [query, setQuery] = useState(""); @@ -111,6 +113,7 @@ export function Discover({ token={r.token} price={r.price} timeframe={tf} + limits={limitsBySymbol.get(r.token) ?? null} onOpen={() => { setSelected({ token: r.token, price: r.price, tf }); onOpen?.(r.token, r.price, tf); diff --git a/examples/predict/components/market-card.tsx b/examples/predict/components/market-card.tsx index 3816f2a..aae783e 100644 --- a/examples/predict/components/market-card.tsx +++ b/examples/predict/components/market-card.tsx @@ -2,14 +2,18 @@ // components/market-card.tsx — the single most important component (Polymarket's // market card is the click): token + live price + a plain-words question + the // YES/NO odds as a split bar. Cents ARE the odds. Semantic colour + ▲/▼ always -// (never colour-alone). The headline is a believable "climb above" call (~45¢), -// priced live by lib/markets.ts; tapping opens the full ladder + ticket. +// (never colour-alone). The headline is a believable "climb above" call, priced +// LIVE through this market's real trade spread + leverage caps by the engine — +// so each card's odds FLOAT (no two read alike). Until the live limits load the +// card shows a skeleton; it NEVER quotes spread-blind odds. Tapping opens the +// full ladder + ticket. // ───────────────────────────────────────────────────────────────────────────── "use client"; import { useMemo } from "react"; -import { cents, priceMarket } from "@/lib/markets"; +import { cents, headlineMarket } from "@/lib/markets"; +import type { MarketLimits } from "@/lib/hooks"; import { timeframe, type TimeframeId } from "@/lib/payoff"; import { fmtPrice } from "@/lib/copy"; import { TokenIcon } from "./token-icon"; @@ -18,41 +22,34 @@ function fmtStrike(n: number): string { return n >= 1000 ? n.toLocaleString(undefined, { maximumFractionDigits: 0 }) : n.toFixed(n >= 1 ? 2 : 4); } -/** The NO-boundary band per timeframe (sets the knockout → the leverage). */ -const KO_BAND: Record = { "5m": 0.012, "15m": 0.025, "1h": 0.05 }; - -/** Round a price UP to a human "nice" number a believable bit above. The distance - * varies per token, so the odds FLOAT — each market gets its own cents — instead - * of every card showing the same solved-for probability. */ -function niceStrikeAbove(price: number): number { - if (price <= 0) return 0; - const min = price * 1.02; - const mag = Math.pow(10, Math.floor(Math.log10(min))); - for (const s of [1, 1.25, 1.5, 2, 2.5, 3, 4, 5, 6, 7, 7.5, 8, 9, 10]) { - const cand = s * mag; - if (cand >= min) return Number(cand.toPrecision(4)); - } - return Number((10 * mag).toPrecision(4)); -} - export function MarketCard({ token, price, timeframe: tf, + limits, onOpen, }: { token: string; price: number; timeframe: TimeframeId; + /** live spread + caps for this market; null until loaded (→ skeleton). */ + limits: MarketLimits | null; onOpen?: () => void; }) { - const m = useMemo(() => { - const strike = niceStrikeAbove(price); - const knockoutPrice = price * (1 - KO_BAND[tf]); - return priceMarket({ token, direction: "ABOVE", entry: price, strike, knockoutPrice, timeframe: tf }); - }, [token, price, tf]); - const yes = cents(m.prob); - const no = 100 - yes; + const m = useMemo( + () => + limits + ? headlineMarket({ + token, + oracle: price, + spread: limits.spreadLongPct, + maxLeverage: limits.maxLeverage, + minLeverage: limits.minLeverage, + timeframe: tf, + }) + : null, + [token, price, tf, limits], + ); return ( ); } + +function Odds({ yes }: { yes: number }) { + const no = 100 - yes; + return ( +
+
+ ▲ YES {yes}¢ + NO {no}¢ ▼ +
+
+
+
+
+ ); +} diff --git a/examples/predict/components/market-detail.tsx b/examples/predict/components/market-detail.tsx index 6dcfd4b..f869170 100644 --- a/examples/predict/components/market-detail.tsx +++ b/examples/predict/components/market-detail.tsx @@ -4,10 +4,12 @@ // • Above / Below direction toggle (each side is a real, separate bet — odds // don't sum to 100¢; the gap is the honest house edge, like a book's vig). // • The STRIKE LADDER: a row per strike, each a YES bet at its own odds. -// • MULTI-OUTCOME BUCKETS: "where does it land?", normalized to ~100%. -// • The BUY TICKET: stake → shares · to-win · max-loss (= stake), locked math. -// Every number is from lib/markets.ts. Real on-chain execution wires in phase 5 -// (needs the wallet/enable/session orchestrator) — here the ticket is a preview. +// • MULTI-OUTCOME BUCKETS: "where does it land?". +// • The BUY TICKET: stake → to-win · profit · max-loss (= stake), locked math. +// Every number is priced LIVE through this market's real trade spread + leverage +// caps (useMarketLimits) by lib/markets.ts — measured from the SIGNED FILL, never +// the oracle, so a take-profit can't land inside the spread. Nothing is shown +// until the limits load (no spread-blind odds, ever). // ───────────────────────────────────────────────────────────────────────────── "use client"; @@ -18,11 +20,13 @@ import { bucketMarket, cents, profitUsd, + slippageForSpread, strikeLadder, toWinUsd, type Direction, type PricedMarket, } from "@/lib/markets"; +import { useMarketLimits } from "@/lib/hooks"; import { timeframe, type TimeframeId } from "@/lib/payoff"; import { fmtPrice } from "@/lib/copy"; import { flash } from "@/lib/flash"; @@ -58,9 +62,33 @@ export function MarketDetail({ onNeedWallet?: () => void; onPlaced?: () => void; }) { + const limits = useMarketLimits(token); const [dir, setDir] = useState("ABOVE"); - const ladder = useMemo(() => strikeLadder({ token, entry: price, direction: dir, timeframe: tf }), [token, price, dir, tf]); - const buckets = useMemo(() => bucketMarket({ token, entry: price, timeframe: tf, edges: defaultEdges(price) }), [token, price, tf]); + // Side-appropriate spread: a LONG (Above) enters through the long spread, a + // SHORT (Below) through the short spread. The engine measures from that fill. + const spread = limits ? (dir === "ABOVE" ? limits.spreadLongPct : limits.spreadShortPct) : 0; + + const ladder = useMemo( + () => + limits + ? strikeLadder({ + token, oracle: price, spread, maxLeverage: limits.maxLeverage, + minLeverage: limits.minLeverage, direction: dir, timeframe: tf, + }) + : [], + [token, price, spread, dir, tf, limits], + ); + const buckets = useMemo( + () => + limits + ? // buckets span both sides; per-side spread is redesigned in fix #7. + bucketMarket({ + token, oracle: price, spread: limits.spreadLongPct, maxLeverage: limits.maxLeverage, + minLeverage: limits.minLeverage, timeframe: tf, edges: defaultEdges(price), + }) + : null, + [token, price, tf, limits], + ); const [picked, setPicked] = useState(null); const active = picked && picked.direction === dir ? picked : ladder[Math.floor(ladder.length / 2)] ?? null; @@ -75,7 +103,9 @@ export function MarketDetail({ // ── place a real bet: a capped-loss perp with the bundled take-profit AT the // strike (the win boundary); the liquidation is the knockout (lose your stake). - // Same openPosition→sendTrade path the existing Updown ticket proves (app.tsx). + // Slippage clears the live spread (a hardcoded 1% would reject a 5–10% fill). + // NOTE: signed-fill reconciliation + the two-step commit land in fix #3; the + // trigger-price validation in fix #4. Until then the "don't trade" banner holds. const place = useCallback(async () => { if (!active || !signer) return; setPlacing(true); @@ -87,7 +117,7 @@ export function MarketDetail({ inputAmountUi: stakeUsd.toFixed(2), leverage: Number(active.construction.leverage.toFixed(4)), tradeType: active.construction.side, - slippagePercentage: "1", + slippagePercentage: slippageForSpread(active.spread), takeProfit: active.construction.takeProfitPrice.toFixed(4), owner: signer.owner, ...signer.tradeFields, @@ -126,87 +156,98 @@ export function MarketDetail({
- {/* the strike ladder — pick your odds by picking a strike */} -
-

{dir === "ABOVE" ? "climbs above" : "drops below"} — pick a strike

- {ladder.map((m) => { - const on = active === m; - const c = cents(m.prob); - return ( - - ); - })} -
+ {!limits ? ( +
+

pricing this market…

+ {Array.from({ length: 3 }).map((_, i) =>
)} +
+ ) : ( + <> + {/* the strike ladder — pick your odds by picking a strike */} +
+

{dir === "ABOVE" ? "climbs above" : "drops below"} — pick a strike

+ {ladder.map((m) => { + const on = active === m; + const c = cents(m.prob); + return ( + + ); + })} +
- {/* multi-outcome buckets — where does it land? */} -
-

where does it land?

- {buckets.buckets.map((b) => ( -
- {b.label} -
-
+ {/* multi-outcome buckets — where does it land? */} + {buckets && ( +
+

where does it land?

+ {buckets.buckets.map((b) => ( +
+ {b.label} +
+
+
+ {Math.round(b.prob * 100)}% +
+ ))}
- {Math.round(b.prob * 100)}% -
- ))} -
+ )} - {/* the buy ticket */} - {active && ( -
-
-

- {token} {dir === "ABOVE" ? "above" : "below"} {fmtStrike(active.strike)} -

- {yesCents}¢ -
+ {/* the buy ticket */} + {active && ( +
+
+

+ {token} {dir === "ABOVE" ? "above" : "below"} {fmtStrike(active.strike)} +

+ {yesCents}¢ +
- - - {/* the honest numbers — locked from the same math the trade uses */} -
- - - -
+ - - {result && ( -

{result.msg}

+ {/* the honest numbers — locked from the same math the trade uses */} +
+ + + +
+ + + {result && ( +

{result.msg}

+ )} +

+ Odds are formula-set on a real capped-loss position. You can never lose more than your stake. +

+
)} -

- Odds are formula-set on a real capped-loss position. You can never lose more than your stake. -

-
+ )}
diff --git a/examples/predict/lib/hooks.ts b/examples/predict/lib/hooks.ts index 1ab0f9e..db98012 100644 --- a/examples/predict/lib/hooks.ts +++ b/examples/predict/lib/hooks.ts @@ -14,6 +14,7 @@ import { type BasketSnapshot, type PositionMetrics, type PriceInfo, + type TokenInfo, } from "flash-v2"; import { useCallback, useEffect, useRef, useState } from "react"; import { baseConnection, flash } from "./flash"; @@ -99,15 +100,72 @@ export interface MarketLimits { // Flash's tiers carry a ×1.1 buffer over the advertised 10x/50x/100x/500x). const LEVERAGE_SCALE = 10_000; -/** - * Live leverage bounds for a market symbol: token config → custody (by mint) - * → pricing.minInitialLeverage/maxInitialLeverage. Cached per session; null - * until loaded (callers keep a conservative fallback meanwhile). Same - * philosophy as useMarkets: Flash changes limits → the UI follows, no code. - */ +type CustodyPricing = { min: number; max: number; spreadL: number; spreadS: number }; + +// ── one parse path, shared by the single + bulk hooks (the spread is a +// fund-critical number — it must be derived in exactly ONE place so the grid and +// the detail can never disagree). Custody + tokens are each fetched ONCE and +// deduped via in-flight promises, so N simultaneous cards trigger one of each. const limitsCache = new Map(); -let custodyPricing: Map | null = null; +let custodyPricing: Map | null = null; +let custodyInFlight: Promise | null> | null = null; +let tokensPromise: Promise | null = null; + +async function loadCustodyPricing(): Promise | null> { + if (custodyPricing && custodyPricing.size > 0) return custodyPricing; + if (custodyInFlight) return custodyInFlight; + custodyInFlight = (async () => { + try { + const res = await fetch(`${flash.network.apiBase}/raw/custodies`); + const json = (await res.json()) as + | Array<{ account?: { tokenMint?: string; pricing?: { minInitialLeverage?: number; maxInitialLeverage?: number; tradeSpreadLong?: number; tradeSpreadShort?: number } } }> + | { custodies?: unknown }; + const arr = Array.isArray(json) ? json : []; + const map = new Map(); + for (const c of arr) { + const a = c.account; + // the custody's token field is `tokenMint` (NOT `mint`) + if (a?.tokenMint && a.pricing?.maxInitialLeverage) { + map.set(a.tokenMint, { + min: (a.pricing.minInitialLeverage ?? LEVERAGE_SCALE) / LEVERAGE_SCALE, + max: a.pricing.maxInitialLeverage / LEVERAGE_SCALE, + // tradeSpread is in bps×1 (1000 = 10%): ÷1e4 → fraction + spreadL: (a.pricing.tradeSpreadLong ?? 0) / 10_000, + spreadS: (a.pricing.tradeSpreadShort ?? 0) / 10_000, + }); + } + } + // only cache a USEFUL result — an empty map must not wedge "loading…" + if (map.size > 0) custodyPricing = map; + return custodyPricing; + } catch { + return custodyPricing; // fallback stays null; next call retries + } finally { + custodyInFlight = null; + } + })(); + return custodyInFlight; +} + +function loadTokens(): Promise { + return (tokensPromise ??= flash.tokens().catch((e) => ((tokensPromise = null), Promise.reject(e)))); +} +/** token config → custody (by mint) → leverage bounds + per-side spreads. The + * SINGLE place a symbol becomes MarketLimits. Returns null when the custody + * isn't found (callers keep a conservative fallback / loading state meanwhile). */ +function buildLimits(symbol: string, pricing: Map, tokens: TokenInfo[]): MarketLimits | null { + const mint = tokens.find((t) => t.symbol.toUpperCase() === symbol.toUpperCase())?.mintKey; + const p = mint ? pricing.get(mint) : undefined; + if (!p) return null; + return { minLeverage: Math.max(1.1, p.min), maxLeverage: p.max, spreadLongPct: p.spreadL, spreadShortPct: p.spreadS }; +} + +/** + * Live leverage bounds + spreads for ONE market symbol. Cached per session; null + * until loaded. Same philosophy as useMarkets: Flash changes limits → the UI + * follows, no code. + */ export function useMarketLimits(marketSymbol: string): MarketLimits | null { const [limits, setLimits] = useState(limitsCache.get(marketSymbol) ?? null); useEffect(() => { @@ -117,41 +175,12 @@ export function useMarketLimits(marketSymbol: string): MarketLimits | null { let dead = false; void (async () => { try { - if (!custodyPricing || custodyPricing.size === 0) { - const res = await fetch(`${flash.network.apiBase}/raw/custodies`); - const json = (await res.json()) as - | Array<{ account?: { tokenMint?: string; pricing?: { minInitialLeverage?: number; maxInitialLeverage?: number; tradeSpreadLong?: number; tradeSpreadShort?: number } } }> - | { custodies?: unknown }; - const arr = Array.isArray(json) ? json : []; - const map = new Map(); - for (const c of arr) { - const a = c.account; - // the custody's token field is `tokenMint` (NOT `mint`) - if (a?.tokenMint && a.pricing?.maxInitialLeverage) { - map.set(a.tokenMint, { - min: (a.pricing.minInitialLeverage ?? LEVERAGE_SCALE) / LEVERAGE_SCALE, - max: a.pricing.maxInitialLeverage / LEVERAGE_SCALE, - // tradeSpread is in bps×1 (1000 = 10%): ÷1e4 → fraction - spreadL: (a.pricing.tradeSpreadLong ?? 0) / 10_000, - spreadS: (a.pricing.tradeSpreadShort ?? 0) / 10_000, - }); - } - } - // only cache a USEFUL result — an empty map must not wedge "loading…" - if (map.size > 0) custodyPricing = map; - } - if (!custodyPricing) return; - const tokens = await flash.tokens(); + const pricing = await loadCustodyPricing(); + if (dead || !pricing) return; + const tokens = await loadTokens(); if (dead) return; - const mint = tokens.find((t) => t.symbol.toUpperCase() === marketSymbol.toUpperCase())?.mintKey; - const p = mint ? custodyPricing.get(mint) : undefined; - if (p) { - const out: MarketLimits = { - minLeverage: Math.max(1.1, p.min), - maxLeverage: p.max, - spreadLongPct: p.spreadL, - spreadShortPct: p.spreadS, - }; + const out = buildLimits(marketSymbol, pricing, tokens); + if (out) { limitsCache.set(marketSymbol, out); setLimits(out); } @@ -162,6 +191,42 @@ export function useMarketLimits(marketSymbol: string): MarketLimits | null { return limits; } +/** + * Live limits for MANY symbols at once (the Discover grid). Shares the deduped + * custody + tokens fetches and the one `buildLimits` path with useMarketLimits, + * so a card and the detail it opens always price through the identical spread. + * Returns a Map keyed by symbol; absent until a symbol's custody resolves. + */ +export function useAllMarketLimits(symbols: string[]): Map { + const key = symbols.join(","); + const seed = () => { + const m = new Map(); + for (const s of symbols) { const c = limitsCache.get(s); if (c) m.set(s, c); } + return m; + }; + const [map, setMap] = useState>(seed); + useEffect(() => { + let dead = false; + void (async () => { + try { + const pricing = await loadCustodyPricing(); + if (dead || !pricing) return; + const tokens = await loadTokens(); + if (dead) return; + const next = new Map(); + for (const s of symbols) { + const out = limitsCache.get(s) ?? buildLimits(s, pricing, tokens); + if (out) { limitsCache.set(s, out); next.set(s, out); } + } + if (next.size > 0) setMap(next); + } catch { /* keep last good map; next change retries */ } + })(); + return () => { dead = true; }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [key]); + return map; +} + // ── owner snapshot stream ──────────────────────────────────────────────────── export type StreamStatus = "connecting" | "open" | "reconnecting" | "polling" | "closed"; diff --git a/examples/predict/lib/markets.ts b/examples/predict/lib/markets.ts index 4acb013..1f93a6d 100644 --- a/examples/predict/lib/markets.ts +++ b/examples/predict/lib/markets.ts @@ -1,5 +1,5 @@ // ───────────────────────────────────────────────────────────────────────────── -// lib/markets.ts — the PREDICTION-MARKET engine. +// lib/markets.ts — the PREDICTION-MARKET engine (v2.1, SPREAD-AWARE). // // Turns a price threshold into a YES/NO market priced in CENTS (= implied // probability), constructed underneath as a capped-loss Flash V2 perp with a @@ -14,19 +14,29 @@ // • What IS real: the underlying price, both barriers, and the iron rule — // you can NEVER lose more than your stake (the knockout = −stake, native). // -// THE MATH (derived in the feasibility study; see SPEC-PREDICT-V2.md): -// A perp is a clipped line; a binary is a step. Clamp BOTH ends — the native -// knockout floors loss at −C, a take-profit at the strike ceils the win at +P -// — and you get a one-touch binary {−C, +P}. Its YES price is: +// ── THE CORRECTED MATH — measure EVERYTHING from the FILL, not the oracle ───── +// A money-path audit (SPEC-PREDICT-V2 §v2.1) found the original engine modeled +// cost as a 0.16% fee and IGNORED Flash's real 5–10% trade spread. A LONG fills +// at oracle·(1+s), so a take-profit computed off the oracle lands INSIDE the +// spread and fires as a ~100% LOSS. The fix: the fill is the origin. // -// R (payout multiple) = L·(t − fee) // profit per $1 of stake -// q (YES prob, 0..1) = 1 / (1 + R) // the "cents" when ×100 +// fillEntry = oracle·(1 ± s) // + for ABOVE/LONG, − for BELOW/SHORT +// strike = fillEntry·(1 ± t) // the TP, set BEYOND the fill (the win) +// knockout = fillEntry·(1 ∓ 0.92/L) // liquidation (the loss) +// R = L·(t − fee) q = 1/(1 + R) // t is the move FROM THE FILL; q honest +// to-win = stake/q max-loss = stake // -// where L = leverage, t = favorable move to the strike (TP), C = stake, -// fee = round-trip fee rate. Leverage is set by the KNOCKOUT distance -// (koDist = 0.92/L), so the two real price levels — strike and knockout — -// fully define the odds. Symmetric barriers give q≈0.52 for ANY leverage; -// ASYMMETRY is what spans the ladder (far TP + near knockout = long shot). +// L = leverage, t = favorable move to the strike, fee = round-trip fee rate. +// Symmetric barriers give q≈0.52 for ANY leverage; ASYMMETRY spans the ladder +// (far TP + near knockout = long shot). Because the strike sits a full spread +// ABOVE the oracle, high-spread tokens (SOL ≈ 10%) are honestly long-shot-only. +// +// ── THE HARD CONSTRAINT — the knockout must clear the spread ────────────────── +// At high L, koDist = 0.92/L can be SMALLER than s → the position opens already +// past liquidation. So leverage is capped: L ≤ 0.92/(s·KO_MARGIN). On SOL +// (s=0.10) that is ≈ 6×. High-spread markets are naturally low-leverage. This +// invariant is enforced at the single chokepoint `priceMarket`, so NO market — +// however constructed — can be quoted with a knockout inside the spread. // ───────────────────────────────────────────────────────────────────────────── import { EST_ROUNDTRIP_FEE_RATE, LIQ_FACTOR, timeframe, type TimeframeId } from "./payoff"; @@ -39,17 +49,33 @@ export type Direction = "ABOVE" | "BELOW"; export const PROB_FLOOR = 0.03; export const PROB_CEIL = 0.97; -/** Far favorable move (%) the longest-shot strike sits at, per timeframe — keeps - * strikes within a believable move so leverage (and the knockout) stay sane. */ +/** The knockout must sit at least this multiple of the spread beyond the fill, + * so a fresh position is never already past liquidation. */ +export const KO_MARGIN = 1.5; + +/** Nominal favorable move (%) per timeframe — the move-scale that sets leverage + * for a target. While leverage is below the cap, rungs differ by leverage (near + * vs far knockout); once leverage pins at the spread cap (high-spread tokens), + * rungs differ by STRIKE instead, so the ladder still spans. */ const FAR_MOVE_PCT: Record = { "5m": 1.2, "15m": 2.5, "1h": 5 }; +/** Generous absolute ceiling on the strike move from the fill, per timeframe — + * bounds pathological far strikes only; large enough to never squash the default + * ladder. A deep long-shot that needs more move than this isn't offered (its q + * floats up to the deepest the timeframe honestly supports). */ +const STRIKE_MOVE_CEIL: Record = { "5m": 0.15, "15m": 0.35, "1h": 0.75 }; + +/** The knockout band per timeframe that sets the card headline's leverage + * (clamped by the spread constraint and custody caps). */ +const KO_BAND: Record = { "5m": 0.012, "15m": 0.025, "1h": 0.05 }; + /** The default odds ladder — the YES prices we offer per token+timeframe. */ const DEFAULT_TARGETS = [0.85, 0.7, 0.55, 0.4, 0.25] as const; export interface MarketConstruction { /** perp side expressing YES (ABOVE→LONG, BELOW→SHORT). */ side: TradeType; - /** target leverage — clamped to the market's live custody caps at open time. */ + /** target leverage — already clamped to the spread constraint + custody caps. */ leverage: number; /** take-profit price = the strike (the YES win boundary). */ takeProfitPrice: number; @@ -62,8 +88,12 @@ export interface Market { direction: Direction; /** the threshold price the question asks about (= the take-profit). */ strike: number; - /** the live price the odds were derived from (odds drift until you commit). */ + /** the live ORACLE price (what the card shows; the question is framed off it). */ + oracle: number; + /** the SIGNED-FILL entry the odds are actually measured from (oracle·(1±s)). */ entry: number; + /** the trade spread this market was priced through (side-appropriate fraction). */ + spread: number; timeframe: TimeframeId; /** implied probability of YES, clamped to [PROB_FLOOR, PROB_CEIL]. */ prob: number; @@ -99,88 +129,190 @@ export function profitUsd(stakeUsd: number, prob: number): number { return toWinUsd(stakeUsd, prob) - stakeUsd; } -// ── the core: a strike + a knockout → leverage, odds, payout ────────────────── +// ── the spread-aware primitives ─────────────────────────────────────────────── + +/** The signed-fill entry price — the ORIGIN every barrier is measured from. + * A LONG buys at the ask (oracle·(1+s)); a SHORT sells at the bid (oracle·(1−s)). */ +export function fillEntryFrom(oracle: number, spread: number, direction: Direction): number { + const s = Math.max(0, spread); + return direction === "ABOVE" ? oracle * (1 + s) : oracle * (1 - s); +} + +/** The highest leverage whose knockout (0.92/L from the fill) still clears the + * spread, intersected with the custody cap. Spread-free markets (FX) are capped + * only by custody. This is THE constraint that stops an already-liquidated open. */ +export function maxLeverageForSpread(spread: number, custodyMaxLeverage: number): number { + const s = Math.max(0, spread); + const spreadCap = s > 0 ? LIQ_FACTOR / (s * KO_MARGIN) : Infinity; + const custody = custodyMaxLeverage > 0 ? custodyMaxLeverage : Infinity; + return Math.max(1, Math.min(custody, spreadCap)); +} -/** Price one market from its two real price levels (strike = TP = YES win, - * knockoutPrice = liquidation = NO/lose). Leverage is implied by the knockout - * distance; the odds fall out of the construction. Pure. */ +// ── the core: oracle + spread + leverage + strike → odds, payout, both barriers ── + +/** Price one market from live facts. THE CHOKEPOINT: leverage is clamped here so + * the knockout always clears the spread (CRIT-2), regardless of caller. The odds + * fall out of the construction. Pure — never throws, never returns NaN. */ export function priceMarket(opts: { token: string; direction: Direction; - entry: number; + /** live oracle price (> 0; a non-positive oracle yields an unpriceable q=floor). */ + oracle: number; + /** side-appropriate trade spread as a fraction (SOL≈0.10, BTC/ETH≈0.05, FX 0). */ + spread: number; + /** desired leverage; clamped down so koDist ≥ spread·KO_MARGIN. */ + leverage: number; + /** the strike / take-profit price the question asks about. */ strike: number; - knockoutPrice: number; timeframe: TimeframeId; feesRate?: number; }): PricedMarket { - const { token, direction, entry, strike, knockoutPrice, timeframe: tf } = opts; + const { token, direction, timeframe: tf } = opts; + const oracle = opts.oracle; + const spread = Math.max(0, opts.spread); const feesRate = opts.feesRate ?? EST_ROUNDTRIP_FEE_RATE; const side: TradeType = direction === "ABOVE" ? "LONG" : "SHORT"; - const t = entry > 0 ? Math.abs(strike - entry) / entry : 0; // favorable move to TP - const koDist = entry > 0 ? Math.abs(entry - knockoutPrice) / entry : 0; // adverse move to knockout - // koDist = 0.92 / L ⇒ L = 0.92 / koDist. Fees scale with size, so fees/C = feesRate·L, - // giving R = L·t − feesRate·L = L·(t − feesRate). - const leverage = koDist > 0 ? LIQ_FACTOR / koDist : 1; - const payoutMult = Math.max(0, leverage * (t - feesRate)); + // Spread invariant enforced HERE: no quoted market can have a knockout inside + // the spread, even if a caller passes an unclamped leverage. + const lev = clampLeverage(opts.leverage, spread, Infinity); + const fillEntry = oracle > 0 ? fillEntryFrom(oracle, spread, direction) : 0; + + // t = favorable move FROM THE FILL to the strike; koDist = adverse move to liq. + const t = fillEntry > 0 ? Math.abs(opts.strike - fillEntry) / fillEntry : 0; + const koDist = LIQ_FACTOR / lev; + const knockoutPrice = direction === "ABOVE" ? fillEntry * (1 - koDist) : fillEntry * (1 + koDist); + + // R = L·t − fees/C = L·(t − fee) (fees scale with size). t ≤ fee ⇒ R floors at 0. + const payoutMult = Math.max(0, lev * (t - feesRate)); const prob = clampProb(1 / (1 + payoutMult)); return { - token, direction, strike, entry, timeframe: tf, prob, payoutMult, - construction: { side, leverage, takeProfitPrice: strike, knockoutPrice }, + token, direction, strike: opts.strike, oracle, entry: fillEntry, spread, timeframe: tf, prob, payoutMult, + construction: { side, leverage: lev, takeProfitPrice: opts.strike, knockoutPrice }, }; } -/** Solve the construction for a TARGET probability — pick leverage so the strike - * lands within the timeframe's believable move, then derive both price levels. - * This is how the ladder is generated (target 85¢, 70¢, 55¢, … and solve). */ +/** Clamp a desired leverage into [floor, spread∧custody cap]. */ +function clampLeverage(desired: number, spread: number, custodyMaxLeverage: number, floor = 1): number { + const cap = maxLeverageForSpread(spread, custodyMaxLeverage); + const lo = Math.max(1, floor); + const d = Number.isFinite(desired) && desired > 0 ? desired : lo; + return Math.max(lo, Math.min(cap, d)); +} + +/** Solve the construction for a TARGET probability — pick the leverage that lands + * the strike within the timeframe's believable move, clamp it to the spread + + * custody caps, then derive both price levels from the FILL. This is how each + * ladder rung is generated. On high-spread tokens a deep long-shot target may be + * unreachable within the leverage cap; q then floats to the deepest the token + * honestly allows (the strike is held at the believable-move boundary). */ export function marketForTargetProb(opts: { token: string; direction: Direction; - entry: number; + oracle: number; + spread: number; + maxLeverage: number; + minLeverage?: number; timeframe: TimeframeId; targetProb: number; - maxLeverage?: number; feesRate?: number; }): PricedMarket { - const { token, direction, entry, timeframe: tf, targetProb } = opts; + const { token, direction, oracle, spread, timeframe: tf, targetProb } = opts; const feesRate = opts.feesRate ?? EST_ROUNDTRIP_FEE_RATE; - const maxLev = opts.maxLeverage ?? 100; + const floor = Math.max(1.1, opts.minLeverage ?? 1.1); const q = clampProb(targetProb); const R = (1 - q) / q; // payout multiple needed for this YES price - const tFar = FAR_MOVE_PCT[tf] / 100; // keep the strike within a believable move - // L from t = R/L + fee ≤ tFar ⇒ L ≥ R / (tFar − fee). Clamp to [1.1, maxLev]. - const span = Math.max(tFar - feesRate, 1e-4); - const leverage = Math.min(maxLev, Math.max(1.1, R / span)); - const t = R / leverage + feesRate; // favorable move to the strike - const koDist = LIQ_FACTOR / leverage; // adverse move to the knockout - - const strike = direction === "ABOVE" ? entry * (1 + t) : entry * (1 - t); - const knockoutPrice = direction === "ABOVE" ? entry * (1 - koDist) : entry * (1 + koDist); - // Re-price from the realized levels so prob reflects the clamped leverage exactly. - return priceMarket({ token, direction, entry, strike, knockoutPrice, timeframe: tf, feesRate }); + const nominal = FAR_MOVE_PCT[tf] / 100; // move-scale that sets leverage + const span = Math.max(nominal - feesRate, 1e-4); + // Target the leverage that hits R at the nominal move, clamped to [floor, cap]. + // When the cap bites (high-spread tokens), leverage pins and the strike extends. + const leverage = clampLeverage(R / span, spread, opts.maxLeverage, floor); + // Strike move follows from the (possibly pinned) leverage; bounded only by the + // pathology ceiling. Pinned leverage ⇒ deeper rungs ⇒ farther strikes (the + // ladder spans by STRIKE). If the ceiling bites, q floats up (honest). + const t = Math.min(R / leverage + feesRate, STRIKE_MOVE_CEIL[tf]); + + const fillEntry = oracle > 0 ? fillEntryFrom(oracle, spread, direction) : 0; + const strike = direction === "ABOVE" ? fillEntry * (1 + t) : fillEntry * (1 - t); + // Re-price from the realized strike + clamped leverage so prob is exact. + return priceMarket({ token, direction, oracle, spread, leverage, strike, timeframe: tf, feesRate }); } -/** A token+timeframe+direction → the odds ladder (a row of YES markets). */ +/** A token+timeframe+direction → the odds ladder (a row of YES markets). Rungs + * that collapse to the same strike under the leverage cap (high-spread tokens) + * are de-duplicated, so the ladder never shows two identical bets. */ export function strikeLadder(opts: { token: string; - entry: number; + oracle: number; + spread: number; + maxLeverage: number; + minLeverage?: number; direction: Direction; timeframe: TimeframeId; targets?: readonly number[]; - maxLeverage?: number; }): PricedMarket[] { const targets = opts.targets ?? DEFAULT_TARGETS; - return targets.map((targetProb) => - marketForTargetProb({ - token: opts.token, direction: opts.direction, entry: opts.entry, - timeframe: opts.timeframe, targetProb, maxLeverage: opts.maxLeverage, - }), - ); + const out: PricedMarket[] = []; + const seen = new Set(); + for (const targetProb of targets) { + const m = marketForTargetProb({ + token: opts.token, direction: opts.direction, oracle: opts.oracle, spread: opts.spread, + maxLeverage: opts.maxLeverage, minLeverage: opts.minLeverage, timeframe: opts.timeframe, targetProb, + }); + const key = m.strike.toPrecision(6); + if (seen.has(key)) continue; + seen.add(key); + out.push(m); + } + return out; +} + +// ── the card headline ───────────────────────────────────────────────────────── + +/** Round a price UP to a human "nice" number a believable bit above. Basing this + * on the FILL (not the oracle) guarantees the strike sits above the entry, so the + * headline bet is genuinely winnable. */ +export function niceStrikeAbove(price: number): number { + if (!(price > 0)) return 0; + const min = price * 1.02; + const mag = Math.pow(10, Math.floor(Math.log10(min))); + for (const s of [1, 1.25, 1.5, 2, 2.5, 3, 4, 5, 6, 7, 7.5, 8, 9, 10]) { + const cand = s * mag; + if (cand >= min) return Number(cand.toPrecision(4)); + } + return Number((10 * mag).toPrecision(4)); +} + +/** The single "climb above" headline a card shows: a nice strike above the fill, + * priced through the live spread at the timeframe's (clamped) leverage. Each + * token's own spread + caps make the cents FLOAT — no two markets read alike. */ +export function headlineMarket(opts: { + token: string; + oracle: number; + spread: number; + maxLeverage: number; + minLeverage?: number; + timeframe: TimeframeId; + direction?: Direction; +}): PricedMarket { + const direction = opts.direction ?? "ABOVE"; + const floor = Math.max(1.1, opts.minLeverage ?? 1.1); + const fillEntry = opts.oracle > 0 ? fillEntryFrom(opts.oracle, opts.spread, direction) : 0; + const strike = direction === "ABOVE" ? niceStrikeAbove(fillEntry) : fillEntry * (1 - KO_BAND[opts.timeframe]); + const leverage = clampLeverage(LIQ_FACTOR / KO_BAND[opts.timeframe], opts.spread, opts.maxLeverage, floor); + return priceMarket({ + token: opts.token, direction, oracle: opts.oracle, spread: opts.spread, + leverage, strike, timeframe: opts.timeframe, + }); } // ── multi-outcome buckets ("where does it land?") ───────────────────────────── +// NOTE: spread-aware port. The OUTCOME SEMANTICS (true bounded buckets vs. a +// one-touch proxy) are redesigned in SPEC §v2.1 fix #7; this keeps the existing +// shape but prices every bucket through the fill + clamped leverage so nothing +// is quoted spread-blind in the meantime. export interface OutcomeBucket { label: string; @@ -189,29 +321,31 @@ export interface OutcomeBucket { hi: number | null; /** normalized probability of landing in this bucket (sums to ~1 across buckets). */ prob: number; - /** the perp that expresses "lands in this bucket" (ABOVE the lo edge, TP'd below hi). */ + /** the perp that expresses "lands in this bucket". */ market: PricedMarket; } export interface BucketMarket { token: string; - entry: number; + oracle: number; timeframe: TimeframeId; buckets: OutcomeBucket[]; } /** Build a multi-outcome market by partitioning the price axis at `edges` - * (ascending prices). Each bucket's raw probability comes from the perp - * construction; raw probabilities are normalized to sum to 1 (a proper - * outcome distribution), the way a real multi-outcome market's prices sum to $1. */ + * (ascending prices). Each bucket's raw probability comes from a spread-aware + * perp construction; raw probabilities are normalized to sum to 1. */ export function bucketMarket(opts: { token: string; - entry: number; + oracle: number; + spread: number; + maxLeverage: number; + minLeverage?: number; timeframe: TimeframeId; edges: number[]; - maxLeverage?: number; }): BucketMarket { - const { token, entry, timeframe: tf } = opts; + const { token, oracle, spread, timeframe: tf } = opts; + const floor = Math.max(1.1, opts.minLeverage ?? 1.1); const edges = [...opts.edges].sort((a, b) => a - b); // bucket boundaries: (-∞, e0), [e0, e1), … , [eN, +∞) const bounds: { lo: number | null; hi: number | null }[] = []; @@ -220,15 +354,14 @@ export function bucketMarket(opts: { if (edges.length > 0) bounds.push({ lo: edges[edges.length - 1]!, hi: null }); const raw = bounds.map((b) => { - // Represent "lands in [lo,hi)" by the side that points at the bucket's centre, - // with the TP at the near edge — a faithful one-touch proxy for the bucket. + // Point the perp at the bucket's centre, TP at the near edge — a one-touch + // proxy for the bucket (redesigned to a true bounded outcome in fix #7). const centre = b.lo == null ? b.hi! * 0.99 : b.hi == null ? b.lo * 1.01 : (b.lo + b.hi) / 2; - const direction: Direction = centre >= entry ? "ABOVE" : "BELOW"; + const direction: Direction = centre >= oracle ? "ABOVE" : "BELOW"; const strike = b.lo == null ? b.hi! : b.hi == null ? b.lo : (direction === "ABOVE" ? b.lo : b.hi); - const koDistPct = FAR_MOVE_PCT[tf] / 100; - const knockoutPrice = direction === "ABOVE" ? entry * (1 - koDistPct) : entry * (1 + koDistPct); - const m = priceMarket({ token, direction, entry, strike, knockoutPrice, timeframe: tf }); - return { b, m, direction }; + const leverage = clampLeverage(LIQ_FACTOR / (FAR_MOVE_PCT[tf] / 100), spread, opts.maxLeverage, floor); + const m = priceMarket({ token, direction, oracle, spread, leverage, strike, timeframe: tf }); + return { b, m }; }); const sum = raw.reduce((s, r) => s + r.m.prob, 0) || 1; @@ -239,7 +372,7 @@ export function bucketMarket(opts: { prob: m.prob / sum, // normalized so the outcome distribution sums to 1 market: m, })); - return { token, entry, timeframe: tf, buckets }; + return { token, oracle, timeframe: tf, buckets }; } function bucketLabel(lo: number | null, hi: number | null): string { @@ -253,6 +386,8 @@ function bucketLabel(lo: number | null, hi: number | null): string { /** The flash.openPosition params that OPEN a YES position for this market at a * given stake — a capped-loss perp with the bundled take-profit = the strike. + * Slippage MUST clear the spread (a fill enters a full spread from the oracle), + * so it is derived from the market's spread, never a hardcoded 1%. * (Shape matches OpenPositionRequest; the caller adds owner + session fields.) */ export function openParamsFor(m: PricedMarket, stakeUsd: number) { return { @@ -262,12 +397,21 @@ export function openParamsFor(m: PricedMarket, stakeUsd: number) { leverage: Number(m.construction.leverage.toFixed(4)), tradeType: m.construction.side, orderType: "MARKET" as const, + slippagePercentage: slippageForSpread(m.spread), // bundled TP = the YES win boundary; the knockout is the native liquidation. takeProfit: m.construction.takeProfitPrice.toFixed(4), }; } -/** Human one-liner for a market question (plain words, no trading vocabulary). */ +/** Slippage tolerance that clears the trade spread with headroom. A hardcoded + * "1" is far below a 5–10% spread and rejects every fill (SPEC §v2.1 fix #8). */ +export function slippageForSpread(spread: number): string { + const pct = Math.max(1, Math.ceil((Math.max(0, spread) * 1.5) * 100)); + return String(pct); +} + +/** Human one-liner for a market question (plain words, no trading vocabulary). + * Framed off the ORACLE — the price a person sees — not the internal fill. */ export function questionFor(m: Market): string { const tf = timeframe(m.timeframe).label; const f = (n: number) => (n >= 1000 ? n.toLocaleString(undefined, { maximumFractionDigits: 0 }) : n.toFixed(2)); From f67e8842bac4ab2ae1ffa928dda9397a284d906f Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 10:37:30 -0400 Subject: [PATCH 15/26] predict v2.1: double-spread engine correction + two-step reconcile (fixes #3,#4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LIVE-PROBE FINDING (read-only mainnet quote): the spec's v2.1 math charged the spread only on ENTRY. The venue charges it AGAIN on exit — a take-profit triggering at K exits at K*(1∓s). A $90 SOL TP came back exitPriceUi=$81, profitUsdUi≈$0: a bet shown as 59¢/'to-win $42' actually won ~$0. The single-spread strike landed at break-even. Engine (lib/markets.ts): strike now grossed up by 1/(1−s) so the NET entry-fill →exit-fill move equals t; priceMarket derives t from exitFill (both legs). headlineMarket targets a real prob via the corrected solver then rounds the strike close (was overshooting 58¢→30¢). VERIFIED LIVE: across SOL+BTC headline + full ladder, engine shown-to-win matches the API takeProfitQuote within 3%. Two-step commit (#3) + validateTriggerPrice (#4): market-detail review→confirm. The review quotes the REAL fill and BLOCKS the sign if (a) the strike fails validateTriggerPrice vs real entry [#4], (b) no takeProfitQuote, (c) profitUsdUi≤0 (inside the round-trip spread), or (d) live payout <60% of shown (stale-price divergence guard). Confirm re-quotes + re-checks before send; the validated quote IS the signed tx (no TOCTOU). The venue's profitUsdUi is ground truth — never trust local math over it. Pure + LIVE verified; typecheck green. Banner stays up pending the 3-agent re-review. Remaining: settlement (#5), gates (#6), buckets (#7). Co-Authored-By: Claude Opus 4.8 --- examples/predict/SPEC-PREDICT-V2.md | 20 +- examples/predict/components/market-detail.tsx | 221 ++++++++++++++---- examples/predict/lib/markets.ts | 80 ++++--- 3 files changed, 240 insertions(+), 81 deletions(-) diff --git a/examples/predict/SPEC-PREDICT-V2.md b/examples/predict/SPEC-PREDICT-V2.md index 421dda8..3e5c733 100644 --- a/examples/predict/SPEC-PREDICT-V2.md +++ b/examples/predict/SPEC-PREDICT-V2.md @@ -75,19 +75,23 @@ Keep the existing "Void Instrument / frosted glass" base (`globals.css`, `DESIGN The 3-reviewer money-path audit (2026-06-15) found the v2 engine **funds-loss-grade broken**: it modeled cost as a 0.16% fee and **ignored the real 5–10% Flash trade spread**, so the bundled take-profit landed *inside* the spread and fired as a ~100% loss on SOL (live `pnlPercentage: −100`), leverage was never clamped (SOL opened at 100× already-liquidated), and nothing was reconciled to the signed fill. Decision: **keep the true fixed-odds binary (Path A), corrected for the spread.** `$11` floor is correct here — a bundled TP needs `> $10` collateral after fees (`guards.ts`: `MIN_COLLATERAL_USD_AFTER_FEES=10`, `RECOMMENDED_MIN_COLLATERAL_USD=11`). (A bare open's floor is ~$5, but our win = a bundled TP, so $11 stands.) -### The corrected construction (the entry is the FILL, not the oracle) +### The corrected construction — the spread is paid on BOTH legs (EMPIRICALLY corrected 2026-06-15) -A LONG fills at `fillEntry = oracle·(1+s)`; a SHORT at `oracle·(1−s)`, where `s = tradeSpread` (live, per market, from `useMarketLimits` — SOL≈0.10, BTC/ETH≈0.05). **Everything is measured from `fillEntry`, never the oracle:** +A LONG fills at `entryFill = oracle·(1+s)`; a SHORT at `oracle·(1−s)`, where `s = tradeSpread` (live, per market, from `useMarketLimits` — SOL≈0.10, BTC/ETH≈0.05). + +**Implementation discovery (live probe, read-only quote):** the v2.1 math above was STILL incomplete — it only charged the spread on ENTRY. The live API charges it AGAIN on exit: a take-profit triggering at price `K` *exits* at `K·(1∓s)` (a LONG sells back at the bid). Proof: a $90 SOL TP returned `exitPriceUi=$81` (=90×0.9) and `profitUsdUi≈$0` — break-even, not the 59¢ winner the entry-only math claimed. **A take-profit must clear TWO spreads to win.** The corrected, live-matched construction: ``` -fillEntry = oracle·(1 ± s) # + for ABOVE/LONG, − for BELOW/SHORT -strike = fillEntry·(1 ± t) # the TP, set BEYOND the fill by t (the win) -knockout = fillEntry·(1 ∓ 0.92/L) # liquidation (the loss) -R = L·(t − fee) q = 1/(1+R) # t is the move FROM THE FILL; q honest +entryFill = oracle·(1 ± s) # + ABOVE/LONG, − BELOW/SHORT (pay the spread on entry) +exitFill = strike·(1 ∓ s) # the TP EXITS through the spread too (pay it again) +t = exitFill/entryFill − 1 (LONG) # the NET move that drives PnL (∓ mirrored for SHORT) +strike = entryFill·(1 ± t)/(1 ∓ s) # ⇒ gross the strike UP by 1/(1−s) so net move = t +knockout = entryFill·(1 ∓ 0.92/L) # liquidation (the loss); builder's liq is degenerate +R = L·(t − fee) q = 1/(1+R) # q now matches the venue's real payout to-win = stake/q max-loss = stake ``` -Because the strike is `fillEntry·(1+t)`, on SOL it sits `≈ s+t ≈ 12%+` above the oracle — honest (the bet truly needs a big move), and **long-shot-only on high-spread tokens, exactly as accepted.** +Break-even (LONG) is `strike = oracle·(1+s)/(1−s)` ≈ ×1.222 on SOL — so a genuine 58¢ SOL headline strike sits at ≈ `$100` when the oracle is `$74` (oracle×1.36), and the bet truly needs a big move. **Verified live end-to-end:** across the SOL and BTC headline + full ladder, the engine's shown to-win matches the API's `takeProfitQuote` to within 3%. The commit-gate reconcile (#3) is the backstop — it reads the venue's own `profitUsdUi` and blocks if the real payout diverges from what was shown. ### The new hard constraint: the knockout must sit BEYOND the spread @@ -103,7 +107,7 @@ SOL (s=0.10) ⇒ `L ≤ 0.92/0.15 ≈ 6×`. High-spread markets are naturally lo 1. **Engine spread-aware** (`lib/markets.ts`): `priceMarket`/`marketForTargetProb`/`strikeLadder`/`bucketMarket` take `oracle` + `spread` + `maxLeverage`; compute from `fillEntry`; clamp `L` by the constraint above; the `Market` carries `oracle` (display) and `entry`=`fillEntry`. **Fixes CRIT-1, CRIT-2, HIGH-4.** 2. **Wire live spread + caps**: card/detail call `useMarketLimits(token)` → pass `tradeSpread` + `maxLeverage` into the engine. No market is built before limits load (gate the ticket). -3. **Reconcile to the signed fill** (`place()`): after `openPosition`, build `lockedQuoteFrom(res,…)`, render the REAL entry/liq/TP/fee, and **block the send / show the result** if `takeProfitQuote.profitUsdUi ≤ 0` or liq is on the wrong side. Re-quote at the commit gate (two-step like `app.tsx doReview→doConfirm`). **Fixes CRIT-3, MEDIUM (stale price).** +3. **Reconcile to the signed fill** (two-step review→confirm): quote the REAL fill, render the venue's own numbers, and **block the commit** if (a) the strike fails `validateTriggerPrice` vs the real entry, (b) the API returns no `takeProfitQuote`, (c) `takeProfitQuote.profitUsdUi ≤ 0` (TP inside the round-trip spread → fires as a loss), or (d) the live payout falls below 60% of the shown odds (stale-price / model-drift divergence guard). Re-quote + re-check at confirm (two-step like `app.tsx doReview→doConfirm`). The venue's `profitUsdUi` is ground truth — never trust local math over it. **Fixes CRIT-3, MEDIUM (stale price).** 4. **Validate the TP**: call `guards.validateTriggerPrice({side, kind:"tp", price:strike, markPrice:fillEntry})` before sending; refuse on `!ok` (avoids on-chain `6057`). 5. **Settlement + expiry** (port `lib/rounds.ts`): record a `Round{…expiresAt}` on a confirmed send; run the reconcile/settle watcher; close at expiry by mark (`inputUsdUi:"0"`) for the between-barrier case. Render a **/markets-specific** disclosure (don't inherit Updown's). **Fixes HIGH-5.** 6. **Gates + errors**: `canBet` floor → `MIN_STAKE` (11); add `stake > balance` block; double-submit guard (in-flight set keyed by owner|market|side, re-check live balance); route `place()`/`enable()` errors through `calmError`; surface the real `enable` step error. **Fixes C3, H1, H2, M1.** diff --git a/examples/predict/components/market-detail.tsx b/examples/predict/components/market-detail.tsx index f869170..62cb51b 100644 --- a/examples/predict/components/market-detail.tsx +++ b/examples/predict/components/market-detail.tsx @@ -14,8 +14,8 @@ "use client"; -import { useCallback, useMemo, useState } from "react"; -import { assertNoErr } from "flash-v2"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { assertNoErr, validateTriggerPrice, type OpenPositionResponse } from "flash-v2"; import { bucketMarket, cents, @@ -28,7 +28,7 @@ import { } from "@/lib/markets"; import { useMarketLimits } from "@/lib/hooks"; import { timeframe, type TimeframeId } from "@/lib/payoff"; -import { fmtPrice } from "@/lib/copy"; +import { calmError, fmtPrice } from "@/lib/copy"; import { flash } from "@/lib/flash"; import type { ActiveSigner } from "@/lib/signer"; import { TokenIcon } from "./token-icon"; @@ -43,6 +43,59 @@ function defaultEdges(price: number): number[] { return [0.985, 0.995, 1.005, 1.015].map((m) => price * m); } +/** The bet reconciled to the SIGNED response — the authoritative numbers the user + * actually signs, never the pre-trade estimate. `ok:false` blocks the commit. */ +interface ReconciledBet { + ok: boolean; + reason?: string; + /** real fill entry from the venue. */ + entry: number; + /** price the take-profit actually fires at. */ + tpExit: number; + /** profit the API computes for the TP ON THIS FILL (the spread guard). */ + profitUsd: number; + toWinUsd: number; + maxLossUsd: number; + entryFeeUsd: number; + liq: number; +} + +/** Reconcile an open-position response against the market we meant to back, and + * decide whether it can honestly be signed. THREE gates, all from the real fill: + * (a) the strike is a valid TP trigger vs the real entry (else on-chain 6057) — fix #4 + * (b) the venue actually priced a take-profit + * (c) THE SPREAD GUARD: the TP nets a profit > 0. profit ≤ 0 means the strike + * landed inside the spread and would fire as a ~100% LOSS — the exact bug + * the v2.1 redesign exists to kill. The user can never sign through it. */ +function reconcileBet(res: OpenPositionResponse, m: PricedMarket, stakeUsd: number): ReconciledBet { + const side = m.construction.side; + const entry = Number(res.newEntryPrice) || 0; + const liq = Number(res.newLiquidationPrice) || 0; + const entryFeeUsd = Number(res.entryFee) || 0; + const tp = res.takeProfitQuote; + // The venue's OWN profit-at-TP on the real fill — ground truth. The spread is + // paid on BOTH legs, so this already nets the exit spread (a TP inside the + // round-trip spread reports ≤ 0). Never trust local math over this number. + const realProfitUsd = tp ? Number(tp.profitUsdUi) || 0 : 0; + const tpExit = tp ? Number(tp.exitPriceUi) || 0 : 0; + const base = { entry, tpExit, profitUsd: realProfitUsd, toWinUsd: stakeUsd + Math.max(0, realProfitUsd), maxLossUsd: stakeUsd, entryFeeUsd, liq }; + + const valid = validateTriggerPrice({ side, kind: "tp", price: m.construction.takeProfitPrice, markPrice: entry }); + if (!valid.ok) return { ...base, ok: false, reason: "This strike isn't beyond the live fill, so the bet can't pay out. Pick a farther strike." }; + if (!tp) return { ...base, ok: false, reason: "Couldn't price the take-profit on this fill. Try again in a moment." }; + // THE SPREAD GUARD: on the real round-trip the TP must net a profit, else the + // move is inside the (two-leg) spread and would fire as a loss. + if (realProfitUsd <= 0) return { ...base, ok: false, reason: "On the live fill this bet can't win right now — the move is inside the spread. Try a farther strike or another market." }; + // DIVERGENCE GUARD: the live payout must be close to the odds we displayed. + // A big shortfall (stale browse price / model drift) blocks rather than let the + // user sign a bet materially worse than advertised. + const shownProfit = profitUsd(stakeUsd, m.prob); + if (shownProfit > 0 && realProfitUsd < shownProfit * 0.6) { + return { ...base, ok: false, reason: `The live payout dropped to about ${fmtUsd(base.toWinUsd)} (we showed ~${fmtUsd(stakeUsd + shownProfit)}). Re-review or pick another strike.` }; + } + return { ...base, ok: true }; +} + export function MarketDetail({ token, price, @@ -98,41 +151,81 @@ export function MarketDetail({ const tooSmall = stakeUsd > 0 && stakeUsd < MIN_STAKE; const yesCents = active ? cents(active.prob) : 0; - const [placing, setPlacing] = useState(false); + type Phase = "idle" | "quoting" | "review" | "signing"; + const [phase, setPhase] = useState("idle"); + const [review, setReview] = useState(null); + const [ticketError, setTicketError] = useState(null); const [result, setResult] = useState<{ ok: boolean; msg: string } | null>(null); - // ── place a real bet: a capped-loss perp with the bundled take-profit AT the - // strike (the win boundary); the liquidation is the knockout (lose your stake). - // Slippage clears the live spread (a hardcoded 1% would reject a 5–10% fill). - // NOTE: signed-fill reconciliation + the two-step commit land in fix #3; the - // trigger-price validation in fix #4. Until then the "don't trade" banner holds. - const place = useCallback(async () => { - if (!active || !signer) return; - setPlacing(true); + // Any change to the bet (strike, side, stake) invalidates a pending review — + // you can only sign numbers you just reviewed. + useEffect(() => { + setPhase((p) => (p === "quoting" || p === "signing" ? p : "idle")); + setReview(null); + }, [active, stakeUsd]); + + // The openPosition request — IDENTICAL for the review quote and the signed + // commit, so the numbers you review are the numbers you sign. Slippage clears + // the live spread; the take-profit IS the YES win (= the strike). + const reqFor = useCallback( + (m: PricedMarket) => ({ + inputTokenSymbol: "USDC", + outputTokenSymbol: m.token, + inputAmountUi: stakeUsd.toFixed(2), + leverage: Number(m.construction.leverage.toFixed(4)), + tradeType: m.construction.side, + slippagePercentage: slippageForSpread(m.spread), + takeProfit: m.construction.takeProfitPrice.toFixed(4), + }), + [stakeUsd], + ); + + // STEP 1 — review: quote the REAL fill (no owner = no tx), reconcile, and BLOCK + // if the take-profit can't pay out on this fill. The user never reaches a sign + // prompt for a bet that's a loss by construction. + const doReview = useCallback(async () => { + if (!active) return; + setTicketError(null); setResult(null); + setPhase("quoting"); + try { + const res = await flash.openPosition(reqFor(active)); + assertNoErr("open-position quote", res); + setReview(reconcileBet(res, active, stakeUsd)); + setPhase("review"); + } catch (e) { + setTicketError(calmError(e)); + setPhase("idle"); + } + }, [active, reqFor, stakeUsd]); + + // STEP 2 — confirm: re-quote WITH owner (the price may have moved since review), + // re-run the same reconciliation, and only sign + send if it STILL passes. A + // re-quote that went bad bumps back to the review with the new reason. + const doConfirm = useCallback(async () => { + if (!active || !signer) return; + setPhase("signing"); + setTicketError(null); try { - const res = await flash.openPosition({ - inputTokenSymbol: "USDC", - outputTokenSymbol: active.token, - inputAmountUi: stakeUsd.toFixed(2), - leverage: Number(active.construction.leverage.toFixed(4)), - tradeType: active.construction.side, - slippagePercentage: slippageForSpread(active.spread), - takeProfit: active.construction.takeProfitPrice.toFixed(4), - owner: signer.owner, - ...signer.tradeFields, - }); + const res = await flash.openPosition({ ...reqFor(active), owner: signer.owner, ...signer.tradeFields }); assertNoErr("open-position", res); + const recon = reconcileBet(res, active, stakeUsd); + if (!recon.ok) { + setReview(recon); + setPhase("review"); + return; + } if (!res.transactionBase64) throw new Error(res.err ?? "no transaction returned"); const sent = await signer.sendTrade(res.transactionBase64); setResult({ ok: true, msg: `Bet placed · ${sent.signature.slice(0, 8)}…` }); + setReview(null); + setPhase("idle"); onPlaced?.(); } catch (e) { - setResult({ ok: false, msg: (e as Error).message }); - } finally { - setPlacing(false); + setTicketError(calmError(e)); + setPhase("review"); } - }, [active, signer, stakeUsd, onPlaced]); + }, [active, signer, reqFor, stakeUsd, onPlaced]); return (
@@ -226,21 +319,56 @@ export function MarketDetail({
- - {result && ( -

{result.msg}

+ {(phase === "review" || phase === "signing") && review ? ( +
+ {/* RECONCILED to the signed fill — the numbers you actually sign */} +
+ + + + = 0 ? "+" : "−"}${fmtUsd(Math.abs(review.profitUsd))}`} tone={review.profitUsd > 0 ? "up" : "down"} /> + + +
+ {!review.ok &&

{review.reason}

} + {ticketError &&

{ticketError}

} +
+ + +
+
+ ) : ( + <> + + {result && ( +

{result.msg}

+ )} + {ticketError &&

{ticketError}

} + )}

Odds are formula-set on a real capped-loss position. You can never lose more than your stake. @@ -262,3 +390,12 @@ function Stat({ label, value, tone }: { label: string; value: string; tone: "up"

); } + +function ReconRow({ k, v, tone }: { k: string; v: string; tone?: "up" | "down" }) { + return ( +
+ {k} + {v} +
+ ); +} diff --git a/examples/predict/lib/markets.ts b/examples/predict/lib/markets.ts index 1f93a6d..578aad9 100644 --- a/examples/predict/lib/markets.ts +++ b/examples/predict/lib/markets.ts @@ -65,13 +65,12 @@ const FAR_MOVE_PCT: Record = { "5m": 1.2, "15m": 2.5, "1h": * floats up to the deepest the timeframe honestly supports). */ const STRIKE_MOVE_CEIL: Record = { "5m": 0.15, "15m": 0.35, "1h": 0.75 }; -/** The knockout band per timeframe that sets the card headline's leverage - * (clamped by the spread constraint and custody caps). */ -const KO_BAND: Record = { "5m": 0.012, "15m": 0.025, "1h": 0.05 }; - /** The default odds ladder — the YES prices we offer per token+timeframe. */ const DEFAULT_TARGETS = [0.85, 0.7, 0.55, 0.4, 0.25] as const; +/** The probability a card's single headline question targets (then rounded). */ +const HEADLINE_TARGET = 0.58; + export interface MarketConstruction { /** perp side expressing YES (ABOVE→LONG, BELOW→SHORT). */ side: TradeType; @@ -138,6 +137,16 @@ export function fillEntryFrom(oracle: number, spread: number, direction: Directi return direction === "ABOVE" ? oracle * (1 + s) : oracle * (1 - s); } +/** The price a position REALIZES when its take-profit triggers at `strike`. The + * exit crosses the spread the OTHER way — a LONG sells at the bid (strike·(1−s)), + * a SHORT buys at the ask (strike·(1+s)) — so the spread is paid AGAIN on exit. + * This is why a take-profit must clear TWO spreads to actually win (verified live: + * a $90 SOL TP exits at $81 and nets ≈ $0). PnL is driven by entryFill→exitFill. */ +export function exitFillFrom(strike: number, spread: number, direction: Direction): number { + const s = Math.max(0, spread); + return direction === "ABOVE" ? strike * (1 - s) : strike * (1 + s); +} + /** The highest leverage whose knockout (0.92/L from the fill) still clears the * spread, intersected with the custody cap. Spread-free markets (FX) are capped * only by custody. This is THE constraint that stops an already-liquidated open. */ @@ -176,19 +185,24 @@ export function priceMarket(opts: { // Spread invariant enforced HERE: no quoted market can have a knockout inside // the spread, even if a caller passes an unclamped leverage. const lev = clampLeverage(opts.leverage, spread, Infinity); - const fillEntry = oracle > 0 ? fillEntryFrom(oracle, spread, direction) : 0; - - // t = favorable move FROM THE FILL to the strike; koDist = adverse move to liq. - const t = fillEntry > 0 ? Math.abs(opts.strike - fillEntry) / fillEntry : 0; + const entryFill = oracle > 0 ? fillEntryFrom(oracle, spread, direction) : 0; + + // The TP triggers at the strike but EXITS through the spread, so PnL is driven + // by the entry-fill → exit-fill move — the spread is paid on BOTH legs. t is + // that NET favorable move; koDist is the adverse move to liquidation. + const exitFill = exitFillFrom(opts.strike, spread, direction); + const t = entryFill > 0 + ? (direction === "ABOVE" ? exitFill / entryFill - 1 : 1 - exitFill / entryFill) + : 0; const koDist = LIQ_FACTOR / lev; - const knockoutPrice = direction === "ABOVE" ? fillEntry * (1 - koDist) : fillEntry * (1 + koDist); + const knockoutPrice = direction === "ABOVE" ? entryFill * (1 - koDist) : entryFill * (1 + koDist); // R = L·t − fees/C = L·(t − fee) (fees scale with size). t ≤ fee ⇒ R floors at 0. const payoutMult = Math.max(0, lev * (t - feesRate)); const prob = clampProb(1 / (1 + payoutMult)); return { - token, direction, strike: opts.strike, oracle, entry: fillEntry, spread, timeframe: tf, prob, payoutMult, + token, direction, strike: opts.strike, oracle, entry: entryFill, spread, timeframe: tf, prob, payoutMult, construction: { side, leverage: lev, takeProfitPrice: opts.strike, knockoutPrice }, }; } @@ -234,8 +248,14 @@ export function marketForTargetProb(opts: { // ladder spans by STRIKE). If the ceiling bites, q floats up (honest). const t = Math.min(R / leverage + feesRate, STRIKE_MOVE_CEIL[tf]); - const fillEntry = oracle > 0 ? fillEntryFrom(oracle, spread, direction) : 0; - const strike = direction === "ABOVE" ? fillEntry * (1 + t) : fillEntry * (1 - t); + const entryFill = oracle > 0 ? fillEntryFrom(oracle, spread, direction) : 0; + const sClamp = Math.min(0.99, Math.max(0, spread)); + // Place the strike so that AFTER the exit spread the NET move is t: + // LONG exit = strike·(1−s) = entryFill·(1+t) ⇒ strike = entryFill·(1+t)/(1−s) + // SHORT exit = strike·(1+s) = entryFill·(1−t) ⇒ strike = entryFill·(1−t)/(1+s) + const strike = direction === "ABOVE" + ? (entryFill * (1 + t)) / (1 - sClamp) + : (entryFill * (1 - t)) / (1 + sClamp); // Re-price from the realized strike + clamped leverage so prob is exact. return priceMarket({ token, direction, oracle, spread, leverage, strike, timeframe: tf, feesRate }); } @@ -271,23 +291,19 @@ export function strikeLadder(opts: { // ── the card headline ───────────────────────────────────────────────────────── -/** Round a price UP to a human "nice" number a believable bit above. Basing this - * on the FILL (not the oracle) guarantees the strike sits above the entry, so the - * headline bet is genuinely winnable. */ -export function niceStrikeAbove(price: number): number { +/** Round a target strike to a clean, human number CLOSE to it (≈2 significant + * figures) for a card headline — close enough that the re-priced odds stay near + * the target, clean enough to read as a real question ("Will SOL hit $100?"). + * Coarse "next nice number up" rounding overshot a 58¢ target to a 30¢ long-shot. */ +function roundStrikeNice(price: number): number { if (!(price > 0)) return 0; - const min = price * 1.02; - const mag = Math.pow(10, Math.floor(Math.log10(min))); - for (const s of [1, 1.25, 1.5, 2, 2.5, 3, 4, 5, 6, 7, 7.5, 8, 9, 10]) { - const cand = s * mag; - if (cand >= min) return Number(cand.toPrecision(4)); - } - return Number((10 * mag).toPrecision(4)); + return Number(price.toPrecision(price >= 1 ? 2 : 3)); } -/** The single "climb above" headline a card shows: a nice strike above the fill, - * priced through the live spread at the timeframe's (clamped) leverage. Each - * token's own spread + caps make the cents FLOAT — no two markets read alike. */ +/** The single "climb above" headline a card shows. Targets a believable headline + * probability through the corrected (two-spread) solver, then rounds the strike + * to a human number and RE-PRICES so the cents stay honest for the rounded + * strike. Each token's own spread + caps make the cents FLOAT — no two alike. */ export function headlineMarket(opts: { token: string; oracle: number; @@ -298,13 +314,15 @@ export function headlineMarket(opts: { direction?: Direction; }): PricedMarket { const direction = opts.direction ?? "ABOVE"; - const floor = Math.max(1.1, opts.minLeverage ?? 1.1); - const fillEntry = opts.oracle > 0 ? fillEntryFrom(opts.oracle, opts.spread, direction) : 0; - const strike = direction === "ABOVE" ? niceStrikeAbove(fillEntry) : fillEntry * (1 - KO_BAND[opts.timeframe]); - const leverage = clampLeverage(LIQ_FACTOR / KO_BAND[opts.timeframe], opts.spread, opts.maxLeverage, floor); + const target = marketForTargetProb({ + token: opts.token, direction, oracle: opts.oracle, spread: opts.spread, + maxLeverage: opts.maxLeverage, minLeverage: opts.minLeverage, + timeframe: opts.timeframe, targetProb: HEADLINE_TARGET, + }); + const strike = direction === "ABOVE" ? roundStrikeNice(target.strike) : target.strike; return priceMarket({ token: opts.token, direction, oracle: opts.oracle, spread: opts.spread, - leverage, strike, timeframe: opts.timeframe, + leverage: target.construction.leverage, strike, timeframe: opts.timeframe, }); } From cd68467a6faf742b69dbf3d5f3ed35c6068ff18b Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 10:44:52 -0400 Subject: [PATCH 16/26] predict v2.1: gates, real enable error, funds + double-submit guards (fix #6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - canBet split from funding: 'enabled' (wallet+session) vs per-stake funding. An enabled-but-underfunded user now gets an 'Add funds to bet' ticket message instead of a dead-end 'enable' CTA. availableUsd threaded markets-app → detail. - stake > balance blocked proactively (tooBig) + stake < MIN_STAKE (11) gate. - enable() surfaces the REAL human-phrased step error (EnableState.error) that was blackholed by onStep:()=>{}; both enable + place route through calmError. - double-submit guards: doReview refuses from an in-flight phase / invalid stake; doConfirm refuses while signing (belt-and-suspenders over the disabled button). typecheck green. Banner stays up pending review. Remaining: settlement (#5), buckets (#7). Co-Authored-By: Claude Opus 4.8 --- examples/predict/components/discover.tsx | 4 +++ examples/predict/components/market-detail.tsx | 20 ++++++++++----- examples/predict/components/markets-app.tsx | 25 ++++++++++++++----- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/examples/predict/components/discover.tsx b/examples/predict/components/discover.tsx index 27b03ab..6673ea6 100644 --- a/examples/predict/components/discover.tsx +++ b/examples/predict/components/discover.tsx @@ -18,12 +18,15 @@ import { MarketDetail } from "./market-detail"; export function Discover({ signer, canBet, + availableUsd, onNeedWallet, onPlaced, onOpen, }: { signer: ActiveSigner | null; canBet: boolean; + /** USDC available to bet (gates stake ≤ balance in the ticket). */ + availableUsd: number | null; onNeedWallet?: () => void; onPlaced?: () => void; onOpen?: (token: string, price: number, tf: TimeframeId) => void; @@ -134,6 +137,7 @@ export function Discover({ timeframe={selected.tf} signer={signer} canBet={canBet} + availableUsd={availableUsd} onClose={() => setSelected(null)} onNeedWallet={onNeedWallet} onPlaced={onPlaced} diff --git a/examples/predict/components/market-detail.tsx b/examples/predict/components/market-detail.tsx index 62cb51b..77d3fbe 100644 --- a/examples/predict/components/market-detail.tsx +++ b/examples/predict/components/market-detail.tsx @@ -102,6 +102,7 @@ export function MarketDetail({ timeframe: tf, signer, canBet, + availableUsd, onClose, onNeedWallet, onPlaced, @@ -111,6 +112,8 @@ export function MarketDetail({ timeframe: TimeframeId; signer: ActiveSigner | null; canBet: boolean; + /** USDC available to bet; null until known. Gates stake ≤ balance. */ + availableUsd: number | null; onClose: () => void; onNeedWallet?: () => void; onPlaced?: () => void; @@ -149,6 +152,7 @@ export function MarketDetail({ const stakeUsd = Math.max(0, Number(stake.replace(/[^0-9.]/g, "")) || 0); const tooSmall = stakeUsd > 0 && stakeUsd < MIN_STAKE; + const tooBig = availableUsd != null && stakeUsd > availableUsd + 1e-9; const yesCents = active ? cents(active.prob) : 0; type Phase = "idle" | "quoting" | "review" | "signing"; @@ -184,7 +188,9 @@ export function MarketDetail({ // if the take-profit can't pay out on this fill. The user never reaches a sign // prompt for a bet that's a loss by construction. const doReview = useCallback(async () => { - if (!active) return; + // Guard: never quote from an in-flight phase or on an invalid/over-balance stake. + if (!active || phase === "quoting" || phase === "signing") return; + if (stakeUsd <= 0 || stakeUsd < MIN_STAKE || (availableUsd != null && stakeUsd > availableUsd + 1e-9)) return; setTicketError(null); setResult(null); setPhase("quoting"); @@ -197,13 +203,13 @@ export function MarketDetail({ setTicketError(calmError(e)); setPhase("idle"); } - }, [active, reqFor, stakeUsd]); + }, [active, reqFor, stakeUsd, phase, availableUsd]); // STEP 2 — confirm: re-quote WITH owner (the price may have moved since review), // re-run the same reconciliation, and only sign + send if it STILL passes. A // re-quote that went bad bumps back to the review with the new reason. const doConfirm = useCallback(async () => { - if (!active || !signer) return; + if (!active || !signer || phase === "signing") return; // double-submit guard setPhase("signing"); setTicketError(null); try { @@ -225,7 +231,7 @@ export function MarketDetail({ setTicketError(calmError(e)); setPhase("review"); } - }, [active, signer, reqFor, stakeUsd, onPlaced]); + }, [active, signer, reqFor, stakeUsd, onPlaced, phase]); return (
@@ -353,7 +359,7 @@ export function MarketDetail({ <> {result && (

{result.msg}

diff --git a/examples/predict/components/markets-app.tsx b/examples/predict/components/markets-app.tsx index 64f42b4..a0484e5 100644 --- a/examples/predict/components/markets-app.tsx +++ b/examples/predict/components/markets-app.tsx @@ -16,8 +16,9 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { ConnectionProvider, WalletProvider, useAnchorWallet, useWallet } from "@solana/wallet-adapter-react"; import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom"; import { SolflareWalletAdapter } from "@solana/wallet-adapter-solflare"; -import { enableOneClickTrading, type EnableWalletCtx } from "@/lib/enable"; +import { enableOneClickTrading, type EnableState, type EnableWalletCtx } from "@/lib/enable"; import { flash } from "@/lib/flash"; +import { calmError } from "@/lib/copy"; import { shortKey } from "@/lib/format"; import { useBalances, useBasketBalance, useUsdcMint } from "@/lib/hooks"; import { loadSession, type LoadedSession } from "@/lib/session"; @@ -66,8 +67,12 @@ function Inner({ owner }: { owner: string | null }) { [session, anchorWallet], ); + // "canBet" = wallet connected + one-tap enabled. Whether THIS stake is funded + // (≥ MIN_STAKE and ≤ available) is gated per-bet in the ticket against + // `availableUsd`, so an enabled-but-underfunded user gets an "add funds" + // message on the ticket rather than a dead-end "enable" CTA. const basketExists = Boolean(snapshot?.basketPubkey); - const canBet = basketExists && Boolean(signer) && (inBasketUsd ?? 0) >= 1; + const canBet = basketExists && Boolean(signer); const [enabling, setEnabling] = useState(false); const [note, setNote] = useState(null); @@ -89,25 +94,33 @@ function Inner({ owner }: { owner: string | null }) { signTransaction: walletCtx.signTransaction, signAllTransactions: walletCtx.signAllTransactions, }; + // Capture the live step state so a failure can name the REAL step error + // (already human-phrased upstream) instead of a generic "check your wallet". + // A container defeats closure-assignment narrowing. + const step: { last: EnableState | null } = { last: null }; const result = await enableOneClickTrading({ wallet: ctx, anchorWallet, snapshot, usdcMint, balances: { sol: balances.sol, usdc: balances.usdc }, - onStep: () => {}, + onStep: (s) => { step.last = s; }, onLog: () => {}, }); if (result.session) setSession(result.session); if (!result.ok) { - setNote(result.needsUsdc ? "Account ready — deposit USDC on the main app, then bet." : "Couldn't finish enabling — check your wallet."); + setNote( + result.needsUsdc + ? "Account ready — deposit USDC on the main app, then bet." + : step.last?.error ?? "Couldn't finish enabling — check your wallet and try again.", + ); } else if (result.needsUsdc) { setNote("Deposit USDC on the main app to start betting."); } void balances.refresh(); void basket.refresh(); } catch (e) { - setNote((e as Error).message); + setNote(calmError(e)); } finally { setEnabling(false); } @@ -148,7 +161,7 @@ function Inner({ owner }: { owner: string | null }) {
{note &&

{note}

} {owner && } - void basket.refresh()} /> + void basket.refresh()} /> ); } From 819b82e687550b360c405a2aca260c55decdcacb Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 10:51:39 -0400 Subject: [PATCH 17/26] predict v2.1: remove degenerate buckets, honest reach semantics in engine (fix #7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Investigation finding: a single capped-loss perp + one take-profit can only express a ONE-TOUCH 'reaches X', never a bounded 'lands between X and Y' (needs a call-spread = 2 positions). The honest relabel (de-normalized 'chance it reaches this zone') was implemented but DEGENERATES: with edges inside the 5-10% spread every near-edge sits below the entry fill, so every zone saturates to the 97c ceiling — and it duplicates the strike ladder, which already gives honest reach-odds. Rather than ship a degenerate/dishonest widget on a real-funds path, the buckets section is REMOVED from the rendered detail. bucketMarket stays in the engine (corrected to honest non-normalized reach semantics) for a future call-spread or vol-aware rebuild. FLAGGED: making the removal permanent vs. a call-spread rebuild is a scope decision for the user. typecheck green. Banner stays up. Remaining: settlement (#5), then 3-agent review. Co-Authored-By: Claude Opus 4.8 --- examples/predict/SPEC-PREDICT-V2.md | 2 +- examples/predict/components/market-detail.tsx | 34 ---------------- examples/predict/lib/markets.ts | 40 ++++++++++--------- 3 files changed, 23 insertions(+), 53 deletions(-) diff --git a/examples/predict/SPEC-PREDICT-V2.md b/examples/predict/SPEC-PREDICT-V2.md index 3e5c733..5c5c3cf 100644 --- a/examples/predict/SPEC-PREDICT-V2.md +++ b/examples/predict/SPEC-PREDICT-V2.md @@ -111,7 +111,7 @@ SOL (s=0.10) ⇒ `L ≤ 0.92/0.15 ≈ 6×`. High-spread markets are naturally lo 4. **Validate the TP**: call `guards.validateTriggerPrice({side, kind:"tp", price:strike, markPrice:fillEntry})` before sending; refuse on `!ok` (avoids on-chain `6057`). 5. **Settlement + expiry** (port `lib/rounds.ts`): record a `Round{…expiresAt}` on a confirmed send; run the reconcile/settle watcher; close at expiry by mark (`inputUsdUi:"0"`) for the between-barrier case. Render a **/markets-specific** disclosure (don't inherit Updown's). **Fixes HIGH-5.** 6. **Gates + errors**: `canBet` floor → `MIN_STAKE` (11); add `stake > balance` block; double-submit guard (in-flight set keyed by owner|market|side, re-check live balance); route `place()`/`enable()` errors through `calmError`; surface the real `enable` step error. **Fixes C3, H1, H2, M1.** -7. **Buckets**: rebuild as bounded (double-barrier / call-spread) outcomes, or relabel honestly as "chance price *reaches* this zone" and drop the fake normalization. **Fixes HIGH-6.** +7. **Buckets** — RESOLVED by REMOVAL (pending a proper rebuild; needs user OK to make permanent). Investigation finding: a single capped-loss perp + one take-profit can only express a ONE-TOUCH "reaches X", never a bounded "lands between X and Y" (that needs a call-spread = two positions). The honest relabel ("chance it reaches this zone", de-normalized) was implemented but DEGENERATES: with edges inside the 5–10% spread every near-edge sits below the entry fill, so every zone saturates to the 97¢ ceiling — and it duplicates the strike ladder, which already gives honest reach-odds. So the degenerate widget was removed from the rendered detail; `bucketMarket` stays in the engine (corrected, reach semantics) for a future **call-spread** or **vol-aware** rebuild. **HIGH-6 closed by removal; rebuild is a flagged scope item.** 8. **Slippage**: set `slippagePercentage` ≥ the spread (or omit and let the spread price it) — `"1"` is below a 5–10% spread. **Fixes MEDIUM-7.** 9. Guard degenerate inputs (`oracle ≤ 0`, `t ≤ s+fee`). **Fixes LOW-8.** diff --git a/examples/predict/components/market-detail.tsx b/examples/predict/components/market-detail.tsx index 77d3fbe..0d1bf87 100644 --- a/examples/predict/components/market-detail.tsx +++ b/examples/predict/components/market-detail.tsx @@ -17,7 +17,6 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { assertNoErr, validateTriggerPrice, type OpenPositionResponse } from "flash-v2"; import { - bucketMarket, cents, profitUsd, slippageForSpread, @@ -38,11 +37,6 @@ const MIN_STAKE = 11; // the $11-after-fees floor (RECOMMENDED_MIN_COLLATERAL_US const fmtUsd = (n: number) => `$${n.toFixed(2)}`; const fmtStrike = (n: number) => (n >= 1000 ? n.toLocaleString(undefined, { maximumFractionDigits: 0 }) : n.toFixed(n >= 1 ? 2 : 4)); -/** Five bucket edges around the live price (±0.5% / ±1.5%) → six outcomes. */ -function defaultEdges(price: number): number[] { - return [0.985, 0.995, 1.005, 1.015].map((m) => price * m); -} - /** The bet reconciled to the SIGNED response — the authoritative numbers the user * actually signs, never the pre-trade estimate. `ok:false` blocks the commit. */ interface ReconciledBet { @@ -134,18 +128,6 @@ export function MarketDetail({ : [], [token, price, spread, dir, tf, limits], ); - const buckets = useMemo( - () => - limits - ? // buckets span both sides; per-side spread is redesigned in fix #7. - bucketMarket({ - token, oracle: price, spread: limits.spreadLongPct, maxLeverage: limits.maxLeverage, - minLeverage: limits.minLeverage, timeframe: tf, edges: defaultEdges(price), - }) - : null, - [token, price, tf, limits], - ); - const [picked, setPicked] = useState(null); const active = picked && picked.direction === dir ? picked : ladder[Math.floor(ladder.length / 2)] ?? null; const [stake, setStake] = useState("25"); @@ -284,22 +266,6 @@ export function MarketDetail({ })} - {/* multi-outcome buckets — where does it land? */} - {buckets && ( - + + ); +} diff --git a/examples/predict/lib/clock.tsx b/examples/predict/lib/clock.tsx deleted file mode 100644 index 8d9c549..0000000 --- a/examples/predict/lib/clock.tsx +++ /dev/null @@ -1,26 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// lib/clock.tsx — one shared 1s clock for every countdown and the settlement -// watcher. THE HARD PART: N components with their own setInterval = N -// re-render storms and a reconcile effect with an unstable dependency. One -// provider ticks; everyone reads the same timestamp. -// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import { createContext, useContext, useEffect, useState, type ReactNode } from "react"; - -const ClockContext = createContext(0); - -export function ClockProvider({ children }: { children: ReactNode }) { - const [now, setNow] = useState(() => Date.now()); - useEffect(() => { - const id = setInterval(() => setNow(Date.now()), 1000); - return () => clearInterval(id); - }, []); - return {children}; -} - -/** Shared wall-clock, ticking once per second. */ -export function useClock(): number { - return useContext(ClockContext); -} diff --git a/examples/predict/lib/use-price-history.ts b/examples/predict/lib/use-price-history.ts deleted file mode 100644 index fe51689..0000000 --- a/examples/predict/lib/use-price-history.ts +++ /dev/null @@ -1,79 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// lib/use-price-history.ts — client-side ring buffer of price ticks feeding -// the canvas chart. THE HARD PART: pushing on every POLL (not every CHANGE) — -// usePrice emits a fresh PriceInfo object per tick, so depending on object -// identity makes a flat price still drift the line rightward. -// GOTCHAS.md → (client-side only; no API gotchas) (../../GOTCHAS.md) -// ───────────────────────────────────────────────────────────────────────────── - -"use client"; - -import type { PriceInfo } from "flash-v2"; -import { useEffect, useState } from "react"; -import { flash } from "./flash"; - -/** - * Seed the chart with REAL history on market switch — Pyth's public - * Benchmarks API (TradingView shim) serves minute closes for every feed the - * app trades, keyed by the SAME `pythTicker` Flash's token config carries. - * One fetch per switch; the live 1s poll keeps appending after the seed, so - * the line has shape immediately instead of a flat creep. - */ -async function fetchSeed(market: string, points: number): Promise { - try { - const tokens = await flash.tokens(); - const ticker = tokens.find((t) => t.symbol.toUpperCase() === market.toUpperCase())?.pythTicker; - if (!ticker) return null; - const to = Math.floor(Date.now() / 1000); - const from = to - points * 60; - const url = - `https://benchmarks.pyth.network/v1/shims/tradingview/history` + - `?symbol=${encodeURIComponent(ticker)}&resolution=1&from=${from}&to=${to}`; - const res = await fetch(url); - if (!res.ok) return null; - const j = (await res.json()) as { s?: string; c?: number[] }; - if (j.s !== "ok" || !Array.isArray(j.c) || j.c.length < 2) return null; - return j.c.filter((v) => Number.isFinite(v)); - } catch { - return null; - } -} - -/** How many ticks the chart keeps (~4 min at the 1s poll). */ -export const PRICE_HISTORY_CAP = 240; - -/** - * Append `price.priceUi` once per poll tick (object identity = one tick), - * keep the last `cap` points. Returns the same array reference between ticks - * so consumers can use it as a stable dependency. - * `resetKey` (the market symbol) WIPES the buffer on change — mixing two - * markets' prices in one buffer draws a deformed cross-scale line. - */ -export function usePriceHistory(price: PriceInfo | null, resetKey = "", cap = PRICE_HISTORY_CAP): number[] { - const [points, setPoints] = useState([]); - - useEffect(() => { - setPoints([]); // new market, fresh chart… - if (!resetKey) return; - let dead = false; - // …then seed it with real Pyth minute-closes (last ~60 min) so the line - // is born with shape. Live ticks append after; if the seed loses the race - // to the first live points, keep the live ones (never clobber freshness). - void fetchSeed(resetKey, 60).then((seed) => { - if (dead || !seed) return; - setPoints((prev) => (prev.length > 5 ? prev : [...seed.slice(-cap + 1), ...prev])); - }); - return () => { dead = true; }; - }, [resetKey, cap]); - - useEffect(() => { - if (!price || !Number.isFinite(price.priceUi)) return; - setPoints((prev) => { - const next = prev.length >= cap ? prev.slice(prev.length - cap + 1) : prev.slice(); - next.push(price.priceUi); - return next; - }); - }, [price, cap]); - - return points; -} From bac10579769da9b01e2b329170da1e1c3f82a52c Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 16:26:46 -0400 Subject: [PATCH 23/26] predict: wallet connect matches the other apps (tap-trade pattern) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The custom WalletPicker diverged from the house pattern and was unreliable: it called select() and relied on autoConnect to race in (pick a wallet → often nothing happens). Replaced with the SAME flow tap-trade uses: - components/wallet-sheet.tsx: the proper wallet-adapter 'ref dance' — select() is async, so a wantConnect ref + an effect fire connect() once the adapter is actually selected. Handles not-installed wallets (open install URL), a connecting… state, a hydration guard (React #418), and closes on connect. Lists EVERY installed wallet via Wallet Standard. Predict glass styling. - components/wallet-menu.tsx: the connected dropdown matching tap-trade's top-bar menu — balance (wallet usdc / in account) · deposit/withdraw · copy address · disconnect. - markets-app: 'Connect Wallet' → wallet sheet; connected → WalletMenu; deleted wallet-picker.tsx and the ad-hoc disconnect/address button. typecheck + production build green; dev server fresh (/ → 200, 'Connect Wallet' present). Co-Authored-By: Claude Opus 4.8 --- examples/predict/components/markets-app.tsx | 36 ++---- examples/predict/components/wallet-menu.tsx | 101 ++++++++++++++++ examples/predict/components/wallet-picker.tsx | 76 ------------ examples/predict/components/wallet-sheet.tsx | 111 ++++++++++++++++++ 4 files changed, 225 insertions(+), 99 deletions(-) create mode 100644 examples/predict/components/wallet-menu.tsx delete mode 100644 examples/predict/components/wallet-picker.tsx create mode 100644 examples/predict/components/wallet-sheet.tsx diff --git a/examples/predict/components/markets-app.tsx b/examples/predict/components/markets-app.tsx index 72e9eda..05ee542 100644 --- a/examples/predict/components/markets-app.tsx +++ b/examples/predict/components/markets-app.tsx @@ -19,7 +19,6 @@ import { enableOneClickTrading, type EnableState, type EnableWalletCtx } from "@ import { depositUsdc, executeWithdrawalStep, withdrawUsdc, type FundsStep } from "@/lib/funds"; import { flash } from "@/lib/flash"; import { calmError } from "@/lib/copy"; -import { shortKey } from "@/lib/format"; import { useBalances, useBasketBalance, useUsdcMint } from "@/lib/hooks"; import { loadSession, type LoadedSession } from "@/lib/session"; import { makeSessionSigner } from "@/lib/signer"; @@ -28,7 +27,8 @@ import { useMarketRounds } from "@/lib/use-market-rounds"; import { Bets } from "./bets"; import { Discover } from "./discover"; import { FundsSheet } from "./funds-sheet"; -import { WalletPicker } from "./wallet-picker"; +import { WalletMenu } from "./wallet-menu"; +import { WalletSheet } from "./wallet-sheet"; export function MarketsApp() { const wallets = useMemo(() => [new PhantomWalletAdapter(), new SolflareWalletAdapter()], []); @@ -84,10 +84,9 @@ function Inner({ owner }: { owner: string | null }) { const [note, setNote] = useState(null); const [pickerOpen, setPickerOpen] = useState(false); - // Open the wallet picker — the user chooses ANY installed wallet (no wallet is - // hardcoded; selection happens in WalletPicker via Wallet Standard). + // Open the wallet sheet — the user chooses ANY installed wallet (no wallet is + // hardcoded; selection + the connect ref-dance happen in WalletSheet). const connect = useCallback(() => setPickerOpen(true), []); - const disconnect = useCallback(() => { void walletCtx.disconnect(); }, [walletCtx]); // ── funds: deposit/withdraw USDC ↔ basket (ported so /markets is standalone). // Withdraw is two-phase by design (request → execute, ~30–90s). @@ -202,15 +201,7 @@ function Inner({ owner }: { owner: string | null }) { {owner ? (
- {canBet ? ( - - ) : ( + {!canBet && ( )} - + setFundsOpen(true)} + />
) : ( )} @@ -245,7 +235,7 @@ function Inner({ owner }: { owner: string | null }) { void basket.refresh(); }} /> - {pickerOpen && setPickerOpen(false)} />} + setPickerOpen(false)} /> + + + ); +} + +export function WalletMenu({ + owner, + walletUsdc, + inBasketUsd, + onOpenFunds, +}: { + owner: string; + walletUsdc: number | null; + inBasketUsd: number | null; + onOpenFunds: () => void; +}) { + const { disconnect } = useWallet(); + const [open, setOpen] = useState(false); + const [copied, setCopied] = useState(false); + + const copy = async () => { + try { + await navigator.clipboard.writeText(owner); + setCopied(true); + setTimeout(() => setCopied(false), 1200); + } catch { + /* clipboard denied — the menu stays */ + } + }; + const usd = (n: number | null) => (n === null ? "—" : `$${n.toFixed(2)}`); + + return ( +
+ + + {open && ( + <> + + + +
+ + )} + + ); +} diff --git a/examples/predict/components/wallet-picker.tsx b/examples/predict/components/wallet-picker.tsx deleted file mode 100644 index 87cc73e..0000000 --- a/examples/predict/components/wallet-picker.tsx +++ /dev/null @@ -1,76 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/wallet-picker.tsx — connect ANY wallet, not a hardcoded one. Lists -// every wallet the browser exposes (Wallet Standard auto-detects Phantom, -// Solflare, Backpack, Glow, … — nothing here is pinned to a specific wallet) and -// lets the user pick. Selecting hands off to the adapter, which connects. -// ───────────────────────────────────────────────────────────────────────────── - -"use client"; - -import { useMemo } from "react"; -import { useWallet, type Wallet } from "@solana/wallet-adapter-react"; -import { WalletReadyState } from "@solana/wallet-adapter-base"; - -const rank = (w: Wallet): number => - w.readyState === WalletReadyState.Installed ? 2 : w.readyState === WalletReadyState.Loadable ? 1 : 0; - -export function WalletPicker({ onClose }: { onClose: () => void }) { - const { wallets, select } = useWallet(); - - // Installed/loadable first; de-dup by name (explicit adapters + Standard - // detection can surface the same wallet twice). - const shown = useMemo(() => { - const seen = new Set(); - const ranked = [...wallets].sort((a, b) => rank(b) - rank(a)); - const deduped = ranked.filter((w) => { - const n = w.adapter.name; - if (seen.has(n)) return false; - seen.add(n); - return true; - }); - const ready = deduped.filter( - (w) => w.readyState === WalletReadyState.Installed || w.readyState === WalletReadyState.Loadable, - ); - return ready.length > 0 ? ready : deduped; - }, [wallets]); - - return ( -
- -
- - {shown.length === 0 ? ( -

- No Solana wallet detected. Install Phantom, Solflare, Backpack, or another wallet, then try again. -

- ) : ( - shown.map((w) => ( - - )) - )} - - - ); -} diff --git a/examples/predict/components/wallet-sheet.tsx b/examples/predict/components/wallet-sheet.tsx new file mode 100644 index 0000000..0f98600 --- /dev/null +++ b/examples/predict/components/wallet-sheet.tsx @@ -0,0 +1,111 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/wallet-sheet.tsx — connect ANY wallet, the SAME flow the other apps +// use (tap-trade's WalletSheet). THE HARD PART: wallet-adapter's select() is +// async state — selecting an adapter then connecting must wait for the selection +// to land (the "ref dance"): a `wantConnect` ref + an effect that fires connect() +// once `wallet` is the chosen adapter. Not-installed wallets open their install +// page; a hydration guard keeps readyState SSR-stable (React #418). Predict glass. +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { WalletReadyState } from "@solana/wallet-adapter-base"; +import { useWallet } from "@solana/wallet-adapter-react"; +import { useEffect, useRef, useState } from "react"; +import { useSheetMount } from "@/lib/use-sheet-mount"; + +export function WalletSheet({ open, onClose }: { open: boolean; onClose: () => void }) { + const { wallets, wallet, select, connect, connecting, connected } = useWallet(); + const { mounted, closing } = useSheetMount(open); + const [error, setError] = useState(null); + const wantConnect = useRef(false); + + // HYDRATION GUARD: wallets/readyState are empty/"not detected" during SSR but + // populate client-side. Render the list only after mount so the server/client + // markup matches (React #418 otherwise). + const [hydrated, setHydrated] = useState(false); + useEffect(() => setHydrated(true), []); + + // select() is async state — finish the connect once the adapter is selected. + useEffect(() => { + if (wantConnect.current && wallet && !connected && !connecting) { + wantConnect.current = false; + connect().catch((e: unknown) => setError(e instanceof Error ? e.message : String(e))); + } + }, [wallet, connected, connecting, connect]); + + // Close on a successful connect. + useEffect(() => { + if (connected && open) onClose(); + }, [connected, open, onClose]); + + if (!mounted) return null; + return ( +
+
+
+
+
+
+

Connect a wallet

+ +
+

+ Mainnet — real funds. Your wallet owns the account; after Enable, taps auto-sign via a session key. +

+ +
+ {hydrated && + wallets.map((w) => { + const installed = + w.readyState === WalletReadyState.Installed || w.readyState === WalletReadyState.Loadable; + const isThis = wallet?.adapter.name === w.adapter.name; + return ( + + ); + })} + {hydrated && wallets.length === 0 && ( +

+ No Solana wallet detected. Install Phantom, Solflare, or Backpack, then try again. +

+ )} +
+ + {error &&

{error}

} +
+
+
+
+ ); +} From 3e042e11c31a07e7ff3cdde2a874ec627eb504ba Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 16:34:21 -0400 Subject: [PATCH 24/26] predict: add session authority guard (exact tap-trade parity, security review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The wallet-flow security review found one divergence from the proven tap-trade pattern: predict's signer useMemo omitted tap-trade's authority guard. During an A→B wallet switch, session state lags anchorWallet by one render, so makeSessionSigner could briefly pair wallet B with wallet A's session token. The review verified it was never consumed (auto-settle bails on the reset null snapshot; all else is user-gated) — LOW/defense-in-depth — but it's the only thing that kept predict from an exact match. Now guarded exactly like tap-trade app.tsx:115. Review verdict: no CRITICAL/HIGH; correct, secure, faithful. typecheck green. Co-Authored-By: Claude Opus 4.8 --- examples/predict/components/markets-app.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/predict/components/markets-app.tsx b/examples/predict/components/markets-app.tsx index 05ee542..ca1d6f4 100644 --- a/examples/predict/components/markets-app.tsx +++ b/examples/predict/components/markets-app.tsx @@ -65,7 +65,13 @@ function Inner({ owner }: { owner: string | null }) { setSession(owner ? loadSession(owner) : null); }, [owner]); const signer = useMemo( - () => (session && anchorWallet ? makeSessionSigner(anchorWallet, session, flash.network) : null), + // Authority guard (matches tap-trade app.tsx): during an A→B wallet switch the + // `session` state lags `anchorWallet` by one render — never build a signer that + // pairs wallet B with wallet A's session token. + () => + session && anchorWallet && session.authority === anchorWallet.publicKey.toBase58() + ? makeSessionSigner(anchorWallet, session, flash.network) + : null, [session, anchorWallet], ); From 834dd5b0a4385317eda65f13d0aaf54178986162 Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 16:57:35 -0400 Subject: [PATCH 25/26] =?UTF-8?q?predict:=20fix=20'Enable=20one-tap'=20get?= =?UTF-8?q?ting=20stuck=20=E2=80=94=20refresh=20owner=20snapshot=20after?= =?UTF-8?q?=20enable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause (found by diffing against the working tap-trade app): enable.ts, session.ts (bar the storage-key name), and signer.ts are all IDENTICAL to tap-trade. The only divergence was the post-enable wiring. tap-trade's doEnable does 'await refresh()' (the OWNER-STREAM refresh) after enabling so the new basket lands; predict's enable() refreshed wallet balance + basket BALANCE but NEVER the owner snapshot. So after a successful enable, snapshot.basketPubkey stayed null → basketExists=false → canBet=false → the UI never left 'Enable one-tap' (the exact stuck-after-enable bug the other apps already fixed). Fix: markets-app now destructures refresh from useStream and 'await refreshSnapshot()' after enable (+ after a placed bet, matching tap-trade app.tsx:373/580). The StreamProvider already exposed refresh(); it just wasn't called. typecheck green; dev server hot-reloaded. Co-Authored-By: Claude Opus 4.8 --- examples/predict/components/markets-app.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/predict/components/markets-app.tsx b/examples/predict/components/markets-app.tsx index ca1d6f4..9331706 100644 --- a/examples/predict/components/markets-app.tsx +++ b/examples/predict/components/markets-app.tsx @@ -54,7 +54,7 @@ function Shell() { function Inner({ owner }: { owner: string | null }) { const walletCtx = useWallet(); const anchorWallet = useAnchorWallet(); - const { snapshot } = useStream(); + const { snapshot, refresh: refreshSnapshot } = useStream(); const usdcMint = useUsdcMint(); const balances = useBalances(owner, usdcMint); const basket = useBasketBalance(owner, snapshot?.basketPubkey ?? null, usdcMint); @@ -184,6 +184,10 @@ function Inner({ owner }: { owner: string | null }) { } else if (result.needsUsdc) { setNote("Tap your balance above to deposit USDC and start betting."); } + // Pull the fresh owner snapshot so the NEW basket lands immediately — this + // is what flips basketExists → true so the user gets PAST "Enable one-tap" + // (the missing step that left it stuck). Matches tap-trade's doEnable. + await refreshSnapshot(); void balances.refresh(); void basket.refresh(); } catch (e) { @@ -191,7 +195,7 @@ function Inner({ owner }: { owner: string | null }) { } finally { setEnabling(false); } - }, [walletCtx, anchorWallet, snapshot, usdcMint, balances, basket, connect]); + }, [walletCtx, anchorWallet, snapshot, usdcMint, balances, basket, connect, refreshSnapshot]); const onNeedWallet = useCallback(() => { if (!owner) connect(); @@ -238,6 +242,7 @@ function Inner({ owner }: { owner: string | null }) { onNeedWallet={onNeedWallet} onPlaced={(round) => { addRound(round); + void refreshSnapshot(); // surface the new position immediately (Bets + reconcile) void basket.refresh(); }} /> From c526f7d49e4ac04717e97d9f00bc1c1d64de1d2b Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 17:12:31 -0400 Subject: [PATCH 26/26] =?UTF-8?q?predict:=20stake=20input=20=E2=80=94=20dr?= =?UTF-8?q?op=20the=20$25=20hardcode,=20add=20max=20button=20+=20min=20hin?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - default stake is now the actual floor (MIN_STAKE $11) instead of an arbitrary $25 - 'max' button → stakes the full available balance (floored to cents) - input sanitizes to [0-9.]; placeholder shows the min - helper line: 'min $11 (a bet bundles a take-profit) · you have $X' so the floor and balance are explicit (a sub-$11 stake can't carry the bundled TP on-chain) Note: this does NOT change the $11 minimum — that's the >$10-after-fees rule for the bundled take-profit. A $9.74 account is below it; deposit to ~$12 to bet. typecheck green. Co-Authored-By: Claude Opus 4.8 --- examples/predict/components/market-detail.tsx | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/examples/predict/components/market-detail.tsx b/examples/predict/components/market-detail.tsx index f498bdd..151cb77 100644 --- a/examples/predict/components/market-detail.tsx +++ b/examples/predict/components/market-detail.tsx @@ -136,7 +136,7 @@ export function MarketDetail({ ); const [picked, setPicked] = useState(null); const active = picked && picked.direction === dir ? picked : ladder[Math.floor(ladder.length / 2)] ?? null; - const [stake, setStake] = useState("25"); + const [stake, setStake] = useState(String(MIN_STAKE)); const stakeUsd = Math.max(0, Number(stake.replace(/[^0-9.]/g, "")) || 0); const tooSmall = stakeUsd > 0 && stakeUsd < MIN_STAKE; @@ -306,11 +306,34 @@ export function MarketDetail({ +

+ min ${MIN_STAKE} (a bet bundles a take-profit) + {availableUsd != null ? ` · you have $${availableUsd.toFixed(2)}` : ""} +

{/* the honest numbers — locked from the same math the trade uses */}
-

where does it land?

- {buckets.buckets.map((b) => ( -
- {b.label} -
-
-
- {Math.round(b.prob * 100)}% -
- ))} -
- )} - {/* the buy ticket */} {active && (
diff --git a/examples/predict/lib/markets.ts b/examples/predict/lib/markets.ts index 578aad9..723d6d7 100644 --- a/examples/predict/lib/markets.ts +++ b/examples/predict/lib/markets.ts @@ -326,20 +326,24 @@ export function headlineMarket(opts: { }); } -// ── multi-outcome buckets ("where does it land?") ───────────────────────────── -// NOTE: spread-aware port. The OUTCOME SEMANTICS (true bounded buckets vs. a -// one-touch proxy) are redesigned in SPEC §v2.1 fix #7; this keeps the existing -// shape but prices every bucket through the fill + clamped leverage so nothing -// is quoted spread-blind in the meantime. +// ── reach zones ("how far could it move?") ──────────────────────────────────── +// HONEST MODEL (SPEC §v2.1 fix #7): a single capped-loss perp + one take-profit +// can only express a ONE-TOUCH "does it reach price X", never a true bounded +// "lands between X and Y" (that needs a call-spread = two positions). So each +// zone's number is the INDEPENDENT chance price REACHES into it (crosses its near +// edge). Reaching a far zone implies the nearer ones — the events OVERLAP, they +// are NOT a partition — so they are NOT normalized and do NOT sum to 100%. +// Pretending they did was the dishonest part this fix removes. export interface OutcomeBucket { label: string; /** inclusive lower / exclusive upper price edge; null = open-ended. */ lo: number | null; hi: number | null; - /** normalized probability of landing in this bucket (sums to ~1 across buckets). */ + /** INDEPENDENT one-touch chance price reaches into this zone (NOT normalized; + * zones overlap, so these do not sum to 1). */ prob: number; - /** the perp that expresses "lands in this bucket". */ + /** the perp that expresses "reaches into this zone" (one-touch to the near edge). */ market: PricedMarket; } @@ -350,9 +354,9 @@ export interface BucketMarket { buckets: OutcomeBucket[]; } -/** Build a multi-outcome market by partitioning the price axis at `edges` - * (ascending prices). Each bucket's raw probability comes from a spread-aware - * perp construction; raw probabilities are normalized to sum to 1. */ +/** Build the reach-zone row by splitting the price axis at `edges` (ascending). + * Each zone's probability is the spread-aware ONE-TOUCH chance price reaches into + * it (crosses the edge nearest the oracle). NOT normalized — see the note above. */ export function bucketMarket(opts: { token: string; oracle: number; @@ -372,22 +376,22 @@ export function bucketMarket(opts: { if (edges.length > 0) bounds.push({ lo: edges[edges.length - 1]!, hi: null }); const raw = bounds.map((b) => { - // Point the perp at the bucket's centre, TP at the near edge — a one-touch - // proxy for the bucket (redesigned to a true bounded outcome in fix #7). - const centre = b.lo == null ? b.hi! * 0.99 : b.hi == null ? b.lo * 1.01 : (b.lo + b.hi) / 2; - const direction: Direction = centre >= oracle ? "ABOVE" : "BELOW"; - const strike = b.lo == null ? b.hi! : b.hi == null ? b.lo : (direction === "ABOVE" ? b.lo : b.hi); + // Which side of the oracle the zone sits on, and the NEAR edge — the threshold + // price crosses to reach INTO the zone. One-touch to that edge = the reach. + const zoneRef = b.lo == null ? b.hi! : b.hi == null ? b.lo : (b.lo + b.hi) / 2; + const direction: Direction = zoneRef >= oracle ? "ABOVE" : "BELOW"; + const nearEdge = direction === "ABOVE" ? (b.lo ?? b.hi!) : (b.hi ?? b.lo!); const leverage = clampLeverage(LIQ_FACTOR / (FAR_MOVE_PCT[tf] / 100), spread, opts.maxLeverage, floor); - const m = priceMarket({ token, direction, oracle, spread, leverage, strike, timeframe: tf }); + const m = priceMarket({ token, direction, oracle, spread, leverage, strike: nearEdge, timeframe: tf }); return { b, m }; }); - const sum = raw.reduce((s, r) => s + r.m.prob, 0) || 1; + // NO normalization — each is an independent reach chance (the zones overlap). const buckets: OutcomeBucket[] = raw.map(({ b, m }) => ({ label: bucketLabel(b.lo, b.hi), lo: b.lo, hi: b.hi, - prob: m.prob / sum, // normalized so the outcome distribution sums to 1 + prob: m.prob, market: m, })); return { token, oracle, timeframe: tf, buckets }; From 6838be1521f97d868074f066bc5504ad98dfb3c8 Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 11:18:59 -0400 Subject: [PATCH 18/26] predict v2.1: settlement engine + expiry + bets view + disclosure (fix #5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes the 'by ' honest. A bet is a capped-loss perp + bundled TP, so it resolves on-chain itself (TP=win, liquidation=lose); a bet that does NEITHER by its deadline would ride forever and bleed borrow fees, so the engine CLOSES it at mark at expiry — mirroring the proven Updown settleRound (explicit '0' full close; reconcile on observed snapshot+clock, not a bare timer; inflight set + 'settling' status guard double-close). - lib/use-market-rounds.ts: the engine. SEPARATE localStorage namespace ('predict-markets') from the Updown app so reconciliation can't cross- contaminate. toSettle (expired+open) -> close at mark, result = watched PnL. closedElsewhere (vanished) -> infer win/lose from live mark vs strike (win uses the reconciled win-profit; loss is the capped -stake). - lib/rounds.ts: optional strike + winProfitUsd on Round; loadRounds/saveRounds take an optional key prefix (Updown path unchanged by default). - components/bets.tsx (replaces portfolio.tsx): active bets show live value + countdown + manual settle; settled bets show WON/LOST. - detail builds the Round on confirm (strike, expiresAt, reconciled win-profit); ticket footer now discloses the settlement rule. typecheck + production build both green (exit 0). FLAG for #10 review: (a) the closedElsewhere win/lose is a mark-vs-strike heuristic (exact realized amount is on-chain); (b) cross-app edge — same wallet trading the same market+side on both / and /markets shares one on-chain position. ②-⑧ COMPLETE. Next: the 3-agent adversarial review (#10), then the banner. Co-Authored-By: Claude Opus 4.8 --- examples/predict/components/bets.tsx | 144 +++++++++++++++++ examples/predict/components/discover.tsx | 3 +- examples/predict/components/market-detail.tsx | 26 ++- examples/predict/components/markets-app.tsx | 21 ++- examples/predict/components/portfolio.tsx | 89 ----------- examples/predict/lib/rounds.ts | 19 ++- examples/predict/lib/use-market-rounds.ts | 148 ++++++++++++++++++ 7 files changed, 346 insertions(+), 104 deletions(-) create mode 100644 examples/predict/components/bets.tsx delete mode 100644 examples/predict/components/portfolio.tsx create mode 100644 examples/predict/lib/use-market-rounds.ts diff --git a/examples/predict/components/bets.tsx b/examples/predict/components/bets.tsx new file mode 100644 index 0000000..e8a86c1 --- /dev/null +++ b/examples/predict/components/bets.tsx @@ -0,0 +1,144 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/bets.tsx — "your bets". Each round is a real Flash V2 position plus +// the question + the clock. ACTIVE bets show live value (Flash-parity mark math, +// never the indexer pnl) + a countdown to the deadline + a manual settle; SETTLED +// bets show WON/LOST. The position is the truth — the round adds the framing. The +// settlement engine (useMarketRounds) auto-closes at the deadline; "settle" is the +// manual override. Marks come from one polled /v2/prices. +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { useEffect, useState } from "react"; +import type { BasketSnapshot, PriceInfo } from "flash-v2"; +import { computePositionView } from "@/lib/format"; +import { flash } from "@/lib/flash"; +import { positionFor, type Round } from "@/lib/rounds"; +import { TokenIcon } from "./token-icon"; + +const fmtUsd = (n: number) => `${n < 0 ? "−" : ""}$${Math.abs(n).toFixed(2)}`; +const fmtStrike = (n: number) => (n >= 1000 ? n.toLocaleString(undefined, { maximumFractionDigits: 0 }) : n.toFixed(n >= 1 ? 2 : 4)); + +function countdown(ms: number): string { + if (ms <= 0) return "settling…"; + const s = Math.floor(ms / 1000); + const m = Math.floor(s / 60); + const h = Math.floor(m / 60); + if (h > 0) return `${h}h ${m % 60}m`; + if (m > 0) return `${m}m ${s % 60}s`; + return `${s}s`; +} + +export function Bets({ + rounds, + snapshot, + now, + onSettleNow, +}: { + rounds: Round[]; + snapshot: BasketSnapshot | null; + now: number; + onSettleNow: (r: Round) => void; +}) { + const [prices, setPrices] = useState>({}); + useEffect(() => { + let alive = true; + let timer: ReturnType | undefined; + const tick = async () => { + try { + const p = await flash.prices(); + if (alive) setPrices(p); + } catch { + /* keep last marks */ + } + if (alive) timer = setTimeout(() => void tick(), 3000); + }; + void tick(); + return () => { + alive = false; + if (timer) clearTimeout(timer); + }; + }, []); + + const active = rounds.filter((r) => r.status === "active" || r.status === "settling"); + const settled = rounds.filter((r) => r.result).slice(0, 8); + if (active.length === 0 && settled.length === 0) return null; + + return ( +
+
+ your bets + + {active.length} live +
+ +
+ {active.map((r) => { + const long = r.side === "LONG"; + const pos = snapshot ? positionFor(snapshot, r.market, r.side) : undefined; + const mark = prices[r.market]?.priceUi ?? null; + const view = pos ? computePositionView(pos, mark) : null; + const pnl = view?.pnlUsd ?? null; + const remaining = r.expiresAt != null ? r.expiresAt - now : 0; + return ( +
+
+ +
+

+ {r.market}{" "} + + {long ? "▲ above" : "below ▼"} {r.strike != null ? fmtStrike(r.strike) : ""} + +

+

+ {r.status === "settling" ? "settling…" : `⏱ ${countdown(remaining)} · stake ${fmtUsd(r.stakeUsd)}`} +

+
+
+
+ {pnl !== null && ( + = 0 ? "text-up" : "text-down"}`}> + {pnl >= 0 ? "+" : ""} + {fmtUsd(pnl)} + + )} + +
+
+ ); + })} + + {settled.map((r) => { + const won = r.result?.won ?? false; + return ( +
+
+ +

+ {r.market} {r.side === "LONG" ? "above" : "below"}{" "} + {r.strike != null ? fmtStrike(r.strike) : ""} +

+
+
+ + {won ? "WON" : "LOST"} + + + {(r.result?.pnlUsd ?? 0) >= 0 ? "+" : ""} + {fmtUsd(r.result?.pnlUsd ?? 0)} + +
+
+ ); + })} +
+
+ ); +} diff --git a/examples/predict/components/discover.tsx b/examples/predict/components/discover.tsx index 6673ea6..0f0904c 100644 --- a/examples/predict/components/discover.tsx +++ b/examples/predict/components/discover.tsx @@ -11,6 +11,7 @@ import { useMemo, useState } from "react"; import { TIMEFRAMES, type TimeframeId } from "@/lib/payoff"; import { usePredictMarkets } from "@/lib/use-markets"; import { useAllMarketLimits } from "@/lib/hooks"; +import type { Round } from "@/lib/rounds"; import type { ActiveSigner } from "@/lib/signer"; import { MarketCard } from "./market-card"; import { MarketDetail } from "./market-detail"; @@ -28,7 +29,7 @@ export function Discover({ /** USDC available to bet (gates stake ≤ balance in the ticket). */ availableUsd: number | null; onNeedWallet?: () => void; - onPlaced?: () => void; + onPlaced?: (round: Round) => void; onOpen?: (token: string, price: number, tf: TimeframeId) => void; }) { const { rows, categories, loading, error } = usePredictMarkets(); diff --git a/examples/predict/components/market-detail.tsx b/examples/predict/components/market-detail.tsx index 0d1bf87..7df63ef 100644 --- a/examples/predict/components/market-detail.tsx +++ b/examples/predict/components/market-detail.tsx @@ -26,7 +26,8 @@ import { type PricedMarket, } from "@/lib/markets"; import { useMarketLimits } from "@/lib/hooks"; -import { timeframe, type TimeframeId } from "@/lib/payoff"; +import { lockedQuoteFrom, timeframe, type TimeframeId } from "@/lib/payoff"; +import { newRoundId, type Round } from "@/lib/rounds"; import { calmError, fmtPrice } from "@/lib/copy"; import { flash } from "@/lib/flash"; import type { ActiveSigner } from "@/lib/signer"; @@ -110,7 +111,7 @@ export function MarketDetail({ availableUsd: number | null; onClose: () => void; onNeedWallet?: () => void; - onPlaced?: () => void; + onPlaced?: (round: Round) => void; }) { const limits = useMarketLimits(token); const [dir, setDir] = useState("ABOVE"); @@ -205,10 +206,26 @@ export function MarketDetail({ } if (!res.transactionBase64) throw new Error(res.err ?? "no transaction returned"); const sent = await signer.sendTrade(res.transactionBase64); + // Record the bet so the settlement engine can run its clock + resolve it. + const lev = active.construction.leverage; + const round: Round = { + id: newRoundId(), + market: active.token, + side: active.construction.side, + stakeUsd, + leverage: lev, + timeframe: tf, + placedAt: Date.now(), + expiresAt: Date.now() + timeframe(tf).ms, + quote: lockedQuoteFrom(res, stakeUsd, lev, active.construction.side), + status: "active", + strike: active.construction.takeProfitPrice, + winProfitUsd: recon.profitUsd, + }; setResult({ ok: true, msg: `Bet placed · ${sent.signature.slice(0, 8)}…` }); setReview(null); setPhase("idle"); - onPlaced?.(); + onPlaced?.(round); } catch (e) { setTicketError(calmError(e)); setPhase("review"); @@ -345,7 +362,8 @@ export function MarketDetail({ )}

- Odds are formula-set on a real capped-loss position. You can never lose more than your stake. + Odds are formula-set on a real capped-loss position. Wins if {token} reaches the strike before the deadline; + otherwise it closes at the deadline price. You can never lose more than your stake.

)} diff --git a/examples/predict/components/markets-app.tsx b/examples/predict/components/markets-app.tsx index a0484e5..16a0020 100644 --- a/examples/predict/components/markets-app.tsx +++ b/examples/predict/components/markets-app.tsx @@ -24,8 +24,9 @@ import { useBalances, useBasketBalance, useUsdcMint } from "@/lib/hooks"; import { loadSession, type LoadedSession } from "@/lib/session"; import { makeSessionSigner } from "@/lib/signer"; import { StreamProvider, useStream } from "@/lib/stream"; +import { useMarketRounds } from "@/lib/use-market-rounds"; +import { Bets } from "./bets"; import { Discover } from "./discover"; -import { Portfolio } from "./portfolio"; export function MarketsApp() { const wallets = useMemo(() => [new PhantomWalletAdapter(), new SolflareWalletAdapter()], []); @@ -56,7 +57,6 @@ function Inner({ owner }: { owner: string | null }) { const balances = useBalances(owner, usdcMint); const basket = useBasketBalance(owner, snapshot?.basketPubkey ?? null, usdcMint); const inBasketUsd = basket.bal?.inBasketUsd ?? null; - const positions = useMemo(() => Object.values(snapshot?.positionMetrics ?? {}), [snapshot]); const [session, setSession] = useState(null); useEffect(() => { @@ -67,6 +67,10 @@ function Inner({ owner }: { owner: string | null }) { [session, anchorWallet], ); + // Settlement engine: records each bet, auto-closes it at its deadline, and + // resolves win/lose. The "by " promise is only honest with this. + const { rounds, now, addRound, settleNow } = useMarketRounds(owner, snapshot, signer); + // "canBet" = wallet connected + one-tap enabled. Whether THIS stake is funded // (≥ MIN_STAKE and ≤ available) is gated per-bet in the ticket against // `availableUsd`, so an enabled-but-underfunded user gets an "add funds" @@ -160,8 +164,17 @@ function Inner({ owner }: { owner: string | null }) { )} {note &&

{note}

} - {owner && } - void basket.refresh()} /> + {owner && } + { + addRound(round); + void basket.refresh(); + }} + /> ); } diff --git a/examples/predict/components/portfolio.tsx b/examples/predict/components/portfolio.tsx deleted file mode 100644 index bcaca14..0000000 --- a/examples/predict/components/portfolio.tsx +++ /dev/null @@ -1,89 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/portfolio.tsx — "your open bets" (READ-ONLY, zero funds risk). -// Each open Flash V2 position IS a live bet: a LONG = an "Above" call, a SHORT = -// a "Below" call. Live value is the Flash-parity client mark-price math -// (computePositionView, GOTCHAS §20) — never the indexer's pnl field. Marks come -// from one polled bulk /v2/prices. No trade is ever placed here. -// ───────────────────────────────────────────────────────────────────────────── - -"use client"; - -import { useEffect, useState } from "react"; -import type { PositionMetrics, PriceInfo } from "flash-v2"; -import { computePositionView } from "@/lib/format"; -import { flash } from "@/lib/flash"; -import { fmtPrice } from "@/lib/copy"; -import { TokenIcon } from "./token-icon"; - -const fmtUsd = (n: number) => `${n < 0 ? "−" : ""}$${Math.abs(n).toFixed(2)}`; - -export function Portfolio({ positions }: { positions: PositionMetrics[] }) { - const [prices, setPrices] = useState>({}); - - useEffect(() => { - let alive = true; - let timer: ReturnType | undefined; - const tick = async () => { - try { - const p = await flash.prices(); - if (alive) setPrices(p); - } catch { - /* keep last marks */ - } - if (alive) timer = setTimeout(() => void tick(), 3000); - }; - void tick(); - return () => { - alive = false; - if (timer) clearTimeout(timer); - }; - }, []); - - if (positions.length === 0) return null; - - return ( -
-
- your open bets - - {positions.length} -
- -
- {positions.map((p) => { - const long = p.sideUi.toUpperCase() === "LONG"; - const mark = prices[p.marketSymbol]?.priceUi ?? null; - const view = computePositionView(p, mark); - const pnl = view?.pnlUsd ?? null; - const pct = view?.pnlPct ?? null; - const stake = Number(p.collateralUsdUi) || 0; - const winning = (pnl ?? 0) >= 0; - return ( -
-
- -
-

- {p.marketSymbol}{" "} - {long ? "▲ Above" : "Below ▼"} -

-

- stake {fmtUsd(stake)} · entry {fmtPrice(Number(p.entryPriceUi) || 0)} -

-
-
-
-

- {pnl === null ? "—" : `${pnl >= 0 ? "+" : ""}${fmtUsd(pnl)}`} -

-

- {pct === null ? "" : `${pct >= 0 ? "+" : ""}${pct.toFixed(1)}%`} -

-
-
- ); - })} -
-
- ); -} diff --git a/examples/predict/lib/rounds.ts b/examples/predict/lib/rounds.ts index 4feccdc..14b1db0 100644 --- a/examples/predict/lib/rounds.ts +++ b/examples/predict/lib/rounds.ts @@ -48,6 +48,11 @@ export interface Round { result?: RoundResult; /** The user saw and dismissed the result card (ack gates new rounds). */ acked?: boolean; + /** Prediction-market: the strike / threshold the question asks about (the TP). */ + strike?: number; + /** Prediction-market: reconciled profit if the bet WINS — used to score a + * position that closed via its take-profit before we observed it. */ + winProfitUsd?: number; } interface StoredRounds { @@ -55,16 +60,18 @@ interface StoredRounds { rounds: Round[]; } -const roundsKey = (owner: string) => `predict-rounds-${owner}`; +// Per-app store namespace: the Updown app and the /markets prediction app keep +// SEPARATE round histories (a shared key would cross-contaminate reconciliation). +const roundsKey = (owner: string, prefix = "predict-rounds") => `${prefix}-${owner}`; -export function loadRounds(owner: string): Round[] { +export function loadRounds(owner: string, prefix?: string): Round[] { if (typeof window === "undefined") return []; try { - const raw = window.localStorage.getItem(roundsKey(owner)); + const raw = window.localStorage.getItem(roundsKey(owner, prefix)); if (!raw) return []; const parsed = JSON.parse(raw) as StoredRounds; if (parsed.v !== SCHEMA_VERSION || !Array.isArray(parsed.rounds)) { - window.localStorage.removeItem(roundsKey(owner)); + window.localStorage.removeItem(roundsKey(owner, prefix)); return []; } return parsed.rounds; @@ -73,10 +80,10 @@ export function loadRounds(owner: string): Round[] { } } -export function saveRounds(owner: string, rounds: Round[]): void { +export function saveRounds(owner: string, rounds: Round[], prefix?: string): void { if (typeof window === "undefined") return; const stored: StoredRounds = { v: SCHEMA_VERSION, rounds }; - window.localStorage.setItem(roundsKey(owner), JSON.stringify(stored)); + window.localStorage.setItem(roundsKey(owner, prefix), JSON.stringify(stored)); } export function newRoundId(): string { diff --git a/examples/predict/lib/use-market-rounds.ts b/examples/predict/lib/use-market-rounds.ts new file mode 100644 index 0000000..a7b74cb --- /dev/null +++ b/examples/predict/lib/use-market-rounds.ts @@ -0,0 +1,148 @@ +// ───────────────────────────────────────────────────────────────────────────── +// lib/use-market-rounds.ts — the prediction-market settlement engine. +// +// Makes the "by " honest: a bet is a capped-loss perp + bundled TP, so +// it resolves ON-CHAIN by itself when the strike is hit (WIN) or it liquidates +// (LOSE). But a bet that does NEITHER by its deadline would otherwise ride forever +// (and bleed borrow fees) — so at expiry we CLOSE it at mark, exactly like the +// proven Updown settleRound (lib/components/app.tsx). Reconciliation is on OBSERVED +// state (snapshot + clock), never a bare timer; an in-flight set + "settling" +// status guard double-close. Rounds are a CACHE — chain positions are the truth. +// +// SEPARATE store namespace from the Updown app (a shared key would cross- +// contaminate reconciliation). Chain truth always wins over the cache. +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { assertNoErr, type BasketSnapshot } from "flash-v2"; +import { flash } from "./flash"; +import { computePositionView } from "./format"; +import type { ActiveSigner } from "./signer"; +import { + loadRounds, + positionFor, + reconcile, + saveRounds, + withResult, + withStatus, + type Round, +} from "./rounds"; + +/** localStorage namespace for /markets bets — distinct from the Updown app. */ +const STORE_PREFIX = "predict-markets"; +const SETTLE_RETRY_MS = 8_000; + +export interface MarketRounds { + rounds: Round[]; + /** monotonic 1s clock for countdowns + expiry. */ + now: number; + addRound: (round: Round) => void; + settleNow: (round: Round) => void; +} + +export function useMarketRounds( + owner: string | null, + snapshot: BasketSnapshot | null, + signer: ActiveSigner | null, +): MarketRounds { + const [rounds, setRounds] = useState([]); + const [now, setNow] = useState(() => Date.now()); + const loadedFor = useRef(null); + + // Load this owner's bets; persist on every change (only after the load landed, + // so we never stomp stored rounds with the empty initial state). + useEffect(() => { + if (owner) { + setRounds(loadRounds(owner, STORE_PREFIX)); + loadedFor.current = owner; + } else { + setRounds([]); + loadedFor.current = null; + } + }, [owner]); + + useEffect(() => { + if (owner && loadedFor.current === owner) saveRounds(owner, rounds, STORE_PREFIX); + }, [owner, rounds]); + + useEffect(() => { + const t = setInterval(() => setNow(Date.now()), 1000); + return () => clearInterval(t); + }, []); + + const addRound = useCallback((round: Round) => setRounds((rs) => [round, ...rs]), []); + + const inflight = useRef>(new Set()); + const retryAt = useRef>(new Map()); + + // Close a still-open position at mark (expiry). Explicit FULL close ("0" — the + // 97% trap never applies); the result is the PnL the user was watching. + const settleRound = useCallback( + async (round: Round) => { + if (inflight.current.has(round.id) || !signer) return; + if (Date.now() < (retryAt.current.get(round.id) ?? 0)) return; + inflight.current.add(round.id); + setRounds((rs) => withStatus(rs, round.id, "settling")); + try { + const metrics = snapshot ? positionFor(snapshot, round.market, round.side) : undefined; + const mark = await flash.price(round.market).then((p) => p.priceUi).catch(() => null); + const view = metrics ? computePositionView(metrics, mark ?? Number(metrics.entryPriceUi)) : null; + const res = await flash.closePosition({ + marketSymbol: round.market, + side: round.side, + inputUsdUi: "0", + withdrawTokenSymbol: "USDC", + owner: signer.owner, + slippagePercentage: "1", + ...signer.tradeFields, + }); + assertNoErr("close-position", res); + if (!res.transactionBase64) throw new Error(res.err ?? "no transaction returned"); + const sent = await signer.sendTrade(res.transactionBase64); + const pnlUsd = view?.pnlUsd ?? 0; + setRounds((rs) => withResult(rs, round.id, { pnlUsd, won: pnlUsd > 0, settledAt: Date.now(), signature: sent.signature })); + } catch (e) { + retryAt.current.set(round.id, Date.now() + SETTLE_RETRY_MS); + const gone = /Position is empty/i.test(e instanceof Error ? e.message : String(e)); + setRounds((rs) => + gone + ? rs.map((r) => (r.id === round.id ? { ...r, status: "closed-elsewhere" as const } : r)) + : withStatus(rs, round.id, "active"), + ); + } finally { + inflight.current.delete(round.id); + } + }, + [signer, snapshot], + ); + + const recon = useMemo(() => reconcile(rounds, snapshot, now), [rounds, snapshot, now]); + + // Expired but still open → settle at mark. + useEffect(() => { + for (const r of recon.toSettle) void settleRound(r); + }, [recon, settleRound]); + + // Vanished without our close → the take-profit fired (WIN) or it liquidated + // (LOSE). Infer from the live mark vs the strike, ONCE per round. The exact + // realized amount is on-chain; this scores the card (win uses the reconciled + // win-profit, loss is the capped −stake). + const resolving = useRef>(new Set()); + useEffect(() => { + for (const r of recon.closedElsewhere) { + if (resolving.current.has(r.id)) continue; + resolving.current.add(r.id); + void (async () => { + const mark = await flash.price(r.market).then((p) => p.priceUi).catch(() => null); + const strike = r.strike ?? r.quote.entryPrice; + const won = mark != null && (r.side === "LONG" ? mark >= strike * 0.999 : mark <= strike * 1.001); + const pnlUsd = won ? (r.winProfitUsd ?? 0) : -r.stakeUsd; + setRounds((rs) => withResult(rs, r.id, { pnlUsd, won, settledAt: Date.now() })); + })(); + } + }, [recon]); + + return { rounds, now, addRound, settleNow: (r) => void settleRound(r) }; +} From 6a648b2a9cbc16a46b0d6fde31589b9c60a8f51b Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 11:39:47 -0400 Subject: [PATCH 19/26] predict v2.1: remediate adversarial-review findings (C1,C2,H1,H2,H4 + scoring) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3-agent review (code-reviewer + silent-failure-hunter + money-math auditor) found real issues in the settlement engine. Fixed the clear, high-value ones: - C1 (CRITICAL): reconcileBet now calls checkCollateralForTriggers(stake, entryFee) — blocks a stake that drops to <= after fees (the bundled TP, the entire win boundary, would silently fail to place on-chain). - C2 (CRITICAL): on mount, demote any persisted 'settling' round -> 'active' so a tab-close mid-settle can't strand a position forever (reconcile only acts on 'active'). - Settlement scoring (CRITICAL, display/stats): resolveVanished now RETURNS on a failed price fetch instead of branding a winner a total loss (mark==null -> -stake was the worst bug). Retry-safe. - H1 (HIGH): doConfirm gains a synchronous useRef lock — a fast double-tap can no longer pass the async 'phase' guard twice and fire two real opens. - H2 (HIGH): settleRound skips the close if the position is already gone (positionFor undefined) -> resolves as vanished, so a timeout-retry can't double-close. - H4 (HIGH): the 'Position is empty' branch now SCORES the round (resolveVanished) instead of leaving a resultless 'closed-elsewhere' that vanishes from the UI. - F2 (HIGH latent): deleted dead bucketMarket + openParamsFor — undefended exported paths that render a guaranteed-loss strike as 97c if re-wired. - markets.ts: oracle<=0 now -> PROB_FLOOR (3c 'no'), not the 97c ceiling a zero entry painted (matches the doc); refs cleared on owner switch (M3); custody fetch checks res.ok before caching (rev2-H4). typecheck + build green. BANNER STAYS UP. Still open for the user: F7 (cross-app shared position — / and /markets share one on-chain position per market+side; needs a product decision) + the residual vanish-scoring heuristic (exact realized PnL is the on-chain receipt). Co-Authored-By: Claude Opus 4.8 --- examples/predict/components/market-detail.tsx | 17 ++- examples/predict/lib/hooks.ts | 3 + examples/predict/lib/markets.ts | 109 ++---------------- examples/predict/lib/use-market-rounds.ts | 83 ++++++++----- 4 files changed, 84 insertions(+), 128 deletions(-) diff --git a/examples/predict/components/market-detail.tsx b/examples/predict/components/market-detail.tsx index 7df63ef..f498bdd 100644 --- a/examples/predict/components/market-detail.tsx +++ b/examples/predict/components/market-detail.tsx @@ -14,8 +14,8 @@ "use client"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { assertNoErr, validateTriggerPrice, type OpenPositionResponse } from "flash-v2"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { assertNoErr, checkCollateralForTriggers, validateTriggerPrice, type OpenPositionResponse } from "flash-v2"; import { cents, profitUsd, @@ -77,6 +77,11 @@ function reconcileBet(res: OpenPositionResponse, m: PricedMarket, stakeUsd: numb const valid = validateTriggerPrice({ side, kind: "tp", price: m.construction.takeProfitPrice, markPrice: entry }); if (!valid.ok) return { ...base, ok: false, reason: "This strike isn't beyond the live fill, so the bet can't pay out. Pick a farther strike." }; + // On-chain rule: TP/SL/limit need > $10 collateral AFTER entry fees. A stake + // whose fee drops it to ≤ $10 opens a perp whose take-profit — the entire win + // boundary — silently fails to place. Never sign that (the fee is in hand here). + const collat = checkCollateralForTriggers(stakeUsd, entryFeeUsd); + if (!collat.ok) return { ...base, ok: false, reason: "Stake too small once fees apply — raise it a little so the take-profit can be placed." }; if (!tp) return { ...base, ok: false, reason: "Couldn't price the take-profit on this fill. Try again in a moment." }; // THE SPREAD GUARD: on the real round-trip the TP must net a profit, else the // move is inside the (two-leg) spread and would fire as a loss. @@ -143,6 +148,9 @@ export function MarketDetail({ const [review, setReview] = useState(null); const [ticketError, setTicketError] = useState(null); const [result, setResult] = useState<{ ok: boolean; msg: string } | null>(null); + // Synchronous re-entrancy lock: React state (phase) updates async, so a fast + // double-tap could pass the `phase` guard twice and fire TWO real opens. + const sending = useRef(false); // Any change to the bet (strike, side, stake) invalidates a pending review — // you can only sign numbers you just reviewed. @@ -192,7 +200,8 @@ export function MarketDetail({ // re-run the same reconciliation, and only sign + send if it STILL passes. A // re-quote that went bad bumps back to the review with the new reason. const doConfirm = useCallback(async () => { - if (!active || !signer || phase === "signing") return; // double-submit guard + if (!active || !signer || phase === "signing" || sending.current) return; // double-submit guard + sending.current = true; setPhase("signing"); setTicketError(null); try { @@ -229,6 +238,8 @@ export function MarketDetail({ } catch (e) { setTicketError(calmError(e)); setPhase("review"); + } finally { + sending.current = false; } }, [active, signer, reqFor, stakeUsd, onPlaced, phase]); diff --git a/examples/predict/lib/hooks.ts b/examples/predict/lib/hooks.ts index db98012..2293fd8 100644 --- a/examples/predict/lib/hooks.ts +++ b/examples/predict/lib/hooks.ts @@ -117,6 +117,9 @@ async function loadCustodyPricing(): Promise | null> custodyInFlight = (async () => { try { const res = await fetch(`${flash.network.apiBase}/raw/custodies`); + // Don't parse a non-OK response (error page / 5xx) into a partial map that + // then sticks for the session — bail so the next call retries (review H4). + if (!res.ok) return custodyPricing; const json = (await res.json()) as | Array<{ account?: { tokenMint?: string; pricing?: { minInitialLeverage?: number; maxInitialLeverage?: number; tradeSpreadLong?: number; tradeSpreadShort?: number } } }> | { custodies?: unknown }; diff --git a/examples/predict/lib/markets.ts b/examples/predict/lib/markets.ts index 723d6d7..e460dc7 100644 --- a/examples/predict/lib/markets.ts +++ b/examples/predict/lib/markets.ts @@ -199,7 +199,10 @@ export function priceMarket(opts: { // R = L·t − fees/C = L·(t − fee) (fees scale with size). t ≤ fee ⇒ R floors at 0. const payoutMult = Math.max(0, lev * (t - feesRate)); - const prob = clampProb(1 / (1 + payoutMult)); + // A non-positive / non-finite oracle is UNPRICEABLE → floor (3¢ "no"), never the + // 97¢ ceiling a zero entry would otherwise paint (a broken feed must not read as + // a near-certain YES). Live cards also gate on loaded limits + a positive price. + const prob = oracle > 0 ? clampProb(1 / (1 + payoutMult)) : PROB_FLOOR; return { token, direction, strike: opts.strike, oracle, entry: entryFill, spread, timeframe: tf, prob, payoutMult, @@ -326,105 +329,17 @@ export function headlineMarket(opts: { }); } -// ── reach zones ("how far could it move?") ──────────────────────────────────── -// HONEST MODEL (SPEC §v2.1 fix #7): a single capped-loss perp + one take-profit -// can only express a ONE-TOUCH "does it reach price X", never a true bounded -// "lands between X and Y" (that needs a call-spread = two positions). So each -// zone's number is the INDEPENDENT chance price REACHES into it (crosses its near -// edge). Reaching a far zone implies the nearer ones — the events OVERLAP, they -// are NOT a partition — so they are NOT normalized and do NOT sum to 100%. -// Pretending they did was the dishonest part this fix removes. - -export interface OutcomeBucket { - label: string; - /** inclusive lower / exclusive upper price edge; null = open-ended. */ - lo: number | null; - hi: number | null; - /** INDEPENDENT one-touch chance price reaches into this zone (NOT normalized; - * zones overlap, so these do not sum to 1). */ - prob: number; - /** the perp that expresses "reaches into this zone" (one-touch to the near edge). */ - market: PricedMarket; -} - -export interface BucketMarket { - token: string; - oracle: number; - timeframe: TimeframeId; - buckets: OutcomeBucket[]; -} - -/** Build the reach-zone row by splitting the price axis at `edges` (ascending). - * Each zone's probability is the spread-aware ONE-TOUCH chance price reaches into - * it (crosses the edge nearest the oracle). NOT normalized — see the note above. */ -export function bucketMarket(opts: { - token: string; - oracle: number; - spread: number; - maxLeverage: number; - minLeverage?: number; - timeframe: TimeframeId; - edges: number[]; -}): BucketMarket { - const { token, oracle, spread, timeframe: tf } = opts; - const floor = Math.max(1.1, opts.minLeverage ?? 1.1); - const edges = [...opts.edges].sort((a, b) => a - b); - // bucket boundaries: (-∞, e0), [e0, e1), … , [eN, +∞) - const bounds: { lo: number | null; hi: number | null }[] = []; - bounds.push({ lo: null, hi: edges[0] ?? null }); - for (let i = 0; i < edges.length - 1; i++) bounds.push({ lo: edges[i]!, hi: edges[i + 1]! }); - if (edges.length > 0) bounds.push({ lo: edges[edges.length - 1]!, hi: null }); - - const raw = bounds.map((b) => { - // Which side of the oracle the zone sits on, and the NEAR edge — the threshold - // price crosses to reach INTO the zone. One-touch to that edge = the reach. - const zoneRef = b.lo == null ? b.hi! : b.hi == null ? b.lo : (b.lo + b.hi) / 2; - const direction: Direction = zoneRef >= oracle ? "ABOVE" : "BELOW"; - const nearEdge = direction === "ABOVE" ? (b.lo ?? b.hi!) : (b.hi ?? b.lo!); - const leverage = clampLeverage(LIQ_FACTOR / (FAR_MOVE_PCT[tf] / 100), spread, opts.maxLeverage, floor); - const m = priceMarket({ token, direction, oracle, spread, leverage, strike: nearEdge, timeframe: tf }); - return { b, m }; - }); - - // NO normalization — each is an independent reach chance (the zones overlap). - const buckets: OutcomeBucket[] = raw.map(({ b, m }) => ({ - label: bucketLabel(b.lo, b.hi), - lo: b.lo, - hi: b.hi, - prob: m.prob, - market: m, - })); - return { token, oracle, timeframe: tf, buckets }; -} - -function bucketLabel(lo: number | null, hi: number | null): string { - const f = (n: number) => (n >= 1000 ? n.toLocaleString(undefined, { maximumFractionDigits: 0 }) : n.toFixed(2)); - if (lo == null) return `< ${f(hi!)}`; - if (hi == null) return `≥ ${f(lo)}`; - return `${f(lo)} – ${f(hi)}`; -} +// NOTE: multi-outcome "buckets" were REMOVED (SPEC §v2.1 fix #7). A single +// capped-loss perp + one take-profit can only express a ONE-TOUCH "reaches X", +// never a bounded "lands between X and Y" (that needs a call-spread = two +// positions), and the honest one-touch relabel degenerated to 97¢ inside the +// spread. The strike ladder already gives honest reach-odds. The old bucketMarket +// / openParamsFor builders were deleted too — they were undefended exported paths +// that would render a guaranteed-loss strike as 97¢ if re-wired. A future +// call-spread rebuild is a flagged, user-approved scope item. // ── the bridge to the trade builder ─────────────────────────────────────────── -/** The flash.openPosition params that OPEN a YES position for this market at a - * given stake — a capped-loss perp with the bundled take-profit = the strike. - * Slippage MUST clear the spread (a fill enters a full spread from the oracle), - * so it is derived from the market's spread, never a hardcoded 1%. - * (Shape matches OpenPositionRequest; the caller adds owner + session fields.) */ -export function openParamsFor(m: PricedMarket, stakeUsd: number) { - return { - inputTokenSymbol: "USDC", - outputTokenSymbol: m.token, - inputAmountUi: stakeUsd.toFixed(2), - leverage: Number(m.construction.leverage.toFixed(4)), - tradeType: m.construction.side, - orderType: "MARKET" as const, - slippagePercentage: slippageForSpread(m.spread), - // bundled TP = the YES win boundary; the knockout is the native liquidation. - takeProfit: m.construction.takeProfitPrice.toFixed(4), - }; -} - /** Slippage tolerance that clears the trade spread with headroom. A hardcoded * "1" is far below a 5–10% spread and rejects every fill (SPEC §v2.1 fix #8). */ export function slippageForSpread(spread: number): string { diff --git a/examples/predict/lib/use-market-rounds.ts b/examples/predict/lib/use-market-rounds.ts index a7b74cb..47a1920 100644 --- a/examples/predict/lib/use-market-rounds.ts +++ b/examples/predict/lib/use-market-rounds.ts @@ -50,12 +50,23 @@ export function useMarketRounds( const [rounds, setRounds] = useState([]); const [now, setNow] = useState(() => Date.now()); const loadedFor = useRef(null); + const inflight = useRef>(new Set()); + const retryAt = useRef>(new Map()); + const resolving = useRef>(new Set()); // Load this owner's bets; persist on every change (only after the load landed, // so we never stomp stored rounds with the empty initial state). useEffect(() => { + // Wallet switch: drop cross-owner in-flight / backoff / resolving state so a + // prior owner's ids can't suppress the new owner's settlement (review M3). + inflight.current.clear(); + retryAt.current.clear(); + resolving.current.clear(); if (owner) { - setRounds(loadRounds(owner, STORE_PREFIX)); + // A round persisted as "settling" means a close was dispatched but the tab + // closed before it confirmed (the in-flight guard is gone). Demote it to + // "active" so reconciliation re-drives it from chain truth (review C2). + setRounds(loadRounds(owner, STORE_PREFIX).map((r) => (r.status === "settling" ? { ...r, status: "active" as const } : r))); loadedFor.current = owner; } else { setRounds([]); @@ -74,8 +85,26 @@ export function useMarketRounds( const addRound = useCallback((round: Round) => setRounds((rs) => [round, ...rs]), []); - const inflight = useRef>(new Set()); - const retryAt = useRef>(new Map()); + // Score a position that VANISHED on-chain (its take-profit fired = WIN, or it + // liquidated = LOSE). The exact realized amount is on-chain; we infer win/lose + // from the live mark vs the strike, ONCE per round. CRITICAL: on a failed price + // fetch we DO NOT resolve (return so a later tick retries) — branding a winner a + // total loss because one price GET failed is the worst outcome (review C1/C2). + // NOTE (documented residual): a sharp reversal between the vanish and this poll + // can still misread win↔lose; the exact realized PnL is the on-chain receipt. + const resolveVanished = useCallback(async (round: Round) => { + if (resolving.current.has(round.id)) return; + resolving.current.add(round.id); + const mark = await flash.price(round.market).then((p) => p.priceUi).catch(() => null); + if (mark == null || !Number.isFinite(mark)) { + resolving.current.delete(round.id); // no guess on a bad fetch — retry next tick + return; + } + const strike = round.strike ?? round.quote.entryPrice; + const won = round.side === "LONG" ? mark >= strike * 0.999 : mark <= strike * 1.001; + const pnlUsd = won ? (round.winProfitUsd ?? 0) : -round.stakeUsd; + setRounds((rs) => withResult(rs, round.id, { pnlUsd, won, settledAt: Date.now() })); + }, []); // Close a still-open position at mark (expiry). Explicit FULL close ("0" — the // 97% trap never applies); the result is the PnL the user was watching. @@ -84,11 +113,20 @@ export function useMarketRounds( if (inflight.current.has(round.id) || !signer) return; if (Date.now() < (retryAt.current.get(round.id) ?? 0)) return; inflight.current.add(round.id); + // If the position is already GONE (TP/liq fired, or a prior timed-out close + // finally landed), don't fire a close against nothing — resolve it as + // vanished. This also stops a timeout-retry from double-closing (review H2). + const metrics = snapshot ? positionFor(snapshot, round.market, round.side) : undefined; + if (!metrics) { + inflight.current.delete(round.id); + setRounds((rs) => rs.map((r) => (r.id === round.id ? { ...r, status: "closed-elsewhere" as const } : r))); + void resolveVanished(round); + return; + } setRounds((rs) => withStatus(rs, round.id, "settling")); try { - const metrics = snapshot ? positionFor(snapshot, round.market, round.side) : undefined; const mark = await flash.price(round.market).then((p) => p.priceUi).catch(() => null); - const view = metrics ? computePositionView(metrics, mark ?? Number(metrics.entryPriceUi)) : null; + const view = computePositionView(metrics, mark ?? Number(metrics.entryPriceUi)); const res = await flash.closePosition({ marketSymbol: round.market, side: round.side, @@ -106,16 +144,19 @@ export function useMarketRounds( } catch (e) { retryAt.current.set(round.id, Date.now() + SETTLE_RETRY_MS); const gone = /Position is empty/i.test(e instanceof Error ? e.message : String(e)); - setRounds((rs) => - gone - ? rs.map((r) => (r.id === round.id ? { ...r, status: "closed-elsewhere" as const } : r)) - : withStatus(rs, round.id, "active"), - ); + if (gone) { + // Already closed (TP/liq landed). Flip + SCORE it — never leave a + // resultless round that vanishes from the UI (review H4). + setRounds((rs) => rs.map((r) => (r.id === round.id ? { ...r, status: "closed-elsewhere" as const } : r))); + void resolveVanished(round); + } else { + setRounds((rs) => withStatus(rs, round.id, "active")); + } } finally { inflight.current.delete(round.id); } }, - [signer, snapshot], + [signer, snapshot, resolveVanished], ); const recon = useMemo(() => reconcile(rounds, snapshot, now), [rounds, snapshot, now]); @@ -125,24 +166,10 @@ export function useMarketRounds( for (const r of recon.toSettle) void settleRound(r); }, [recon, settleRound]); - // Vanished without our close → the take-profit fired (WIN) or it liquidated - // (LOSE). Infer from the live mark vs the strike, ONCE per round. The exact - // realized amount is on-chain; this scores the card (win uses the reconciled - // win-profit, loss is the capped −stake). - const resolving = useRef>(new Set()); + // Vanished without our close → score it (retry-safe win/lose inference). useEffect(() => { - for (const r of recon.closedElsewhere) { - if (resolving.current.has(r.id)) continue; - resolving.current.add(r.id); - void (async () => { - const mark = await flash.price(r.market).then((p) => p.priceUi).catch(() => null); - const strike = r.strike ?? r.quote.entryPrice; - const won = mark != null && (r.side === "LONG" ? mark >= strike * 0.999 : mark <= strike * 1.001); - const pnlUsd = won ? (r.winProfitUsd ?? 0) : -r.stakeUsd; - setRounds((rs) => withResult(rs, r.id, { pnlUsd, won, settledAt: Date.now() })); - })(); - } - }, [recon]); + for (const r of recon.closedElsewhere) void resolveVanished(r); + }, [recon, resolveVanished]); return { rounds, now, addRound, settleNow: (r) => void settleRound(r) }; } From 55197e5ba48aaef7818f0c2136705f8de345a3a0 Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 11:51:37 -0400 Subject: [PATCH 20/26] predict v2.1: fix lost-result regression from the remediation (verifier MEDIUM) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The re-review caught a regression I introduced: settleRound's gone-branches flipped status -> 'closed-elsewhere' BEFORE resolveVanished, but reconcile only re-emits 'active' rounds, so a null price-fetch in resolveVanished stranded the round resultless forever (funds safe — position already closed on-chain; only the local win/lose card/stat was lost). Fix (single-writer transition): the gone-branches now LEAVE the round 'active' and let resolveVanished own the active->settled transition. A failed score-fetch keeps it 'active', so reconcile re-emits + retries it. No round is ever stranded resultless. (No status writes 'closed-elsewhere' in the markets path anymore; active->settled directly.) Verifier confirmed 7/8 prior fixes correct + the doConfirm lock clean (finally releases on early return). typecheck + build green. Co-Authored-By: Claude Opus 4.8 --- examples/predict/lib/use-market-rounds.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/predict/lib/use-market-rounds.ts b/examples/predict/lib/use-market-rounds.ts index 47a1920..3369efc 100644 --- a/examples/predict/lib/use-market-rounds.ts +++ b/examples/predict/lib/use-market-rounds.ts @@ -118,8 +118,11 @@ export function useMarketRounds( // vanished. This also stops a timeout-retry from double-closing (review H2). const metrics = snapshot ? positionFor(snapshot, round.market, round.side) : undefined; if (!metrics) { + // Already gone — don't close nothing. LEAVE the round "active" and let + // resolveVanished record the result; if its price fetch fails it stays + // "active" so reconcile re-emits it (the retry path needs "active" — a + // pre-flip to "closed-elsewhere" would strand it resultless). inflight.current.delete(round.id); - setRounds((rs) => rs.map((r) => (r.id === round.id ? { ...r, status: "closed-elsewhere" as const } : r))); void resolveVanished(round); return; } @@ -144,14 +147,11 @@ export function useMarketRounds( } catch (e) { retryAt.current.set(round.id, Date.now() + SETTLE_RETRY_MS); const gone = /Position is empty/i.test(e instanceof Error ? e.message : String(e)); - if (gone) { - // Already closed (TP/liq landed). Flip + SCORE it — never leave a - // resultless round that vanishes from the UI (review H4). - setRounds((rs) => rs.map((r) => (r.id === round.id ? { ...r, status: "closed-elsewhere" as const } : r))); - void resolveVanished(round); - } else { - setRounds((rs) => withStatus(rs, round.id, "active")); - } + // Revert to "active" in BOTH cases. If the position is gone, also score it; + // keeping it "active" (not "closed-elsewhere") means a failed score-fetch + // is re-emitted by reconcile and retried — never stranded resultless (H4). + setRounds((rs) => withStatus(rs, round.id, "active")); + if (gone) void resolveVanished(round); } finally { inflight.current.delete(round.id); } From 55d3b3fae82600a0dadf1f8424eafb39f7bf8b38 Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 11:56:32 -0400 Subject: [PATCH 21/26] predict v2.1: F7 cross-app guard + spec review-outcome record MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F7 (cross-app shared position): / (Updown) and /markets share one on-chain position per market+side on a wallet, so one app's auto-settle could FULL-close the other's bet. settleRound now takes an 'auto' flag: the expiry watcher (auto=true) REFUSES to full-close a position whose on-chain size > 1.5x this bet's recorded size (a blend tell-tale), leaving it 'active'; the user's explicit manual settle (auto=false) is allowed through. Imperfect for small blends (sub-account namespacing is the complete fix) — documented. SPEC-PREDICT-V2.md: added the v2.1 REVIEW OUTCOME section — every fixed CRITICAL/HIGH (C1,C2,scoring,H1,H2,F2,F7 + oracle/refs/res.ok), the one regression caught+fixed, and the documented residuals that gate the banner. typecheck + build green. Banner STAYS UP pending a final re-verify of the F7 guard + the product decision on whether the two example apps may share a wallet. Co-Authored-By: Claude Opus 4.8 --- examples/predict/SPEC-PREDICT-V2.md | 20 ++++++++++++++++++++ examples/predict/lib/use-market-rounds.ts | 22 +++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/examples/predict/SPEC-PREDICT-V2.md b/examples/predict/SPEC-PREDICT-V2.md index 5c5c3cf..15aa192 100644 --- a/examples/predict/SPEC-PREDICT-V2.md +++ b/examples/predict/SPEC-PREDICT-V2.md @@ -117,6 +117,26 @@ SOL (s=0.10) ⇒ `L ≤ 0.92/0.15 ≈ 6×`. High-spread markets are naturally lo **Re-run the 3-agent adversarial review before flipping the "don't trade real funds" banner.** +## v2.1 REVIEW OUTCOME (2026-06-15) — ②–⑧ built + reviewed + remediated + +All of ②–⑧ (plus ①, ⑨) implemented and verified (typecheck + production build green; the odds engine verified against the **live mainnet API** to within 3% across SOL + BTC headline + full ladder). Two bugs the original audit missed were found en route: the **two-leg spread** (a TP at K exits at `K·(1−s)` — strike now grossed up by `1/(1−s)`, live-confirmed) and **buckets are inexpressible** on a single-TP perp (removed; the dead builder deleted). + +The mandatory 3-agent adversarial review (code-reviewer + silent-failure-hunter + money-math auditor) then ran, a 4th agent verified the fixes. Outcome — all fixed + re-verified (commits `6a648b2`, `55197e5`, F7 guard): + +- **C1** (CRITICAL): `reconcileBet` now calls `checkCollateralForTriggers(stake, entryFee)` — blocks a stake that drops ≤ $10 after fees (bundled TP would silently fail to place). Runs on both the review + owner quotes. +- **C2** (CRITICAL): mount-sweep demotes a persisted `"settling"` round → `"active"` so a tab-close mid-settle can't strand a position. +- **Settlement scoring** (CRITICAL, display/stats only): `resolveVanished` RETURNS on a failed price fetch (never brands a winner a `−stake` loss); retry-safe; rounds go `active → settled` only (no resultless `closed-elsewhere`). +- **H1**: synchronous `useRef` lock on `doConfirm` (the async `phase` guard couldn't stop a double-tap double-open). +- **H2**: `settleRound` skips the close if the position is already gone (no timeout double-close). +- **F2** (HIGH latent): deleted dead `bucketMarket` + `openParamsFor` (undefended paths that rendered guaranteed-loss strikes at 97¢). +- **F7** (the one architecture finding): `/` (Updown) and `/markets` share ONE on-chain position per market+side. The auto-settle watcher now **refuses to FULL-close a position whose on-chain size ≫ this bet's recorded size** (a blend tell-tale); the user's explicit manual `settle` is allowed through. `settleRound(round, auto)`. +- Plus: `oracle ≤ 0 → PROB_FLOOR` (was 97¢ ceiling); cross-owner refs cleared on wallet switch; custody fetch checks `res.ok`. + +### Residuals (no funds at risk; documented, gate the banner) +- **F7 guard is a heuristic** — a SMALL blend (this bet + a tiny other) under the 1.5× threshold could still be auto-closed; sub-account namespacing is the complete fix. A blended bet past expiry shows `settling…` until manually settled or the blend clears. +- **Vanish-scoring is a mark-vs-strike inference** — the exact realized win/lose is the on-chain receipt; a sharp reversal between vanish and poll can still misread the card (the `−stake`-on-null case is fixed). +- **Banner stays UP.** Before flipping it: re-verify the F7 guard + the residuals are acceptable, and decide whether the two example apps may share a wallet (the cleanest fix is per-app sub-accounts). + ## Verification bar (every phase) `bun run --cwd examples/predict typecheck` and `build` must stay green. Mainnet, real funds — no shortcuts. Adversarial review before anything is called done. diff --git a/examples/predict/lib/use-market-rounds.ts b/examples/predict/lib/use-market-rounds.ts index 3369efc..2777e99 100644 --- a/examples/predict/lib/use-market-rounds.ts +++ b/examples/predict/lib/use-market-rounds.ts @@ -109,7 +109,7 @@ export function useMarketRounds( // Close a still-open position at mark (expiry). Explicit FULL close ("0" — the // 97% trap never applies); the result is the PnL the user was watching. const settleRound = useCallback( - async (round: Round) => { + async (round: Round, auto = false) => { if (inflight.current.has(round.id) || !signer) return; if (Date.now() < (retryAt.current.get(round.id) ?? 0)) return; inflight.current.add(round.id); @@ -126,6 +126,21 @@ export function useMarketRounds( void resolveVanished(round); return; } + // F7 guard: on AUTO-settle, refuse to FULL-close a position whose on-chain + // size is far larger than THIS bet recorded — it's BLENDED with another + // position (the same market+side opened in the other app, /, on this wallet; + // Flash keeps ONE position per market+side). A full close would settle funds + // that aren't this bet's. The user's EXPLICIT manual settle is allowed + // through; auto leaves it "active" until the shared position clears. + if (auto) { + const onChainSize = Number(metrics.sizeUsdUi) || 0; + const roundSize = round.quote.sizeUsd || 0; + if (roundSize > 0 && onChainSize > roundSize * 1.5) { + inflight.current.delete(round.id); + retryAt.current.set(round.id, Date.now() + SETTLE_RETRY_MS); + return; + } + } setRounds((rs) => withStatus(rs, round.id, "settling")); try { const mark = await flash.price(round.market).then((p) => p.priceUi).catch(() => null); @@ -161,9 +176,10 @@ export function useMarketRounds( const recon = useMemo(() => reconcile(rounds, snapshot, now), [rounds, snapshot, now]); - // Expired but still open → settle at mark. + // Expired but still open → AUTO-settle at mark (F7-guarded against closing a + // position blended with the other app's bet). useEffect(() => { - for (const r of recon.toSettle) void settleRound(r); + for (const r of recon.toSettle) void settleRound(r, true); }, [recon, settleRound]); // Vanished without our close → score it (retry-safe win/lose inference). From 2ef139d6fa0c689cda2b7d7738e44df4a25a6f67 Mon Sep 17 00:00:00 2001 From: MightyMags Date: Mon, 15 Jun 2026 14:26:16 -0400 Subject: [PATCH 22/26] predict: make markets THE app at /, any-wallet connect, standalone funds; remove Updown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wallet (was locked to one wallet): the connect flow force-selected wallets[0]. New WalletPicker lists EVERY installed wallet (Wallet Standard auto-detects Phantom/Solflare/Backpack/Glow/…); nothing is hardcoded. Added disconnect/switch (tap the address). wallet-adapter-react 0.15.39 already does Standard detection. Routing (couldn't reach /markets without the URL): app/page.tsx now renders the prediction app at /, /markets 307-redirects to /, layout title updated. The prediction market is the homepage. Standalone funds: ported deposit/withdraw (FundsSheet + lib/funds) into the markets shell — tap the balance pill to deposit USDC. Markets no longer depends on the old app for funding. Removed the Updown app: deleted 13 Updown-only files (app, active-round, enable-sheet, history, mechanics-disclosure, price-chart, settle-card, stats-strip, ticket, market-picker, wallet-bar, lib/clock, lib/use-price-history). Verified the delete closure is self-contained (every importer was itself Updown-only) and the kept markets tree references none of them. BONUS: removing the second app ELIMINATES the F7 cross-app shared-position hazard entirely. typecheck + production build green; dev server fresh on :3000 (/ → 200 predict, /markets → 307 →/). Co-Authored-By: Claude Opus 4.8 --- examples/predict/app/layout.tsx | 4 +- examples/predict/app/markets/page.tsx | 10 +- examples/predict/app/page.tsx | 11 +- examples/predict/components/active-round.tsx | 88 --- examples/predict/components/app.tsx | 555 ------------------ examples/predict/components/enable-sheet.tsx | 85 --- examples/predict/components/history.tsx | 46 -- examples/predict/components/market-picker.tsx | 207 ------- examples/predict/components/markets-app.tsx | 113 +++- .../components/mechanics-disclosure.tsx | 57 -- examples/predict/components/price-chart.tsx | 120 ---- examples/predict/components/settle-card.tsx | 76 --- examples/predict/components/stats-strip.tsx | 31 - examples/predict/components/ticket.tsx | 253 -------- examples/predict/components/wallet-bar.tsx | 139 ----- examples/predict/components/wallet-picker.tsx | 76 +++ examples/predict/lib/clock.tsx | 26 - examples/predict/lib/use-price-history.ts | 79 --- 18 files changed, 185 insertions(+), 1791 deletions(-) delete mode 100644 examples/predict/components/active-round.tsx delete mode 100644 examples/predict/components/app.tsx delete mode 100644 examples/predict/components/enable-sheet.tsx delete mode 100644 examples/predict/components/history.tsx delete mode 100644 examples/predict/components/market-picker.tsx delete mode 100644 examples/predict/components/mechanics-disclosure.tsx delete mode 100644 examples/predict/components/price-chart.tsx delete mode 100644 examples/predict/components/settle-card.tsx delete mode 100644 examples/predict/components/stats-strip.tsx delete mode 100644 examples/predict/components/ticket.tsx delete mode 100644 examples/predict/components/wallet-bar.tsx create mode 100644 examples/predict/components/wallet-picker.tsx delete mode 100644 examples/predict/lib/clock.tsx delete mode 100644 examples/predict/lib/use-price-history.ts diff --git a/examples/predict/app/layout.tsx b/examples/predict/app/layout.tsx index 927850b..df5812a 100644 --- a/examples/predict/app/layout.tsx +++ b/examples/predict/app/layout.tsx @@ -15,9 +15,9 @@ const sora = Sora({ subsets: ["latin"], weight: ["400", "500", "600", "700"], va const jbMono = JetBrains_Mono({ subsets: ["latin"], weight: ["400", "500", "700"], variable: "--font-jbmono" }); export const metadata: Metadata = { - title: "Updown — pick up or down", + title: "predict — what happens next?", description: - "Pick up or down. Your stake is the most you can lose. Timed price rounds backed by real Flash Trade V2 positions on Solana mainnet.", + "Pick a side. Cents are the odds. Your stake is the most you can lose. Markets backed by real Flash Trade V2 positions on Solana mainnet.", }; export const viewport: Viewport = { diff --git a/examples/predict/app/markets/page.tsx b/examples/predict/app/markets/page.tsx index 5fa9b4c..cbd9767 100644 --- a/examples/predict/app/markets/page.tsx +++ b/examples/predict/app/markets/page.tsx @@ -1,9 +1,7 @@ -// app/markets/page.tsx — the prediction-market app (phase 5). -// Browse markets wallet-free; connect + enable only when you back one. Real bets -// open a capped-loss Flash V2 perp with a bundled take-profit (the YES win). -// Visit /markets. -import { MarketsApp } from "@/components/markets-app"; +// app/markets/page.tsx — the prediction market moved to the root route (/). +// Redirect any old /markets links there so there is one canonical home. +import { redirect } from "next/navigation"; export default function MarketsPage() { - return ; + redirect("/"); } diff --git a/examples/predict/app/page.tsx b/examples/predict/app/page.tsx index 09e79e2..19d605d 100644 --- a/examples/predict/app/page.tsx +++ b/examples/predict/app/page.tsx @@ -1,12 +1,11 @@ // ───────────────────────────────────────────────────────────────────────────── -// app/page.tsx — single route; hands straight off to the client app. All -// state is browser state by design: wallet, session key, rounds cache. -// THE HARD PART: nothing — the client boundary is components/app.tsx. -// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) +// app/page.tsx — THE app: the prediction market (formerly at /markets). All +// state is browser state by design: wallet, session key, rounds cache. Connect +// any installed wallet; browsing is wallet-free. // ───────────────────────────────────────────────────────────────────────────── -import { App } from "@/components/app"; +import { MarketsApp } from "@/components/markets-app"; export default function Page() { - return ; + return ; } diff --git a/examples/predict/components/active-round.tsx b/examples/predict/components/active-round.tsx deleted file mode 100644 index e4d7325..0000000 --- a/examples/predict/components/active-round.tsx +++ /dev/null @@ -1,88 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/active-round.tsx — a running round: countdown, live PnL, exit. -// THE HARD PART: PnL is computed CLIENT-SIDE at mark price (computePosition- -// View) — the indexer's pnlWithFee is never rendered (GOTCHAS §20). The bar -// depletes on the shared clock; urgency arrives in the final fifth; expiry -// flips copy to "settling at close" — taps land on explained, disabled UI. -// GOTCHAS.md → "The indexer's PnL is NOT the product's PnL" (../../GOTCHAS.md) -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import { useClock } from "@/lib/clock"; -import { fmtCountdown, fmtPrice } from "@/lib/copy"; -import { computePositionView, fmtPnlUsd, fmtUsd } from "@/lib/format"; -import { usePrice } from "@/lib/hooks"; -import { timeframe, type TimeframeId } from "@/lib/payoff"; -import { positionFor, type Round } from "@/lib/rounds"; -import { useStream } from "@/lib/stream"; - -interface Props { - round: Round; - onSettleNow: (round: Round) => void; -} - -export function ActiveRound({ round, onSettleNow }: Props) { - const now = useClock(); - const { price } = usePrice(round.market, 1000); - const { snapshot } = useStream(); - - const metrics = snapshot ? positionFor(snapshot, round.market, round.side) : undefined; - const view = metrics ? computePositionView(metrics, price?.priceUi ?? null) : null; - - const settling = round.status === "settling"; - const timed = round.expiresAt !== null && round.timeframe !== null; - const totalMs = timed ? timeframe(round.timeframe as TimeframeId).ms : 0; - const remaining = timed ? Math.max(0, (round.expiresAt as number) - now) : 0; - const frac = timed && totalMs > 0 ? remaining / totalMs : 0; - const urgent = timed && frac > 0 && frac < 0.2; - const expired = timed && remaining <= 0; - - const pnl = view?.pnlUsd ?? null; - const pnlClass = pnl === null ? "text-dim" : pnl >= 0 ? "text-up" : "text-down"; - - return ( -
-
-
- - {round.side === "LONG" ? "▲" : "▼"} {round.market} - - - {timed ? timeframe(round.timeframe as TimeframeId).label : "manual"} · {fmtUsd(round.stakeUsd)} stake - - - {pnl === null ? "—" : fmtPnlUsd(pnl)} - -
- - {timed && ( -
-
- - {settling ? "settling…" : expired ? "settling at close" : fmtCountdown(remaining)} - - - in at {fmtPrice(round.quote.entryPrice)} · ends early at {fmtPrice(round.quote.liqPrice)} - -
-
-
-
-
- )} - - -
-
- ); -} diff --git a/examples/predict/components/app.tsx b/examples/predict/components/app.tsx deleted file mode 100644 index 44cef28..0000000 --- a/examples/predict/components/app.tsx +++ /dev/null @@ -1,555 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/app.tsx — the orchestrator: providers, session, rounds, and the -// settlement watcher. THE HARD PART: settlement is driven by OBSERVED state -// (snapshot + shared clock), guarded three ways against double-close — -// "settling" status, an in-flight ref, and a retry backoff. The settled PnL -// shown is the same client-side mark-price number the user watched live -// (GOTCHAS §20) — never the indexer's, never a raw-unit guess. -// GOTCHAS.md → "Two chains, one flow" · "The 97% full-close threshold" -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { ConnectionProvider, WalletProvider, useAnchorWallet, useWallet } from "@solana/wallet-adapter-react"; -import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom"; -import { SolflareWalletAdapter } from "@solana/wallet-adapter-solflare"; -import { assertNoErr } from "flash-v2"; - -import { ClockProvider, useClock } from "@/lib/clock"; -import { APP_NAME, TAGLINE, calmError } from "@/lib/copy"; -import { enableOneClickTrading, type EnableState, type EnableWalletCtx } from "@/lib/enable"; -import { flash, baseConnection } from "@/lib/flash"; -import { depositUsdc, executeWithdrawalStep, withdrawUsdc, type FundsStep } from "@/lib/funds"; -import { computePositionView } from "@/lib/format"; -import { useBalances, useBasketBalance, useLatencyLog, useMarketLimits, useMarkets, usePrice, useUsdcMint } from "@/lib/hooks"; -import { lockedQuoteFrom, timeframe, type LockedQuote, type TimeframeId } from "@/lib/payoff"; -import { - adoptOrphan, loadRounds, newRoundId, pendingAcks, positionFor, reconcile, roundStats, saveRounds, withAck, withResult, withStatus, - type Round, type Side, -} from "@/lib/rounds"; -import { loadSession, revokeSession, type LoadedSession } from "@/lib/session"; -import { makeSessionSigner } from "@/lib/signer"; -import { StreamProvider, useStream } from "@/lib/stream"; - -import { ActiveRound } from "./active-round"; -import { EnableSheet } from "./enable-sheet"; -import { FundsSheet } from "./funds-sheet"; -import { History } from "./history"; -import { MechanicsDisclosure } from "./mechanics-disclosure"; -import { PriceChart } from "./price-chart"; -import { SettleCard } from "./settle-card"; -import { StatsStrip } from "./stats-strip"; -import { Ticket, type TicketPhase } from "./ticket"; -import { WalletBar } from "./wallet-bar"; - -const DISCLOSURE_ACK_KEY = "predict-disclosure-ack"; -const SETTLE_RETRY_MS = 10_000; - -/** The ambient radial's tint — the one sanctioned document mutation. */ -const setMood = (mood: "up" | "down") => { - document.body.dataset.mood = mood; -}; - -export function App() { - const wallets = useMemo(() => [new PhantomWalletAdapter(), new SolflareWalletAdapter()], []); - return ( - - - - - - - - ); -} - -function Shell() { - const { publicKey } = useWallet(); - const owner = publicKey ? publicKey.toBase58() : null; - return ( - - - - ); -} - -function Inner({ owner }: { owner: string | null }) { - const walletCtx = useWallet(); - const anchorWallet = useAnchorWallet(); - const { snapshot, loaded } = useStream(); - const now = useClock(); - const latency = useLatencyLog(); - - // ── market + ticket state ────────────────────────────────────────────────── - const markets = useMarkets(); - const [market, setMarket] = useState("SOL"); - const [side, setSide] = useState("LONG"); - const [timeframeId, setTimeframeId] = useState("5m"); - const [stake, setStake] = useState("11"); - const { price } = usePrice(market, 1000); - const limits = useMarketLimits(market); - - const profileLev = timeframe(timeframeId).leverage; - const leverage = useMemo(() => { - if (!limits) return profileLev; - const max = limits.maxLeverage > 0 ? limits.maxLeverage : profileLev; - const min = limits.minLeverage > 0 ? limits.minLeverage : 1; - return Math.max(min, Math.min(profileLev, max)); - }, [limits, profileLev]); - - // ── balances ─────────────────────────────────────────────────────────────── - const usdcMint = useUsdcMint(); - const balances = useBalances(owner, usdcMint); - const basket = useBasketBalance(owner, snapshot?.basketPubkey ?? null, usdcMint); - const inBasketUsd = basket.bal?.inBasketUsd ?? null; - - // ── session ──────────────────────────────────────────────────────────────── - const [session, setSession] = useState(null); - useEffect(() => { - setSession(owner ? loadSession(owner) : null); - }, [owner]); - const signer = useMemo( - () => (session && anchorWallet ? makeSessionSigner(anchorWallet, session, flash.network) : null), - [session, anchorWallet], - ); - - // ── rounds (chain is truth; this is the cache) ───────────────────────────── - const [rounds, setRounds] = useState([]); - const prevSaved = useRef(null); - useEffect(() => { - const loadedRounds = owner ? loadRounds(owner) : []; - setRounds(loadedRounds); - prevSaved.current = loadedRounds; - }, [owner]); - useEffect(() => { - if (!owner) return; - if (prevSaved.current !== rounds) { - saveRounds(owner, rounds); - prevSaved.current = rounds; - } - }, [owner, rounds]); - - // ── toasts (calm copy only) ──────────────────────────────────────────────── - const [toast, setToast] = useState(null); - const toastTimer = useRef | null>(null); - const say = useCallback((msg: string) => { - setToast(msg); - if (toastTimer.current) clearTimeout(toastTimer.current); - toastTimer.current = setTimeout(() => setToast(null), 6000); - }, []); - - // ── settlement engine ────────────────────────────────────────────────────── - const inflight = useRef>(new Set()); - const retryAt = useRef>(new Map()); - - const settleRound = useCallback( - async (round: Round) => { - if (inflight.current.has(round.id)) return; - if (!signer) { - say("Your session key needs a refresh — re-enable one-tap rounds to settle."); - return; - } - const due = retryAt.current.get(round.id) ?? 0; - if (Date.now() < due) return; - inflight.current.add(round.id); - setRounds((rs) => withStatus(rs, round.id, "settling")); - try { - // The PnL the user watched is the PnL they settle with (GOTCHAS §20) — - // priced at the round's OWN market, fetched at settle time. - const metrics = snapshot ? positionFor(snapshot, round.market, round.side) : undefined; - const mark = await flash.price(round.market).then((p) => p.priceUi).catch(() => null); - const view = metrics ? computePositionView(metrics, mark ?? Number(metrics.entryPriceUi)) : null; - - const res = await flash.closePosition({ - marketSymbol: round.market, - side: round.side, - inputUsdUi: "0", // explicit FULL close — the 97% trap never applies - withdrawTokenSymbol: "USDC", - owner: signer.owner, - slippagePercentage: "1", - ...signer.tradeFields, - }); - assertNoErr("close-position", res); - if (!res.transactionBase64) throw new Error(res.err ?? "no transaction returned"); - const sent = await signer.sendTrade(res.transactionBase64); - latency.add({ action: `settle ${round.market}`, chain: "er", ms: sent.confirmMs, sendMs: sent.sendMs, signature: sent.signature }); - - const pnlUsd = view?.pnlUsd ?? 0; - setRounds((rs) => - withResult(rs, round.id, { pnlUsd, won: pnlUsd > 0, settledAt: Date.now(), signature: sent.signature }), - ); - setMood(pnlUsd > 0 ? "up" : "down"); - void basket.refresh(); - } catch (e) { - console.error("[settle]", e); - retryAt.current.set(round.id, Date.now() + SETTLE_RETRY_MS); - const gone = /Position is empty/i.test(e instanceof Error ? e.message : String(e)); - setRounds((rs) => - gone - ? rs.map((r) => (r.id === round.id ? { ...r, status: "closed-elsewhere" as const } : r)) - : withStatus(rs, round.id, "active"), - ); - say(calmError(e)); - } finally { - inflight.current.delete(round.id); - } - }, - [signer, snapshot, latency, basket, say], - ); - - // The watcher: observed state in, settle dispatches out. - const recon = useMemo(() => reconcile(rounds, snapshot, now), [rounds, snapshot, now]); - useEffect(() => { - if (!loaded) return; - if (recon.closedElsewhere.length) { - setRounds((rs) => - rs.map((r) => (recon.closedElsewhere.some((c) => c.id === r.id) ? { ...r, status: "closed-elsewhere" as const } : r)), - ); - } - for (const r of recon.toSettle) void settleRound(r); - }, [recon, loaded, settleRound]); - - // ── commit flow: review (one quote) → confirm (sign those numbers) ───────── - const [phase, setPhase] = useState("idle"); - const [review, setReview] = useState(null); - const [ticketError, setTicketError] = useState(null); - - const doReview = useCallback(async () => { - setTicketError(null); - setPhase("quoting"); - try { - const stakeNum = Number(stake); - const res = await flash.openPosition({ - inputTokenSymbol: "USDC", - outputTokenSymbol: market, - inputAmountUi: stake, - leverage, - tradeType: side, - slippagePercentage: "1", - }); - assertNoErr("open-position quote", res); - setReview(lockedQuoteFrom(res, stakeNum, leverage, side)); - setPhase("review"); - } catch (e) { - console.error("[quote]", e); - setTicketError(calmError(e)); - setPhase("idle"); - } - }, [stake, market, leverage, side]); - - const doConfirm = useCallback(async () => { - if (!signer) return; - setPhase("signing"); - setTicketError(null); - try { - const stakeNum = Number(stake); - const res = await flash.openPosition({ - inputTokenSymbol: "USDC", - outputTokenSymbol: market, - inputAmountUi: stake, - leverage, - tradeType: side, - slippagePercentage: "1", - owner: signer.owner, - ...signer.tradeFields, - }); - assertNoErr("open-position", res); - if (!res.transactionBase64) throw new Error(res.err ?? "no transaction returned"); - const sent = await signer.sendTrade(res.transactionBase64); - latency.add({ action: `${side} ${leverage}× ${market}`, chain: "er", ms: sent.confirmMs, sendMs: sent.sendMs, signature: sent.signature }); - - const tf = timeframe(timeframeId); - const round: Round = { - id: newRoundId(), - market, - side, - stakeUsd: stakeNum, - leverage, - timeframe: timeframeId, - placedAt: Date.now(), - expiresAt: Date.now() + tf.ms, - // Authoritative numbers: the SIGNED response, not the preview. - quote: lockedQuoteFrom(res, stakeNum, leverage, side), - status: "active", - }; - setRounds((rs) => [...rs, round]); - setMood(side === "LONG" ? "up" : "down"); - setPhase("idle"); - setReview(null); - void basket.refresh(); - } catch (e) { - console.error("[open]", e); - setTicketError(calmError(e)); - setPhase("review"); - } - }, [signer, stake, market, leverage, side, timeframeId, latency, basket]); - - // ── enable / revoke ──────────────────────────────────────────────────────── - const [enableOpen, setEnableOpen] = useState(false); - const [enableState, setEnableState] = useState(null); - const [busy, setBusy] = useState(false); - - const doEnable = useCallback(async () => { - if (!walletCtx.publicKey || !walletCtx.signTransaction || !anchorWallet) return; - setBusy(true); - try { - const ctx: EnableWalletCtx = { - publicKey: walletCtx.publicKey, - signTransaction: walletCtx.signTransaction, - signAllTransactions: walletCtx.signAllTransactions, - }; - const result = await enableOneClickTrading({ - wallet: ctx, - anchorWallet, - snapshot, - usdcMint, - balances: { sol: balances.sol, usdc: balances.usdc }, - onStep: setEnableState, - onLog: latency.add, - }); - if (result.session) setSession(result.session); - if (result.ok) { - setEnableOpen(false); - if (result.needsUsdc) setFundsOpen(true); - } - } catch (e) { - say(calmError(e)); - } finally { - setBusy(false); - } - }, [walletCtx, anchorWallet, snapshot, usdcMint, balances.sol, balances.usdc, latency, say]); - - const doRevoke = useCallback(async () => { - if (!session) return; - setBusy(true); - try { - await revokeSession(session, baseConnection); - setSession(null); - say("Session revoked — rent returned to your wallet."); - } catch (e) { - say(calmError(e)); - } finally { - setBusy(false); - } - }, [session, say]); - - // ── funds ────────────────────────────────────────────────────────────────── - const [fundsOpen, setFundsOpen] = useState(false); - const [fundsStep, setFundsStep] = useState(null); - const [executePending, setExecutePending] = useState(false); - - const fundsWallet = useMemo(() => { - if (!walletCtx.publicKey || !walletCtx.signTransaction) return null; - return { - publicKey: walletCtx.publicKey, - signTransaction: walletCtx.signTransaction, - signAllTransactions: walletCtx.signAllTransactions, - }; - }, [walletCtx.publicKey, walletCtx.signTransaction, walletCtx.signAllTransactions]); - - const doDeposit = useCallback( - async (amount: string) => { - const w = fundsWallet; - if (!w || !usdcMint) return; - setBusy(true); - try { - const r = await depositUsdc({ wallet: w, usdcMint, amount, onStep: setFundsStep, onLog: latency.add }); - if (!r.ok && r.error) say(r.error); - await Promise.all([balances.refresh(), basket.refresh()]); - } finally { - setBusy(false); - } - }, - [fundsWallet, usdcMint, latency, balances, basket, say], - ); - - const doWithdraw = useCallback( - async (amount: string) => { - const w = fundsWallet; - if (!w || !usdcMint) return; - setBusy(true); - try { - const r = await withdrawUsdc({ wallet: w, usdcMint, amount, onStep: setFundsStep, onLog: latency.add }); - setExecutePending(Boolean(r.executePending)); - if (!r.ok && r.error) say(r.error); - await Promise.all([balances.refresh(), basket.refresh()]); - } finally { - setBusy(false); - } - }, - [fundsWallet, usdcMint, latency, balances, basket, say], - ); - - const doExecuteWithdraw = useCallback(async () => { - const w = fundsWallet; - if (!w || !usdcMint) return; - setBusy(true); - try { - const r = await executeWithdrawalStep({ wallet: w, usdcMint, onStep: setFundsStep, onLog: latency.add }); - setExecutePending(Boolean(r.executePending)); - if (!r.ok && r.error) say(r.error); - await Promise.all([balances.refresh(), basket.refresh()]); - } finally { - setBusy(false); - } - }, [fundsWallet, usdcMint, latency, balances, basket, say]); - - // ── disclosure gate ──────────────────────────────────────────────────────── - const [disclosureAcked, setDisclosureAcked] = useState(true); // assume acked until mount check - const [disclosureOpen, setDisclosureOpen] = useState(false); - useEffect(() => { - const acked = typeof window !== "undefined" && window.localStorage.getItem(DISCLOSURE_ACK_KEY) === "1"; - setDisclosureAcked(acked); - setDisclosureOpen(!acked); - }, []); - const ackDisclosure = useCallback(() => { - window.localStorage.setItem(DISCLOSURE_ACK_KEY, "1"); - setDisclosureAcked(true); - setDisclosureOpen(false); - }, []); - - // ── derived view state ───────────────────────────────────────────────────── - const active = rounds.filter((r) => r.status === "active" || r.status === "settling"); - const acks = pendingAcks(rounds); - const stats = roundStats(rounds); - const chartRound = active.find((r) => r.market === market); - const chartEntry = chartRound ? { price: chartRound.quote.entryPrice, side: chartRound.side } : null; - const sessionActive = Boolean(session && signer); - - const blockedReason = !owner - ? "Connect a wallet to play." - : !sessionActive - ? "Enable one-tap rounds to play — one approval, no funds move." - : inBasketUsd !== null && inBasketUsd < Number(stake || 0) - ? "Deposit USDC to your basket first." - : null; - - return ( -
- setFundsOpen(true)} - onEnable={() => setEnableOpen(true)} - onRevoke={doRevoke} - onShowDisclosure={() => setDisclosureOpen(true)} - /> - -
- {/* ticket column */} -
-

- Call the next move. -

-

{TAGLINE}

- { - setSide(s); - setMood(s === "LONG" ? "up" : "down"); - }} - timeframeId={timeframeId} - onTimeframe={setTimeframeId} - stake={stake} - onStake={setStake} - maxStake={inBasketUsd} - leverage={leverage} - price={price} - blockedReason={blockedReason} - phase={phase} - review={review} - error={ticketError} - onReview={doReview} - onConfirm={doConfirm} - onCancelReview={() => { - setPhase("idle"); - setReview(null); - }} - /> -
- - {/* live column */} -
- - - - {recon.orphans.length > 0 && ( -
-

- {recon.orphans.length === 1 ? "A position" : `${recon.orphans.length} positions`} on this wallet{" "} - {recon.orphans.length === 1 ? "isn't" : "aren't"} tracked here (opened elsewhere or cache cleared). -

-
- {recon.orphans.map((o) => ( - - ))} -
-
- )} - - {active.length > 0 ? ( -
-

- Live rounds -

- {active.map((r) => ( - - ))} -
- ) : ( -
- No live rounds. Your next call starts on the left. -
- )} - - -
-
- -
-

- {APP_NAME} is a starter built on Flash Trade V2 — every round is a real position on Solana mainnet, with real money. - Most people who play short-term price games lose money. Never stake more than you can afford to lose.{" "} - -

-
- - {/* overlays */} - {acks.length > 0 && setRounds((rs) => withAck(rs, r.id))} />} - - void doEnable()} onClose={() => setEnableOpen(false)} /> - void doDeposit(a)} - onWithdraw={(a) => void doWithdraw(a)} - onExecuteWithdraw={() => void doExecuteWithdraw()} - onClose={() => setFundsOpen(false)} - /> - - {toast && ( -
-
{toast}
-
- )} -
- ); -} diff --git a/examples/predict/components/enable-sheet.tsx b/examples/predict/components/enable-sheet.tsx deleted file mode 100644 index 3e43515..0000000 --- a/examples/predict/components/enable-sheet.tsx +++ /dev/null @@ -1,85 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/enable-sheet.tsx — Enable One-Tap: consent screen + live steps. -// THE HARD PART: the consent rule. Enable is account setup ONLY — the single -// disclosed transfer is the 0.01 SOL rent top-up to the user's OWN session -// key, stated here at the moment of consent, returned on revoke. -// GOTCHAS.md → "Funds move on consent only" (../../GOTCHAS.md) -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import type { EnableState } from "@/lib/enable"; -import { ENABLE_EXPLAINER } from "@/lib/copy"; -import { useSheetMount } from "@/lib/use-sheet-mount"; - -interface Props { - open: boolean; - busy: boolean; - state: EnableState | null; - onEnable: () => void; - onClose: () => void; -} - -const dot: Record = { - idle: "bg-edge2", - active: "bg-warn soft-pulse", - done: "bg-up", - skipped: "bg-faint", - error: "bg-down", -}; - -export function EnableSheet({ open, busy, state, onEnable, onClose }: Props) { - const { mounted, closing } = useSheetMount(open); - if (!mounted) return null; - return ( -
-
-
-
-
-

{ENABLE_EXPLAINER.title}

-

{ENABLE_EXPLAINER.body}

- -
    - {(state?.steps ?? ENABLE_EXPLAINER.steps.map((label, i) => ({ id: String(i), label, status: "idle" as const, note: undefined }))).map( - (s) => ( -
  • - - {s.label} - {s.note ? {s.note} : null} -
  • - ), - )} -
- - {state?.fundingHint ?

{state.fundingHint}

: null} - {state?.error ?

{state.error}

: null} - -
- - -
-
-
-
-
- ); -} diff --git a/examples/predict/components/history.tsx b/examples/predict/components/history.tsx deleted file mode 100644 index 08d21a2..0000000 --- a/examples/predict/components/history.tsx +++ /dev/null @@ -1,46 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/history.tsx — settled rounds, newest first, each with receipt. -// THE HARD PART: nothing clever — terse rows, tabular numbers, capped at 50. -// "closed-elsewhere" rows stay honest about what we don't know. -// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import { explorerTxUrl } from "@/lib/copy"; -import { fmtPnlUsd, fmtUsd } from "@/lib/format"; -import type { Round } from "@/lib/rounds"; - -export function History({ rounds }: { rounds: Round[] }) { - const done = rounds - .filter((r) => r.status === "settled" || r.status === "closed-elsewhere") - .sort((a, b) => (b.result?.settledAt ?? b.placedAt) - (a.result?.settledAt ?? a.placedAt)) - .slice(0, 50); - - if (done.length === 0) return null; - - return ( -
-

History

-
-
- ); -} diff --git a/examples/predict/components/market-picker.tsx b/examples/predict/components/market-picker.tsx deleted file mode 100644 index 88bc52a..0000000 --- a/examples/predict/components/market-picker.tsx +++ /dev/null @@ -1,207 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/market-picker.tsx — pick what you're calling. A glass button -// (icon · symbol · live price) opens a searchable sheet: 45+ markets need -// search, not a scroll row. THE HARD PART: category pills are derived from -// Pyth ticker prefixes, which is best-effort taxonomy — if less than 70% of -// tokens classify cleanly, the pills hide themselves (council: never ship a -// silently-wrong filter). Prices in the grid come from ONE bulk prices() -// call on open. GOTCHAS.md → "Deposits take a MINT, trading takes SYMBOLS" -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import { useEffect, useMemo, useRef, useState } from "react"; -import type { PriceInfo, TokenInfo } from "flash-v2"; -import { fmtPrice } from "@/lib/copy"; -import { flash } from "@/lib/flash"; -import { useSheetMount } from "@/lib/use-sheet-mount"; -import { TokenIcon } from "./token-icon"; - -// Module-level caches: the token list is static per session, and the grid's -// price hints don't need to be fresher than a few seconds across re-opens. -let tokensOnce: Promise | null = null; -const cachedTokens = () => (tokensOnce ??= flash.tokens().catch((e) => ((tokensOnce = null), Promise.reject(e)))); -let pricesAt = 0; -let pricesCache: Record = {}; -const PRICES_TTL_MS = 10_000; -async function cachedPrices(): Promise> { - if (Date.now() - pricesAt < PRICES_TTL_MS) return pricesCache; - pricesCache = await flash.prices(); - pricesAt = Date.now(); - return pricesCache; -} - -interface Props { - markets: string[] | null; - market: string; - price: PriceInfo | null; - disabled?: boolean; - onSelect: (symbol: string) => void; -} - -const CATEGORY_LABELS: Record = { - Crypto: "Crypto", - Equity: "Stocks", - FX: "FX", - Metal: "Metals", - Commodity: "Commodities", - Commodities: "Commodities", - Energy: "Commodities", - Rates: "Rates", -}; - -function categoryOf(pythTicker: string | null | undefined): string | null { - if (!pythTicker) return null; - const prefix = pythTicker.split(".")[0] ?? ""; - return CATEGORY_LABELS[prefix] ?? null; -} - -export function MarketPicker({ markets, market, price, disabled, onSelect }: Props) { - const [open, setOpen] = useState(false); - const { mounted, closing } = useSheetMount(open); - const [query, setQuery] = useState(""); - const [category, setCategory] = useState("All"); - const [categories, setCategories] = useState>(new Map()); // symbol → category - const [pills, setPills] = useState([]); - const [gridPrices, setGridPrices] = useState>({}); - const searchRef = useRef(null); - - // Best-effort taxonomy from token metadata; hide pills unless ≥70% classify. - useEffect(() => { - let alive = true; - cachedTokens() - .then((tokens) => { - if (!alive) return; - const map = new Map(); - let classified = 0; - const tradeable = tokens.filter((t) => !t.isStable); - for (const t of tradeable) { - const cat = categoryOf(t.pythTicker); - if (cat) { - map.set(t.symbol, cat); - classified++; - } - } - setCategories(map); - if (tradeable.length > 0 && classified / tradeable.length >= 0.7) { - setPills(["All", ...Array.from(new Set(map.values())).sort()]); - } else { - setPills([]); - } - }) - .catch(() => setPills([])); - return () => { - alive = false; - }; - }, []); - - // One bulk price snapshot when the sheet opens (grid hints, not live ticks). - useEffect(() => { - if (!open) return; - searchRef.current?.focus(); - cachedPrices() - .then(setGridPrices) - .catch(() => {}); - }, [open]); - - const list = useMemo(() => { - const all = markets ?? []; - const q = query.trim().toUpperCase(); - return all.filter((s) => { - if (q && !s.toUpperCase().includes(q)) return false; - if (category !== "All" && pills.length > 0 && categories.get(s) !== category) return false; - return true; - }); - }, [markets, query, category, categories, pills.length]); - - const pick = (s: string) => { - onSelect(s); - setOpen(false); - setQuery(""); - setCategory("All"); - }; - - return ( - <> - - - {mounted && ( -
-
setOpen(false)} - /> -
-
- setQuery(e.target.value)} - placeholder="Search markets…" - aria-label="Search markets" - className="film w-full px-4 py-3 font-mono text-sm text-ink placeholder:text-faint focus:outline-none" - /> - - {pills.length > 0 && ( -
- {pills.map((p) => ( - - ))} -
- )} - -
- {(markets === null ? [] : list).map((s) => { - const p = gridPrices[s]; - return ( - - ); - })} - {markets === null && loading markets…} - {markets !== null && list.length === 0 && ( - nothing matches “{query}” - )} -
-
-
-
- )} - - ); -} diff --git a/examples/predict/components/markets-app.tsx b/examples/predict/components/markets-app.tsx index 16a0020..72e9eda 100644 --- a/examples/predict/components/markets-app.tsx +++ b/examples/predict/components/markets-app.tsx @@ -1,13 +1,12 @@ // ───────────────────────────────────────────────────────────────────────────── -// components/markets-app.tsx — the prediction-market app shell at /markets. -// Reuses the SAME proven wiring as the Updown orchestrator (providers → owner -// stream → session → signer → enable) so a connected + enabled user places REAL -// bets through MarketDetail. Browsing stays wallet-free; the wallet is asked for -// only at "back this". Deposits live on the main app for now (phase 5b: bring -// the funds sheet here + a portfolio of open bets). +// components/markets-app.tsx — THE app shell, at the root route /. Standalone: +// providers → owner stream → session → signer → enable → bet → settle, plus its +// OWN deposit/withdraw (FundsSheet) and an ANY-wallet picker (no wallet is +// hardcoded). Browsing is wallet-free; the wallet is asked for only at "back +// this". (The old Updown app + the cross-app shared-position hazard are gone.) // -// ⚠️ Real-funds path — built + compiles, but NOT yet through the mandatory -// adversarial review (subagents rate-limited). Don't trade real funds until it is. +// ⚠️ Real-funds path on Solana mainnet. The disclosure stays until the F7 guard + +// scoring residuals are signed off (SPEC-PREDICT-V2 → v2.1 REVIEW OUTCOME). // ───────────────────────────────────────────────────────────────────────────── "use client"; @@ -17,6 +16,7 @@ import { ConnectionProvider, WalletProvider, useAnchorWallet, useWallet } from " import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom"; import { SolflareWalletAdapter } from "@solana/wallet-adapter-solflare"; import { enableOneClickTrading, type EnableState, type EnableWalletCtx } from "@/lib/enable"; +import { depositUsdc, executeWithdrawalStep, withdrawUsdc, type FundsStep } from "@/lib/funds"; import { flash } from "@/lib/flash"; import { calmError } from "@/lib/copy"; import { shortKey } from "@/lib/format"; @@ -27,6 +27,8 @@ import { StreamProvider, useStream } from "@/lib/stream"; import { useMarketRounds } from "@/lib/use-market-rounds"; import { Bets } from "./bets"; import { Discover } from "./discover"; +import { FundsSheet } from "./funds-sheet"; +import { WalletPicker } from "./wallet-picker"; export function MarketsApp() { const wallets = useMemo(() => [new PhantomWalletAdapter(), new SolflareWalletAdapter()], []); @@ -80,10 +82,66 @@ function Inner({ owner }: { owner: string | null }) { const [enabling, setEnabling] = useState(false); const [note, setNote] = useState(null); + const [pickerOpen, setPickerOpen] = useState(false); - const connect = useCallback(() => { - walletCtx.select?.(walletCtx.wallets[0]?.adapter.name ?? null); - }, [walletCtx]); + // Open the wallet picker — the user chooses ANY installed wallet (no wallet is + // hardcoded; selection happens in WalletPicker via Wallet Standard). + const connect = useCallback(() => setPickerOpen(true), []); + const disconnect = useCallback(() => { void walletCtx.disconnect(); }, [walletCtx]); + + // ── funds: deposit/withdraw USDC ↔ basket (ported so /markets is standalone). + // Withdraw is two-phase by design (request → execute, ~30–90s). + const [fundsOpen, setFundsOpen] = useState(false); + const [fundsStep, setFundsStep] = useState(null); + const [executePending, setExecutePending] = useState(false); + const [fundsBusy, setFundsBusy] = useState(false); + + const fundsWallet = useMemo(() => { + if (!walletCtx.publicKey || !walletCtx.signTransaction) return null; + return { + publicKey: walletCtx.publicKey, + signTransaction: walletCtx.signTransaction, + signAllTransactions: walletCtx.signAllTransactions, + }; + }, [walletCtx.publicKey, walletCtx.signTransaction, walletCtx.signAllTransactions]); + + const doDeposit = useCallback(async (amount: string) => { + if (!fundsWallet || !usdcMint) return; + setFundsBusy(true); + try { + const r = await depositUsdc({ wallet: fundsWallet, usdcMint, amount, onStep: setFundsStep, onLog: () => {} }); + if (!r.ok && r.error) setNote(r.error); + await Promise.all([balances.refresh(), basket.refresh()]); + } finally { + setFundsBusy(false); + } + }, [fundsWallet, usdcMint, balances, basket]); + + const doWithdraw = useCallback(async (amount: string) => { + if (!fundsWallet || !usdcMint) return; + setFundsBusy(true); + try { + const r = await withdrawUsdc({ wallet: fundsWallet, usdcMint, amount, onStep: setFundsStep, onLog: () => {} }); + setExecutePending(Boolean(r.executePending)); + if (!r.ok && r.error) setNote(r.error); + await Promise.all([balances.refresh(), basket.refresh()]); + } finally { + setFundsBusy(false); + } + }, [fundsWallet, usdcMint, balances, basket]); + + const doExecuteWithdraw = useCallback(async () => { + if (!fundsWallet || !usdcMint) return; + setFundsBusy(true); + try { + const r = await executeWithdrawalStep({ wallet: fundsWallet, usdcMint, onStep: setFundsStep, onLog: () => {} }); + setExecutePending(Boolean(r.executePending)); + if (!r.ok && r.error) setNote(r.error); + await Promise.all([balances.refresh(), basket.refresh()]); + } finally { + setFundsBusy(false); + } + }, [fundsWallet, usdcMint, balances, basket]); const enable = useCallback(async () => { if (!walletCtx.publicKey || !walletCtx.signTransaction || !anchorWallet) { @@ -115,11 +173,11 @@ function Inner({ owner }: { owner: string | null }) { if (!result.ok) { setNote( result.needsUsdc - ? "Account ready — deposit USDC on the main app, then bet." + ? "Account ready — tap your balance above to deposit USDC, then bet." : step.last?.error ?? "Couldn't finish enabling — check your wallet and try again.", ); } else if (result.needsUsdc) { - setNote("Deposit USDC on the main app to start betting."); + setNote("Tap your balance above to deposit USDC and start betting."); } void balances.refresh(); void basket.refresh(); @@ -145,7 +203,13 @@ function Inner({ owner }: { owner: string | null }) { {owner ? (
{canBet ? ( - ${(inBasketUsd ?? 0).toFixed(2)} + ) : ( )} - {shortKey(owner)} +
) : ( -
-
-
- ); -} diff --git a/examples/predict/components/price-chart.tsx b/examples/predict/components/price-chart.tsx deleted file mode 100644 index 46891ac..0000000 --- a/examples/predict/components/price-chart.tsx +++ /dev/null @@ -1,120 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/price-chart.tsx — the live pulse: where the price has been and -// where it is right now. THE HARD PART: zero extra polling — it feeds off the -// app's existing 1s price tick (usePriceHistory seeds with Pyth minute closes -// and appends live ticks). Fixed height, tabular digits, entry-line overlay -// when you have a round riding this market. Trend tints teal/coral. -// GOTCHAS.md → "Latency: never bill the rollup for the user's geography" -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import { useMemo } from "react"; -import type { PriceInfo } from "flash-v2"; -import { fmtPrice } from "@/lib/copy"; -import { usePriceHistory } from "@/lib/use-price-history"; -import type { Side } from "@/lib/rounds"; - -interface Props { - symbol: string; - price: PriceInfo | null; - /** An active round on this market — draws your entry on the chart. */ - entry?: { price: number; side: Side } | null; -} - -const W = 100; -const H = 36; - -export function PriceChart({ symbol, price, entry }: Props) { - const history = usePriceHistory(price, symbol); - - const view = useMemo(() => { - if (history.length < 2) return null; - const lo = Math.min(...history); - const hi = Math.max(...history); - const pad = (hi - lo) * 0.12 || hi * 0.0005 || 1; - const min = lo - pad; - const max = hi + pad; - const xOf = (i: number) => (i / (history.length - 1)) * W; - const yOf = (p: number) => H - ((p - min) / (max - min)) * H; - - const pts = history.map((p, i) => `${xOf(i).toFixed(2)},${yOf(p).toFixed(2)}`); - const first = history[0] as number; - const last = history[history.length - 1] as number; - const changePct = first > 0 ? ((last - first) / first) * 100 : 0; - const lastX = xOf(history.length - 1); - const lastY = yOf(last); - const entryY = entry && entry.price >= min && entry.price <= max ? yOf(entry.price) : null; - - return { - line: pts.join(" "), - area: `0,${H} ${pts.join(" ")} ${W},${H}`, - up: changePct >= 0, - changePct, - lo, - hi, - lastX, - lastY, - entryY, - }; - }, [history, entry]); - - const tone = view?.up ? "var(--color-up)" : "var(--color-down)"; - - return ( -
-
-
- {symbol} - live -
- {view && ( - - {view.up ? "▲" : "▼"} {Math.abs(view.changePct).toFixed(2)}% - - )} -
- - {/* the number — on its own film so it never fights the frost */} -
- - {price ? fmtPrice(price.priceUi) : "—"} - -
- -
- {view ? ( - <> - - - - - - - - - - {view.entryY !== null && ( - - )} - - - {view.entryY !== null && ( - - your entry - - )} - {fmtPrice(view.hi, "compact")} - {fmtPrice(view.lo, "compact")} - - ) : ( -
- drawing the last hour… -
- )} -
-
- ); -} diff --git a/examples/predict/components/settle-card.tsx b/examples/predict/components/settle-card.tsx deleted file mode 100644 index ab8f373..0000000 --- a/examples/predict/components/settle-card.tsx +++ /dev/null @@ -1,76 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/settle-card.tsx — the trust crucible: a settled round's result. -// THE HARD PART: the stake must never look stolen — a settling round shows -// custody ("being settled now"), the result lands with its receipt link, and -// acknowledgment gates the next round. Win is celebratory; loss is calm and -// exact ("your full stake, nothing more"). -// GOTCHAS.md → "err arrives inside HTTP 200" (../../GOTCHAS.md) -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import { explorerTxUrl } from "@/lib/copy"; -import { fmtPnlUsd, fmtUsd } from "@/lib/format"; -import type { Round } from "@/lib/rounds"; - -interface Props { - round: Round; - onAck: (round: Round) => void; -} - -export function SettleCard({ round, onAck }: Props) { - const r = round.result; - const elsewhere = round.status === "closed-elsewhere"; - const won = r?.won ?? false; - - return ( -
-
-
-

- {round.market} {round.side === "LONG" ? "▲ up" : "▼ down"} · {fmtUsd(round.stakeUsd)} stake -

- - {elsewhere ? ( - <> -

SETTLED ELSEWHERE

-

- This position was closed outside Updown — another device, the Flash app, or a knockout. Your balance reflects the on-chain outcome. -

- - ) : ( - <> -

- {won ? "CALLED IT" : "NOT THIS TIME"} -

-

{r ? fmtPnlUsd(r.pnlUsd) : ""}

-

- {won - ? "Settled to your basket. Withdraw any time." - : `That's your full stake — nothing more. The cap is the feature.`} -

- - )} - - {r?.signature && ( - - receipt ↗ - - )} - - -
-
-
- ); -} diff --git a/examples/predict/components/stats-strip.tsx b/examples/predict/components/stats-strip.tsx deleted file mode 100644 index fd4b194..0000000 --- a/examples/predict/components/stats-strip.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/stats-strip.tsx — your record: rounds, win rate, net PnL, streak. -// THE HARD PART: numbers from settled rounds only — live PnL never leaks in -// here (it belongs to the active round card). Tabular mono keeps it still. -// GOTCHAS.md → (no API gotchas here) (../../GOTCHAS.md) -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import { fmtPnlUsd } from "@/lib/format"; -import type { RoundStats } from "@/lib/rounds"; - -export function StatsStrip({ stats }: { stats: RoundStats }) { - if (stats.played === 0) return null; - const pnlClass = stats.totalPnlUsd > 0 ? "text-up" : stats.totalPnlUsd < 0 ? "text-down" : "text-dim"; - const streakLabel = - stats.streak > 1 ? `${stats.streak}× win streak` : stats.streak < -1 ? `${-stats.streak}× loss streak` : null; - return ( -
- - {stats.wins}W · {stats.losses}L - - {stats.winRatePct.toFixed(0)}% win rate - {fmtPnlUsd(stats.totalPnlUsd)} all-time - {streakLabel && ( - 0 ? "border-up/30 text-up" : "border-down/30 text-down"}`}> - {streakLabel} - - )} -
- ); -} diff --git a/examples/predict/components/ticket.tsx b/examples/predict/components/ticket.tsx deleted file mode 100644 index af39678..0000000 --- a/examples/predict/components/ticket.tsx +++ /dev/null @@ -1,253 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/ticket.tsx — the commit surface, v2: glass, plain words, zero -// trading vocabulary. Market via picker sheet (no scroll row); stake is a -// labeled glass field + balance slider + "use max" (no amount chips); facts -// speak in the user's dollars; ONE real quote at Review locks the numbers -// before anything signs. $11 floor enforced (the chain's, not ours). -// GOTCHAS.md → "The $11 rule" · "owner is optional exactly once" (../../GOTCHAS.md) -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import { useMemo } from "react"; -import type { PriceInfo } from "flash-v2"; -import { RECOMMENDED_MIN_COLLATERAL_USD } from "flash-v2"; -import { fmtPrice } from "@/lib/copy"; -import { fmtUsd } from "@/lib/format"; -import { payoffFacts, TIMEFRAMES, type LockedQuote, type TimeframeId } from "@/lib/payoff"; -import type { Side } from "@/lib/rounds"; -import { MarketPicker } from "./market-picker"; - -const MIN_STAKE = RECOMMENDED_MIN_COLLATERAL_USD; // $11 — the chain's floor - -export type TicketPhase = "idle" | "quoting" | "review" | "signing"; - -interface Props { - markets: string[] | null; - market: string; - onMarket: (m: string) => void; - side: Side; - onSide: (s: Side) => void; - timeframeId: TimeframeId; - onTimeframe: (t: TimeframeId) => void; - stake: string; - onStake: (s: string) => void; - maxStake: number | null; - leverage: number; // clamped to market caps by the app - price: PriceInfo | null; - blockedReason: string | null; - phase: TicketPhase; - review: LockedQuote | null; - error: string | null; - onReview: () => void; - onConfirm: () => void; - onCancelReview: () => void; -} - -export function Ticket(props: Props) { - const { - markets, market, onMarket, side, onSide, timeframeId, onTimeframe, stake, onStake, maxStake, - leverage, price, blockedReason, phase, review, error, onReview, onConfirm, onCancelReview, - } = props; - - const stakeNum = Number(stake) || 0; - const facts = useMemo(() => payoffFacts(stakeNum, leverage), [stakeNum, leverage]); - const stakeOk = stakeNum >= MIN_STAKE && (maxStake === null || stakeNum <= maxStake + 1e-9); - const inReview = phase === "review" || phase === "signing"; - const sliderMax = maxStake !== null ? Math.max(MIN_STAKE, Math.floor(maxStake)) : null; - const stakeShown = stakeNum || MIN_STAKE; - - return ( -
- {/* market */} - - - {/* direction — the product's two verbs */} -
- - -
- - {/* how long */} -

How long

-
- {TIMEFRAMES.map((tf) => ( - - ))} -
- - {/* your stake */} -

Your stake

-
-
- $ - onStake(e.target.value.replace(/[^0-9.]/g, ""))} - onFocus={(e) => e.target.select()} - aria-label="Your stake in dollars" - className="w-full bg-transparent font-mono text-[28px] font-medium leading-none text-ink placeholder:text-faint focus:outline-none disabled:opacity-60" - placeholder={String(MIN_STAKE)} - /> - {maxStake !== null && ( - - )} -
- {sliderMax !== null && sliderMax > MIN_STAKE && ( - onStake(e.target.value)} - aria-label="Slide to set your stake" - className="stake-range mt-3 w-full cursor-pointer" - /> - )} -

- {maxStake === null ? `$${MIN_STAKE} minimum` : `Balance ${fmtUsd(maxStake)} · $${MIN_STAKE} minimum`} -

-
- {stakeNum > 0 && stakeNum < MIN_STAKE && ( -

${MIN_STAKE} minimum — below that the blockchain rejects the round.

- )} - {maxStake !== null && stakeNum > maxStake && ( -

That's more than your balance ({fmtUsd(maxStake)}). Deposit first.

- )} - - {/* what can happen — three lines, nothing to decode */} -

What can happen

-
    -
  • - - Moves your way 1% - +${facts.perPctUsd.toFixed(2)} -
  • -
  • - - Moves against you 1% - −${facts.perPctUsd.toFixed(2)} -
  • -
  • - - - {side === "LONG" ? "Falls" : "Jumps"} {facts.knockoutPct.toFixed(0)}% — round ends - - −{fmtUsd(stakeShown)} max -
  • -
- - {/* commit gate */} - {!inReview ? ( - <> - - {blockedReason &&

{blockedReason}

} - - ) : ( -
-

Your numbers — locked, they don't drift

-
-
You get in at
{fmtPrice(review?.entryPrice)}
-
Round ends early at
{fmtPrice(review?.liqPrice)}
-
Backing your call
{fmtUsd(review?.sizeUsd ?? 0)}
-
Each 1% your way
+{fmtUsd(review?.perPctUsd ?? 0)}
-
Fees, in and out (≈)
{fmtUsd((review?.entryFeeUsd ?? 0) * 2)}
-
Cost per hour held
{(review?.marginFeePctHourly ?? 0).toFixed(4)}%
-
- {(() => { - const mark = price?.priceUi ?? null; - const entry = review?.entryPrice ?? null; - const gapPct = mark && entry ? ((entry - mark) / mark) * 100 : 0; - return Math.abs(gapPct) >= 0.25 ? ( -

- Heads up: you'd get in {Math.abs(gapPct).toFixed(1)}% {gapPct > 0 ? "above" : "below"} the live price — that's - the venue's cut of the entry. The round starts that far {gapPct > 0 === (side === "LONG") ? "behind" : "ahead"}. -

- ) : null; - })()} -

- {fmtUsd(stakeNum)} says {market} {side === "LONG" ? "goes up" : "goes down"} in{" "} - {TIMEFRAMES.find((t) => t.id === timeframeId)?.label}. Worst case: −{fmtUsd(stakeNum)}. -

-
- - -
- {blockedReason &&

{blockedReason}

} -
- )} - - {error &&

{error}

} -
- ); -} diff --git a/examples/predict/components/wallet-bar.tsx b/examples/predict/components/wallet-bar.tsx deleted file mode 100644 index 901054e..0000000 --- a/examples/predict/components/wallet-bar.tsx +++ /dev/null @@ -1,139 +0,0 @@ -// ───────────────────────────────────────────────────────────────────────────── -// components/wallet-bar.tsx — wordmark, connect, balances, session controls. -// THE HARD PART: balances must be labeled by WHERE they live (wallet vs -// basket) — an unlabeled number reads as "my deposit" and that's a consent -// bug. Mainnet warning shows at the connect moment, not buried in a footer. -// GOTCHAS.md → "Your balance is the DEPOSIT LEDGER" (../../GOTCHAS.md) -// ───────────────────────────────────────────────────────────────────────────── -"use client"; - -import { useCallback, useEffect, useRef, useState } from "react"; -import { useWallet } from "@solana/wallet-adapter-react"; -import type { WalletName } from "@solana/wallet-adapter-base"; -import { CONNECT_WARNING } from "@/lib/copy"; -import { fmtUsd, shortKey } from "@/lib/format"; - -interface Props { - inBasketUsd: number | null; - walletUsdc: number | null; - sessionActive: boolean; - busy: boolean; - onFunds: () => void; - onEnable: () => void; - onRevoke: () => void; - onShowDisclosure: () => void; -} - -export function WalletBar({ inBasketUsd, walletUsdc, sessionActive, busy, onFunds, onEnable, onRevoke, onShowDisclosure }: Props) { - const { wallets, select, connect, disconnect, connected, connecting, publicKey, wallet } = useWallet(); - const [picking, setPicking] = useState(false); - const wantConnect = useRef(false); - - // select() is async-ish in adapter-land: connect on the effect after the - // wallet object lands, not in the same tick as select(). - useEffect(() => { - if (wallet && wantConnect.current && !connected && !connecting) { - wantConnect.current = false; - connect().catch(() => {}); - } - }, [wallet, connected, connecting, connect]); - - const pick = useCallback( - (name: WalletName) => { - setPicking(false); - wantConnect.current = true; - select(name); - }, - [select], - ); - - return ( -
-
- UP - DOWN -
- - -
- {connected && publicKey ? ( - <> - - {sessionActive ? ( - - ) : ( - - )} - - - ) : ( -
- - {picking && ( -
-

{CONNECT_WARNING}

- {wallets.map((w) => ( - - ))} -
- )} -
- )} -
-
- ); -} diff --git a/examples/predict/components/wallet-picker.tsx b/examples/predict/components/wallet-picker.tsx new file mode 100644 index 0000000..87cc73e --- /dev/null +++ b/examples/predict/components/wallet-picker.tsx @@ -0,0 +1,76 @@ +// ───────────────────────────────────────────────────────────────────────────── +// components/wallet-picker.tsx — connect ANY wallet, not a hardcoded one. Lists +// every wallet the browser exposes (Wallet Standard auto-detects Phantom, +// Solflare, Backpack, Glow, … — nothing here is pinned to a specific wallet) and +// lets the user pick. Selecting hands off to the adapter, which connects. +// ───────────────────────────────────────────────────────────────────────────── + +"use client"; + +import { useMemo } from "react"; +import { useWallet, type Wallet } from "@solana/wallet-adapter-react"; +import { WalletReadyState } from "@solana/wallet-adapter-base"; + +const rank = (w: Wallet): number => + w.readyState === WalletReadyState.Installed ? 2 : w.readyState === WalletReadyState.Loadable ? 1 : 0; + +export function WalletPicker({ onClose }: { onClose: () => void }) { + const { wallets, select } = useWallet(); + + // Installed/loadable first; de-dup by name (explicit adapters + Standard + // detection can surface the same wallet twice). + const shown = useMemo(() => { + const seen = new Set(); + const ranked = [...wallets].sort((a, b) => rank(b) - rank(a)); + const deduped = ranked.filter((w) => { + const n = w.adapter.name; + if (seen.has(n)) return false; + seen.add(n); + return true; + }); + const ready = deduped.filter( + (w) => w.readyState === WalletReadyState.Installed || w.readyState === WalletReadyState.Loadable, + ); + return ready.length > 0 ? ready : deduped; + }, [wallets]); + + return ( +
+ +
+ + {shown.length === 0 ? ( +

+ No Solana wallet detected. Install Phantom, Solflare, Backpack, or another wallet, then try again. +

+ ) : ( + shown.map((w) => ( + + )) + )} +