mirror of
https://github.com/theodore-song/polymarket-analyst.git
synced 2026-07-28 00:17:46 +00:00
Add selected provider stack setup
This commit is contained in:
+21
-6
@@ -9,20 +9,27 @@ PRODUCTION_APP_URL=https://polymarket-site-eta.vercel.app
|
||||
LIVE_TRADING_ENABLED=false
|
||||
|
||||
# Accounts / authentication
|
||||
AUTH_PROVIDER=
|
||||
AUTH_PROVIDER=Clerk
|
||||
CLERK_PUBLISHABLE_KEY=
|
||||
CLERK_SECRET_KEY=
|
||||
CLERK_WEBHOOK_SIGNING_SECRET=
|
||||
DATABASE_URL=
|
||||
NEON_DATABASE_URL=
|
||||
SESSION_SECRET=
|
||||
ACCOUNT_SESSION_SECRET=
|
||||
ENCRYPTION_KEY=
|
||||
PMA_ACCOUNTS_INDEX_PATH=accounts/index.json
|
||||
|
||||
# Eligibility / compliance provider
|
||||
KYC_PROVIDER=
|
||||
KYC_PROVIDER=Veriff
|
||||
KYC_API_KEY=
|
||||
KYC_WEBHOOK_SECRET=
|
||||
SANCTIONS_PROVIDER=
|
||||
VERIFF_API_KEY=
|
||||
VERIFF_SECRET_KEY=
|
||||
VERIFF_WEBHOOK_SECRET=
|
||||
SANCTIONS_PROVIDER=Veriff
|
||||
SANCTIONS_API_KEY=
|
||||
GEOIP_PROVIDER=
|
||||
GEOIP_PROVIDER=Veriff
|
||||
GEOIP_API_KEY=
|
||||
RESTRICTED_JURISDICTIONS=US,CA-NY,CA-ON
|
||||
TERMS_VERSION=
|
||||
@@ -31,16 +38,19 @@ RISK_DISCLOSURE_VERSION=
|
||||
MARKET_POLICY_STORE=
|
||||
|
||||
# Wallet / deposit-wallet provider
|
||||
WALLET_PROVIDER=
|
||||
WALLET_PROVIDER=Circle
|
||||
WALLET_PROJECT_ID=
|
||||
WALLET_API_KEY=
|
||||
CIRCLE_WALLET_SET_ID=
|
||||
DEPOSIT_WALLET_ADDRESS=
|
||||
POLYMARKET_SIGNATURE_TYPE=3
|
||||
|
||||
# Payments / deposits / withdrawals
|
||||
PAYMENTS_PROVIDER=
|
||||
PAYMENTS_PROVIDER=Circle
|
||||
PAYMENTS_API_KEY=
|
||||
PAYMENTS_WEBHOOK_SECRET=
|
||||
CIRCLE_API_KEY=
|
||||
CIRCLE_WEBHOOK_SECRET=
|
||||
WEBHOOK_BASE_URL=
|
||||
SETTLEMENT_ASSET=pUSD
|
||||
SETTLEMENT_CHAIN=polygon
|
||||
@@ -58,6 +68,11 @@ ACCOUNTING_EXPORT_STORE=
|
||||
RATE_LIMIT_STORE=
|
||||
AUDIT_LOG_STORE=
|
||||
MONITORING_DSN=
|
||||
SENTRY_DSN=
|
||||
SENTRY_AUTH_TOKEN=
|
||||
SENTRY_ORG=
|
||||
SENTRY_PROJECT=
|
||||
SENTRY_WEBHOOK_URL=
|
||||
INCIDENT_WEBHOOK_URL=
|
||||
ADMIN_ALERT_EMAIL=
|
||||
CUSTOMER_SUPPORT_EMAIL=
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# Provider Setup
|
||||
|
||||
You signed up with `emplusbodyworks@gmail.com` for the selected launch stack:
|
||||
|
||||
- Clerk for authentication.
|
||||
- Neon for Postgres storage.
|
||||
- Veriff for KYC / KYB / AML workflow.
|
||||
- Circle for USDC, wallets, deposits, withdrawals, and webhooks.
|
||||
- Sentry for monitoring.
|
||||
|
||||
The app is wired for these providers, but private credentials must be copied
|
||||
from each provider dashboard into Vercel environment variables. Never commit
|
||||
real credentials to GitHub.
|
||||
|
||||
## Vercel environment variables to add
|
||||
|
||||
### Clerk
|
||||
|
||||
- `AUTH_PROVIDER=Clerk`
|
||||
- `CLERK_PUBLISHABLE_KEY`
|
||||
- `CLERK_SECRET_KEY`
|
||||
- `CLERK_WEBHOOK_SIGNING_SECRET`
|
||||
|
||||
Clerk docs list `CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY`, and
|
||||
`CLERK_WEBHOOK_SIGNING_SECRET` for app connection and webhook verification.
|
||||
|
||||
### Neon
|
||||
|
||||
- `DATABASE_URL`
|
||||
- `NEON_DATABASE_URL` optional alias; use the same value as `DATABASE_URL`
|
||||
|
||||
Use the pooled Neon connection string when possible. Neon connection strings
|
||||
look like `postgresql://...neon.tech/dbname?sslmode=require...`.
|
||||
|
||||
### Veriff
|
||||
|
||||
- `KYC_PROVIDER=Veriff`
|
||||
- `SANCTIONS_PROVIDER=Veriff`
|
||||
- `GEOIP_PROVIDER=Veriff`
|
||||
- `VERIFF_API_KEY`
|
||||
- `VERIFF_SECRET_KEY`
|
||||
- `VERIFF_WEBHOOK_SECRET`
|
||||
|
||||
The app treats Veriff as the selected KYC provider. Actual verification-session
|
||||
creation is still locked until the final compliance flow is approved.
|
||||
|
||||
### Circle
|
||||
|
||||
- `PAYMENTS_PROVIDER=Circle`
|
||||
- `WALLET_PROVIDER=Circle`
|
||||
- `CIRCLE_API_KEY`
|
||||
- `CIRCLE_WEBHOOK_SECRET`
|
||||
- `CIRCLE_WALLET_SET_ID`
|
||||
|
||||
Circle is wired as the selected payments/wallet provider. Live deposits,
|
||||
withdrawals, and wallet execution remain disabled until compliance, wallet
|
||||
signing, reconciliation, and testing are complete.
|
||||
|
||||
### Sentry
|
||||
|
||||
- `MONITORING_DSN`
|
||||
- `SENTRY_DSN`
|
||||
- `SENTRY_AUTH_TOKEN`
|
||||
- `SENTRY_ORG`
|
||||
- `SENTRY_PROJECT`
|
||||
- `SENTRY_WEBHOOK_URL`
|
||||
|
||||
`SENTRY_DSN` is exposed to the browser through `/api/config` so the frontend can
|
||||
initialize Sentry. Keep auth tokens server-side only.
|
||||
|
||||
## Already safe to set
|
||||
|
||||
- `ADMIN_ALERT_EMAIL=emplusbodyworks@gmail.com`
|
||||
- `CUSTOMER_SUPPORT_EMAIL=emplusbodyworks@gmail.com`
|
||||
- `PRODUCTION_APP_URL=https://polymarket-site-eta.vercel.app`
|
||||
- `RESTRICTED_JURISDICTIONS=US,CA-NY,CA-ON`
|
||||
- `LIVE_TRADING_ENABLED=false`
|
||||
|
||||
## What is still intentionally locked
|
||||
|
||||
- Real deposits.
|
||||
- Real withdrawals.
|
||||
- Live Polymarket order placement.
|
||||
- Automatic agent trading with real funds.
|
||||
|
||||
Those should stay locked until legal review, eligibility checks, signed-order
|
||||
execution, reconciliation, audit logs, and incident response are complete.
|
||||
@@ -47,6 +47,8 @@ Use `.env.example` as the setup template.
|
||||
- `ACCOUNT_SESSION_SECRET` signs cloud paper-account sessions. If omitted, the
|
||||
app falls back to the existing server secret/token, but production should use
|
||||
a dedicated value.
|
||||
- `PROVIDER_SETUP.md` maps the current stack — Clerk, Neon, Veriff, Circle, and
|
||||
Sentry — to the exact Vercel environment variables still needed.
|
||||
- `/api/live` reports whether KYC, payments, wallet/deposit-wallet, Polymarket
|
||||
CLOB, authentication, geofencing, sanctions, audit, support, and monitoring
|
||||
providers are configured.
|
||||
|
||||
@@ -11,6 +11,10 @@ interfaces visible without moving funds or placing orders.
|
||||
- `/api/live` accepts order intents only as dry-runs and returns an audit event.
|
||||
- `/api/accounts` creates, logs into, and saves password-backed paper accounts
|
||||
through Vercel Blob storage.
|
||||
- `/api/config` exposes safe public provider configuration, including the
|
||||
Sentry browser DSN and Clerk publishable key once configured.
|
||||
- `/api/live` now checks the selected provider stack: Clerk, Neon, Veriff,
|
||||
Circle, and Sentry.
|
||||
- The Live Money tab shows launch checks, wallet/deposit settings, agent risk
|
||||
limits, a dry-run order console, and an audit preview.
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
export default async function handler(req, res) {
|
||||
res.setHeader("Cache-Control", "no-store");
|
||||
if (req.method !== "GET") {
|
||||
res.setHeader("Allow", "GET");
|
||||
return res.status(405).json({ ok: false, error: "Method not allowed" });
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
ok: true,
|
||||
providers: {
|
||||
auth: process.env.AUTH_PROVIDER || "Clerk",
|
||||
database: process.env.DATABASE_PROVIDER || "Neon",
|
||||
kyc: process.env.KYC_PROVIDER || "Veriff",
|
||||
payments: process.env.PAYMENTS_PROVIDER || "Circle",
|
||||
wallet: process.env.WALLET_PROVIDER || "Circle",
|
||||
monitoring: process.env.MONITORING_PROVIDER || "Sentry",
|
||||
},
|
||||
public: {
|
||||
clerk_publishable_key: process.env.CLERK_PUBLISHABLE_KEY || "",
|
||||
sentry_dsn: process.env.SENTRY_DSN || process.env.MONITORING_DSN || "",
|
||||
},
|
||||
});
|
||||
}
|
||||
+70
-1
@@ -40,6 +40,57 @@ const REQUIRED_ENV = [
|
||||
["CUSTOMER_SUPPORT_EMAIL", "Customer support contact"],
|
||||
];
|
||||
|
||||
const ENV_ALIASES = {
|
||||
DATABASE_URL: ["NEON_DATABASE_URL"],
|
||||
KYC_API_KEY: ["VERIFF_API_KEY"],
|
||||
KYC_WEBHOOK_SECRET: ["VERIFF_WEBHOOK_SECRET", "VERIFF_SECRET_KEY"],
|
||||
SANCTIONS_API_KEY: ["VERIFF_API_KEY"],
|
||||
GEOIP_API_KEY: ["VERIFF_API_KEY"],
|
||||
PAYMENTS_API_KEY: ["CIRCLE_API_KEY"],
|
||||
PAYMENTS_WEBHOOK_SECRET: ["CIRCLE_WEBHOOK_SECRET"],
|
||||
WALLET_PROJECT_ID: ["CIRCLE_WALLET_SET_ID"],
|
||||
WALLET_API_KEY: ["CIRCLE_API_KEY"],
|
||||
RECONCILIATION_STORE: ["DATABASE_URL", "NEON_DATABASE_URL"],
|
||||
ACCOUNTING_EXPORT_STORE: ["DATABASE_URL", "NEON_DATABASE_URL"],
|
||||
RATE_LIMIT_STORE: ["DATABASE_URL", "NEON_DATABASE_URL"],
|
||||
AUDIT_LOG_STORE: ["DATABASE_URL", "NEON_DATABASE_URL"],
|
||||
MONITORING_DSN: ["SENTRY_DSN"],
|
||||
INCIDENT_WEBHOOK_URL: ["SENTRY_WEBHOOK_URL"],
|
||||
};
|
||||
|
||||
const PROVIDER_STACK = [
|
||||
{
|
||||
provider: "Clerk",
|
||||
role: "User auth, sessions, sign-in/sign-out, account identity",
|
||||
env: ["AUTH_PROVIDER", "CLERK_PUBLISHABLE_KEY", "CLERK_SECRET_KEY", "CLERK_WEBHOOK_SIGNING_SECRET"],
|
||||
dashboard: "https://dashboard.clerk.com/",
|
||||
},
|
||||
{
|
||||
provider: "Neon",
|
||||
role: "Postgres database for users, portfolios, audit metadata, reconciliation, and history",
|
||||
env: ["DATABASE_URL"],
|
||||
dashboard: "https://console.neon.tech/",
|
||||
},
|
||||
{
|
||||
provider: "Veriff",
|
||||
role: "KYC/KYB, identity verification, age checks, AML/sanctions workflow",
|
||||
env: ["KYC_PROVIDER", "VERIFF_API_KEY", "VERIFF_SECRET_KEY", "VERIFF_WEBHOOK_SECRET"],
|
||||
dashboard: "https://station.veriff.com/",
|
||||
},
|
||||
{
|
||||
provider: "Circle",
|
||||
role: "USDC, embedded wallets, payments/deposits/withdrawals, webhooks",
|
||||
env: ["PAYMENTS_PROVIDER", "WALLET_PROVIDER", "CIRCLE_API_KEY", "CIRCLE_WEBHOOK_SECRET", "CIRCLE_WALLET_SET_ID"],
|
||||
dashboard: "https://console.circle.com/",
|
||||
},
|
||||
{
|
||||
provider: "Sentry",
|
||||
role: "Error monitoring, browser/backend issue tracking, incident visibility",
|
||||
env: ["MONITORING_DSN", "SENTRY_DSN"],
|
||||
dashboard: "https://sentry.io/",
|
||||
},
|
||||
];
|
||||
|
||||
const LAUNCH_REQUIREMENTS = [
|
||||
["legal", "Terms, privacy policy, risk disclosures, and jurisdiction policy approved"],
|
||||
["eligibility", "KYC/KYB, age, sanctions, watchlist, and location screening active"],
|
||||
@@ -56,15 +107,32 @@ const LAUNCH_REQUIREMENTS = [
|
||||
["testing", "Paper-to-live parity, dry-runs, test wallets, webhook tests, and edge-case QA completed"],
|
||||
];
|
||||
|
||||
function envValue(key) {
|
||||
if (process.env[key]) return process.env[key];
|
||||
return (ENV_ALIASES[key] || []).map((alias) => process.env[alias]).find(Boolean);
|
||||
}
|
||||
|
||||
function providerStatus() {
|
||||
return REQUIRED_ENV.map(([key, label]) => ({
|
||||
key,
|
||||
label,
|
||||
configured: Boolean(process.env[key]),
|
||||
configured: Boolean(envValue(key)),
|
||||
aliases: ENV_ALIASES[key] || [],
|
||||
expected: key === "POLYMARKET_SIGNATURE_TYPE" ? "3 for new deposit-wallet API users" : undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
function stackStatus() {
|
||||
return PROVIDER_STACK.map((provider) => {
|
||||
const checks = provider.env.map((key) => ({ key, configured: Boolean(envValue(key)) }));
|
||||
return {
|
||||
...provider,
|
||||
configured: checks.every((x) => x.configured),
|
||||
checks,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function liveTradingReady() {
|
||||
const requiredConfigured = providerStatus().every((x) => x.configured);
|
||||
const liveFlagEnabled = process.env.LIVE_TRADING_ENABLED === "true";
|
||||
@@ -92,6 +160,7 @@ function baseStatus() {
|
||||
? null
|
||||
: "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,
|
||||
provider_stack: stackStatus(),
|
||||
launch_requirements: LAUNCH_REQUIREMENTS.map(([key, label]) => ({ key, label })),
|
||||
next_required: missing,
|
||||
docs: {
|
||||
|
||||
+19
-1
@@ -683,6 +683,7 @@ let PAPER_SEARCH_RESULTS = null;
|
||||
let PAPER_MARKET_OFFSET = 0;
|
||||
let PAPER_MARKET_QUERY = "";
|
||||
let LIVE_BACKEND_STATUS = null;
|
||||
let PROVIDER_CONFIG = null;
|
||||
const catColor = (c) => CAT_COLORS[c] || CAT_COLORS.Other;
|
||||
const getFocus = () => localStorage.getItem(FOCUS_KEY) || "All";
|
||||
const setFocus = (c) => localStorage.setItem(FOCUS_KEY, c);
|
||||
@@ -702,6 +703,21 @@ function applyPersonalMode(){
|
||||
});
|
||||
}
|
||||
|
||||
async function initProviderConfig(){
|
||||
try{
|
||||
const r=await fetch("/api/config",{cache:"no-store"});
|
||||
PROVIDER_CONFIG=await r.json();
|
||||
const dsn=PROVIDER_CONFIG&&PROVIDER_CONFIG.public&&PROVIDER_CONFIG.public.sentry_dsn;
|
||||
if(dsn&&!window.Sentry){
|
||||
const script=document.createElement("script");
|
||||
script.src="https://browser.sentry-cdn.com/8.55.0/bundle.tracing.min.js";
|
||||
script.crossOrigin="anonymous";
|
||||
script.onload=()=>{if(window.Sentry)window.Sentry.init({dsn,tracesSampleRate:0.1,environment:"production"});};
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
}catch(e){}
|
||||
}
|
||||
|
||||
/* ---------- the ten competing agents ----------
|
||||
Each shares the same hourly suggestions but ranks/sizes them differently. */
|
||||
const AGENTS = [
|
||||
@@ -2339,9 +2355,10 @@ function renderLiveBackendStatus(){
|
||||
const s=LIVE_BACKEND_STATUS;
|
||||
if(!s){root.innerHTML="Checking backend readiness...";return;}
|
||||
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("");
|
||||
const stack=(s.provider_stack||[]).map(p=>`<div class="leader-row"><span><b>${esc(p.provider)}</b><div class="small muted">${esc(p.role)}</div><div class="small muted">${(p.checks||[]).map(c=>`${esc(c.key)}: ${c.configured?"ok":"missing"}`).join(" · ")}</div></span><b class="${p.configured?"pos-val":"neg-val"}">${p.configured?"ready":"needs keys"}</b></div>`).join("");
|
||||
const next=(s.next_required||[]).slice(0,12).map(x=>`<li>${esc(x)}</li>`).join("");
|
||||
const reqs=(s.launch_requirements||[]).map(x=>`<li>${esc(x.label)}</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>`:""}${reqs?`<div class="locked-panel" style="margin-top:10px"><b>Full launch requirements</b><ol>${reqs}</ol></div>`:""}<div class="small muted" style="margin-top:8px">Live execution stays disabled until every provider is configured, every launch requirement is complete, and final approval flips LIVE_TRADING_ENABLED=true.</div>`;
|
||||
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>${stack?`<div class="locked-panel" style="margin-top:10px"><b>Your provider stack</b>${stack}</div>`:""}${providers}${next?`<div class="locked-panel" style="margin-top:10px"><b>Next setup items</b><ul>${next}</ul></div>`:""}${reqs?`<div class="locked-panel" style="margin-top:10px"><b>Full launch requirements</b><ol>${reqs}</ol></div>`:""}<div class="small muted" style="margin-top:8px">Live execution stays disabled until every provider is configured, every launch requirement is complete, and final approval flips LIVE_TRADING_ENABLED=true.</div>`;
|
||||
}
|
||||
async function dryRunLiveOrderIntent(){
|
||||
const s=loadLiveState();
|
||||
@@ -2411,6 +2428,7 @@ window.addEventListener("hashchange",()=>showTab(location.hash.slice(1)));
|
||||
|
||||
/* ---------- Wire up ---------- */
|
||||
applyPersonalMode();
|
||||
initProviderConfig();
|
||||
$("runBtn").addEventListener("click",async()=>{
|
||||
const btn=$("runBtn");btn.disabled=true;btn.textContent="Running…";
|
||||
try{const r=await runDailyCycle();const b=board();toast(r.skipped?`Already ran this hour · ${b[0].c.emoji} ${b[0].c.name} leads`:`Cycle done · ${r.suggestions} ideas · ${b[0].c.emoji} ${b[0].c.name} leads`);renderAll();}
|
||||
|
||||
Reference in New Issue
Block a user