2026-07-12 16:50:03 -04:00
const REQUIRED_ENV = [
[ "KYC_PROVIDER" , "KYC / eligibility provider" ],
2026-07-12 16:58:10 -04:00
[ "KYC_API_KEY" , "KYC provider API key" ],
[ "KYC_WEBHOOK_SECRET" , "KYC webhook secret" ],
2026-07-12 16:50:03 -04:00
[ "PAYMENTS_PROVIDER" , "Deposit and withdrawal provider" ],
2026-07-12 16:58:10 -04:00
[ "PAYMENTS_API_KEY" , "Payments provider API key" ],
[ "PAYMENTS_WEBHOOK_SECRET" , "Payments webhook secret" ],
2026-07-12 16:50:03 -04:00
[ "WALLET_PROVIDER" , "Wallet or deposit-wallet provider" ],
2026-07-12 16:58:10 -04:00
[ "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" ],
2026-07-12 16:50:03 -04:00
[ "POLYMARKET_CLOB_API_KEY" , "Polymarket CLOB API key" ],
[ "POLYMARKET_CLOB_SECRET" , "Polymarket CLOB API secret" ],
[ "POLYMARKET_CLOB_PASSPHRASE" , "Polymarket CLOB passphrase" ],
2026-07-12 16:58:10 -04:00
[ "SETTLEMENT_ASSET" , "Settlement asset" ],
[ "SETTLEMENT_CHAIN" , "Settlement chain" ],
2026-07-12 16:50:03 -04:00
[ "AUDIT_LOG_STORE" , "Durable audit log store" ],
2026-07-12 16:58:10 -04:00
[ "ADMIN_ALERT_EMAIL" , "Admin alert destination" ],
2026-07-12 16:50:03 -04:00
];
function providerStatus () {
return REQUIRED_ENV . map (([ key , label ]) => ({
key ,
label ,
configured : Boolean ( process . env [ key ]),
2026-07-12 16:58:10 -04:00
expected : key === "POLYMARKET_SIGNATURE_TYPE" ? "3 for new deposit-wallet API users" : undefined ,
2026-07-12 16:50:03 -04:00
}));
}
function liveTradingReady () {
2026-07-12 16:58:10 -04:00
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 ;
2026-07-12 16:50:03 -04:00
}
function baseStatus () {
const providers = providerStatus ();
2026-07-12 16:58:10 -04:00
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" );
2026-07-12 16:50:03 -04:00
return {
ok : true ,
live_trading_enabled : liveTradingReady (),
2026-07-12 16:58:10 -04:00
live_flag_enabled : liveFlagEnabled ,
signature_type_ok : signatureTypeOk ,
chain_ok : chainOk ,
2026-07-12 16:50:03 -04:00
locked_reason : liveTradingReady ()
? null
2026-07-12 16:58:10 -04:00
: "Live trading is locked until eligibility, payments, wallet/deposit-wallet signing, Polymarket CLOB credentials, audit storage, monitoring, and LIVE_TRADING_ENABLED=true are configured." ,
2026-07-12 16:50:03 -04:00
providers ,
2026-07-12 16:58:10 -04:00
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" ,
},
2026-07-12 16:50:03 -04:00
};
}
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" });
}