Skip to content

Route CoinGecko via Pro endpoint with proxy support and add health watchdogs#31

Merged
TaprootFreak merged 5 commits into
developfrom
feature/coingecko-pro-routing
May 10, 2026
Merged

Route CoinGecko via Pro endpoint with proxy support and add health watchdogs#31
TaprootFreak merged 5 commits into
developfrom
feature/coingecko-pro-routing

Conversation

@TaprootFreak

@TaprootFreak TaprootFreak commented May 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Pro keys were being sent to api.coingecko.com with the demo header, so calls counted against the anonymous IP-shared quota and ~47% of BTC fetches per day returned 429. WCBTC suspicious-liq-price detection silently degraded whenever the BTC spot cache went stale.
  • New COINGECKO_BASE_URL env var: when set, requests go to that origin (typically a caching pricing proxy that injects the upstream key itself) and no auth header is added. When unset, a Pro key in COINGECKO_API_KEY correctly routes to pro-api.coingecko.com with x-cg-pro-api-key. Anonymous public host stays as the no-key fallback.
  • Hourly staleness watchdog: if BTC spot has not refreshed for 60 min, raise a critical Telegram alert (re-arms every 6 h while the condition persists, clears on the next successful fetch).
  • Daily /api/v3/key probe: critical Telegram alert when the Pro account's monthly remaining call credit drops below 25k.

Resolution priority

  1. COINGECKO_BASE_URL set → proxy mode, no key sent from this service.
  2. COINGECKO_API_KEY set → pro-api.coingecko.com + x-cg-pro-api-key.
  3. Neither set → api.coingecko.com unauthenticated.

Hardening (follow-up commits in this PR)

  • btcLastSuccessMs now seeds with Date.now() at construction. With the earlier null default, a container that started while CoinGecko was down would never raise the staleness alert: the cron returned early on null and the gate could only open after a first success that wasn't arriving.
  • checkCoingeckoQuota now routes through resolveCoingeckoEndpoint(), so a proxy-mode deployment (no local API key, key held by the proxy) is still covered. The previous if (!apiKey) return silently skipped the daily probe in exactly the configuration this PR was meant to roll out.

Test plan

  • Build (npm run build) passes locally.
  • Deploy to dev with COINGECKO_BASE_URL=http://pricing-proxy:8080/coingecko and no COINGECKO_API_KEY — confirm BTC fetch logs show success and no 429 in the next 24 h.
  • Confirm the daily quota probe logs CoinGecko quota: ... remaining after deploy.
  • Force a stale BTC condition (e.g. set the proxy unreachable for >60 min) and confirm the staleness alert fires once and re-arms after 6 h.
  • Backwards compat: with only COINGECKO_API_KEY set (no base URL), confirm calls go to pro-api.coingecko.com.

…tchdogs

The price service was sending Pro keys to api.coingecko.com with the
demo header, so CoinGecko counted the calls against the anonymous
IP-shared quota and routinely returned 429 (~47% of BTC fetches per day).
WCBTC suspicious-liq-price detection silently degraded whenever the BTC
spot cache went stale.

- COINGECKO_BASE_URL env var lets a service be pointed at a caching
  pricing proxy that injects the upstream key itself; otherwise a Pro
  key in COINGECKO_API_KEY now correctly routes to pro-api.coingecko.com
  with x-cg-pro-api-key.
- @Cron hourly staleness watchdog: if BTC spot has not refreshed for
  60 min, raise a critical Telegram alert (re-arms every 6 h while the
  condition persists, clears on the next successful fetch).
- @Cron daily quota probe of /api/v3/key: alert when the Pro account's
  monthly remaining call credit drops below 25k, so the account does
  not silently run dry.
- btcLastSuccessMs now seeds with Date.now() at construction. With the
  earlier `null` default, a container that started while CoinGecko was
  down would never raise the staleness alert: the cron returned early on
  `null` and the gate could only open after a first success that wasn't
  arriving.
- checkCoingeckoQuota now routes through resolveCoingeckoEndpoint(), so
  a proxy-mode deployment (no local API key, key held by the proxy) is
  still covered. The previous `if (!apiKey) return` silently skipped
  the daily probe in exactly the configuration this PR was meant to roll
  out, leaving no signal when the Pro account approaches exhaustion.
- Single Date.now() per success path on the BTC fetch so the cache
  timestamp and the staleness anchor stay aligned.
The resolver previously returned \`https://api.coingecko.com\`
(anonymous public host) when neither COINGECKO_BASE_URL nor
COINGECKO_API_KEY was set. That is a silent fallback and exactly the
shape \"no fallbacks\" is meant to prevent: a misconfigured service
would happily come up, route every call through the IP-shared
anonymous quota, and surface only later as sporadic 429s.

The constructor now throws when both env vars are empty, and the
resolver no longer carries an unauthenticated branch. The only two
remaining paths are the explicit proxy origin and direct Pro.
COINGECKO_API_KEY is no longer read by this service. The proxy stack
holds the upstream key. The resolver now has a single path
(`COINGECKO_BASE_URL` only) and the constructor refuses to start
without it. No more two-branch logic, no more dead code.
@TaprootFreak TaprootFreak marked this pull request as ready for review May 10, 2026 19:51
Operators without a pricing-proxy can now point COINGECKO_BASE_URL at
pro-api.coingecko.com directly and supply COINGECKO_API_KEY — the key
is attached as the x-cg-pro-api-key header on every request when set,
no two-branch logic, no fallback. In the DFX setup COINGECKO_API_KEY
stays unset because the proxy injects its own key.

README documents the pattern and links the proxy reference
implementation at github.com/DFXswiss/pricing-proxy.
@TaprootFreak TaprootFreak merged commit 8ba7c0e into develop May 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant