const { useState, useEffect, useRef, useMemo, useCallback } = React; /* Snipe.Run — views part 4: Market Detail (Polymarket-style + comments), Activity */ /* ============ MARKET DETAIL ============ */ function MarketDetail({ ctx }) { const { marketId, onBackMarket, authed, onRequireConnect, onOpenTrader } = ctx; const real = sniIsReal(); const [tf, setTf] = useState('1W'); // Real: marketId is the market slug (the markets list sets id===slug). Hook // no-ops to null/loading=false when not on the platform so the demo branch runs. const { data: rm, loading: rmLoading } = useMarket(real ? marketId : null); // Demo market — also the fallback while a real market is still loading. const demoM = MARKETS.find((x) => x.id === marketId) || MARKETS[0]; // Map the real market detail into the shape the view renders. YES price comes // from the live book of the first clob token (Polymarket: tokens[0] = YES). const m = useMemo(() => { if (!real || !rm) return demoM; const yesTok = (rm.tokens && rm.tokens[0]) || null; const px = (rm.prices && yesTok && rm.prices[yesTok]) || null; // mid of bid/ask, fall back to bid, then 50%. let yes = 50; if (px) { const bid = +px.bid || 0, ask = +px.ask || 0; const mid = (bid && ask) ? (bid + ask) / 2 : (bid || ask); if (mid) yes = Math.round(mid * 100); } yes = Math.max(1, Math.min(99, yes)); return { id: rm.slug || marketId, slug: rm.slug || marketId, q: rm.q || 'Polymarket market', cat: rm.cat || 'Markets', desc: rm.desc || null, ico: '◈', yes, chg: 0, vol: +rm.vol || 0, liq: +rm.liq || 0, close: rm.end ? new Date(rm.end).toLocaleDateString('en-US', { month: 'short', day: '2-digit' }) : '—', end: rm.end || null, tokens: rm.tokens || [], prices: rm.prices || {}, condition_id: rm.condition_id || null, }; }, [real, rm, demoM, marketId]); const series = useMemo(() => { const r = seedRand('mk' + (m.id || marketId) + tf); let v = m.yes; const out = []; for (let i = 0; i < 60; i++) { v += (r() - 0.5) * 6; v = Math.max(8, Math.min(92, v)); out.push(v); } out[out.length - 1] = m.yes; return out; }, [m.id, m.yes, tf, marketId]); // Top holders / "copy traders here" have no real source yet — demo only. const holders = useMemo(() => { if (real) return []; const r = seedRand('hold' + m.id); return TRADERS.slice().sort(() => r() - 0.5).slice(0, 6).map((t, i) => ({ name: t.name, id: t.id, side: r() > 0.3 ? 'YES' : 'NO', shares: Math.floor(r() * 80000 + 8000), avg: Math.floor(r() * 30 + 40) })); }, [m.id, real]); const inMarketTraders = holders.filter((h) => h.side === 'YES').slice(0, 3); // Loading shell for the first real fetch (keeps hook order stable above). if (real && rmLoading && !rm) { return (
Loading market…
); } if (real && !rm) { return (
Market unavailable.
); } return (
{/* header */}
{m.ico}
{m.cat} Live

{m.q}

{moneyShort(m.vol)} Vol{moneyShort(real ? (m.liq || 0) : m.vol * 0.4)} LiquidityCloses {m.close}{real ? '' : ', 2026'}{!real && {(holders.length * 318).toLocaleString()} holders}
Chance (YES)
{m.yes}%
{real ? Live price : = 0 ? 'up' : 'down'}>{m.chg >= 0 ? '▲' : '▼'} {Math.abs(m.chg)}¢ 24h}
{/* LEFT */}
Probability history
{['1H', '1D', '1W', '1M', 'ALL'].map((t) => )}
{real &&
Live YES probability {m.yes}% · price history chart coming soon.
}
{/* top holders — demo only; no real holder source yet */} {real ? (
Top holders
On-chain holder breakdown coming soon.
) : (
Top holders
{holders.map((h, i) => ( onOpenTrader(h.id)}> ))}
TraderPositionSharesAvg
{h.name}
{h.side} {h.shares.toLocaleString()} {h.avg}¢ e.stopPropagation()}>
)} {/* comments — demo only; real comment feed not available yet */} {real ? (
Comments
Market comments are coming soon.
) : ( )}
{/* RIGHT */}
{real ? (
Copy traders here
Per-market copyable traders coming soon. Browse the leaderboard to copy a trader across all their positions.
) : (
Copy traders here
{inMarketTraders.length}
{inMarketTraders.map((h) => { const t = TRADERS.find((x) => x.id === h.id); return (
onOpenTrader(h.id)}>
{h.name}
{pct(t.roi)} · {t.win}% win
); })}
)}
Resolution
{real && m.desc ? m.desc : <>This market resolves YES if the outcome is officially confirmed by the listed source on or before {m.close}{real ? '' : ', 2026'}. Resolves NO otherwise.}
ResolverUMA Oracle ↗
); } function OrderPanel({ market, real, authed, onRequireConnect }) { const [side, setSide] = useState('YES'); const [amt, setAmt] = useState(100); const [done, setDone] = useState(false); const [busy, setBusy] = useState(false); const [err, setErr] = useState(null); const price = side === 'YES' ? market.yes : 100 - market.yes; const shares = (amt / (price / 100)).toFixed(0); // Real token ids — Polymarket clobTokenIds: [0]=YES, [1]=NO. const tokens = (real && market.tokens) || []; const tokenId = side === 'YES' ? (tokens[0] || null) : (tokens[1] || null); const buy = () => { if (!authed) return onRequireConnect(); setErr(null); if (real) { if (!tokenId) { setErr('Market not tradable right now.'); return; } if (!amt || amt <= 0) { setErr('Enter an amount.'); return; } setBusy(true); sniApi('market/order', { method: 'POST', body: JSON.stringify({ token_id: tokenId, side: 'BUY', usdc: amt }) }) .then((r) => { setBusy(false); if (r && r.ok) { setDone(true); setTimeout(() => setDone(false), 2600); } else setErr((r && (r.message || r.code)) || 'Order failed.'); }) .catch(() => { setBusy(false); setErr('Order failed.'); }); return; } // demo: instant optimistic fill setDone(true); setTimeout(() => setDone(false), 2200); }; return (
setAmt(+e.target.value || 0)} />
{[50, 100, 500, 1000].map((a) => )}
{err &&
{err}
} {done ? : }
{real ? 'Paper order — simulated against the live book' : 'Or auto-mirror this via a copied trader'}
); } function CommentsSection({ market, authed, onRequireConnect }) { const seeded = useMemo(() => { const r = seedRand('cm' + market.id); const base = COMMENTS.slice().sort(() => r() - 0.5).slice(0, 5); return base.map((c, i) => ({ ...c, id: 'c' + i })); }, [market.id]); const [list, setList] = useState(seeded); const [text, setText] = useState(''); const [sort, setSort] = useState('top'); useEffect(() => { setList(seeded); }, [seeded]); const post = () => { if (!authed) return onRequireConnect(); if (!text.trim()) return; setList((l) => [{ id: 'me' + Date.now(), name: 'degen_whale', side: 'YES', text: text.trim(), likes: 0, time: 'now', badge: 'You', mine: true }, ...l]); setText(''); }; const shown = sort === 'top' ? [...list].sort((a, b) => b.likes - a.likes) : list; return (
Comments {list.length}
setText(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && post()} onFocus={() => !authed && onRequireConnect()} />
{shown.map((c) => )}
); } function Comment({ c }) { const [likes, setLikes] = useState(c.likes); const [liked, setLiked] = useState(false); return (
{c.name} {c.side} holder {c.badge && {c.badge}} {c.time}

{c.text}

); } /* ============ ACTIVITY ============ */ function Activity({ ctx }) { const live = useLiveData(); const real = sniIsReal(); const [filter, setFilter] = useState('all'); const [rows, setRows] = useState(() => ACTIVITY.map((a, i) => ({ ...a, id: i }))); const filters = [['all', 'All'], ['fill', 'Copies'], ['tp', 'Profits'], ['deposit', 'Wallet'], ['rank', 'System']]; useInterval(() => { if (real) return; const tr = TRADERS[Math.floor(Math.random() * TRADERS.length)]; const mk = MARKETS[Math.floor(Math.random() * MARKETS.length)]; setRows((r) => [{ id: Date.now(), type: 'fill', side: Math.random() > 0.4 ? 'BUY' : 'SELL', trader: tr.name, market: mk.q, amt: Math.floor(Math.random() * 12000 + 500), price: mk.yes, ms: Math.floor(Math.random() * 14 + 7), t: 'just now', fresh: true }, ...r].slice(0, 40)); }, 2600); // Real: live fills + closes from the worker (no demo fallback). Demo otherwise. const baseRows = real ? sniMapActivity(live) : rows; const stats = sniStats(live); const realizedReal = +stats.realized_pnl || 0; const tradesReal = +stats.trades || 0; const shown = filter === 'all' ? baseRows : baseRows.filter((r) => filter === 'tp' ? (r.type === 'tp' || r.type === 'sl') : filter === 'rank' ? (r.type === 'rank' || r.type === 'resolve') : r.type === filter); return (
= 0 ? '+' : '') + moneyShort(realizedReal)) : '+$18.4k'} icon="award" accent={realizedReal >= 0} /> } icon="gauge" accent />
Activity
Every copy, fill and payout across your wallets · for raw engine telemetry see Bot Logs
{filters.map(([id, l]) => )}
{shown.map((a) => )} {!shown.length &&
{real ? 'No activity yet — start the bot to copy live fills.' : 'No activity in this category yet.'}
}
); } function ActivityRow({ a, onOpenTrader }) { const cfg = { fill: { ico: a.side === 'BUY' ? 'arrowUp' : 'arrowDown', col: a.side === 'BUY' ? 'var(--green)' : 'var(--red)', bg: a.side === 'BUY' ? 'rgba(22,255,122,0.1)' : 'rgba(255,77,94,0.1)' }, tp: { ico: 'award', col: 'var(--green)', bg: 'rgba(22,255,122,0.1)' }, sl: { ico: 'arrowDown', col: 'var(--red)', bg: 'rgba(255,77,94,0.1)' }, rank: { ico: 'trophy', col: 'var(--amber)', bg: 'rgba(255,194,75,0.1)' }, deposit: { ico: 'arrowDownC', col: 'var(--cyan)', bg: 'rgba(63,224,255,0.1)' }, resolve: { ico: 'flag', col: 'var(--violet)', bg: 'rgba(154,140,255,0.1)' }, }[a.type] || { ico: 'pulse', col: 'var(--tx-2)', bg: 'var(--bg-2)' }; return (
{a.type === 'fill' && <>Copied onOpenTrader && onOpenTrader()}>{a.trader} · {a.side} {a.market}} {a.type === 'tp' && <>Take-profit hit on {a.trader}'s {a.market}} {a.type === 'sl' && <>Stop-loss triggered on {a.trader}'s {a.market}} {a.type === 'rank' && <>{a.trader} {a.detail}} {a.type === 'deposit' && <>{a.detail}} {a.type === 'resolve' && <>Market {a.market} {a.detail}}
{a.type === 'fill' && <>{moneyShort(a.amt)} @ {a.price}¢{a.ms ? <> · filled in {a.ms}ms : null}} {(a.type === 'tp' || a.type === 'sl') && = 0 ? 'var(--green)' : 'var(--red)' }}>{a.pnl >= 0 ? '+' : ''}{money(a.pnl)} realized}
{a.t}
); } Object.assign(window, { MarketDetail, Activity });