first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
---
name: sp-currency-provider-binance
description: Binance public-ticker crypto-rates provider implementing the CurrencyProvider contract for @suaveplan/currency.
---
# Binance Currency Provider for Suaveplan
Binance public-ticker crypto-rates provider that implements the `CurrencyProvider`
contract from `@suaveplan/currency` on top of the slim `@suaveplan/currency-providers`
core. Fetches from Binance's free, key-less `/api/v3/ticker/price` endpoint and
returns inverted spot mid-prices (`1 / price`) ready for `CurrencyConverter`.
## Architecture
| Component | Responsibility |
|-----------|---------------|
| `BinanceProvider` | Main class implementing `CurrencyProvider`; fetches ticker data, decodes rates, and returns `RateFetchResult` |
| `BINANCE_PROVIDER_NAME` | Stable identifier constant (`"binance"`) used as registry key and `provider.name` |
| `BINANCE_DEFAULT_BASE_URL` | Default REST base URL (`"https://api.binance.com/api/v3"`) overridable via constructor |
| `ensureTickerArray` | Validates the upstream JSON payload is an array of `{ symbol: string; price: string }` rows; throws `UPSTREAM_ERROR` on mismatch |
| `decodeRates` | Parses ticker rows, filters by base suffix (`endsWith`), inverts prices (`1 / price`), and silently skips zero/negative/non-finite prices |
| `filterTargets` | When `options.targets` is provided, restricts the rate map to requested currencies and throws `UNKNOWN_CURRENCY` for any missing target |
## File Structure
```
money/finance/currency-provider-binance/
src/binance/binance.ts # BinanceProvider class, constants, and internal helpers
src/binance/index.ts # Barrel re-exporting the binance module
src/index.ts # Root barrel re-exporting from binance/
```
Sibling packages:
- `@suaveplan/currency` — universal currency conversion engine (`CurrencyConverter`, `RateCache`, `CurrencyProvider` interface)
- `@suaveplan/currency-providers` — slim core providing `CurrencyProviderRegistry`, `httpRequest`, `CurrencyProviderError`, and shared config types (`NoKeyConfig`, `ApiKeyConfig`)
- `@suaveplan/currency-provider-ecb` — fiat provider sibling using the European Central Bank daily rates
- `@suaveplan/currency-provider-coingecko` — crypto provider sibling using CoinGecko's API
## Patterns
### Quick Start
```typescript
import { CurrencyConverter } from "@suaveplan/currency";
import { BinanceProvider } from "@suaveplan/currency-provider-binance";
const provider = new BinanceProvider();
const converter = new CurrencyConverter({
provider,
staleDurationMs: 30_000,
});
const rate = await converter.getRate("USDT", "BTC");
```
### Registry Registration
```typescript
import { CurrencyProviderRegistry } from "@suaveplan/currency-providers";
import { BinanceProvider, BINANCE_PROVIDER_NAME } from "@suaveplan/currency-provider-binance";
const registry = new CurrencyProviderRegistry();
registry.register(new BinanceProvider());
const result = await registry
.get(BINANCE_PROVIDER_NAME)
.fetchRates({ base: "USDT", targets: ["BTC", "ETH", "SOL"] });
```
### Injectable Fetch for Testing or Proxies
```typescript
import type { FetchLike } from "@suaveplan/types";
const proxiedFetch: FetchLike = async (url, init) => {
return await fetch(`https://proxy.example.com/${String(url)}`, init);
};
const binance = new BinanceProvider({
fetch: proxiedFetch,
baseUrl: "https://api.binance.com/api/v3",
timeoutMs: 8_000,
});
```
### Custom Base Suffix
```typescript
const provider = new BinanceProvider();
// Fetch EUR-quoted pairs instead of the default USDT
const result = await provider.fetchRates({ base: "EUR" });
// result.rates.BTC === 1 / 62000 (from BTC/EUR ticker)
```
## Constraints
- `supportsCrypto === true`, `supportsFiat === false` — this provider only covers crypto assets listed on Binance spot markets
- No API key required; the provider uses Binance's public `/api/v3/ticker/price` endpoint
- Rates are stored as `1 / price` so the result answers "1 unit of base = N units of asset"
- Zero, negative, and non-finite ticker prices are silently skipped (not surfaced as errors) to handle delisted-but-published symbols
- When `options.targets` is provided, any target absent from the upstream listing raises `UNKNOWN_CURRENCY` instead of being omitted
- Symbols whose entire name equals the base suffix (e.g. `USDTUSDT`) are defensively skipped to avoid empty-string rate keys
- Transport errors (non-2xx) are raised as `CurrencyProviderError` with code `HTTP_ERROR` by the shared `httpRequest` helper
## Checklist
1. Import from `@suaveplan/currency-provider-binance` for the root barrel, or `@suaveplan/currency-provider-binance/binance/binance` for the subpath export
2. Pass an injectable `FetchLike` in the constructor when testing or proxying
3. Use `targets` to restrict the returned rate map and surface missing pairs as typed errors
4. Remember prices are inverted (`1 / price`) — expect small numbers for expensive assets
5. Verify `provider.supportsFiat` is `false` before using this provider for fiat conversions