7.5 KiB
Add @suaveplan/currency-provider-binance — Binance public-ticker provider
Package: @suaveplan/currency-provider-binance
Status: ARCHIVED
Type: feature (sibling provider in the slim currency-providers wave)
- Design: ./design.md
- Spec delta: ./specs/currency-provider-binance/spec.md
Why
The 2026-05-17 slim refactor of @suaveplan/currency-providers removed
nine concrete provider classes from the umbrella package and committed
to landing each as its own @suaveplan/currency-provider-<name>
sibling. BinanceProvider is the Binance leg of that wave. It is the
key-less crypto-rates provider in the line-up — alongside CoinGecko,
CryptoCompare, and Kraken — and the only one that ships sub-second
trading-desk pricing. Without it the slim core has no working crypto
provider out of the box; downstream agents would have to hand-roll one,
which is exactly the bug the slim refactor exists to fix.
This change ports the Binance implementation that previously lived at
packages/finance/currency-providers/src/crypto/binance.ts upstream
into the genesis monorepo as a standalone package, with three quality
improvements over the upstream form:
- HTTP transport is delegated to the slim core's
httpRequesthelper, soHTTP_ERRORcarries the typedCurrencyProviderErrorshape instead of a plainErrorand abort/timeout composition is uniform across every sibling. - Failure paths are split into the four typed codes the slim spec
defines (
HTTP_ERROR,PARSE_ERROR,UPSTREAM_ERROR,UNKNOWN_CURRENCY) — the upstream form raised a single plainErroron the HTTP path and silently dropped malformed payloads or missing targets. - Payload shape is structurally validated (array of
{ symbol: string; price: string }); upstream took the wire shape on faith and wouldTypeErroron a Cloudflare error page.
What changes
ADDED
BinanceProvider— class implementing theCurrencyProvidercontract on top of the Binance public spot ticker (GET /api/v3/ticker/price).BINANCE_PROVIDER_NAME("binance") andBINANCE_DEFAULT_BASE_URL("https://api.binance.com/api/v3") constants so consumers can wire the registry and overrides without string literals.
NOT INCLUDED (out of scope — same deferrals as the slim core)
- WebSocket streaming (Binance
/ws/!ticker@arr) — REST-only for v1 - Historical klines / candlesticks
- Authenticated endpoints (account, orders) — non-goal for a
CurrencyProvider - Per-call rate-limit accounting — deferred to the future
RateLimitedProviderwrapper from the slim core
Source mapping
| Module | Upstream source |
|---|---|
BinanceProvider |
../suaveplan/packages/finance/currency-providers/src/crypto/binance.ts |
| Sibling-shape contract | openspec/archive/2026-05-17-slim-currency-providers/design.md |
| Slim core surface consumed | packages/finance/currency-providers/src/{http,error-codes,types}/ |
The new genesis-original wrappers (httpRequest delegation, typed
PARSE_ERROR / UPSTREAM_ERROR / UNKNOWN_CURRENCY paths, structural
payload validation) are listed in the design rationale; they have no
upstream analogue because upstream raised plain Error and ignored
payload shape.
Parity Checklist
| Capability | Upstream | Migrated | Notes |
|---|---|---|---|
Class-based BinanceProvider implements CurrencyProvider |
✅ | ✅ | Identical constructor shape; takes NoKeyConfig now (super-set of upstream's optional { baseUrl }). |
name === "binance", supportsCrypto === true, supportsFiat === false |
✅ | ✅ | Verbatim. Exposed as BINANCE_PROVIDER_NAME for registry wiring. |
Default base URL https://api.binance.com/api/v3 |
✅ | ✅ | Exported as BINANCE_DEFAULT_BASE_URL constant. |
Custom baseUrl override |
✅ | ✅ | Via NoKeyConfig.baseUrl. |
GET /ticker/price fetch |
✅ | ✅ | Now via slim-core httpRequest. |
Default base USDT |
✅ | ✅ | Verbatim. |
Suffix-match decoding (BTCUSDT → BTC when base is USDT) |
✅ | ✅ | Plus a defensive length check that rejects symbols equal to the suffix. |
| Skip zero-price tickers | ✅ | ✅ | Verbatim. |
| Skip negative-price / non-finite tickers | 🚫 | ⬆️ | Upstream only checked price > 0 — fails open on NaN. Genesis form adds Number.isFinite. |
Optional targets filter |
✅ | ⬆️ | Upstream silently dropped missing targets; genesis raises UNKNOWN_CURRENCY so consumers learn about upstream listing changes immediately. |
Plain Error on non-2xx HTTP |
✅ | 🚫 | Replaced by typed CurrencyProviderError(HTTP_ERROR) raised by the slim core's httpRequest. Reason: the slim core's contract requires typed errors from every sibling. |
| Structural payload validation | 🚫 | ⬆️ | Upstream cast the response shape without checking; genesis raises UPSTREAM_ERROR on malformed rows. |
PARSE_ERROR on non-JSON body |
🚫 | ⬆️ | Upstream would leak a runtime SyntaxError; genesis wraps response.json() in a typed catch. |
Injectable FetchLike |
🚫 | ⬆️ | Upstream used globalThis.fetch directly; genesis form takes a NoKeyConfig.fetch override for tests / proxies / edge runtimes. |
| Optional per-request timeout | 🚫 | ⬆️ | New: timeoutMs flows through to httpRequest and composes with the caller's signal. |
AbortSignal propagation from RateFetchOptions |
🚫 | ⬆️ | Upstream did not honour options.signal; genesis forwards it through httpRequest. |
| Upstream test suite (vi.spyOn-based fetch mock) | ✅ | ⬆️ | Rewritten to @suaveplan/testing/runner with a recording FetchLike closure — no vi.mock, no vi.spyOn. |
Sibling .md per source file + ≥200-word README |
🚫 | ⬆️ | Upstream binance.md was 46 lines; genesis form ships the §4 doc contract. |
No 🚫 row is left without justification.
Impact
Affected dependents
Run bunx turbo ls --filter=...@suaveplan/currency-provider-binance to
enumerate.
- No current dependents (verified by
bunx turbo ls --filter=...@suaveplan/currency-provider-binance— this is a new package and downstream commerce stacks consume it through theCurrencyProviderRegistrylookup, not by direct import). @suaveplan/currency-providers— [ ] tests run / [x] docs reviewed / [x] no action needed (this package consumes the slim core; the slim core has no knowledge of any sibling and was not modified)@suaveplan/currency— [ ] tests run / [x] docs reviewed / [x] no action needed (we depend on theCurrencyProvidercontract — the contract is unchanged)
Migration
There is no migration path because this is a new package. Consumers
that previously used the upstream BinanceProvider from the
pre-slim-refactor @suaveplan/currency-providers (which has never
shipped on genesis) replace:
// Before — upstream pre-slim-refactor form
import { BinanceProvider } from "@suaveplan/currency-providers";
with:
// After — genesis sibling form
import { BinanceProvider } from "@suaveplan/currency-provider-binance";
Constructor signatures are compatible: new BinanceProvider() and
new BinanceProvider({ baseUrl: "…" }) both work unchanged. New
capabilities (fetch, timeoutMs) are additive and optional.
Out of scope
- The other eight sibling provider packages — each lands under its own OpenSpec change per AGENTS.md §0c.
- Streaming / WebSocket / authenticated endpoints — REST-only for v1.
- Retry orchestration, circuit breaker, rate-limit accounting — those
ship as wrapper providers consuming the
CurrencyProvidercontract.