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>
This commit is contained in:
+41
-40
@@ -1,26 +1,28 @@
|
||||
{
|
||||
"mode": "live",
|
||||
"_comment": "Live-test config, SECRET-FREE and committed. Two uses: (1) cp -> config.live.json, fill live.private_key/funder_address, run on a geo-TRADABLE Mac; (2) the Fly live worker reads THIS file directly and gets the key from LIVE_PRIVATE_KEY/LIVE_FUNDER_ADDRESS env vars (copybot 1.2). $5/trade test caps \u2014 see LIVE_ROLLOUT.md before raising anything.",
|
||||
"bankroll_usd": 250.0,
|
||||
"bankroll_pct": 0.02,
|
||||
"_comment": "Live-test config, SECRET-FREE and committed. The Fly live worker (wwf-copybot-live, COPYBOT_ROLE=live) reads THIS file and takes secrets from env (LIVE_PRIVATE_KEY/LIVE_FUNDER_ADDRESS + LIVE_CONFIRM to arm). Mac use: cp -> config.live.json and fill live.*. RULES: bankroll_usd MUST equal the actual USDC deposit at arm time (the 1.4 cash-anchor checks state cash vs chain); risk caps are LIVE_ROLLOUT rule 0.6 \u2014 never raise before Phase 6; follow set mirrors copybot.paper.json (Set E) so paper and live books stay comparable.",
|
||||
"bankroll_usd": 50.0,
|
||||
"bankroll_pct": 0.1,
|
||||
"price_guard_pct": 0.05,
|
||||
"taker_fee_rate": 0.03,
|
||||
"discord_webhook": "https://discord.com/api/webhooks/1515187084409704660/VbKWKIIo2ks1sSs2r1cr9M3OSIj_6oKDOBRp7Z5DneQCyk8AnPiyxVKelxensLP-aHaH",
|
||||
"discord_webhook": "",
|
||||
"follow": {
|
||||
"buy_only": true,
|
||||
"min_their_usd": 25.0,
|
||||
"per_wallet_min_usd": {
|
||||
"0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb": 123.27,
|
||||
"0xf3488e52ac2d7f0628b04481db5a5b0446f0e543": 727.08,
|
||||
"0x86c878cde72660ec52f5e6f0f0438b76de8fc867": 990.45,
|
||||
"0x41558102a796ba971c7567cad41c307e59f8fa41": 231.0,
|
||||
"0xd96750bf8d941a8186e592b0ae6e096da66aa266": 115.27
|
||||
"0x41558102a796ba971c7567cad41c307e59f8fa41": 258.51,
|
||||
"0xd96750bf8d941a8186e592b0ae6e096da66aa266": 122.32,
|
||||
"0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb": 126.84,
|
||||
"0xbadaf319415c17f28824a43ae0cd912b9d84d874": 44.98,
|
||||
"0xd7d36345c4aab150e59577e360696b01d01d698b": 304.34,
|
||||
"0x921433c93558b9a4ba807ec824d02aad7ea2ddbf": 295.32,
|
||||
"0xa1d57d329227c75b12b09f927fb3d6d6ef8f1343": 320.95
|
||||
},
|
||||
"min_entry": 0.0,
|
||||
"max_entry": 0.95,
|
||||
"class_pct": {
|
||||
"volume": 0.02,
|
||||
"whale": 0.02
|
||||
"volume": 0.1,
|
||||
"whale": 0.1
|
||||
}
|
||||
},
|
||||
"risk": {
|
||||
@@ -42,50 +44,49 @@
|
||||
"auto_redeem": true,
|
||||
"rpc_url": ""
|
||||
},
|
||||
"stake_cap_usd": 250.0,
|
||||
"stake_cap_usd": 50.0,
|
||||
"wallets": [
|
||||
{
|
||||
"wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb",
|
||||
"name": "Kruto2027",
|
||||
"class": "volume"
|
||||
},
|
||||
{
|
||||
"wallet": "0x86c878cde72660ec52f5e6f0f0438b76de8fc867",
|
||||
"name": "fortuneking",
|
||||
"class": "volume"
|
||||
},
|
||||
{
|
||||
"wallet": "0x41558102a796ba971c7567cad41c307e59f8fa41",
|
||||
"name": "LSB1",
|
||||
"class": "volume"
|
||||
"class": "volume",
|
||||
"floor": 258.51
|
||||
},
|
||||
{
|
||||
"wallet": "0xd96750bf8d941a8186e592b0ae6e096da66aa266",
|
||||
"name": "imwalkinghere",
|
||||
"class": "volume"
|
||||
},
|
||||
{
|
||||
"wallet": "0xfc81760d44a21acc9fd4b749a5bf9a9b2eeae072",
|
||||
"name": "iohihoo",
|
||||
"class": "volume"
|
||||
},
|
||||
{
|
||||
"wallet": "0xf5fe759cece500f58a431ef8dacea321f6e3e23d",
|
||||
"name": "Stavenson",
|
||||
"class": "volume",
|
||||
"floor": 4759.62
|
||||
"floor": 122.32
|
||||
},
|
||||
{
|
||||
"wallet": "0x73afc8160c17830c0c7281a7bf570c871455b880",
|
||||
"name": "0x4bFb-whale",
|
||||
"wallet": "0xe8ca3f758c93f44f3ec210542ab78afb7c0bcccb",
|
||||
"name": "Kruto2027",
|
||||
"class": "volume",
|
||||
"floor": 1090.46
|
||||
"floor": 126.84
|
||||
},
|
||||
{
|
||||
"wallet": "0x72e1597864456eda62878413cf3e60c332e4a45d",
|
||||
"name": "ArbTrader",
|
||||
"wallet": "0xbadaf319415c17f28824a43ae0cd912b9d84d874",
|
||||
"name": "0xbadaf319",
|
||||
"class": "volume",
|
||||
"floor": 168.28
|
||||
"floor": 44.98
|
||||
},
|
||||
{
|
||||
"wallet": "0xd7d36345c4aab150e59577e360696b01d01d698b",
|
||||
"name": "gkmgkldfmg",
|
||||
"class": "volume",
|
||||
"floor": 304.34
|
||||
},
|
||||
{
|
||||
"wallet": "0x921433c93558b9a4ba807ec824d02aad7ea2ddbf",
|
||||
"name": "AIcAIc",
|
||||
"class": "volume",
|
||||
"floor": 295.32
|
||||
},
|
||||
{
|
||||
"wallet": "0xa1d57d329227c75b12b09f927fb3d6d6ef8f1343",
|
||||
"name": "1kto1m",
|
||||
"class": "volume",
|
||||
"floor": 320.95
|
||||
}
|
||||
],
|
||||
"feed_path": "live/copybot_live_real.json",
|
||||
|
||||
@@ -599,6 +599,18 @@ def confirm_live(cfg):
|
||||
f" · daily cap ${cfg['risk']['daily_spend_cap_usd']:.0f}")
|
||||
print(f" Watching {len(cfg['watchlist'])} wallets.")
|
||||
print("=" * 64)
|
||||
# Headless arm (Fly live worker): the USER types the exact phrase into
|
||||
# `flyctl secrets set LIVE_CONFIRM="…"` — still a human checkpoint, never
|
||||
# baked into config or code (LIVE_ROLLOUT rule 0.7). Known property: while
|
||||
# the secret stays set, restarts RE-ARM automatically (desired during the
|
||||
# days-long Phase 5 matrix); `flyctl secrets unset LIVE_CONFIRM` disarms
|
||||
# at the next boot, and any wrong value aborts instead of prompting.
|
||||
env_phrase = os.environ.get("LIVE_CONFIRM")
|
||||
if env_phrase is not None:
|
||||
if env_phrase.strip() == CONFIRM_PHRASE:
|
||||
print("confirmed via LIVE_CONFIRM env — armed.")
|
||||
return
|
||||
sys.exit("Aborted — LIVE_CONFIRM is set but does not match the phrase.")
|
||||
typed = input(f'Type "{CONFIRM_PHRASE}" to proceed (anything else aborts): ')
|
||||
if typed.strip() != CONFIRM_PHRASE:
|
||||
sys.exit("Aborted — not confirmed.")
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Fly.io config for the REAL MONEY copybot worker — a SEPARATE app from the
|
||||
# paper worker (wwf-copybot) so the two books never share a process, state
|
||||
# file, or failure mode (LIVE_ROLLOUT Phase 3.3). Region rules: see fly.toml.
|
||||
#
|
||||
# Poll mode only (60s; the Alchemy push webhook stays on the paper app), no
|
||||
# inbound services, no health checks — the unarmed machine idles in
|
||||
# geocheck --idle until the USER sets LIVE_PRIVATE_KEY + LIVE_FUNDER_ADDRESS
|
||||
# + LIVE_CONFIRM (the typed confirmation phrase) via flyctl secrets set.
|
||||
app = "wwf-copybot-live"
|
||||
primary_region = "arn"
|
||||
|
||||
[build]
|
||||
dockerfile = "fly.Dockerfile"
|
||||
|
||||
[env]
|
||||
COPYBOT_ROLE = "live"
|
||||
|
||||
[[vm]]
|
||||
size = "shared-cpu-1x"
|
||||
memory = "512mb"
|
||||
@@ -39,6 +39,21 @@ fi
|
||||
python3 "$(dirname "$0")/geocheck.py" \
|
||||
|| echo "⚠ geo-gate BLOCKED/unknown here — fine for paper, do NOT go live from this box"
|
||||
|
||||
# COPYBOT_ROLE=live -> the REAL MONEY worker (separate Fly app
|
||||
# wwf-copybot-live; NEVER convert the paper worker — the two roles must never
|
||||
# share a process or a state file). Unarmed = missing any of
|
||||
# LIVE_PRIVATE_KEY / LIVE_FUNDER_ADDRESS / LIVE_CONFIRM -> idle harmlessly
|
||||
# (no clone, no book, no orders) so the machine can exist safely before
|
||||
# Phase 2 funding. Arming is three `flyctl secrets set` calls by the USER;
|
||||
# copybot.py additionally re-checks the geo-gate fatally and validates the
|
||||
# phrase itself (LIVE_ROLLOUT 0.7, 1.5).
|
||||
if [ "${COPYBOT_ROLE:-paper}" = "live" ]; then
|
||||
if [ -z "${LIVE_PRIVATE_KEY:-}" ] || [ -z "${LIVE_FUNDER_ADDRESS:-}" ] || [ -z "${LIVE_CONFIRM:-}" ]; then
|
||||
echo "live role UNARMED (need LIVE_PRIVATE_KEY + LIVE_FUNDER_ADDRESS + LIVE_CONFIRM) — idling"
|
||||
exec python3 "$(dirname "$0")/geocheck.py" --idle
|
||||
fi
|
||||
fi
|
||||
|
||||
: "${GITHUB_TOKEN:?set GITHUB_TOKEN (PAT with repo contents read+write)}"
|
||||
REPO_URL="https://x-access-token:${GITHUB_TOKEN}@github.com/jaxperro/winning-wallet-finder.git"
|
||||
DIR="${COPYBOT_DIR:-/tmp/wwf}"
|
||||
@@ -49,6 +64,17 @@ cd "$DIR"
|
||||
git config user.name "copybot[bot]"
|
||||
git config user.email "copybot@users.noreply.github.com"
|
||||
|
||||
# live role, ARMED: own config (committed template + env secrets), own state
|
||||
# file, own feed/fills (config paths), ALWAYS poll mode — the Alchemy push
|
||||
# webhook stays pointed at the paper app (LIVE_ROLLOUT Phase 3.3).
|
||||
if [ "${COPYBOT_ROLE:-paper}" = "live" ]; then
|
||||
exec python3 copybot.py \
|
||||
--config config.live.example.json \
|
||||
--state copybot_state.live.json \
|
||||
--live \
|
||||
--poll "${POLL_SECONDS:-60}"
|
||||
fi
|
||||
|
||||
# paper config is committed (no secrets); state resumes from the last commit.
|
||||
# ALCHEMY_SIGNING_KEY set -> push mode (webhook server); else 60s poll mode.
|
||||
if [ -n "${ALCHEMY_SIGNING_KEY:-}" ]; then
|
||||
|
||||
Reference in New Issue
Block a user