Add live money configuration setup

This commit is contained in:
Theodore Song
2026-07-12 16:58:10 -04:00
parent c4acfd047a
commit f66155da69
6 changed files with 157 additions and 17 deletions
+40
View File
@@ -0,0 +1,40 @@
# Cloud state
BLOB_READ_WRITE_TOKEN=
PMA_STATE_PATH=shared/state.json
# Live-money feature gate
# Keep false until legal/compliance approval, provider credentials, wallet signing,
# payment rails, and audit logging are production-ready.
LIVE_TRADING_ENABLED=false
# Eligibility / compliance provider
KYC_PROVIDER=
KYC_API_KEY=
KYC_WEBHOOK_SECRET=
RESTRICTED_JURISDICTIONS=US,CA-NY,CA-ON
# Wallet / deposit-wallet provider
WALLET_PROVIDER=
WALLET_PROJECT_ID=
WALLET_API_KEY=
DEPOSIT_WALLET_ADDRESS=
POLYMARKET_SIGNATURE_TYPE=3
# Payments / deposits / withdrawals
PAYMENTS_PROVIDER=
PAYMENTS_API_KEY=
PAYMENTS_WEBHOOK_SECRET=
SETTLEMENT_ASSET=pUSD
SETTLEMENT_CHAIN=polygon
# Polymarket CLOB credentials
POLYMARKET_CLOB_HOST=https://clob.polymarket.com
POLYMARKET_CHAIN_ID=137
POLYMARKET_CLOB_API_KEY=
POLYMARKET_CLOB_SECRET=
POLYMARKET_CLOB_PASSPHRASE=
# Audit / monitoring
AUDIT_LOG_STORE=
ADMIN_ALERT_EMAIL=
RISK_ADMIN_WALLET=
+1
View File
@@ -3,3 +3,4 @@ node_modules/
.vercel .vercel
.claude/ .claude/
.env* .env*
!.env.example
+26 -11
View File
@@ -1,14 +1,17 @@
# 📊 Polymarket Analyst — standalone website # Polymarket Analyst
A **single-file website**. Everything (fetching live Polymarket data, the A Vercel-hosted Polymarket agent arena with shared paper-trading state, agent
analysis engine, and the ten AI paper-trading agents) runs in your browser. The return charts, paper accounts, market browsing, and live-money readiness rails.
paper portfolio is saved in your browser's local storage. No server, no keys.
## Just look at it now ## Just look at it now
Double-click **`index.html`** — it opens in your browser and works immediately.
On first open it fetches live markets, generates today's suggestions, and the Open the deployed site:
agents run a seven-day backtest, then begin live tracking. It auto-refreshes
once per day; the **Run cycle** button forces a refresh anytime. https://polymarket-site-eta.vercel.app
The site fetches live Polymarket markets, generates agent suggestions, lets you
run hourly paper cycles, and syncs the shared arena state through the Vercel API
when `BLOB_READ_WRITE_TOKEN` is configured.
## Put it online (free) so you can reach it from any device ## Put it online (free) so you can reach it from any device
@@ -28,8 +31,20 @@ Pick one — all give you a public URL:
1. <https://vercel.com> → Add New → Project → import this GitHub repo under the 1. <https://vercel.com> → Add New → Project → import this GitHub repo under the
`theodore_song` Vercel account (or use the `vercel` CLI in this folder) → Deploy. `theodore_song` Vercel account (or use the `vercel` CLI in this folder) → Deploy.
## Configuration
Use `.env.example` as the setup template.
- `BLOB_READ_WRITE_TOKEN` enables cross-device shared state.
- `/api/live` reports whether KYC, payments, wallet/deposit-wallet, Polymarket
CLOB, audit, and monitoring providers are configured.
- `LIVE_TRADING_ENABLED` should stay `false` until legal review, provider setup,
wallet signing, reconciliation, and dry-run testing are complete.
- See `REAL_MONEY_ROADMAP.md` for the launch requirements before any real funds
or live order execution are enabled.
## Notes ## Notes
- Paper trading only — no real money, nothing places real orders. - Paper trading only right now — no real money, nothing places real orders.
- The analysis is a transparent heuristic, **not financial advice**. - The analysis is a transparent heuristic, **not financial advice**.
- Because the portfolio lives in your browser, each browser/device keeps its - The shared arena uses cloud state when configured. Individual paper accounts
own portfolio. Clearing site data resets it (so does the **Reset** button). still use local browser storage unless a backend account database is added.
+53 -1
View File
@@ -4,6 +4,17 @@ This app is currently paper trading only. The Live Money tab and `/api/live`
endpoint are readiness scaffolding: they make the required product and backend endpoint are readiness scaffolding: they make the required product and backend
interfaces visible without moving funds or placing orders. interfaces visible without moving funds or placing orders.
## What is configured now
- `.env.example` lists the environment variables the production app needs.
- `/api/live` reports which live-money providers are configured or missing.
- `/api/live` accepts order intents only as dry-runs and returns an audit event.
- The Live Money tab shows launch checks, wallet/deposit settings, agent risk
limits, a dry-run order console, and an audit preview.
The app still refuses to place live orders. That is intentional until the legal,
provider, wallet-signing, reconciliation, and monitoring steps below are done.
## Required before live deposits or trading ## Required before live deposits or trading
1. Legal and eligibility 1. Legal and eligibility
@@ -42,15 +53,56 @@ interfaces visible without moving funds or placing orders.
## Environment variables expected by `/api/live` ## Environment variables expected by `/api/live`
Copy `.env.example` into your local environment for development, then add the
same variables in Vercel Project Settings -> Environment Variables for
production.
- `KYC_PROVIDER` - `KYC_PROVIDER`
- `KYC_API_KEY`
- `KYC_WEBHOOK_SECRET`
- `PAYMENTS_PROVIDER` - `PAYMENTS_PROVIDER`
- `PAYMENTS_API_KEY`
- `PAYMENTS_WEBHOOK_SECRET`
- `WALLET_PROVIDER` - `WALLET_PROVIDER`
- `WALLET_PROJECT_ID`
- `WALLET_API_KEY`
- `DEPOSIT_WALLET_ADDRESS`
- `POLYMARKET_SIGNATURE_TYPE=3`
- `POLYMARKET_CHAIN_ID=137`
- `SETTLEMENT_ASSET=pUSD`
- `SETTLEMENT_CHAIN=polygon`
- `POLYMARKET_CLOB_API_KEY` - `POLYMARKET_CLOB_API_KEY`
- `POLYMARKET_CLOB_SECRET` - `POLYMARKET_CLOB_SECRET`
- `POLYMARKET_CLOB_PASSPHRASE` - `POLYMARKET_CLOB_PASSPHRASE`
- `AUDIT_LOG_STORE` - `AUDIT_LOG_STORE`
- `LIVE_TRADING_ENABLED=true` - `ADMIN_ALERT_EMAIL`
- `LIVE_TRADING_ENABLED=true` only after final approval and end-to-end testing
Even when all variables are present, live order placement remains intentionally Even when all variables are present, live order placement remains intentionally
unimplemented until wallet signing, compliance approval, and safe order routing unimplemented until wallet signing, compliance approval, and safe order routing
are completed. are completed.
## Polymarket integration notes
Polymarket's public docs describe trading as non-custodial. Orders are signed
and matched through the CLOB, and new API users generally use the deposit-wallet
flow with signature type `3`.
Useful official docs:
- Trading overview: https://docs.polymarket.com/trading/overview
- Trading quickstart: https://docs.polymarket.com/trading/quickstart
- Deposit wallets: https://docs.polymarket.com/trading/deposit-wallets
## Final setup checklist
1. Choose and contract the KYC / eligibility provider.
2. Choose the deposit and withdrawal provider.
3. Choose the wallet or embedded-wallet provider and create a deposit wallet.
4. Derive or create Polymarket CLOB credentials for the approved signing flow.
5. Create durable append-only audit storage.
6. Add all environment variables in Vercel.
7. Run dry-runs against `/api/live` and verify audit events.
8. Complete legal review, terms, restricted jurisdiction rules, and support
procedures.
9. Only then set `LIVE_TRADING_ENABLED=true`.
+34 -3
View File
@@ -1,11 +1,22 @@
const REQUIRED_ENV = [ const REQUIRED_ENV = [
["KYC_PROVIDER", "KYC / eligibility provider"], ["KYC_PROVIDER", "KYC / eligibility provider"],
["KYC_API_KEY", "KYC provider API key"],
["KYC_WEBHOOK_SECRET", "KYC webhook secret"],
["PAYMENTS_PROVIDER", "Deposit and withdrawal provider"], ["PAYMENTS_PROVIDER", "Deposit and withdrawal provider"],
["PAYMENTS_API_KEY", "Payments provider API key"],
["PAYMENTS_WEBHOOK_SECRET", "Payments webhook secret"],
["WALLET_PROVIDER", "Wallet or deposit-wallet provider"], ["WALLET_PROVIDER", "Wallet or deposit-wallet provider"],
["WALLET_PROJECT_ID", "Wallet provider project ID"],
["WALLET_API_KEY", "Wallet provider API key"],
["DEPOSIT_WALLET_ADDRESS", "Polymarket deposit-wallet address"],
["POLYMARKET_SIGNATURE_TYPE", "Polymarket signature type"],
["POLYMARKET_CLOB_API_KEY", "Polymarket CLOB API key"], ["POLYMARKET_CLOB_API_KEY", "Polymarket CLOB API key"],
["POLYMARKET_CLOB_SECRET", "Polymarket CLOB API secret"], ["POLYMARKET_CLOB_SECRET", "Polymarket CLOB API secret"],
["POLYMARKET_CLOB_PASSPHRASE", "Polymarket CLOB passphrase"], ["POLYMARKET_CLOB_PASSPHRASE", "Polymarket CLOB passphrase"],
["SETTLEMENT_ASSET", "Settlement asset"],
["SETTLEMENT_CHAIN", "Settlement chain"],
["AUDIT_LOG_STORE", "Durable audit log store"], ["AUDIT_LOG_STORE", "Durable audit log store"],
["ADMIN_ALERT_EMAIL", "Admin alert destination"],
]; ];
function providerStatus() { function providerStatus() {
@@ -13,23 +24,43 @@ function providerStatus() {
key, key,
label, label,
configured: Boolean(process.env[key]), configured: Boolean(process.env[key]),
expected: key === "POLYMARKET_SIGNATURE_TYPE" ? "3 for new deposit-wallet API users" : undefined,
})); }));
} }
function liveTradingReady() { function liveTradingReady() {
return providerStatus().every((x) => x.configured) && process.env.LIVE_TRADING_ENABLED === "true"; const requiredConfigured = providerStatus().every((x) => x.configured);
const liveFlagEnabled = process.env.LIVE_TRADING_ENABLED === "true";
const signatureTypeOk = String(process.env.POLYMARKET_SIGNATURE_TYPE || "") === "3";
const chainOk = String(process.env.POLYMARKET_CHAIN_ID || "137") === "137";
return requiredConfigured && liveFlagEnabled && signatureTypeOk && chainOk;
} }
function baseStatus() { function baseStatus() {
const providers = providerStatus(); const providers = providerStatus();
const liveFlagEnabled = process.env.LIVE_TRADING_ENABLED === "true";
const signatureTypeOk = String(process.env.POLYMARKET_SIGNATURE_TYPE || "") === "3";
const chainOk = String(process.env.POLYMARKET_CHAIN_ID || "137") === "137";
const missing = providers.filter((x) => !x.configured).map((x) => x.label);
if (!liveFlagEnabled) missing.push("LIVE_TRADING_ENABLED=true after final approval");
if (!signatureTypeOk) missing.push("POLYMARKET_SIGNATURE_TYPE=3 for deposit-wallet users");
if (!chainOk) missing.push("POLYMARKET_CHAIN_ID=137");
return { return {
ok: true, ok: true,
live_trading_enabled: liveTradingReady(), live_trading_enabled: liveTradingReady(),
live_flag_enabled: liveFlagEnabled,
signature_type_ok: signatureTypeOk,
chain_ok: chainOk,
locked_reason: liveTradingReady() locked_reason: liveTradingReady()
? null ? null
: "Live trading is locked until KYC, payments, wallet signing, CLOB credentials, audit storage, and LIVE_TRADING_ENABLED=true are configured.", : "Live trading is locked until eligibility, payments, wallet/deposit-wallet signing, Polymarket CLOB credentials, audit storage, monitoring, and LIVE_TRADING_ENABLED=true are configured.",
providers, providers,
next_required: providers.filter((x) => !x.configured).map((x) => x.label), next_required: missing,
docs: {
polymarket_overview: "https://docs.polymarket.com/trading/overview",
polymarket_quickstart: "https://docs.polymarket.com/trading/quickstart",
deposit_wallets: "https://docs.polymarket.com/trading/deposit-wallets",
},
}; };
} }
+3 -2
View File
@@ -2259,8 +2259,9 @@ function renderLiveBackendStatus(){
const root=$("liveBackendStatus");if(!root)return; const root=$("liveBackendStatus");if(!root)return;
const s=LIVE_BACKEND_STATUS; const s=LIVE_BACKEND_STATUS;
if(!s){root.innerHTML="Checking backend readiness...";return;} if(!s){root.innerHTML="Checking backend readiness...";return;}
const providers=(s.providers||[]).map(p=>`<div class="leader-row"><span>${esc(p.label)}</span><b class="${p.configured?"pos-val":"neg-val"}">${p.configured?"configured":"missing"}</b></div>`).join(""); const providers=(s.providers||[]).map(p=>`<div class="leader-row"><span>${esc(p.label)}${p.expected?`<div class="small muted">${esc(p.expected)}</div>`:""}</span><b class="${p.configured?"pos-val":"neg-val"}">${p.configured?"configured":"missing"}</b></div>`).join("");
root.innerHTML=`<b>${s.live_trading_enabled?"Live trading unlocked":"Live trading locked"}</b><div class="small muted" style="margin:6px 0">${esc(s.locked_reason||"All required backend providers are configured.")}</div>${providers}`; const next=(s.next_required||[]).slice(0,8).map(x=>`<li>${esc(x)}</li>`).join("");
root.innerHTML=`<b>${s.live_trading_enabled?"Live trading unlocked":"Live trading locked"}</b><div class="small muted" style="margin:6px 0">${esc(s.locked_reason||"All required backend providers are configured.")}</div>${providers}${next?`<div class="locked-panel" style="margin-top:10px"><b>Next setup items</b><ul>${next}</ul></div>`:""}<div class="small muted" style="margin-top:8px">Live execution stays disabled until every provider is configured and final approval flips LIVE_TRADING_ENABLED=true.</div>`;
} }
async function dryRunLiveOrderIntent(){ async function dryRunLiveOrderIntent(){
const s=loadLiveState(); const s=loadLiveState();