109 lines
5.0 KiB
Markdown
109 lines
5.0 KiB
Markdown
# 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-<name>` 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<string, Record<string, number>>` 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.
|