diff --git a/HANDOFF.md b/HANDOFF.md index abee07ed..eac999ef 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -1,193 +1,141 @@ -# Session handoff — 2026-07-08 (rev 3: books aligned, Set E live, fresh $1k) +# Session handoff — 2026-07-10 (rev 4: real money one step from live) Self-contained pickup for a fresh session (human or AI). Read -[README.md](README.md) gotchas 1–15, [FINDINGS.md](FINDINGS.md) (especially -the three 2026-07-08 sections at the end), and [LIVE_ROLLOUT.md](LIVE_ROLLOUT.md). -The repo is authoritative; project memory has deeper history. +[README.md](README.md) gotchas 1–15, [FINDINGS.md](FINDINGS.md) (the +2026-07-08 sections at the end), [ETHERSCAN_MIGRATION.md](ETHERSCAN_MIGRATION.md), +and [LIVE_ROLLOUT.md](LIVE_ROLLOUT.md). The repo is authoritative. -## Where things stand (all green) +## THE CRITICAL PATH — finish live placement (one wrap + one port) -- **Live paper bot**: Fly.io `wwf-copybot`, region `arn` (Stockholm), push - mode, ONE machine. Following **Set E** (7 volume wallets): LSB1, - imwalkinghere, Kruto2027, 0xbadaf319, gkmgkldfmg, AIcAIc, 1kto1m. - `flyctl logs --app wwf-copybot`. -- **The book is a fresh $1,000, reset 2026-07-08 ~20:45 UTC** — this is the - CALIBRATION EXPERIMENT: measure the live-to-backtest ratio for Set E with - every fix live from day one; that ratio (not the replay %) sizes real money. - Old book (Jun 25 → Jul 8, +$229.79) is in git history + - `archive/copybot_fills.pre-reset-2026-07-08.jsonl`. -- **The three books agree by construction** (2026-07-08 alignment work): - same W/L/R/S taxonomy (win% = held-to-resolution only), same - sold-vs-redeem price discriminator (redeem prints exactly the payout), same - pinned floors, same follow set. Row-level cross-check bot↔backtest: 7/9 - exact, 0 absent, 2 legit execution diffs. Sharps table reproduces PM's - per-position books to the dollar; PM's LEADERBOARD is the biased number. -- **Dashboards**: [jaxperro.com/trading](https://jaxperro.com/trading) = - paper book + backtest + sharps; [jaxperro.com/live](https://jaxperro.com/live) - = REAL MONEY page (NOT STARTED until Phase 2). Shared renderer: - `jaxperro/trading/copybot-section.js`. `?botFeed=`/`?rmFeed=` query params - override feeds for local testing. -- **Watchdog live (2026-07-08)**: Fly `/health` check auto-restarts a dark - machine; GH Actions `watchdog.yml` probes from GitHub's infra and pings - Discord after 3 failed probes. `copytrade.py` now lives at repo root - (archive/ is 100% retired code again). -- **Daily pipeline** (Mac launchd 08:00, fires on wake — typically ~10:00, - caffeinate-wrapped) - appends one live-vs-model row/day to `live/history/calibration.csv` and - re-derives everything under the new rules: entries cursors now invalidate - daily, `_open_split` folds by the `redeemable` flag, `rtally` splits SOLD. -- **Backtest**: `portfolio.py` takes `--bank` (backtest.json `"bank"`). - Published feed stays at $1,000 to match the paper book. $500 run: +2728% - (small banks compound faster — their-bet ceilings bind later). +The real-money bot (`wwf-copybot-live`, armed, $24.73) cannot place yet. +Root-cause chain, every link PROVEN 2026-07-09/10: -## LIVE PLACEMENT — root cause SOLVED, one user action + one port remain +1. First two qualifying signals CRASHED the bot inside the unguarded live + order path (Fly events: exit_code=1 at 21:21:55Z and 21:53:35Z, seconds + after each signal); boot baseline then marked the fresh trades seen. + ALL FIXED: executor never raises, per-wallet poll guard, baseline + exempts trades younger than the stale window. +2. **py-clob-client is ARCHIVED** (May 2026) — the CLOB rejects its orders + ('invalid order version') globally. Reads/auth still work; placement is + dead for everyone on it. No newer PyPI release exists. +3. Successor: **Polymarket/py-sdk** (`pip install --pre polymarket-client`, + `SecureClient`). Proven working for us tonight: auth ✓, the signer's + **Deposit Wallet deployed: `0x455e252e45Ee46d6C4cc1c8fAdD3899d68f245a1`** ✓, + gasless relayer transactions execute ✓ (real txs: 0x935be727…, + 0x1044d3c7…), `setup_trading_approvals()` ✓ (max-uint on all spenders), + order FORMAT accepted by the exchange ✓. +4. **Sole remaining gap: the wrap.** The user's $24.73 sits SAFE as native + USDC at the deposit wallet (his key controls it). The 2026 exchange + trades a wrapped-collateral token (`0xC011a7…`; wraps native USDC; UI + flows wrap on deposit — our raw transfer bypassed it), and the CLOB's + balance view counts ONLY that token → orders reject with 'balance: 0'. + Attempted: periphery `0x93070a…`.wrap(asset,to,amount) via gasless batch + → 'batch would revert' (likely caller-gated). Neg-risk adapter ruled out. -Why the real-money bot placed nothing (2026-07-09/10 investigation, every -link proven): (1) the first two qualifying signals CRASHED the bot inside -the unguarded live order path (machine events: exit_code=1 at 21:21:55 and -21:53:35, seconds after each signal) and boot-baseline then marked the -fresh trades seen — both bugs FIXED (executor never raises; poll guard; -baseline exempts trades younger than the stale window). (2) With crashes -fixed, the probe exposed the real wall: **py-clob-client is ARCHIVED** and -the CLOB rejects its orders ('invalid order version') — reads/auth still -work, placement is dead for everyone on the old client. (3) The successor -is **Polymarket/py-sdk** (`pip install --pre polymarket-client`, -SecureClient). (4) Under it, the account's legacy proxy (0xbb2aC6b8…) is -refused as maker ('use the deposit wallet flow'); bootstrap -(host/wallet_bootstrap.py) minted a Builder API key with existing auth and -DEPLOYED the signer's Deposit Wallet: -**0x455e252e45Ee46d6C4cc1c8fAdD3899d68f245a1**. +**Next session, in order (do NOT guess with live funds):** +1. Read **Polymarket/ts-sdk** (same unified family) for the deposit-wallet + wrap/deposit implementation — it contains the exact supported call. + Also check docs.polymarket.com's new wallet/collateral pages. +2. Replicate that one call via `client.execute_transaction` (the gasless + path is proven). Success = `get_balance_allowance` shows ~$24.73. +3. Port `LedgerLiveExecutor` to the new SDK: `SecureClient.create( + private_key=…)` (wallet auto-resolves; NO api_key needed at runtime once + approvals exist), BUY = `place_market_order(token_id, side="BUY", + amount=, order_type="FAK")`, SELL = `shares=`; parse the result + for filled shares/price; keep py-clob-client temporarily for reads. + Repoint the 1.4 cash anchor at the deposit wallet's collateral view — + the live bot currently alarms `CASH≠CHAIN +24.73` because the anchor + still reads the emptied legacy proxy (expected, harmless). +4. `pip` add `polymarket-client` (--pre) to fly.Dockerfile, deploy, re-run + `host/order_probe_v2.py` (contains the ENTIRE working onboarding + sequence) until a $5 round trip fills, then run LIVE_ROLLOUT Phase 4 on + the first organic fill. +5. Cleanup: probe runs minted several Builder API keys — + `fetch_builder_api_keys` / revoke extras. -Remaining — WRAP STEP ONLY (status 2026-07-10 early AM): the $24.73 sits -SAFE as native USDC at the deposit wallet (user's key controls it; -recoverable/transferable at any time). Proven working tonight via probes: -auth ✓, gasless execution ✓ (two real relayer txs: USDC approval -0x1044d3c7…, earlier 0x935be727…), setup_trading_approvals ✓ (max-uint on -all spenders), order FORMAT accepted ✓. The one remaining gap: the -exchange's balance view counts only the COLLATERAL token -(0xC011a7… wraps native USDC; UI flows wrap on deposit; our raw transfer -bypassed it). Attempted: public wrapper periphery 0x93070a… -wrap(asset,to,amount) via gasless batch → 'batch would revert' (likely -caller-role-gated); neg-risk adapter ruled out (its mints are redeem -change). NEXT SESSION (do NOT guess with live funds): read the ts-sdk's -deposit/wrap implementation for deposit wallets (Polymarket/ts-sdk — same -unified family, will contain the exact supported call), replicate via -execute_transaction, THEN port LedgerLiveExecutor to the new SDK -(place_market_order: BUY amount=$USD / SELL shares=, FAK; construct -SecureClient(private_key) — wallet resolves, no api_key needed at runtime -once approvals exist), add polymarket-client to fly.Dockerfile, re-probe -(host/order_probe_v2.py — contains the full working onboarding sequence), -re-arm. CLEANUP: several builder API keys were minted by probe runs — -fetch_builder_api_keys/revoke the extras. The armed live bot remains -harmless (ok:False + honest missed rows on every attempt). +Meanwhile the armed live bot is harmless-by-construction: every attempt +returns ok:False and records an honest missed row. -## Queued next-session work (in value order) +## System state (all healthy, 2026-07-10 00:47Z) -0. **MORNING FORENSICS — paper-book drift −11.32 (alarm intentionally red).** - The clobber bug is FIXED (settled records archive to token#ts keys on - re-entry; two vanished records recovered from state git history and - restored). What remains: with truth restored, drift reads −11.32 — a - separate cash-outflow-without-ledger-counterpart accumulated during - 2026-07-09's 27-copy day. Suspect classes: sell fills drained against - already-settled records (sold_proceeds mutated post-pnl counts nowhere), - settle math on re-entered tokens, trim flows. Method: replay - copybot_fills.jsonl + state-history settles against the invariant - term-by-term until the residual attributes. The fills ledger now has - per-token BUY lines + book snapshots; state history is committed every - few minutes. LIVE BOOK UNAFFECTED (0 trades, drift 0.00) and protected - by the clobber fix before its first re-entry. +- **Paper bot** (`wwf-copybot`, push mode): Set E, fresh $1,000 book reset + 2026-07-08 20:45Z. **Day 1½: realized +$1,107.57, 36 copies, 6 open, + drift CLEAN** (the −11.32 forensics closed at 0.00 — see below). + Biggest single win: mirrored Kruto's 3¢→48¢ Hive scalp for +$722. +- **Live bot** (`wwf-copybot-live`, poll 60s): armed via LIVE_CONFIRM, + $24.73 book, 0 trades (see critical path), Discord pings wired + (DISCORD_WEBHOOK secret), auto_redeem off. Disarm: + `flyctl secrets unset LIVE_CONFIRM` or `flyctl apps stop`. +- **Set E** (both books + backtest): LSB1, imwalkinghere, Kruto2027, + 0xbadaf319, gkmgkldfmg, AIcAIc, 1kto1m — pinned floors, identical rules + (min_price 0.01 everywhere since 2026-07-09 18:05Z, user-approved). +- **Dashboards**: jaxperro.com/trading (paper+backtest+sharps), + jaxperro.com/live (real money). Shared renderer + jaxperro/trading/copybot-section.js; `?botFeed=`/`?rmFeed=` overrides. +- **Watchdog**: Fly /health check (paper app) + GH Actions watchdog.yml → + Discord. NOTE: the live app has NO watchdog (poll mode, no HTTP) — + liveness = flyctl logs + feed freshness; wire one before raising caps. +- **Daily pipeline**: Mac launchd 08:00 + pmset RTC wake 07:58 + + caffeinate; appends live-vs-model calibration.csv row daily. -1. **Exact-res_t migration**: plan agreed 2026-07-08 — execute - [ETHERSCAN_MIGRATION.md](ETHERSCAN_MIGRATION.md) phase by phase - (0: chain sweep backfill → 1: shadow audit → 2: validate_timing → - 3: portfolio → 4: selection+floors → 5: trust simplification LAST). - One phase per session, before/after diffs, never coupled with - follow-set changes. -2. **Empirical fill model**: every copy now logs a `book` snapshot (spread + - 5c depth) to the fills ledger; once a few weeks accumulate, fit slippage - vs stake/depth and replace portfolio.py's flat SLIP haircut; add a depth - gate before real-money sizing. -3. **Funding-cluster tracer port** to Etherscan logs (Alchemy free tier now - caps getLogs at 10 blocks — insider.py's ring detection is degraded - until then). +## Completed 2026-07-09 (the honesty & detection sprint) -## Watch list (the standing question: does Set E hold up?) +- **Fill-split merging**: same-token BUY clips ≤120s apart merge into the + bet the sharp actually made before the floor gate (gkmg's $612 MOUZ entry + read as 3×$204 sub-floor clips; he sold +60% in 5 min). Both books. +- **Missed-ledger truth**: missed bets settle at the sharp's EXIT when they + sold pre-resolution (Kruto Hive miss = honest +$75 counterfactual on $5); + settled won/lost rows get one exit-print re-check (fast-resolve race); + every row relabeled with its diagnosed cause; the two silent follow-path + exits (no-ask book, order rejection) now record misses. +- **Clobber fix + drift forensics CLOSED**: re-entry on a settled token + archives (token#ts keys) instead of overwriting; three vanished records + reconstructed from fills-ledger/state-history; paper drift 0.00. +- **Order-book snapshots** (`book` field) logged per copy → empirical fill + model later. **payouts.resolution_time(cond)** wired (Etherscan V2, + exact on-chain res_t; metadata was 29h wrong on the test market). +- **min_price parity** 0.05→0.01 on the live book (user-approved; the band + — not detection — blocked the 16x Hive copy). -- **0xbadaf319** — 2026-07-08 evening revealed his structure: PAIRED YES+NO - clips ($6–37 each, sums ≈ $1.00) = two-sided arb/merge flow, not - directional conviction. Copying one leg of a hedge is a bet HE never took; - his replay P&L rode the larger leg of pairs. Moves to the TOP of the - demotion watch list — judge on forward copies, not his account P&L. -- **AIcAIc** — held-win only 42%; his edge is sell-timing (most lag-fragile - class). Second demotion candidate. -- **1kto1m** (z=2.4) and **gkmgkldfmg** (z=2.05) — near the gate floor; - strong month, shallow statistical edge. -- Re-ranking is cheap: the per-wallet replay harness pattern is in FINDINGS - ("Aligning the three books"); `python3 live/portfolio.py --wallets 0x… - --days 30 --out /tmp/w.json` per candidate, then combined-set runs. -- Rejected-with-evidence (don't re-add without new facts): oliman2 (true - lifetime ~$19k), leegunner (negative to copy), 0xb0E43B/ArbTraderRookie - (refund harvesters — real edge, uncopyable margins), lma0o0o0o (negative in - the shared book), Winnertraders (6.6-day capital locks). +## Queued work (after the critical path) -## Real-money worker — ARMED AND LIVE (2026-07-09 ~04:37 UTC) +1. ETHERSCAN_MIGRATION.md phases 0→5 (chain-sweep res_t backfill → shadow + audit → consumer flips → trust.py simplification LAST). +2. Empirical fill model from the accumulating book snapshots; depth gate + before real-money sizing. +3. Funding-cluster tracer port to Etherscan logs (Alchemy free tier caps + getLogs at 10 blocks). +4. Live-app watchdog (no /health in poll mode). +5. Decide the leftover **$32 on polymarket.us** (user's first deposit, + wrong venue — withdraw or abandon). -`wwf-copybot-live` is trading REAL MONEY: $24.73 book, Set E, $5 stakes, -rule-0.6 caps ($5/trade · $25/day · $30 exposure), poll mode 60s, own state -`copybot_state.live.json`, feed `live/copybot_live_real.json` → -jaxperro.com/live. Armed via LIVE_CONFIRM env (user-typed phrase; unset to -disarm at next boot; `flyctl apps stop wwf-copybot-live` = instant stop). -auto_redeem OFF (no POL gas) — after a WIN, redeem manually in the UI; -CASH≠CHAIN nags until done. Discord pings per placement/exit/settle via -DISCORD_WEBHOOK secret. Phase 4 (supervised first fill checklist in -LIVE_ROLLOUT) runs when the first conviction signal lands — zero live -trades so far; fill-split merging (see below) shipped 2026-07-09 evening -after 13h of sub-floor-clip skips. No watchdog on the live app yet (poll -mode, no HTTP) — liveness = flyctl logs + feed freshness. +## Watch list (does Set E hold up?) -## Venue fork — RESOLVED 2026-07-09 (user funded polymarket.com) +- **0xbadaf319** — two-sided arb/merge clips, not directional conviction; + TOP demotion candidate; judge on forward copies. +- **AIcAIc** — 42% held-win; sell-timing edge (lag-fragile). Second. +- **1kto1m** (z=2.4), **gkmgkldfmg** (z=2.05) — near the gate floor. +- Rejected-with-evidence (don't re-add): oliman2 (true lifetime ~$19k, not + PM's $112k), leegunner (negative to copy), 0xb0E43B/ArbTraderRookie + (refund harvesters), lma0o0o0o (negative in shared book), Winnertraders + (6.6-day locks). -The first deposit landed on polymarket.us by mistake (off-chain venue, no -keys — incompatible with the stack; $32 still sits there, user may withdraw). -User then funded a polymarket.com account ($24.73 after fees) and the live -worker armed against it. Residual security to-dos: revoke the polymarket.us -API key f03d9eb5… (secret appeared in a chat screenshot) and rotate the -Discord webhook committed to this repo's history pre-2026-07-09. +## User to-dos (security) -## Next: Phase 2 — funding (USER ONLY) - -Phase 1 is 8/8 complete (LIVE_ROLLOUT.md). Before funding, the user should -decide the stranding tolerance explicitly: real money turns the primary risk -from P&L into the ACCOUNT (US person routing orders via Stockholm through the -geoblock — a frozen wallet is a different loss category than a losing bet). -Caps stay $5/trade · $25/day · $30 exposure until Phase 6. Then Phase 3 -preflight (`preflight_live.py`) → Phase 4 supervised first fill → Phase 5 -edge-case matrix. The typed-phrase interlock is the human checkpoint — never -automated. - -## Standing to-dos (user-only or external) - -- **None open.** Alchemy key ROTATED 2026-07-08: old app "Jax's First App" - (key `…OdWgOi`, the one leaked to public git history) deleted; new app - `wwf-rpc-2026-07` (key `…w0BxV5`) wired into gitignored `config.json` + - the `ALCHEMY_RPC_URL` Fly secret. Verified: old key → HTTP 401 (the key - in git history is now dead, so no history rewrite needed), new key serves - Polygon, bot rebooted with settle-fallback ON and made a live copy, Notify - webhook `wh_blf4qjjvfdbqs9mc` survived (app-delete doesn't touch Notify - resources) — 7 addresses in sync. Railway project deleted (purges 07-10). +- Revoke the polymarket.us API key `f03d9eb5…` (secret appeared in a chat + screenshot) — if not already done. +- Rotate the Discord webhook that was committed to this repo's history + pre-2026-07-09 (spam-risk only). ## Operational quick-reference -- **Change the follow set**: edit `live/copybot.paper.json` wallets → - `./live/deploy_bot.sh` (validates → pins floors → syncs Alchemy webhook → - restarts Fly → confirms banner). Keep `backtest.json` mirrored. -- **State surgery (reset/repair the book)**: `flyctl machine stop` FIRST, - push the new state, `flyctl apps restart`, then VERIFY the first heartbeat - shows the book you wrote (gotcha 15: the bot's memory wins conflicts by - design, and a boot clone can read a stale GitHub replica for a few seconds). -- Never 2 Fly machines (`flyctl scale count 1`) — one book, one writer. -- Cache is single-writer; don't run cache-touching scripts during the daily - pipeline or a regen. Three cursors per wallet (gotcha 12). -- Bot commits its own state/feed — `git pull --rebase --autostash` before - pushing. Image changes (fly.Dockerfile, host/) need - `flyctl deploy --remote-only`; code/config changes just push + restart. +- Follow-set change: edit live/copybot.paper.json → `./live/deploy_bot.sh`; + keep backtest.json mirrored. +- State surgery: `flyctl machine stop` FIRST → edit → push → + `flyctl apps restart` → VERIFY the first heartbeat (gotcha 15; bot memory + wins conflicts; boot clones can read a stale GitHub replica briefly). +- Never 2 Fly machines per app (`flyctl scale count 1`). +- Cache single-writer; three cursors per wallet (gotcha 12). +- Bot commits its own state/feed — `git pull --rebase --autostash` first. +- Image changes (fly.Dockerfile, host/) → `flyctl deploy --remote-only`; + code/config → push + restart. Live app config = fly.live.toml.