import { Pool } from 'pg'; import { deriveIntentRequestOutcomeRecords } from '../core/intent-request-outcomes.mjs'; import { buildCashEquivalentValuationAssets } from '../core/portfolio-metrics.mjs'; import { deriveQuoteOutcomeRecords } from '../core/quote-outcomes.mjs'; import { CURRENT_NBTC_ASSET_ID, CURRENT_USDC_ASSET_ID, ONE_CLICK_TOKENS_URL, buildBtcUsdcPriceRoute, buildSeedAssets, buildSeedPairs, buildSeedPriceRoute, buildSeedStrategyConfig, hashJson, normalizeOneClickTokenResponse, normalizePairMode, pairCanMake, pairCanObserve, pairCanTake, } from '../core/trading-config.mjs'; const TABLES = [ 'raw_near_intents_quotes', 'swap_demand_events', 'market_price_events', 'intent_inventory_snapshots', 'liquidity_actions', 'funding_observations', 'ops_alerts', 'environment_status_events', 'trade_decisions', 'execute_trade_commands', 'trade_execution_results', 'intent_request_preflights', 'intent_request_submission_results', ]; const PORTFOLIO_METRICS_TABLE = 'portfolio_metrics_snapshots'; const QUOTE_OUTCOMES_TABLE = 'quote_outcome_attributions'; const INTENT_REQUEST_OUTCOMES_TABLE = 'intent_request_outcomes'; const SUPPORTED_ASSET_IMPORT_RUNS_TABLE = 'supported_asset_import_runs'; const TRADING_ASSETS_TABLE = 'trading_assets'; const TRADING_PAIRS_TABLE = 'trading_pairs'; const PAIR_STRATEGY_CONFIGS_TABLE = 'pair_strategy_configs'; const PAIR_PRICE_ROUTES_TABLE = 'pair_price_routes'; const PAIR_CONFIG_AUDIT_LOG_TABLE = 'pair_config_audit_log'; const CREDITED_LIQUIDITY_STATUSES = ['CREDITED', 'COMPLETED', 'FINALIZED', 'SETTLED']; const COMPLETED_WITHDRAWAL_STATUSES = ['COMPLETED', 'FINALIZED', 'SETTLED']; const REFRESHABLE_INTENT_REQUEST_OUTCOME_STATUSES = [ 'draft', 'submitted', 'accepted_by_relay', 'awaiting_settlement', ]; export function createPostgresPool({ connectionString }) { return new Pool({ connectionString, }); } async function withTransaction(pool, operation) { if (typeof pool.connect !== 'function') return operation(pool); const client = await pool.connect(); try { await client.query('BEGIN'); const result = await operation(client); await client.query('COMMIT'); return result; } catch (error) { try { await client.query('ROLLBACK'); } catch { // Preserve the original transaction failure. } throw error; } finally { client.release(); } } export async function ensureHistorySchema(pool) { await ensureTradingConfigSchema(pool); for (const table of TABLES) { await pool.query(` CREATE TABLE IF NOT EXISTS ${table} ( event_id TEXT PRIMARY KEY, topic TEXT NOT NULL, venue TEXT NOT NULL, source TEXT, event_type TEXT NOT NULL, observed_at TIMESTAMPTZ, ingested_at TIMESTAMPTZ NOT NULL, quote_id TEXT, pair TEXT, decision_key TEXT, payload JSONB NOT NULL, raw JSONB ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${table}_quote_id_idx ON ${table} (quote_id) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${table}_decision_key_idx ON ${table} (decision_key) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${table}_ingested_at_idx ON ${table} (ingested_at DESC) `); } await ensureExpressionIndex(pool, { name: 'funding_observations_tx_hash_idx', table: 'funding_observations', expression: "(payload->>'tx_hash')", }); await ensureExpressionIndex(pool, { name: 'funding_observations_handle_idx', table: 'funding_observations', expression: "(payload->>'funding_handle')", }); await ensureExpressionIndex(pool, { name: 'funding_observations_asset_id_idx', table: 'funding_observations', expression: "(payload->>'asset_id')", }); await ensureExpressionIndex(pool, { name: 'funding_observations_chain_idx', table: 'funding_observations', expression: "(payload->>'chain')", }); await ensureExpressionIndex(pool, { name: 'funding_observations_status_idx', table: 'funding_observations', expression: "(payload->>'status')", }); await ensureExpressionIndex(pool, { name: 'ops_alerts_alert_code_idx', table: 'ops_alerts', expression: "(payload->>'alert_code')", }); await ensureExpressionIndex(pool, { name: 'ops_alerts_status_idx', table: 'ops_alerts', expression: "(payload->>'status')", }); await ensureExpressionIndex(pool, { name: 'ops_alerts_asset_id_idx', table: 'ops_alerts', expression: "(payload->>'asset_id')", }); await ensureExpressionIndex(pool, { name: 'environment_status_events_key_idx', table: 'environment_status_events', expression: "(payload->>'environment_key')", }); await ensureExpressionIndex(pool, { name: 'environment_status_events_status_idx', table: 'environment_status_events', expression: "(payload->>'status')", }); await ensureExpressionIndex(pool, { name: 'environment_status_events_fingerprint_idx', table: 'environment_status_events', expression: "(payload->>'status_fingerprint')", }); await pool.query(` CREATE TABLE IF NOT EXISTS ${PORTFOLIO_METRICS_TABLE} ( metric_id TEXT PRIMARY KEY, computed_at TIMESTAMPTZ NOT NULL, baseline_anchor_at TIMESTAMPTZ, baseline_status TEXT NOT NULL, payload JSONB NOT NULL ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${PORTFOLIO_METRICS_TABLE}_computed_at_idx ON ${PORTFOLIO_METRICS_TABLE} (computed_at DESC) `); await pool.query(` CREATE TABLE IF NOT EXISTS notification_deliveries ( notification_key TEXT PRIMARY KEY, notification_type TEXT NOT NULL, source_kind TEXT NOT NULL, source_id TEXT, status TEXT NOT NULL, attempt_count INTEGER NOT NULL DEFAULT 0, first_attempt_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), last_attempt_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), delivered_at TIMESTAMPTZ, payload JSONB NOT NULL, response JSONB, error JSONB ) `); await pool.query(` CREATE INDEX IF NOT EXISTS notification_deliveries_status_idx ON notification_deliveries (status, last_attempt_at DESC) `); await pool.query(` CREATE INDEX IF NOT EXISTS notification_deliveries_type_idx ON notification_deliveries (notification_type, last_attempt_at DESC) `); await pool.query(` CREATE TABLE IF NOT EXISTS ${QUOTE_OUTCOMES_TABLE} ( quote_id TEXT PRIMARY KEY, decision_id TEXT, command_id TEXT, execution_result_status TEXT NOT NULL, execution_result_code TEXT, submitted_at TIMESTAMPTZ, command_at TIMESTAMPTZ, outcome_status TEXT NOT NULL, outcome_observed_at TIMESTAMPTZ, outcome_source TEXT NOT NULL, attribution_status TEXT NOT NULL, attribution_method TEXT, attributed_inventory_delta JSONB, computed_at TIMESTAMPTZ NOT NULL, payload JSONB NOT NULL ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${QUOTE_OUTCOMES_TABLE}_outcome_observed_at_idx ON ${QUOTE_OUTCOMES_TABLE} (outcome_observed_at DESC) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${QUOTE_OUTCOMES_TABLE}_outcome_status_idx ON ${QUOTE_OUTCOMES_TABLE} (outcome_status) `); await ensureExpressionIndex(pool, { name: 'intent_request_preflights_request_id_idx', table: 'intent_request_preflights', expression: "(payload->>'request_id')", }); await ensureExpressionIndex(pool, { name: 'intent_request_preflights_idempotency_key_idx', table: 'intent_request_preflights', expression: "(payload->>'idempotency_key')", }); await ensureExpressionIndex(pool, { name: 'intent_request_submission_results_request_id_idx', table: 'intent_request_submission_results', expression: "(payload->>'request_id')", }); await ensureExpressionIndex(pool, { name: 'intent_request_submission_results_idempotency_key_idx', table: 'intent_request_submission_results', expression: "(payload->>'idempotency_key')", }); await pool.query(` CREATE TABLE IF NOT EXISTS ${INTENT_REQUEST_OUTCOMES_TABLE} ( request_id TEXT PRIMARY KEY, idempotency_key TEXT NOT NULL, submission_id TEXT, intent_hash TEXT, submission_status TEXT, relay_status TEXT, submitted_at TIMESTAMPTZ, outcome_status TEXT NOT NULL, outcome_observed_at TIMESTAMPTZ, outcome_source TEXT NOT NULL, outcome_reason TEXT NOT NULL, attribution_status TEXT NOT NULL, attribution_method TEXT, attributed_inventory_delta JSONB, computed_at TIMESTAMPTZ NOT NULL, payload JSONB NOT NULL ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${INTENT_REQUEST_OUTCOMES_TABLE}_outcome_observed_at_idx ON ${INTENT_REQUEST_OUTCOMES_TABLE} (outcome_observed_at DESC) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${INTENT_REQUEST_OUTCOMES_TABLE}_outcome_status_idx ON ${INTENT_REQUEST_OUTCOMES_TABLE} (outcome_status) `); } export async function ensureTradingConfigSchema(pool) { await pool.query(` CREATE TABLE IF NOT EXISTS ${SUPPORTED_ASSET_IMPORT_RUNS_TABLE} ( run_id TEXT PRIMARY KEY, source_url TEXT NOT NULL, fetched_at TIMESTAMPTZ NOT NULL, status TEXT NOT NULL, token_count INTEGER NOT NULL DEFAULT 0, added_count INTEGER NOT NULL DEFAULT 0, updated_count INTEGER NOT NULL DEFAULT 0, unchanged_count INTEGER NOT NULL DEFAULT 0, retired_count INTEGER NOT NULL DEFAULT 0, raw_response_hash TEXT, error TEXT, raw_response JSONB ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${SUPPORTED_ASSET_IMPORT_RUNS_TABLE}_fetched_at_idx ON ${SUPPORTED_ASSET_IMPORT_RUNS_TABLE} (fetched_at DESC) `); await pool.query(` CREATE TABLE IF NOT EXISTS ${TRADING_ASSETS_TABLE} ( asset_id TEXT PRIMARY KEY, venue TEXT NOT NULL, symbol TEXT NOT NULL, label TEXT NOT NULL, decimals INTEGER NOT NULL, blockchain TEXT, chain TEXT, contract_address TEXT, latest_price TEXT, price_updated_at TIMESTAMPTZ, supported BOOLEAN NOT NULL DEFAULT false, retired_at TIMESTAMPTZ, enabled_for_inventory BOOLEAN NOT NULL DEFAULT false, role TEXT, withdraw_address TEXT, raw_payload JSONB NOT NULL DEFAULT '{}'::jsonb, first_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), last_supported_at TIMESTAMPTZ, updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${TRADING_ASSETS_TABLE}_supported_idx ON ${TRADING_ASSETS_TABLE} (supported, updated_at DESC) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${TRADING_ASSETS_TABLE}_inventory_idx ON ${TRADING_ASSETS_TABLE} (enabled_for_inventory, asset_id) `); await pool.query(` CREATE TABLE IF NOT EXISTS ${TRADING_PAIRS_TABLE} ( pair_id TEXT PRIMARY KEY, venue TEXT NOT NULL, asset_in TEXT NOT NULL REFERENCES ${TRADING_ASSETS_TABLE}(asset_id), asset_out TEXT NOT NULL REFERENCES ${TRADING_ASSETS_TABLE}(asset_id), mode TEXT NOT NULL, enabled BOOLEAN NOT NULL DEFAULT false, status TEXT NOT NULL DEFAULT 'disabled', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (venue, asset_in, asset_out) ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${TRADING_PAIRS_TABLE}_enabled_idx ON ${TRADING_PAIRS_TABLE} (enabled, status, updated_at DESC) `); await pool.query(` CREATE TABLE IF NOT EXISTS ${PAIR_STRATEGY_CONFIGS_TABLE} ( config_id TEXT PRIMARY KEY, pair_id TEXT NOT NULL REFERENCES ${TRADING_PAIRS_TABLE}(pair_id), version INTEGER NOT NULL, active BOOLEAN NOT NULL DEFAULT false, edge_bps INTEGER NOT NULL, max_notional NUMERIC NOT NULL, min_notional NUMERIC NOT NULL DEFAULT 0, slippage_bps INTEGER NOT NULL DEFAULT 0, min_deadline_ms INTEGER NOT NULL, price_max_age_ms INTEGER NOT NULL, inventory_max_age_ms INTEGER NOT NULL, request_default_notional NUMERIC, request_max_notional NUMERIC, request_max_slippage_bps INTEGER, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by TEXT NOT NULL, reason TEXT, UNIQUE (pair_id, version) ) `); await pool.query(` CREATE UNIQUE INDEX IF NOT EXISTS ${PAIR_STRATEGY_CONFIGS_TABLE}_active_one_idx ON ${PAIR_STRATEGY_CONFIGS_TABLE} (pair_id) WHERE active `); await pool.query(` CREATE TABLE IF NOT EXISTS ${PAIR_PRICE_ROUTES_TABLE} ( route_id TEXT PRIMARY KEY, pair_id TEXT NOT NULL REFERENCES ${TRADING_PAIRS_TABLE}(pair_id), source TEXT NOT NULL, base_asset_id TEXT NOT NULL REFERENCES ${TRADING_ASSETS_TABLE}(asset_id), quote_asset_id TEXT NOT NULL REFERENCES ${TRADING_ASSETS_TABLE}(asset_id), route_config JSONB NOT NULL DEFAULT '{}'::jsonb, max_age_ms INTEGER NOT NULL, enabled BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${PAIR_PRICE_ROUTES_TABLE}_pair_enabled_idx ON ${PAIR_PRICE_ROUTES_TABLE} (pair_id, enabled) `); await pool.query(` CREATE TABLE IF NOT EXISTS ${PAIR_CONFIG_AUDIT_LOG_TABLE} ( audit_id TEXT PRIMARY KEY, entity_type TEXT NOT NULL, entity_id TEXT NOT NULL, action TEXT NOT NULL, old_value JSONB, new_value JSONB, changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), changed_by TEXT NOT NULL, reason TEXT ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${PAIR_CONFIG_AUDIT_LOG_TABLE}_entity_idx ON ${PAIR_CONFIG_AUDIT_LOG_TABLE} (entity_type, entity_id, changed_at DESC) `); } export async function seedTradingConfig(pool, { now = new Date().toISOString(), changedBy = 'repo_seed', } = {}) { await ensureTradingConfigSchema(pool); for (const asset of buildSeedAssets()) { await upsertSeedAsset(pool, { asset, now }); } for (const pair of buildSeedPairs()) { await upsertSeedPair(pool, { pair, now, preserveRuntimeState: true }); await upsertSeedStrategyConfig(pool, { config: buildSeedStrategyConfig(pair.pairId, { createdBy: changedBy }), }); await upsertSeedPriceRoute(pool, { route: buildSeedPriceRoute(pair.pairId), now, }); } await seedKnownEnabledPairRuntimeConfig(pool, { now }); return loadTradingConfig(pool); } export async function importSupportedAssets(pool, { sourceUrl = ONE_CLICK_TOKENS_URL, fetchedAt = new Date().toISOString(), fetchImpl = globalThis.fetch, response = null, } = {}) { await ensureTradingConfigSchema(pool); let rawResponse = response; let rawResponseHash = null; let runId = null; try { if (rawResponse == null) { if (typeof fetchImpl !== 'function') throw new Error('fetch implementation is unavailable'); const fetchResponse = await fetchImpl(sourceUrl); const text = await fetchResponse.text(); if (!fetchResponse.ok) throw new Error(`HTTP ${fetchResponse.status}: ${text.slice(0, 200)}`); rawResponse = text ? JSON.parse(text) : null; } const normalizedTokens = normalizeOneClickTokenResponse(rawResponse); rawResponseHash = hashJson(rawResponse); runId = `asset-import:${Date.parse(fetchedAt)}:${rawResponseHash.slice(0, 16)}`; const existing = await loadTradingAssetsById(pool); let addedCount = 0; let updatedCount = 0; let unchangedCount = 0; for (const token of normalizedTokens) { const previous = existing.get(token.assetId) || null; if (!previous) addedCount += 1; else if (importedAssetChanged(previous, token)) updatedCount += 1; else unchangedCount += 1; await upsertImportedAsset(pool, { asset: token, fetchedAt, }); } const importedAssetIds = normalizedTokens.map((token) => token.assetId); const retiredResult = await pool.query( ` UPDATE ${TRADING_ASSETS_TABLE} SET supported = false, retired_at = COALESCE(retired_at, $1), updated_at = $1 WHERE venue = 'near-intents' AND supported = true AND NOT (asset_id = ANY($2::text[])) RETURNING asset_id `, [fetchedAt, importedAssetIds], ); const summary = { run_id: runId, source_url: sourceUrl, fetched_at: fetchedAt, status: 'success', token_count: normalizedTokens.length, added_count: addedCount, updated_count: updatedCount, unchanged_count: unchangedCount, retired_count: retiredResult.rowCount, raw_response_hash: rawResponseHash, error: null, raw_response: rawResponse, }; await insertAssetImportRun(pool, summary); return publicAssetImportRunSummary(summary); } catch (error) { const fallbackHash = rawResponse == null ? null : hashJson(rawResponse); runId ||= `asset-import:${Date.parse(fetchedAt)}:${fallbackHash?.slice(0, 16) || 'failed'}`; const summary = { run_id: runId, source_url: sourceUrl, fetched_at: fetchedAt, status: 'failed', token_count: 0, added_count: 0, updated_count: 0, unchanged_count: 0, retired_count: 0, raw_response_hash: fallbackHash, error: error.message, raw_response: rawResponse, }; await insertAssetImportRun(pool, summary); throw Object.assign(error, { importRun: publicAssetImportRunSummary(summary) }); } } export async function loadTradingConfig(pool) { await ensureTradingConfigSchema(pool); const [ assetResult, pairResult, strategyResult, routeResult, latestImportResult, ] = await Promise.all([ pool.query(` SELECT * FROM ${TRADING_ASSETS_TABLE} ORDER BY symbol ASC, asset_id ASC `), pool.query(` SELECT * FROM ${TRADING_PAIRS_TABLE} ORDER BY created_at ASC, pair_id ASC `), pool.query(` SELECT * FROM ${PAIR_STRATEGY_CONFIGS_TABLE} WHERE active = true ORDER BY pair_id ASC, version DESC `), pool.query(` SELECT * FROM ${PAIR_PRICE_ROUTES_TABLE} WHERE enabled = true ORDER BY pair_id ASC, created_at DESC `), pool.query(` SELECT * FROM ${SUPPORTED_ASSET_IMPORT_RUNS_TABLE} ORDER BY fetched_at DESC LIMIT 1 `), ]); return buildTradingConfigSnapshot({ assetRows: assetResult.rows, pairRows: pairResult.rows, strategyRows: strategyResult.rows, routeRows: routeResult.rows, latestImportRun: latestImportResult.rows[0] || null, }); } export function createTradingConfigStore({ pool, cacheTtlMs = 5_000, logger = null, } = {}) { if (!pool) throw new Error('pool is required'); let cached = null; let cachedAtMs = 0; let lastError = null; async function refresh({ force = false } = {}) { const nowMs = Date.now(); if (!force && cached && nowMs - cachedAtMs <= cacheTtlMs) return cached; try { cached = await loadTradingConfig(pool); cachedAtMs = nowMs; lastError = null; return cached; } catch (error) { lastError = error; logger?.error?.('trading_config_load_failed', { details: { error: error.message }, }); cached = buildFailClosedTradingConfig(error); cachedAtMs = nowMs; return cached; } } return { getConfig: refresh, async forceRefresh() { return refresh({ force: true }); }, getCachedConfig() { return cached || buildFailClosedTradingConfig(lastError || new Error('config not loaded')); }, getState() { return summarizeTradingConfigSnapshot( cached || buildFailClosedTradingConfig(lastError || new Error('config not loaded')), ); }, }; } export async function loadAssetCatalogSummary(pool, { limit = 250 } = {}) { await ensureTradingConfigSchema(pool); const boundedLimit = Math.max(1, Number(limit) || 50); const [latestImportResult, countResult, assetResult] = await Promise.all([ pool.query(` SELECT * FROM ${SUPPORTED_ASSET_IMPORT_RUNS_TABLE} ORDER BY fetched_at DESC LIMIT 1 `), pool.query(` SELECT COUNT(*)::INT AS known_count, COUNT(*) FILTER (WHERE supported)::INT AS supported_count, COUNT(*) FILTER (WHERE retired_at IS NOT NULL OR supported = false)::INT AS retired_count, COUNT(*) FILTER (WHERE enabled_for_inventory)::INT AS inventory_enabled_count FROM ${TRADING_ASSETS_TABLE} `), pool.query(` SELECT asset_id, venue, symbol, label, decimals, blockchain, chain, contract_address, latest_price, price_updated_at, supported, retired_at, enabled_for_inventory, role, withdraw_address, raw_payload <> '{}'::jsonb AS raw_payload_available, updated_at FROM ${TRADING_ASSETS_TABLE} ORDER BY symbol ASC, asset_id ASC LIMIT $1 `, [boundedLimit]), ]); const counts = countResult.rows[0] || {}; return { latest_import: normalizeAssetImportRunRow(latestImportResult.rows[0] || null), counts: { known: Number(counts.known_count || 0), supported: Number(counts.supported_count || 0), retired: Number(counts.retired_count || 0), inventory_enabled: Number(counts.inventory_enabled_count || 0), }, items: assetResult.rows.map(normalizeAssetCatalogSummaryRow), }; } export async function loadPairConfigSummary(pool) { const snapshot = await loadTradingConfig(pool); return { ok: snapshot.ok, block_reason: snapshot.blockReason, loaded_at: snapshot.loadedAt, pairs: snapshot.pairs, }; } export async function createPairStrategyConfigVersion(pool, { pairId = null, pair = null, edgeBps = null, maxNotional = null, minNotional = null, slippageBps = null, minDeadlineMs = null, priceMaxAgeMs = null, inventoryMaxAgeMs = null, requestDefaultNotional = undefined, requestMaxNotional = undefined, requestMaxSlippageBps = undefined, changedBy = 'operator', reason = 'operator config update', } = {}) { await ensureTradingConfigSchema(pool); const resolvedPairId = pairId || pair; if (!resolvedPairId) throw new Error('pair_id is required'); return withTransaction(pool, async (client) => { const activeResult = await client.query( ` SELECT * FROM ${PAIR_STRATEGY_CONFIGS_TABLE} WHERE pair_id = $1 AND active = true ORDER BY version DESC LIMIT 1 `, [resolvedPairId], ); const active = activeResult.rows[0]; if (!active) throw new Error(`active strategy config missing for pair ${resolvedPairId}`); const nextVersion = Number(active.version || 0) + 1; const nextEdgeBps = edgeBps == null ? Number(active.edge_bps) : Number(edgeBps); if (!Number.isInteger(nextEdgeBps) || nextEdgeBps <= 0) { throw new Error('edge_bps must be a positive integer'); } const nextMaxNotional = positiveNumberStringOrDefault(maxNotional, active.max_notional, 'max_notional'); const nextConfig = { configId: `${resolvedPairId}:v${nextVersion}`, pairId: resolvedPairId, version: nextVersion, edgeBps: nextEdgeBps, maxNotional: nextMaxNotional, minNotional: minNotional == null ? String(active.min_notional) : String(minNotional), slippageBps: slippageBps == null ? Number(active.slippage_bps) : Number(slippageBps), minDeadlineMs: minDeadlineMs == null ? Number(active.min_deadline_ms) : Number(minDeadlineMs), priceMaxAgeMs: priceMaxAgeMs == null ? Number(active.price_max_age_ms) : Number(priceMaxAgeMs), inventoryMaxAgeMs: inventoryMaxAgeMs == null ? Number(active.inventory_max_age_ms) : Number(inventoryMaxAgeMs), requestDefaultNotional: requestDefaultNotional === undefined ? active.request_default_notional == null ? null : String(active.request_default_notional) : nullablePositiveNumberString(requestDefaultNotional, 'request_default_notional'), requestMaxNotional: requestMaxNotional === undefined ? active.request_max_notional == null ? null : String(active.request_max_notional) : nullablePositiveNumberString(requestMaxNotional, 'request_max_notional'), requestMaxSlippageBps: requestMaxSlippageBps === undefined ? active.request_max_slippage_bps == null ? null : Number(active.request_max_slippage_bps) : nullableNonNegativeInteger(requestMaxSlippageBps, 'request_max_slippage_bps'), createdBy: changedBy, reason, }; await client.query( `UPDATE ${PAIR_STRATEGY_CONFIGS_TABLE} SET active = false WHERE pair_id = $1 AND active = true`, [resolvedPairId], ); await insertPairStrategyConfig(client, { config: nextConfig, active: true }); await insertConfigAuditLog(client, { entityType: 'pair_strategy_config', entityId: resolvedPairId, action: 'version_created', oldValue: normalizeStrategyConfigRow(active), newValue: nextConfig, changedBy, reason, }); return normalizeStrategyConfigRow({ ...active, config_id: nextConfig.configId, pair_id: nextConfig.pairId, version: nextConfig.version, active: true, edge_bps: nextConfig.edgeBps, max_notional: nextConfig.maxNotional, min_notional: nextConfig.minNotional, slippage_bps: nextConfig.slippageBps, min_deadline_ms: nextConfig.minDeadlineMs, price_max_age_ms: nextConfig.priceMaxAgeMs, inventory_max_age_ms: nextConfig.inventoryMaxAgeMs, request_default_notional: nextConfig.requestDefaultNotional, request_max_notional: nextConfig.requestMaxNotional, request_max_slippage_bps: nextConfig.requestMaxSlippageBps, created_by: changedBy, reason, }); }); } export async function enableObserveOnlyPair(pool, { venue = 'near-intents', assetIn, assetOut, changedBy = 'operator', reason = 'operator enabled observe-only pair', } = {}) { await ensureTradingConfigSchema(pool); if (!assetIn || !assetOut) throw new Error('asset_in and asset_out are required'); const pairId = `${assetIn}->${assetOut}`; const existingResult = await pool.query( ` SELECT * FROM ${TRADING_PAIRS_TABLE} WHERE pair_id = $1 LIMIT 1 `, [pairId], ); const existingPair = existingResult.rows[0] ? normalizeTradingPairRow(existingResult.rows[0]) : null; if (existingPair?.enabled && existingPair.status !== 'disabled') { return existingPair; } const pair = { pairId, venue, assetIn, assetOut, mode: 'observe_only', enabled: true, status: 'observe_only', }; await upsertSeedPair(pool, { pair, now: new Date().toISOString() }); await insertConfigAuditLog(pool, { entityType: 'trading_pair', entityId: pairId, action: 'observe_only_enabled', oldValue: null, newValue: pair, changedBy, reason, }); return pair; } export async function setTradingPairMode(pool, { venue = 'near-intents', pairId = null, pair = null, assetIn = null, assetOut = null, mode = 'observe_only', edgeBps = null, maxNotional = null, minNotional = null, slippageBps = null, minDeadlineMs = null, priceMaxAgeMs = null, inventoryMaxAgeMs = null, requestDefaultNotional = undefined, requestMaxNotional = undefined, requestMaxSlippageBps = undefined, changedBy = 'operator', reason = 'operator pair mode update', } = {}) { await ensureTradingConfigSchema(pool); const normalizedMode = normalizePairMode(mode); const resolvedPairId = pairId || pair || (assetIn && assetOut ? `${assetIn}->${assetOut}` : null); if (!resolvedPairId) throw new Error('pair_id or asset_in/asset_out is required'); return withTransaction(pool, async (client) => { const existingResult = await client.query( ` SELECT * FROM ${TRADING_PAIRS_TABLE} WHERE pair_id = $1 LIMIT 1 `, [resolvedPairId], ); const existingPair = existingResult.rows[0] ? normalizeTradingPairRow(existingResult.rows[0]) : null; const [pairAssetIn, pairAssetOut] = splitPairId(resolvedPairId); const resolvedAssetIn = assetIn || existingPair?.assetIn || pairAssetIn; const resolvedAssetOut = assetOut || existingPair?.assetOut || pairAssetOut; if (!resolvedAssetIn || !resolvedAssetOut) throw new Error('asset_in and asset_out are required'); const assets = await loadTradingAssetsById(client); if (!assets.has(resolvedAssetIn)) throw new Error(`asset_in is not registered: ${resolvedAssetIn}`); if (!assets.has(resolvedAssetOut)) throw new Error(`asset_out is not registered: ${resolvedAssetOut}`); const nextPair = { pairId: resolvedPairId, venue: existingPair?.venue || venue, assetIn: resolvedAssetIn, assetOut: resolvedAssetOut, mode: normalizedMode, enabled: true, status: normalizedMode, }; await upsertSeedPair(client, { pair: nextPair, now: new Date().toISOString() }); let strategyConfig = null; if (pairCanMake(nextPair) || pairCanTake(nextPair)) { await enableInventoryForAssets(client, { assetIds: [resolvedAssetIn, resolvedAssetOut], now: new Date().toISOString(), }); const activeConfigResult = await client.query( ` SELECT * FROM ${PAIR_STRATEGY_CONFIGS_TABLE} WHERE pair_id = $1 AND active = true ORDER BY version DESC LIMIT 1 `, [resolvedPairId], ); strategyConfig = activeConfigResult.rows[0] ? normalizeStrategyConfigRow(activeConfigResult.rows[0]) : null; if (!strategyConfig) { const nextConfig = buildInitialPairStrategyConfig(resolvedPairId, { edgeBps, maxNotional, minNotional, slippageBps, minDeadlineMs, priceMaxAgeMs, inventoryMaxAgeMs, requestDefaultNotional, requestMaxNotional, requestMaxSlippageBps, changedBy, reason, }); await insertPairStrategyConfig(client, { config: nextConfig, active: true }); strategyConfig = normalizeStrategyConfigRow({ config_id: nextConfig.configId, pair_id: nextConfig.pairId, version: nextConfig.version, active: true, edge_bps: nextConfig.edgeBps, max_notional: nextConfig.maxNotional, min_notional: nextConfig.minNotional, slippage_bps: nextConfig.slippageBps, min_deadline_ms: nextConfig.minDeadlineMs, price_max_age_ms: nextConfig.priceMaxAgeMs, inventory_max_age_ms: nextConfig.inventoryMaxAgeMs, request_default_notional: nextConfig.requestDefaultNotional, request_max_notional: nextConfig.requestMaxNotional, request_max_slippage_bps: nextConfig.requestMaxSlippageBps, created_by: changedBy, reason, }); await insertConfigAuditLog(client, { entityType: 'pair_strategy_config', entityId: resolvedPairId, action: 'initial_version_created', oldValue: null, newValue: strategyConfig, changedBy, reason, }); } const knownRoute = buildKnownPriceRouteForPair(nextPair); if (knownRoute) { await upsertSeedPriceRoute(client, { route: knownRoute, now: new Date().toISOString(), }); } } await insertConfigAuditLog(client, { entityType: 'trading_pair', entityId: resolvedPairId, action: 'mode_set', oldValue: existingPair, newValue: nextPair, changedBy, reason, }); return { ...nextPair, strategyConfig, }; }); } export async function pauseTradingPair(pool, { pairId = null, pair = null, changedBy = 'operator', reason = 'operator paused pair', } = {}) { await ensureTradingConfigSchema(pool); const resolvedPairId = pairId || pair; if (!resolvedPairId) throw new Error('pair_id is required'); return withTransaction(pool, async (client) => { const existingResult = await client.query( ` SELECT * FROM ${TRADING_PAIRS_TABLE} WHERE pair_id = $1 LIMIT 1 `, [resolvedPairId], ); const existingPair = existingResult.rows[0] ? normalizeTradingPairRow(existingResult.rows[0]) : null; if (!existingPair) throw new Error(`trading pair not found: ${resolvedPairId}`); const nextPair = { pairId: existingPair.pairId, venue: existingPair.venue, assetIn: existingPair.assetIn, assetOut: existingPair.assetOut, mode: existingPair.mode, enabled: false, status: 'disabled', }; await upsertSeedPair(client, { pair: nextPair, now: new Date().toISOString() }); await insertConfigAuditLog(client, { entityType: 'trading_pair', entityId: resolvedPairId, action: 'paused', oldValue: existingPair, newValue: nextPair, changedBy, reason, }); return nextPair; }); } function buildTradingConfigSnapshot({ assetRows, pairRows, strategyRows, routeRows, latestImportRun, }) { const loadedAt = new Date().toISOString(); const assets = assetRows.map(normalizeTradingAssetRow); const assetRegistry = new Map(assets.map((asset) => [asset.assetId, asset])); const strategyByPairId = new Map( strategyRows.map((row) => [row.pair_id, normalizeStrategyConfigRow(row)]), ); const routeByPairId = new Map(); for (const row of routeRows) { if (!routeByPairId.has(row.pair_id)) routeByPairId.set(row.pair_id, normalizePriceRouteRow(row)); } const pairs = pairRows.map((row) => { const pair = normalizeTradingPairRow(row); const assetIn = assetRegistry.get(pair.assetIn) || null; const assetOut = assetRegistry.get(pair.assetOut) || null; const strategyConfig = strategyByPairId.get(pair.pairId) || null; const priceRoute = routeByPairId.get(pair.pairId) || null; const blockReasons = []; if (!assetIn) blockReasons.push('asset_in_missing'); if (!assetOut) blockReasons.push('asset_out_missing'); if (assetIn && !Number.isInteger(assetIn.decimals)) blockReasons.push('asset_in_decimals_missing'); if (assetOut && !Number.isInteger(assetOut.decimals)) blockReasons.push('asset_out_decimals_missing'); if ((pairCanMake(pair) || pairCanTake(pair)) && !strategyConfig) { blockReasons.push('pair_strategy_config_missing'); } if ((pairCanMake(pair) || pairCanTake(pair)) && !priceRoute) { blockReasons.push('price_route_missing'); } if (strategyConfig && (!Number.isInteger(strategyConfig.edgeBps) || strategyConfig.edgeBps <= 0)) { blockReasons.push('edge_bps_invalid'); } if (strategyConfig && !(Number(strategyConfig.maxNotional) > 0)) { blockReasons.push('max_notional_invalid'); } const observeEnabled = pairCanObserve(pair); const makerEnabled = pairCanMake(pair); const takerEnabled = pairCanTake(pair); const canTrade = (makerEnabled || takerEnabled) && blockReasons.length === 0; return { ...pair, key: pair.pairId, assetIn, assetOut, asset_in: pair.assetIn, asset_out: pair.assetOut, asset_in_symbol: assetIn?.symbol || pair.assetIn, asset_out_symbol: assetOut?.symbol || pair.assetOut, strategyConfig, priceRoute, observeEnabled, makerEnabled, takerEnabled, canTrade, blockReason: blockReasons[0] || null, blockReasons, }; }); const observedPairs = pairs.filter((pair) => pair.observeEnabled); const enabledPairKeys = new Set(observedPairs.map((pair) => pair.key)); const makerPairKeys = new Set(pairs.filter((pair) => pair.makerEnabled).map((pair) => pair.key)); const takerPairKeys = new Set(pairs.filter((pair) => pair.takerEnabled).map((pair) => pair.key)); const pairByKey = new Map(pairs.map((pair) => [pair.key, pair])); const pairById = new Map(pairs.map((pair) => [pair.pairId, pair])); const pairStrategyByPairKey = new Map( pairs .filter((pair) => pair.strategyConfig) .map((pair) => [pair.key, pair.strategyConfig]), ); const pairPriceRouteByPairKey = new Map( pairs .filter((pair) => pair.priceRoute) .map((pair) => [pair.key, pair.priceRoute]), ); const trackedAssets = assets.filter((asset) => asset.enabledForInventory); const currentBtc = assetRegistry.get('nep141:nbtc.bridge.near') || trackedAssets.find((asset) => asset.symbol === 'BTC') || null; const legacyBtc = assetRegistry.get('nep141:btc.omft.near') || null; const currentEure = assetRegistry.get('nep141:gnosis-0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430.omft.near') || trackedAssets.find((asset) => asset.symbol === 'EURe') || null; const preferredActivePair = pairByKey.get('nep141:nbtc.bridge.near->nep141:gnosis-0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430.omft.near') || observedPairs[0] || null; const defaultTakerPair = pairByKey.get('nep141:gnosis-0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430.omft.near->nep141:nbtc.bridge.near') || pairs.find((pair) => pair.takerEnabled && pair.canTrade) || null; const activeAssetIds = preferredActivePair?.assetIn && preferredActivePair?.assetOut ? [preferredActivePair.assetIn.assetId, preferredActivePair.assetOut.assetId] : []; const blockReason = observedPairs.length === 0 ? 'no_enabled_pairs' : trackedAssets.length === 0 ? 'no_inventory_tracked_assets' : null; return { ok: blockReason == null, source: 'postgres', loadedAt, blockReason, latestImportRun: normalizeAssetImportRunRow(latestImportRun), assets, assetRegistry, trackedAssets, trackedAssetIds: trackedAssets.map((asset) => asset.assetId), tradingBtc: currentBtc, tradingBtcAssets: [currentBtc, legacyBtc].filter(Boolean), tradingEure: currentEure, activePair: preferredActivePair?.key || null, activeAssetIds, pairs, observedPairs, enabledPairKeys, makerPairKeys, takerPairKeys, pairByKey, pairById, pairStrategyByPairKey, pairPriceRouteByPairKey, defaultTakerPair, tradingConfigLoaded: true, requireDbTradingConfig: true, }; } function buildFailClosedTradingConfig(error) { return { ok: false, source: 'postgres', loadedAt: new Date().toISOString(), blockReason: 'trading_config_unavailable', error: error?.message || 'trading config unavailable', latestImportRun: null, assets: [], assetRegistry: new Map(), trackedAssets: [], trackedAssetIds: [], tradingBtc: null, tradingBtcAssets: [], tradingEure: null, activePair: null, activeAssetIds: [], pairs: [], observedPairs: [], enabledPairKeys: new Set(), makerPairKeys: new Set(), takerPairKeys: new Set(), pairByKey: new Map(), pairById: new Map(), pairStrategyByPairKey: new Map(), pairPriceRouteByPairKey: new Map(), defaultTakerPair: null, tradingConfigLoaded: false, requireDbTradingConfig: true, }; } export function summarizeTradingConfigSnapshot(snapshot) { return { ok: snapshot.ok, source: snapshot.source, loaded_at: snapshot.loadedAt, block_reason: snapshot.blockReason || null, error: snapshot.error || null, latest_import: snapshot.latestImportRun || null, asset_count: snapshot.assets?.length || 0, tracked_asset_count: snapshot.trackedAssets?.length || 0, enabled_pair_count: snapshot.observedPairs?.length || 0, active_pair: snapshot.activePair || null, pairs: (snapshot.pairs || []).map((pair) => ({ pair_id: pair.pairId, pair: pair.key, mode: pair.mode, status: pair.status, enabled: pair.enabled, can_trade: pair.canTrade, block_reason: pair.blockReason, strategy_config_id: pair.strategyConfig?.configId || null, strategy_config_version: pair.strategyConfig?.version || null, edge_bps: pair.strategyConfig?.edgeBps ?? null, max_notional: pair.strategyConfig?.maxNotional ?? null, price_route_id: pair.priceRoute?.routeId || null, })), }; } function normalizeTradingAssetRow(row) { return { assetId: row.asset_id, asset_id: row.asset_id, venue: row.venue, symbol: row.symbol, label: row.label || row.symbol, decimals: Number(row.decimals), blockchain: row.blockchain || null, chain: row.chain || row.blockchain || null, contractAddress: row.contract_address || null, contract_address: row.contract_address || null, latestPrice: row.latest_price == null ? null : String(row.latest_price), latest_price: row.latest_price == null ? null : String(row.latest_price), priceUpdatedAt: toIsoTimestamp(row.price_updated_at), price_updated_at: toIsoTimestamp(row.price_updated_at), supported: row.supported === true, retiredAt: toIsoTimestamp(row.retired_at), retired_at: toIsoTimestamp(row.retired_at), enabledForInventory: row.enabled_for_inventory === true, enabled_for_inventory: row.enabled_for_inventory === true, role: row.role || null, withdrawAddress: row.withdraw_address || '', withdraw_address: row.withdraw_address || '', rawPayload: row.raw_payload || {}, raw_payload: row.raw_payload || {}, updated_at: toIsoTimestamp(row.updated_at), }; } function normalizeAssetCatalogSummaryRow(row) { return { assetId: row.asset_id, asset_id: row.asset_id, venue: row.venue, symbol: row.symbol, label: row.label || row.symbol, decimals: Number(row.decimals), blockchain: row.blockchain || null, chain: row.chain || row.blockchain || null, contractAddress: row.contract_address || null, contract_address: row.contract_address || null, latestPrice: row.latest_price == null ? null : String(row.latest_price), latest_price: row.latest_price == null ? null : String(row.latest_price), priceUpdatedAt: toIsoTimestamp(row.price_updated_at), price_updated_at: toIsoTimestamp(row.price_updated_at), supported: row.supported === true, retiredAt: toIsoTimestamp(row.retired_at), retired_at: toIsoTimestamp(row.retired_at), enabledForInventory: row.enabled_for_inventory === true, enabled_for_inventory: row.enabled_for_inventory === true, role: row.role || null, withdrawAddress: row.withdraw_address || '', withdraw_address: row.withdraw_address || '', rawPayloadAvailable: row.raw_payload_available === true, raw_payload_available: row.raw_payload_available === true, updated_at: toIsoTimestamp(row.updated_at), }; } function normalizeTradingPairRow(row) { return { pairId: row.pair_id, pair_id: row.pair_id, venue: row.venue, assetIn: row.asset_in, assetOut: row.asset_out, mode: row.mode, enabled: row.enabled === true, status: row.status, created_at: toIsoTimestamp(row.created_at), updated_at: toIsoTimestamp(row.updated_at), }; } function splitPairId(pairId) { const parts = String(pairId || '').split('->'); if (parts.length !== 2 || !parts[0] || !parts[1]) return [null, null]; return parts; } function buildInitialPairStrategyConfig(pairId, { edgeBps = null, maxNotional = null, minNotional = null, slippageBps = null, minDeadlineMs = null, priceMaxAgeMs = null, inventoryMaxAgeMs = null, requestDefaultNotional = undefined, requestMaxNotional = undefined, requestMaxSlippageBps = undefined, changedBy = 'operator', reason = 'operator pair strategy config initialization', } = {}) { const baseConfig = buildSeedStrategyConfig(pairId, { createdBy: changedBy, reason, }); return { ...baseConfig, edgeBps: positiveIntegerOrDefault(edgeBps, baseConfig.edgeBps, 'edge_bps'), maxNotional: positiveNumberStringOrDefault(maxNotional, baseConfig.maxNotional, 'max_notional'), minNotional: nonNegativeNumberStringOrDefault(minNotional, baseConfig.minNotional, 'min_notional'), slippageBps: nonNegativeIntegerOrDefault(slippageBps, baseConfig.slippageBps, 'slippage_bps'), minDeadlineMs: positiveIntegerOrDefault(minDeadlineMs, baseConfig.minDeadlineMs, 'min_deadline_ms'), priceMaxAgeMs: positiveIntegerOrDefault(priceMaxAgeMs, baseConfig.priceMaxAgeMs, 'price_max_age_ms'), inventoryMaxAgeMs: positiveIntegerOrDefault(inventoryMaxAgeMs, baseConfig.inventoryMaxAgeMs, 'inventory_max_age_ms'), requestDefaultNotional: nullablePositiveNumberStringOrDefault( requestDefaultNotional, baseConfig.requestDefaultNotional, 'request_default_notional', ), requestMaxNotional: nullablePositiveNumberStringOrDefault( requestMaxNotional, baseConfig.requestMaxNotional, 'request_max_notional', ), requestMaxSlippageBps: nullableNonNegativeIntegerOrDefault( requestMaxSlippageBps, baseConfig.requestMaxSlippageBps, 'request_max_slippage_bps', ), }; } function hasConfigOverride(value) { return value != null && String(value).trim() !== ''; } function positiveIntegerOrDefault(value, fallback, field) { if (!hasConfigOverride(value)) return Number(fallback); const next = Number(value); if (!Number.isInteger(next) || next <= 0) throw new Error(`${field} must be a positive integer`); return next; } function nonNegativeIntegerOrDefault(value, fallback, field) { if (!hasConfigOverride(value)) return Number(fallback); const next = Number(value); if (!Number.isInteger(next) || next < 0) throw new Error(`${field} must be a non-negative integer`); return next; } function positiveNumberStringOrDefault(value, fallback, field) { if (!hasConfigOverride(value)) return String(fallback); const next = String(value).trim(); if (!(Number(next) > 0)) throw new Error(`${field} must be greater than zero`); return next; } function nonNegativeNumberStringOrDefault(value, fallback, field) { if (!hasConfigOverride(value)) return String(fallback); const next = String(value).trim(); if (!(Number(next) >= 0)) throw new Error(`${field} must be zero or greater`); return next; } function nullablePositiveNumberStringOrDefault(value, fallback, field) { if (value === undefined) return fallback == null ? null : positiveNumberStringOrDefault(fallback, '1', field); return nullablePositiveNumberString(value, field); } function nullablePositiveNumberString(value, field) { if (!hasConfigOverride(value)) return null; return positiveNumberStringOrDefault(value, '1', field); } function nullableNonNegativeIntegerOrDefault(value, fallback, field) { if (value === undefined) return fallback == null ? null : nonNegativeIntegerOrDefault(fallback, 0, field); return nullableNonNegativeInteger(value, field); } function nullableNonNegativeInteger(value, field) { if (!hasConfigOverride(value)) return null; return nonNegativeIntegerOrDefault(value, 0, field); } function normalizeStrategyConfigRow(row) { if (!row) return null; return { configId: row.config_id, config_id: row.config_id, pairId: row.pair_id, pair_id: row.pair_id, version: Number(row.version), active: row.active === true, edgeBps: Number(row.edge_bps), edge_bps: Number(row.edge_bps), maxNotional: String(row.max_notional), max_notional: String(row.max_notional), minNotional: String(row.min_notional ?? '0'), min_notional: String(row.min_notional ?? '0'), slippageBps: Number(row.slippage_bps ?? 0), slippage_bps: Number(row.slippage_bps ?? 0), minDeadlineMs: Number(row.min_deadline_ms), min_deadline_ms: Number(row.min_deadline_ms), priceMaxAgeMs: Number(row.price_max_age_ms), price_max_age_ms: Number(row.price_max_age_ms), inventoryMaxAgeMs: Number(row.inventory_max_age_ms), inventory_max_age_ms: Number(row.inventory_max_age_ms), requestDefaultNotional: row.request_default_notional == null ? null : String(row.request_default_notional), request_default_notional: row.request_default_notional == null ? null : String(row.request_default_notional), requestMaxNotional: row.request_max_notional == null ? null : String(row.request_max_notional), request_max_notional: row.request_max_notional == null ? null : String(row.request_max_notional), requestMaxSlippageBps: row.request_max_slippage_bps == null ? null : Number(row.request_max_slippage_bps), request_max_slippage_bps: row.request_max_slippage_bps == null ? null : Number(row.request_max_slippage_bps), created_at: toIsoTimestamp(row.created_at), created_by: row.created_by || null, reason: row.reason || null, }; } function normalizePriceRouteRow(row) { if (!row) return null; return { routeId: row.route_id, route_id: row.route_id, pairId: row.pair_id, pair_id: row.pair_id, source: row.source, baseAssetId: row.base_asset_id, base_asset_id: row.base_asset_id, quoteAssetId: row.quote_asset_id, quote_asset_id: row.quote_asset_id, routeConfig: row.route_config || {}, route_config: row.route_config || {}, maxAgeMs: Number(row.max_age_ms), max_age_ms: Number(row.max_age_ms), enabled: row.enabled === true, }; } function normalizeAssetImportRunRow(row) { if (!row) return null; return { run_id: row.run_id, source_url: row.source_url, fetched_at: toIsoTimestamp(row.fetched_at), status: row.status, token_count: Number(row.token_count || 0), added_count: Number(row.added_count || 0), updated_count: Number(row.updated_count || 0), unchanged_count: Number(row.unchanged_count || 0), retired_count: Number(row.retired_count || 0), raw_response_hash: row.raw_response_hash || null, error: row.error || null, }; } function publicAssetImportRunSummary(run) { const { raw_response: _rawResponse, ...publicRun } = run; return publicRun; } async function seedKnownEnabledPairRuntimeConfig(pool, { now }) { const pairResult = await pool.query(`SELECT * FROM ${TRADING_PAIRS_TABLE}`); for (const row of pairResult.rows) { const pair = normalizeTradingPairRow(row); if (!pairCanMake(pair) && !pairCanTake(pair)) continue; await enableInventoryForAssets(pool, { assetIds: [pair.assetIn, pair.assetOut], now, }); const route = buildKnownPriceRouteForPair(pair); if (route) await upsertSeedPriceRoute(pool, { route, now }); } } function buildKnownPriceRouteForPair(pair) { const assets = new Set([pair?.assetIn, pair?.assetOut]); if (assets.has(CURRENT_NBTC_ASSET_ID) && assets.has(CURRENT_USDC_ASSET_ID)) { return buildBtcUsdcPriceRoute(pair.pairId); } return null; } async function enableInventoryForAssets(pool, { assetIds, now }) { const uniqueAssetIds = [...new Set(assetIds.filter(Boolean))]; if (!uniqueAssetIds.length) return; await pool.query( ` UPDATE ${TRADING_ASSETS_TABLE} SET enabled_for_inventory = true, updated_at = $2 WHERE asset_id = ANY($1::text[]) `, [uniqueAssetIds, now], ); } async function upsertSeedAsset(pool, { asset, now }) { await pool.query( ` INSERT INTO ${TRADING_ASSETS_TABLE} ( asset_id, venue, symbol, label, decimals, blockchain, chain, contract_address, latest_price, price_updated_at, supported, retired_at, enabled_for_inventory, role, withdraw_address, raw_payload, last_supported_at, updated_at ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,NULL,$12,$13,$14,$15::jsonb,$16,$16) ON CONFLICT (asset_id) DO UPDATE SET venue = EXCLUDED.venue, symbol = EXCLUDED.symbol, label = EXCLUDED.label, decimals = EXCLUDED.decimals, blockchain = EXCLUDED.blockchain, chain = EXCLUDED.chain, contract_address = COALESCE(${TRADING_ASSETS_TABLE}.contract_address, EXCLUDED.contract_address), supported = ${TRADING_ASSETS_TABLE}.supported OR EXCLUDED.supported, enabled_for_inventory = ${TRADING_ASSETS_TABLE}.enabled_for_inventory OR EXCLUDED.enabled_for_inventory, role = COALESCE(${TRADING_ASSETS_TABLE}.role, EXCLUDED.role), withdraw_address = COALESCE(NULLIF(${TRADING_ASSETS_TABLE}.withdraw_address, ''), EXCLUDED.withdraw_address), raw_payload = CASE WHEN ${TRADING_ASSETS_TABLE}.raw_payload = '{}'::jsonb THEN EXCLUDED.raw_payload ELSE ${TRADING_ASSETS_TABLE}.raw_payload END, last_supported_at = COALESCE(${TRADING_ASSETS_TABLE}.last_supported_at, EXCLUDED.last_supported_at), updated_at = EXCLUDED.updated_at `, [ asset.assetId, asset.venue, asset.symbol, asset.label, asset.decimals, asset.blockchain, asset.chain || asset.blockchain, asset.contractAddress, asset.latestPrice, asset.priceUpdatedAt, asset.supported, asset.enabledForInventory, asset.role, asset.withdrawAddress || '', JSON.stringify(asset.rawPayload || {}), now, ], ); } async function upsertSeedPair(pool, { pair, now, preserveRuntimeState = false }) { const conflictUpdate = preserveRuntimeState ? ` venue = EXCLUDED.venue, asset_in = EXCLUDED.asset_in, asset_out = EXCLUDED.asset_out, mode = ${TRADING_PAIRS_TABLE}.mode, enabled = ${TRADING_PAIRS_TABLE}.enabled, status = ${TRADING_PAIRS_TABLE}.status, updated_at = EXCLUDED.updated_at ` : ` venue = EXCLUDED.venue, asset_in = EXCLUDED.asset_in, asset_out = EXCLUDED.asset_out, mode = EXCLUDED.mode, enabled = EXCLUDED.enabled, status = EXCLUDED.status, updated_at = EXCLUDED.updated_at `; await pool.query( ` INSERT INTO ${TRADING_PAIRS_TABLE} ( pair_id, venue, asset_in, asset_out, mode, enabled, status, created_at, updated_at ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$8) ON CONFLICT (pair_id) DO UPDATE SET ${conflictUpdate} `, [ pair.pairId, pair.venue, pair.assetIn, pair.assetOut, pair.mode, pair.enabled, pair.status, now, ], ); } async function upsertSeedStrategyConfig(pool, { config }) { await insertPairStrategyConfig(pool, { config, active: config.active !== false }); } async function insertPairStrategyConfig(pool, { config, active = true }) { await pool.query( ` INSERT INTO ${PAIR_STRATEGY_CONFIGS_TABLE} ( config_id, pair_id, version, active, edge_bps, max_notional, min_notional, slippage_bps, min_deadline_ms, price_max_age_ms, inventory_max_age_ms, request_default_notional, request_max_notional, request_max_slippage_bps, created_by, reason ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16) ON CONFLICT (config_id) DO NOTHING `, [ config.configId, config.pairId, config.version, active, config.edgeBps, config.maxNotional, config.minNotional, config.slippageBps, config.minDeadlineMs, config.priceMaxAgeMs, config.inventoryMaxAgeMs, config.requestDefaultNotional, config.requestMaxNotional, config.requestMaxSlippageBps, config.createdBy, config.reason, ], ); } async function upsertSeedPriceRoute(pool, { route, now }) { await pool.query( ` INSERT INTO ${PAIR_PRICE_ROUTES_TABLE} ( route_id, pair_id, source, base_asset_id, quote_asset_id, route_config, max_age_ms, enabled, created_at, updated_at ) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7,$8,$9,$9) ON CONFLICT (route_id) DO UPDATE SET source = EXCLUDED.source, base_asset_id = EXCLUDED.base_asset_id, quote_asset_id = EXCLUDED.quote_asset_id, route_config = EXCLUDED.route_config, max_age_ms = EXCLUDED.max_age_ms, enabled = ${PAIR_PRICE_ROUTES_TABLE}.enabled, updated_at = EXCLUDED.updated_at `, [ route.routeId, route.pairId, route.source, route.baseAssetId, route.quoteAssetId, JSON.stringify(route.routeConfig || {}), route.maxAgeMs, route.enabled, now, ], ); } async function loadTradingAssetsById(pool) { const result = await pool.query(`SELECT * FROM ${TRADING_ASSETS_TABLE}`); return new Map(result.rows.map((row) => [row.asset_id, normalizeTradingAssetRow(row)])); } async function upsertImportedAsset(pool, { asset, fetchedAt }) { await pool.query( ` INSERT INTO ${TRADING_ASSETS_TABLE} ( asset_id, venue, symbol, label, decimals, blockchain, chain, contract_address, latest_price, price_updated_at, supported, retired_at, enabled_for_inventory, raw_payload, last_supported_at, updated_at ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,true,NULL,false,$11::jsonb,$12,$12) ON CONFLICT (asset_id) DO UPDATE SET venue = EXCLUDED.venue, symbol = EXCLUDED.symbol, label = EXCLUDED.label, decimals = EXCLUDED.decimals, blockchain = EXCLUDED.blockchain, chain = EXCLUDED.chain, contract_address = EXCLUDED.contract_address, latest_price = EXCLUDED.latest_price, price_updated_at = EXCLUDED.price_updated_at, supported = true, retired_at = NULL, enabled_for_inventory = ${TRADING_ASSETS_TABLE}.enabled_for_inventory, raw_payload = EXCLUDED.raw_payload, last_supported_at = EXCLUDED.last_supported_at, updated_at = EXCLUDED.updated_at `, [ asset.assetId, asset.venue, asset.symbol, asset.label, asset.decimals, asset.blockchain, asset.chain || asset.blockchain, asset.contractAddress, asset.latestPrice, asset.priceUpdatedAt, JSON.stringify(asset.rawPayload || {}), fetchedAt, ], ); } async function insertAssetImportRun(pool, run) { await pool.query( ` INSERT INTO ${SUPPORTED_ASSET_IMPORT_RUNS_TABLE} ( run_id, source_url, fetched_at, status, token_count, added_count, updated_count, unchanged_count, retired_count, raw_response_hash, error, raw_response ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12::jsonb) ON CONFLICT (run_id) DO UPDATE SET status = EXCLUDED.status, token_count = EXCLUDED.token_count, added_count = EXCLUDED.added_count, updated_count = EXCLUDED.updated_count, unchanged_count = EXCLUDED.unchanged_count, retired_count = EXCLUDED.retired_count, raw_response_hash = EXCLUDED.raw_response_hash, error = EXCLUDED.error, raw_response = EXCLUDED.raw_response `, [ run.run_id, run.source_url, run.fetched_at, run.status, run.token_count, run.added_count, run.updated_count, run.unchanged_count, run.retired_count, run.raw_response_hash, run.error, run.raw_response == null ? null : JSON.stringify(run.raw_response), ], ); } async function insertConfigAuditLog(pool, { entityType, entityId, action, oldValue = null, newValue = null, changedBy, reason, }) { await pool.query( ` INSERT INTO ${PAIR_CONFIG_AUDIT_LOG_TABLE} ( audit_id, entity_type, entity_id, action, old_value, new_value, changed_by, reason ) VALUES ($1,$2,$3,$4,$5::jsonb,$6::jsonb,$7,$8) `, [ `audit:${Date.now()}:${Math.random().toString(16).slice(2)}`, entityType, entityId, action, oldValue == null ? null : JSON.stringify(oldValue), newValue == null ? null : JSON.stringify(newValue), changedBy, reason, ], ); } function importedAssetChanged(previous, next) { return ( previous.symbol !== next.symbol || previous.label !== next.label || previous.decimals !== next.decimals || previous.blockchain !== next.blockchain || previous.contractAddress !== next.contractAddress || previous.latestPrice !== next.latestPrice || previous.priceUpdatedAt !== next.priceUpdatedAt || previous.supported !== true || previous.retiredAt != null ); } export async function insertHistoryEvent(pool, { table, topic, event, record }) { await pool.query( ` INSERT INTO ${table} ( event_id, topic, venue, source, event_type, observed_at, ingested_at, quote_id, pair, decision_key, payload, raw ) VALUES ( $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12::jsonb ) ON CONFLICT (event_id) DO NOTHING `, [ event.event_id, topic, event.venue, event.source, event.event_type, event.observed_at, event.ingested_at, record.quote_id, record.pair, record.decision_key, JSON.stringify(event.payload), event.raw ? JSON.stringify(event.raw) : null, ], ); } export async function insertEnvironmentStatusChange(pool, { topic, event, record }) { const fingerprint = event.payload?.status_fingerprint || null; const environmentKey = event.payload?.environment_key || record.decision_key || null; const result = await pool.query( ` WITH latest AS ( SELECT payload->>'status_fingerprint' AS status_fingerprint FROM environment_status_events WHERE decision_key = $10 ORDER BY COALESCE(observed_at, ingested_at) DESC, ingested_at DESC LIMIT 1 ) INSERT INTO environment_status_events ( event_id, topic, venue, source, event_type, observed_at, ingested_at, quote_id, pair, decision_key, payload, raw ) SELECT $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12::jsonb WHERE NOT EXISTS ( SELECT 1 FROM latest WHERE status_fingerprint = $13 ) ON CONFLICT (event_id) DO NOTHING RETURNING event_id `, [ event.event_id, topic, event.venue, event.source, event.event_type, event.observed_at, event.ingested_at, record.quote_id, record.pair, environmentKey, JSON.stringify(event.payload), event.raw ? JSON.stringify(event.raw) : null, fingerprint, ], ); return { inserted: result.rowCount > 0, status_fingerprint: fingerprint, environment_key: environmentKey, }; } export async function loadPortfolioMetricInputs(pool, { btcAsset = null, btcAssets = null, eureAsset = null, trackedAssets = [], } = {}) { const effectiveBtcAssets = normalizeBtcAssets({ btcAsset, btcAssets }); const [currentInventory, currentPrice, currentPriceEvents, commandAggregate, resultAggregate] = await Promise.all([ loadLatestEventPayload(pool, 'intent_inventory_snapshots'), loadLatestEventPayload( pool, 'market_price_events', "WHERE COALESCE(payload->>'eure_per_btc', '') <> '' ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT 1", ), loadLatestMarketPricePayloadsByRoute(pool), pool.query(` SELECT MIN(ingested_at) AS first_command_at, COUNT(*)::INT AS command_count FROM execute_trade_commands `), pool.query(` SELECT COUNT(*)::INT AS result_count FROM trade_execution_results `), ]); const firstCommandAt = commandAggregate.rows[0]?.first_command_at || null; const commandCount = Number(commandAggregate.rows[0]?.command_count || 0); const resultCount = Number(resultAggregate.rows[0]?.result_count || 0); const valuationAssets = buildCashEquivalentValuationAssets({ trackedAssets, btcAsset: effectiveBtcAssets[0], btcAssets: effectiveBtcAssets, eureAsset, priceEvents: currentPriceEvents.map((entry) => entry.payload), }); if (!firstCommandAt) { return { currentInventory, currentPrice, currentPriceEvents, valuationAssets, baseline: null, commandCount, resultCount, }; } const baselineInventory = await loadLatestEventPayload( pool, 'intent_inventory_snapshots', 'WHERE ingested_at <= $1 ORDER BY ingested_at DESC LIMIT 1', [firstCommandAt], ); const baselinePrice = await loadNearestPricePayload(pool, baselineInventory?.ingested_at || firstCommandAt); const externalFlows = ( baselineInventory && baselinePrice && effectiveBtcAssets.length && eureAsset?.assetId ) ? await loadExternalAssetFlowsSince(pool, { since: firstCommandAt, btcAsset: effectiveBtcAssets[0], btcAssets: effectiveBtcAssets, eureAsset, valuationAssets, }) : []; return { currentInventory, currentPrice, currentPriceEvents, valuationAssets, baseline: baselineInventory && baselinePrice ? { anchor: 'latest_inventory_before_first_command', command_at: new Date(firstCommandAt).toISOString(), inventory: baselineInventory.payload, price: baselinePrice.payload, } : null, externalFlows, commandCount, resultCount, }; } async function loadLatestMarketPricePayloadsByRoute(pool) { const result = await pool.query(` SELECT DISTINCT ON (payload->>'price_route_id') observed_at, ingested_at, payload FROM market_price_events WHERE COALESCE(payload->>'price_route_id', '') <> '' ORDER BY payload->>'price_route_id', COALESCE(observed_at, ingested_at) DESC `); return result.rows.map((row) => ({ observed_at: toIsoTimestamp(row.observed_at), ingested_at: toIsoTimestamp(row.ingested_at), payload: { ...(row.payload || {}), observed_at: row.payload?.observed_at || toIsoTimestamp(row.observed_at), ingested_at: row.payload?.ingested_at || toIsoTimestamp(row.ingested_at), }, })); } export async function upsertPortfolioMetric(pool, { metricId, computedAt, baselineAnchorAt = null, baselineStatus, payload, }) { await pool.query( ` INSERT INTO ${PORTFOLIO_METRICS_TABLE} ( metric_id, computed_at, baseline_anchor_at, baseline_status, payload ) VALUES ($1, $2, $3, $4, $5::jsonb) ON CONFLICT (metric_id) DO UPDATE SET computed_at = EXCLUDED.computed_at, baseline_anchor_at = EXCLUDED.baseline_anchor_at, baseline_status = EXCLUDED.baseline_status, payload = EXCLUDED.payload `, [ metricId, computedAt, baselineAnchorAt, baselineStatus, JSON.stringify(payload), ], ); } export async function loadLatestPortfolioMetric(pool) { const result = await pool.query(` SELECT metric_id, computed_at, baseline_anchor_at, baseline_status, payload FROM ${PORTFOLIO_METRICS_TABLE} ORDER BY computed_at DESC LIMIT 1 `); if (!result.rows[0]) return null; return normalizePortfolioMetricRow(result.rows[0]); } export async function claimNotificationDelivery(pool, { notificationKey, notificationType, sourceKind, sourceId = null, payload = {}, }) { if (!notificationKey) throw new Error('notificationKey is required'); if (!notificationType) throw new Error('notificationType is required'); if (!sourceKind) throw new Error('sourceKind is required'); const result = await pool.query( ` INSERT INTO notification_deliveries ( notification_key, notification_type, source_kind, source_id, status, attempt_count, payload, response, error ) VALUES ($1, $2, $3, $4, 'pending', 1, $5::jsonb, NULL, NULL) ON CONFLICT (notification_key) DO UPDATE SET status = 'pending', attempt_count = notification_deliveries.attempt_count + 1, last_attempt_at = NOW(), payload = EXCLUDED.payload, response = NULL, error = NULL WHERE notification_deliveries.status <> 'delivered' RETURNING notification_key `, [ notificationKey, notificationType, sourceKind, sourceId, JSON.stringify(payload || {}), ], ); return result.rowCount > 0; } export async function finishNotificationDelivery(pool, { notificationKey, ok, response = null, error = null, }) { if (!notificationKey) throw new Error('notificationKey is required'); await pool.query( ` UPDATE notification_deliveries SET status = $2, delivered_at = CASE WHEN $2 = 'delivered' THEN NOW() ELSE delivered_at END, last_attempt_at = NOW(), response = $3::jsonb, error = $4::jsonb WHERE notification_key = $1 `, [ notificationKey, ok ? 'delivered' : 'failed', response ? JSON.stringify(response) : null, error ? JSON.stringify(error) : null, ], ); } export async function refreshQuoteOutcomes(pool, { btcAsset = null, eureAsset = null, now = Date.now(), submissionLimit = 1000, inventoryLimit = 5000, } = {}) { if (!btcAsset?.assetId || !eureAsset?.assetId) return []; const safeSubmissionLimit = Math.max(1, Number(submissionLimit) || 1000); const safeInventoryLimit = Math.max(1, Number(inventoryLimit) || 5000); const submissionsResult = await pool.query( ` SELECT event_id, observed_at, ingested_at, quote_id, payload FROM ( SELECT event_id, observed_at, ingested_at, quote_id, payload FROM trade_execution_results WHERE payload->>'status' = 'submitted' ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT $1 ) recent_submissions ORDER BY COALESCE(observed_at, ingested_at) ASC `, [safeSubmissionLimit], ); if (!submissionsResult.rows.length) return []; const quoteIds = [...new Set(submissionsResult.rows.map((row) => row.quote_id).filter(Boolean))]; if (!quoteIds.length) return []; const [ commandsResult, decisionsResult, inventoryResult, ] = await Promise.all([ pool.query(` SELECT event_id, observed_at, ingested_at, quote_id, payload FROM execute_trade_commands WHERE quote_id = ANY($1::text[]) ORDER BY COALESCE(observed_at, ingested_at) ASC `, [quoteIds]), pool.query(` SELECT event_id, observed_at, ingested_at, quote_id, payload FROM trade_decisions WHERE quote_id = ANY($1::text[]) ORDER BY COALESCE(observed_at, ingested_at) ASC `, [quoteIds]), pool.query(` SELECT event_id, observed_at, ingested_at, quote_id, payload FROM ( SELECT event_id, observed_at, ingested_at, quote_id, payload FROM intent_inventory_snapshots ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT $1 ) recent_inventory_snapshots ORDER BY COALESCE(observed_at, ingested_at) ASC `, [safeInventoryLimit]), ]); const records = deriveQuoteOutcomeRecords({ submissions: submissionsResult.rows, commands: commandsResult.rows, decisions: decisionsResult.rows, inventorySnapshots: inventoryResult.rows, btcAsset, eureAsset, now, }); if (!records.length) return []; const computedAt = new Date( typeof now === 'number' ? now : Date.parse(now), ).toISOString(); for (const record of records) { await upsertQuoteOutcome(pool, { ...record, computedAt, }); } return records; } export async function upsertQuoteOutcome(pool, { quote_id, decision_id = null, command_id = null, execution_result_status, execution_result_code = null, submitted_at = null, command_at = null, outcome_status, outcome_observed_at = null, outcome_source, attribution_status, attribution_method = null, attributed_inventory_delta = null, computedAt, payload, }) { await pool.query( ` INSERT INTO ${QUOTE_OUTCOMES_TABLE} ( quote_id, decision_id, command_id, execution_result_status, execution_result_code, submitted_at, command_at, outcome_status, outcome_observed_at, outcome_source, attribution_status, attribution_method, attributed_inventory_delta, computed_at, payload ) VALUES ( $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13::jsonb,$14,$15::jsonb ) ON CONFLICT (quote_id) DO UPDATE SET decision_id = EXCLUDED.decision_id, command_id = EXCLUDED.command_id, execution_result_status = EXCLUDED.execution_result_status, execution_result_code = EXCLUDED.execution_result_code, submitted_at = EXCLUDED.submitted_at, command_at = EXCLUDED.command_at, outcome_status = EXCLUDED.outcome_status, outcome_observed_at = EXCLUDED.outcome_observed_at, outcome_source = EXCLUDED.outcome_source, attribution_status = EXCLUDED.attribution_status, attribution_method = EXCLUDED.attribution_method, attributed_inventory_delta = EXCLUDED.attributed_inventory_delta, computed_at = EXCLUDED.computed_at, payload = EXCLUDED.payload `, [ quote_id, decision_id, command_id, execution_result_status, execution_result_code, submitted_at, command_at, outcome_status, outcome_observed_at, outcome_source, attribution_status, attribution_method, attributed_inventory_delta ? JSON.stringify(attributed_inventory_delta) : null, computedAt, JSON.stringify(payload || {}), ], ); } export async function loadRecentQuoteOutcomes(pool, { limit = 200 } = {}) { const result = await pool.query( ` SELECT quote_id, decision_id, command_id, execution_result_status, execution_result_code, submitted_at, command_at, outcome_status, outcome_observed_at, outcome_source, attribution_status, attribution_method, attributed_inventory_delta, computed_at, payload FROM ${QUOTE_OUTCOMES_TABLE} ORDER BY CASE outcome_status WHEN 'completed' THEN 0 WHEN 'not_filled' THEN 1 ELSE 2 END, COALESCE(outcome_observed_at, submitted_at, computed_at) DESC LIMIT $1 `, [Math.max(1, Number(limit) || 200)], ); return result.rows.map(normalizeQuoteOutcomeRow); } export async function loadIntentRequestPreflightByIdOrKey(pool, { requestId = null, idempotencyKey = null, } = {}) { const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM intent_request_preflights WHERE ($1::text IS NOT NULL AND payload->>'request_id' = $1) OR ($2::text IS NOT NULL AND payload->>'idempotency_key' = $2) ORDER BY COALESCE(observed_at, ingested_at) DESC, CASE payload->>'status' WHEN 'accepted_by_relay' THEN 0 WHEN 'failed' THEN 1 WHEN 'blocked' THEN 2 WHEN 'submit_requested' THEN 3 ELSE 4 END LIMIT 1 `, [requestId, idempotencyKey], ); return normalizeEventPayloadRow(result.rows[0])?.payload || null; } export async function loadLatestIntentRequestSubmission(pool, { requestId } = {}) { if (!requestId) return null; const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM intent_request_submission_results WHERE payload->>'request_id' = $1 ORDER BY COALESCE(observed_at, ingested_at) DESC, CASE payload->>'status' WHEN 'accepted_by_relay' THEN 0 WHEN 'failed' THEN 1 WHEN 'blocked' THEN 2 WHEN 'submit_requested' THEN 3 ELSE 4 END LIMIT 1 `, [requestId], ); return normalizeEventPayloadRow(result.rows[0])?.payload || null; } export async function loadIntentRequestSubmissionsForStatusRefresh(pool, { limit = 20, } = {}) { const result = await pool.query( ` WITH latest_submissions AS ( SELECT DISTINCT ON (payload->>'request_id') observed_at, ingested_at, payload FROM intent_request_submission_results WHERE COALESCE(payload->>'request_id', '') <> '' ORDER BY payload->>'request_id', COALESCE(observed_at, ingested_at) DESC, CASE payload->>'status' WHEN 'accepted_by_relay' THEN 0 WHEN 'failed' THEN 1 WHEN 'blocked' THEN 2 WHEN 'submit_requested' THEN 3 ELSE 4 END ) SELECT observed_at, ingested_at, payload FROM latest_submissions WHERE payload->>'status' = 'accepted_by_relay' AND COALESCE(payload->>'intent_hash', '') <> '' AND COALESCE(payload->>'relay_status', '') NOT IN ('SETTLED', 'NOT_FOUND_OR_NOT_VALID') ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT $1 `, [Math.max(1, Number(limit) || 20)], ); return result.rows .map((row) => normalizeIntentRequestSubmissionPayload(normalizeEventPayloadRow(row)?.payload || null)) .filter(Boolean); } export async function refreshIntentRequestOutcomes(pool, { btcAsset = null, eureAsset = null, now = Date.now(), preflightLimit = 100, submissionLimit = 500, inventoryLimit = 5000, } = {}) { if (!btcAsset?.assetId || !eureAsset?.assetId) return []; const safePreflightLimit = Math.max(1, Number(preflightLimit) || 100); const safeSubmissionLimit = Math.max(1, Number(submissionLimit) || 500); const safeInventoryLimit = Math.max(1, Number(inventoryLimit) || 5000); const preflightResult = await loadIntentRequestPreflightRefreshCandidates(pool, { limit: safePreflightLimit, }); if (!preflightResult.rows.length) return []; const requestIds = [ ...new Set(preflightResult.rows .map((row) => row.payload?.request_id) .filter(Boolean)), ]; if (!requestIds.length) return []; const submissions = await loadIntentRequestSubmissionsForRefresh(pool, { requestIds, limit: safeSubmissionLimit, }); const inventorySnapshots = await loadIntentInventorySnapshotsForRequestRefresh(pool, { submissions: submissions.rows, limit: safeInventoryLimit, }); const records = deriveIntentRequestOutcomeRecords({ preflights: preflightResult.rows, submissions: submissions.rows, inventorySnapshots: inventorySnapshots.rows, btcAsset, eureAsset, now, }); if (!records.length) return []; const computedAt = new Date( typeof now === 'number' ? now : Date.parse(now), ).toISOString(); for (const record of records) { await upsertIntentRequestOutcome(pool, { ...record, computedAt, }); } return records; } async function loadIntentRequestPreflightRefreshCandidates(pool, { limit }) { return pool.query( ` WITH latest_preflights AS ( SELECT DISTINCT ON (payload->>'request_id') event_id, observed_at, ingested_at, payload FROM intent_request_preflights WHERE COALESCE(payload->>'request_id', '') <> '' ORDER BY payload->>'request_id', COALESCE(observed_at, ingested_at) DESC ), latest_submissions AS ( SELECT DISTINCT ON (payload->>'request_id') payload->>'request_id' AS request_id, observed_at, ingested_at, payload FROM intent_request_submission_results WHERE COALESCE(payload->>'request_id', '') <> '' ORDER BY payload->>'request_id', COALESCE(observed_at, ingested_at) DESC, CASE payload->>'status' WHEN 'accepted_by_relay' THEN 0 WHEN 'failed' THEN 1 WHEN 'blocked' THEN 2 WHEN 'submit_requested' THEN 3 ELSE 4 END ), refresh_candidates AS ( SELECT p.event_id, p.observed_at, p.ingested_at, p.payload, COALESCE( s.observed_at, s.ingested_at, p.observed_at, p.ingested_at ) AS refresh_sort_at FROM latest_preflights p LEFT JOIN latest_submissions s ON s.request_id = p.payload->>'request_id' LEFT JOIN ${INTENT_REQUEST_OUTCOMES_TABLE} o ON o.request_id = p.payload->>'request_id' WHERE o.request_id IS NULL OR o.outcome_status = ANY($1::text[]) OR COALESCE( s.observed_at, s.ingested_at, p.observed_at, p.ingested_at ) > o.computed_at ORDER BY refresh_sort_at DESC LIMIT $2 ) SELECT event_id, observed_at, ingested_at, payload FROM refresh_candidates ORDER BY COALESCE(observed_at, ingested_at) ASC `, [REFRESHABLE_INTENT_REQUEST_OUTCOME_STATUSES, limit], ); } async function loadIntentRequestSubmissionsForRefresh(pool, { requestIds, limit }) { return pool.query( ` SELECT event_id, observed_at, ingested_at, payload FROM intent_request_submission_results WHERE payload->>'request_id' = ANY($1::text[]) ORDER BY COALESCE(observed_at, ingested_at) ASC LIMIT $2 `, [requestIds, limit], ); } async function loadIntentInventorySnapshotsForRequestRefresh(pool, { submissions, limit, }) { const anchorAt = earliestIntentRequestInventoryAnchor({ submissions, }); if (!anchorAt) return { rows: [] }; return pool.query( ` WITH previous_snapshot AS ( SELECT event_id, observed_at, ingested_at, quote_id, payload, COALESCE(observed_at, ingested_at) AS sort_at FROM intent_inventory_snapshots WHERE COALESCE(observed_at, ingested_at) < $1 ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT 1 ), following_snapshots AS ( SELECT event_id, observed_at, ingested_at, quote_id, payload, COALESCE(observed_at, ingested_at) AS sort_at FROM intent_inventory_snapshots WHERE COALESCE(observed_at, ingested_at) >= $1 ORDER BY COALESCE(observed_at, ingested_at) ASC LIMIT $2 ), bounded_snapshots AS ( SELECT event_id, observed_at, ingested_at, quote_id, payload, sort_at FROM previous_snapshot UNION ALL SELECT event_id, observed_at, ingested_at, quote_id, payload, sort_at FROM following_snapshots ) SELECT event_id, observed_at, ingested_at, quote_id, payload FROM bounded_snapshots ORDER BY sort_at ASC `, [anchorAt, limit], ); } function earliestIntentRequestInventoryAnchor({ submissions = [] } = {}) { const anchors = submissions .map((row) => row.payload?.submitted_at || row.observed_at || row.ingested_at) .map((value) => { const timestamp = timestampValue(value); return Number.isFinite(timestamp) ? timestamp : null; }) .filter((value) => value != null) .sort((left, right) => left - right); return anchors.length ? new Date(anchors[0]).toISOString() : null; } export async function upsertIntentRequestOutcome(pool, { request_id, idempotency_key, submission_id = null, intent_hash = null, submission_status = null, relay_status = null, submitted_at = null, outcome_status, outcome_observed_at = null, outcome_source, outcome_reason, attribution_status, attribution_method = null, attributed_inventory_delta = null, computedAt, payload, }) { await pool.query( ` INSERT INTO ${INTENT_REQUEST_OUTCOMES_TABLE} ( request_id, idempotency_key, submission_id, intent_hash, submission_status, relay_status, submitted_at, outcome_status, outcome_observed_at, outcome_source, outcome_reason, attribution_status, attribution_method, attributed_inventory_delta, computed_at, payload ) VALUES ( $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14::jsonb,$15,$16::jsonb ) ON CONFLICT (request_id) DO UPDATE SET idempotency_key = EXCLUDED.idempotency_key, submission_id = EXCLUDED.submission_id, intent_hash = EXCLUDED.intent_hash, submission_status = EXCLUDED.submission_status, relay_status = EXCLUDED.relay_status, submitted_at = EXCLUDED.submitted_at, outcome_status = EXCLUDED.outcome_status, outcome_observed_at = EXCLUDED.outcome_observed_at, outcome_source = EXCLUDED.outcome_source, outcome_reason = EXCLUDED.outcome_reason, attribution_status = EXCLUDED.attribution_status, attribution_method = EXCLUDED.attribution_method, attributed_inventory_delta = EXCLUDED.attributed_inventory_delta, computed_at = EXCLUDED.computed_at, payload = EXCLUDED.payload `, [ request_id, idempotency_key, submission_id, intent_hash, submission_status, relay_status, submitted_at, outcome_status, outcome_observed_at, outcome_source, outcome_reason, attribution_status, attribution_method, attributed_inventory_delta ? JSON.stringify(attributed_inventory_delta) : null, computedAt, JSON.stringify(payload || {}), ], ); } export async function loadRecentIntentRequests(pool, { limit = 20, btcAsset = null, eureAsset = null, now = Date.now(), refreshOutcomes = true, } = {}) { if (refreshOutcomes && btcAsset?.assetId && eureAsset?.assetId) { await refreshIntentRequestOutcomes(pool, { btcAsset, eureAsset, now }).catch(() => []); } const result = await pool.query( ` WITH latest_preflights AS ( SELECT DISTINCT ON (payload->>'request_id') observed_at AS preflight_observed_at, ingested_at AS preflight_ingested_at, payload AS preflight_payload FROM intent_request_preflights WHERE COALESCE(payload->>'request_id', '') <> '' ORDER BY payload->>'request_id', COALESCE(observed_at, ingested_at) DESC ), latest_submissions AS ( SELECT DISTINCT ON (payload->>'request_id') observed_at AS submission_observed_at, ingested_at AS submission_ingested_at, payload AS submission_payload FROM intent_request_submission_results WHERE COALESCE(payload->>'request_id', '') <> '' ORDER BY payload->>'request_id', COALESCE(observed_at, ingested_at) DESC, CASE payload->>'status' WHEN 'accepted_by_relay' THEN 0 WHEN 'failed' THEN 1 WHEN 'blocked' THEN 2 WHEN 'submit_requested' THEN 3 ELSE 4 END ) SELECT p.preflight_observed_at, p.preflight_ingested_at, p.preflight_payload, s.submission_observed_at, s.submission_ingested_at, s.submission_payload, o.outcome_observed_at, o.computed_at AS outcome_computed_at, o.payload AS outcome_payload FROM latest_preflights p LEFT JOIN latest_submissions s ON s.submission_payload->>'request_id' = p.preflight_payload->>'request_id' LEFT JOIN ${INTENT_REQUEST_OUTCOMES_TABLE} o ON o.request_id = p.preflight_payload->>'request_id' ORDER BY COALESCE( o.outcome_observed_at, s.submission_observed_at, s.submission_ingested_at, p.preflight_observed_at, p.preflight_ingested_at ) DESC LIMIT $1 `, [Math.max(1, Number(limit) || 20)], ); return result.rows.map(normalizeIntentRequestRow); } export async function loadLatestInventorySnapshot(pool) { const latest = await loadLatestEventPayload(pool, 'intent_inventory_snapshots'); if (!latest) return null; return { ingested_at: latest.ingested_at, payload: latest.payload, }; } export async function loadLatestMarketPrice(pool) { const latest = await loadLatestEventPayload(pool, 'market_price_events'); if (!latest) return null; return { ingested_at: latest.ingested_at, payload: latest.payload, }; } export async function loadRecentQuotes(pool, { limit = 10 } = {}) { const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM swap_demand_events ORDER BY ingested_at DESC LIMIT $1 `, [limit], ); return result.rows.map(normalizeRecentQuoteRow); } export async function loadSubmissionSummary(pool) { const result = await pool.query(` SELECT COUNT(*)::INT AS total, MAX(COALESCE(observed_at, ingested_at)) AS last_submission_at FROM trade_execution_results WHERE payload->>'status' = 'submitted' `); return { total: Number(result.rows[0]?.total || 0), last_submission_at: toIsoTimestamp(result.rows[0]?.last_submission_at), }; } export async function loadSubmissionPage(pool, { page = 1, pageSize = 20 } = {}) { const normalizedPage = Math.max(1, Number(page) || 1); const normalizedPageSize = Math.max(1, Number(pageSize) || 20); const offset = (normalizedPage - 1) * normalizedPageSize; const [countResult, rowsResult] = await Promise.all([ pool.query(` SELECT COUNT(*)::INT AS total FROM trade_execution_results WHERE payload->>'status' = 'submitted' `), pool.query( ` SELECT r.observed_at AS result_observed_at, r.ingested_at AS result_ingested_at, r.payload AS result_payload, c.payload AS command_payload, d.payload AS decision_payload, o.payload AS outcome_payload FROM trade_execution_results r LEFT JOIN execute_trade_commands c ON c.decision_key = r.decision_key LEFT JOIN trade_decisions d ON d.decision_key = COALESCE(c.payload->>'decision_id', r.payload->>'decision_id') LEFT JOIN ${QUOTE_OUTCOMES_TABLE} o ON o.quote_id = r.quote_id WHERE r.payload->>'status' = 'submitted' ORDER BY COALESCE(r.observed_at, r.ingested_at) DESC LIMIT $1 OFFSET $2 `, [normalizedPageSize, offset], ), ]); const total = Number(countResult.rows[0]?.total || 0); return { page: normalizedPage, page_size: normalizedPageSize, total, total_pages: total > 0 ? Math.ceil(total / normalizedPageSize) : 1, items: rowsResult.rows.map(normalizeSubmissionRow), }; } export async function loadRecentExecutionResults(pool, { limit = 20 } = {}) { const result = await pool.query( ` SELECT r.observed_at AS result_observed_at, r.ingested_at AS result_ingested_at, r.payload AS result_payload, c.ingested_at AS command_ingested_at, c.payload AS command_payload, d.payload AS decision_payload, o.payload AS outcome_payload FROM trade_execution_results r LEFT JOIN execute_trade_commands c ON c.decision_key = r.decision_key LEFT JOIN trade_decisions d ON d.decision_key = COALESCE(c.payload->>'decision_id', r.payload->>'decision_id') LEFT JOIN ${QUOTE_OUTCOMES_TABLE} o ON o.quote_id = r.quote_id ORDER BY COALESCE(r.observed_at, r.ingested_at) DESC LIMIT $1 `, [limit], ); return result.rows.map((row) => normalizeExecutionResultRow(row)); } export async function loadRecentExecuteTradeCommands(pool, { limit = 20 } = {}) { const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM execute_trade_commands ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT $1 `, [limit], ); return result.rows.map((row) => normalizeExecuteTradeCommandRow(row)); } export async function loadCurrentFundingObservations(pool) { const result = await pool.query(` SELECT DISTINCT ON (decision_key) observed_at, ingested_at, payload FROM funding_observations WHERE decision_key IS NOT NULL ORDER BY decision_key, ingested_at DESC `); return result.rows .map((row) => ({ observed_at: toIsoTimestamp(row.observed_at), ingested_at: toIsoTimestamp(row.ingested_at), payload: row.payload, })) .sort((left, right) => ( Date.parse( right.payload?.last_seen_at || right.observed_at || right.ingested_at || '', ) - Date.parse( left.payload?.last_seen_at || left.observed_at || left.ingested_at || '', ) )); } export async function loadRecentDepositStatuses(pool, { limit = 20 } = {}) { const normalizedLimit = Math.max(1, Number(limit) || 20); const fetchLimit = Math.max(normalizedLimit * 50, 500); const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM liquidity_actions WHERE payload->>'action_type' = 'deposit_status_observed' ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT $1 `, [fetchLimit], ); return normalizeDepositStatusRows(result.rows) .sort((left, right) => ( timestampValue(right.observed_at || right.ingested_at) - timestampValue(left.observed_at || left.ingested_at) )) .slice(0, normalizedLimit); } export async function loadRecentAlertTransitions(pool, { limit = 20 } = {}) { const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM ops_alerts ORDER BY ingested_at DESC LIMIT $1 `, [limit], ); return result.rows.map((row) => ({ observed_at: toIsoTimestamp(row.observed_at), ingested_at: toIsoTimestamp(row.ingested_at), payload: row.payload, })); } export async function loadRecentEnvironmentStatuses(pool, { limit = 20 } = {}) { const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM environment_status_events ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT $1 `, [Math.max(1, Number(limit) || 20)], ); return result.rows.map((row) => ({ observed_at: toIsoTimestamp(row.observed_at), ingested_at: toIsoTimestamp(row.ingested_at), payload: row.payload, })); } export async function loadRecentTradeDecisions(pool, { limit = 20 } = {}) { const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM trade_decisions ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT $1 `, [limit], ); return result.rows.map((row) => ({ observed_at: toIsoTimestamp(row.observed_at), ingested_at: toIsoTimestamp(row.ingested_at), payload: row.payload, })); } async function loadLatestEventPayload(pool, table, clause = 'ORDER BY ingested_at DESC LIMIT 1', params = []) { const result = await pool.query( ` SELECT ingested_at, payload FROM ${table} ${clause} `, params, ); if (!result.rows[0]) return null; return { ingested_at: result.rows[0].ingested_at ? new Date(result.rows[0].ingested_at).toISOString() : null, payload: result.rows[0].payload, }; } async function loadNearestPricePayload(pool, anchorAt) { const before = await loadLatestEventPayload( pool, 'market_price_events', 'WHERE ingested_at <= $1 ORDER BY ingested_at DESC LIMIT 1', [anchorAt], ); if (before) return before; return loadLatestEventPayload( pool, 'market_price_events', 'WHERE ingested_at >= $1 ORDER BY ingested_at ASC LIMIT 1', [anchorAt], ); } async function loadExternalAssetFlowsSince(pool, { since, btcAsset, btcAssets = null, eureAsset, valuationAssets = [], } = {}) { const effectiveBtcAssets = normalizeBtcAssets({ btcAsset, btcAssets }); const [depositRows, withdrawalRows] = await Promise.all([ loadCreditedDepositRowsSince(pool, since), loadCompletedWithdrawalRowsSince(pool, since), ]); const flows = []; for (const row of depositRows) { flows.push(await normalizeExternalFlowRow(pool, { row, kind: 'deposit', sign: 1n, btcAsset: effectiveBtcAssets[0], btcAssets: effectiveBtcAssets, eureAsset, valuationAssets, })); } for (const row of withdrawalRows) { flows.push(await normalizeExternalFlowRow(pool, { row, kind: 'withdrawal', sign: -1n, btcAsset: effectiveBtcAssets[0], btcAssets: effectiveBtcAssets, eureAsset, valuationAssets, })); } return flows .filter(Boolean) .sort((left, right) => timestampValue(left.effective_at) - timestampValue(right.effective_at)); } async function loadCreditedDepositRowsSince(pool, since) { const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM liquidity_actions WHERE payload->>'action_type' = 'deposit_status_observed' AND UPPER(payload->>'status') = ANY($1) AND COALESCE(payload->'details'->>'tx_hash', '') <> '' ORDER BY COALESCE(observed_at, ingested_at) DESC `, [CREDITED_LIQUIDITY_STATUSES], ); return normalizeDepositStatusRows(result.rows) .filter((row) => timestampValue(row.observed_at || row.ingested_at) > timestampValue(since)); } async function loadCompletedWithdrawalRowsSince(pool, since) { const result = await pool.query( ` SELECT DISTINCT ON ( payload->'details'->>'withdrawal_hash', payload->>'chain', payload->>'asset_id' ) observed_at, ingested_at, payload FROM liquidity_actions WHERE ingested_at > $1 AND payload->>'action_type' = 'withdrawal_status_changed' AND UPPER(payload->>'status') = ANY($2) AND COALESCE(payload->'details'->>'withdrawal_hash', '') <> '' ORDER BY payload->'details'->>'withdrawal_hash', payload->>'chain', payload->>'asset_id', ingested_at DESC `, [since, COMPLETED_WITHDRAWAL_STATUSES], ); return result.rows; } async function normalizeExternalFlowRow(pool, { row, kind, sign, btcAsset, btcAssets = null, eureAsset, valuationAssets = [], } = {}) { const payload = row?.payload || {}; const details = payload.details || {}; const assetId = payload.asset_id || details.asset_id || null; if (!assetId) return null; const amount = String(details.amount || '0'); const effectiveAt = toIsoTimestamp(details.created_at || row.observed_at || row.ingested_at); const signedUnits = (sign * BigInt(amount)).toString(); let referencePriceAtFlowTime = null; let referencePriceEurePerUnitAtFlowTime = null; const effectiveBtcAssets = normalizeBtcAssets({ btcAsset, btcAssets }); const flowBtcAsset = effectiveBtcAssets.find((asset) => asset.assetId === assetId); const valuationAsset = valuationAssets.find((asset) => asset.assetId === assetId); if (flowBtcAsset) { const nearestPrice = await loadNearestPricePayload(pool, effectiveAt); referencePriceAtFlowTime = nearestPrice?.payload?.eure_per_btc || null; } else if (valuationAsset) { referencePriceEurePerUnitAtFlowTime = valuationAsset.currentUnitValueEure || null; } else if (assetId !== eureAsset?.assetId) { return null; } return { flow_id: kind === 'deposit' ? `deposit:${details.tx_hash || effectiveAt || Math.random().toString(16).slice(2)}` : `withdrawal:${details.withdrawal_hash || effectiveAt || Math.random().toString(16).slice(2)}`, kind, asset_id: assetId, effective_at: effectiveAt, signed_units: signedUnits, tx_hash: details.tx_hash || null, withdrawal_hash: details.withdrawal_hash || null, reference_price_eure_per_btc_at_flow_time: referencePriceAtFlowTime, reference_price_eure_per_unit_at_flow_time: referencePriceEurePerUnitAtFlowTime, }; } function normalizeBtcAssets({ btcAsset = null, btcAssets = null } = {}) { const assets = btcAssets?.length ? btcAssets : [btcAsset]; return [...new Map( assets .filter((asset) => asset?.assetId) .map((asset) => [asset.assetId, asset]), ).values()]; } function normalizePortfolioMetricRow(row) { return { metric_id: row.metric_id, computed_at: row.computed_at ? new Date(row.computed_at).toISOString() : null, baseline_anchor_at: row.baseline_anchor_at ? new Date(row.baseline_anchor_at).toISOString() : null, baseline_status: row.baseline_status, payload: row.payload, }; } function normalizeQuoteOutcomeRow(row) { const payload = row.payload || {}; return { quote_id: row.quote_id || payload.quote_id || null, decision_id: row.decision_id || payload.decision_id || null, command_id: row.command_id || payload.command_id || null, pair: payload.pair || null, direction: payload.direction || null, request_kind: payload.request_kind || null, gross_edge_pct: payload.gross_edge_pct || null, eure_notional: payload.eure_notional || null, execution_result_status: row.execution_result_status || payload.execution_result_status || null, execution_result_code: row.execution_result_code || payload.execution_result_code || null, submitted_at: toIsoTimestamp(row.submitted_at || payload.submitted_at), command_at: toIsoTimestamp(row.command_at || payload.command_at), outcome_status: row.outcome_status || payload.outcome_status || null, outcome_observed_at: toIsoTimestamp(row.outcome_observed_at || payload.outcome_observed_at), outcome_source: row.outcome_source || payload.outcome_source || null, outcome_reason: payload.outcome_reason || null, attribution_status: row.attribution_status || payload.attribution_status || null, attribution_method: row.attribution_method || payload.attribution_method || null, attributed_inventory_delta: row.attributed_inventory_delta || payload.attributed_inventory_delta || null, computed_at: toIsoTimestamp(row.computed_at), evidence: payload.evidence || null, }; } function normalizeEventPayloadRow(row) { if (!row) return null; return { observed_at: toIsoTimestamp(row.observed_at), ingested_at: toIsoTimestamp(row.ingested_at), payload: row.payload || {}, }; } function normalizeIntentRequestSubmissionPayload(payload = null) { if (!payload) return null; return { request_id: payload.request_id || null, idempotency_key: payload.idempotency_key || null, submission_id: payload.submission_id || null, status: payload.status || null, result_code: payload.result_code || null, result_text: payload.result_text || null, quote_hash: payload.quote_hash || null, intent_hash: payload.intent_hash || null, destination_amount_units: payload.destination_amount_units || null, nonce: payload.nonce || null, submitted_at: toIsoTimestamp(payload.submitted_at), relay_status: payload.relay_status || null, relay_status_response: payload.relay_status_response || null, status_checked_at: toIsoTimestamp(payload.status_checked_at), }; } export function normalizeIntentRequestRow(row) { const preflight = row.preflight_payload || {}; const submission = row.submission_payload || null; const outcome = row.outcome_payload || null; const state = outcome?.outcome_status || mapSubmissionStatusToRequestState(submission?.status) || preflight.state || 'unknown'; const reasonCode = outcome?.outcome_reason || submission?.result_code || preflight.reason_code || 'reason_unknown'; const reasonText = outcome?.reason_text || (outcome?.outcome_reason ? humanizeIntentRequestReason(outcome.outcome_reason) : null) || submission?.result_text || preflight.reason_text || reasonCode.replaceAll('_', ' '); return { request_id: preflight.request_id || null, idempotency_key: preflight.idempotency_key || null, submission_id: submission?.submission_id || outcome?.submission_id || null, intent_hash: submission?.intent_hash || outcome?.intent_hash || null, quote_hash: submission?.quote_hash || preflight.selected_quote?.quote_hash || null, created_at: toIsoTimestamp(preflight.created_at || row.preflight_observed_at || row.preflight_ingested_at), submitted_at: toIsoTimestamp(submission?.submitted_at || outcome?.submitted_at || row.submission_observed_at || row.submission_ingested_at), resolved_at: isTerminalIntentRequestState(state) ? toIsoTimestamp(outcome?.outcome_observed_at || row.outcome_observed_at || submission?.submitted_at) : null, state, state_label: labelIntentRequestState(state), reason_code: reasonCode, reason_text: reasonText, source_asset_id: preflight.source_asset_id || null, source_symbol: preflight.source_symbol || null, source_decimals: preflight.source_decimals ?? null, destination_asset_id: preflight.destination_asset_id || null, destination_symbol: preflight.destination_symbol || null, destination_decimals: preflight.destination_decimals ?? null, source_amount_units: preflight.source_amount_units || null, expected_destination_amount_units: preflight.expected_destination_amount_units || null, min_destination_amount_units: preflight.min_destination_amount_units || null, quoted_destination_amount_units: submission?.destination_amount_units || preflight.quoted_destination_amount_units || preflight.selected_quote?.amount_out || null, slippage_bps: preflight.slippage_bps ?? null, deadline_at: preflight.deadline_at || null, signer_account_id: preflight.signer_account_id || null, signer_public_key: preflight.signer_public_key || null, verifier_contract: preflight.verifier_contract || null, nonce: submission?.nonce || null, nonce_policy: preflight.nonce_policy || null, live_submit_capable: preflight.live_submit_capable === true && !submission, solver_quote_count: preflight.solver_quote_count || 0, selected_quote: preflight.selected_quote || null, submission_status: submission?.status || outcome?.submission_status || null, relay_status: submission?.relay_status || outcome?.evidence?.relay_status || null, relay_response: submission?.relay_response || null, relay_status_response: submission?.relay_status_response || null, outcome_status: outcome?.outcome_status || null, outcome_source: outcome?.outcome_source || null, attribution_status: outcome?.attribution_status || null, attribution_method: outcome?.attribution_method || null, attributed_inventory_delta: outcome?.attributed_inventory_delta || null, has_settlement_evidence: Boolean( outcome?.attributed_inventory_delta && ['heuristic_match', 'linked_settlement'].includes(outcome?.attribution_status), ), lifecycle: { preflight, submission, outcome, }, }; } function humanizeIntentRequestReason(reason) { const normalized = String(reason || '').trim(); const labels = { accepted_by_relay_without_settlement: 'Relay accepted the signed request; waiting for durable EURe decrease and BTC increase evidence.', relay_settled_but_inventory_delta_missing: 'Relay reported settlement, but no matching durable inventory movement is linked yet.', deadline_elapsed_without_settlement: 'Deadline and grace window elapsed without matching EURe decrease and BTC increase evidence.', matched_inventory_delta: 'Matched durable EURe decrease and BTC increase evidence.', ambiguous_inventory_delta_match: 'More than one inventory movement could match this request; no completion is assigned.', relay_not_found_or_not_valid: 'Relay reported the intent as not found or not valid.', relay_settled_without_expected_inventory_delta: 'Relay reports settlement, but durable inventory does not show the expected EURe decrease and BTC increase.', solver_quote_unanswered: 'The relay returned no solver quotes for this request.', }; return labels[normalized] || normalized.replaceAll('_', ' '); } function mapSubmissionStatusToRequestState(status) { if (status === 'accepted_by_relay') return 'awaiting_settlement'; if (status === 'submit_requested') return 'submitted'; if (status === 'blocked') return 'blocked'; if (status === 'failed') return 'failed'; return null; } function labelIntentRequestState(state) { const labels = { draft: 'Draft', blocked: 'Blocked', submitted: 'Submitted', accepted_by_relay: 'Accepted by relay', awaiting_settlement: 'Awaiting settlement', failed: 'Failed', not_filled: 'Not filled', completed: 'Completed', }; return labels[state] || state || 'Unknown'; } function isTerminalIntentRequestState(state) { return ['blocked', 'failed', 'not_filled', 'completed'].includes(state); } function normalizeRecentQuoteRow(row) { const payload = row.payload || {}; return { quote_id: payload.quote_id || null, pair: payload.pair || buildPair(payload.asset_in, payload.asset_out), asset_in: payload.asset_in || null, asset_out: payload.asset_out || null, request_kind: payload.request_kind || null, amount_in: payload.amount_in ?? null, amount_out: payload.amount_out ?? null, min_deadline_ms: payload.min_deadline_ms ?? null, observed_at: toIsoTimestamp(row.observed_at), ingested_at: toIsoTimestamp(row.ingested_at), }; } function normalizeSubmissionRow(row) { const resultPayload = row.result_payload || {}; const commandPayload = row.command_payload || {}; const decisionPayload = row.decision_payload || {}; const outcomePayload = row.outcome_payload || {}; return { command_id: resultPayload.command_id || commandPayload.command_id || null, decision_id: commandPayload.decision_id || resultPayload.decision_id || decisionPayload.decision_id || null, execution_key: resultPayload.execution_key || commandPayload.execution_key || null, quote_id: resultPayload.quote_id || commandPayload.quote_id || decisionPayload.quote_id || null, pair: resultPayload.pair || commandPayload.pair || decisionPayload.pair || null, pair_id: commandPayload.pair_id || decisionPayload.pair_id || resultPayload.pair_id || null, pair_config_id: commandPayload.pair_config_id || decisionPayload.pair_config_id || resultPayload.pair_config_id || null, pair_config_version: commandPayload.pair_config_version || decisionPayload.pair_config_version || resultPayload.pair_config_version || null, edge_bps: commandPayload.edge_bps || decisionPayload.edge_bps || resultPayload.edge_bps || null, observed_at: toIsoTimestamp(row.result_observed_at || row.result_ingested_at), ingested_at: toIsoTimestamp(row.result_ingested_at), status: resultPayload.status || null, result_code: resultPayload.result_code || null, outcome_status: outcomePayload.outcome_status || null, outcome_reason: outcomePayload.outcome_reason || null, attribution_status: outcomePayload.attribution_status || null, attributed_inventory_delta: outcomePayload.attributed_inventory_delta || null, request_kind: commandPayload.request_kind || decisionPayload.request_kind || null, asset_in: commandPayload.asset_in || null, asset_out: commandPayload.asset_out || null, amount_in: resolveTradeAmount(commandPayload, 'amount_in'), amount_out: resolveTradeAmount(commandPayload, 'amount_out'), quoted_amount_in: commandPayload.amount_in || null, quoted_amount_out: commandPayload.amount_out || null, gross_edge_pct: decisionPayload.gross_edge_pct || null, decision_reason: decisionPayload.decision_reason || null, direction: decisionPayload.direction || null, }; } function normalizeExecuteTradeCommandRow(row) { const payload = row.payload || {}; return { command_id: payload.command_id || null, decision_id: payload.decision_id || null, execution_key: payload.execution_key || null, quote_id: payload.quote_id || null, pair: payload.pair || null, pair_id: payload.pair_id || null, pair_config_id: payload.pair_config_id || null, pair_config_version: payload.pair_config_version || null, edge_bps: payload.edge_bps || null, direction: payload.direction || null, request_kind: payload.request_kind || null, asset_in: payload.asset_in || null, asset_out: payload.asset_out || null, amount_in: resolveTradeAmount(payload, 'amount_in'), amount_out: resolveTradeAmount(payload, 'amount_out'), observed_at: toIsoTimestamp(row.observed_at || row.ingested_at), ingested_at: toIsoTimestamp(row.ingested_at), }; } function normalizeExecutionResultRow(row) { const resultPayload = row.result_payload || {}; const commandPayload = row.command_payload || {}; const decisionPayload = row.decision_payload || {}; const outcomePayload = row.outcome_payload || {}; return { command_id: resultPayload.command_id || commandPayload.command_id || null, decision_id: commandPayload.decision_id || resultPayload.decision_id || decisionPayload.decision_id || null, execution_key: resultPayload.execution_key || commandPayload.execution_key || null, quote_id: resultPayload.quote_id || commandPayload.quote_id || decisionPayload.quote_id || null, pair: resultPayload.pair || commandPayload.pair || decisionPayload.pair || null, pair_id: commandPayload.pair_id || decisionPayload.pair_id || resultPayload.pair_id || null, pair_config_id: commandPayload.pair_config_id || decisionPayload.pair_config_id || resultPayload.pair_config_id || null, pair_config_version: commandPayload.pair_config_version || decisionPayload.pair_config_version || resultPayload.pair_config_version || null, edge_bps: commandPayload.edge_bps || decisionPayload.edge_bps || resultPayload.edge_bps || null, command_at: toIsoTimestamp(row.command_ingested_at), result_at: toIsoTimestamp(row.result_observed_at || row.result_ingested_at), status: resultPayload.status || null, result_code: resultPayload.result_code || null, outcome_status: outcomePayload.outcome_status || resultPayload.outcome_status || resultPayload.venue_outcome_status || resultPayload.trade_outcome_status || null, outcome_reason: outcomePayload.outcome_reason || resultPayload.outcome_reason || resultPayload.venue_outcome_reason || resultPayload.trade_outcome_reason || null, outcome_source: outcomePayload.outcome_source || null, attribution_status: outcomePayload.attribution_status || null, attribution_method: outcomePayload.attribution_method || null, attributed_inventory_delta: outcomePayload.attributed_inventory_delta || null, outcome_payload: outcomePayload.quote_id ? outcomePayload : null, venue_response: resultPayload.venue_response || null, error_message: resultPayload.error?.message || null, note: resultPayload.note || null, }; } function resolveTradeAmount(commandPayload, field) { const quoteOutputField = commandPayload?.quote_output?.[field]; const proposedField = commandPayload?.[`proposed_${field}`]; return quoteOutputField || proposedField || commandPayload?.[field] || null; } function buildPair(assetIn, assetOut) { if (!assetIn || !assetOut) return null; return `${assetIn}->${assetOut}`; } function toIsoTimestamp(value) { if (!value) return null; const date = new Date(value); return Number.isNaN(date.getTime()) ? null : date.toISOString(); } function timestampValue(value) { const parsed = Date.parse(value || ''); return Number.isFinite(parsed) ? parsed : -Infinity; } function buildDepositStatusRowKey(payload) { const details = payload?.details || {}; return [ details.tx_hash || 'no-tx', payload?.chain || details.chain || 'no-chain', payload?.asset_id || details.asset_id || 'no-asset', details.address || 'no-address', details.amount || 'no-amount', ].join('|'); } function normalizeDepositStatusRows(rows = []) { const byKey = new Map(); for (const row of rows) { const key = buildDepositStatusRowKey(row.payload); const effectiveAt = depositStatusEffectiveAt(row); const latestSortAt = timestampValue(row.observed_at || row.ingested_at); const existing = byKey.get(key); if (!existing) { byKey.set(key, { latest: row, latestSortAt, firstEffectiveAt: effectiveAt, }); continue; } if (latestSortAt > existing.latestSortAt) { existing.latest = row; existing.latestSortAt = latestSortAt; } const candidateEffectiveTs = timestampValue(effectiveAt); const existingEffectiveTs = timestampValue(existing.firstEffectiveAt); if ( Number.isFinite(candidateEffectiveTs) && (!Number.isFinite(existingEffectiveTs) || candidateEffectiveTs < existingEffectiveTs) ) { existing.firstEffectiveAt = effectiveAt; } } return [...byKey.values()].map(({ latest, firstEffectiveAt }) => { const effectiveAt = firstEffectiveAt || toIsoTimestamp(latest.observed_at || latest.ingested_at); return { observed_at: effectiveAt, ingested_at: toIsoTimestamp(latest.ingested_at), payload: withDepositCreatedAt(latest.payload, effectiveAt), }; }); } function depositStatusEffectiveAt(row) { return toIsoTimestamp( row?.payload?.details?.created_at || row?.observed_at || row?.ingested_at, ); } function withDepositCreatedAt(payload, effectiveAt) { if (!effectiveAt) return payload; const details = payload?.details || {}; return { ...payload, details: { ...details, created_at: details.created_at || effectiveAt, }, }; } async function ensureExpressionIndex(pool, { name, table, expression }) { await pool.query(` CREATE INDEX IF NOT EXISTS ${name} ON ${table} (${expression}) `); }