# Add @suaveplan/currency-provider-coingecko **Package**: `@suaveplan/currency-provider-coingecko` **Status**: PROPOSED **Type**: new package (sibling-plugin landing from the 2026-05-17 `slim-currency-providers` refactor wave) - **Spec delta**: ./specs/currency-provider-coingecko/spec.md ## Why `@suaveplan/currency-providers` was slimmed on 2026-05-17 to a thin foundation shipping the `CurrencyProvider` contract, `CurrencyProviderRegistry`, `defineCurrencyProvider` factory, shared `httpRequest` + `composeAbortSignal` HTTP helper, the `ApiKeyConfig` / `NoKeyConfig` config shapes, and the typed `CurrencyProviderError` + `CURRENCY_PROVIDER_CODES` registry. The nine concrete providers (ECB, Fixer, OXR, Binance, CoinGecko, CoinMarketCap, CryptoCompare, Kraken, Virtual) moved to sibling `@suaveplan/currency-provider-` packages so consumers install only what they wire in and each provider iterates on its own version cadence. This change lands the CoinGecko sibling. CoinGecko exposes a free public endpoint (no key, rate-limited at 10–30 calls/min) and a paid Pro tier that accepts the API key via an `x-cg-pro-api-key` header. Both shapes share the same request URL pattern (`/simple/price?ids=...&vs_currencies=...`) and the same response shape, so the key is genuinely optional rather than mutually exclusive — consumers upgrade by passing `apiKey` without otherwise changing their wiring. ## What changes ### ADDED - `CoinGeckoProvider` class implementing `CurrencyProvider from @suaveplan/currency` — `name = "coingecko"`, `supportsCrypto = true`, `supportsFiat = true`. Constructor accepts an optional config object of shape `NoKeyConfig & { readonly apiKey?: string }`; the `apiKey` field is truly optional (free tier requires nothing). - `fetchRates(options?)` issues a single request through the shared `httpRequest()` helper, attaches the `x-cg-pro-api-key` header iff `apiKey` was supplied, parses the JSON body, inverts the upstream price ("1 crypto = X fiat") into the converter's rate convention ("1 base = X crypto"), and filters to `options.targets` when given. - `getCoinGeckoId(symbol)` lookup helper exposing the curated symbol → CoinGecko-ID mapping (BTC, ETH, SOL, USDT, USDC, BNB, XRP, ADA, DOGE, DOT, AVAX, MATIC, LINK, UNI, ATOM, LTC). Case-insensitive; returns `undefined` for unknown symbols. - Failure paths raise `CurrencyProviderError`: - `HTTP_ERROR` — non-2xx response (delegated to `httpRequest()`). - `PARSE_ERROR` — response body is not valid JSON. - `UPSTREAM_ERROR` — JSON parses but is not the expected `Record>` shape. - `UNKNOWN_CURRENCY` — caller-supplied `options.targets` includes a symbol with no CoinGecko-ID mapping; raised proactively before the HTTP call so the diagnostic surfaces the bad symbol rather than a silent empty `rates` field. ## Source mapping | Migrated module | Upstream source | |---|---| | `CoinGeckoProvider`, `getCoinGeckoId` | `../suaveplan/packages/finance/currency-providers/src/crypto/coingecko.ts` | Upstream used a bare `fetch` call, bare `Error`, and no key support. This migration upgrades all three: routes through the shared `httpRequest` helper (so abort-signal composition + per-call timeouts + typed `HTTP_ERROR` come for free), raises typed `CurrencyProviderError`s with stable codes (so consumers can dispatch on `err.code`), and adds the optional `apiKey` field with `x-cg-pro-api-key` header attachment for the Pro tier. ## Impact ### Affected dependents Run `bunx turbo ls --filter=...@suaveplan/currency-provider-coingecko` to enumerate. As this is a new package, the dependent list is empty at landing. - No current dependents (new package; verified by `bunx turbo ls --filter=...@suaveplan/currency-provider-coingecko`). The package consumes (does not modify): - `@suaveplan/currency` — `CurrencyProvider`, `RateFetchOptions`, `RateFetchResult` contract types only. [x] no action needed (contract consumer, not modifier). - `@suaveplan/currency-providers` — `httpRequest`, `composeAbortSignal`, `CurrencyProviderError`, `CURRENCY_PROVIDER_CODES`, `NoKeyConfig`. [x] no action needed (slim core is the surface this package is built against). - `@suaveplan/types` — `FetchLike`. [x] no action needed. - `@suaveplan/error` — transitively via `CurrencyProviderError`'s base class. [x] no action needed. ## Out of scope - Historical-rate queries (`/coins/{id}/history`), trending coins, market cap data — `CurrencyProvider` only contracts for spot rates. - WebSocket / push subscriptions — CoinGecko does not offer a public WebSocket; sibling providers that do (Binance, Kraken) own that surface. - Auto-discovery of CoinGecko IDs beyond the curated 16-entry map — consumers needing a different symbol register it via their own wrapper or open a follow-up change extending the map. - Rate-limit awareness, retry, OTel instrumentation — same deferral as the slim core and every other sibling provider; once those wrappers land they apply uniformly via the registry.