All checks were successful
deploy / deploy (push) Successful in 52s
Proof: Core bootstrap is shell-only; page endpoints, canonical lifecycle selection, cursor submissions, persisted statistics reads, bounded dashboard transactions, and resilient client resources are covered by targeted regressions, full npm test, and the production dashboard build. Assumptions: Existing durable indexes and history-writer maintenance path remain valid; persisted lifecycle statistics and retained terminal attribution are the truthful dashboard sources. Still fake: Exact all-time submission totals remain unavailable, venue-native fill truth remains limited to retained durable evidence, and production latency/deployment evidence remains to be collected after push.
3083 lines
114 KiB
JavaScript
3083 lines
114 KiB
JavaScript
import { unitsToNumber } from './assets.mjs';
|
|
import { bridgeDepositObservedAt } from './bridge-assets.mjs';
|
|
import { CREDITED_STATUSES, summarizeFundingObservations } from './funding-observations.mjs';
|
|
import { buildMakerCompetitivenessSummary } from './maker-competitiveness.mjs';
|
|
import { resolveDashboardRequestAuth } from './operator-dashboard-auth.mjs';
|
|
import { buildQuoteLifecycleStatistics } from './quote-lifecycle-statistics.mjs';
|
|
import { TERMINAL_SETTLEMENT_ATTRIBUTION_STATUSES } from './quote-outcomes.mjs';
|
|
import { inferServiceFreshnessTimestamp as inferRuntimeFreshnessTimestamp } from './runtime-health.mjs';
|
|
|
|
export const DASHBOARD_LIVE_QUOTE_LIMIT = 10;
|
|
export const DASHBOARD_LIVE_LIFECYCLE_LIMIT = 20;
|
|
export const DASHBOARD_WEBSOCKET_MAX_BUFFERED_BYTES = 1_000_000;
|
|
|
|
const DECIMAL_SCALE = 18;
|
|
const DECIMAL_FACTOR = 10n ** BigInt(DECIMAL_SCALE);
|
|
const ALERT_SEVERITY_ORDER = {
|
|
critical: 3,
|
|
warning: 2,
|
|
info: 1,
|
|
};
|
|
|
|
const CONTROL_DEFINITIONS = [
|
|
{
|
|
service: 'near-intents-ingest',
|
|
action: 'reconnect',
|
|
method: 'POST',
|
|
path: '/reconnect',
|
|
label: 'Reconnect Ingest',
|
|
description: 'Reconnect the ingest websocket without restarting the deployment.',
|
|
page: 'system',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'market-reference-ingest',
|
|
action: 'refresh',
|
|
method: 'POST',
|
|
path: '/refresh',
|
|
label: 'Refresh Price',
|
|
description: 'Fetch the latest BTC/EUR reference price.',
|
|
page: 'funds',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'inventory-sync',
|
|
action: 'refresh',
|
|
method: 'POST',
|
|
path: '/refresh',
|
|
label: 'Refresh Inventory',
|
|
description: 'Sync verifier balances into the latest inventory snapshot.',
|
|
page: 'funds',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'operator-dashboard',
|
|
action: 'import-supported-assets',
|
|
method: 'POST',
|
|
path: '/internal/import-supported-assets',
|
|
label: 'Import Assets',
|
|
description: 'Fetch and store the current 1Click supported token catalog.',
|
|
page: 'strategy',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'operator-dashboard',
|
|
action: 'update-pair-edge',
|
|
method: 'POST',
|
|
path: '/internal/update-pair-edge',
|
|
label: 'Update Edge',
|
|
description: 'Create a new active strategy config version for a pair.',
|
|
page: 'strategy',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'operator-dashboard',
|
|
action: 'enable-observe-only-pair',
|
|
method: 'POST',
|
|
path: '/internal/enable-observe-only-pair',
|
|
label: 'Enable Observe-Only',
|
|
description: 'Approve a directed pair for observation without enabling trading.',
|
|
page: 'strategy',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'operator-dashboard',
|
|
action: 'set-pair-mode',
|
|
method: 'POST',
|
|
path: '/internal/set-pair-mode',
|
|
label: 'Set Pair Mode',
|
|
description: 'Activate a directed pair mode from durable DB config. Trading still requires valid strategy and price route state.',
|
|
page: 'strategy',
|
|
risk_class: 'live_funds',
|
|
},
|
|
{
|
|
service: 'operator-dashboard',
|
|
action: 'pause-pair',
|
|
method: 'POST',
|
|
path: '/internal/pause-pair',
|
|
label: 'Pause Pair',
|
|
description: 'Disable a directed pair in durable DB config without deleting historical strategy versions.',
|
|
page: 'strategy',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'trade-executor',
|
|
action: 'intent-request-preflight',
|
|
method: 'POST',
|
|
path: '/intent-request/preflight',
|
|
label: 'Preflight Pair Request',
|
|
description: 'Ask solvers for a configured pair request quote without submitting live funds.',
|
|
page: 'funds',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'trade-executor',
|
|
action: 'intent-request-submit',
|
|
method: 'POST',
|
|
path: '/intent-request/submit',
|
|
label: 'Submit Pair Request',
|
|
description: 'Submit a previously drafted pair request. Relay acceptance is not settlement.',
|
|
page: 'funds',
|
|
risk_class: 'live_funds',
|
|
},
|
|
{
|
|
service: 'trade-executor',
|
|
action: 'intent-request-refresh-outcomes',
|
|
method: 'POST',
|
|
path: '/intent-request/refresh-outcomes',
|
|
label: 'Refresh Request Outcomes',
|
|
description: 'Recompute own-request settlement attribution from durable inventory snapshots.',
|
|
page: 'funds',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'liquidity-manager',
|
|
action: 'refresh',
|
|
method: 'POST',
|
|
path: '/refresh',
|
|
label: 'Refresh Liquidity',
|
|
description: 'Refresh bridge deposit handles, deposits, and tracked withdrawals.',
|
|
page: 'funds',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'liquidity-manager',
|
|
action: 'pause',
|
|
method: 'POST',
|
|
path: '/pause',
|
|
label: 'Pause Liquidity',
|
|
description: 'Pause non-fund-moving liquidity state refreshes.',
|
|
page: 'system',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'liquidity-manager',
|
|
action: 'resume',
|
|
method: 'POST',
|
|
path: '/resume',
|
|
label: 'Resume Liquidity',
|
|
description: 'Resume liquidity state refreshes.',
|
|
page: 'system',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'liquidity-manager',
|
|
action: 'pause-funding-observer',
|
|
method: 'POST',
|
|
path: '/pause-funding-observer',
|
|
label: 'Pause Funding Observer',
|
|
description: 'Pause pre-credit funding observations without touching spendable truth.',
|
|
page: 'funds',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'liquidity-manager',
|
|
action: 'resume-funding-observer',
|
|
method: 'POST',
|
|
path: '/resume-funding-observer',
|
|
label: 'Resume Funding Observer',
|
|
description: 'Resume pre-credit funding observations.',
|
|
page: 'funds',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'liquidity-manager',
|
|
action: 'freeze-withdrawals',
|
|
method: 'POST',
|
|
path: '/freeze-withdrawals',
|
|
label: 'Update Withdrawal Freeze',
|
|
description: 'Toggle withdrawal freeze without submitting live withdrawals.',
|
|
page: 'funds',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'liquidity-manager',
|
|
action: 'withdrawal-estimate',
|
|
method: 'POST',
|
|
path: '/withdrawal-estimate',
|
|
label: 'Estimate Withdrawal',
|
|
description: 'Estimate a bridge withdrawal without moving funds.',
|
|
page: 'funds',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'trade-executor',
|
|
action: 'reconnect',
|
|
method: 'POST',
|
|
path: '/reconnect',
|
|
label: 'Reconnect Relay',
|
|
description: 'Reconnect the trade-executor solver relay websocket.',
|
|
page: 'system',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'trade-executor',
|
|
action: 'arm',
|
|
method: 'POST',
|
|
path: '/arm',
|
|
label: 'Arm Executor',
|
|
description: 'Persistently arm trade-executor so strategy-approved decisions can submit.',
|
|
page: 'system',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'trade-executor',
|
|
action: 'pause',
|
|
method: 'POST',
|
|
path: '/pause',
|
|
label: 'Pause Executor Intake',
|
|
description: 'Pause trade-executor command consumption without changing armed state.',
|
|
page: 'system',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'trade-executor',
|
|
action: 'resume',
|
|
method: 'POST',
|
|
path: '/resume',
|
|
label: 'Resume Executor Intake',
|
|
description: 'Resume trade-executor command consumption without changing armed state.',
|
|
page: 'system',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'trade-executor',
|
|
action: 'disarm',
|
|
method: 'POST',
|
|
path: '/disarm',
|
|
label: 'Disarm Executor',
|
|
description: 'Force the executor into a non-fund-moving safe state.',
|
|
page: 'system',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'ops-sentinel',
|
|
action: 'pause',
|
|
method: 'POST',
|
|
path: '/pause',
|
|
label: 'Pause Sentinel',
|
|
description: 'Pause background observation without changing trade arming state.',
|
|
page: 'system',
|
|
risk_class: 'safe',
|
|
},
|
|
{
|
|
service: 'ops-sentinel',
|
|
action: 'resume',
|
|
method: 'POST',
|
|
path: '/resume',
|
|
label: 'Resume Sentinel',
|
|
description: 'Resume background observation.',
|
|
page: 'system',
|
|
risk_class: 'safe',
|
|
},
|
|
];
|
|
|
|
const SERVICE_DEFINITIONS = [
|
|
['near-intents-ingest', 'Intents Ingest', 'nearIntentsControlBaseUrl'],
|
|
['market-reference-ingest', 'Reference Price', 'marketReferenceControlBaseUrl'],
|
|
['inventory-sync', 'Inventory Sync', 'inventorySyncControlBaseUrl'],
|
|
['liquidity-manager', 'Liquidity Manager', 'liquidityManagerControlBaseUrl'],
|
|
['history-writer', 'History Writer', 'historyWriterControlBaseUrl'],
|
|
['ops-sentinel', 'Ops Sentinel', 'opsSentinelControlBaseUrl'],
|
|
['strategy-engine', 'Strategy Engine', 'strategyEngineControlBaseUrl'],
|
|
['trade-executor', 'Trade Executor', 'tradeExecutorControlBaseUrl'],
|
|
['operator-dashboard', 'Operator Dashboard', 'operatorDashboardControlBaseUrl'],
|
|
];
|
|
|
|
export function resolveDashboardAuth({ mode = 'stub' } = {}) {
|
|
return resolveDashboardRequestAuth({ mode });
|
|
}
|
|
|
|
export function listDashboardControls({ page = null } = {}) {
|
|
const controls = CONTROL_DEFINITIONS.map((definition) => ({ ...definition }));
|
|
if (!page) return controls;
|
|
return controls.filter((definition) => definition.page === page);
|
|
}
|
|
|
|
export function resolveDashboardControl({ service, action }) {
|
|
return CONTROL_DEFINITIONS.find((definition) => (
|
|
definition.service === service && definition.action === action
|
|
)) || null;
|
|
}
|
|
|
|
export function buildDashboardControlErrorResponse(error, { control = null } = {}) {
|
|
const name = String(error?.name || 'Error');
|
|
const message = String(error?.message || 'Control request failed.');
|
|
const timedOut = name === 'TimeoutError' || /timeout|aborted/i.test(message);
|
|
return {
|
|
statusCode: timedOut ? 504 : 502,
|
|
payload: {
|
|
ok: false,
|
|
error: timedOut ? 'control_timeout' : 'control_failed',
|
|
reason: message,
|
|
control,
|
|
},
|
|
};
|
|
}
|
|
|
|
export function isDashboardWebSocketBackpressured(socket, {
|
|
maxBufferedBytes = DASHBOARD_WEBSOCKET_MAX_BUFFERED_BYTES,
|
|
} = {}) {
|
|
const bufferedAmount = Number(socket?.bufferedAmount || 0);
|
|
const maxBytes = Number(maxBufferedBytes);
|
|
return Number.isFinite(bufferedAmount)
|
|
&& Number.isFinite(maxBytes)
|
|
&& maxBytes >= 0
|
|
&& bufferedAmount > maxBytes;
|
|
}
|
|
|
|
export function resolveDashboardControlTimeoutMs({ control, config } = {}) {
|
|
const baseTimeoutMs = Number(config?.operatorDashboardUpstreamTimeoutMs || 3_000);
|
|
if (control?.service !== 'trade-executor') return baseTimeoutMs;
|
|
|
|
if (control.action === 'intent-request-preflight') {
|
|
return Math.max(baseTimeoutMs, Number(config?.intentRequestQuoteTimeoutMs || 10_000) + 2_000);
|
|
}
|
|
if (control.action === 'intent-request-submit') {
|
|
return Math.max(
|
|
baseTimeoutMs,
|
|
Number(config?.intentRequestPublishTimeoutMs || 10_000)
|
|
+ Number(config?.intentRequestStatusTimeoutMs || 10_000)
|
|
+ 5_000,
|
|
);
|
|
}
|
|
if (control.action === 'intent-request-refresh-outcomes') {
|
|
return Math.max(baseTimeoutMs, Number(config?.intentRequestStatusTimeoutMs || 10_000) + 5_000);
|
|
}
|
|
return baseTimeoutMs;
|
|
}
|
|
|
|
export function listDashboardServices(config) {
|
|
return SERVICE_DEFINITIONS.map(([service, label, configKey]) => ({
|
|
service,
|
|
label,
|
|
base_url: config[configKey],
|
|
}));
|
|
}
|
|
|
|
export function createDashboardLiveState({
|
|
config,
|
|
recentQuotes = [],
|
|
recentTradeDecisions = [],
|
|
recentExecuteTradeCommands = [],
|
|
recentExecutionResults = [],
|
|
recentQuoteOutcomes = [],
|
|
latestMarketPrice = null,
|
|
latestInventory = null,
|
|
recentSubmissionCount = 0,
|
|
lastSubmissionAt = null,
|
|
activeAlerts = [],
|
|
nearIntentsStatus = null,
|
|
} = {}) {
|
|
const state = {
|
|
config,
|
|
active_pair: config.activePair,
|
|
btc_asset: config.tradingBtc,
|
|
btc_assets: config.tradingBtcAssets || [config.tradingBtc],
|
|
eure_asset: config.tradingEure,
|
|
quote_limit: config.operatorDashboardQuoteLimit || DASHBOARD_LIVE_QUOTE_LIMIT,
|
|
lifecycle_limit: config.operatorDashboardLifecycleLimit || DASHBOARD_LIVE_LIFECYCLE_LIMIT,
|
|
recent_quotes: recentQuotes.slice(0, config.operatorDashboardQuoteLimit || DASHBOARD_LIVE_QUOTE_LIMIT),
|
|
recent_trade_decisions: recentTradeDecisions.slice(0, config.operatorDashboardLifecycleLimit || DASHBOARD_LIVE_LIFECYCLE_LIMIT),
|
|
recent_execute_trade_commands: recentExecuteTradeCommands.slice(0, config.operatorDashboardLifecycleLimit || DASHBOARD_LIVE_LIFECYCLE_LIMIT),
|
|
recent_execution_results: recentExecutionResults.slice(0, config.operatorDashboardLifecycleLimit || DASHBOARD_LIVE_LIFECYCLE_LIMIT),
|
|
recent_quote_outcomes: recentQuoteOutcomes.slice(0, config.operatorDashboardLifecycleLimit || DASHBOARD_LIVE_LIFECYCLE_LIMIT),
|
|
latest_market_price: latestMarketPrice?.payload || latestMarketPrice || null,
|
|
latest_inventory: latestInventory?.payload || latestInventory || null,
|
|
recent_submission_count: Number(recentSubmissionCount || 0),
|
|
last_submission_at: lastSubmissionAt || null,
|
|
near_intents_status: nearIntentsStatus,
|
|
active_alerts: new Map(),
|
|
};
|
|
|
|
for (const alert of activeAlerts) {
|
|
if (!alert) continue;
|
|
state.active_alerts.set(buildAlertKey(alert), alert);
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
export function buildLiveQuoteLifecycleRows(state, { flashQuoteId = null, flashAt = null } = {}) {
|
|
const rows = deriveQuoteLifecycleRows({
|
|
recentQuotes: state.recent_quotes,
|
|
recentTradeDecisions: state.recent_trade_decisions,
|
|
recentExecuteTradeCommands: state.recent_execute_trade_commands,
|
|
recentExecutionResults: state.recent_execution_results,
|
|
recentQuoteOutcomes: state.recent_quote_outcomes,
|
|
limit: state.lifecycle_limit,
|
|
}).map((row) => enrichLifecycleRowForUi({ config: state.config, row }));
|
|
|
|
if (!flashQuoteId) return rows;
|
|
const highlightedAt = flashAt || new Date().toISOString();
|
|
return rows.map((row) => (
|
|
row.quote_id === flashQuoteId
|
|
? { ...row, live_flash_at: highlightedAt }
|
|
: row
|
|
));
|
|
}
|
|
|
|
export function buildLiveQuoteLifecycleStatistics(state, {
|
|
computedAt = new Date().toISOString(),
|
|
} = {}) {
|
|
const lifecycleRows = deriveQuoteLifecycleRows({
|
|
recentQuotes: state.recent_quotes,
|
|
recentTradeDecisions: state.recent_trade_decisions,
|
|
recentExecuteTradeCommands: state.recent_execute_trade_commands,
|
|
recentExecutionResults: state.recent_execution_results,
|
|
recentQuoteOutcomes: state.recent_quote_outcomes,
|
|
limit: null,
|
|
}).map((row) => enrichLifecycleRowForUi({ config: state.config, row }));
|
|
return {
|
|
source: 'live_recent',
|
|
generated_at: computedAt,
|
|
rows: buildQuoteLifecycleStatistics({
|
|
lifecycleRows,
|
|
computedAt,
|
|
}),
|
|
};
|
|
}
|
|
|
|
export function applyDashboardLiveEvent(state, { topic, event }) {
|
|
if (!event?.payload) return [];
|
|
|
|
switch (normalizeDashboardLiveTopic(state, topic)) {
|
|
case 'norm.swap_demand': {
|
|
const quote = normalizeLiveQuote(event.payload, event);
|
|
if (!quote) return [];
|
|
state.recent_quotes = appendUniqueRecentQuote(state.recent_quotes, quote, state.quote_limit);
|
|
return [
|
|
{
|
|
type: 'quotes.recent',
|
|
recent_quotes: state.recent_quotes,
|
|
},
|
|
buildQuoteLifecycleUpdate(state, { flashQuoteId: quote.quote_id }),
|
|
];
|
|
}
|
|
case 'decision.trade_decision': {
|
|
const decision = normalizeLiveDecision(event.payload, event);
|
|
if (!decision) return [];
|
|
state.recent_trade_decisions = appendUniqueRecentEvent(
|
|
state.recent_trade_decisions,
|
|
decision,
|
|
state.lifecycle_limit,
|
|
(entry) => livePayloadKey(entry, ['decision_id', 'quote_id']),
|
|
);
|
|
return [buildQuoteLifecycleUpdate(state, { flashQuoteId: decision.payload?.quote_id })];
|
|
}
|
|
case 'cmd.execute_trade': {
|
|
const command = normalizeLiveCommand(event.payload, event);
|
|
if (!command) return [];
|
|
state.recent_execute_trade_commands = appendUniqueRecentEvent(
|
|
state.recent_execute_trade_commands,
|
|
command,
|
|
state.lifecycle_limit,
|
|
(entry) => livePayloadKey(entry, ['command_id', 'decision_id', 'quote_id']),
|
|
);
|
|
return [buildQuoteLifecycleUpdate(state, { flashQuoteId: command.payload?.quote_id })];
|
|
}
|
|
case 'ref.market_price':
|
|
state.latest_market_price = {
|
|
...event.payload,
|
|
observed_at: event.observed_at || event.payload.observed_at || null,
|
|
ingested_at: event.ingested_at || null,
|
|
};
|
|
return [{
|
|
type: 'status_bar.updated',
|
|
status_bar: buildLiveStatusBar(state),
|
|
}];
|
|
case 'state.intent_inventory':
|
|
state.latest_inventory = {
|
|
...event.payload,
|
|
observed_at: event.observed_at || event.payload.synced_at || null,
|
|
ingested_at: event.ingested_at || null,
|
|
};
|
|
return [{
|
|
type: 'status_bar.updated',
|
|
status_bar: buildLiveStatusBar(state),
|
|
}];
|
|
case 'ops.alert': {
|
|
return [];
|
|
}
|
|
case 'ops.environment_status': {
|
|
state.near_intents_status = event.payload;
|
|
return [
|
|
{
|
|
type: 'status_bar.updated',
|
|
status_bar: buildLiveStatusBar(state),
|
|
},
|
|
{
|
|
type: 'environment_status.updated',
|
|
environment_status: {
|
|
observed_at: event.observed_at || event.payload.observed_at || null,
|
|
ingested_at: event.ingested_at || null,
|
|
payload: event.payload,
|
|
},
|
|
},
|
|
];
|
|
}
|
|
case 'exec.trade_result': {
|
|
const execution = normalizeLiveExecutionResult(event.payload, event);
|
|
if (!execution) return [];
|
|
state.recent_execution_results = appendUniqueRecentEvent(
|
|
state.recent_execution_results,
|
|
execution,
|
|
state.lifecycle_limit,
|
|
(entry) => livePayloadKey(entry, ['command_id', 'decision_id', 'quote_id', 'result_at']),
|
|
);
|
|
const updates = [];
|
|
if (event.payload.status === 'submitted') {
|
|
state.recent_submission_count += 1;
|
|
state.last_submission_at = event.observed_at || event.ingested_at || new Date().toISOString();
|
|
updates.push({
|
|
type: 'status_bar.updated',
|
|
status_bar: buildLiveStatusBar(state),
|
|
});
|
|
}
|
|
updates.push(buildQuoteLifecycleUpdate(state, { flashQuoteId: execution.quote_id }));
|
|
return updates;
|
|
}
|
|
default:
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export function buildDashboardBootstrap({
|
|
config,
|
|
auth,
|
|
portfolioMetric,
|
|
inventorySnapshot,
|
|
marketPrice,
|
|
recentQuotes,
|
|
submissionPage,
|
|
submissionSummary,
|
|
fundingObservations,
|
|
recentDepositStatuses,
|
|
recentTradeDecisions,
|
|
recentExecuteTradeCommands,
|
|
recentExecutionResults,
|
|
recentQuoteOutcomes = [],
|
|
successfulQuoteLifecycle = null,
|
|
quoteLifecycleRetention = null,
|
|
quoteLifecycleRollups = [],
|
|
quoteLifecycleStatistics = null,
|
|
recentIntentRequests = [],
|
|
recentAlertTransitions,
|
|
recentEnvironmentStatuses = [],
|
|
assetCatalog = null,
|
|
pairConfig = null,
|
|
serviceSnapshots,
|
|
nearIntentsStatus = null,
|
|
sourceErrors = [],
|
|
} = {}) {
|
|
const servicesByName = Object.fromEntries(
|
|
(serviceSnapshots || []).map((snapshot) => [snapshot.service, snapshot]),
|
|
);
|
|
const activeAlerts = normalizeAlertList(
|
|
servicesByName['ops-sentinel']?.state?.active_alerts || [],
|
|
);
|
|
const recentAlerts = summarizeRecentAlertTransitions(normalizeAlertList(
|
|
servicesByName['ops-sentinel']?.state?.recent_transitions
|
|
|| recentAlertTransitions?.map((entry) => entry.payload)
|
|
|| [],
|
|
));
|
|
const profitability = buildProfitabilitySummary({
|
|
metric: portfolioMetric,
|
|
submissionSummary,
|
|
});
|
|
const balances = buildBalanceSummary({
|
|
inventorySnapshot,
|
|
marketPrice,
|
|
config,
|
|
portfolioMetric,
|
|
});
|
|
const funding = buildFundingSummary({
|
|
config,
|
|
fundingObservations,
|
|
recentDepositStatuses,
|
|
liquidityState: servicesByName['liquidity-manager']?.state || {},
|
|
});
|
|
const normalizedSubmissionPage = normalizeSubmissionPage({
|
|
config,
|
|
submissionPage,
|
|
});
|
|
const environmentStatus = buildEnvironmentStatusHistory({
|
|
recentEnvironmentStatuses,
|
|
currentStatus: nearIntentsStatus,
|
|
});
|
|
const effectiveNearIntentsStatus = nearIntentsStatus || environmentStatus.current;
|
|
|
|
return {
|
|
session: auth,
|
|
source_errors: sourceErrors,
|
|
default_page: 'funds',
|
|
status_bar: buildStatusBar({
|
|
config,
|
|
profitability,
|
|
inventorySnapshot,
|
|
marketPrice,
|
|
activeAlerts,
|
|
servicesByName,
|
|
nearIntentsStatus: effectiveNearIntentsStatus,
|
|
}),
|
|
funds: {
|
|
profitability,
|
|
balances,
|
|
funding,
|
|
recent_deposits: funding.credited_deposits,
|
|
recent_withdrawals: buildRecentWithdrawals({
|
|
config,
|
|
liquidityState: servicesByName['liquidity-manager']?.state || {},
|
|
}),
|
|
recent_submission_terms: buildRecentSubmissionTerms({
|
|
config,
|
|
submissions: normalizedSubmissionPage.items,
|
|
}),
|
|
recent_quotes: (recentQuotes || []).slice(0, config.operatorDashboardQuoteLimit || DASHBOARD_LIVE_QUOTE_LIMIT),
|
|
intent_requests: buildIntentRequestSummary({
|
|
config,
|
|
intentRequests: recentIntentRequests,
|
|
executorState: servicesByName['trade-executor']?.state || {},
|
|
}),
|
|
submission_ledger: normalizedSubmissionPage,
|
|
controls: listDashboardControls({ page: 'funds' }),
|
|
caveats: profitability.caveats,
|
|
},
|
|
strategy: buildStrategySummary({
|
|
config,
|
|
servicesByName,
|
|
activeAlerts,
|
|
assetCatalog,
|
|
pairConfig,
|
|
fundingHandles: funding.handles,
|
|
recentQuotes,
|
|
recentTradeDecisions,
|
|
recentExecuteTradeCommands,
|
|
recentExecutionResults,
|
|
recentQuoteOutcomes,
|
|
successfulQuoteLifecycle,
|
|
quoteLifecycleRollups,
|
|
quoteLifecycleStatistics,
|
|
}),
|
|
system: buildSystemSummary({
|
|
servicesByName,
|
|
activeAlerts,
|
|
recentAlerts,
|
|
nearIntentsStatus: effectiveNearIntentsStatus,
|
|
environmentStatus,
|
|
quoteLifecycleRetention,
|
|
quoteLifecycleRollups,
|
|
}),
|
|
};
|
|
}
|
|
|
|
function dashboardMetadata({ generatedAt = new Date().toISOString(), sourceErrors = [], freshness = {} } = {}) {
|
|
return {
|
|
generated_at: generatedAt,
|
|
source_freshness: freshness,
|
|
source_errors: sourceErrors,
|
|
};
|
|
}
|
|
|
|
// The shell contract is intentionally independent from every page read. Live state is
|
|
// already maintained by the dashboard process and is the only source used here.
|
|
export function buildDashboardCorePayload({ config, auth, liveState, sourceErrors = [] } = {}) {
|
|
const generatedAt = new Date().toISOString();
|
|
return {
|
|
session: auth,
|
|
default_page: 'funds',
|
|
status_bar: buildLiveStatusBar(liveState || {}),
|
|
navigation: {
|
|
pages: ['funds', 'strategy', 'system'],
|
|
controls: listDashboardControls(),
|
|
},
|
|
...dashboardMetadata({ generatedAt, sourceErrors, freshness: {
|
|
live: generatedAt,
|
|
} }),
|
|
};
|
|
}
|
|
|
|
export function buildDashboardPagePayload({ page, payload, sourceErrors = [], freshness = {} } = {}) {
|
|
return {
|
|
[page]: payload,
|
|
...dashboardMetadata({ sourceErrors, freshness }),
|
|
};
|
|
}
|
|
|
|
export function buildProfitabilitySummary({ metric, submissionSummary } = {}) {
|
|
const externalCashFlows = metric?.payload?.external_cash_flows || {};
|
|
const externalFlowCount = Number(externalCashFlows.flow_count || 0);
|
|
const externalFlowAdjusted = externalFlowCount > 0;
|
|
const summary = {
|
|
computed_at: metric?.computed_at || null,
|
|
current_total_portfolio_value_eure: metric?.payload?.current_portfolio_value_eure || null,
|
|
deposit_baseline_value_eure: metric?.payload?.baseline_portfolio_value_eure_at_baseline_price || null,
|
|
simple_hold_value_eure: metric?.payload?.baseline_portfolio_value_eure_at_current_price || null,
|
|
pnl_vs_deposit_baseline_eure: null,
|
|
pnl_vs_simple_hold_eure: null,
|
|
market_move_contribution_eure: null,
|
|
portfolio_vs_simple_hold_eure: null,
|
|
trading_contribution_eure: null,
|
|
baseline_anchor_at: metric?.baseline_anchor_at || null,
|
|
baseline_status: metric?.baseline_status || metric?.payload?.baseline_status || 'unavailable',
|
|
external_flow_adjusted: externalFlowAdjusted,
|
|
external_flow_count: externalFlowCount,
|
|
external_deposit_count: Number(externalCashFlows.deposit_count || 0),
|
|
external_withdrawal_count: Number(externalCashFlows.withdrawal_count || 0),
|
|
latest_external_flow_at: externalCashFlows.latest_effective_at || null,
|
|
net_external_flow_value_eure: externalCashFlows.net_value_eure_at_flow_time || '0',
|
|
recent_submission_count: submissionSummary?.total ?? 0,
|
|
last_submission_at: submissionSummary?.last_submission_at || null,
|
|
caveats: [
|
|
'Portfolio value and simple-hold comparison use durable inventory and reference prices.',
|
|
'This is not realized per-trade PnL; completed trades require linked outcome and settlement records.',
|
|
],
|
|
};
|
|
|
|
if (summary.current_total_portfolio_value_eure == null) {
|
|
summary.caveats.unshift('Profitability is unavailable until durable portfolio metrics exist.');
|
|
return summary;
|
|
}
|
|
|
|
if (summary.deposit_baseline_value_eure) {
|
|
summary.pnl_vs_deposit_baseline_eure = formatDecimalDifference(
|
|
summary.current_total_portfolio_value_eure,
|
|
summary.deposit_baseline_value_eure,
|
|
);
|
|
}
|
|
|
|
if (summary.simple_hold_value_eure) {
|
|
summary.pnl_vs_simple_hold_eure = formatDecimalDifference(
|
|
summary.current_total_portfolio_value_eure,
|
|
summary.simple_hold_value_eure,
|
|
);
|
|
}
|
|
|
|
if (summary.deposit_baseline_value_eure && summary.simple_hold_value_eure) {
|
|
summary.market_move_contribution_eure = formatDecimalDifference(
|
|
summary.simple_hold_value_eure,
|
|
summary.deposit_baseline_value_eure,
|
|
);
|
|
}
|
|
|
|
if (summary.simple_hold_value_eure) {
|
|
summary.portfolio_vs_simple_hold_eure = formatDecimalDifference(
|
|
summary.current_total_portfolio_value_eure,
|
|
summary.simple_hold_value_eure,
|
|
);
|
|
summary.trading_contribution_eure = null;
|
|
}
|
|
|
|
if (summary.external_flow_adjusted) {
|
|
summary.caveats.unshift(
|
|
`Later credited deposits and completed withdrawals (${summary.external_flow_count}) are treated as external cash flows, not trading PnL.`,
|
|
);
|
|
}
|
|
|
|
return summary;
|
|
}
|
|
|
|
export function buildLiveStatusBar(state) {
|
|
const referenceLabel = state.latest_market_price?.reference_pair || 'Reference route';
|
|
const referenceValue = state.latest_market_price?.quote_per_base
|
|
|| state.latest_market_price?.eure_per_btc
|
|
|| null;
|
|
return {
|
|
near_intents_upstream_status: state.near_intents_status?.status || null,
|
|
near_intents_upstream_label: state.near_intents_status?.label || null,
|
|
near_intents_upstream_reason: state.near_intents_status?.decisive_reason || null,
|
|
near_intents_upstream_observed_at: state.near_intents_status?.observed_at || null,
|
|
latest_reference_price_eure_per_btc: state.latest_market_price?.eure_per_btc || null,
|
|
latest_reference_route_label: referenceLabel,
|
|
latest_reference_route_value: referenceValue,
|
|
market_observed_at:
|
|
state.latest_market_price?.observed_at
|
|
|| state.latest_market_price?.ingested_at
|
|
|| null,
|
|
market_freshness_ms: ageMs(
|
|
state.latest_market_price?.observed_at || state.latest_market_price?.ingested_at,
|
|
),
|
|
inventory_observed_at:
|
|
state.latest_inventory?.synced_at
|
|
|| state.latest_inventory?.observed_at
|
|
|| state.latest_inventory?.ingested_at
|
|
|| null,
|
|
inventory_freshness_ms: ageMs(
|
|
state.latest_inventory?.synced_at
|
|
|| state.latest_inventory?.observed_at
|
|
|| state.latest_inventory?.ingested_at,
|
|
),
|
|
current_total_portfolio_value_eure: computeCurrentPortfolioValue({
|
|
inventory: state.latest_inventory,
|
|
marketPrice: state.latest_market_price,
|
|
btcAsset: state.btc_asset,
|
|
btcAssets: state.btc_assets,
|
|
eureAsset: state.eure_asset,
|
|
}),
|
|
active_alert_count: 0,
|
|
highest_alert_severity: null,
|
|
recent_submission_count: state.recent_submission_count,
|
|
last_submission_at: state.last_submission_at,
|
|
};
|
|
}
|
|
|
|
function buildStatusBar({
|
|
config,
|
|
profitability,
|
|
inventorySnapshot,
|
|
marketPrice,
|
|
activeAlerts,
|
|
servicesByName,
|
|
nearIntentsStatus = null,
|
|
}) {
|
|
const activePairs = (config.observedPairs || config.pairs || [])
|
|
.filter((pair) => pair.observeEnabled || pair.enabled)
|
|
.map((pair) => pair.key || pair.pairId)
|
|
.filter(Boolean);
|
|
const referenceRoutes = (config.pairs || [])
|
|
.filter((pair) => pair.priceRoute?.routeId)
|
|
.map((pair) => ({
|
|
pair: pair.key || pair.pairId,
|
|
price_route_id: pair.priceRoute.routeId,
|
|
reference_pair: pair.priceRoute.routeConfig?.reference_pair || pair.priceRoute.source || null,
|
|
}));
|
|
const referenceLabel = marketPrice?.payload?.reference_pair
|
|
|| referenceRoutes[0]?.reference_pair
|
|
|| 'Reference route';
|
|
const referenceValue = marketPrice?.payload?.quote_per_base
|
|
|| marketPrice?.payload?.eure_per_btc
|
|
|| null;
|
|
return {
|
|
active_pair: config.activePair,
|
|
active_pairs: activePairs,
|
|
active_pair_count: activePairs.length,
|
|
reference_routes: referenceRoutes,
|
|
near_intents_upstream_status: nearIntentsStatus?.status || null,
|
|
near_intents_upstream_label: nearIntentsStatus?.label || null,
|
|
near_intents_upstream_reason: nearIntentsStatus?.decisive_reason || null,
|
|
near_intents_upstream_observed_at: nearIntentsStatus?.observed_at || null,
|
|
latest_reference_price_eure_per_btc: marketPrice?.payload?.eure_per_btc || null,
|
|
latest_reference_route_label: referenceLabel,
|
|
latest_reference_route_value: referenceValue,
|
|
market_observed_at: marketPrice?.payload?.observed_at || marketPrice?.ingested_at || null,
|
|
market_freshness_ms: ageMs(marketPrice?.payload?.observed_at || marketPrice?.ingested_at),
|
|
inventory_observed_at:
|
|
inventorySnapshot?.payload?.synced_at || inventorySnapshot?.ingested_at || null,
|
|
inventory_freshness_ms: ageMs(
|
|
inventorySnapshot?.payload?.synced_at || inventorySnapshot?.ingested_at,
|
|
),
|
|
active_alert_count: 0,
|
|
highest_alert_severity: null,
|
|
strategy_armed: servicesByName['strategy-engine']?.state?.armed ?? null,
|
|
executor_armed: servicesByName['trade-executor']?.state?.armed ?? null,
|
|
current_total_portfolio_value_eure: profitability.current_total_portfolio_value_eure,
|
|
recent_submission_count: profitability.recent_submission_count,
|
|
last_submission_at: profitability.last_submission_at,
|
|
};
|
|
}
|
|
|
|
function buildBalanceSummary({ inventorySnapshot, marketPrice, config, portfolioMetric = null }) {
|
|
const inventory = inventorySnapshot?.payload || {};
|
|
const spendable = inventory.spendable || {};
|
|
const pendingInbound = inventory.pending_inbound || {};
|
|
const pendingOutbound = inventory.pending_outbound || {};
|
|
const balanceAssets = config.trackedAssets?.length
|
|
? config.trackedAssets
|
|
: [...config.assetRegistry.values()];
|
|
const metricValuationsByAssetId = new Map(
|
|
(portfolioMetric?.payload?.current_inventory?.valued_assets || [])
|
|
.filter((asset) => asset?.asset_id)
|
|
.map((asset) => [asset.asset_id, asset]),
|
|
);
|
|
const metricUnvaluedByAssetId = new Map(
|
|
(portfolioMetric?.payload?.current_inventory?.unvalued_assets || [])
|
|
.filter((asset) => asset?.asset_id)
|
|
.map((asset) => [asset.asset_id, asset]),
|
|
);
|
|
|
|
return {
|
|
synced_at: inventory.synced_at || inventorySnapshot?.ingested_at || null,
|
|
reconciliation_status: inventory.reconciliation_status || null,
|
|
items: balanceAssets.map((asset) => {
|
|
const spendableUnits = String(spendable[asset.assetId] || '0');
|
|
const pendingInboundUnits = String(pendingInbound[asset.assetId] || '0');
|
|
const pendingOutboundUnits = String(pendingOutbound[asset.assetId] || '0');
|
|
const metricValuation = metricValuationsByAssetId.get(asset.assetId);
|
|
const metricUnvalued = metricUnvaluedByAssetId.get(asset.assetId);
|
|
const fallbackValue = valueAssetInEur({
|
|
asset,
|
|
units: spendableUnits,
|
|
marketPrice: marketPrice?.payload || marketPrice || null,
|
|
});
|
|
const value = metricValuation?.value_eure || fallbackValue;
|
|
return {
|
|
asset_id: asset.assetId,
|
|
symbol: asset.symbol,
|
|
label: asset.label || asset.symbol,
|
|
role: asset.role || null,
|
|
chain: asset.chain,
|
|
spendable_units: spendableUnits,
|
|
spendable: formatUnits(spendableUnits, asset.decimals),
|
|
pending_inbound_units: pendingInboundUnits,
|
|
pending_inbound: formatUnits(pendingInboundUnits, asset.decimals),
|
|
pending_outbound_units: pendingOutboundUnits,
|
|
pending_outbound: formatUnits(pendingOutboundUnits, asset.decimals),
|
|
eur_value_eure: value,
|
|
eur_value_source: metricValuation?.valuation_source || null,
|
|
valuation_status: value != null ? 'valued' : 'unvalued',
|
|
valuation_reason: value == null
|
|
? metricUnvalued?.valuation_reason || 'valuation_route_missing'
|
|
: null,
|
|
};
|
|
}),
|
|
};
|
|
}
|
|
|
|
function buildFundingSummary({ config, fundingObservations, recentDepositStatuses, liquidityState }) {
|
|
const observations = (fundingObservations || []).map((entry) => entry.payload || entry);
|
|
const summary = summarizeFundingObservations(observations, {
|
|
now: new Date().toISOString(),
|
|
});
|
|
const observerItems = observations.map((observation) => normalizeFundingObservationForUi({
|
|
config,
|
|
observation,
|
|
}));
|
|
const depositItems = buildRecentDepositItems({
|
|
config,
|
|
recentDepositStatuses,
|
|
liquidityState,
|
|
});
|
|
const recentFundingActivity = mergeFundingActivityItems({
|
|
observerItems,
|
|
depositItems,
|
|
});
|
|
|
|
return {
|
|
latest_observed_at: latestFundingActivityAt(recentFundingActivity, summary.latest_funding_observation_at),
|
|
control_state: {
|
|
paused: liquidityState?.paused ?? null,
|
|
funding_observer_paused: liquidityState?.funding_observer_paused ?? null,
|
|
withdrawals_frozen: liquidityState?.withdrawals_frozen ?? null,
|
|
withdrawal_defaults: liquidityState?.withdrawal_defaults || {},
|
|
},
|
|
handles: Object.entries(liquidityState?.deposit_addresses || {}).map(([chain, details]) => {
|
|
const chainAssets = (config.trackedAssets || [...config.assetRegistry.values()])
|
|
.filter((asset) => asset.chain === chain);
|
|
return {
|
|
chain,
|
|
asset_id: chainAssets.length === 1 ? chainAssets[0].assetId : null,
|
|
asset_ids: chainAssets.map((asset) => asset.assetId),
|
|
symbol: chainAssets.length === 1
|
|
? chainAssets[0].symbol
|
|
: chainAssets.map((asset) => asset.label || asset.symbol).join(', '),
|
|
label: chainAssets.length === 1
|
|
? (chainAssets[0].label || chainAssets[0].symbol)
|
|
: `${chain} funding handle`,
|
|
address: details?.address || null,
|
|
memo: details?.memo || null,
|
|
refreshed_at: details?.refreshed_at || null,
|
|
};
|
|
}),
|
|
credited_deposits: recentFundingActivity
|
|
.filter((observation) => CREDITED_STATUSES.has(String(observation?.status || '').toUpperCase()))
|
|
.sort((left, right) => sortTimestamps(
|
|
fundingActivityTimestamp(right),
|
|
fundingActivityTimestamp(left),
|
|
))
|
|
.slice(0, 10)
|
|
.map((observation) => ({ ...observation })),
|
|
pre_credit_by_asset: Object.values(summary.funding_visibility_by_asset || {}).map((entry) => {
|
|
const asset = config.assetRegistry.get(entry.asset_id);
|
|
return {
|
|
asset_id: entry.asset_id,
|
|
symbol: asset?.symbol || entry.asset_id,
|
|
asset_symbol: asset?.label || asset?.symbol || entry.asset_id,
|
|
pre_credit_total_units: entry.pre_credit_total || '0',
|
|
pre_credit_total: formatUnits(entry.pre_credit_total || '0', asset?.decimals || 0),
|
|
latest_status: entry.latest_status,
|
|
latest_observation_at: entry.latest_observation_at,
|
|
};
|
|
}),
|
|
pre_credit_by_handle: Object.values(summary.funding_observations_by_handle || {}).map((entry) => {
|
|
const asset = config.assetRegistry.get(entry.asset_id);
|
|
return {
|
|
funding_handle: entry.funding_handle,
|
|
chain: entry.chain,
|
|
asset_id: entry.asset_id,
|
|
symbol: asset?.symbol || entry.asset_id,
|
|
asset_symbol: asset?.label || asset?.symbol || entry.asset_id,
|
|
pre_credit_total_units: entry.pre_credit_total || '0',
|
|
pre_credit_total: formatUnits(entry.pre_credit_total || '0', asset?.decimals || 0),
|
|
latest_status: entry.latest_status,
|
|
latest_observation_at: entry.latest_observation_at,
|
|
observation_count: entry.observations?.length || 0,
|
|
};
|
|
}),
|
|
recent_observations: recentFundingActivity
|
|
.sort((left, right) => sortTimestamps(
|
|
fundingActivityTimestamp(right),
|
|
fundingActivityTimestamp(left),
|
|
))
|
|
.slice(0, 10)
|
|
.map((observation) => ({ ...observation })),
|
|
};
|
|
}
|
|
|
|
function buildIntentRequestSummary({ config, intentRequests = [], executorState = {} } = {}) {
|
|
const defaultPair = config.defaultTakerPair || null;
|
|
const strategyConfig = defaultPair?.strategyConfig || null;
|
|
const usingDbPairConfig = Boolean(config.tradingConfigLoaded && strategyConfig);
|
|
const sourceAsset = defaultPair?.assetIn || config.tradingEure;
|
|
const destinationAsset = defaultPair?.assetOut || config.tradingBtc;
|
|
const takerPairs = (config.pairs || []).filter((pair) => pair.takerEnabled);
|
|
return {
|
|
defaults: {
|
|
pair_id: defaultPair?.pairId || null,
|
|
pair_label: defaultPair
|
|
? `${sourceAsset?.symbol || defaultPair.asset_in} -> ${destinationAsset?.symbol || defaultPair.asset_out}`
|
|
: null,
|
|
source_symbol: sourceAsset?.symbol || 'Source',
|
|
destination_symbol: destinationAsset?.symbol || 'Destination',
|
|
source_amount: String(strategyConfig?.requestDefaultNotional ?? config.intentRequestDefaultAmountEure ?? 5),
|
|
max_source_amount: usingDbPairConfig
|
|
? strategyConfig.requestMaxNotional ?? null
|
|
: String(config.intentRequestMaxAmountEure || 5),
|
|
amount_eure: sourceAsset?.assetId === config.tradingEure?.assetId
|
|
? String(strategyConfig?.requestDefaultNotional ?? config.intentRequestDefaultAmountEure ?? 5)
|
|
: null,
|
|
max_amount_eure: sourceAsset?.assetId === config.tradingEure?.assetId
|
|
? (usingDbPairConfig ? strategyConfig.requestMaxNotional ?? null : String(config.intentRequestMaxAmountEure || 5))
|
|
: null,
|
|
slippage_bps: Number(strategyConfig?.slippageBps ?? config.intentRequestDefaultSlippageBps ?? 200),
|
|
max_slippage_bps: usingDbPairConfig
|
|
? strategyConfig.requestMaxSlippageBps ?? null
|
|
: Number(config.intentRequestMaxSlippageBps ?? 200),
|
|
},
|
|
pairs: takerPairs.map((pair) => ({
|
|
pair_id: pair.pairId,
|
|
pair: pair.key,
|
|
label: `${pair.assetIn?.symbol || pair.asset_in} -> ${pair.assetOut?.symbol || pair.asset_out}`,
|
|
mode: pair.mode,
|
|
can_trade: pair.canTrade,
|
|
block_reason: pair.blockReason || null,
|
|
source_symbol: pair.assetIn?.symbol || pair.asset_in,
|
|
destination_symbol: pair.assetOut?.symbol || pair.asset_out,
|
|
})),
|
|
executor_armed: executorState.armed ?? null,
|
|
executor_paused: executorState.paused ?? null,
|
|
request_creation_state: executorState.request_creation || null,
|
|
items: (intentRequests || []).map((request) => normalizeIntentRequestForUi({ config, request })),
|
|
caveat:
|
|
'Own request relay acceptance is not a completed trade. Completed requires durable source decrease and destination increase evidence.',
|
|
};
|
|
}
|
|
|
|
function normalizeIntentRequestForUi({ config, request }) {
|
|
const sourceAsset = config.assetRegistry.get(request.source_asset_id) || config.tradingEure;
|
|
const destinationAsset = config.assetRegistry.get(request.destination_asset_id) || config.tradingBtc;
|
|
return {
|
|
...request,
|
|
source_symbol: request.source_symbol || sourceAsset?.symbol || 'Source',
|
|
destination_symbol: request.destination_symbol || destinationAsset?.symbol || 'Destination',
|
|
source_amount: formatUnits(request.source_amount_units || '0', sourceAsset?.decimals || 0),
|
|
expected_destination_amount: formatUnits(
|
|
request.expected_destination_amount_units || '0',
|
|
destinationAsset?.decimals || 0,
|
|
),
|
|
min_destination_amount: formatUnits(
|
|
request.min_destination_amount_units || '0',
|
|
destinationAsset?.decimals || 0,
|
|
),
|
|
quoted_destination_amount: request.quoted_destination_amount_units == null
|
|
? null
|
|
: formatUnits(request.quoted_destination_amount_units || '0', destinationAsset?.decimals || 0),
|
|
settlement_summary: buildSettlementSummary({
|
|
config,
|
|
delta: request.attributed_inventory_delta,
|
|
attributionStatus: request.attribution_status,
|
|
attributionMethod: request.attribution_method,
|
|
subject: 'request',
|
|
}),
|
|
};
|
|
}
|
|
|
|
function buildRecentWithdrawals({ config, liquidityState }) {
|
|
return Object.values(liquidityState?.tracked_withdrawals || {})
|
|
.sort((left, right) => sortTimestamps(
|
|
right.last_checked_at || right.submitted_at || right.noted_at,
|
|
left.last_checked_at || left.submitted_at || left.noted_at,
|
|
))
|
|
.slice(0, 10)
|
|
.map((withdrawal) => {
|
|
const asset = config.assetRegistry.get(withdrawal.asset_id);
|
|
return {
|
|
withdrawal_hash: withdrawal.withdrawal_hash,
|
|
asset_id: withdrawal.asset_id,
|
|
symbol: asset?.symbol || withdrawal.asset_id,
|
|
asset_symbol: asset?.label || asset?.symbol || withdrawal.asset_id,
|
|
chain: withdrawal.chain || null,
|
|
amount_units: String(withdrawal.amount || '0'),
|
|
amount_display: formatUnits(withdrawal.amount || '0', asset?.decimals || 0),
|
|
amount: formatUnits(withdrawal.amount || '0', asset?.decimals || 0),
|
|
status: withdrawal.status || null,
|
|
address: withdrawal.address || null,
|
|
destination_address: withdrawal.address || null,
|
|
requested_at: withdrawal.submitted_at || withdrawal.noted_at || null,
|
|
completed_at: withdrawal.status === 'COMPLETED' ? withdrawal.last_checked_at : null,
|
|
submitted_at: withdrawal.submitted_at || null,
|
|
last_checked_at: withdrawal.last_checked_at || null,
|
|
noted_at: withdrawal.noted_at || null,
|
|
};
|
|
});
|
|
}
|
|
|
|
function buildRecentSubmissionTerms({ config, submissions }) {
|
|
return (submissions || []).slice(0, 10).map((submission) => {
|
|
const assetIn = config.assetRegistry.get(submission.asset_in);
|
|
const assetOut = config.assetRegistry.get(submission.asset_out);
|
|
return {
|
|
observed_at: submission.observed_at,
|
|
quote_id: submission.quote_id,
|
|
request_kind: submission.request_kind,
|
|
asset_in: submission.asset_in,
|
|
asset_in_symbol: assetIn?.symbol || submission.asset_in,
|
|
amount_in_units: submission.amount_in,
|
|
amount_in: formatUnits(submission.amount_in || '0', assetIn?.decimals || 0),
|
|
asset_out: submission.asset_out,
|
|
asset_out_symbol: assetOut?.symbol || submission.asset_out,
|
|
amount_out_units: submission.amount_out,
|
|
amount_out: formatUnits(submission.amount_out || '0', assetOut?.decimals || 0),
|
|
};
|
|
});
|
|
}
|
|
|
|
function normalizeSubmissionPage({ config, submissionPage }) {
|
|
return {
|
|
...submissionPage,
|
|
items: (submissionPage?.items || []).map((trade) => normalizeTradeForUi({
|
|
config,
|
|
trade,
|
|
})),
|
|
};
|
|
}
|
|
|
|
const LIFECYCLE_TONE_BY_STATE = {
|
|
observed: 'info',
|
|
evaluated: 'healthy',
|
|
command_emitted: 'info',
|
|
rejected: 'warning',
|
|
blocked: 'warning',
|
|
failed: 'critical',
|
|
submitted: 'healthy',
|
|
awaiting_outcome: 'info',
|
|
not_filled: 'warning',
|
|
completed: 'healthy',
|
|
};
|
|
|
|
const HUMAN_REASON_TEXT = {
|
|
actionable: 'Strategy approved the quote.',
|
|
below_edge_threshold: 'Below edge threshold.',
|
|
duplicate_quote_id: 'Duplicate quote id.',
|
|
executor_disarmed: 'Executor is disarmed.',
|
|
executor_paused: 'Executor intake is paused.',
|
|
inventory_unavailable: 'Inventory unavailable.',
|
|
min_notional_not_met: 'Below minimum notional.',
|
|
maker_quote_age_unavailable: 'Maker quote age is unavailable.',
|
|
maker_quote_response_policy_invalid: 'Maker response-age policy is invalid.',
|
|
maker_quote_too_old: 'Maker quote is too old for the configured response-age policy.',
|
|
pending_deposit_not_credited: 'Funding is not credited yet.',
|
|
pending_outbound_reserved: 'Pending outbound inventory is reserved.',
|
|
quote_expired: 'Quote expired.',
|
|
quote_not_found_or_finished: 'Relay reported the quote as not found or already finished.',
|
|
quote_response_ack: 'Quote response acknowledged by the relay.',
|
|
quote_response_ok: 'Quote response accepted by the relay.',
|
|
awaiting_outcome: 'No durable venue outcome is recorded yet.',
|
|
deadline_elapsed_without_settlement:
|
|
'No matching inventory delta was observed after the quote-response deadline.',
|
|
matched_inventory_delta:
|
|
'Matched to an observed inventory delta. Attribution is heuristic unless the source says linked settlement.',
|
|
ambiguous_inventory_delta_match: 'Inventory movement matched more than one quote candidate.',
|
|
reason_unknown: 'Reason not recorded.',
|
|
stale_reference_price: 'Reference price is stale.',
|
|
strategy_approved: 'Strategy approved the quote.',
|
|
strategy_disarmed: 'Strategy is disarmed.',
|
|
submission_failed: 'Submission failed.',
|
|
unsupported_pair: 'Unsupported pair.',
|
|
};
|
|
|
|
const COMPLETED_OUTCOME_STATUSES = new Set(['completed', 'filled', 'settled', 'finalized']);
|
|
const NOT_FILLED_OUTCOME_STATUSES = new Set(['expired', 'not_filled', 'cancelled']);
|
|
|
|
export function deriveQuoteLifecycleRows({
|
|
recentQuotes = [],
|
|
recentTradeDecisions = [],
|
|
recentExecuteTradeCommands = [],
|
|
recentExecutionResults = [],
|
|
recentQuoteOutcomes = [],
|
|
limit = 20,
|
|
} = {}) {
|
|
const rowsByKey = new Map();
|
|
|
|
for (const quote of recentQuotes || []) {
|
|
const normalizedQuote = normalizeLifecycleQuote(quote);
|
|
const row = ensureLifecycleRow(rowsByKey, normalizedQuote?.quote_id || `quote:${normalizedQuote?.observed_at || normalizedQuote?.ingested_at || rowsByKey.size}`);
|
|
mergeLifecycleEvidence(row, {
|
|
quote_id: normalizedQuote?.quote_id || null,
|
|
pair: normalizedQuote?.pair || null,
|
|
request_kind: normalizedQuote?.request_kind || null,
|
|
asset_in: normalizedQuote?.asset_in || null,
|
|
asset_out: normalizedQuote?.asset_out || null,
|
|
amount_in: normalizedQuote?.amount_in || null,
|
|
amount_out: normalizedQuote?.amount_out || null,
|
|
maker_timing: normalizedQuote?.maker_timing || null,
|
|
quote: normalizedQuote,
|
|
quote_observed_at: normalizedQuote?.observed_at || normalizedQuote?.ingested_at || null,
|
|
});
|
|
}
|
|
|
|
for (const decisionEntry of recentTradeDecisions || []) {
|
|
const decision = normalizeDecision({
|
|
...(decisionEntry?.payload || decisionEntry || {}),
|
|
decision_at:
|
|
decisionEntry?.payload?.decision_at
|
|
|| decisionEntry?.decision_at
|
|
|| decisionEntry?.observed_at
|
|
|| decisionEntry?.ingested_at
|
|
|| null,
|
|
});
|
|
if (!decision) continue;
|
|
const row = ensureLifecycleRow(rowsByKey, decision.quote_id || decision.decision_id || `decision:${decision.decision_at || rowsByKey.size}`);
|
|
mergeLifecycleEvidence(row, {
|
|
quote_id: decision.quote_id,
|
|
decision_id: decision.decision_id,
|
|
pair: decision.pair,
|
|
request_pair: decision.request_pair,
|
|
request_pair_id: decision.request_pair_id,
|
|
maker_trade_pair: decision.maker_trade_pair,
|
|
maker_trade_pair_id: decision.maker_trade_pair_id,
|
|
pair_id: decision.pair_id,
|
|
pair_config_id: decision.pair_config_id,
|
|
pair_config_version: decision.pair_config_version,
|
|
edge_bps: decision.edge_bps,
|
|
direction: decision.direction,
|
|
request_kind: decision.request_kind,
|
|
gross_edge_pct: decision.gross_edge_pct,
|
|
inventory_asset: decision.inventory_asset,
|
|
inventory_required: decision.inventory_required,
|
|
inventory_available: decision.inventory_available,
|
|
inventory_spendable: decision.inventory_spendable,
|
|
inventory_pending_outbound: decision.inventory_pending_outbound,
|
|
maker_send_asset_id: decision.maker_send_asset_id,
|
|
maker_receive_asset_id: decision.maker_receive_asset_id,
|
|
maker_send_amount_units: decision.maker_send_amount_units,
|
|
maker_receive_amount_units: decision.maker_receive_amount_units,
|
|
pending_inbound: decision.pending_inbound,
|
|
pending_outbound: decision.pending_outbound,
|
|
notional: decision.notional,
|
|
notional_asset_id: decision.notional_asset_id,
|
|
notional_symbol: decision.notional_symbol,
|
|
eure_notional: decision.eure_notional,
|
|
maker_timing: decision.maker_timing || null,
|
|
decision,
|
|
decision_at: decision.decision_at || null,
|
|
});
|
|
}
|
|
|
|
for (const commandEntry of recentExecuteTradeCommands || []) {
|
|
const command = normalizeCommand({
|
|
...(commandEntry?.payload || commandEntry || {}),
|
|
command_at:
|
|
commandEntry?.command_at
|
|
|| commandEntry?.observed_at
|
|
|| commandEntry?.ingested_at
|
|
|| null,
|
|
});
|
|
if (!command) continue;
|
|
const row = ensureLifecycleRow(rowsByKey, command.quote_id || command.decision_id || command.command_id || `command:${command.command_at || rowsByKey.size}`);
|
|
mergeLifecycleEvidence(row, {
|
|
quote_id: command.quote_id,
|
|
decision_id: command.decision_id,
|
|
command_id: command.command_id,
|
|
pair: command.pair,
|
|
request_pair: command.request_pair,
|
|
request_pair_id: command.request_pair_id,
|
|
maker_trade_pair: command.maker_trade_pair,
|
|
maker_trade_pair_id: command.maker_trade_pair_id,
|
|
pair_id: command.pair_id,
|
|
pair_config_id: command.pair_config_id,
|
|
pair_config_version: command.pair_config_version,
|
|
edge_bps: command.edge_bps,
|
|
direction: command.direction,
|
|
request_kind: command.request_kind,
|
|
asset_in: command.asset_in || null,
|
|
asset_out: command.asset_out || null,
|
|
amount_in: command.amount_in || null,
|
|
amount_out: command.amount_out || null,
|
|
maker_send_asset_id: command.maker_send_asset_id || null,
|
|
maker_receive_asset_id: command.maker_receive_asset_id || null,
|
|
maker_send_amount_units: command.maker_send_amount_units || null,
|
|
maker_receive_amount_units: command.maker_receive_amount_units || null,
|
|
notional: command.notional || null,
|
|
notional_asset_id: command.notional_asset_id || null,
|
|
notional_symbol: command.notional_symbol || null,
|
|
eure_notional: command.eure_notional || null,
|
|
maker_timing: command.maker_timing || null,
|
|
command,
|
|
command_at: command.command_at || null,
|
|
});
|
|
}
|
|
|
|
for (const execution of recentExecutionResults || []) {
|
|
const row = ensureLifecycleRow(rowsByKey, execution?.quote_id || execution?.decision_id || execution?.command_id || `execution:${execution?.result_at || rowsByKey.size}`);
|
|
mergeLifecycleEvidence(row, {
|
|
quote_id: execution?.quote_id || null,
|
|
decision_id: execution?.decision_id || null,
|
|
command_id: execution?.command_id || null,
|
|
pair: execution?.pair || null,
|
|
maker_timing: execution?.maker_timing || null,
|
|
execution,
|
|
execution_result_at: execution?.result_at || null,
|
|
});
|
|
}
|
|
|
|
for (const outcome of recentQuoteOutcomes || []) {
|
|
const row = ensureLifecycleRow(rowsByKey, outcome?.quote_id || outcome?.decision_id || outcome?.command_id || `outcome:${outcome?.outcome_observed_at || rowsByKey.size}`);
|
|
mergeLifecycleEvidence(row, {
|
|
quote_id: outcome?.quote_id || null,
|
|
decision_id: outcome?.decision_id || null,
|
|
command_id: outcome?.command_id || null,
|
|
pair: outcome?.pair || null,
|
|
direction: outcome?.direction || null,
|
|
request_kind: outcome?.request_kind || null,
|
|
edge_bps: outcome?.edge_bps || null,
|
|
gross_edge_pct: outcome?.gross_edge_pct || null,
|
|
notional: outcome?.notional || null,
|
|
notional_asset_id: outcome?.notional_asset_id || null,
|
|
notional_symbol: outcome?.notional_symbol || null,
|
|
eure_notional: outcome?.eure_notional || null,
|
|
maker_timing: outcome?.maker_timing || null,
|
|
outcome,
|
|
command_at: outcome?.command_at || null,
|
|
execution_result_at: outcome?.submitted_at || null,
|
|
outcome_observed_at: outcome?.outcome_observed_at || outcome?.submitted_at || null,
|
|
});
|
|
}
|
|
|
|
const finalized = [...rowsByKey.values()]
|
|
.map((row) => finalizeLifecycleRow(row))
|
|
.sort((left, right) => sortTimestamps(right.quote_activity_at, left.quote_activity_at));
|
|
|
|
return limit == null ? finalized : finalized.slice(0, limit);
|
|
}
|
|
|
|
function ensureLifecycleRow(rowsByKey, key) {
|
|
if (!rowsByKey.has(key)) {
|
|
rowsByKey.set(key, {
|
|
quote_id: null,
|
|
decision_id: null,
|
|
command_id: null,
|
|
pair: null,
|
|
request_pair: null,
|
|
request_pair_id: null,
|
|
maker_trade_pair: null,
|
|
maker_trade_pair_id: null,
|
|
pair_id: null,
|
|
pair_config_id: null,
|
|
pair_config_version: null,
|
|
edge_bps: null,
|
|
direction: null,
|
|
request_kind: null,
|
|
gross_edge_pct: null,
|
|
inventory_asset: null,
|
|
inventory_required: null,
|
|
inventory_available: null,
|
|
inventory_spendable: null,
|
|
inventory_pending_outbound: null,
|
|
pending_inbound: null,
|
|
pending_outbound: null,
|
|
notional: null,
|
|
notional_asset_id: null,
|
|
notional_symbol: null,
|
|
eure_notional: null,
|
|
quote_observed_at: null,
|
|
decision_at: null,
|
|
asset_in: null,
|
|
asset_out: null,
|
|
amount_in: null,
|
|
amount_out: null,
|
|
maker_send_asset_id: null,
|
|
maker_receive_asset_id: null,
|
|
maker_send_amount_units: null,
|
|
maker_receive_amount_units: null,
|
|
command_at: null,
|
|
execution_result_at: null,
|
|
outcome_observed_at: null,
|
|
maker_timing: null,
|
|
quote: null,
|
|
decision: null,
|
|
command: null,
|
|
execution: null,
|
|
outcome: null,
|
|
});
|
|
}
|
|
return rowsByKey.get(key);
|
|
}
|
|
|
|
function mergeLifecycleEvidence(row, next) {
|
|
for (const [key, value] of Object.entries(next || {})) {
|
|
if (value != null && row[key] == null) {
|
|
row[key] = value;
|
|
}
|
|
}
|
|
if (next?.quote) row.quote = next.quote;
|
|
if (next?.decision) row.decision = next.decision;
|
|
if (next?.command) row.command = next.command;
|
|
if (next?.execution) row.execution = next.execution;
|
|
if (next?.outcome) row.outcome = next.outcome;
|
|
}
|
|
|
|
function finalizeLifecycleRow(row) {
|
|
const decision = row.decision || null;
|
|
const execution = row.execution || null;
|
|
const outcome = row.outcome || null;
|
|
const outcomeStatus = normalizeLifecycleToken(
|
|
outcome?.outcome_status
|
|
|| execution?.outcome_status
|
|
|| execution?.outcome_reason
|
|
|| null,
|
|
);
|
|
let lifecycle_state = 'observed';
|
|
let lifecycle_label = 'Observed';
|
|
let reason_code = 'reason_unknown';
|
|
let reason_text = 'Strategy evaluation not recorded yet.';
|
|
|
|
if (outcomeStatus && COMPLETED_OUTCOME_STATUSES.has(outcomeStatus)) {
|
|
lifecycle_state = 'completed';
|
|
lifecycle_label = 'Completed';
|
|
reason_code = normalizeLifecycleToken(
|
|
outcome?.outcome_reason
|
|
|| execution?.outcome_reason
|
|
|| execution?.result_code
|
|
|| 'completed',
|
|
);
|
|
reason_text = buildCompletedOutcomeText({ outcome, reasonCode: reason_code });
|
|
} else if (outcomeStatus && NOT_FILLED_OUTCOME_STATUSES.has(outcomeStatus)) {
|
|
lifecycle_state = 'not_filled';
|
|
lifecycle_label = 'Not filled';
|
|
reason_code = normalizeLifecycleToken(
|
|
outcome?.outcome_reason
|
|
|| execution?.outcome_reason
|
|
|| execution?.result_code
|
|
|| outcomeStatus,
|
|
);
|
|
reason_text = buildNotFilledText({ outcome, reasonCode: reason_code });
|
|
} else if (outcomeStatus === 'awaiting_outcome') {
|
|
lifecycle_state = 'awaiting_outcome';
|
|
lifecycle_label = 'Awaiting outcome';
|
|
reason_code = normalizeLifecycleToken(outcome?.outcome_reason || 'awaiting_outcome');
|
|
reason_text = humanizeReasonCode(reason_code, 'No durable venue outcome is recorded yet.');
|
|
} else if (execution?.status === 'submitted') {
|
|
lifecycle_state = 'submitted';
|
|
lifecycle_label = 'Submitted';
|
|
reason_code = normalizeLifecycleToken(execution?.result_code || 'awaiting_outcome');
|
|
reason_text = `${humanizeReasonCode(reason_code, 'Submitted to the relay.')} No durable venue outcome is recorded yet.`;
|
|
} else if (execution?.status === 'failed') {
|
|
lifecycle_state = 'failed';
|
|
lifecycle_label = 'Submission failed';
|
|
reason_code = normalizeLifecycleToken(
|
|
execution?.failure_category
|
|
|| execution?.result_code
|
|
|| 'submission_failed',
|
|
);
|
|
reason_text = buildExecutionFailureText(execution, reason_code);
|
|
} else if (execution?.status === 'rejected') {
|
|
lifecycle_state = 'blocked';
|
|
lifecycle_label = 'Blocked before submit';
|
|
reason_code = normalizeLifecycleToken(execution?.result_code || 'reason_unknown');
|
|
reason_text = buildExecutorBlockText(execution, reason_code);
|
|
} else if (row.command_id || row.command_at) {
|
|
lifecycle_state = 'command_emitted';
|
|
lifecycle_label = 'Awaiting executor';
|
|
reason_code = 'awaiting_executor';
|
|
reason_text = 'Execute command recorded, but no executor result is stored yet.';
|
|
} else if (decision?.decision === 'blocked') {
|
|
lifecycle_state = 'blocked';
|
|
lifecycle_label = 'Policy skip';
|
|
reason_code = normalizeLifecycleToken(decision?.decision_reason || 'reason_unknown');
|
|
reason_text = buildPolicySkipText(decision, reason_code);
|
|
} else if (decision?.decision === 'rejected') {
|
|
lifecycle_state = 'rejected';
|
|
lifecycle_label = 'Rejected by strategy';
|
|
reason_code = normalizeLifecycleToken(decision?.decision_reason || 'reason_unknown');
|
|
reason_text = humanizeReasonCode(reason_code, 'Strategy rejected the quote.');
|
|
} else if (decision?.decision) {
|
|
lifecycle_state = 'evaluated';
|
|
lifecycle_label = 'Approved by strategy';
|
|
reason_code = normalizeLifecycleToken(decision?.decision_reason || 'strategy_approved');
|
|
reason_text = 'Strategy approved the quote, but no durable execute command is recorded yet.';
|
|
}
|
|
|
|
return {
|
|
...row,
|
|
lifecycle_state,
|
|
lifecycle_label,
|
|
lifecycle_tone: LIFECYCLE_TONE_BY_STATE[lifecycle_state] || 'unknown',
|
|
reason_code,
|
|
reason_text,
|
|
latest_stage_at:
|
|
row.outcome_observed_at
|
|
|| row.execution_result_at
|
|
|| row.command_at
|
|
|| row.decision_at
|
|
|| row.quote_observed_at
|
|
|| null,
|
|
quote_activity_at:
|
|
row.quote_observed_at
|
|
|| row.decision_at
|
|
|| row.command_at
|
|
|| row.execution_result_at
|
|
|| row.outcome_observed_at
|
|
|| null,
|
|
stage_details: {
|
|
quote_observed_at: row.quote_observed_at,
|
|
decision_at: row.decision_at,
|
|
command_at: row.command_at,
|
|
execution_result_at: row.execution_result_at,
|
|
strategy_decision: decision?.decision || null,
|
|
strategy_reason_code: decision?.decision_reason || null,
|
|
execution_status: execution?.status || null,
|
|
execution_result_code: execution?.result_code || null,
|
|
execution_outcome_status: execution?.outcome_status || null,
|
|
durable_outcome_status: outcome?.outcome_status || null,
|
|
durable_outcome_source: outcome?.outcome_source || null,
|
|
attribution_status: outcome?.attribution_status || null,
|
|
attribution_method: outcome?.attribution_method || null,
|
|
maker_timing: row.maker_timing || null,
|
|
},
|
|
outcome_source: outcome?.outcome_source || null,
|
|
outcome_status: outcome?.outcome_status || execution?.outcome_status || null,
|
|
attribution_status: outcome?.attribution_status || execution?.attribution_status || null,
|
|
attribution_method: outcome?.attribution_method || execution?.attribution_method || null,
|
|
attributed_inventory_delta:
|
|
outcome?.attributed_inventory_delta
|
|
|| execution?.attributed_inventory_delta
|
|
|| null,
|
|
has_settlement_evidence: hasSettlementEvidence(outcome || execution),
|
|
maker_timing: row.maker_timing || null,
|
|
};
|
|
}
|
|
|
|
export function buildQuoteLifecycleLookupResponse({
|
|
config,
|
|
identifier,
|
|
evidence = {},
|
|
now = new Date().toISOString(),
|
|
} = {}) {
|
|
const requestedIdentifier = String(identifier || '').trim();
|
|
const rows = deriveQuoteLifecycleRows({
|
|
recentQuotes: evidence.recentQuotes || [],
|
|
recentTradeDecisions: evidence.recentTradeDecisions || [],
|
|
recentExecuteTradeCommands: evidence.recentExecuteTradeCommands || [],
|
|
recentExecutionResults: evidence.recentExecutionResults || [],
|
|
recentQuoteOutcomes: evidence.recentQuoteOutcomes || [],
|
|
limit: null,
|
|
}).map((row) => enrichLifecycleRowForUi({ config, row }));
|
|
const lifecycleRow = selectLookupLifecycleRow(rows, requestedIdentifier);
|
|
const found = Boolean(lifecycleRow);
|
|
const lookup = {
|
|
requested_identifier: requestedIdentifier,
|
|
matched_identifier_type: found
|
|
? inferLookupIdentifierType(requestedIdentifier, lifecycleRow, evidence)
|
|
: null,
|
|
found,
|
|
lookup_at: evidence.lookup_at || now,
|
|
latest_stage_at: lifecycleRow?.latest_stage_at || null,
|
|
matched_identifiers: found
|
|
? collectLifecycleIdentifiers(lifecycleRow, evidence.matched_identifiers)
|
|
: normalizeMatchedIdentifiers(evidence.matched_identifiers),
|
|
};
|
|
|
|
return {
|
|
ok: found,
|
|
lifecycle_row: lifecycleRow || null,
|
|
lookup,
|
|
};
|
|
}
|
|
|
|
function selectLookupLifecycleRow(rows, identifier) {
|
|
if (!rows.length) return null;
|
|
const normalizedIdentifier = String(identifier || '').trim();
|
|
if (!normalizedIdentifier) return rows[0] || null;
|
|
return rows.find((row) => lifecycleRowHasIdentifier(row, normalizedIdentifier))
|
|
|| rows[0]
|
|
|| null;
|
|
}
|
|
|
|
function lifecycleRowHasIdentifier(row, identifier) {
|
|
return row?.quote_id === identifier
|
|
|| row?.decision_id === identifier
|
|
|| row?.command_id === identifier;
|
|
}
|
|
|
|
function inferLookupIdentifierType(identifier, row, evidence = {}) {
|
|
if (evidence.matched_identifier_type) return evidence.matched_identifier_type;
|
|
if (row?.quote_id === identifier) return 'quote_id';
|
|
if (row?.decision_id === identifier) return 'decision_id';
|
|
if (row?.command_id === identifier) return 'command_id';
|
|
return 'linked_identifier';
|
|
}
|
|
|
|
function collectLifecycleIdentifiers(row, matchedIdentifiers = null) {
|
|
const normalized = normalizeMatchedIdentifiers(matchedIdentifiers);
|
|
return {
|
|
quote_ids: uniqueStrings([
|
|
...normalized.quote_ids,
|
|
row?.quote_id,
|
|
]),
|
|
decision_ids: uniqueStrings([
|
|
...normalized.decision_ids,
|
|
row?.decision_id,
|
|
]),
|
|
command_ids: uniqueStrings([
|
|
...normalized.command_ids,
|
|
row?.command_id,
|
|
]),
|
|
};
|
|
}
|
|
|
|
function normalizeMatchedIdentifiers(value = null) {
|
|
return {
|
|
quote_ids: uniqueStrings(value?.quote_ids),
|
|
decision_ids: uniqueStrings(value?.decision_ids),
|
|
command_ids: uniqueStrings(value?.command_ids),
|
|
};
|
|
}
|
|
|
|
function uniqueStrings(values = []) {
|
|
return [...new Set(
|
|
(values || [])
|
|
.map((value) => String(value || '').trim())
|
|
.filter(Boolean),
|
|
)];
|
|
}
|
|
|
|
function buildPolicySkipText(decision, reasonCode) {
|
|
const base = humanizeReasonCode(reasonCode, 'Policy skipped the response.');
|
|
const policy = decision?.response_policy || {};
|
|
const measured = policy.measured_quote_age_ms == null ? null : `${policy.measured_quote_age_ms}ms`;
|
|
const max = policy.max_quote_age_ms == null ? null : `${policy.max_quote_age_ms}ms`;
|
|
if (measured && max) return `${base} Quote age ${measured} exceeded configured max ${max}.`;
|
|
if (measured) return `${base} Quote age ${measured}.`;
|
|
return base;
|
|
}
|
|
|
|
function buildCompletedOutcomeText({ outcome, reasonCode }) {
|
|
const base = humanizeReasonCode(reasonCode, 'Completed.');
|
|
if (!outcome?.attribution_status) return `${base} Settlement attribution is not stored.`;
|
|
if (outcome.attribution_status === 'heuristic_match') {
|
|
return `${base} Matched to inventory movement by exact asset-unit delta; venue terminal status is not stored.`;
|
|
}
|
|
return base;
|
|
}
|
|
|
|
function buildNotFilledText({ outcome, reasonCode }) {
|
|
const base = humanizeReasonCode(reasonCode, 'Not filled.');
|
|
const uncertainty = outcome?.evidence?.uncertainty;
|
|
return uncertainty ? `${base} ${uncertainty}` : base;
|
|
}
|
|
|
|
function hasSettlementEvidence(outcome) {
|
|
return Boolean(
|
|
outcome?.attributed_inventory_delta
|
|
&& TERMINAL_SETTLEMENT_ATTRIBUTION_STATUSES.has(outcome.attribution_status),
|
|
);
|
|
}
|
|
|
|
function buildExecutionFailureText(execution, reasonCode) {
|
|
const base = humanizeReasonCode(reasonCode, 'Submission failed.');
|
|
if (execution?.error_message) return `${base} ${execution.error_message}`;
|
|
if (execution?.note) return `${base} ${execution.note}`;
|
|
return base;
|
|
}
|
|
|
|
function buildExecutorBlockText(execution, reasonCode) {
|
|
if (execution?.note) return execution.note;
|
|
return humanizeReasonCode(reasonCode, 'Executor blocked the submission.');
|
|
}
|
|
|
|
function humanizeReasonCode(code, fallback = 'Reason not recorded.') {
|
|
const normalized = normalizeLifecycleToken(code || '');
|
|
if (!normalized) return fallback;
|
|
return HUMAN_REASON_TEXT[normalized] || normalized.replaceAll('_', ' ');
|
|
}
|
|
|
|
function normalizeLifecycleToken(value) {
|
|
return String(value || '')
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/[\s-]+/g, '_');
|
|
}
|
|
|
|
function normalizeLifecycleQuote(quote) {
|
|
if (!quote) return null;
|
|
return {
|
|
quote_id: quote.quote_id || null,
|
|
pair: quote.pair || null,
|
|
asset_in: quote.asset_in || null,
|
|
asset_out: quote.asset_out || null,
|
|
request_kind: quote.request_kind || null,
|
|
amount_in: quote.amount_in ?? null,
|
|
amount_out: quote.amount_out ?? null,
|
|
min_deadline_ms: quote.min_deadline_ms ?? null,
|
|
maker_timing: quote.maker_timing || null,
|
|
observed_at: quote.observed_at || null,
|
|
ingested_at: quote.ingested_at || null,
|
|
};
|
|
}
|
|
|
|
function normalizeCommand(command) {
|
|
if (!command) return null;
|
|
return {
|
|
command_id: command.command_id || null,
|
|
decision_id: command.decision_id || null,
|
|
execution_key: command.execution_key || null,
|
|
quote_id: command.quote_id || null,
|
|
pair: command.pair || null,
|
|
request_pair: command.request_pair || null,
|
|
request_pair_id: command.request_pair_id || null,
|
|
maker_trade_pair: command.maker_trade_pair || null,
|
|
maker_trade_pair_id: command.maker_trade_pair_id || null,
|
|
pair_id: command.pair_id || null,
|
|
pair_config_id: command.pair_config_id || null,
|
|
pair_config_version: command.pair_config_version || null,
|
|
edge_bps: command.edge_bps || null,
|
|
direction: command.direction || null,
|
|
request_kind: command.request_kind || null,
|
|
notional: command.notional || null,
|
|
notional_asset_id: command.notional_asset_id || null,
|
|
notional_symbol: command.notional_symbol || null,
|
|
eure_notional: command.eure_notional || null,
|
|
asset_in: command.asset_in || null,
|
|
asset_out: command.asset_out || null,
|
|
amount_in: command.amount_in ?? null,
|
|
amount_out: command.amount_out ?? null,
|
|
maker_send_asset_id: command.maker_send_asset_id || null,
|
|
maker_receive_asset_id: command.maker_receive_asset_id || null,
|
|
maker_send_symbol: command.maker_send_symbol || null,
|
|
maker_receive_symbol: command.maker_receive_symbol || null,
|
|
maker_send_amount_units: command.maker_send_amount_units || null,
|
|
maker_receive_amount_units: command.maker_receive_amount_units || null,
|
|
maker_timing: command.maker_timing || null,
|
|
quote_age_at_command_ms: command.quote_age_at_command_ms ?? null,
|
|
command_at: command.command_at || command.observed_at || command.ingested_at || null,
|
|
};
|
|
}
|
|
|
|
function buildStrategySummary({
|
|
config,
|
|
servicesByName,
|
|
activeAlerts,
|
|
assetCatalog = null,
|
|
pairConfig = null,
|
|
fundingHandles = [],
|
|
recentQuotes = [],
|
|
recentTradeDecisions = [],
|
|
recentExecuteTradeCommands = [],
|
|
recentExecutionResults = [],
|
|
recentQuoteOutcomes = [],
|
|
successfulQuoteLifecycle = null,
|
|
quoteLifecycleRollups = [],
|
|
quoteLifecycleStatistics = null,
|
|
}) {
|
|
const strategyState = servicesByName['strategy-engine']?.state || {};
|
|
const executorState = servicesByName['trade-executor']?.state || {};
|
|
const durableDecisionsById = new Map(
|
|
(recentTradeDecisions || [])
|
|
.map((entry) => normalizeDecision({
|
|
...(entry.payload || {}),
|
|
decision_at:
|
|
entry?.payload?.decision_at
|
|
|| entry?.observed_at
|
|
|| entry?.ingested_at
|
|
|| null,
|
|
}))
|
|
.filter((entry) => entry?.decision_id)
|
|
.map((entry) => [entry.decision_id, entry]),
|
|
);
|
|
const recentDecisions = (strategyState.recent_decisions || []).slice(0, 20).map((decision) => {
|
|
const durableDecision = durableDecisionsById.get(decision?.decision_id) || null;
|
|
return normalizeDecision({
|
|
...(durableDecision || {}),
|
|
...(decision || {}),
|
|
decision_at:
|
|
decision?.decision_at
|
|
|| durableDecision?.decision_at
|
|
|| null,
|
|
});
|
|
});
|
|
const latestDecision = normalizeDecision({
|
|
...(durableDecisionsById.get(strategyState.latest_decision?.decision_id) || {}),
|
|
...(strategyState.latest_decision || {}),
|
|
decision_at:
|
|
strategyState.latest_decision?.decision_at
|
|
|| durableDecisionsById.get(strategyState.latest_decision?.decision_id)?.decision_at
|
|
|| null,
|
|
});
|
|
const lifecycleEvidence = mergeSuccessfulQuoteLifecycleEvidence({
|
|
recentQuotes,
|
|
recentTradeDecisions,
|
|
recentExecuteTradeCommands,
|
|
recentExecutionResults,
|
|
recentQuoteOutcomes,
|
|
successfulQuoteLifecycle,
|
|
});
|
|
const allLifecycleRows = deriveQuoteLifecycleRows({
|
|
recentQuotes: lifecycleEvidence.recentQuotes,
|
|
recentTradeDecisions: lifecycleEvidence.recentTradeDecisions,
|
|
recentExecuteTradeCommands: lifecycleEvidence.recentExecuteTradeCommands,
|
|
recentExecutionResults: lifecycleEvidence.recentExecutionResults,
|
|
recentQuoteOutcomes: lifecycleEvidence.recentQuoteOutcomes,
|
|
limit: null,
|
|
}).map((row) => enrichLifecycleRowForUi({ config, row }));
|
|
const lifecycleRows = allLifecycleRows.slice(0, 20);
|
|
const tradeFunnel = buildTradeFunnelSummary(allLifecycleRows);
|
|
const makerCompetitiveness = buildMakerCompetitivenessSummary({
|
|
lifecycleRows: allLifecycleRows,
|
|
});
|
|
makerCompetitiveness.persisted_rollups = quoteLifecycleRollups || [];
|
|
makerCompetitiveness.persisted_rollup_count = quoteLifecycleRollups?.length || 0;
|
|
|
|
return {
|
|
strategy_state: {
|
|
armed: strategyState.armed ?? null,
|
|
paused: strategyState.paused ?? null,
|
|
threshold_pct:
|
|
strategyState.threshold_pct
|
|
?? firstActivePairConfig(pairConfig)?.edge_pct
|
|
?? null,
|
|
max_notional_eure:
|
|
strategyState.max_notional_eure
|
|
?? firstActivePairConfig(pairConfig)?.max_notional
|
|
?? null,
|
|
latest_decision: latestDecision?.decision_id ? latestDecision : null,
|
|
recent_decisions: recentDecisions.length
|
|
? recentDecisions
|
|
: [...durableDecisionsById.values()].slice(0, 20),
|
|
recent_lifecycle_rows: lifecycleRows,
|
|
quote_lifecycle_statistics: quoteLifecycleStatistics || {
|
|
persisted: {
|
|
source: 'persisted',
|
|
generated_at: null,
|
|
rows: [],
|
|
},
|
|
live: {
|
|
source: 'live_recent',
|
|
generated_at: null,
|
|
rows: [],
|
|
},
|
|
},
|
|
trade_funnel: tradeFunnel,
|
|
maker_competitiveness: makerCompetitiveness,
|
|
skipped_counts: strategyState.skipped_counts || {},
|
|
durable_control_state: strategyState.durable_control_state || null,
|
|
trading_config: strategyState.trading_config || null,
|
|
},
|
|
asset_catalog: attachDepositHandlesToAssetCatalog(
|
|
assetCatalog || buildFallbackAssetCatalog(config),
|
|
fundingHandles,
|
|
),
|
|
pair_config: pairConfig || buildFallbackPairConfig(config),
|
|
executor_state: {
|
|
armed: executorState.armed ?? null,
|
|
paused: executorState.paused ?? null,
|
|
draining: executorState.draining ?? null,
|
|
in_flight_count: executorState.in_flight_count ?? 0,
|
|
submitted_count: executorState.submitted_count ?? executorState.completed_count ?? 0,
|
|
last_command: executorState.last_command || null,
|
|
last_venue_response: executorState.last_venue_response || null,
|
|
last_error: executorState.last_error || null,
|
|
signer_registered: executorState.signer_registered ?? null,
|
|
account_id: executorState.account_id || null,
|
|
signer_public_key: executorState.signer_public_key || null,
|
|
},
|
|
relevant_alerts: [],
|
|
omitted_controls: [
|
|
'Strategy arm and disarm are intentionally absent in this turn.',
|
|
'Executor drain remains intentionally absent in this turn.',
|
|
'Live withdrawal submission is intentionally absent in this turn.',
|
|
],
|
|
};
|
|
}
|
|
|
|
function attachDepositHandlesToAssetCatalog(assetCatalog, fundingHandles = []) {
|
|
const handlesByAssetId = new Map();
|
|
const handlesByChain = new Map();
|
|
for (const handle of fundingHandles || []) {
|
|
if (!handle) continue;
|
|
if (handle.chain) handlesByChain.set(handle.chain, handle);
|
|
if (handle.asset_id) handlesByAssetId.set(handle.asset_id, handle);
|
|
for (const assetId of handle.asset_ids || []) {
|
|
handlesByAssetId.set(assetId, handle);
|
|
}
|
|
}
|
|
|
|
return {
|
|
...(assetCatalog || {}),
|
|
items: (assetCatalog?.items || []).map((asset) => {
|
|
const assetId = asset.asset_id || asset.assetId;
|
|
const chain = asset.chain || asset.blockchain || null;
|
|
const handle = handlesByAssetId.get(assetId) || handlesByChain.get(chain) || null;
|
|
return {
|
|
...asset,
|
|
depositAddress: handle?.address || null,
|
|
deposit_address: handle?.address || null,
|
|
depositChain: handle?.chain || chain,
|
|
deposit_chain: handle?.chain || chain,
|
|
depositMemo: handle?.memo || null,
|
|
deposit_memo: handle?.memo || null,
|
|
depositRefreshedAt: handle?.refreshed_at || null,
|
|
deposit_refreshed_at: handle?.refreshed_at || null,
|
|
};
|
|
}),
|
|
};
|
|
}
|
|
|
|
function buildTradeFunnelSummary(lifecycleRows = []) {
|
|
const counts = {
|
|
observed: 0,
|
|
rejected: 0,
|
|
blocked: 0,
|
|
submitted: 0,
|
|
awaiting_outcome: 0,
|
|
failed: 0,
|
|
not_filled: 0,
|
|
completed: 0,
|
|
};
|
|
|
|
const successfulTrades = [];
|
|
const unresolvedSubmissions = [];
|
|
const noTradeRows = [];
|
|
|
|
for (const row of lifecycleRows || []) {
|
|
if (Object.hasOwn(counts, row.lifecycle_state)) {
|
|
counts[row.lifecycle_state] += 1;
|
|
}
|
|
|
|
if (row.lifecycle_state === 'completed' && row.has_settlement_evidence) {
|
|
successfulTrades.push(row);
|
|
} else if (['submitted', 'awaiting_outcome'].includes(row.lifecycle_state)) {
|
|
unresolvedSubmissions.push(row);
|
|
noTradeRows.push(row);
|
|
} else if (['observed', 'evaluated', 'command_emitted', 'rejected', 'blocked', 'failed', 'not_filled'].includes(row.lifecycle_state)) {
|
|
noTradeRows.push(row);
|
|
}
|
|
}
|
|
|
|
const grossEdgeEstimate = summarizeGrossEdgeEstimate(successfulTrades);
|
|
|
|
return {
|
|
successful_trade_count: successfulTrades.length,
|
|
successful_trade_gross_edge_estimate_eure: grossEdgeEstimate.total_eure,
|
|
successful_trade_gross_edge_estimate_count: grossEdgeEstimate.count,
|
|
unresolved_submission_count: unresolvedSubmissions.length,
|
|
no_trade_count: noTradeRows.length,
|
|
successful_trades: successfulTrades,
|
|
unresolved_submissions: unresolvedSubmissions.slice(0, 20),
|
|
no_trade_rows: [],
|
|
counts,
|
|
caveat:
|
|
successfulTrades.length > 0
|
|
? 'Successful trades require durable terminal outcome evidence.'
|
|
: 'No quote currently has linked terminal outcome and settled inventory evidence, so there are no successful trades to show yet.',
|
|
};
|
|
}
|
|
|
|
function firstActivePairConfig(pairConfig) {
|
|
const pair = pairConfig?.pairs?.find((entry) => entry.strategyConfig || entry.strategy_config);
|
|
const strategyConfig = pair?.strategyConfig || pair?.strategy_config || null;
|
|
if (!strategyConfig) return null;
|
|
return {
|
|
edge_pct: strategyConfig.edge_bps == null ? null : String(Number(strategyConfig.edge_bps) / 100),
|
|
max_notional: strategyConfig.max_notional == null ? null : String(strategyConfig.max_notional),
|
|
};
|
|
}
|
|
|
|
function buildFallbackAssetCatalog(config) {
|
|
const items = [...(config.assetRegistry?.values?.() || [])].map((asset) => ({
|
|
asset_id: asset.assetId,
|
|
symbol: asset.symbol,
|
|
label: asset.label || asset.symbol,
|
|
decimals: asset.decimals,
|
|
blockchain: asset.blockchain || asset.chain || null,
|
|
chain: asset.chain || asset.blockchain || null,
|
|
supported: asset.supported ?? null,
|
|
retired_at: asset.retiredAt || null,
|
|
enabled_for_inventory: asset.enabledForInventory ?? true,
|
|
}));
|
|
return {
|
|
latest_import: null,
|
|
counts: {
|
|
known: items.length,
|
|
supported: items.filter((asset) => asset.supported === true).length,
|
|
retired: items.filter((asset) => asset.retired_at).length,
|
|
inventory_enabled: items.filter((asset) => asset.enabled_for_inventory !== false).length,
|
|
},
|
|
items,
|
|
};
|
|
}
|
|
|
|
function buildFallbackPairConfig(config) {
|
|
return {
|
|
ok: false,
|
|
block_reason: 'pair_config_unavailable',
|
|
loaded_at: null,
|
|
pairs: config.activePair ? [{
|
|
pair_id: config.activePair,
|
|
key: config.activePair,
|
|
mode: 'legacy',
|
|
status: 'legacy',
|
|
enabled: false,
|
|
canTrade: false,
|
|
blockReason: 'pair_config_unavailable',
|
|
}] : [],
|
|
};
|
|
}
|
|
|
|
function mergeSuccessfulQuoteLifecycleEvidence({
|
|
recentQuotes = [],
|
|
recentTradeDecisions = [],
|
|
recentExecuteTradeCommands = [],
|
|
recentExecutionResults = [],
|
|
recentQuoteOutcomes = [],
|
|
successfulQuoteLifecycle = null,
|
|
} = {}) {
|
|
if (!successfulQuoteLifecycle?.found) {
|
|
return {
|
|
recentQuotes,
|
|
recentTradeDecisions,
|
|
recentExecuteTradeCommands,
|
|
recentExecutionResults,
|
|
recentQuoteOutcomes,
|
|
};
|
|
}
|
|
|
|
return {
|
|
recentQuotes: mergeLifecycleEvidenceRows(
|
|
recentQuotes,
|
|
successfulQuoteLifecycle.recentQuotes,
|
|
(row) => row?.quote_id,
|
|
),
|
|
recentTradeDecisions: mergeLifecycleEvidenceRows(
|
|
recentTradeDecisions,
|
|
successfulQuoteLifecycle.recentTradeDecisions,
|
|
(row) => row?.payload?.decision_id || row?.payload?.quote_id || row?.quote_id,
|
|
),
|
|
recentExecuteTradeCommands: mergeLifecycleEvidenceRows(
|
|
recentExecuteTradeCommands,
|
|
successfulQuoteLifecycle.recentExecuteTradeCommands,
|
|
(row) => row?.command_id || row?.decision_id || row?.quote_id,
|
|
),
|
|
recentExecutionResults: mergeLifecycleEvidenceRows(
|
|
recentExecutionResults,
|
|
successfulQuoteLifecycle.recentExecutionResults,
|
|
(row) => row?.command_id || `${row?.quote_id || 'quote'}:${row?.result_at || row?.observed_at || ''}`,
|
|
),
|
|
recentQuoteOutcomes: mergeLifecycleEvidenceRows(
|
|
recentQuoteOutcomes,
|
|
successfulQuoteLifecycle.recentQuoteOutcomes,
|
|
(row) => row?.quote_id || row?.command_id || row?.decision_id,
|
|
),
|
|
};
|
|
}
|
|
|
|
function mergeLifecycleEvidenceRows(primary = [], additional = [], keyFn = null) {
|
|
const merged = [];
|
|
const seen = new Set();
|
|
for (const row of [...(primary || []), ...(additional || [])]) {
|
|
if (!row) continue;
|
|
const key = keyFn?.(row) || JSON.stringify(row);
|
|
if (key && seen.has(key)) continue;
|
|
if (key) seen.add(key);
|
|
merged.push(row);
|
|
}
|
|
return merged;
|
|
}
|
|
|
|
function summarizeGrossEdgeEstimate(rows = []) {
|
|
let total = 0n;
|
|
let count = 0;
|
|
|
|
for (const row of rows || []) {
|
|
const value = row?.gross_edge_value_eure || estimateGrossEdgeValueEure(row);
|
|
if (value == null) continue;
|
|
total += parseScaledDecimal(value);
|
|
count += 1;
|
|
}
|
|
|
|
return {
|
|
count,
|
|
total_eure: count > 0 ? formatScaledDecimal(total) : null,
|
|
};
|
|
}
|
|
|
|
function buildEnvironmentStatusHistory({ recentEnvironmentStatuses = [], currentStatus = null } = {}) {
|
|
const recentChanges = (recentEnvironmentStatuses || []).map((entry) => ({
|
|
observed_at: entry.observed_at || entry.payload?.observed_at || null,
|
|
ingested_at: entry.ingested_at || null,
|
|
payload: entry.payload || entry,
|
|
}));
|
|
const latestDurable = recentChanges[0]?.payload || null;
|
|
const current = currentStatus || latestDurable;
|
|
|
|
return {
|
|
current,
|
|
recent_changes: recentChanges,
|
|
latest_durable_change_at:
|
|
recentChanges[0]?.payload?.changed_at
|
|
|| recentChanges[0]?.observed_at
|
|
|| recentChanges[0]?.ingested_at
|
|
|| null,
|
|
change_count: recentChanges.length,
|
|
};
|
|
}
|
|
|
|
function buildSystemSummary({
|
|
servicesByName,
|
|
activeAlerts,
|
|
recentAlerts,
|
|
nearIntentsStatus = null,
|
|
environmentStatus = { current: null, recent_changes: [] },
|
|
quoteLifecycleRetention = null,
|
|
quoteLifecycleRollups = [],
|
|
}) {
|
|
const historyWriterState = servicesByName['history-writer']?.state || {};
|
|
void activeAlerts;
|
|
void recentAlerts;
|
|
|
|
return {
|
|
service_health: Object.values(servicesByName).map((snapshot) => (
|
|
summarizeServiceSnapshot(snapshot, {
|
|
authoritativeHealth: null,
|
|
activeAlerts: [],
|
|
nearIntentsStatus,
|
|
})
|
|
)),
|
|
alerts: {
|
|
active: [],
|
|
recent: [],
|
|
},
|
|
environment_status: environmentStatus,
|
|
persistence: {
|
|
database_connectivity: historyWriterState.database_connectivity ?? null,
|
|
last_write_at: historyWriterState.last_write_at || null,
|
|
last_alert_write_at: historyWriterState.last_alert_write_at || null,
|
|
last_funding_observation_write_at: historyWriterState.last_funding_observation_write_at || null,
|
|
last_environment_status_write_at: historyWriterState.last_environment_status_write_at || null,
|
|
last_environment_status_seen_at: historyWriterState.last_environment_status_seen_at || null,
|
|
last_environment_status_duplicate_at: historyWriterState.last_environment_status_duplicate_at || null,
|
|
last_metrics_at: historyWriterState.last_metrics_at || null,
|
|
last_quote_outcomes_at: historyWriterState.last_quote_outcomes_at || null,
|
|
latest_portfolio_metrics: historyWriterState.latest_portfolio_metrics || null,
|
|
latest_quote_outcomes: historyWriterState.latest_quote_outcomes || null,
|
|
offsets: historyWriterState.offsets || {},
|
|
metrics_error: historyWriterState.metrics_error || null,
|
|
quote_outcomes_error: historyWriterState.quote_outcomes_error || null,
|
|
retention_mode:
|
|
quoteLifecycleRetention?.retention_mode
|
|
|| historyWriterState.retention_mode
|
|
|| null,
|
|
retention_summary:
|
|
quoteLifecycleRetention
|
|
|| historyWriterState.retention_summary
|
|
|| null,
|
|
quote_lifecycle_rollups: quoteLifecycleRollups || [],
|
|
quote_lifecycle_statistics:
|
|
historyWriterState.latest_quote_lifecycle_statistics
|
|
|| null,
|
|
last_quote_lifecycle_statistics_at:
|
|
historyWriterState.last_quote_lifecycle_statistics_at
|
|
|| null,
|
|
quote_lifecycle_statistics_count:
|
|
historyWriterState.quote_lifecycle_statistics_count
|
|
?? null,
|
|
quote_lifecycle_statistics_error:
|
|
historyWriterState.quote_lifecycle_statistics_error
|
|
|| null,
|
|
last_retention_maintenance_at:
|
|
quoteLifecycleRetention?.latest_run?.completed_at
|
|
|| historyWriterState.last_retention_maintenance_at
|
|
|| null,
|
|
last_quote_lifecycle_rollup_at:
|
|
quoteLifecycleRetention?.rollups?.latest_rollup_at
|
|
|| historyWriterState.last_quote_lifecycle_rollup_at
|
|
|| null,
|
|
last_quote_lifecycle_prune_at:
|
|
quoteLifecycleRetention?.latest_run?.completed_at
|
|
|| historyWriterState.last_quote_lifecycle_prune_at
|
|
|| null,
|
|
quote_lifecycle_prune_deleted_count:
|
|
historyWriterState.quote_lifecycle_prune_deleted_count ?? null,
|
|
raw_quote_prune_deleted_count:
|
|
historyWriterState.raw_quote_prune_deleted_count ?? null,
|
|
preserved_successful_quote_count:
|
|
quoteLifecycleRetention?.preserved_successful_quote_count
|
|
?? historyWriterState.retention_summary?.preserved_successful_quote_count
|
|
?? null,
|
|
},
|
|
controls: listDashboardControls({ page: 'system' }),
|
|
};
|
|
}
|
|
|
|
function summarizeServiceSnapshot(snapshot, { authoritativeHealth = null, activeAlerts = [], nearIntentsStatus = null } = {}) {
|
|
const state = snapshot.state || {};
|
|
const health = snapshot.health || {};
|
|
void authoritativeHealth;
|
|
void activeAlerts;
|
|
const freshnessAt = inferServiceFreshnessTimestamp(snapshot.service, state, health);
|
|
const reachable = snapshot.reachable !== false;
|
|
const online = reachable && health.ok !== false;
|
|
const upstreamStatus = resolveServiceUpstreamStatus(snapshot.service, nearIntentsStatus);
|
|
const upstreamDisrupted = upstreamStatus?.status === 'disrupted';
|
|
const healthStatus = upstreamDisrupted
|
|
? 'upstream_paused'
|
|
: online
|
|
? 'online'
|
|
: reachable
|
|
? 'reachable'
|
|
: 'offline';
|
|
const healthLabel = upstreamDisrupted ? 'upstream paused' : healthStatus;
|
|
const healthReasons = upstreamDisrupted && upstreamStatus.decisive_reason
|
|
? [upstreamStatus.decisive_reason]
|
|
: [];
|
|
|
|
return {
|
|
service: snapshot.service,
|
|
label: snapshot.label,
|
|
base_url: snapshot.base_url,
|
|
reachable,
|
|
health_ok: healthStatus === 'online',
|
|
health_status: healthStatus,
|
|
health_label: healthLabel,
|
|
health_reasons: healthReasons,
|
|
upstream_status: upstreamStatus,
|
|
highest_alert_severity: null,
|
|
paused: state.paused ?? health.paused ?? null,
|
|
armed: state.armed ?? null,
|
|
draining: state.draining ?? null,
|
|
freshness_at: freshnessAt,
|
|
freshness_age_ms: ageMs(freshnessAt),
|
|
last_error: state.last_error || health.last_error || null,
|
|
summary: buildServiceSummary(snapshot.service, state),
|
|
};
|
|
}
|
|
|
|
const NEAR_INTENTS_RELAY_SERVICES = new Set(['near-intents-ingest', 'trade-executor']);
|
|
|
|
function resolveServiceUpstreamStatus(service, nearIntentsStatus) {
|
|
if (!NEAR_INTENTS_RELAY_SERVICES.has(service) || !nearIntentsStatus) return null;
|
|
return {
|
|
source: nearIntentsStatus.source || 'near_intents_status_page',
|
|
status: nearIntentsStatus.status || 'unknown',
|
|
label: nearIntentsStatus.label || nearIntentsStatus.status || 'unknown',
|
|
observed_at: nearIntentsStatus.observed_at || null,
|
|
decisive_reason: nearIntentsStatus.decisive_reason || null,
|
|
current_incident_count: nearIntentsStatus.current_incident_count || 0,
|
|
affected_services: nearIntentsStatus.affected_services || [],
|
|
observed_incident_count: nearIntentsStatus.observed_incident_count ?? null,
|
|
observed_affected_services: nearIntentsStatus.observed_affected_services || [],
|
|
unrelated_incident_count: nearIntentsStatus.unrelated_incident_count || 0,
|
|
quoting_stopped: nearIntentsStatus.quoting_stopped ?? null,
|
|
};
|
|
}
|
|
|
|
function buildServiceSummary(service, state) {
|
|
switch (service) {
|
|
case 'near-intents-ingest':
|
|
return {
|
|
connected: state.ingest?.connected ?? null,
|
|
reconnect_count: state.ingest?.reconnect_count ?? null,
|
|
active_pair: state.trading_config?.active_pair || null,
|
|
enabled_pair_count: state.trading_config?.enabled_pair_count ?? null,
|
|
last_message_at: state.ingest?.last_message_at || null,
|
|
last_matching_quote_at: state.ingest?.last_matching_quote_at || null,
|
|
last_published_at: state.ingest?.last_published_at || null,
|
|
};
|
|
case 'market-reference-ingest':
|
|
return {
|
|
last_published_at: state.last_published_at || null,
|
|
source_used: state.kraken?.healthy ? 'kraken' : state.coingecko?.healthy ? 'coingecko' : null,
|
|
};
|
|
case 'inventory-sync':
|
|
return {
|
|
last_sync_at: state.last_sync_at || null,
|
|
reconciliation_status: state.last_snapshot?.reconciliation_status || null,
|
|
};
|
|
case 'liquidity-manager':
|
|
return {
|
|
last_refresh_at: state.last_refresh_at || null,
|
|
funding_observer_paused: state.funding_observer_paused ?? null,
|
|
withdrawals_frozen: state.withdrawals_frozen ?? null,
|
|
};
|
|
case 'history-writer':
|
|
return {
|
|
last_write_at: state.last_write_at || null,
|
|
last_alert_write_at: state.last_alert_write_at || null,
|
|
last_quote_outcomes_at: state.last_quote_outcomes_at || null,
|
|
database_connectivity: state.database_connectivity ?? null,
|
|
retention_mode: state.retention_mode || null,
|
|
last_retention_maintenance_at: state.last_retention_maintenance_at || null,
|
|
};
|
|
case 'ops-sentinel':
|
|
return {
|
|
last_event_at: state.last_event_at || null,
|
|
last_runtime_eval_at: state.last_runtime_eval_at || null,
|
|
active_alert_count: state.active_alerts?.length || 0,
|
|
stale: state.stale ?? null,
|
|
};
|
|
case 'strategy-engine':
|
|
return {
|
|
latest_decision_id: state.latest_decision?.decision_id || null,
|
|
threshold_pct: state.threshold_pct ?? null,
|
|
max_notional_eure: state.max_notional_eure ?? null,
|
|
};
|
|
case 'trade-executor':
|
|
return {
|
|
in_flight_count: state.in_flight_count ?? 0,
|
|
submitted_count: state.submitted_count ?? state.completed_count ?? 0,
|
|
salt_unavailable_reject_count: state.salt_unavailable_reject_count ?? 0,
|
|
last_salt_unavailable_at: state.last_salt_unavailable_at || null,
|
|
signer_registered: state.signer_registered ?? null,
|
|
relay_connected: state.relay?.connected ?? null,
|
|
relay_last_message_at: state.relay?.last_message_at || null,
|
|
verifier_salt_cache: state.verifier_salt_cache || null,
|
|
executor_queue_delay: state.executor_queue_delay || null,
|
|
};
|
|
case 'operator-dashboard':
|
|
return {
|
|
source_error_count: state.source_error_count ?? 0,
|
|
last_source_error_at: state.last_source_error_at || null,
|
|
last_bootstrap_at: state.last_bootstrap_at || null,
|
|
};
|
|
default:
|
|
return {};
|
|
}
|
|
}
|
|
|
|
function inferServiceFreshnessTimestamp(service, state, health) {
|
|
return inferRuntimeFreshnessTimestamp(service, state, health);
|
|
}
|
|
|
|
function normalizeTradeForUi({ config, trade }) {
|
|
const assetIn = config.assetRegistry.get(trade.asset_in);
|
|
const assetOut = config.assetRegistry.get(trade.asset_out);
|
|
|
|
return {
|
|
...trade,
|
|
decision_reason: normalizeDecisionReason(trade.decision_reason),
|
|
asset_in_symbol: assetIn?.symbol || trade.asset_in,
|
|
asset_out_symbol: assetOut?.symbol || trade.asset_out,
|
|
amount_in_display: formatUnits(trade.amount_in || '0', assetIn?.decimals || 0),
|
|
amount_out_display: formatUnits(trade.amount_out || '0', assetOut?.decimals || 0),
|
|
};
|
|
}
|
|
|
|
export function enrichLifecycleRowForUi({ config, row }) {
|
|
const notionalDisplay = formatNotional(row);
|
|
const rawEdge = row.gross_edge_pct;
|
|
const edgeBps = row.edge_bps ?? row.command?.edge_bps ?? row.decision?.edge_bps ?? null;
|
|
const edgePct = (rawEdge != null && rawEdge !== '')
|
|
? rawEdge
|
|
: (edgeBps != null ? String((Number(edgeBps) / 100).toFixed(6)) : null);
|
|
|
|
const enrichedRow = {
|
|
...row,
|
|
gross_edge_pct: edgePct,
|
|
notional_display: notionalDisplay,
|
|
request_terms: buildLifecycleTerms({
|
|
config,
|
|
terms: row.quote || row,
|
|
}),
|
|
submitted_terms: buildLifecycleTerms({
|
|
config,
|
|
terms: row.command || row.execution || null,
|
|
}),
|
|
maker_terms: buildMakerLifecycleTerms({
|
|
config,
|
|
terms: row.command || row.execution || row.decision || row,
|
|
}),
|
|
inventory_check: buildInventoryCheck({
|
|
config,
|
|
decision: row.decision || row,
|
|
}),
|
|
};
|
|
|
|
return {
|
|
...enrichedRow,
|
|
gross_edge_value: estimateGrossEdgeValue(enrichedRow),
|
|
gross_edge_value_eure: estimateGrossEdgeValueEure(enrichedRow),
|
|
settlement_summary: buildSettlementSummary({
|
|
config,
|
|
delta: row.attributed_inventory_delta,
|
|
attributionStatus: row.attribution_status,
|
|
attributionMethod: row.attribution_method,
|
|
}),
|
|
};
|
|
}
|
|
|
|
function buildLifecycleTerms({ config, terms }) {
|
|
if (!terms?.asset_in && !terms?.asset_out) return null;
|
|
|
|
const assetIn = config.assetRegistry.get(terms.asset_in);
|
|
const assetOut = config.assetRegistry.get(terms.asset_out);
|
|
const amountIn = terms.amount_in ?? null;
|
|
const amountOut = terms.amount_out ?? null;
|
|
|
|
return {
|
|
asset_in: terms.asset_in || null,
|
|
asset_in_symbol: assetIn?.symbol || terms.asset_in || null,
|
|
amount_in_units: amountIn,
|
|
amount_in: amountIn == null ? null : formatUnits(amountIn, assetIn?.decimals || 0),
|
|
asset_out: terms.asset_out || null,
|
|
asset_out_symbol: assetOut?.symbol || terms.asset_out || null,
|
|
amount_out_units: amountOut,
|
|
amount_out: amountOut == null ? null : formatUnits(amountOut, assetOut?.decimals || 0),
|
|
};
|
|
}
|
|
|
|
function buildMakerLifecycleTerms({ config, terms }) {
|
|
if (terms?.maker_send_asset_id || terms?.maker_receive_asset_id) {
|
|
const sendAsset = config.assetRegistry.get(terms.maker_send_asset_id);
|
|
const receiveAsset = config.assetRegistry.get(terms.maker_receive_asset_id);
|
|
const sendAmount = terms.maker_send_amount_units ?? null;
|
|
const receiveAmount = terms.maker_receive_amount_units ?? null;
|
|
const sendDisplay = sendAmount == null ? null : formatUnits(sendAmount, sendAsset?.decimals || 0);
|
|
const receiveDisplay = receiveAmount == null ? null : formatUnits(receiveAmount, receiveAsset?.decimals || 0);
|
|
const sendSymbol = sendAsset?.symbol || terms.maker_send_symbol || terms.maker_send_asset_id || null;
|
|
const receiveSymbol = receiveAsset?.symbol || terms.maker_receive_symbol || terms.maker_receive_asset_id || null;
|
|
return {
|
|
receive_asset: terms.maker_receive_asset_id || null,
|
|
receive_asset_symbol: receiveSymbol,
|
|
receive_amount_units: receiveAmount,
|
|
receive_amount: receiveDisplay,
|
|
send_asset: terms.maker_send_asset_id || null,
|
|
send_asset_symbol: sendSymbol,
|
|
send_amount_units: sendAmount,
|
|
send_amount: sendDisplay,
|
|
delta_text: [
|
|
sendSymbol ? `-${sendDisplay || ''} ${sendSymbol}`.trim() : null,
|
|
receiveSymbol ? `+${receiveDisplay || ''} ${receiveSymbol}`.trim() : null,
|
|
].filter(Boolean).join(' / '),
|
|
};
|
|
}
|
|
|
|
const relayTerms = buildLifecycleTerms({ config, terms });
|
|
if (!relayTerms?.asset_in && !relayTerms?.asset_out) return null;
|
|
return {
|
|
receive_asset: relayTerms.asset_in,
|
|
receive_asset_symbol: relayTerms.asset_in_symbol,
|
|
receive_amount_units: relayTerms.amount_in_units,
|
|
receive_amount: relayTerms.amount_in,
|
|
send_asset: relayTerms.asset_out,
|
|
send_asset_symbol: relayTerms.asset_out_symbol,
|
|
send_amount_units: relayTerms.amount_out_units,
|
|
send_amount: relayTerms.amount_out,
|
|
delta_text: [
|
|
relayTerms.asset_out ? `-${relayTerms.amount_out || ''} ${relayTerms.asset_out_symbol || relayTerms.asset_out}`.trim() : null,
|
|
relayTerms.asset_in ? `+${relayTerms.amount_in || ''} ${relayTerms.asset_in_symbol || relayTerms.asset_in}`.trim() : null,
|
|
].filter(Boolean).join(' / '),
|
|
};
|
|
}
|
|
|
|
function buildInventoryCheck({ config, decision }) {
|
|
if (!decision?.inventory_asset) return null;
|
|
const asset = config.assetRegistry.get(decision.inventory_asset);
|
|
const decimals = asset?.decimals || 0;
|
|
const required = decision.inventory_required ?? null;
|
|
const available = decision.inventory_available ?? null;
|
|
const spendable = decision.inventory_spendable ?? null;
|
|
const pendingOutbound = decision.inventory_pending_outbound ?? decision.pending_outbound ?? null;
|
|
const pendingInbound = decision.pending_inbound ?? null;
|
|
return {
|
|
asset_id: decision.inventory_asset,
|
|
asset_symbol: asset?.symbol || decision.inventory_asset,
|
|
required_units: required,
|
|
required: required == null ? null : formatUnits(required, decimals),
|
|
available_units: available,
|
|
available: available == null ? null : formatUnits(available, decimals),
|
|
spendable_units: spendable,
|
|
spendable: spendable == null ? null : formatUnits(spendable, decimals),
|
|
pending_outbound_units: pendingOutbound,
|
|
pending_outbound: pendingOutbound == null ? null : formatUnits(pendingOutbound, decimals),
|
|
pending_inbound_units: pendingInbound,
|
|
pending_inbound: pendingInbound == null ? null : formatUnits(pendingInbound, decimals),
|
|
};
|
|
}
|
|
|
|
function estimateGrossEdgeValueEure(row) {
|
|
if (row?.notional && row?.notional_symbol && row.notional_symbol !== 'EURe') return null;
|
|
return estimateGrossEdgeValue(row);
|
|
}
|
|
|
|
function estimateGrossEdgeValue(row) {
|
|
let edge = Number(row?.gross_edge_pct);
|
|
if (!Number.isFinite(edge)) {
|
|
const edgeBps = row?.edge_bps ?? row?.command?.edge_bps ?? row?.decision?.edge_bps;
|
|
if (edgeBps != null) {
|
|
edge = Number(edgeBps) / 100;
|
|
}
|
|
}
|
|
const notional = Number(row?.notional ?? row?.eure_notional);
|
|
if (!Number.isFinite(edge) || !Number.isFinite(notional)) return null;
|
|
const value = (notional * edge) / 100;
|
|
return value.toFixed(8).replace(/\.?0+$/, '');
|
|
}
|
|
|
|
function formatNotional(row) {
|
|
const notional = row?.notional ?? row?.eure_notional ?? null;
|
|
if (notional == null) return null;
|
|
const symbol = row?.notional_symbol || (row?.eure_notional ? 'EURe' : null);
|
|
return symbol ? `${notional} ${symbol}` : String(notional);
|
|
}
|
|
|
|
function buildSettlementSummary({
|
|
config,
|
|
delta,
|
|
attributionStatus,
|
|
attributionMethod,
|
|
subject = 'quote',
|
|
}) {
|
|
if (!delta?.delta_units) {
|
|
return {
|
|
status: attributionStatus || 'unattributed',
|
|
method: attributionMethod || null,
|
|
lines: [],
|
|
text: attributionStatus === 'ambiguous'
|
|
? `Inventory movement is ambiguous and is not assigned to this ${subject}.`
|
|
: `No settled inventory delta is linked to this ${subject}.`,
|
|
};
|
|
}
|
|
|
|
const lines = Object.entries(delta.delta_units).map(([assetId, units]) => {
|
|
const asset = config.assetRegistry.get(assetId);
|
|
const symbol = asset?.symbol || assetId;
|
|
const formatted = formatUnits(units, asset?.decimals || 0);
|
|
const signed = BigInt(String(units || '0')) > 0n ? `+${formatted}` : formatted;
|
|
return {
|
|
asset_id: assetId,
|
|
symbol,
|
|
units,
|
|
amount: signed,
|
|
};
|
|
});
|
|
|
|
return {
|
|
status: attributionStatus || 'unattributed',
|
|
method: attributionMethod || null,
|
|
observed_at: delta.observed_at || null,
|
|
previous_observed_at: delta.previous_observed_at || null,
|
|
lines,
|
|
text: lines.map((line) => `${line.amount} ${line.symbol}`).join(', '),
|
|
caveat: delta.uncertainty || null,
|
|
};
|
|
}
|
|
|
|
function buildRecentDepositItems({ config, recentDepositStatuses, liquidityState }) {
|
|
const recentItems = (recentDepositStatuses || []).map((entry) => normalizeDepositStatusForUi({
|
|
config,
|
|
depositStatus: entry,
|
|
}));
|
|
const itemsByKey = new Map(
|
|
recentItems.map((item) => [buildFundingActivityKey(item), item]),
|
|
);
|
|
|
|
for (const deposit of Object.values(liquidityState?.deposits || {})) {
|
|
const fallbackItem = normalizeLiquidityDepositForUi({
|
|
config,
|
|
deposit,
|
|
observedAt: null,
|
|
});
|
|
const key = buildFundingActivityKey(fallbackItem);
|
|
const existing = itemsByKey.get(key);
|
|
if (!existing || shouldReplaceFundingActivityItem(existing, fallbackItem)) {
|
|
itemsByKey.set(key, fallbackItem);
|
|
}
|
|
}
|
|
|
|
return [...itemsByKey.values()];
|
|
}
|
|
|
|
function mergeFundingActivityItems({ observerItems, depositItems }) {
|
|
const merged = new Map();
|
|
|
|
for (const item of depositItems || []) {
|
|
merged.set(buildFundingActivityKey(item), item);
|
|
}
|
|
for (const item of observerItems || []) {
|
|
merged.set(buildFundingActivityKey(item), item);
|
|
}
|
|
|
|
return [...merged.values()];
|
|
}
|
|
|
|
function latestFundingActivityAt(items, fallback = null) {
|
|
let latest = fallback;
|
|
for (const item of items || []) {
|
|
if (sortTimestamps(fundingActivityTimestamp(item), latest) > 0) {
|
|
latest = fundingActivityTimestamp(item);
|
|
}
|
|
}
|
|
return latest;
|
|
}
|
|
|
|
function fundingActivityTimestamp(item) {
|
|
return item?.last_seen_at || item?.credited_at || item?.first_seen_at || null;
|
|
}
|
|
|
|
function shouldReplaceFundingActivityItem(existing, candidate) {
|
|
if (!candidate) return false;
|
|
if (!existing) return true;
|
|
if (candidate.bridge_created_at && !existing.bridge_created_at) return true;
|
|
|
|
const existingTs = timestampValue(fundingActivityTimestamp(existing));
|
|
const candidateTs = timestampValue(fundingActivityTimestamp(candidate));
|
|
return Number.isFinite(candidateTs)
|
|
&& (!Number.isFinite(existingTs) || candidateTs < existingTs);
|
|
}
|
|
|
|
function buildFundingActivityKey(item) {
|
|
return [
|
|
item?.tx_hash || 'no-tx',
|
|
item?.chain || 'no-chain',
|
|
item?.asset_id || 'no-asset',
|
|
item?.funding_handle || 'no-handle',
|
|
item?.amount_units || 'no-amount',
|
|
].join('|');
|
|
}
|
|
|
|
function normalizeFundingObservationForUi({ config, observation }) {
|
|
const asset = config.assetRegistry.get(observation.asset_id);
|
|
return {
|
|
funding_observation_id: observation.funding_observation_id,
|
|
asset_id: observation.asset_id,
|
|
symbol: asset?.symbol || observation.asset_id,
|
|
asset_symbol: asset?.label || asset?.symbol || observation.asset_id,
|
|
chain: observation.chain,
|
|
funding_handle: observation.funding_handle,
|
|
tx_hash: observation.tx_hash,
|
|
status: observation.status,
|
|
amount_units: observation.amount,
|
|
amount_display: formatUnits(observation.amount || '0', asset?.decimals || 0),
|
|
amount: formatUnits(observation.amount || '0', asset?.decimals || 0),
|
|
confirmations: observation.confirmations,
|
|
first_seen_at: observation.first_seen_at,
|
|
last_seen_at: observation.last_seen_at,
|
|
credited_at: observation.credited_at || null,
|
|
};
|
|
}
|
|
|
|
function normalizeDepositStatusForUi({ config, depositStatus }) {
|
|
const payload = depositStatus?.payload || {};
|
|
const details = payload.details || {};
|
|
return normalizeLiquidityDepositForUi({
|
|
config,
|
|
deposit: {
|
|
tx_hash: details.tx_hash || null,
|
|
chain: payload.chain || details.chain || null,
|
|
asset_id: payload.asset_id || details.asset_id || null,
|
|
amount: details.amount || '0',
|
|
address: details.address || details.deposit_address || null,
|
|
status: payload.status || details.status || null,
|
|
created_at: details.created_at || payload.created_at || null,
|
|
},
|
|
observedAt: depositStatus?.observed_at || depositStatus?.ingested_at || null,
|
|
});
|
|
}
|
|
|
|
function normalizeLiquidityDepositForUi({ config, deposit, observedAt }) {
|
|
const asset = config.assetRegistry.get(deposit?.asset_id);
|
|
const status = deposit?.status || null;
|
|
const bridgeCreatedAt = bridgeDepositObservedAt(deposit);
|
|
const timestamp = bridgeCreatedAt || observedAt || null;
|
|
|
|
return {
|
|
funding_observation_id: null,
|
|
asset_id: deposit?.asset_id || null,
|
|
symbol: asset?.symbol || deposit?.asset_id || null,
|
|
asset_symbol: asset?.label || asset?.symbol || deposit?.asset_id || null,
|
|
chain: deposit?.chain || null,
|
|
funding_handle: deposit?.address || null,
|
|
tx_hash: deposit?.tx_hash || null,
|
|
status,
|
|
amount_units: String(deposit?.amount || '0'),
|
|
amount_display: formatUnits(deposit?.amount || '0', asset?.decimals || 0),
|
|
amount: formatUnits(deposit?.amount || '0', asset?.decimals || 0),
|
|
confirmations: null,
|
|
first_seen_at: timestamp,
|
|
last_seen_at: timestamp,
|
|
credited_at: CREDITED_STATUSES.has(String(status || '').toUpperCase()) ? timestamp : null,
|
|
bridge_created_at: bridgeCreatedAt,
|
|
};
|
|
}
|
|
|
|
function normalizeDecision(decision) {
|
|
if (!decision) return null;
|
|
return {
|
|
decision_id: decision.decision_id || null,
|
|
decision_at: decision.decision_at || null,
|
|
quote_id: decision.quote_id || null,
|
|
pair: decision.pair || null,
|
|
request_pair: decision.request_pair || null,
|
|
request_pair_id: decision.request_pair_id || null,
|
|
maker_trade_pair: decision.maker_trade_pair || null,
|
|
maker_trade_pair_id: decision.maker_trade_pair_id || null,
|
|
pair_id: decision.pair_id || null,
|
|
pair_config_id: decision.pair_config_id || null,
|
|
pair_config_version: decision.pair_config_version || null,
|
|
edge_bps: decision.edge_bps || null,
|
|
direction: decision.direction || null,
|
|
request_kind: decision.request_kind || null,
|
|
decision: normalizeDecisionVerdict(decision.decision),
|
|
decision_reason: normalizeDecisionReason(decision.decision_reason),
|
|
gross_edge_pct: decision.gross_edge_pct || null,
|
|
threshold_pct: decision.threshold_pct || null,
|
|
max_notional: decision.max_notional || null,
|
|
max_notional_eure: decision.max_notional_eure || null,
|
|
strategy_armed: decision.strategy_armed ?? null,
|
|
inventory_asset: decision.inventory_asset || null,
|
|
inventory_required: decision.inventory_required || null,
|
|
inventory_available: decision.inventory_available || null,
|
|
inventory_spendable: decision.inventory_spendable || null,
|
|
inventory_pending_outbound: decision.inventory_pending_outbound || null,
|
|
maker_send_asset_id: decision.maker_send_asset_id || null,
|
|
maker_receive_asset_id: decision.maker_receive_asset_id || null,
|
|
maker_send_symbol: decision.maker_send_symbol || null,
|
|
maker_receive_symbol: decision.maker_receive_symbol || null,
|
|
maker_send_amount_units: decision.maker_send_amount_units || null,
|
|
maker_receive_amount_units: decision.maker_receive_amount_units || null,
|
|
pending_inbound: decision.pending_inbound || null,
|
|
pending_outbound: decision.pending_outbound || null,
|
|
notional: decision.notional || null,
|
|
notional_asset_id: decision.notional_asset_id || null,
|
|
notional_symbol: decision.notional_symbol || null,
|
|
eure_notional: decision.eure_notional || null,
|
|
maker_timing: decision.maker_timing || null,
|
|
quote_age_at_decision_ms: decision.quote_age_at_decision_ms ?? null,
|
|
response_policy: decision.response_policy || null,
|
|
};
|
|
}
|
|
|
|
function normalizeDecisionVerdict(value) {
|
|
if (value === 'actionable') return 'approved';
|
|
return value || null;
|
|
}
|
|
|
|
function normalizeDecisionReason(value) {
|
|
if (value === 'actionable') return 'strategy_approved';
|
|
return value || null;
|
|
}
|
|
|
|
function normalizeAlertList(alerts) {
|
|
return (alerts || []).map(normalizeAlert).sort((left, right) => sortTimestamps(
|
|
right.raised_at || right.first_raised_at || right.cleared_at,
|
|
left.raised_at || left.first_raised_at || left.cleared_at,
|
|
));
|
|
}
|
|
|
|
function normalizeAlert(alert) {
|
|
return {
|
|
alert_code: alert.alert_code,
|
|
status: alert.status,
|
|
severity: alert.severity,
|
|
reason: alert.reason,
|
|
service_scope: alert.service_scope,
|
|
pair: alert.pair || null,
|
|
asset_id: alert.asset_id || null,
|
|
tx_hash: alert.tx_hash || null,
|
|
raised_at: alert.raised_at || null,
|
|
first_raised_at: alert.first_raised_at || null,
|
|
cleared_at: alert.cleared_at || null,
|
|
last_evaluated_at: alert.last_evaluated_at || null,
|
|
details: alert.details || {},
|
|
transition_count: Number(alert.transition_count || 1),
|
|
raised_count: Number(alert.raised_count || (alert.status === 'raised' ? 1 : 0)),
|
|
cleared_count: Number(alert.cleared_count || (alert.status === 'cleared' ? 1 : 0)),
|
|
};
|
|
}
|
|
|
|
function summarizeRecentAlertTransitions(alerts) {
|
|
const summaries = new Map();
|
|
|
|
for (const alert of alerts || []) {
|
|
const key = buildAlertKey(alert);
|
|
const existing = summaries.get(key);
|
|
if (!existing) {
|
|
summaries.set(key, {
|
|
...alert,
|
|
transition_count: alert.transition_count || 1,
|
|
raised_count: alert.status === 'raised' ? 1 : 0,
|
|
cleared_count: alert.status === 'cleared' ? 1 : 0,
|
|
});
|
|
continue;
|
|
}
|
|
|
|
existing.transition_count += 1;
|
|
existing.raised_count += alert.status === 'raised' ? 1 : 0;
|
|
existing.cleared_count += alert.status === 'cleared' ? 1 : 0;
|
|
}
|
|
|
|
return [...summaries.values()].sort((left, right) => sortTimestamps(
|
|
right.cleared_at || right.raised_at || right.last_evaluated_at,
|
|
left.cleared_at || left.raised_at || left.last_evaluated_at,
|
|
));
|
|
}
|
|
|
|
function normalizeDashboardLiveTopic(state, topic) {
|
|
const config = state?.config || {};
|
|
const aliases = new Map([
|
|
[config.kafkaTopicNormSwapDemand, 'norm.swap_demand'],
|
|
[config.kafkaTopicDecisionTradeDecision, 'decision.trade_decision'],
|
|
[config.kafkaTopicCmdExecuteTrade, 'cmd.execute_trade'],
|
|
[config.kafkaTopicRefMarketPrice, 'ref.market_price'],
|
|
[config.kafkaTopicStateIntentInventory, 'state.intent_inventory'],
|
|
[config.kafkaTopicOpsAlert, 'ops.alert'],
|
|
[config.kafkaTopicOpsEnvironmentStatus, 'ops.environment_status'],
|
|
[config.kafkaTopicExecTradeResult, 'exec.trade_result'],
|
|
]);
|
|
return aliases.get(topic) || topic;
|
|
}
|
|
|
|
function buildQuoteLifecycleUpdate(state, { flashQuoteId = null } = {}) {
|
|
const receivedAt = new Date().toISOString();
|
|
const lifecycleRows = buildLiveQuoteLifecycleRows(state, {
|
|
flashQuoteId,
|
|
flashAt: receivedAt,
|
|
});
|
|
return {
|
|
type: 'quote_lifecycle.updated',
|
|
recent_lifecycle_rows: lifecycleRows,
|
|
maker_competitiveness: buildMakerCompetitivenessSummary({ lifecycleRows, generatedAt: receivedAt }),
|
|
quote_lifecycle_statistics: {
|
|
source: 'live_recent',
|
|
generated_at: receivedAt,
|
|
rows: buildQuoteLifecycleStatistics({
|
|
lifecycleRows,
|
|
computedAt: receivedAt,
|
|
}),
|
|
},
|
|
flash_quote_id: flashQuoteId || null,
|
|
received_at: receivedAt,
|
|
};
|
|
}
|
|
|
|
function appendUniqueRecentEvent(items, nextItem, limit, keyFn) {
|
|
const nextKey = keyFn(nextItem);
|
|
const deduped = [
|
|
nextItem,
|
|
...(items || []).filter((item) => keyFn(item) !== nextKey),
|
|
];
|
|
return deduped.slice(0, limit);
|
|
}
|
|
|
|
function livePayloadKey(entry, fields) {
|
|
const payload = entry?.payload || entry || {};
|
|
for (const field of fields) {
|
|
if (payload[field] != null && payload[field] !== '') return `${field}:${payload[field]}`;
|
|
}
|
|
return `event:${entry?.observed_at || entry?.ingested_at || JSON.stringify(payload)}`;
|
|
}
|
|
|
|
function normalizeLiveDecision(payload, event) {
|
|
if (!payload?.decision_id && !payload?.quote_id) return null;
|
|
const decisionAt = payload.decision_at || event.observed_at || event.ingested_at || null;
|
|
return {
|
|
observed_at: event.observed_at || decisionAt,
|
|
ingested_at: event.ingested_at || null,
|
|
payload: {
|
|
...payload,
|
|
decision_at: decisionAt,
|
|
maker_timing: payload.maker_timing || null,
|
|
},
|
|
};
|
|
}
|
|
|
|
function normalizeLiveCommand(payload, event) {
|
|
if (!payload?.command_id && !payload?.decision_id && !payload?.quote_id) return null;
|
|
return {
|
|
observed_at: event.observed_at || event.ingested_at || null,
|
|
ingested_at: event.ingested_at || null,
|
|
payload: {
|
|
...payload,
|
|
amount_in: payload.quote_output?.amount_in ?? payload.proposed_amount_in ?? payload.amount_in ?? null,
|
|
amount_out: payload.quote_output?.amount_out ?? payload.proposed_amount_out ?? payload.amount_out ?? null,
|
|
maker_timing: payload.maker_timing || null,
|
|
},
|
|
};
|
|
}
|
|
|
|
function normalizeLiveExecutionResult(payload, event) {
|
|
if (!payload?.command_id && !payload?.decision_id && !payload?.quote_id) return null;
|
|
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,
|
|
request_pair: payload.request_pair || null,
|
|
request_pair_id: payload.request_pair_id || null,
|
|
maker_trade_pair: payload.maker_trade_pair || null,
|
|
maker_trade_pair_id: payload.maker_trade_pair_id || null,
|
|
result_at: event.observed_at || event.ingested_at || new Date().toISOString(),
|
|
status: payload.status || null,
|
|
result_code: payload.result_code || null,
|
|
failure_category: payload.failure_category || null,
|
|
outcome_status: payload.outcome_status || payload.venue_outcome_status || payload.trade_outcome_status || null,
|
|
outcome_reason: payload.outcome_reason || payload.venue_outcome_reason || payload.trade_outcome_reason || null,
|
|
attribution_status: payload.attribution_status || null,
|
|
attribution_method: payload.attribution_method || null,
|
|
attributed_inventory_delta: payload.attributed_inventory_delta || null,
|
|
venue_response: payload.venue_response || null,
|
|
error_message: payload.relay_error_message || payload.error?.message || null,
|
|
note: payload.note || null,
|
|
timing: payload.executor_timing || null,
|
|
maker_send_asset_id: payload.maker_send_asset_id || null,
|
|
maker_receive_asset_id: payload.maker_receive_asset_id || null,
|
|
maker_send_amount_units: payload.maker_send_amount_units || null,
|
|
maker_receive_amount_units: payload.maker_receive_amount_units || null,
|
|
maker_timing: payload.maker_timing || null,
|
|
};
|
|
}
|
|
|
|
function appendUniqueRecentQuote(quotes, nextQuote, limit) {
|
|
const deduped = [nextQuote, ...quotes.filter((quote) => quote.quote_id !== nextQuote.quote_id)];
|
|
return deduped.slice(0, limit);
|
|
}
|
|
|
|
function normalizeLiveQuote(payload, event) {
|
|
if (!payload?.quote_id) return null;
|
|
return {
|
|
quote_id: payload.quote_id,
|
|
pair: payload.pair || `${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,
|
|
maker_timing: payload.maker_timing || null,
|
|
observed_at: event.observed_at || null,
|
|
ingested_at: event.ingested_at || null,
|
|
};
|
|
}
|
|
|
|
function buildAlertKey(alert) {
|
|
return [
|
|
alert.alert_code,
|
|
alert.service_scope,
|
|
alert.pair || '',
|
|
alert.asset_id || '',
|
|
alert.tx_hash || '',
|
|
].join('|');
|
|
}
|
|
|
|
function highestAlertSeverity(alerts) {
|
|
return (alerts || []).reduce((highest, alert) => {
|
|
const currentRank = ALERT_SEVERITY_ORDER[highest] || 0;
|
|
const nextRank = ALERT_SEVERITY_ORDER[alert.severity] || 0;
|
|
return nextRank > currentRank ? alert.severity : highest;
|
|
}, null);
|
|
}
|
|
|
|
function computeCurrentPortfolioValue({
|
|
inventory,
|
|
marketPrice,
|
|
btcAsset,
|
|
btcAssets = null,
|
|
eureAsset,
|
|
}) {
|
|
if (!inventory || !marketPrice || !btcAsset || !eureAsset) return null;
|
|
|
|
const effectiveBtcAssets = btcAssets?.length ? btcAssets : [btcAsset];
|
|
const btcScaled = effectiveBtcAssets.reduce((total, asset) => {
|
|
const units = String(inventory.spendable?.[asset.assetId] || '0');
|
|
return total + unitsToScaledDecimal(units, asset.decimals);
|
|
}, 0n);
|
|
const eureUnits = String(inventory.spendable?.[eureAsset.assetId] || '0');
|
|
const eureScaled = unitsToScaledDecimal(eureUnits, eureAsset.decimals);
|
|
const priceScaled = parseScaledDecimal(marketPrice.eure_per_btc || marketPrice.eur_per_btc || '0');
|
|
const total = eureScaled + multiplyScaled(btcScaled, priceScaled);
|
|
|
|
return formatScaledDecimal(total);
|
|
}
|
|
|
|
function valueAssetInEur({ asset, units, marketPrice }) {
|
|
if (!asset) return null;
|
|
if (asset.symbol === 'EURe') {
|
|
return formatUnits(units || '0', asset.decimals);
|
|
}
|
|
if (!marketPrice || asset.symbol !== 'BTC') return null;
|
|
|
|
const scaledUnits = unitsToScaledDecimal(units || '0', asset.decimals);
|
|
const priceScaled = parseScaledDecimal(marketPrice.eure_per_btc || marketPrice.eur_per_btc || '0');
|
|
return formatScaledDecimal(multiplyScaled(scaledUnits, priceScaled));
|
|
}
|
|
|
|
function formatDecimalDifference(left, right) {
|
|
return formatScaledDecimal(parseScaledDecimal(left) - parseScaledDecimal(right));
|
|
}
|
|
|
|
function unitsToScaledDecimal(units, decimals) {
|
|
return BigInt(String(units || '0')) * 10n ** BigInt(DECIMAL_SCALE - decimals);
|
|
}
|
|
|
|
function parseScaledDecimal(value) {
|
|
const normalized = String(value ?? '0').trim();
|
|
const negative = normalized.startsWith('-');
|
|
const unsigned = normalized.replace(/^[+-]/, '');
|
|
const [wholePart, fractionalPart = ''] = unsigned.split('.');
|
|
const whole = BigInt(wholePart || '0');
|
|
const fractional = BigInt(
|
|
(fractionalPart.padEnd(DECIMAL_SCALE, '0')).slice(0, DECIMAL_SCALE) || '0',
|
|
);
|
|
const scaled = (whole * DECIMAL_FACTOR) + fractional;
|
|
return negative ? -scaled : scaled;
|
|
}
|
|
|
|
function multiplyScaled(left, right) {
|
|
return (left * right) / DECIMAL_FACTOR;
|
|
}
|
|
|
|
function formatScaledDecimal(value) {
|
|
const negative = value < 0n;
|
|
const absolute = negative ? -value : value;
|
|
const whole = absolute / DECIMAL_FACTOR;
|
|
const fractional = absolute % DECIMAL_FACTOR;
|
|
if (fractional === 0n) {
|
|
return `${negative ? '-' : ''}${whole}`;
|
|
}
|
|
const fractionalText = fractional
|
|
.toString()
|
|
.padStart(DECIMAL_SCALE, '0')
|
|
.replace(/0+$/, '');
|
|
return `${negative ? '-' : ''}${whole}.${fractionalText}`;
|
|
}
|
|
|
|
function formatUnits(units, decimals) {
|
|
const numeric = unitsToNumber(units, decimals);
|
|
if (!Number.isFinite(numeric)) return '0';
|
|
if (Math.abs(numeric) >= 1000) return numeric.toLocaleString('en-US', { maximumFractionDigits: 8 });
|
|
return numeric.toLocaleString('en-US', { maximumFractionDigits: 8 });
|
|
}
|
|
|
|
function ageMs(value) {
|
|
const parsed = Date.parse(value || '');
|
|
if (!Number.isFinite(parsed)) return null;
|
|
return Math.max(0, Date.now() - parsed);
|
|
}
|
|
|
|
function sortTimestamps(left, right) {
|
|
return timestampValue(left) - timestampValue(right);
|
|
}
|
|
|
|
function timestampValue(value) {
|
|
const parsed = Date.parse(value || '');
|
|
return Number.isFinite(parsed) ? parsed : -Infinity;
|
|
}
|