Scope NEAR upstream pauses to tracked assets
Some checks failed
deploy / deploy (push) Failing after 37s

Proof: npm test; npm run operator-dashboard:build; live NEAR Intents status API normalized the current HOT destination-chain incident as operational for btc:mainnet and eth:100 tracked assets.

Assumptions: BSC, MONAD, XLAYER, PLASMA, POL, TON, OP, AVAX, STELLAR, and ADI incidents do not block our configured BTC/Gnosis NEAR Intents quote or executor paths unless the official incident also names core 1Click, solver, message bus, protocol-wide swaps, BTC, or Gnosis scope.

Still fake: Status relevance is text/service scoped to configured assets; it is not backed by a venue-provided per-asset machine-readable impact matrix.
This commit is contained in:
philipp 2026-05-07 18:16:59 +02:00
parent 4adc705a2b
commit 754a95c6d2
6 changed files with 320 additions and 21 deletions

View file

@ -576,6 +576,7 @@ async function loadNearIntentsStatus() {
postsResponse,
postEnumsResponse,
observedAt: new Date().toISOString(),
trackedAssets: config.trackedAssets,
});
}

View file

@ -631,6 +631,7 @@ async function pollNearIntentsEnvironmentStatus() {
postsResponse,
postEnumsResponse,
observedAt,
trackedAssets: config.trackedAssets,
});
state.near_intents_status = normalized;

View file

@ -12,6 +12,10 @@ export function normalizeNearIntentsStatus({
servicesResponse = null,
postEnumsResponse = null,
observedAt = new Date().toISOString(),
trackedAssets = [],
relevantChains = [],
relevantAssetIds = [],
relevantServiceNames = [],
} = {}) {
const posts = Array.isArray(postsResponse?.posts)
? postsResponse.posts
@ -41,31 +45,60 @@ export function normalizeNearIntentsStatus({
);
const serviceById = new Map(services.map((entry) => [entry.id, entry]));
const incidents = posts
const scope = buildRelevanceScope({
trackedAssets,
relevantChains,
relevantAssetIds,
relevantServiceNames,
});
const activeIncidents = posts
.map((post) => normalizePost(post, { statusById, severityById, serviceById }))
.filter(Boolean)
.filter((post) => post.active);
const relevantIncidents = activeIncidents.filter((incident) => (
isRelevantIncident(incident, scope)
));
const unrelatedIncidents = activeIncidents.filter((incident) => (
!isRelevantIncident(incident, scope)
));
const affectedServices = [...new Set(
incidents.flatMap((incident) => incident.impacts.map((impact) => impact.service_name || impact.service_id)),
relevantIncidents.flatMap((incident) => incident.impacts.map((impact) => (
impact.service_name || impact.service_id
))),
)].filter(Boolean);
const primaryIncident = incidents[0] || null;
const status = incidents.length > 0 ? 'disrupted' : 'operational';
const label = incidents.length > 0 ? 'upstream paused' : 'operational';
const decisiveReason = primaryIncident
? [primaryIncident.title, primaryIncident.message_text].filter(Boolean).join(': ')
: 'NEAR Intents status page reports no active incident.';
const quotingStopped = incidents.some((incident) => /1click|quoting|solver|swap/i.test(
`${incident.title || ''} ${incident.message_text || ''}`,
const observedAffectedServices = [...new Set(
activeIncidents.flatMap((incident) => incident.impacts.map((impact) => (
impact.service_name || impact.service_id
))),
)].filter(Boolean);
const primaryIncident = relevantIncidents[0] || null;
const primaryUnrelatedIncident = unrelatedIncidents[0] || null;
const status = relevantIncidents.length > 0 ? 'disrupted' : 'operational';
const label = relevantIncidents.length > 0 ? 'upstream paused' : 'operational';
const decisiveReason = buildDecisiveReason({
primaryIncident,
primaryUnrelatedIncident,
activeIncidentCount: activeIncidents.length,
scope,
});
const quotingStopped = relevantIncidents.some((incident) => /1click|quoting|solver|swap/i.test(
incidentText(incident),
));
const normalized = {
observed_at: observedAt,
source: 'near_intents_status_page',
status,
label,
current_incident_count: incidents.length,
current_incidents: incidents,
current_incident_count: relevantIncidents.length,
current_incidents: relevantIncidents,
affected_services: affectedServices,
observed_incident_count: activeIncidents.length,
observed_incidents: activeIncidents,
observed_affected_services: observedAffectedServices,
unrelated_incident_count: unrelatedIncidents.length,
unrelated_incidents: unrelatedIncidents,
relevance_scope: scope.publicScope,
quoting_stopped: quotingStopped,
decisive_reason: decisiveReason,
};
@ -95,6 +128,12 @@ export function buildNearIntentsStatusEventPayload(status, {
current_incident_count: status.current_incident_count || 0,
current_incidents: status.current_incidents || [],
affected_services: status.affected_services || [],
observed_incident_count: status.observed_incident_count || status.current_incident_count || 0,
observed_incidents: status.observed_incidents || status.current_incidents || [],
observed_affected_services: status.observed_affected_services || status.affected_services || [],
unrelated_incident_count: status.unrelated_incident_count || 0,
unrelated_incidents: status.unrelated_incidents || [],
relevance_scope: status.relevance_scope || null,
quoting_stopped: status.quoting_stopped ?? null,
};
}
@ -104,21 +143,160 @@ export function buildNearIntentsStatusFingerprint(status = {}) {
status: status.status || 'unknown',
label: status.label || null,
decisive_reason: status.decisive_reason || null,
current_incidents: (status.current_incidents || []).map((incident) => ({
id: incident.id || null,
title: incident.title || null,
status: incident.status || null,
severity: incident.severity || null,
last_update_at: incident.last_update_at || null,
message_text: incident.message_text || null,
impacts: incident.impacts || [],
})),
current_incidents: (status.current_incidents || []).map(stableIncident),
observed_incidents: (status.observed_incidents || status.current_incidents || []).map(stableIncident),
affected_services: [...(status.affected_services || [])].sort(),
observed_affected_services: [...(status.observed_affected_services || [])].sort(),
unrelated_incident_count: status.unrelated_incident_count || 0,
relevance_scope: status.relevance_scope || null,
quoting_stopped: status.quoting_stopped ?? null,
};
return crypto.createHash('sha256').update(JSON.stringify(stable)).digest('hex');
}
function stableIncident(incident) {
return {
id: incident.id || null,
title: incident.title || null,
status: incident.status || null,
severity: incident.severity || null,
last_update_at: incident.last_update_at || null,
message_text: incident.message_text || null,
impacts: incident.impacts || [],
};
}
function buildDecisiveReason({
primaryIncident,
primaryUnrelatedIncident,
activeIncidentCount,
scope,
}) {
if (primaryIncident) {
return [primaryIncident.title, primaryIncident.message_text].filter(Boolean).join(': ');
}
if (primaryUnrelatedIncident) {
const scopeText = scope.publicScope.terms.length
? scope.publicScope.terms.slice(0, 8).join(', ')
: 'unscoped deployment';
return [
`NEAR Intents status page has ${activeIncidentCount} active incident(s), but none match this deployment scope (${scopeText}).`,
[primaryUnrelatedIncident.title, primaryUnrelatedIncident.message_text].filter(Boolean).join(': '),
].filter(Boolean).join(' ');
}
return 'NEAR Intents status page reports no active incident.';
}
const CORE_INTENTS_SERVICE_PATTERN = /\b(1click|solver|message bus)\b/i;
const GLOBAL_INTENTS_DISRUPTION_PATTERN = /\b(1click|quoting|solver|message bus|protocol is paused|swaps are paused|all swaps)\b/i;
function isRelevantIncident(incident, scope) {
if (scope.unfiltered) return true;
const text = incidentText(incident);
if (GLOBAL_INTENTS_DISRUPTION_PATTERN.test(text)) return true;
const impactedServices = (incident.impacts || [])
.map((impact) => `${impact.service_name || ''} ${impact.service_id || ''}`.trim())
.filter(Boolean);
if (impactedServices.some((service) => CORE_INTENTS_SERVICE_PATTERN.test(service))) return true;
if (impactedServices.some((service) => scope.serviceNames.has(normalizeToken(service)))) return true;
return scope.termPatterns.some((pattern) => pattern.test(text));
}
function incidentText(incident) {
return [
incident?.title,
incident?.message_text,
...(incident?.impacts || []).map((impact) => impact.service_name || impact.service_id),
].filter(Boolean).join(' ');
}
function buildRelevanceScope({
trackedAssets = [],
relevantChains = [],
relevantAssetIds = [],
relevantServiceNames = [],
} = {}) {
const assets = Array.isArray(trackedAssets) ? trackedAssets : [];
const configuredChains = Array.isArray(relevantChains) ? relevantChains : [relevantChains];
const configuredAssetIds = Array.isArray(relevantAssetIds) ? relevantAssetIds : [relevantAssetIds];
const configuredServiceNames = Array.isArray(relevantServiceNames)
? relevantServiceNames
: [relevantServiceNames];
const chains = unique([
...configuredChains,
...assets.map((asset) => asset?.chain),
]);
const assetIds = unique([
...configuredAssetIds,
...assets.map((asset) => asset?.assetId),
]);
const serviceNames = new Set(configuredServiceNames.map(normalizeToken).filter(Boolean));
const terms = unique([
...chains.flatMap(chainTerms),
...assetIds.flatMap(assetTerms),
]).map(normalizeToken).filter(Boolean);
return {
unfiltered: assets.length === 0
&& configuredChains.filter(Boolean).length === 0
&& configuredAssetIds.filter(Boolean).length === 0
&& configuredServiceNames.filter(Boolean).length === 0,
termPatterns: terms.map(termPattern),
serviceNames,
publicScope: {
chains,
asset_ids: assetIds,
service_names: [...serviceNames],
terms,
},
};
}
function chainTerms(chain) {
const normalized = String(chain || '').trim().toLowerCase();
const terms = [normalized];
if (normalized === 'btc:mainnet') terms.push('btc', 'bitcoin');
const [, evmChainId] = normalized.match(/^eth:(\d+)$/) || [];
if (evmChainId === '1') terms.push('ethereum', 'eth mainnet');
if (evmChainId === '100') terms.push('gnosis', 'gno', 'xdai');
return terms;
}
function assetTerms(assetId) {
const normalized = String(assetId || '').trim().toLowerCase();
const terms = [normalized];
if (normalized.includes('nbtc') || normalized.includes('btc.')) {
terms.push('btc', 'bitcoin');
}
if (normalized.includes('gnosis')) {
terms.push('gnosis', 'gno', 'xdai');
}
return terms;
}
function termPattern(term) {
const escaped = escapeRegExp(term);
if (/^[a-z0-9]+$/i.test(term)) {
return new RegExp(`(^|[^a-z0-9])${escaped}([^a-z0-9]|$)`, 'i');
}
return new RegExp(escaped, 'i');
}
function normalizeToken(value) {
return String(value || '').trim().toLowerCase();
}
function unique(values) {
return [...new Set((values || []).filter(Boolean))];
}
function escapeRegExp(value) {
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function normalizePost(post, { statusById, severityById, serviceById }) {
if (!post || typeof post !== 'object') return null;
const latestUpdate = post.latest_update || [...(post.updates || [])].pop() || {};

View file

@ -1625,6 +1625,9 @@ function resolveServiceUpstreamStatus(service, nearIntentsStatus) {
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,
};
}

View file

@ -20,9 +20,21 @@ const servicesResponse = {
services: [
{ id: 'PXQFSY1', name: 'Cross-Chain Bridging', display_name: 'Cross-Chain Bridging' },
{ id: 'PLT88AT', name: 'Solvers Network', display_name: 'Solvers Network' },
{ id: 'PNEJBRE', name: 'Other Blockchains', display_name: 'Other Blockchains' },
],
};
const trackedAssets = [
{
assetId: 'nep141:nbtc.bridge.near',
chain: 'btc:mainnet',
},
{
assetId: 'nep141:gnosis-0x420ca0f9b9b604ce0fd9c18ef134c705e5fa3430.omft.near',
chain: 'eth:100',
},
];
test('NEAR Intents status normalizer exposes current quoting disruption as upstream paused evidence', () => {
const normalized = normalizeNearIntentsStatus({
observedAt: '2026-04-16T12:40:00.000Z',
@ -85,6 +97,40 @@ test('resolved NEAR Intents status posts do not make the relay look disrupted',
assert.match(normalized.decisive_reason, /no active incident/i);
});
test('scoped NEAR Intents status ignores unrelated paused destination chains', () => {
const normalized = normalizeNearIntentsStatus({
observedAt: '2026-05-07T15:55:00.000Z',
servicesResponse,
postEnumsResponse,
trackedAssets,
postsResponse: {
posts: [{
id: 'PO2LXSS',
title: 'BSC, TON, XML destinations are temporarily paused',
post_type: 'incident',
latest_update: {
status_id: 'PSCS3IV',
severity_id: 'P187122',
reported_at: 1778165160000,
impacts: [{ service_id: 'PNEJBRE', severity_id: 'PCIGMKW' }],
message: '<p>HOT bridge is having a reliability incident. Full list of chains: BSC, MONAD, XLAYER, PLASMA, POL, TON, OP, AVAX, STELLAR, ADI</p>',
},
}],
},
});
assert.equal(normalized.status, 'operational');
assert.equal(normalized.label, 'operational');
assert.equal(normalized.current_incident_count, 0);
assert.equal(normalized.observed_incident_count, 1);
assert.equal(normalized.unrelated_incident_count, 1);
assert.equal(normalized.quoting_stopped, false);
assert.deepEqual(normalized.affected_services, []);
assert.deepEqual(normalized.observed_affected_services, ['Other Blockchains']);
assert.match(normalized.decisive_reason, /none match this deployment scope/);
assert.match(normalized.decisive_reason, /BSC, TON, XML/);
});
test('NEAR Intents status fingerprint is stable across polls and changes on official updates', () => {
const first = normalizeNearIntentsStatus({

View file

@ -1735,6 +1735,76 @@ test('dashboard surfaces NEAR upstream disruption without calling submitted work
});
test('dashboard does not pause relay services for unrelated destination-chain incident', () => {
const config = buildConfig();
const nearIntentsStatus = {
source: 'near_intents_status_page',
status: 'operational',
label: 'operational',
observed_at: '2026-05-07T15:55:00.000Z',
decisive_reason: 'NEAR Intents status page has 1 active incident(s), but none match this deployment scope.',
current_incident_count: 0,
affected_services: [],
observed_incident_count: 1,
observed_affected_services: ['Other Blockchains'],
unrelated_incident_count: 1,
quoting_stopped: false,
};
const dashboard = buildDashboardBootstrap({
config,
auth: { authenticated: true },
portfolioMetric: null,
inventorySnapshot: null,
marketPrice: null,
recentQuotes: [],
submissionPage: { page: 1, page_size: 20, total: 0, total_pages: 1, items: [] },
submissionSummary: { total: 0, last_submission_at: null },
fundingObservations: [],
recentDepositStatuses: [],
recentTradeDecisions: [],
recentExecuteTradeCommands: [],
recentExecutionResults: [],
recentQuoteOutcomes: [],
recentIntentRequests: [],
recentAlertTransitions: [],
nearIntentsStatus,
serviceSnapshots: [
{
service: 'near-intents-ingest',
label: 'NEAR Intents Ingest',
base_url: 'http://near-intents-ingest',
reachable: true,
health: { ok: true },
state: { ingest: { connected: true, last_message_at: '2026-05-07T15:54:59.000Z' } },
},
{
service: 'trade-executor',
label: 'Trade Executor',
base_url: 'http://trade-executor',
reachable: true,
health: { ok: true },
state: { relay: { connected: true, last_message_at: '2026-05-07T15:54:59.000Z' } },
},
],
});
assert.equal(dashboard.status_bar.near_intents_upstream_status, 'operational');
assert.match(dashboard.status_bar.near_intents_upstream_reason, /none match this deployment scope/);
const services = Object.fromEntries(
dashboard.system.service_health.map((service) => [service.service, service]),
);
assert.equal(services['near-intents-ingest'].health_status, 'online');
assert.equal(services['near-intents-ingest'].health_label, 'online');
assert.equal(services['near-intents-ingest'].health_ok, true);
assert.equal(services['trade-executor'].health_status, 'online');
assert.equal(services['trade-executor'].health_label, 'online');
assert.equal(services['trade-executor'].upstream_status.status, 'operational');
assert.equal(services['trade-executor'].upstream_status.unrelated_incident_count, 1);
});
test('bootstrap exposes deduped environment status history as environmental conditions', () => {
const config = buildConfig();
const dashboard = buildDashboardBootstrap({