diff --git a/harnesses/perp-fees/cmd/script/metrics.go b/harnesses/perp-fees/cmd/script/metrics.go index b1127391..6bcb6565 100644 --- a/harnesses/perp-fees/cmd/script/metrics.go +++ b/harnesses/perp-fees/cmd/script/metrics.go @@ -121,8 +121,24 @@ func init() { func recordSample(s PerpSample) { if s.Err != "" { - fetchErrorsCtr.WithLabelValues(s.Venue, s.Asset, classifyErr(s.Err)).Inc() + errType := classifyErr(s.Err) + fetchErrorsCtr.WithLabelValues(s.Venue, s.Asset, errType).Inc() healthGauge.WithLabelValues(s.Venue, s.Asset).Set(0) + // Permanent failures (asset delisted, unsupported venue) must delete + // the gauge series so stale values don't persist as ghost metrics. + // Transient failures (timeout, rate limit, server error) intentionally + // keep the last good value so a brief outage doesn't blank the board. + if errType == "not_found" { + allInGauge.DeleteLabelValues(s.Venue, s.Asset) + spreadGauge.DeleteLabelValues(s.Venue, s.Asset) + takerFeeGauge.DeleteLabelValues(s.Venue, s.Asset) + fundingGauge.DeleteLabelValues(s.Venue, s.Asset) + fetchLatencyGauge.DeleteLabelValues(s.Venue, s.Asset) + lastRefreshGauge.DeleteLabelValues(s.Venue, s.Asset) + for _, n := range tierNotionals { + allInTierGauge.DeleteLabelValues(s.Venue, s.Asset, notionalLabel(n)) + } + } return } takerFeeGauge.WithLabelValues(s.Venue, s.Asset).Set(s.TakerFeeBps) @@ -149,6 +165,8 @@ func recordSample(s PerpSample) { func classifyErr(msg string) string { switch { + case contains(msg, "asset_not_found") || contains(msg, "unsupported_venue"): + return "not_found" case contains(msg, "timeout"): return "timeout" case contains(msg, "401") || contains(msg, "403"): diff --git a/src/data/provider-registry.ts b/src/data/provider-registry.ts index c9ca3c02..ec533927 100644 --- a/src/data/provider-registry.ts +++ b/src/data/provider-registry.ts @@ -373,6 +373,34 @@ export const PROVIDER_REGISTRY: Record = { "Cross-chain intent protocol using the DLN solver network. Liquidity is filled by solvers on the destination chain, no wrapped assets or LP pools.", twitter: "@deBridgeFinance", }, + across: { + url: "https://across.to", + description: + "Optimistic cross-chain bridge by Risk Labs. Intents are filled by relayers who front liquidity on the destination, repaid from a canonical hub pool. Settlement is typically sub-30 seconds on liquid corridors.", + twitter: "@AcrossProtocol", + docs: "https://docs.across.to", + }, + mayan: { + url: "https://mayan.finance", + description: + "Cross-chain swap protocol using Wormhole messaging. Routes trades via a solver network (Mayan Swift) with optimistic confirmation, covering EVM and Solana corridors.", + twitter: "@MayanFinance", + docs: "https://docs.mayan.finance", + }, + squid: { + url: "https://squidrouter.com", + description: + "Cross-chain swap and liquidity routing built on Axelar. The Squid Router API sources routes across EVM chains via Axelar GMP, covering swaps and bridges in a single call.", + twitter: "@squidrouter", + docs: "https://docs.squidrouter.com", + }, + socket: { + url: "https://socket.tech", + description: + "Cross-chain interoperability protocol and bridge aggregator. The Socket API routes bridging and swap transactions across major EVM chains via underlying bridges including Across, Stargate, and CCTP.", + twitter: "@SocketDotTech", + docs: "https://docs.socket.tech", + }, "near-intents": { url: "https://near-intents.org", description: diff --git a/src/lib/logo-manifest.ts b/src/lib/logo-manifest.ts index ef38b348..30e2d749 100644 --- a/src/lib/logo-manifest.ts +++ b/src/lib/logo-manifest.ts @@ -108,6 +108,7 @@ const RAW: Record = { lighter: "/logos/lighter.svg", debridge: "/logos/debridge.svg", "near-intents": "/logos/near-intents.svg", + mayan: "/logos/mayan.svg", squid: "/logos/squid.svg", socket: "/logos/socket.webp", wormhole: "/logos/wormhole.png",