import { Pool } from 'pg'; import { deriveIntentRequestOutcomeRecords } from '../core/intent-request-outcomes.mjs'; import { deriveQuoteLifecycleRows } from '../core/operator-dashboard.mjs'; import { buildCashEquivalentValuationAssets } from '../core/portfolio-metrics.mjs'; import { deriveQuoteOutcomeRecords } from '../core/quote-outcomes.mjs'; import { QUOTE_LIFECYCLE_STATISTIC_GRAINS, normalizeQuoteLifecycleStatisticsGrains, normalizeQuoteLifecycleStatisticsRow, } from '../core/quote-lifecycle-statistics.mjs'; import { DEFAULT_QUOTE_LIFECYCLE_RETENTION_POLICY, QUOTE_LIFECYCLE_DATA_CLASSES, QUOTE_LIFECYCLE_DETAIL_TABLES, QUOTE_LIFECYCLE_RETENTION_POLICY_KEY, buildQuoteLifecycleRollups, decideQuoteLifecycleRetentionMode, normalizeQuoteLifecycleRetentionPolicy, normalizeQuoteLifecycleRollupRow, } from '../core/quote-lifecycle-retention.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 RETENTION_POLICIES_TABLE = 'retention_policies'; const QUOTE_LIFECYCLE_ROLLUPS_TABLE = 'quote_lifecycle_rollups'; const QUOTE_LIFECYCLE_ROLLUP_WATERMARKS_TABLE = 'quote_lifecycle_rollup_watermarks'; const QUOTE_LIFECYCLE_RETENTION_RUNS_TABLE = 'quote_lifecycle_retention_runs'; const QUOTE_LIFECYCLE_STATISTICS_TABLE = 'quote_lifecycle_statistics'; const QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE = 'quote_lifecycle_stat_subjects'; const QUOTE_LIFECYCLE_ROLLUP_WINDOWS_PER_PASS = 48; const QUOTE_LIFECYCLE_STATISTICS_SOURCE_OVERLAP_MS = 5 * 60 * 1000; 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, max: 30, }); } 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 pool.query(` CREATE INDEX IF NOT EXISTS ${table}_coalesced_at_idx ON ${table} (COALESCE(observed_at, ingested_at) DESC) `); } await ensureExpressionIndex(pool, { name: 'funding_observations_tx_hash_idx', table: 'funding_observations', expression: "(payload->>'tx_hash')", }); await ensureExpressionIndex(pool, { name: 'trade_execution_results_status_idx', table: 'trade_execution_results', expression: "(payload->>'status')", }); 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 ensureQuoteLifecycleRetentionSchema(pool); 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 ensureQuoteLifecycleRetentionSchema(pool) { await pool.query(` CREATE TABLE IF NOT EXISTS ${RETENTION_POLICIES_TABLE} ( policy_key TEXT PRIMARY KEY, enabled BOOLEAN NOT NULL, normal_detail_retention_ms BIGINT NOT NULL, pressure_detail_retention_ms BIGINT NOT NULL, raw_retention_ms BIGINT NOT NULL, rollup_granularity_ms BIGINT NOT NULL, rollup_retention_ms BIGINT NOT NULL, rollup_stale_after_ms BIGINT NOT NULL, preserve_successful_evidence BOOLEAN NOT NULL, max_delete_rows_per_pass INTEGER NOT NULL, pressure_table_bytes BIGINT NOT NULL, pressure_database_bytes BIGINT NOT NULL, updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), config_version INTEGER NOT NULL ) `); await pool.query(` CREATE TABLE IF NOT EXISTS ${QUOTE_LIFECYCLE_ROLLUPS_TABLE} ( rollup_id TEXT PRIMARY KEY, window_start TIMESTAMPTZ NOT NULL, window_end TIMESTAMPTZ NOT NULL, data_class TEXT NOT NULL, source_data_class TEXT NOT NULL, pair TEXT, direction TEXT NOT NULL, request_kind TEXT NOT NULL, edge_bps TEXT, notional_asset TEXT, notional_bucket TEXT NOT NULL, quote_age_bucket TEXT NOT NULL, result_code TEXT NOT NULL, failure_category TEXT, outcome_status TEXT NOT NULL, quote_count INTEGER NOT NULL DEFAULT 0, decision_count INTEGER NOT NULL DEFAULT 0, command_count INTEGER NOT NULL DEFAULT 0, executor_result_count INTEGER NOT NULL DEFAULT 0, relay_accepted_count INTEGER NOT NULL DEFAULT 0, relay_failed_count INTEGER NOT NULL DEFAULT 0, not_filled_count INTEGER NOT NULL DEFAULT 0, completed_count INTEGER NOT NULL DEFAULT 0, timing_bucket_counts JSONB NOT NULL DEFAULT '{}'::jsonb, min_observed_at TIMESTAMPTZ, max_observed_at TIMESTAMPTZ, source_watermark_at TIMESTAMPTZ, computed_at TIMESTAMPTZ NOT NULL ) `); await pool.query(` CREATE UNIQUE INDEX IF NOT EXISTS ${QUOTE_LIFECYCLE_ROLLUPS_TABLE}_dimension_key_idx ON ${QUOTE_LIFECYCLE_ROLLUPS_TABLE} ( window_start, window_end, source_data_class, COALESCE(pair, ''), direction, request_kind, COALESCE(edge_bps, ''), COALESCE(notional_asset, ''), notional_bucket, quote_age_bucket, result_code, COALESCE(failure_category, ''), outcome_status ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${QUOTE_LIFECYCLE_ROLLUPS_TABLE}_window_idx ON ${QUOTE_LIFECYCLE_ROLLUPS_TABLE} (window_end DESC, window_start DESC) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${QUOTE_LIFECYCLE_ROLLUPS_TABLE}_pair_idx ON ${QUOTE_LIFECYCLE_ROLLUPS_TABLE} (pair, window_end DESC) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${QUOTE_LIFECYCLE_ROLLUPS_TABLE}_source_class_idx ON ${QUOTE_LIFECYCLE_ROLLUPS_TABLE} (source_data_class, window_end DESC) `); await pool.query(` CREATE TABLE IF NOT EXISTS ${QUOTE_LIFECYCLE_STATISTICS_TABLE} ( stat_id TEXT PRIMARY KEY, grain TEXT NOT NULL, window_start TIMESTAMPTZ, window_end TIMESTAMPTZ, quote_count INTEGER NOT NULL DEFAULT 0, missing_identifier_count INTEGER NOT NULL DEFAULT 0, missing_timestamp_count INTEGER NOT NULL DEFAULT 0, bucket_counts JSONB NOT NULL DEFAULT '{}'::jsonb, computed_at TIMESTAMPTZ NOT NULL ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${QUOTE_LIFECYCLE_STATISTICS_TABLE}_grain_window_idx ON ${QUOTE_LIFECYCLE_STATISTICS_TABLE} (grain, window_start DESC NULLS LAST) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${QUOTE_LIFECYCLE_STATISTICS_TABLE}_computed_at_idx ON ${QUOTE_LIFECYCLE_STATISTICS_TABLE} (computed_at DESC) `); await pool.query(` CREATE TABLE IF NOT EXISTS ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE} ( quote_id TEXT PRIMARY KEY, first_activity_at TIMESTAMPTZ, latest_stage_at TIMESTAMPTZ, bucket TEXT NOT NULL, lifecycle_state TEXT, reason_code TEXT, updated_at TIMESTAMPTZ NOT NULL, payload JSONB NOT NULL DEFAULT '{}'::jsonb ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}_first_activity_idx ON ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE} (first_activity_at ASC NULLS LAST) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}_bucket_idx ON ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE} (bucket, latest_stage_at DESC NULLS LAST) `); await ensureSchemaIndex(pool, ` CREATE INDEX IF NOT EXISTS ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}_updated_idx ON ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE} (updated_at DESC) `); await pool.query(` CREATE TABLE IF NOT EXISTS ${QUOTE_LIFECYCLE_ROLLUP_WATERMARKS_TABLE} ( rollup_key TEXT PRIMARY KEY, covered_until TIMESTAMPTZ, computed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), status TEXT NOT NULL, error JSONB, row_count INTEGER NOT NULL DEFAULT 0, rollup_count INTEGER NOT NULL DEFAULT 0 ) `); await pool.query(` CREATE TABLE IF NOT EXISTS ${QUOTE_LIFECYCLE_RETENTION_RUNS_TABLE} ( run_id TEXT PRIMARY KEY, started_at TIMESTAMPTZ NOT NULL, completed_at TIMESTAMPTZ NOT NULL, mode TEXT NOT NULL, status TEXT NOT NULL, policy_key TEXT, detail_cutoff TIMESTAMPTZ, raw_cutoff TIMESTAMPTZ, rollup_covered_until TIMESTAMPTZ, preserved_success_count INTEGER NOT NULL DEFAULT 0, pruned_counts JSONB NOT NULL DEFAULT '{}'::jsonb, rollup_counts JSONB NOT NULL DEFAULT '{}'::jsonb, storage_metrics JSONB NOT NULL DEFAULT '{}'::jsonb, error JSONB ) `); await pool.query(` CREATE INDEX IF NOT EXISTS ${QUOTE_LIFECYCLE_RETENTION_RUNS_TABLE}_completed_at_idx ON ${QUOTE_LIFECYCLE_RETENTION_RUNS_TABLE} (completed_at DESC) `); await seedQuoteLifecycleRetentionPolicy(pool); } export async function seedQuoteLifecycleRetentionPolicy(pool, { now = new Date().toISOString(), policy = DEFAULT_QUOTE_LIFECYCLE_RETENTION_POLICY, } = {}) { await pool.query( ` INSERT INTO ${RETENTION_POLICIES_TABLE} ( policy_key, enabled, normal_detail_retention_ms, pressure_detail_retention_ms, raw_retention_ms, rollup_granularity_ms, rollup_retention_ms, rollup_stale_after_ms, preserve_successful_evidence, max_delete_rows_per_pass, pressure_table_bytes, pressure_database_bytes, updated_at, config_version ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14) ON CONFLICT (policy_key) DO NOTHING `, [ policy.policy_key, policy.enabled, policy.normal_detail_retention_ms, policy.pressure_detail_retention_ms, policy.raw_retention_ms, policy.rollup_granularity_ms, policy.rollup_retention_ms, policy.rollup_stale_after_ms, policy.preserve_successful_evidence, policy.max_delete_rows_per_pass, policy.pressure_table_bytes, policy.pressure_database_bytes, now, policy.config_version, ], ); } 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, maker_max_quote_age_enabled BOOLEAN NOT NULL DEFAULT false, maker_max_quote_age_ms INTEGER, maker_latency_policy_reason TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by TEXT NOT NULL, reason TEXT, UNIQUE (pair_id, version) ) `); await pool.query(` ALTER TABLE ${PAIR_STRATEGY_CONFIGS_TABLE} ADD COLUMN IF NOT EXISTS maker_max_quote_age_enabled BOOLEAN NOT NULL DEFAULT false `); await pool.query(` ALTER TABLE ${PAIR_STRATEGY_CONFIGS_TABLE} ADD COLUMN IF NOT EXISTS maker_max_quote_age_ms INTEGER `); await pool.query(` ALTER TABLE ${PAIR_STRATEGY_CONFIGS_TABLE} ADD COLUMN IF NOT EXISTS maker_latency_policy_reason TEXT `); 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, makerMaxQuoteAgeEnabled = undefined, makerMaxQuoteAgeMs = undefined, makerLatencyPolicyReason = 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 nextMinNotional = nonNegativeNumberStringOrDefault(minNotional, active.min_notional, 'min_notional'); const nextConfig = { configId: `${resolvedPairId}:v${nextVersion}`, pairId: resolvedPairId, version: nextVersion, edgeBps: nextEdgeBps, maxNotional: nextMaxNotional, minNotional: nextMinNotional, 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'), makerMaxQuoteAgeEnabled: makerMaxQuoteAgeEnabled === undefined ? active.maker_max_quote_age_enabled === true : Boolean(makerMaxQuoteAgeEnabled), makerMaxQuoteAgeMs: makerMaxQuoteAgeMs === undefined ? active.maker_max_quote_age_ms == null ? null : Number(active.maker_max_quote_age_ms) : nullablePositiveInteger(makerMaxQuoteAgeMs, 'maker_max_quote_age_ms'), makerLatencyPolicyReason: makerLatencyPolicyReason === undefined ? active.maker_latency_policy_reason || null : nullableString(makerLatencyPolicyReason), createdBy: changedBy, reason, }; validateStrategyNotionalBounds(nextConfig); validateMakerQuoteAgePolicy(nextConfig); 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, maker_max_quote_age_enabled: nextConfig.makerMaxQuoteAgeEnabled, maker_max_quote_age_ms: nextConfig.makerMaxQuoteAgeMs, maker_latency_policy_reason: nextConfig.makerLatencyPolicyReason, 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, makerMaxQuoteAgeEnabled = undefined, makerMaxQuoteAgeMs = undefined, makerLatencyPolicyReason = 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, makerMaxQuoteAgeEnabled, makerMaxQuoteAgeMs, makerLatencyPolicyReason, 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, maker_max_quote_age_enabled: nextConfig.makerMaxQuoteAgeEnabled, maker_max_quote_age_ms: nextConfig.makerMaxQuoteAgeMs, maker_latency_policy_reason: nextConfig.makerLatencyPolicyReason, 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'); } if (strategyConfig && !(Number(strategyConfig.minNotional) >= 0)) { blockReasons.push('min_notional_invalid'); } if (strategyConfig && Number(strategyConfig.minNotional) > Number(strategyConfig.maxNotional)) { blockReasons.push('min_notional_exceeds_max_notional'); } 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 = [...new Set( observedPairs .flatMap((pair) => [pair.assetIn?.assetId, pair.assetOut?.assetId]) .filter(Boolean), )]; 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, active_pairs: (snapshot.observedPairs || []).map((pair) => pair.key || pair.pairId), 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, maker_max_quote_age_enabled: pair.strategyConfig?.makerMaxQuoteAgeEnabled ?? false, maker_max_quote_age_ms: pair.strategyConfig?.makerMaxQuoteAgeMs ?? null, maker_latency_policy_reason: pair.strategyConfig?.makerLatencyPolicyReason ?? 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, makerMaxQuoteAgeEnabled = undefined, makerMaxQuoteAgeMs = undefined, makerLatencyPolicyReason = undefined, changedBy = 'operator', reason = 'operator pair strategy config initialization', } = {}) { const baseConfig = buildSeedStrategyConfig(pairId, { createdBy: changedBy, reason, }); const next = { ...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', ), makerMaxQuoteAgeEnabled: makerMaxQuoteAgeEnabled === undefined ? baseConfig.makerMaxQuoteAgeEnabled : Boolean(makerMaxQuoteAgeEnabled), makerMaxQuoteAgeMs: makerMaxQuoteAgeMs === undefined ? baseConfig.makerMaxQuoteAgeMs : nullablePositiveInteger(makerMaxQuoteAgeMs, 'maker_max_quote_age_ms'), makerLatencyPolicyReason: makerLatencyPolicyReason === undefined ? baseConfig.makerLatencyPolicyReason : nullableString(makerLatencyPolicyReason), }; validateStrategyNotionalBounds(next); validateMakerQuoteAgePolicy(next); return next; } 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 nullablePositiveInteger(value, field) { if (!hasConfigOverride(value)) return null; return positiveIntegerOrDefault(value, 1, field); } function nullableString(value) { const normalized = String(value ?? '').trim(); return normalized || null; } function validateStrategyNotionalBounds(config) { if (Number(config.minNotional) > Number(config.maxNotional)) { throw new Error('min_notional must be less than or equal to max_notional'); } } function validateMakerQuoteAgePolicy(config) { if (config.makerMaxQuoteAgeEnabled !== true) return; if (!Number.isInteger(config.makerMaxQuoteAgeMs) || config.makerMaxQuoteAgeMs <= 0) { throw new Error('maker_max_quote_age_ms must be a positive integer when maker quote age policy is enabled'); } } 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), makerMaxQuoteAgeEnabled: row.maker_max_quote_age_enabled === true, maker_max_quote_age_enabled: row.maker_max_quote_age_enabled === true, makerMaxQuoteAgeMs: row.maker_max_quote_age_ms == null ? null : Number(row.maker_max_quote_age_ms), maker_max_quote_age_ms: row.maker_max_quote_age_ms == null ? null : Number(row.maker_max_quote_age_ms), makerLatencyPolicyReason: row.maker_latency_policy_reason || null, maker_latency_policy_reason: row.maker_latency_policy_reason || null, 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, maker_max_quote_age_enabled, maker_max_quote_age_ms, maker_latency_policy_reason, created_by, reason ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19) 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.makerMaxQuoteAgeEnabled === true, config.makerMaxQuoteAgeMs, config.makerLatencyPolicyReason, 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 }) { const result = 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 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, record.decision_key, JSON.stringify(event.payload), event.raw ? JSON.stringify(event.raw) : null, ], ); return { inserted: result.rowCount > 0 }; } export async function insertHistoryEvents(pool, entries = []) { const insertedEventIds = new Set(); const normalizedEntries = (entries || []).filter(Boolean); if (!normalizedEntries.length) return { insertedEventIds }; const entriesByTable = new Map(); for (const entry of normalizedEntries) { if (!TABLES.includes(entry.table)) { throw new Error(`unsupported history table: ${entry.table}`); } const list = entriesByTable.get(entry.table) || []; list.push(entry); entriesByTable.set(entry.table, list); } for (const [table, tableEntries] of entriesByTable.entries()) { for (let start = 0; start < tableEntries.length; start += 1000) { const chunk = tableEntries.slice(start, start + 1000); const values = []; const placeholders = chunk.map((entry, index) => { const offset = index * 12; values.push( entry.event.event_id, entry.topic, entry.event.venue, entry.event.source, entry.event.event_type, entry.event.observed_at, entry.event.ingested_at, entry.record.quote_id, entry.record.pair, entry.record.decision_key, JSON.stringify(entry.event.payload), entry.event.raw ? JSON.stringify(entry.event.raw) : null, ); return `($${offset + 1},$${offset + 2},$${offset + 3},$${offset + 4},$${offset + 5},$${offset + 6},$${offset + 7},$${offset + 8},$${offset + 9},$${offset + 10},$${offset + 11}::jsonb,$${offset + 12}::jsonb)`; }).join(','); const result = 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 ${placeholders} ON CONFLICT (event_id) DO NOTHING RETURNING event_id `, values, ); for (const row of result.rows || []) { insertedEventIds.add(row.event_id); } } } return { insertedEventIds }; } export async function loadQuoteLifecycleRetentionPolicy(pool, { policyKey = QUOTE_LIFECYCLE_RETENTION_POLICY_KEY, } = {}) { const result = await pool.query( ` SELECT * FROM ${RETENTION_POLICIES_TABLE} WHERE policy_key = $1 LIMIT 1 `, [policyKey], ); const normalized = normalizeQuoteLifecycleRetentionPolicy(result.rows[0] || null); return { ...normalized, policy_key: policyKey, }; } export async function loadQuoteLifecycleStorageMetrics(pool) { const tables = [ ...QUOTE_LIFECYCLE_DETAIL_TABLES, QUOTE_LIFECYCLE_ROLLUPS_TABLE, QUOTE_LIFECYCLE_RETENTION_RUNS_TABLE, ]; const [tableResult, databaseResult] = await Promise.all([ pool.query( ` WITH table_names AS ( SELECT UNNEST($1::text[]) AS table_name ) SELECT table_names.table_name AS table, COALESCE(pg_total_relation_size(to_regclass(table_names.table_name)), 0)::BIGINT AS total_bytes, COALESCE(pg_relation_size(to_regclass(table_names.table_name)), 0)::BIGINT AS heap_bytes, GREATEST(COALESCE(pg_total_relation_size(to_regclass(table_names.table_name)), 0)::BIGINT - COALESCE(pg_relation_size(to_regclass(table_names.table_name)), 0)::BIGINT, 0)::BIGINT AS index_bytes, COALESCE(pg_stat_user_tables.n_live_tup, 0)::BIGINT AS approximate_rows FROM table_names LEFT JOIN pg_stat_user_tables ON pg_stat_user_tables.relname = table_names.table_name ORDER BY total_bytes DESC, table_names.table_name ASC `, [tables], ), pool.query(` SELECT pg_database_size(current_database())::BIGINT AS total_bytes `), ]); return { captured_at: new Date().toISOString(), database: { total_bytes: Number(databaseResult.rows[0]?.total_bytes || 0), }, tables: tableResult.rows.map((row) => ({ table: row.table, total_bytes: Number(row.total_bytes || 0), heap_bytes: Number(row.heap_bytes || 0), index_bytes: Number(row.index_bytes || 0), approximate_rows: Number(row.approximate_rows || 0), })), }; } export async function loadQuoteLifecycleRetentionSummary(pool, { now = new Date().toISOString(), } = {}) { const [ policyResult, storageMetrics, watermark, latestRun, rollupAggregate, preservedSuccessCount, ] = await Promise.all([ loadQuoteLifecycleRetentionPolicy(pool), loadQuoteLifecycleStorageMetrics(pool).catch((error) => ({ captured_at: now, database: { total_bytes: 0 }, tables: [], error: error.message, })), loadQuoteLifecycleRollupWatermark(pool), loadLatestQuoteLifecycleRetentionRun(pool), loadQuoteLifecycleRollupAggregate(pool), countSuccessfulQuoteEvidence(pool), ]); const modeDecision = decideQuoteLifecycleRetentionMode({ policy: policyResult, storageMetrics, }); const policy = policyResult.policy || null; const detailCutoff = policy && modeDecision.detail_retention_ms ? floorTimestamp( new Date(Date.parse(now) - modeDecision.detail_retention_ms).toISOString(), policy.rollup_granularity_ms, ) : null; const rollupRequiredUntil = policy && detailCutoff ? detailCutoff : null; const rollupCoveredUntil = watermark?.covered_until || null; const rollupFresh = !policy ? false : rollupRequiredUntil == null ? true : timestampValue(rollupCoveredUntil) >= timestampValue(rollupRequiredUntil); const rollupFreshnessAgeMs = watermark?.computed_at ? Math.max(0, Date.parse(now) - timestampValue(watermark.computed_at)) : null; const retentionMode = modeDecision.mode === 'blocked_invalid_policy' ? modeDecision.mode : rollupFresh ? modeDecision.mode : 'blocked_rollup_stale'; return { generated_at: now, retention_mode: retentionMode, mode_reason: retentionMode === 'blocked_rollup_stale' ? 'rollup watermark does not cover the current prune cutoff' : modeDecision.reason, policy_ok: policyResult.ok, policy_block_reason: policyResult.block_reason, policy, detail_retention_ms: modeDecision.detail_retention_ms, raw_retention_ms: policy?.raw_retention_ms ?? null, detail_cutoff: detailCutoff, rollup_required_until: rollupRequiredUntil, rollup_fresh: rollupFresh, rollup_freshness_age_ms: Number.isFinite(rollupFreshnessAgeMs) ? rollupFreshnessAgeMs : null, rollup_watermark: watermark, latest_run: latestRun, storage: storageMetrics, rollups: rollupAggregate, preserved_successful_quote_count: preservedSuccessCount, }; } export async function runQuoteLifecycleRetentionMaintenance(pool, { now = new Date().toISOString(), } = {}) { const startedAt = new Date().toISOString(); const policyResult = await loadQuoteLifecycleRetentionPolicy(pool); let mode = 'blocked_invalid_policy'; let status = 'skipped'; let policy = policyResult.policy || null; let storageMetrics = null; let modeDecision = { mode, detail_retention_ms: null, reason: policyResult.block_reason }; let detailCutoff = null; let rawCutoff = null; let rollupCutoff = null; let rollupResult = null; let pruneResult = null; let preservedSuccessCount = 0; let errorPayload = null; try { storageMetrics = await loadQuoteLifecycleStorageMetrics(pool); preservedSuccessCount = await countSuccessfulQuoteEvidence(pool); if (!policyResult.ok) { errorPayload = { reason: policyResult.block_reason }; return await finishRetentionRun(); } modeDecision = decideQuoteLifecycleRetentionMode({ policy: policyResult, storageMetrics, }); mode = modeDecision.mode; policy = policyResult.policy; detailCutoff = floorTimestamp( new Date(Date.parse(now) - modeDecision.detail_retention_ms).toISOString(), policy.rollup_granularity_ms, ); rawCutoff = floorTimestamp( new Date(Date.parse(now) - policy.raw_retention_ms).toISOString(), policy.rollup_granularity_ms, ); rollupCutoff = laterTimestamp(detailCutoff, rawCutoff); rollupResult = await rollupQuoteLifecycleDetail(pool, { now, policy, windowEnd: rollupCutoff, }); const watermark = await loadQuoteLifecycleRollupWatermark(pool); if (!watermark || timestampValue(watermark.covered_until) < timestampValue(rollupCutoff)) { mode = 'blocked_rollup_stale'; status = 'skipped'; errorPayload = { reason: 'rollup watermark does not cover prune cutoff', rollup_cutoff: rollupCutoff, covered_until: watermark?.covered_until || null, }; return await finishRetentionRun(); } pruneResult = await pruneQuoteLifecycleDetailWithPolicy(pool, { policy, mode: modeDecision.mode, now, detailCutoff, rawCutoff, rollupCoveredUntil: watermark.covered_until, }); status = 'success'; return await finishRetentionRun(); } catch (error) { mode = mode === 'blocked_invalid_policy' ? mode : 'blocked_rollup_stale'; status = 'skipped'; errorPayload = { message: error.message, name: error.name }; return await finishRetentionRun(); } async function finishRetentionRun() { const completedAt = new Date().toISOString(); const run = { run_id: `quote-lifecycle-retention:${Date.parse(startedAt)}:${Math.random().toString(16).slice(2, 10)}`, started_at: startedAt, completed_at: completedAt, mode, status, policy_key: policy?.policy_key || policyResult.policy_key || null, detail_cutoff: detailCutoff, raw_cutoff: rawCutoff, rollup_covered_until: rollupResult?.covered_until || null, preserved_success_count: preservedSuccessCount, pruned_counts: pruneResult || {}, rollup_counts: rollupResult || {}, storage_metrics: storageMetrics || {}, error: errorPayload, }; await insertQuoteLifecycleRetentionRun(pool, run); return { ...run, policy_ok: policyResult.ok, policy_block_reason: policyResult.block_reason, detail_retention_ms: modeDecision.detail_retention_ms, pressure: modeDecision.pressure === true, }; } } export async function countQuoteLifecyclePruneCandidates(pool, { policy, mode = 'normal', now = new Date().toISOString(), detailCutoff = null, rawCutoff = null, } = {}) { const normalized = normalizeQuoteLifecycleRetentionPolicy(policy); if (!normalized.ok) { return { ok: false, block_reason: normalized.block_reason, tables: [], total: 0, }; } const activePolicy = normalized.policy; const effectiveDetailCutoff = detailCutoff || floorTimestamp( new Date(Date.parse(now) - ( mode === 'pressure' ? activePolicy.pressure_detail_retention_ms : activePolicy.normal_detail_retention_ms )).toISOString(), activePolicy.rollup_granularity_ms, ); const effectiveRawCutoff = rawCutoff || floorTimestamp( new Date(Date.parse(now) - activePolicy.raw_retention_ms).toISOString(), activePolicy.rollup_granularity_ms, ); const tables = []; let total = 0; for (const spec of quoteLifecyclePruneTableSpecs()) { const cutoff = spec.table === 'raw_near_intents_quotes' ? effectiveRawCutoff : effectiveDetailCutoff; const result = await pool.query( ` SELECT COUNT(*)::INT AS count FROM ${spec.table} target WHERE COALESCE(target.${spec.timestampColumn}, 'epoch'::timestamptz) < $1::timestamptz AND NOT ${successfulQuoteEvidencePredicate(spec)} `, [cutoff], ); const count = Number(result.rows[0]?.count || 0); total += count; tables.push({ table: spec.table, cutoff, candidate_count: count, }); } return { ok: true, total, tables, detail_cutoff: effectiveDetailCutoff, raw_cutoff: effectiveRawCutoff, }; } async function rollupQuoteLifecycleDetail(pool, { now, policy, windowEnd, } = {}) { const currentWatermark = await loadQuoteLifecycleRollupWatermark(pool); const windowStart = currentWatermark?.covered_until || await loadEarliestQuoteLifecycleTimestamp(pool); const alignedWindowStart = windowStart ? floorTimestamp(windowStart, policy.rollup_granularity_ms) : windowEnd; if (!alignedWindowStart || timestampValue(alignedWindowStart) >= timestampValue(windowEnd)) { await upsertQuoteLifecycleRollupWatermark(pool, { coveredUntil: windowEnd, computedAt: now, status: 'success', rowCount: 0, rollupCount: 0, }); return { ok: true, row_count: 0, rollup_count: 0, window_start: alignedWindowStart, covered_until: windowEnd, skipped: true, }; } const boundedWindowEnd = new Date(Math.min( timestampValue(windowEnd), timestampValue(alignedWindowStart) + ( policy.rollup_granularity_ms * QUOTE_LIFECYCLE_ROLLUP_WINDOWS_PER_PASS ), )).toISOString(); const rows = await loadQuoteLifecycleRollupInputRows(pool, { windowStart: alignedWindowStart, windowEnd: boundedWindowEnd, }); const rollups = buildQuoteLifecycleRollups({ rows, rollupGranularityMs: policy.rollup_granularity_ms, computedAt: now, now, }); await upsertQuoteLifecycleRollups(pool, rollups); await upsertQuoteLifecycleRollupWatermark(pool, { coveredUntil: boundedWindowEnd, computedAt: now, status: 'success', rowCount: rows.length, rollupCount: rollups.length, }); return { ok: true, row_count: rows.length, rollup_count: rollups.length, window_start: alignedWindowStart, covered_until: boundedWindowEnd, target_covered_until: windowEnd, partial: timestampValue(boundedWindowEnd) < timestampValue(windowEnd), }; } async function pruneQuoteLifecycleDetailWithPolicy(pool, { policy, mode, detailCutoff, rawCutoff, rollupCoveredUntil, } = {}) { const rollupCutoff = laterTimestamp(detailCutoff, rawCutoff); if (timestampValue(rollupCoveredUntil) < timestampValue(rollupCutoff)) { return { ok: false, blocked: true, block_reason: 'rollup_watermark_stale', deletedCount: 0, tables: [], }; } const boundedBatchSize = Math.max( 1, Math.min(500_000, Math.floor(Number(policy.max_delete_rows_per_pass) || 0)), ); const tables = []; let deletedCount = 0; for (const spec of quoteLifecyclePruneTableSpecs()) { const cutoff = spec.table === 'raw_near_intents_quotes' ? rawCutoff : detailCutoff; const result = await pool.query( ` WITH stale_rows AS ( SELECT target.ctid FROM ${spec.table} target WHERE COALESCE(target.${spec.timestampColumn}, 'epoch'::timestamptz) < $1::timestamptz AND NOT ${successfulQuoteEvidencePredicate(spec)} ORDER BY target.${spec.timestampColumn} ASC NULLS FIRST LIMIT $2 ) DELETE FROM ${spec.table} target USING stale_rows WHERE target.ctid = stale_rows.ctid `, [cutoff, boundedBatchSize], ); const tableDeletedCount = Number(result.rowCount || 0); deletedCount += tableDeletedCount; tables.push({ table: spec.table, cutoff, deletedCount: tableDeletedCount, }); } return { ok: true, mode, deletedCount, batchSize: boundedBatchSize, detail_cutoff: detailCutoff, raw_cutoff: rawCutoff, tables, }; } async function loadQuoteLifecycleRollupInputRows(pool, { windowStart, windowEnd, } = {}) { const linkedResult = await pool.query( ` WITH subject_events AS ( SELECT quote_id, COALESCE(observed_at, ingested_at) AS activity_at FROM raw_near_intents_quotes WHERE quote_id IS NOT NULL AND COALESCE(observed_at, ingested_at) >= $1::timestamptz AND COALESCE(observed_at, ingested_at) < $2::timestamptz UNION ALL SELECT quote_id, COALESCE(observed_at, ingested_at) AS activity_at FROM swap_demand_events WHERE quote_id IS NOT NULL AND COALESCE(observed_at, ingested_at) >= $1::timestamptz AND COALESCE(observed_at, ingested_at) < $2::timestamptz UNION ALL SELECT quote_id, COALESCE(observed_at, ingested_at) AS activity_at FROM trade_decisions WHERE quote_id IS NOT NULL AND COALESCE(observed_at, ingested_at) >= $1::timestamptz AND COALESCE(observed_at, ingested_at) < $2::timestamptz UNION ALL SELECT quote_id, COALESCE(observed_at, ingested_at) AS activity_at FROM execute_trade_commands WHERE quote_id IS NOT NULL AND COALESCE(observed_at, ingested_at) >= $1::timestamptz AND COALESCE(observed_at, ingested_at) < $2::timestamptz UNION ALL SELECT quote_id, COALESCE(observed_at, ingested_at) AS activity_at FROM trade_execution_results WHERE quote_id IS NOT NULL AND COALESCE(observed_at, ingested_at) >= $1::timestamptz AND COALESCE(observed_at, ingested_at) < $2::timestamptz UNION ALL SELECT quote_id, COALESCE(outcome_observed_at, computed_at) AS activity_at FROM quote_outcome_attributions WHERE quote_id IS NOT NULL AND COALESCE(outcome_observed_at, computed_at) >= $1::timestamptz AND COALESCE(outcome_observed_at, computed_at) < $2::timestamptz ), quote_subjects AS ( SELECT quote_id, MIN(activity_at) AS activity_at FROM subject_events GROUP BY quote_id ), latest_raw AS ( SELECT DISTINCT ON (quote_id) quote_id, pair, observed_at, ingested_at, payload FROM raw_near_intents_quotes WHERE quote_id IN (SELECT quote_id FROM quote_subjects) ORDER BY quote_id, COALESCE(observed_at, ingested_at) DESC ), latest_demand AS ( SELECT DISTINCT ON (quote_id) quote_id, pair, observed_at, ingested_at, payload FROM swap_demand_events WHERE quote_id IN (SELECT quote_id FROM quote_subjects) ORDER BY quote_id, COALESCE(observed_at, ingested_at) DESC ), latest_decision AS ( SELECT DISTINCT ON (quote_id) quote_id, pair, observed_at, ingested_at, payload FROM trade_decisions WHERE quote_id IN (SELECT quote_id FROM quote_subjects) ORDER BY quote_id, COALESCE(observed_at, ingested_at) DESC ), latest_command AS ( SELECT DISTINCT ON (quote_id) quote_id, pair, observed_at, ingested_at, payload FROM execute_trade_commands WHERE quote_id IN (SELECT quote_id FROM quote_subjects) ORDER BY quote_id, COALESCE(observed_at, ingested_at) DESC ), latest_execution AS ( SELECT DISTINCT ON (quote_id) quote_id, pair, observed_at, ingested_at, payload FROM trade_execution_results WHERE quote_id IN (SELECT quote_id FROM quote_subjects) ORDER BY quote_id, COALESCE(observed_at, ingested_at) DESC ) SELECT quote_subjects.quote_id, quote_subjects.activity_at, COALESCE(latest_demand.pair, latest_decision.pair, latest_command.pair, latest_execution.pair, latest_raw.pair) AS pair, latest_demand.payload AS quote_payload, latest_demand.observed_at AS quote_observed_at, latest_demand.ingested_at AS quote_ingested_at, latest_decision.payload AS decision_payload, latest_decision.observed_at AS decision_at, latest_command.payload AS command_payload, latest_command.observed_at AS command_at, latest_execution.payload AS execution_payload, latest_execution.observed_at AS execution_result_at, quote_outcome_attributions.payload AS outcome_payload, quote_outcome_attributions.outcome_observed_at, quote_outcome_attributions.computed_at FROM quote_subjects LEFT JOIN latest_raw ON latest_raw.quote_id = quote_subjects.quote_id LEFT JOIN latest_demand ON latest_demand.quote_id = quote_subjects.quote_id LEFT JOIN latest_decision ON latest_decision.quote_id = quote_subjects.quote_id LEFT JOIN latest_command ON latest_command.quote_id = quote_subjects.quote_id LEFT JOIN latest_execution ON latest_execution.quote_id = quote_subjects.quote_id LEFT JOIN quote_outcome_attributions ON quote_outcome_attributions.quote_id = quote_subjects.quote_id ORDER BY quote_subjects.activity_at ASC `, [windowStart, windowEnd], ); const rawDebugResult = await pool.query( ` SELECT event_id, NULL::text AS quote_id, COALESCE(observed_at, ingested_at) AS activity_at, pair, payload AS quote_payload, observed_at AS quote_observed_at, ingested_at AS quote_ingested_at FROM raw_near_intents_quotes WHERE quote_id IS NULL AND COALESCE(observed_at, ingested_at) >= $1::timestamptz AND COALESCE(observed_at, ingested_at) < $2::timestamptz ORDER BY COALESCE(observed_at, ingested_at) ASC `, [windowStart, windowEnd], ); return [ ...linkedResult.rows.map(normalizeQuoteLifecycleRollupInputRow), ...rawDebugResult.rows.map((row) => ({ ...normalizeQuoteLifecycleRollupInputRow(row), source_table: 'raw_near_intents_quotes', })), ]; } function normalizeQuoteLifecycleRollupInputRow(row) { return { quote_id: row.quote_id || null, pair: row.pair || null, activity_at: toIsoTimestamp(row.activity_at), quote: withEventTimestamps(row.quote_payload, { observed_at: row.quote_observed_at, ingested_at: row.quote_ingested_at, }), decision: withEventTimestamps(row.decision_payload, { observed_at: row.decision_at, decision_at: row.decision_at, }), command: withEventTimestamps(row.command_payload, { observed_at: row.command_at, command_at: row.command_at, }), execution: withEventTimestamps(row.execution_payload, { observed_at: row.execution_result_at, result_at: row.execution_result_at, }), outcome: row.outcome_payload || null, outcome_observed_at: toIsoTimestamp(row.outcome_observed_at), computed_at: toIsoTimestamp(row.computed_at), }; } function withEventTimestamps(payload, timestamps = {}) { if (!payload) return null; const next = { ...payload }; for (const [key, value] of Object.entries(timestamps)) { if (value && !next[key]) next[key] = toIsoTimestamp(value); } return next; } async function upsertQuoteLifecycleRollups(pool, rollups = []) { for (const rollup of rollups || []) { await pool.query( ` INSERT INTO ${QUOTE_LIFECYCLE_ROLLUPS_TABLE} ( rollup_id, window_start, window_end, data_class, source_data_class, pair, direction, request_kind, edge_bps, notional_asset, notional_bucket, quote_age_bucket, result_code, failure_category, outcome_status, quote_count, decision_count, command_count, executor_result_count, relay_accepted_count, relay_failed_count, not_filled_count, completed_count, timing_bucket_counts, min_observed_at, max_observed_at, source_watermark_at, computed_at ) VALUES ( $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24::jsonb,$25,$26,$27,$28 ) ON CONFLICT (rollup_id) DO UPDATE SET quote_count = EXCLUDED.quote_count, decision_count = EXCLUDED.decision_count, command_count = EXCLUDED.command_count, executor_result_count = EXCLUDED.executor_result_count, relay_accepted_count = EXCLUDED.relay_accepted_count, relay_failed_count = EXCLUDED.relay_failed_count, not_filled_count = EXCLUDED.not_filled_count, completed_count = EXCLUDED.completed_count, timing_bucket_counts = EXCLUDED.timing_bucket_counts, min_observed_at = EXCLUDED.min_observed_at, max_observed_at = EXCLUDED.max_observed_at, source_watermark_at = EXCLUDED.source_watermark_at, computed_at = EXCLUDED.computed_at `, [ rollup.rollup_id, rollup.window_start, rollup.window_end, rollup.data_class, rollup.source_data_class, rollup.pair, rollup.direction, rollup.request_kind, rollup.edge_bps, rollup.notional_asset, rollup.notional_bucket, rollup.quote_age_bucket, rollup.result_code, rollup.failure_category, rollup.outcome_status, rollup.quote_count, rollup.decision_count, rollup.command_count, rollup.executor_result_count, rollup.relay_accepted_count, rollup.relay_failed_count, rollup.not_filled_count, rollup.completed_count, JSON.stringify(rollup.timing_bucket_counts || {}), rollup.min_observed_at, rollup.max_observed_at, rollup.source_watermark_at, rollup.computed_at, ], ); } } async function loadQuoteLifecycleRollupWatermark(pool) { const result = await pool.query( ` SELECT * FROM ${QUOTE_LIFECYCLE_ROLLUP_WATERMARKS_TABLE} WHERE rollup_key = 'quote_lifecycle' LIMIT 1 `, ); const row = result.rows[0]; if (!row) return null; return { rollup_key: row.rollup_key, covered_until: toIsoTimestamp(row.covered_until), computed_at: toIsoTimestamp(row.computed_at), status: row.status, error: row.error || null, row_count: Number(row.row_count || 0), rollup_count: Number(row.rollup_count || 0), }; } async function upsertQuoteLifecycleRollupWatermark(pool, { coveredUntil, computedAt, status, error = null, rowCount = 0, rollupCount = 0, }) { await pool.query( ` INSERT INTO ${QUOTE_LIFECYCLE_ROLLUP_WATERMARKS_TABLE} ( rollup_key, covered_until, computed_at, status, error, row_count, rollup_count ) VALUES ('quote_lifecycle', $1, $2, $3, $4::jsonb, $5, $6) ON CONFLICT (rollup_key) DO UPDATE SET covered_until = GREATEST( COALESCE(${QUOTE_LIFECYCLE_ROLLUP_WATERMARKS_TABLE}.covered_until, 'epoch'::timestamptz), EXCLUDED.covered_until ), computed_at = EXCLUDED.computed_at, status = EXCLUDED.status, error = EXCLUDED.error, row_count = EXCLUDED.row_count, rollup_count = EXCLUDED.rollup_count `, [ coveredUntil, computedAt, status, error ? JSON.stringify(error) : null, rowCount, rollupCount, ], ); } async function loadEarliestQuoteLifecycleTimestamp(pool) { const result = await pool.query(` SELECT MIN(activity_at) AS earliest_at FROM ( SELECT COALESCE(observed_at, ingested_at) AS activity_at FROM raw_near_intents_quotes UNION ALL SELECT COALESCE(observed_at, ingested_at) AS activity_at FROM swap_demand_events UNION ALL SELECT COALESCE(observed_at, ingested_at) AS activity_at FROM trade_decisions UNION ALL SELECT COALESCE(observed_at, ingested_at) AS activity_at FROM execute_trade_commands UNION ALL SELECT COALESCE(observed_at, ingested_at) AS activity_at FROM trade_execution_results UNION ALL SELECT COALESCE(outcome_observed_at, computed_at) AS activity_at FROM quote_outcome_attributions ) lifecycle_times `); return toIsoTimestamp(result.rows[0]?.earliest_at); } async function loadLatestQuoteLifecycleRetentionRun(pool) { const result = await pool.query(` SELECT * FROM ${QUOTE_LIFECYCLE_RETENTION_RUNS_TABLE} ORDER BY completed_at DESC LIMIT 1 `); const row = result.rows[0]; if (!row) return null; return { run_id: row.run_id, started_at: toIsoTimestamp(row.started_at), completed_at: toIsoTimestamp(row.completed_at), mode: row.mode, status: row.status, policy_key: row.policy_key || null, detail_cutoff: toIsoTimestamp(row.detail_cutoff), raw_cutoff: toIsoTimestamp(row.raw_cutoff), rollup_covered_until: toIsoTimestamp(row.rollup_covered_until), preserved_success_count: Number(row.preserved_success_count || 0), pruned_counts: row.pruned_counts || {}, rollup_counts: row.rollup_counts || {}, storage_metrics: row.storage_metrics || {}, error: row.error || null, }; } async function loadQuoteLifecycleRollupAggregate(pool) { const result = await pool.query(` SELECT COUNT(*)::INT AS rollup_row_count, MAX(computed_at) AS latest_rollup_at, MAX(window_end) AS latest_window_end, COALESCE(SUM(quote_count), 0)::BIGINT AS quote_count, COALESCE(SUM(decision_count), 0)::BIGINT AS decision_count, COALESCE(SUM(command_count), 0)::BIGINT AS command_count, COALESCE(SUM(executor_result_count), 0)::BIGINT AS executor_result_count, COALESCE(SUM(relay_accepted_count), 0)::BIGINT AS relay_accepted_count, COALESCE(SUM(relay_failed_count), 0)::BIGINT AS relay_failed_count, COALESCE(SUM(not_filled_count), 0)::BIGINT AS not_filled_count, COALESCE(SUM(completed_count), 0)::BIGINT AS completed_count FROM ${QUOTE_LIFECYCLE_ROLLUPS_TABLE} `); const row = result.rows[0] || {}; return { rollup_row_count: Number(row.rollup_row_count || 0), latest_rollup_at: toIsoTimestamp(row.latest_rollup_at), latest_window_end: toIsoTimestamp(row.latest_window_end), quote_count: Number(row.quote_count || 0), decision_count: Number(row.decision_count || 0), command_count: Number(row.command_count || 0), executor_result_count: Number(row.executor_result_count || 0), relay_accepted_count: Number(row.relay_accepted_count || 0), relay_failed_count: Number(row.relay_failed_count || 0), not_filled_count: Number(row.not_filled_count || 0), completed_count: Number(row.completed_count || 0), }; } export async function loadRecentQuoteLifecycleRollups(pool, { limit = 50 } = {}) { const result = await pool.query( ` SELECT * FROM ${QUOTE_LIFECYCLE_ROLLUPS_TABLE} ORDER BY window_end DESC, quote_count DESC LIMIT $1 `, [Math.max(1, Number(limit) || 50)], ); return result.rows.map(normalizeQuoteLifecycleRollupRow); } export async function refreshQuoteLifecycleStatistics(pool, { now = new Date().toISOString(), grains = QUOTE_LIFECYCLE_STATISTIC_GRAINS, } = {}) { const sourceSince = await loadQuoteLifecycleStatisticSourceSince(pool); const subjectUpsertResult = await upsertQuoteLifecycleStatisticSubjectsFromHistory(pool, { now, sourceSince, }); const statistics = await buildQuoteLifecycleStatisticsFromSubjects(pool, { grains, computedAt: now, }); await upsertQuoteLifecycleStatistics(pool, statistics); return { computed_at: now, quote_count: statistics.find((row) => row.grain === 'all_time')?.quote_count || 0, statistic_row_count: statistics.length, evidence_counts: { statistic_subjects: subjectUpsertResult.statistic_subjects, statistic_subject_upserts: subjectUpsertResult.upserted_count, source_since: subjectUpsertResult.source_since, }, statistics, }; } async function loadQuoteLifecycleStatisticSourceSince(pool) { const result = await pool.query(` SELECT EXISTS ( SELECT 1 FROM ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE} LIMIT 1 ) AS has_statistic_subjects, MAX(updated_at) AS latest_subject_updated_at FROM ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE} `); const row = result.rows[0] || {}; const hasSubjects = row.has_statistic_subjects === true || row.has_statistic_subjects === 't'; const latestUpdatedAt = toIsoTimestamp(row.latest_subject_updated_at); if (!hasSubjects || !latestUpdatedAt) return null; const cutoffMs = Date.parse(latestUpdatedAt) - QUOTE_LIFECYCLE_STATISTICS_SOURCE_OVERLAP_MS; if (!Number.isFinite(cutoffMs)) return null; return new Date(cutoffMs).toISOString(); } async function upsertQuoteLifecycleStatisticSubjectsFromHistory(pool, { now = new Date().toISOString(), sourceSince = null, } = {}) { const result = await pool.query( ` WITH source_rows AS ( SELECT NULLIF(COALESCE(quote_id, payload->>'quote_id'), '') AS quote_id, COALESCE(observed_at, ingested_at) AS first_activity_at, COALESCE(observed_at, ingested_at) AS latest_stage_at, 'observed_no_decision' AS bucket, 'observed' AS lifecycle_state, COALESCE(NULLIF(payload->>'reason_code', ''), 'observed_no_decision') AS reason_code, NULLIF(payload->>'decision_id', '') AS decision_id, NULLIF(payload->>'command_id', '') AS command_id, jsonb_build_object( 'quote_id', NULLIF(COALESCE(quote_id, payload->>'quote_id'), ''), 'decision_id', NULLIF(payload->>'decision_id', ''), 'command_id', NULLIF(payload->>'command_id', ''), 'lifecycle_state', 'observed', 'reason_code', COALESCE(NULLIF(payload->>'reason_code', ''), 'observed_no_decision'), 'statistics_bucket', 'observed_no_decision', 'first_activity_at', COALESCE(observed_at, ingested_at), 'latest_stage_at', COALESCE(observed_at, ingested_at) ) AS payload FROM swap_demand_events WHERE NULLIF(COALESCE(quote_id, payload->>'quote_id'), '') IS NOT NULL AND ($2::timestamptz IS NULL OR COALESCE(ingested_at, observed_at) >= $2::timestamptz) UNION ALL SELECT NULLIF(COALESCE(quote_id, payload->>'quote_id'), '') AS quote_id, COALESCE(observed_at, ingested_at) AS first_activity_at, COALESCE(observed_at, ingested_at) AS latest_stage_at, CASE WHEN regexp_replace(lower(trim(COALESCE(payload->>'decision', ''))), '[[:space:]-]+', '_', 'g') = 'rejected' THEN 'rejected_by_strategy' WHEN regexp_replace(lower(trim(COALESCE(payload->>'decision', ''))), '[[:space:]-]+', '_', 'g') = 'blocked' THEN 'blocked_before_submit' WHEN regexp_replace(lower(trim(COALESCE(payload->>'decision', ''))), '[[:space:]-]+', '_', 'g') IN ('approved', 'actionable') THEN 'approved_no_command' ELSE 'unavailable' END AS bucket, CASE WHEN regexp_replace(lower(trim(COALESCE(payload->>'decision', ''))), '[[:space:]-]+', '_', 'g') = 'rejected' THEN 'rejected' WHEN regexp_replace(lower(trim(COALESCE(payload->>'decision', ''))), '[[:space:]-]+', '_', 'g') = 'blocked' THEN 'blocked' WHEN regexp_replace(lower(trim(COALESCE(payload->>'decision', ''))), '[[:space:]-]+', '_', 'g') IN ('approved', 'actionable') THEN 'evaluated' ELSE 'observed' END AS lifecycle_state, COALESCE(NULLIF(payload->>'decision_reason', ''), 'reason_unknown') AS reason_code, NULLIF(payload->>'decision_id', '') AS decision_id, NULLIF(payload->>'command_id', '') AS command_id, jsonb_build_object( 'quote_id', NULLIF(COALESCE(quote_id, payload->>'quote_id'), ''), 'decision_id', NULLIF(payload->>'decision_id', ''), 'command_id', NULLIF(payload->>'command_id', ''), 'lifecycle_state', regexp_replace(lower(trim(COALESCE(payload->>'decision', 'observed'))), '[[:space:]-]+', '_', 'g'), 'reason_code', COALESCE(NULLIF(payload->>'decision_reason', ''), 'reason_unknown'), 'first_activity_at', COALESCE(observed_at, ingested_at), 'latest_stage_at', COALESCE(observed_at, ingested_at) ) AS payload FROM trade_decisions WHERE NULLIF(COALESCE(quote_id, payload->>'quote_id'), '') IS NOT NULL AND ($2::timestamptz IS NULL OR COALESCE(ingested_at, observed_at) >= $2::timestamptz) UNION ALL SELECT NULLIF(COALESCE(quote_id, payload->>'quote_id'), '') AS quote_id, COALESCE(observed_at, ingested_at) AS first_activity_at, COALESCE(observed_at, ingested_at) AS latest_stage_at, 'awaiting_executor' AS bucket, 'command_emitted' AS lifecycle_state, 'awaiting_executor' AS reason_code, NULLIF(payload->>'decision_id', '') AS decision_id, NULLIF(payload->>'command_id', '') AS command_id, jsonb_build_object( 'quote_id', NULLIF(COALESCE(quote_id, payload->>'quote_id'), ''), 'decision_id', NULLIF(payload->>'decision_id', ''), 'command_id', NULLIF(payload->>'command_id', ''), 'lifecycle_state', 'command_emitted', 'reason_code', 'awaiting_executor', 'statistics_bucket', 'awaiting_executor', 'first_activity_at', COALESCE(observed_at, ingested_at), 'latest_stage_at', COALESCE(observed_at, ingested_at) ) AS payload FROM execute_trade_commands WHERE NULLIF(COALESCE(quote_id, payload->>'quote_id'), '') IS NOT NULL AND ($2::timestamptz IS NULL OR COALESCE(ingested_at, observed_at) >= $2::timestamptz) UNION ALL SELECT NULLIF(COALESCE(quote_id, payload->>'quote_id'), '') AS quote_id, COALESCE(observed_at, ingested_at) AS first_activity_at, COALESCE(observed_at, ingested_at) AS latest_stage_at, CASE WHEN regexp_replace(lower(trim(COALESCE(payload->>'outcome_status', payload->>'venue_outcome_status', payload->>'trade_outcome_status', ''))), '[[:space:]-]+', '_', 'g') IN ('completed', 'filled', 'settled', 'finalized') THEN 'success' WHEN regexp_replace(lower(trim(COALESCE(payload->>'outcome_status', payload->>'venue_outcome_status', payload->>'trade_outcome_status', ''))), '[[:space:]-]+', '_', 'g') IN ('expired', 'not_filled', 'cancelled') THEN 'not_filled' WHEN regexp_replace(lower(trim(COALESCE(payload->>'status', ''))), '[[:space:]-]+', '_', 'g') = 'failed' THEN 'submission_failed' WHEN regexp_replace(lower(trim(COALESCE(payload->>'status', ''))), '[[:space:]-]+', '_', 'g') = 'rejected' THEN 'blocked_before_submit' WHEN regexp_replace(lower(trim(COALESCE(payload->>'status', ''))), '[[:space:]-]+', '_', 'g') = 'submitted' THEN 'submitted_no_reply' ELSE 'unavailable' END AS bucket, CASE WHEN regexp_replace(lower(trim(COALESCE(payload->>'outcome_status', payload->>'venue_outcome_status', payload->>'trade_outcome_status', ''))), '[[:space:]-]+', '_', 'g') IN ('completed', 'filled', 'settled', 'finalized') THEN 'completed' WHEN regexp_replace(lower(trim(COALESCE(payload->>'outcome_status', payload->>'venue_outcome_status', payload->>'trade_outcome_status', ''))), '[[:space:]-]+', '_', 'g') IN ('expired', 'not_filled', 'cancelled') THEN 'not_filled' WHEN regexp_replace(lower(trim(COALESCE(payload->>'status', ''))), '[[:space:]-]+', '_', 'g') = 'failed' THEN 'failed' WHEN regexp_replace(lower(trim(COALESCE(payload->>'status', ''))), '[[:space:]-]+', '_', 'g') = 'rejected' THEN 'blocked' WHEN regexp_replace(lower(trim(COALESCE(payload->>'status', ''))), '[[:space:]-]+', '_', 'g') = 'submitted' THEN 'submitted' ELSE 'observed' END AS lifecycle_state, COALESCE(NULLIF(payload->>'outcome_reason', ''), NULLIF(payload->>'failure_category', ''), NULLIF(payload->>'result_code', ''), 'reason_unknown') AS reason_code, NULLIF(payload->>'decision_id', '') AS decision_id, NULLIF(payload->>'command_id', '') AS command_id, jsonb_build_object( 'quote_id', NULLIF(COALESCE(quote_id, payload->>'quote_id'), ''), 'decision_id', NULLIF(payload->>'decision_id', ''), 'command_id', NULLIF(payload->>'command_id', ''), 'reason_code', COALESCE(NULLIF(payload->>'outcome_reason', ''), NULLIF(payload->>'failure_category', ''), NULLIF(payload->>'result_code', ''), 'reason_unknown'), 'outcome_status', COALESCE(payload->>'outcome_status', payload->>'venue_outcome_status', payload->>'trade_outcome_status'), 'attribution_status', payload->>'attribution_status', 'first_activity_at', COALESCE(observed_at, ingested_at), 'latest_stage_at', COALESCE(observed_at, ingested_at) ) AS payload FROM trade_execution_results WHERE NULLIF(COALESCE(quote_id, payload->>'quote_id'), '') IS NOT NULL AND ($2::timestamptz IS NULL OR COALESCE(ingested_at, observed_at) >= $2::timestamptz) UNION ALL SELECT NULLIF(COALESCE(quote_id, payload->>'quote_id'), '') AS quote_id, COALESCE(submitted_at, command_at, outcome_observed_at, computed_at) AS first_activity_at, COALESCE(outcome_observed_at, submitted_at, command_at, computed_at) AS latest_stage_at, CASE WHEN regexp_replace(lower(trim(COALESCE(outcome_status, payload->>'outcome_status', ''))), '[[:space:]-]+', '_', 'g') IN ('completed', 'filled', 'settled', 'finalized') THEN 'success' WHEN regexp_replace(lower(trim(COALESCE(outcome_status, payload->>'outcome_status', ''))), '[[:space:]-]+', '_', 'g') IN ('expired', 'not_filled', 'cancelled') THEN 'not_filled' WHEN regexp_replace(lower(trim(COALESCE(execution_result_status, payload->>'execution_result_status', ''))), '[[:space:]-]+', '_', 'g') = 'failed' THEN 'submission_failed' WHEN regexp_replace(lower(trim(COALESCE(execution_result_status, payload->>'execution_result_status', ''))), '[[:space:]-]+', '_', 'g') = 'submitted' THEN 'submitted_no_reply' ELSE 'unavailable' END AS bucket, CASE WHEN regexp_replace(lower(trim(COALESCE(outcome_status, payload->>'outcome_status', ''))), '[[:space:]-]+', '_', 'g') IN ('completed', 'filled', 'settled', 'finalized') THEN 'completed' WHEN regexp_replace(lower(trim(COALESCE(outcome_status, payload->>'outcome_status', ''))), '[[:space:]-]+', '_', 'g') IN ('expired', 'not_filled', 'cancelled') THEN 'not_filled' WHEN regexp_replace(lower(trim(COALESCE(execution_result_status, payload->>'execution_result_status', ''))), '[[:space:]-]+', '_', 'g') = 'failed' THEN 'failed' WHEN regexp_replace(lower(trim(COALESCE(execution_result_status, payload->>'execution_result_status', ''))), '[[:space:]-]+', '_', 'g') = 'submitted' THEN 'submitted' ELSE 'observed' END AS lifecycle_state, COALESCE(NULLIF(payload->>'outcome_reason', ''), NULLIF(execution_result_code, ''), NULLIF(payload->>'execution_result_code', ''), 'reason_unknown') AS reason_code, NULLIF(COALESCE(decision_id, payload->>'decision_id'), '') AS decision_id, NULLIF(COALESCE(command_id, payload->>'command_id'), '') AS command_id, jsonb_build_object( 'quote_id', NULLIF(COALESCE(quote_id, payload->>'quote_id'), ''), 'decision_id', NULLIF(COALESCE(decision_id, payload->>'decision_id'), ''), 'command_id', NULLIF(COALESCE(command_id, payload->>'command_id'), ''), 'reason_code', COALESCE(NULLIF(payload->>'outcome_reason', ''), NULLIF(execution_result_code, ''), NULLIF(payload->>'execution_result_code', ''), 'reason_unknown'), 'outcome_status', COALESCE(outcome_status, payload->>'outcome_status'), 'attribution_status', COALESCE(attribution_status, payload->>'attribution_status'), 'has_settlement_evidence', attributed_inventory_delta IS NOT NULL OR payload ? 'attributed_inventory_delta', 'first_activity_at', COALESCE(submitted_at, command_at, outcome_observed_at, computed_at), 'latest_stage_at', COALESCE(outcome_observed_at, submitted_at, command_at, computed_at) ) AS payload FROM ${QUOTE_OUTCOMES_TABLE} WHERE NULLIF(COALESCE(quote_id, payload->>'quote_id'), '') IS NOT NULL AND ($2::timestamptz IS NULL OR COALESCE(computed_at, outcome_observed_at, submitted_at, command_at) >= $2::timestamptz) ), source_ranked AS ( SELECT *, ${quoteLifecycleBucketRankSql('bucket')} AS bucket_rank FROM source_rows WHERE quote_id IS NOT NULL ), first_activity AS ( SELECT quote_id, MIN(first_activity_at) AS first_activity_at FROM source_ranked GROUP BY quote_id ), winners AS ( SELECT * FROM ( SELECT source_ranked.*, ROW_NUMBER() OVER ( PARTITION BY quote_id ORDER BY bucket_rank DESC, latest_stage_at DESC NULLS LAST ) AS row_rank FROM source_ranked ) ranked WHERE row_rank = 1 ) INSERT INTO ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE} ( quote_id, first_activity_at, latest_stage_at, bucket, lifecycle_state, reason_code, updated_at, payload ) SELECT winners.quote_id, first_activity.first_activity_at, winners.latest_stage_at, winners.bucket, winners.lifecycle_state, winners.reason_code, $1::timestamptz, winners.payload || jsonb_build_object( 'statistics_bucket', winners.bucket, 'lifecycle_state', winners.lifecycle_state, 'first_activity_at', first_activity.first_activity_at, 'latest_stage_at', winners.latest_stage_at ) FROM winners JOIN first_activity ON first_activity.quote_id = winners.quote_id ON CONFLICT (quote_id) DO UPDATE SET first_activity_at = CASE WHEN ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.first_activity_at IS NULL THEN EXCLUDED.first_activity_at WHEN EXCLUDED.first_activity_at IS NULL THEN ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.first_activity_at ELSE LEAST(${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.first_activity_at, EXCLUDED.first_activity_at) END, latest_stage_at = CASE WHEN ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.latest_stage_at IS NULL THEN EXCLUDED.latest_stage_at WHEN EXCLUDED.latest_stage_at IS NULL THEN ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.latest_stage_at ELSE GREATEST(${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.latest_stage_at, EXCLUDED.latest_stage_at) END, bucket = CASE WHEN ${quoteLifecycleBucketRankSql('EXCLUDED.bucket')} > ${quoteLifecycleBucketRankSql(`${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.bucket`)} OR ( ${quoteLifecycleBucketRankSql('EXCLUDED.bucket')} = ${quoteLifecycleBucketRankSql(`${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.bucket`)} AND COALESCE(EXCLUDED.latest_stage_at, '-infinity'::timestamptz) >= COALESCE(${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.latest_stage_at, '-infinity'::timestamptz) ) THEN EXCLUDED.bucket ELSE ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.bucket END, lifecycle_state = CASE WHEN ${quoteLifecycleBucketRankSql('EXCLUDED.bucket')} > ${quoteLifecycleBucketRankSql(`${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.bucket`)} OR ( ${quoteLifecycleBucketRankSql('EXCLUDED.bucket')} = ${quoteLifecycleBucketRankSql(`${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.bucket`)} AND COALESCE(EXCLUDED.latest_stage_at, '-infinity'::timestamptz) >= COALESCE(${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.latest_stage_at, '-infinity'::timestamptz) ) THEN EXCLUDED.lifecycle_state ELSE ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.lifecycle_state END, reason_code = CASE WHEN ${quoteLifecycleBucketRankSql('EXCLUDED.bucket')} > ${quoteLifecycleBucketRankSql(`${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.bucket`)} OR ( ${quoteLifecycleBucketRankSql('EXCLUDED.bucket')} = ${quoteLifecycleBucketRankSql(`${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.bucket`)} AND COALESCE(EXCLUDED.latest_stage_at, '-infinity'::timestamptz) >= COALESCE(${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.latest_stage_at, '-infinity'::timestamptz) ) THEN EXCLUDED.reason_code ELSE ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.reason_code END, payload = CASE WHEN ${quoteLifecycleBucketRankSql('EXCLUDED.bucket')} > ${quoteLifecycleBucketRankSql(`${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.bucket`)} OR ( ${quoteLifecycleBucketRankSql('EXCLUDED.bucket')} = ${quoteLifecycleBucketRankSql(`${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.bucket`)} AND COALESCE(EXCLUDED.latest_stage_at, '-infinity'::timestamptz) >= COALESCE(${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.latest_stage_at, '-infinity'::timestamptz) ) THEN EXCLUDED.payload ELSE ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.payload END, updated_at = GREATEST(${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE}.updated_at, EXCLUDED.updated_at) `, [now, sourceSince], ); const countResult = await pool.query(` SELECT COUNT(*)::INT AS subject_count FROM ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE} `); return { upserted_count: Number(result.rowCount || 0), statistic_subjects: Number(countResult.rows[0]?.subject_count || 0), source_since: sourceSince, }; } async function buildQuoteLifecycleStatisticsFromSubjects(pool, { grains = QUOTE_LIFECYCLE_STATISTIC_GRAINS, computedAt = new Date().toISOString(), } = {}) { const normalizedGrains = normalizeQuoteLifecycleStatisticsGrains(grains); const result = await pool.query( ` WITH subjects AS ( SELECT first_activity_at, COALESCE(bucket, payload->>'statistics_bucket', 'unavailable') AS bucket FROM ${QUOTE_LIFECYCLE_STATISTIC_SUBJECTS_TABLE} ), grain_rows AS ( SELECT 'all_time'::text AS grain, NULL::timestamptz AS window_start, NULL::timestamptz AS window_end, bucket, first_activity_at IS NOT NULL AS has_timestamp FROM subjects WHERE 'all_time' = ANY($1::text[]) UNION ALL SELECT 'month'::text AS grain, (date_trunc('month', first_activity_at AT TIME ZONE 'UTC') AT TIME ZONE 'UTC') AS window_start, (date_trunc('month', first_activity_at AT TIME ZONE 'UTC') AT TIME ZONE 'UTC') + INTERVAL '1 month' AS window_end, bucket, true AS has_timestamp FROM subjects WHERE 'month' = ANY($1::text[]) AND first_activity_at IS NOT NULL UNION ALL SELECT 'week'::text AS grain, (date_trunc('week', first_activity_at AT TIME ZONE 'UTC') AT TIME ZONE 'UTC') AS window_start, (date_trunc('week', first_activity_at AT TIME ZONE 'UTC') AT TIME ZONE 'UTC') + INTERVAL '1 week' AS window_end, bucket, true AS has_timestamp FROM subjects WHERE 'week' = ANY($1::text[]) AND first_activity_at IS NOT NULL UNION ALL SELECT 'day'::text AS grain, (date_trunc('day', first_activity_at AT TIME ZONE 'UTC') AT TIME ZONE 'UTC') AS window_start, (date_trunc('day', first_activity_at AT TIME ZONE 'UTC') AT TIME ZONE 'UTC') + INTERVAL '1 day' AS window_end, bucket, true AS has_timestamp FROM subjects WHERE 'day' = ANY($1::text[]) AND first_activity_at IS NOT NULL UNION ALL SELECT 'hour'::text AS grain, (date_trunc('hour', first_activity_at AT TIME ZONE 'UTC') AT TIME ZONE 'UTC') AS window_start, (date_trunc('hour', first_activity_at AT TIME ZONE 'UTC') AT TIME ZONE 'UTC') + INTERVAL '1 hour' AS window_end, bucket, true AS has_timestamp FROM subjects WHERE 'hour' = ANY($1::text[]) AND first_activity_at IS NOT NULL UNION ALL SELECT 'five_minute'::text AS grain, to_timestamp(floor(extract(epoch FROM first_activity_at) / 300) * 300) AS window_start, to_timestamp(floor(extract(epoch FROM first_activity_at) / 300) * 300) + INTERVAL '5 minutes' AS window_end, bucket, true AS has_timestamp FROM subjects WHERE 'five_minute' = ANY($1::text[]) AND first_activity_at IS NOT NULL ), group_totals AS ( SELECT grain, window_start, window_end, COUNT(*)::INT AS quote_count, COUNT(*) FILTER (WHERE NOT has_timestamp)::INT AS missing_timestamp_count FROM grain_rows GROUP BY grain, window_start, window_end ), bucket_groups AS ( SELECT grain, window_start, window_end, bucket, COUNT(*)::INT AS bucket_count FROM grain_rows GROUP BY grain, window_start, window_end, bucket ), bucket_counts AS ( SELECT grain, window_start, window_end, jsonb_object_agg(bucket, bucket_count) AS bucket_counts FROM bucket_groups GROUP BY grain, window_start, window_end ) SELECT group_totals.grain, group_totals.window_start, group_totals.window_end, group_totals.quote_count, 0::INT AS missing_identifier_count, group_totals.missing_timestamp_count, COALESCE(bucket_counts.bucket_counts, '{}'::jsonb) AS bucket_counts, $2::timestamptz AS computed_at FROM group_totals LEFT JOIN bucket_counts ON bucket_counts.grain = group_totals.grain AND bucket_counts.window_start IS NOT DISTINCT FROM group_totals.window_start AND bucket_counts.window_end IS NOT DISTINCT FROM group_totals.window_end ORDER BY CASE group_totals.grain WHEN 'all_time' THEN 0 WHEN 'month' THEN 1 WHEN 'week' THEN 2 WHEN 'day' THEN 3 WHEN 'hour' THEN 4 WHEN 'five_minute' THEN 5 ELSE 99 END, group_totals.window_start DESC NULLS LAST `, [normalizedGrains, computedAt], ); return result.rows.map(normalizeQuoteLifecycleStatisticsRow); } function quoteLifecycleBucketRankSql(expression) { return `( CASE ${expression} WHEN 'success' THEN 8 WHEN 'not_filled' THEN 7 WHEN 'submission_failed' THEN 6 WHEN 'submitted_no_reply' THEN 5 WHEN 'rejected_by_strategy' THEN 4 WHEN 'blocked_before_submit' THEN 4 WHEN 'awaiting_executor' THEN 3 WHEN 'approved_no_command' THEN 2 WHEN 'observed_no_decision' THEN 1 ELSE 0 END )`; } export async function loadQuoteLifecycleStatistics(pool, { grains = QUOTE_LIFECYCLE_STATISTIC_GRAINS, limit = 96, } = {}) { const normalizedGrains = normalizeQuoteLifecycleStatisticsGrains(grains); const normalizedLimit = Math.max(1, Math.min(1000, Number(limit) || 96)); const result = await pool.query( ` WITH deduped AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY grain, window_start, window_end ORDER BY computed_at DESC, stat_id DESC ) AS duplicate_rank FROM ${QUOTE_LIFECYCLE_STATISTICS_TABLE} WHERE grain = ANY($1::text[]) ), ranked AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY grain ORDER BY window_start DESC NULLS LAST, computed_at DESC ) AS grain_rank FROM deduped WHERE duplicate_rank = 1 ) SELECT * FROM ranked WHERE grain = 'all_time' OR grain_rank <= $2 ORDER BY CASE grain WHEN 'all_time' THEN 0 WHEN 'month' THEN 1 WHEN 'week' THEN 2 WHEN 'day' THEN 3 WHEN 'hour' THEN 4 WHEN 'five_minute' THEN 5 ELSE 99 END, window_start DESC NULLS LAST `, [normalizedGrains, normalizedLimit], ); return result.rows.map(normalizeQuoteLifecycleStatisticsRow); } async function loadQuoteLifecycleStatisticsEvidence(pool) { const [ quotesResult, decisionsResult, commandsResult, executionResultsResult, outcomesResult, ] = await Promise.all([ pool.query(` SELECT observed_at, ingested_at, payload FROM swap_demand_events ORDER BY COALESCE(observed_at, ingested_at) ASC `), pool.query(` SELECT observed_at, ingested_at, payload FROM trade_decisions ORDER BY COALESCE(observed_at, ingested_at) ASC `), pool.query(` SELECT observed_at, ingested_at, payload FROM execute_trade_commands ORDER BY COALESCE(observed_at, ingested_at) ASC `), 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) ASC `), 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 COALESCE(outcome_observed_at, computed_at) ASC `), ]); return { recentQuotes: quotesResult.rows.map(normalizeRecentQuoteRow), recentTradeDecisions: decisionsResult.rows.map((row) => ({ observed_at: toIsoTimestamp(row.observed_at), ingested_at: toIsoTimestamp(row.ingested_at), payload: row.payload, })), recentExecuteTradeCommands: commandsResult.rows.map((row) => normalizeExecuteTradeCommandRow(row)), recentExecutionResults: executionResultsResult.rows.map((row) => normalizeExecutionResultRow(row)), recentQuoteOutcomes: outcomesResult.rows.map((row) => normalizeQuoteOutcomeRow(row)), }; } async function upsertQuoteLifecycleStatistics(pool, statistics = []) { for (const row of statistics || []) { await pool.query( ` INSERT INTO ${QUOTE_LIFECYCLE_STATISTICS_TABLE} ( stat_id, grain, window_start, window_end, quote_count, missing_identifier_count, missing_timestamp_count, bucket_counts, computed_at ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9) ON CONFLICT (stat_id) DO UPDATE SET quote_count = EXCLUDED.quote_count, missing_identifier_count = EXCLUDED.missing_identifier_count, missing_timestamp_count = EXCLUDED.missing_timestamp_count, bucket_counts = EXCLUDED.bucket_counts, computed_at = EXCLUDED.computed_at `, [ row.stat_id, row.grain, row.window_start, row.window_end, row.quote_count, row.missing_identifier_count, row.missing_timestamp_count, JSON.stringify(row.bucket_counts || {}), row.computed_at, ], ); } } async function countSuccessfulQuoteEvidence(pool) { const result = await pool.query(` SELECT COUNT(*)::INT AS count FROM quote_outcome_attributions WHERE ${successfulQuoteEvidenceSelfPredicate()} `); return Number(result.rows[0]?.count || 0); } async function insertQuoteLifecycleRetentionRun(pool, run) { await pool.query( ` INSERT INTO ${QUOTE_LIFECYCLE_RETENTION_RUNS_TABLE} ( run_id, started_at, completed_at, mode, status, policy_key, detail_cutoff, raw_cutoff, rollup_covered_until, preserved_success_count, pruned_counts, rollup_counts, storage_metrics, error ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12::jsonb,$13::jsonb,$14::jsonb) `, [ run.run_id, run.started_at, run.completed_at, run.mode, run.status, run.policy_key, run.detail_cutoff, run.raw_cutoff, run.rollup_covered_until, run.preserved_success_count, JSON.stringify(run.pruned_counts || {}), JSON.stringify(run.rollup_counts || {}), JSON.stringify(run.storage_metrics || {}), run.error ? JSON.stringify(run.error) : null, ], ); } function quoteLifecyclePruneTableSpecs() { return [ { table: 'raw_near_intents_quotes', timestampColumn: 'ingested_at' }, { table: 'swap_demand_events', timestampColumn: 'ingested_at' }, { table: 'trade_decisions', timestampColumn: 'ingested_at' }, { table: 'execute_trade_commands', timestampColumn: 'ingested_at' }, { table: 'trade_execution_results', timestampColumn: 'ingested_at' }, { table: 'quote_outcome_attributions', timestampColumn: 'computed_at', selfOutcome: true }, ]; } function successfulQuoteEvidencePredicate(spec) { if (spec.selfOutcome) { return ` ( ${successfulQuoteEvidenceTargetPredicate()} OR ${successfulExecutionEvidenceExistsPredicate('target')} ) `; } return ` ( ${successfulQuotePayloadPredicate('target.payload')} OR ${successfulExecutionEvidenceExistsPredicate('target')} OR EXISTS ( SELECT 1 FROM quote_outcome_attributions outcome WHERE outcome.quote_id = target.quote_id AND ${successfulQuoteEvidenceSelfPredicate('outcome')} ) ) `; } function successfulQuoteEvidenceSelfPredicate(alias = null) { const prefix = alias ? `${alias}.` : ''; return ` ( ${prefix}outcome_status = 'completed' OR ${prefix}attribution_status IN ('heuristic_match', 'linked_settlement', 'exact_match', 'attributed') ) `; } function successfulQuotePayloadPredicate(payloadExpression) { return ` ( ${payloadExpression}->>'outcome_status' = 'completed' OR ${payloadExpression}->>'attribution_status' IN ('heuristic_match', 'linked_settlement', 'exact_match', 'attributed') ) `; } function successfulExecutionEvidenceExistsPredicate(targetExpression) { return ` EXISTS ( SELECT 1 FROM trade_execution_results execution WHERE execution.quote_id = ${targetExpression}.quote_id AND ${successfulQuotePayloadPredicate('execution.payload')} ) `; } function successfulQuoteEvidenceTargetPredicate() { return ` ( target.outcome_status = 'completed' OR target.attribution_status IN ('heuristic_match', 'linked_settlement', 'exact_match', 'attributed') ) `; } export async function pruneRawNearIntentsQuoteHistory(pool, { now = new Date().toISOString(), retainRecentMs = 30 * 60 * 1000, batchSize = 100_000, maxBatches = 1, } = {}) { const nowMs = Date.parse(now); if (!Number.isFinite(nowMs)) throw new Error('now must be a valid timestamp'); if (!Number.isInteger(retainRecentMs) || retainRecentMs <= 0) { throw new Error('retain_recent_ms must be a positive integer'); } const boundedBatchSize = Math.max(1, Math.min(500_000, Math.floor(Number(batchSize) || 0))); const boundedMaxBatches = Math.max(1, Math.min(100, Math.floor(Number(maxBatches) || 0))); const cutoff = new Date(nowMs - retainRecentMs).toISOString(); let deletedCount = 0; let batches = 0; for (let batch = 0; batch < boundedMaxBatches; batch += 1) { const result = await pool.query( ` WITH stale_raw_quotes AS ( SELECT raw.event_id FROM raw_near_intents_quotes raw WHERE raw.ingested_at < $1::timestamptz AND NOT EXISTS ( SELECT 1 FROM trade_decisions decision WHERE decision.quote_id = raw.quote_id ) AND NOT EXISTS ( SELECT 1 FROM execute_trade_commands command WHERE command.quote_id = raw.quote_id ) AND NOT EXISTS ( SELECT 1 FROM trade_execution_results execution WHERE execution.quote_id = raw.quote_id ) AND NOT EXISTS ( SELECT 1 FROM quote_outcome_attributions outcome WHERE outcome.quote_id = raw.quote_id ) ORDER BY raw.ingested_at ASC LIMIT $2 ) DELETE FROM raw_near_intents_quotes raw USING stale_raw_quotes WHERE raw.event_id = stale_raw_quotes.event_id `, [cutoff, boundedBatchSize], ); const batchDeleted = Number(result.rowCount || 0); deletedCount += batchDeleted; batches += 1; if (batchDeleted < boundedBatchSize) break; } return { deletedCount, cutoff, retainRecentMs, batchSize: boundedBatchSize, maxBatches: boundedMaxBatches, batches, }; } export async function pruneNonSuccessfulQuoteLifecycleHistory(pool, { now = new Date().toISOString(), retainRecentMs = 30 * 60 * 1000, batchSize = 100_000, maxBatches = 1, } = {}) { const nowMs = Date.parse(now); if (!Number.isFinite(nowMs)) throw new Error('now must be a valid timestamp'); if (!Number.isInteger(retainRecentMs) || retainRecentMs <= 0) { throw new Error('retain_recent_ms must be a positive integer'); } const boundedBatchSize = Math.max(1, Math.min(500_000, Math.floor(Number(batchSize) || 0))); const boundedMaxBatches = Math.max(1, Math.min(100, Math.floor(Number(maxBatches) || 0))); const cutoff = new Date(nowMs - retainRecentMs).toISOString(); const tables = [ { table: 'raw_near_intents_quotes', timestampColumn: 'ingested_at' }, { table: 'swap_demand_events', timestampColumn: 'ingested_at' }, { table: 'trade_decisions', timestampColumn: 'ingested_at' }, { table: 'execute_trade_commands', timestampColumn: 'ingested_at' }, { table: 'trade_execution_results', timestampColumn: 'ingested_at' }, { table: 'quote_outcome_attributions', timestampColumn: 'computed_at', selfOutcome: true }, ]; const perTable = []; let deletedCount = 0; for (const spec of tables) { let tableDeletedCount = 0; let batches = 0; for (let batch = 0; batch < boundedMaxBatches; batch += 1) { const successPredicate = spec.selfOutcome ? ` ( target.outcome_status = 'completed' OR target.attribution_status IN ('heuristic_match', 'linked_settlement', 'exact_match', 'attributed') OR EXISTS ( SELECT 1 FROM trade_execution_results execution WHERE execution.quote_id = target.quote_id AND ( execution.payload->>'outcome_status' = 'completed' OR execution.payload->>'attribution_status' IN ('heuristic_match', 'linked_settlement', 'exact_match', 'attributed') ) ) ) ` : ` ( target.payload->>'outcome_status' = 'completed' OR target.payload->>'attribution_status' IN ('heuristic_match', 'linked_settlement', 'exact_match', 'attributed') OR EXISTS ( SELECT 1 FROM trade_execution_results execution WHERE execution.quote_id = target.quote_id AND ( execution.payload->>'outcome_status' = 'completed' OR execution.payload->>'attribution_status' IN ('heuristic_match', 'linked_settlement', 'exact_match', 'attributed') ) ) OR EXISTS ( SELECT 1 FROM quote_outcome_attributions outcome WHERE outcome.quote_id = target.quote_id AND ( outcome.outcome_status = 'completed' OR outcome.attribution_status IN ('heuristic_match', 'linked_settlement', 'exact_match', 'attributed') ) ) ) `; const result = await pool.query( ` WITH stale_rows AS ( SELECT target.ctid FROM ${spec.table} target WHERE COALESCE(target.${spec.timestampColumn}, 'epoch'::timestamptz) < $1::timestamptz AND NOT ${successPredicate} ORDER BY target.${spec.timestampColumn} ASC NULLS FIRST LIMIT $2 ) DELETE FROM ${spec.table} target USING stale_rows WHERE target.ctid = stale_rows.ctid `, [cutoff, boundedBatchSize], ); const batchDeleted = Number(result.rowCount || 0); tableDeletedCount += batchDeleted; deletedCount += batchDeleted; batches += 1; if (batchDeleted < boundedBatchSize) break; } perTable.push({ table: spec.table, deletedCount: tableDeletedCount, batches, }); } return { deletedCount, cutoff, retainRecentMs, batchSize: boundedBatchSize, maxBatches: boundedMaxBatches, tables: perTable, }; } 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, inventoryWindowBufferMs = 15 * 60 * 1000, } = {}) { const safeSubmissionLimit = Math.max(1, Number(submissionLimit) || 1000); const submissionsResult = await pool.query( ` WITH recent_submissions AS ( SELECT s.event_id, s.observed_at, s.ingested_at, s.quote_id, s.payload FROM trade_execution_results s LEFT JOIN ${QUOTE_OUTCOMES_TABLE} o ON o.quote_id = s.quote_id WHERE s.payload->>'status' = 'submitted' AND ( o.quote_id IS NULL OR o.outcome_status IN ('not_filled', 'submitted', 'awaiting_outcome') OR COALESCE(s.observed_at, s.ingested_at) > o.computed_at ) ORDER BY o.computed_at ASC NULLS FIRST LIMIT $1 ) SELECT event_id, observed_at, ingested_at, quote_id, payload FROM 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 []; // Scope the inventory fetch to the time window of this submission batch // instead of a blind LIMIT that pulls hundreds of MB. const submissionTimestamps = submissionsResult.rows .map((row) => new Date(row.observed_at || row.ingested_at).getTime()) .filter(Number.isFinite); const bufferMs = Math.max(0, Number(inventoryWindowBufferMs) || 15 * 60 * 1000); const inventoryWindowStart = new Date(Math.min(...submissionTimestamps) - bufferMs).toISOString(); const inventoryWindowEnd = new Date(Math.max(...submissionTimestamps) + bufferMs).toISOString(); 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, jsonb_build_object('spendable', payload->'spendable', 'synced_at', payload->'synced_at') AS payload FROM intent_inventory_snapshots WHERE COALESCE(observed_at, ingested_at) BETWEEN $1 AND $2 ORDER BY COALESCE(observed_at, ingested_at) ASC `, [inventoryWindowStart, inventoryWindowEnd]), ]); 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(); const computedRecords = records.map((record) => ({ ...record, computed_at: computedAt, })); for (const record of computedRecords) { await upsertQuoteOutcome(pool, { ...record, computedAt, }); } return computedRecords; } 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 loadSuccessfulQuoteLifecycleEvidence(pool, { limit = 50 } = {}) { const lookupAt = new Date().toISOString(); const boundedLimit = Math.max(1, Math.min(500, Number(limit) || 50)); const candidatesResult = await pool.query( ` WITH successful_quotes AS ( SELECT quote_id, COALESCE(outcome_observed_at, submitted_at, command_at, computed_at) AS matched_at FROM ${QUOTE_OUTCOMES_TABLE} WHERE quote_id IS NOT NULL AND ( outcome_status = 'completed' OR attribution_status IN ('heuristic_match', 'linked_settlement', 'exact_match', 'attributed') ) UNION ALL SELECT quote_id, COALESCE(observed_at, ingested_at) AS matched_at FROM trade_execution_results WHERE quote_id IS NOT NULL AND ( payload->>'outcome_status' = 'completed' OR payload->>'attribution_status' IN ('heuristic_match', 'linked_settlement', 'exact_match', 'attributed') ) ), ranked AS ( SELECT quote_id, MAX(matched_at) AS matched_at FROM successful_quotes GROUP BY quote_id ) SELECT quote_id, matched_at FROM ranked ORDER BY matched_at DESC NULLS LAST LIMIT $1 `, [boundedLimit], ); const identifiers = { quote_ids: uniqueTextValues(candidatesResult.rows.map((row) => row.quote_id)), decision_ids: [], command_ids: [], }; if (!hasLookupIdentifiers(identifiers)) { return buildQuoteLifecycleLookupEvidence({ requestedIdentifier: 'successful_quote_lifecycle', lookupAt, matchedIdentifiers: identifiers, }); } const [ recentQuotes, recentTradeDecisions, recentExecuteTradeCommands, recentExecutionResults, recentQuoteOutcomes, ] = await Promise.all([ loadLifecycleLookupQuotes(pool, identifiers), loadLifecycleLookupTradeDecisions(pool, identifiers), loadLifecycleLookupExecuteTradeCommands(pool, identifiers), loadLifecycleLookupExecutionResults(pool, identifiers), loadLifecycleLookupQuoteOutcomes(pool, identifiers), ]); return buildQuoteLifecycleLookupEvidence({ requestedIdentifier: 'successful_quote_lifecycle', lookupAt, matchedIdentifierType: 'quote_id', matchedIdentifiers: identifiers, recentQuotes, recentTradeDecisions, recentExecuteTradeCommands, recentExecutionResults, recentQuoteOutcomes, }); } export async function loadQuoteLifecycleLookupEvidence(pool, { identifier } = {}) { const requestedIdentifier = String(identifier || '').trim(); const lookupAt = new Date().toISOString(); if (!requestedIdentifier) { return buildQuoteLifecycleLookupEvidence({ requestedIdentifier, lookupAt, }); } const candidatesResult = await pool.query( ` WITH candidates AS ( SELECT 'quote_id' AS matched_identifier_type, COALESCE(quote_id, payload->>'quote_id') AS quote_id, payload->>'decision_id' AS decision_id, payload->>'command_id' AS command_id, COALESCE(observed_at, ingested_at) AS matched_at FROM swap_demand_events WHERE quote_id = $1 OR payload->>'quote_id' = $1 UNION ALL SELECT CASE WHEN quote_id = $1 OR payload->>'quote_id' = $1 THEN 'quote_id' WHEN decision_key = $1 OR payload->>'decision_id' = $1 THEN 'decision_id' WHEN payload->>'command_id' = $1 THEN 'command_id' ELSE 'linked_identifier' END AS matched_identifier_type, COALESCE(quote_id, payload->>'quote_id') AS quote_id, COALESCE(payload->>'decision_id', decision_key) AS decision_id, payload->>'command_id' AS command_id, COALESCE(observed_at, ingested_at) AS matched_at FROM trade_decisions WHERE quote_id = $1 OR decision_key = $1 OR payload->>'quote_id' = $1 OR payload->>'decision_id' = $1 OR payload->>'command_id' = $1 UNION ALL SELECT CASE WHEN quote_id = $1 OR payload->>'quote_id' = $1 THEN 'quote_id' WHEN payload->>'decision_id' = $1 THEN 'decision_id' WHEN decision_key = $1 OR payload->>'command_id' = $1 THEN 'command_id' ELSE 'linked_identifier' END AS matched_identifier_type, COALESCE(quote_id, payload->>'quote_id') AS quote_id, payload->>'decision_id' AS decision_id, COALESCE(payload->>'command_id', decision_key) AS command_id, COALESCE(observed_at, ingested_at) AS matched_at FROM execute_trade_commands WHERE quote_id = $1 OR decision_key = $1 OR payload->>'quote_id' = $1 OR payload->>'decision_id' = $1 OR payload->>'command_id' = $1 UNION ALL SELECT CASE WHEN quote_id = $1 OR payload->>'quote_id' = $1 THEN 'quote_id' WHEN payload->>'decision_id' = $1 THEN 'decision_id' WHEN decision_key = $1 OR payload->>'command_id' = $1 THEN 'command_id' ELSE 'linked_identifier' END AS matched_identifier_type, COALESCE(quote_id, payload->>'quote_id') AS quote_id, payload->>'decision_id' AS decision_id, COALESCE(payload->>'command_id', decision_key) AS command_id, COALESCE(observed_at, ingested_at) AS matched_at FROM trade_execution_results WHERE quote_id = $1 OR decision_key = $1 OR payload->>'quote_id' = $1 OR payload->>'decision_id' = $1 OR payload->>'command_id' = $1 UNION ALL SELECT CASE WHEN quote_id = $1 OR payload->>'quote_id' = $1 THEN 'quote_id' WHEN decision_id = $1 OR payload->>'decision_id' = $1 THEN 'decision_id' WHEN command_id = $1 OR payload->>'command_id' = $1 THEN 'command_id' ELSE 'linked_identifier' END AS matched_identifier_type, COALESCE(quote_id, payload->>'quote_id') AS quote_id, COALESCE(decision_id, payload->>'decision_id') AS decision_id, COALESCE(command_id, payload->>'command_id') AS command_id, COALESCE(outcome_observed_at, submitted_at, command_at, computed_at) AS matched_at FROM ${QUOTE_OUTCOMES_TABLE} WHERE quote_id = $1 OR decision_id = $1 OR command_id = $1 OR payload->>'quote_id' = $1 OR payload->>'decision_id' = $1 OR payload->>'command_id' = $1 ) SELECT * FROM candidates ORDER BY matched_at DESC NULLS LAST LIMIT 100 `, [requestedIdentifier], ); const identifiers = collectQuoteLifecycleLookupIdentifiers(candidatesResult.rows); if (!hasLookupIdentifiers(identifiers)) { return buildQuoteLifecycleLookupEvidence({ requestedIdentifier, lookupAt, matchedIdentifierType: null, matchedIdentifiers: identifiers, }); } const [ recentQuotes, recentTradeDecisions, recentExecuteTradeCommands, recentExecutionResults, recentQuoteOutcomes, ] = await Promise.all([ loadLifecycleLookupQuotes(pool, identifiers), loadLifecycleLookupTradeDecisions(pool, identifiers), loadLifecycleLookupExecuteTradeCommands(pool, identifiers), loadLifecycleLookupExecutionResults(pool, identifiers), loadLifecycleLookupQuoteOutcomes(pool, identifiers), ]); return buildQuoteLifecycleLookupEvidence({ requestedIdentifier, lookupAt, matchedIdentifierType: inferLookupMatchedIdentifierType( requestedIdentifier, candidatesResult.rows, ), matchedIdentifiers: identifiers, recentQuotes, recentTradeDecisions, recentExecuteTradeCommands, recentExecutionResults, recentQuoteOutcomes, }); } function buildQuoteLifecycleLookupEvidence({ requestedIdentifier, lookupAt, matchedIdentifierType = null, matchedIdentifiers = null, recentQuotes = [], recentTradeDecisions = [], recentExecuteTradeCommands = [], recentExecutionResults = [], recentQuoteOutcomes = [], } = {}) { return { found: Boolean( recentQuotes.length || recentTradeDecisions.length || recentExecuteTradeCommands.length || recentExecutionResults.length || recentQuoteOutcomes.length, ), requested_identifier: requestedIdentifier, matched_identifier_type: matchedIdentifierType, matched_identifiers: normalizeQuoteLifecycleLookupIdentifiers(matchedIdentifiers), lookup_at: lookupAt, recentQuotes, recentTradeDecisions, recentExecuteTradeCommands, recentExecutionResults, recentQuoteOutcomes, }; } function collectQuoteLifecycleLookupIdentifiers(rows = []) { return normalizeQuoteLifecycleLookupIdentifiers({ quote_ids: rows.map((row) => row.quote_id), decision_ids: rows.map((row) => row.decision_id), command_ids: rows.map((row) => row.command_id), }); } function normalizeQuoteLifecycleLookupIdentifiers(value = null) { return { quote_ids: uniqueTextValues(value?.quote_ids), decision_ids: uniqueTextValues(value?.decision_ids), command_ids: uniqueTextValues(value?.command_ids), }; } function uniqueTextValues(values = []) { return [...new Set( (values || []) .map((value) => String(value || '').trim()) .filter(Boolean), )]; } function hasLookupIdentifiers(identifiers) { return Boolean( identifiers?.quote_ids?.length || identifiers?.decision_ids?.length || identifiers?.command_ids?.length, ); } function inferLookupMatchedIdentifierType(identifier, rows = []) { const requestedIdentifier = String(identifier || '').trim(); const direct = rows.find((row) => ( row.matched_identifier_type && ( row.quote_id === requestedIdentifier || row.decision_id === requestedIdentifier || row.command_id === requestedIdentifier ) )); return direct?.matched_identifier_type || rows[0]?.matched_identifier_type || null; } async function loadLifecycleLookupQuotes(pool, identifiers) { if (!identifiers.quote_ids.length) return []; const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM swap_demand_events WHERE quote_id = ANY($1::text[]) OR payload->>'quote_id' = ANY($1::text[]) ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT 20 `, [identifiers.quote_ids], ); return result.rows.map(normalizeRecentQuoteRow); } async function loadLifecycleLookupTradeDecisions(pool, identifiers) { const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM trade_decisions WHERE quote_id = ANY($1::text[]) OR payload->>'quote_id' = ANY($1::text[]) OR decision_key = ANY($2::text[]) OR payload->>'decision_id' = ANY($2::text[]) ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT 20 `, [identifiers.quote_ids, identifiers.decision_ids], ); return result.rows.map((row) => ({ observed_at: toIsoTimestamp(row.observed_at), ingested_at: toIsoTimestamp(row.ingested_at), payload: row.payload, })); } async function loadLifecycleLookupExecuteTradeCommands(pool, identifiers) { const result = await pool.query( ` SELECT observed_at, ingested_at, payload FROM execute_trade_commands WHERE quote_id = ANY($1::text[]) OR payload->>'quote_id' = ANY($1::text[]) OR payload->>'decision_id' = ANY($2::text[]) OR decision_key = ANY($3::text[]) OR payload->>'command_id' = ANY($3::text[]) ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT 20 `, [identifiers.quote_ids, identifiers.decision_ids, identifiers.command_ids], ); return result.rows.map((row) => normalizeExecuteTradeCommandRow(row)); } async function loadLifecycleLookupExecutionResults(pool, identifiers) { 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 WHERE r.quote_id = ANY($1::text[]) OR r.payload->>'quote_id' = ANY($1::text[]) OR r.payload->>'decision_id' = ANY($2::text[]) OR r.decision_key = ANY($3::text[]) OR r.payload->>'command_id' = ANY($3::text[]) ORDER BY COALESCE(r.observed_at, r.ingested_at) DESC LIMIT 20 `, [identifiers.quote_ids, identifiers.decision_ids, identifiers.command_ids], ); return result.rows.map((row) => normalizeExecutionResultRow(row)); } async function loadLifecycleLookupQuoteOutcomes(pool, identifiers) { 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} WHERE quote_id = ANY($1::text[]) OR payload->>'quote_id' = ANY($1::text[]) OR decision_id = ANY($2::text[]) OR payload->>'decision_id' = ANY($2::text[]) OR command_id = ANY($3::text[]) OR payload->>'command_id' = ANY($3::text[]) ORDER BY COALESCE(outcome_observed_at, submitted_at, command_at, computed_at) DESC LIMIT 20 `, [identifiers.quote_ids, identifiers.decision_ids, identifiers.command_ids], ); 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) || 50000); 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(); const computedRecords = records.map((record) => ({ ...record, computed_at: computedAt, })); for (const record of computedRecords) { await upsertIntentRequestOutcome(pool, { ...record, computedAt, }); } return computedRecords; } 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, jsonb_build_object('spendable', payload->'spendable', 'synced_at', payload->'synced_at') AS 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, jsonb_build_object('spendable', payload->'spendable', 'synced_at', payload->'synced_at') AS 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 loadLatestMarketPriceForRoute(pool, { priceRouteId } = {}) { if (!priceRouteId) return loadLatestMarketPrice(pool); const latest = await loadLatestEventPayload( pool, 'market_price_events', "WHERE payload->>'price_route_id' = $1 ORDER BY COALESCE(observed_at, ingested_at) DESC LIMIT 1", [priceRouteId], ); 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, edge_bps: payload.edge_bps || null, gross_edge_pct: payload.gross_edge_pct || null, notional: payload.notional || null, notional_asset_id: payload.notional_asset_id || null, notional_symbol: payload.notional_symbol || 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, failure_category: payload.failure_category || null, maker_timing: payload.maker_timing || 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, maker_timing: payload.maker_timing || 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, failure_category: resultPayload.failure_category || null, relay_error_message: resultPayload.relay_error_message || resultPayload.error?.message || 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, maker_timing: resultPayload.maker_timing || commandPayload.maker_timing || decisionPayload.maker_timing || 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'), maker_timing: payload.maker_timing || null, quote_age_at_command_ms: payload.quote_age_at_command_ms ?? null, 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, failure_category: resultPayload.failure_category || 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.relay_error_message || resultPayload.error?.message || null, note: resultPayload.note || null, timing: resultPayload.executor_timing || null, maker_timing: resultPayload.maker_timing || commandPayload.maker_timing || decisionPayload.maker_timing || 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 floorTimestamp(value, granularityMs) { const parsed = Date.parse(value || ''); const granularity = Number(granularityMs); if (!Number.isFinite(parsed) || !Number.isInteger(granularity) || granularity <= 0) return null; return new Date(Math.floor(parsed / granularity) * granularity).toISOString(); } function laterTimestamp(left, right) { const leftMs = timestampValue(left); const rightMs = timestampValue(right); if (!Number.isFinite(leftMs)) return toIsoTimestamp(right); if (!Number.isFinite(rightMs)) return toIsoTimestamp(left); return new Date(Math.max(leftMs, rightMs)).toISOString(); } 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 ensureSchemaIndex(pool, ` CREATE INDEX IF NOT EXISTS ${name} ON ${table} (${expression}) `); } export async function ensureSchemaIndex(pool, sql) { try { await pool.query(sql); } catch (error) { if (isConcurrentSchemaIndexExistsError(error)) return; throw error; } } function isConcurrentSchemaIndexExistsError(error) { return error?.code === '42P07' || ( error?.code === '23505' && error?.constraint === 'pg_class_relname_nsp_index' ); }