unrip/src/core/intent-request-outcomes.mjs
philipp 729d2ade0e
Some checks failed
deploy / deploy (push) Failing after 43s
Implement pair-native trade semantics
Proof: Pair-native trade semantics and multi-asset outcome truth; strategy, request preflight, outcome attribution, valuation visibility, dashboard labels, alerts, and ops watch paths now use DB pair/asset/route metadata with nBTC/EURe compatibility and nBTC/USDC regressions covered.

Assumptions: Postgres asset, pair, strategy config, and price route rows remain canonical; supported reference adapters remain BTC/EUR and BTC/USDC; deployment is push-driven through the existing Forgejo workflow.

Still fake: Arbitrary multi-hop valuation, new execution venues, fee-complete realized PnL, venue-native terminal fill ingestion, and autonomous optimization remain unbuilt.
2026-05-18 18:52:18 +02:00

522 lines
18 KiB
JavaScript

const DEFAULT_ATTRIBUTION_WINDOW_MS = 10 * 60 * 1000;
const DEFAULT_SETTLEMENT_GRACE_MS = 60 * 1000;
export const REQUEST_TERMINAL_SETTLEMENT_ATTRIBUTION_STATUSES = new Set([
'linked_settlement',
'heuristic_match',
]);
export function deriveIntentRequestOutcomeRecords({
preflights = [],
submissions = [],
inventorySnapshots = [],
btcAsset,
eureAsset,
now = Date.now(),
attributionWindowMs = DEFAULT_ATTRIBUTION_WINDOW_MS,
settlementGraceMs = DEFAULT_SETTLEMENT_GRACE_MS,
} = {}) {
const preflightsByRequest = new Map(
preflights
.map(normalizePreflight)
.filter((entry) => entry?.request_id)
.map((entry) => [entry.request_id, entry]),
);
const expectedDeltasByRequest = new Map();
for (const preflight of preflightsByRequest.values()) {
const expectedDelta = buildExpectedRequestDelta(preflight, null);
if (expectedDelta) expectedDeltasByRequest.set(preflight.request_id, expectedDelta);
}
const activeAssetIds = uniqueAssetIds([
...expectedDeltasByRequest.values(),
Object.fromEntries([btcAsset?.assetId, eureAsset?.assetId].filter(Boolean).map((assetId) => [assetId, 0n])),
]);
const latestSubmissionByRequest = new Map();
for (const submission of submissions.map(normalizeSubmission).filter(Boolean)) {
if (!submission.request_id) continue;
const previous = latestSubmissionByRequest.get(submission.request_id);
if (!previous || timestampValue(submission.submitted_at) >= timestampValue(previous.submitted_at)) {
latestSubmissionByRequest.set(submission.request_id, submission);
}
}
const inventoryDeltas = deriveInventoryDeltas({
inventorySnapshots,
activeAssetIds,
});
const latestInventoryAt = latestSnapshotTimestamp(inventorySnapshots);
return [...preflightsByRequest.values()]
.map((preflight) => deriveOneOutcome({
preflight,
submission: latestSubmissionByRequest.get(preflight.request_id) || null,
inventoryDeltas,
latestInventoryAt,
now,
attributionWindowMs,
settlementGraceMs,
}))
.filter(Boolean);
}
export function deriveInventoryDeltas({ inventorySnapshots = [], activeAssetIds = [] } = {}) {
const sorted = inventorySnapshots
.map(normalizeInventorySnapshot)
.filter((entry) => entry?.observed_at)
.sort((left, right) => timestampValue(left.observed_at) - timestampValue(right.observed_at));
const deltas = [];
for (let index = 1; index < sorted.length; index += 1) {
const previous = sorted[index - 1];
const current = sorted[index];
const deltaUnits = {};
let changed = false;
for (const assetId of activeAssetIds) {
const delta = safeBigInt(current.spendable?.[assetId]) - safeBigInt(previous.spendable?.[assetId]);
deltaUnits[assetId] = delta.toString();
if (delta !== 0n) changed = true;
}
if (!changed) continue;
deltas.push({
movement_id: `${previous.observed_at}->${current.observed_at}`,
observed_at: current.observed_at,
previous_observed_at: previous.observed_at,
inventory_id: current.inventory_id,
previous_inventory_id: previous.inventory_id,
delta_units: deltaUnits,
});
}
return deltas;
}
function deriveOneOutcome({
preflight,
submission,
inventoryDeltas,
latestInventoryAt,
now,
attributionWindowMs,
settlementGraceMs,
}) {
if (!submission) {
return baseOutcomeRecord({
preflight,
submission: null,
outcome_status: preflight.state === 'draft' ? 'draft' : 'blocked',
outcome_observed_at: preflight.created_at,
outcome_source: 'request_preflight',
outcome_reason: preflight.reason_code || 'preflight_recorded',
attribution_status: 'unattributed',
attribution_method: null,
attributed_inventory_delta: null,
evidence: { preflight_state: preflight.state },
});
}
if (['failed', 'blocked'].includes(submission.status)) {
return baseOutcomeRecord({
preflight,
submission,
outcome_status: submission.status,
outcome_observed_at: submission.submitted_at,
outcome_source: 'request_submission_result',
outcome_reason: submission.result_code || submission.status,
attribution_status: 'unattributed',
attribution_method: null,
attributed_inventory_delta: null,
evidence: { relay_status: submission.relay_status || null },
});
}
const expectedDelta = buildExpectedRequestDelta(preflight, submission);
const matches = expectedDelta
? inventoryDeltas.filter((movement) => movementMatchesExpectedDelta({
movement,
expectedDelta,
submittedAt: submission.submitted_at,
attributionWindowMs,
}))
: [];
if (matches.length === 1) {
const movement = matches[0];
return baseOutcomeRecord({
preflight,
submission,
outcome_status: 'completed',
outcome_observed_at: movement.observed_at,
outcome_source: 'intent_inventory_spendable_delta',
outcome_reason: 'matched_inventory_delta',
attribution_status: 'heuristic_match',
attribution_method: 'exact_asset_delta_within_window',
attributed_inventory_delta: {
inventory_id: movement.inventory_id,
previous_inventory_id: movement.previous_inventory_id,
observed_at: movement.observed_at,
previous_observed_at: movement.previous_observed_at,
delta_units: movement.delta_units,
attribution_window_ms: attributionWindowMs,
uncertainty:
'Matched by exact asset-unit delta after request submission; no venue-native fill id is linked.',
},
evidence: {
settlement_movement_id: movement.movement_id,
settlement_source: 'intent_inventory_snapshots',
relay_status: submission.relay_status || null,
},
});
}
if (matches.length > 1) {
return baseOutcomeRecord({
preflight,
submission,
outcome_status: 'awaiting_settlement',
outcome_observed_at: submission.submitted_at,
outcome_source: 'request_submission_and_inventory_snapshots',
outcome_reason: 'ambiguous_inventory_delta_match',
attribution_status: 'ambiguous',
attribution_method: null,
attributed_inventory_delta: null,
evidence: {
candidate_movement_count: matches.length,
candidate_movement_ids: matches.map((entry) => entry.movement_id),
relay_status: submission.relay_status || null,
},
});
}
if (submission.relay_status === 'SETTLED' && hasFreshInventoryAfterStatusCheck({ submission, latestInventoryAt })) {
return baseOutcomeRecord({
preflight,
submission,
outcome_status: 'failed',
outcome_observed_at: latestInventoryAt || submission.status_checked_at || submission.submitted_at,
outcome_source: 'solver_relay_status_and_inventory_snapshots',
outcome_reason: 'relay_settled_without_expected_inventory_delta',
attribution_status: 'unattributed',
attribution_method: null,
attributed_inventory_delta: null,
evidence: {
relay_status: submission.relay_status,
relay_status_response: submission.relay_status_response,
latest_inventory_observed_at: latestInventoryAt,
uncertainty:
'Relay reported settlement, but durable inventory did not show the expected source decrease and destination increase.',
},
});
}
if (submission.relay_status === 'NOT_FOUND_OR_NOT_VALID') {
return baseOutcomeRecord({
preflight,
submission,
outcome_status: 'not_filled',
outcome_observed_at: submission.status_checked_at || submission.submitted_at,
outcome_source: 'solver_relay_get_status',
outcome_reason: 'relay_not_found_or_not_valid',
attribution_status: 'unattributed',
attribution_method: null,
attributed_inventory_delta: null,
evidence: {
relay_status: submission.relay_status,
relay_status_response: submission.relay_status_response,
},
});
}
const expiredWindow = getExpiredSettlementWindow({
submission,
preflight,
latestInventoryAt,
now,
settlementGraceMs,
});
if (expiredWindow) {
return baseOutcomeRecord({
preflight,
submission,
outcome_status: 'not_filled',
outcome_observed_at: expiredWindow.latestInventoryAt || expiredWindow.expiresAt || submission.submitted_at,
outcome_source: 'request_deadline_and_inventory_snapshots',
outcome_reason: 'deadline_elapsed_without_settlement',
attribution_status: 'unattributed',
attribution_method: null,
attributed_inventory_delta: null,
evidence: {
deadline_at: preflight.deadline_at || null,
settlement_grace_ms: settlementGraceMs,
settlement_window_expired_at: expiredWindow.expiresAt,
latest_inventory_observed_at: expiredWindow.latestInventoryAt,
relay_status: submission.relay_status || null,
},
});
}
return baseOutcomeRecord({
preflight,
submission,
outcome_status: 'awaiting_settlement',
outcome_observed_at: submission.submitted_at,
outcome_source: 'request_submission_result',
outcome_reason: submission.relay_status === 'SETTLED'
? 'relay_settled_but_inventory_delta_missing'
: 'accepted_by_relay_without_settlement',
attribution_status: 'unattributed',
attribution_method: null,
attributed_inventory_delta: null,
evidence: {
relay_status: submission.relay_status || null,
relay_status_response: submission.relay_status_response || null,
uncertainty:
'Relay acceptance or status is not counted as completed until inventory movement is linked.',
},
});
}
function buildExpectedRequestDelta(preflight, submission) {
const destinationAmount = submission?.destination_amount_units
|| preflight?.destination_amount_units
|| preflight?.selected_quote?.amount_out
|| preflight?.quoted_destination_amount_units;
if (!preflight?.source_asset_id || !preflight?.destination_asset_id) return null;
if (!preflight?.source_amount_units || !destinationAmount) return null;
return {
[preflight.source_asset_id]: -safeBigInt(preflight.source_amount_units),
[preflight.destination_asset_id]: safeBigInt(destinationAmount),
};
}
function baseOutcomeRecord({
preflight,
submission,
outcome_status,
outcome_observed_at,
outcome_source,
outcome_reason,
attribution_status,
attribution_method,
attributed_inventory_delta,
evidence,
}) {
const payload = {
request_id: preflight.request_id,
idempotency_key: preflight.idempotency_key,
submission_id: submission?.submission_id || null,
intent_hash: submission?.intent_hash || null,
source_asset_id: preflight.source_asset_id,
source_symbol: preflight.source_symbol || null,
destination_asset_id: preflight.destination_asset_id,
destination_symbol: preflight.destination_symbol || null,
source_amount_units: preflight.source_amount_units,
destination_amount_units: submission?.destination_amount_units || preflight.destination_amount_units || null,
notional: preflight.notional || null,
notional_asset_id: preflight.notional_asset_id || null,
notional_symbol: preflight.notional_symbol || null,
min_destination_amount_units: preflight.min_destination_amount_units,
quote_hash: submission?.quote_hash || preflight.selected_quote?.quote_hash || null,
submitted_at: submission?.submitted_at || null,
deadline_at: preflight.deadline_at || null,
outcome_status,
outcome_observed_at,
outcome_source,
outcome_reason,
attribution_status,
attribution_method,
attributed_inventory_delta,
evidence,
};
return {
request_id: preflight.request_id,
idempotency_key: preflight.idempotency_key,
submission_id: submission?.submission_id || null,
intent_hash: submission?.intent_hash || null,
submission_status: submission?.status || null,
relay_status: submission?.relay_status || null,
submitted_at: submission?.submitted_at || null,
outcome_status,
outcome_observed_at,
outcome_source,
outcome_reason,
attribution_status,
attribution_method,
attributed_inventory_delta,
payload,
};
}
function movementMatchesExpectedDelta({
movement,
expectedDelta,
submittedAt,
attributionWindowMs,
}) {
const submittedTs = timestampValue(submittedAt);
const movementTs = timestampValue(movement.observed_at);
if (!Number.isFinite(submittedTs) || !Number.isFinite(movementTs)) return false;
if (movementTs < submittedTs) return false;
if (movementTs - submittedTs > attributionWindowMs) return false;
for (const [assetId, expected] of Object.entries(expectedDelta)) {
if (safeBigInt(movement.delta_units?.[assetId]) !== expected) return false;
}
for (const [assetId, actual] of Object.entries(movement.delta_units || {})) {
if (Object.prototype.hasOwnProperty.call(expectedDelta, assetId)) continue;
if (safeBigInt(actual) !== 0n) return false;
}
return true;
}
function hasFreshInventoryAfterStatusCheck({ submission, latestInventoryAt }) {
const statusCheckedTs = timestampValue(submission.status_checked_at || submission.submitted_at);
const latestInventoryTs = timestampValue(latestInventoryAt);
return Number.isFinite(statusCheckedTs)
&& Number.isFinite(latestInventoryTs)
&& latestInventoryTs >= statusCheckedTs;
}
function getExpiredSettlementWindow({
submission,
preflight,
latestInventoryAt,
now,
settlementGraceMs,
}) {
const submittedTs = timestampValue(submission.submitted_at);
if (!Number.isFinite(submittedTs)) return null;
const deadlineTs = timestampValue(preflight.deadline_at);
const fallbackDeadlineMs = Number(preflight.min_deadline_ms || 60_000);
const expiresAt = Number.isFinite(deadlineTs)
? deadlineTs + settlementGraceMs
: submittedTs + (Number.isFinite(fallbackDeadlineMs) ? fallbackDeadlineMs : 60_000) + settlementGraceMs;
const nowTs = typeof now === 'number' ? now : timestampValue(now);
const latestInventoryTs = timestampValue(latestInventoryAt);
if (
Number.isFinite(nowTs)
&& nowTs >= expiresAt
&& Number.isFinite(latestInventoryTs)
&& latestInventoryTs >= expiresAt
) {
return {
expiresAt: new Date(expiresAt).toISOString(),
latestInventoryAt: toIsoTimestamp(latestInventoryAt),
};
}
return null;
}
function normalizePreflight(entry) {
const payload = payloadOf(entry);
if (!payload) return null;
return {
request_id: payload.request_id || entry?.request_id || null,
idempotency_key: payload.idempotency_key || entry?.idempotency_key || null,
state: payload.state || null,
reason_code: payload.reason_code || null,
source_asset_id: payload.source_asset_id || null,
source_symbol: payload.source_symbol || null,
destination_asset_id: payload.destination_asset_id || null,
destination_symbol: payload.destination_symbol || null,
source_amount_units: payload.source_amount_units || null,
destination_amount_units: payload.destination_amount_units || null,
notional: payload.notional || null,
notional_asset_id: payload.notional_asset_id || null,
notional_symbol: payload.notional_symbol || null,
min_destination_amount_units: payload.min_destination_amount_units || null,
quoted_destination_amount_units: payload.quoted_destination_amount_units || null,
selected_quote: payload.selected_quote || null,
min_deadline_ms: payload.min_deadline_ms || null,
deadline_at: payload.deadline_at || null,
created_at: toIsoTimestamp(
payload.created_at
|| entry?.observed_at
|| entry?.ingested_at,
),
};
}
function normalizeSubmission(entry) {
const payload = payloadOf(entry);
if (!payload) return null;
return {
request_id: payload.request_id || entry?.request_id || null,
idempotency_key: payload.idempotency_key || entry?.idempotency_key || null,
submission_id: payload.submission_id || null,
status: payload.status || null,
result_code: payload.result_code || null,
quote_hash: payload.quote_hash || null,
intent_hash: payload.intent_hash || null,
destination_amount_units: payload.destination_amount_units || null,
submitted_at: toIsoTimestamp(
payload.submitted_at
|| entry?.observed_at
|| entry?.ingested_at,
),
relay_status: payload.relay_status || null,
relay_status_response: payload.relay_status_response || null,
status_checked_at: payload.status_checked_at || null,
};
}
function normalizeInventorySnapshot(entry) {
const payload = payloadOf(entry);
if (!payload?.spendable) return null;
return {
inventory_id: payload.inventory_id || null,
observed_at: toIsoTimestamp(
entry?.observed_at
|| entry?.ingested_at
|| payload.observed_at
|| payload.synced_at,
),
spendable: payload.spendable || {},
};
}
function latestSnapshotTimestamp(inventorySnapshots) {
const timestamps = inventorySnapshots
.map((entry) => normalizeInventorySnapshot(entry)?.observed_at)
.filter(Boolean)
.sort((left, right) => timestampValue(right) - timestampValue(left));
return timestamps[0] || null;
}
function payloadOf(entry) {
if (!entry) return null;
return entry.payload || entry;
}
function uniqueAssetIds(deltaRecords = []) {
const ids = new Set();
for (const record of deltaRecords || []) {
for (const assetId of Object.keys(record || {})) {
if (assetId) ids.add(assetId);
}
}
return [...ids];
}
function safeBigInt(value) {
if (value == null || value === '') return 0n;
return BigInt(String(value));
}
function toIsoTimestamp(value) {
if (!value) return null;
const date = new Date(value);
return Number.isNaN(date.getTime()) ? null : date.toISOString();
}
function timestampValue(value) {
const parsed = Date.parse(value || '');
return Number.isFinite(parsed) ? parsed : NaN;
}