mirror of
https://github.com/theodore-song/polymarket-analyst.git
synced 2026-07-28 16:37:45 +00:00
165 lines
7.6 KiB
JavaScript
165 lines
7.6 KiB
JavaScript
const REQUIRED_ENV = [
|
|
["PRODUCTION_APP_URL", "Production app URL"],
|
|
["AUTH_PROVIDER", "Real account authentication provider"],
|
|
["DATABASE_URL", "Production account database"],
|
|
["SESSION_SECRET", "Session signing secret"],
|
|
["ENCRYPTION_KEY", "Encryption key for sensitive user settings"],
|
|
["KYC_PROVIDER", "KYC / eligibility provider"],
|
|
["KYC_API_KEY", "KYC provider API key"],
|
|
["KYC_WEBHOOK_SECRET", "KYC webhook secret"],
|
|
["SANCTIONS_PROVIDER", "Sanctions / watchlist screening provider"],
|
|
["SANCTIONS_API_KEY", "Sanctions provider API key"],
|
|
["GEOIP_PROVIDER", "Geo-IP / geofencing provider"],
|
|
["GEOIP_API_KEY", "Geo-IP provider API key"],
|
|
["RESTRICTED_JURISDICTIONS", "Restricted jurisdiction rules"],
|
|
["TERMS_VERSION", "Approved terms version"],
|
|
["PRIVACY_VERSION", "Approved privacy policy version"],
|
|
["RISK_DISCLOSURE_VERSION", "Approved risk disclosure version"],
|
|
["MARKET_POLICY_STORE", "Market eligibility and category policy store"],
|
|
["PAYMENTS_PROVIDER", "Deposit and withdrawal provider"],
|
|
["PAYMENTS_API_KEY", "Payments provider API key"],
|
|
["PAYMENTS_WEBHOOK_SECRET", "Payments webhook secret"],
|
|
["WEBHOOK_BASE_URL", "Public webhook base URL"],
|
|
["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_SECRET", "Polymarket CLOB API secret"],
|
|
["POLYMARKET_CLOB_PASSPHRASE", "Polymarket CLOB passphrase"],
|
|
["SETTLEMENT_ASSET", "Settlement asset"],
|
|
["SETTLEMENT_CHAIN", "Settlement chain"],
|
|
["RECONCILIATION_STORE", "Balance, position, and fill reconciliation store"],
|
|
["ACCOUNTING_EXPORT_STORE", "Statements, tax, and accounting export store"],
|
|
["RATE_LIMIT_STORE", "Rate limit and abuse prevention store"],
|
|
["AUDIT_LOG_STORE", "Durable audit log store"],
|
|
["MONITORING_DSN", "Error and performance monitoring"],
|
|
["INCIDENT_WEBHOOK_URL", "Incident alert webhook"],
|
|
["ADMIN_ALERT_EMAIL", "Admin alert destination"],
|
|
["CUSTOMER_SUPPORT_EMAIL", "Customer support contact"],
|
|
];
|
|
|
|
const LAUNCH_REQUIREMENTS = [
|
|
["legal", "Terms, privacy policy, risk disclosures, and jurisdiction policy approved"],
|
|
["eligibility", "KYC/KYB, age, sanctions, watchlist, and location screening active"],
|
|
["market_policy", "Allowed market categories, restricted events, and manipulation rules defined"],
|
|
["auth", "Production accounts, sessions, password reset, MFA path, and sign-out implemented"],
|
|
["wallets", "User-controlled wallet/deposit-wallet signing and permission revocation implemented"],
|
|
["payments", "Deposits, withdrawals, webhooks, failed-payment handling, and support flows implemented"],
|
|
["execution", "Signed order creation, CLOB submission, cancellation, retry, and fill tracking implemented"],
|
|
["reconciliation", "Cash, open orders, positions, fills, fees, and withdrawals reconciled continuously"],
|
|
["risk", "Agent allocation caps, market caps, stop rules, manual approval, and emergency pause enforced server-side"],
|
|
["security", "Secret management, encryption, rate limits, abuse controls, and security review completed"],
|
|
["records", "Append-only audit logs, customer statements, exports, and retention policy active"],
|
|
["operations", "Monitoring, incident response, customer support, and rollback plan ready"],
|
|
["testing", "Paper-to-live parity, dry-runs, test wallets, webhook tests, and edge-case QA completed"],
|
|
];
|
|
|
|
function providerStatus() {
|
|
return REQUIRED_ENV.map(([key, label]) => ({
|
|
key,
|
|
label,
|
|
configured: Boolean(process.env[key]),
|
|
expected: key === "POLYMARKET_SIGNATURE_TYPE" ? "3 for new deposit-wallet API users" : undefined,
|
|
}));
|
|
}
|
|
|
|
function liveTradingReady() {
|
|
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() {
|
|
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 {
|
|
ok: true,
|
|
live_trading_enabled: liveTradingReady(),
|
|
live_flag_enabled: liveFlagEnabled,
|
|
signature_type_ok: signatureTypeOk,
|
|
chain_ok: chainOk,
|
|
locked_reason: liveTradingReady()
|
|
? 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,
|
|
launch_requirements: LAUNCH_REQUIREMENTS.map(([key, label]) => ({ key, 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",
|
|
},
|
|
};
|
|
}
|
|
|
|
function validateIntent(intent) {
|
|
const missing = [];
|
|
["user_id", "wallet_address", "agent_id", "market_id", "side", "max_amount"].forEach((key) => {
|
|
if (!intent || intent[key] === undefined || intent[key] === null || intent[key] === "") missing.push(key);
|
|
});
|
|
if (intent && !["YES", "NO"].includes(String(intent.side).toUpperCase())) missing.push("side must be YES or NO");
|
|
if (intent && Number(intent.max_amount) <= 0) missing.push("max_amount must be positive");
|
|
return missing;
|
|
}
|
|
|
|
export default async function handler(req, res) {
|
|
res.setHeader("Cache-Control", "no-store");
|
|
|
|
if (req.method === "GET") {
|
|
return res.status(200).json(baseStatus());
|
|
}
|
|
|
|
if (req.method === "POST") {
|
|
const body = typeof req.body === "string" ? JSON.parse(req.body || "{}") : (req.body || {});
|
|
const intent = body.intent || body;
|
|
const errors = validateIntent(intent);
|
|
if (errors.length) {
|
|
return res.status(400).json({ ok: false, error: "Invalid order intent", details: errors });
|
|
}
|
|
|
|
const status = baseStatus();
|
|
const auditEvent = {
|
|
at: new Date().toISOString(),
|
|
type: "ORDER_INTENT_DRY_RUN",
|
|
user_id: String(intent.user_id),
|
|
wallet_address: String(intent.wallet_address),
|
|
agent_id: String(intent.agent_id),
|
|
market_id: String(intent.market_id),
|
|
side: String(intent.side).toUpperCase(),
|
|
max_amount: Number(intent.max_amount),
|
|
execution_mode: intent.execution_mode || "manual_approval",
|
|
};
|
|
|
|
if (!status.live_trading_enabled) {
|
|
return res.status(423).json({
|
|
ok: false,
|
|
dry_run: true,
|
|
error: status.locked_reason,
|
|
audit_event: auditEvent,
|
|
status,
|
|
});
|
|
}
|
|
|
|
return res.status(501).json({
|
|
ok: false,
|
|
dry_run: true,
|
|
error: "Live trading credentials are configured, but signed order placement is intentionally not implemented yet.",
|
|
audit_event: auditEvent,
|
|
status,
|
|
});
|
|
}
|
|
|
|
res.setHeader("Allow", "GET, POST");
|
|
return res.status(405).json({ ok: false, error: "Method not allowed" });
|
|
}
|