From 079108c206970b0e0819f0a1223ed42d8e241259 Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Tue, 28 Jul 2026 14:44:37 +0200 Subject: [PATCH 1/2] perp-fees: delete gauges on asset_not_found (Lighter ghost metrics) (#1621) * perp-fees: delete gauges on asset_not_found to prevent ghost metrics * add mayan/squid/socket/across to provider registry and logo manifest --- harnesses/perp-fees/cmd/script/metrics.go | 20 +++++++++++++++- src/data/provider-registry.ts | 28 +++++++++++++++++++++++ src/lib/logo-manifest.ts | 1 + 3 files changed, 48 insertions(+), 1 deletion(-) 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: "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/logos/lighter.svg", debridge: "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/logos/debridge.svg", "near-intents": "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/logos/near-intents.svg", + mayan: "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/logos/mayan.svg", squid: "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/logos/squid.svg", socket: "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/logos/socket.webp", wormhole: "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/logos/wormhole.png", From 8232eecbfeef845bf4096b23f631dab8ed3df660 Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Tue, 28 Jul 2026 14:48:09 +0200 Subject: [PATCH 2/2] perp-fees: fix GMX subsquid schema + edgeX depth level (#1622) GMX: Subsquid removed positionFeeFactor field; query was 400ing on every tick. Remove the dropped field and its Go fallback. edgeX: depth endpoint only accepts level=15 or 200, not 100. The invalid level returned empty_orderbook on every tick. --- harnesses/perp-fees/cmd/script/edgex.go | 2 +- harnesses/perp-fees/cmd/script/gmx.go | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/harnesses/perp-fees/cmd/script/edgex.go b/harnesses/perp-fees/cmd/script/edgex.go index 1b29b527..5cfb9a3f 100644 --- a/harnesses/perp-fees/cmd/script/edgex.go +++ b/harnesses/perp-fees/cmd/script/edgex.go @@ -59,7 +59,7 @@ func fetchEdgex(v VenueConfig) PerpSample { s.TakerFeeBps = edgexTakerBps var book edgexDepthResp - url := fmt.Sprintf("%s/api/v1/public/quote/getDepth?contractId=%s&level=100", edgexBase, contractID) + url := fmt.Sprintf("%s/api/v1/public/quote/getDepth?contractId=%s&level=200", edgexBase, contractID) if err := edgexGet(client, url, &book); err != nil { s.Err = fmt.Sprintf("orderbook: %v", err) s.FetchLatencyMs = time.Since(start).Milliseconds() diff --git a/harnesses/perp-fees/cmd/script/gmx.go b/harnesses/perp-fees/cmd/script/gmx.go index 5bd6637b..01f773fe 100644 --- a/harnesses/perp-fees/cmd/script/gmx.go +++ b/harnesses/perp-fees/cmd/script/gmx.go @@ -41,10 +41,6 @@ type gmxMarketInfo struct { ID string `json:"id"` PositionFeeFactorForPositiveImpact string `json:"positionFeeFactorForPositiveImpact"` PositionFeeFactorForNegativeImpact string `json:"positionFeeFactorForNegativeImpact"` - // Some Subsquid deployments expose an unsuffixed field name. - // We fall back to it when the impact-branch fields are absent - // so a schema rename does not silently zero the fee. - PositionFeeFactor string `json:"positionFeeFactor"` } `json:"marketInfos"` } `json:"data"` Errors []struct { @@ -80,7 +76,7 @@ func fetchGMX(v VenueConfig) PerpSample { // 1) Position fee from Subsquid (live on-chain factor) q := gmxGqlReq{ Query: `query { marketInfos(where: {id_eq: "` + market + `"}) { - id positionFeeFactorForPositiveImpact positionFeeFactorForNegativeImpact positionFeeFactor + id positionFeeFactorForPositiveImpact positionFeeFactorForNegativeImpact }}`, } bodyBytes, _ := json.Marshal(q) @@ -126,9 +122,6 @@ func fetchGMX(v VenueConfig) PerpSample { // If the schema is on an older/unsuffixed version we fall back to // positionFeeFactor so a rename does not silently zero the reported fee. rawFactor := m.PositionFeeFactorForNegativeImpact - if rawFactor == "" { - rawFactor = m.PositionFeeFactor - } if rawFactor == "" { s.Err = "subsquid_failed: missing positionFeeFactor fields" s.FetchLatencyMs = time.Since(start).Milliseconds()