26 Commits

Author SHA1 Message Date
gavindiaz 125470b24a sync: upstream + our customizations (dashboard, healthcheck, docs)
Synced from github/main: aebcfa2d copybot: live paper feed [skip ci]
Our additions: serve_dashboard.py, bot_dashboard.html, mihomo-healthcheck.sh,
中文文档, 我们的钱包配置
2026-07-21 07:35:48 +08:00
jaxperro 7d20588c6f clone-guard: verify over the git transport (ls-remote), not the REST API
api.github.com is effectively unreachable from the Fly boxes (Bearer 401,
anonymous rate-limited on shared egress) — the guard failed open on every
boot. ls-remote rides the same authenticated smart-HTTP path the clone
itself uses, so it works by construction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 22:04:40 -04:00
jaxperro 476c1059c8 overnight batch: H3 cursor fetch, Bearer clone-guard, SDK preflight, 5c drift alarm
- H3 (burst overflow): per-wallet trade cursor + paginated activity fetch
  (up to 5×100, walks back to the last processed timestamp) — a gkmg-scale
  clip burst can no longer scroll past a single newest-100 page. Cursor
  only advances after a fetch; 600s overlap re-serves boundary rows into
  the seen-tx dedupe. offset pagination verified against the data-api.
- clone-guard: Bearer auth (fine-grained PATs reject the 'token' scheme)
  with unauthenticated fallback + 40-hex sanity on the answer — Fly's
  shared egress IPs rate-limit the anonymous path.
- preflight_live.py rewritten for the unified SDK: deposit-wallet auth,
  pUSD collateral, order-book reach, RTDS stream delivery, geo verdict.
- ledger-drift ALARM floor 1c → 5c (4dp fill rounding accumulates pennies
  across 60+ copies; the check stays for real bugs — it caught $15.45 and
  $7.24. The self-heal path keeps its own threshold.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 22:00:41 -04:00
jaxperro 15952b9b24 clone-guard: query the public commits API unauthenticated
The fine-grained-PAT Authorization header 401'd on the box (06:04 boot →
'API unreachable', fail-open). Public repo: no auth needed; 60/hr
unauthenticated limit is plenty for boot-time checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 02:05:58 -04:00
jaxperro 3784b288cd hardening trio: exit-retry (1.6), RTDS shadow ledger, boot clone-guard
- failed mirror-exits queue for up to 10 heartbeat retries (recovered
  exits ping Discord; exhaustion pages ⚠ EXIT STUCK and the position
  rides knowingly; in-play holds hand to the pending registry). 3 stub
  paths pass incl. the 1.6 spec's fail-fail-fill.
- every RTDS Set E detection appends a durable shadow-ledger row
  (lat_s + which trigger won) that rides the publish commit — the 24h
  go/no-go data for flipping RTDS on the live app.
- start.sh verifies the boot clone's HEAD against the GitHub API and
  re-clones stale replicas (bit twice today).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:43:22 -04:00
jaxperro df3c750efe live app goes push-mode: own Alchemy webhook + health check + watchdog
60s poll's ~39s avg detection lag is slippage the copy edge pays; push is
~3s (paper-measured). fly.live.toml gains http_service + /health check
(self-heal); start.sh live role switches on ALCHEMY_SIGNING_KEY like paper
(poll fallback intact; push mode keeps the 60s heartbeat + 5min backstop
poll). sync_webhook.py syncs BOTH webhooks (alchemy_webhook_id_live).
watchdog.yml probes both apps; a disarmed live app paging is expected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 01:32:22 -04:00
jaxperro ca6661cb94 docs+cleanup: HANDOFF rev 5 (placement proven), gotcha 16, key self-revoke
Critical path closed: bridge conversion ($24.73→pUSD, fee-free), executor
ported and round-trip proven ($5 FAK both legs), armed bot restarted clean.
README gains gotcha 16 (pUSD stack: paused native-USDC onramp, bridge is
the sanctioned conversion, builder-key revocation semantics) + bridge/
relayer data-source rows. Probe & wrap scripts atexit-revoke their builder
keys (9 inert ones accumulated during bring-up).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 01:19:57 -04:00
jaxperro 0cf163abe5 host: flatten_positions.py + probe polls the indexer
First live fill (2026-07-10, $5 @ 0.68 UFC 329 main): buy leg FILLED on the
unified SDK but the probe's fixed 3s wait beat the data-api indexer, so the
sell-back never ran. Probe now polls up to 60s; flatten_positions.py is the
standalone sell-everything utility (and the emergency flatten).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 01:06:03 -04:00
jaxperro 67d2c00843 fly image: add unified SDK; probe reads positions from client.wallet
polymarket-client==0.1.0b16 baked into the worker image (py-clob-client
stays for legacy read paths). Probe: drop the manual-wrap block (bankroll
already converted to pUSD via the bridge — the onramp pauses native USDC)
and stop reading positions from LIVE_FUNDER_ADDRESS (the emptied legacy
proxy); the SDK-resolved deposit wallet is the position holder now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 00:36:47 -04:00
jaxperro 555d49b961 wrap_via_bridge: delivery is pUSD — the bridge wraps on deposit
The $3 test slice landed as pUSD 3.00 (bridge did native→USDC.e swap then
the wrap itself, zero fees). Watch pUSD+USDC.e for delivery; zero USDC.e
residue is the success case (manual wrap kept only as fallback).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 00:32:04 -04:00
jaxperro c71448a090 host: wrap_via_bridge.py — bankroll conversion via the sanctioned bridge path
On-chain forensics (2026-07-10): pUSD accepts native USDC by contract, but
the public CollateralOnramp has native USDC PAUSED (paused()=1) — the exact
cause of the direct-wrap revert. Bridge native→USDC.e (Polymarket Bridge,
the UI deposit flow, ~$0.005 on $24.73 per quote), then gasless
approve+wrap USDC.e→pUSD. Stage-gated: $3 test slice first.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 00:10:36 -04:00
jaxperro fa7968d0bb probe: gasless approve+wrap of full USDC balance via the public wrapper periphery
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:42:34 -04:00
jaxperro 0ef5b9c2a2 probe: approve_erc20 correct kwargs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:39:30 -04:00
jaxperro aab3d1c965 probe: approve native USDC to the collateral contract (wrap-at-match path)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:38:53 -04:00
jaxperro a34b40e69f probe v2: setup_trading_approvals before first order (the SDK's onboarding call)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:37:08 -04:00
jaxperro f7b38f6f54 probe v2: in-process builder key for allowance recovery on first order
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:28:18 -04:00
jaxperro c278aebb9e host: deposit-wallet bootstrap probe (builder key + gasless deploy)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:22:17 -04:00
jaxperro 222bc0b80d probe v2: let the SDK derive the Deposit Wallet (maker-not-allowed fix)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:20:57 -04:00
jaxperro 8791591b48 order probe v2: new unified SDK (polymarket-client) — old client archived, orders 400
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:19:29 -04:00
jaxperro 99dcca1899 order probe: plain-binary market first (negRisk suspected in the 400)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:15:59 -04:00
jaxperro bf11de9759 live executor crash-proofed + baseline stops eating fresh trades + poll guard
Root cause of zero live placements: the FIRST two qualifying signals
(Kruto 21:21, badaf Epic 21:53) each CRASHED the bot inside the
unguarded live order path (machine events: exit_code=1 seconds after
each signal; tick-size rejection the likely thrower — price was rounded
to 3dp on 1c-tick books). Fly restarted it and boot baseline marked the
fresh trades seen-without-copying: crash -> restart -> trade eaten,
twice. Fixes: _order never raises (failures return ok:False and feed
the new missed-row instrumentation); tick-size-aware pricing (cached
get_tick_size, buys ceil / sells floor, 0.01 fallback valid on every
book); size floored to 2dp; per-wallet poll guard (parity with the push
handler); baseline exempts trades younger than the stale window so a
restart can never eat a copyable trade again. host/order_probe.py =
controlled end-to-end placement proof (~$2 round trip).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:14:38 -04:00
jaxperro 87082938a0 host: allowance probe — balance vs exchange allowance on the live funder
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 14:38:44 -04:00
jaxperro 58b89c106a real-money worker scaffolding (LIVE_ROLLOUT Phase 3 prep)
- start.sh: COPYBOT_ROLE=live — unarmed (missing LIVE_PRIVATE_KEY /
  LIVE_FUNDER_ADDRESS / LIVE_CONFIRM) idles in geocheck, no clone, no
  book, no orders; armed runs config.live.example.json + own state file
  copybot_state.live.json, --live, poll mode (webhook stays on paper).
- confirm_live: accepts the phrase from LIVE_CONFIRM env — the USER
  types it into flyctl secrets set (rule 0.7 human checkpoint, headless);
  wrong value aborts, unset disarms at next boot.
- config.live.example.json: Set E + pinned floors (mirrors paper),
  bankroll $50 @ 10% = $5 stakes, caps untouched (rule 0.6), leaked
  Discord webhook URL removed (ROTATE it — was committed publicly).
- fly.live.toml: separate app wwf-copybot-live, arn, no inbound, no
  checks, role env baked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 23:17:36 -04:00
jaxperro b7b1487175 copybot: migrate Railway (US region, geo-blocked) -> Fly.io arn Stockholm
Polymarket geoblocks order placement by IP and Railway ran the worker in a
US region — fine for the paper bot (reads only) but a dead end for going
live. Fly arn (Sweden, unrestricted, ~25ms from the CLOB's eu-west-2
primaries) passes: geocheck VERDICT TRADABLE, pm geoblock blocked:false.

- host/geocheck.py: 3-probe geo-gate verdict (ipinfo, pm /api/geoblock,
  unauth CLOB order POST); --idle keeps a test machine up
- host/start.sh: geocheck at boot (GEOCHECK_ONLY=1 = probe-and-idle test
  mode, no second book-writer); warns loudly if a region is blocked
- fly.toml/fly.Dockerfile: wwf-copybot, single machine, http_service :8080
  for the Alchemy push webhook (now wh_blf4qjjvfdbqs9mc -> fly.dev domain)
- deploy_bot.sh: railway redeploy -> flyctl apps restart + banner wait

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 16:02:06 -04:00
jaxperro 0c7ee62ed7 copybot: push mode — Alchemy webhook + heartbeat + backstop poll
Setting ALCHEMY_SIGNING_KEY on the worker switches start.sh to webhook
mode: instant copies on POST /alchemy (signature-verified), a 60s
housekeeping heartbeat (settle, feed, summary), and a full backstop poll
every 5 min so a dropped push costs minutes, not a silent miss. Webhook
mode now baselines at boot and publishes an initial feed like poll mode.
No signing key -> classic 60s poll, unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 00:07:23 -04:00
jaxperro f18162058d July live test: follow Kruto/shisan/fortuneking/LSB1; fee+lag backtest; 24/7 host runner
- portfolio.py: follow set -> the fee-aware copy-positive sharps (Kruto2027,
  shisan888, fortuneking) + LSB1; entries pay the 0.03 sports taker fee and a
  +0.5%/~90s lag haircut (entry_model); backfilled from June 1; feed exposes
  fee_rate/slip/lag_est_s/fees_paid and unreal uses true cost basis.
- copybot.paper.json: same four wallets, p80 conviction floors from the cache
  (Kruto $123.08, shisan $704.33, fortuneking $970, LSB1 $231). Fresh $1k state
  committed separately by the bot - clean July book.
- host/start.sh + nixpacks: turnkey 24/7 worker for Railway/Fly/VPS - clones
  with GITHUB_TOKEN, resumes the committed state, polls 60s, publishes
  state+feed+fills back through publish_feed. Replaces the Mac poller.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 09:51:35 -04:00