Some checks failed
deploy / deploy (push) Failing after 43s
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.
522 lines
18 KiB
JavaScript
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;
|
|
}
|