Skip to content

Commit 15a8fcc

Browse files
committed
Implement V12 support for TypeScript.
1 parent 75423f2 commit 15a8fcc

File tree

12 files changed

+317
-41
lines changed

12 files changed

+317
-41
lines changed

typescript/README.md

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ TypeScript SDK for accessing Chainlink Data Streams with real-time streaming and
2727
- **Real-time streaming** via WebSocket connections
2828
- **High Availability mode** with multiple connections and automatic failover
2929
- **Historical data access** via REST API
30-
- **Automatic report decoding** for all supported formats (V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V13)
30+
- **Automatic report decoding** for all supported formats (V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13)
3131
- **Metrics** for monitoring and observability
3232
- **Type-safe** with full TypeScript support
3333
- **Event-driven architecture** for complete developer control
@@ -48,12 +48,14 @@ npm install @chainlink/data-streams-sdk
4848
**Set your credentials:**
4949

5050
Option 1 - Environment variables:
51+
5152
```bash
5253
export API_KEY="your_api_key_here"
5354
export USER_SECRET="your_user_secret_here"
5455
```
5556

5657
Option 2 - `.env` file:
58+
5759
```bash
5860
# Create .env file from template
5961
cp .env.example .env
@@ -64,6 +66,7 @@ USER_SECRET="your_user_secret_here"
6466
```
6567

6668
**Basic streaming:**
69+
6770
```typescript
6871
import { createClient, LogLevel } from '@chainlink/data-streams-sdk';
6972

@@ -104,17 +107,17 @@ interface Config {
104107
userSecret: string; // User secret for authentication
105108
endpoint: string; // REST API URL
106109
wsEndpoint: string; // WebSocket URL
107-
110+
108111
// Optional - Request & Retry
109112
timeout?: number; // Request timeout (default: 30000ms)
110113
retryAttempts?: number; // Retry attempts (default: 3)
111114
retryDelay?: number; // Retry delay (default: 1000ms)
112-
115+
113116
// Optional - High Availability
114117
haMode?: boolean; // Enable HA mode (default: false)
115118
haConnectionTimeout?: number; // HA connection timeout (default: 10000ms)
116119
connectionStatusCallback?: (isConnected: boolean, host: string, origin: string) => void;
117-
120+
118121
// Optional - Logging
119122
logging?: LoggingConfig; // See Logging Configuration section
120123
}
@@ -148,6 +151,7 @@ const haClient = createClient({
148151
## Examples
149152

150153
**Quick Commands:**
154+
151155
```bash
152156
# Real-time streaming
153157
npx ts-node examples/stream-reports.ts 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782
@@ -166,7 +170,7 @@ npx ts-node examples/list-feeds.ts
166170
See [`examples/README.md`](./examples/README.md) for detailed usage instructions, setup, and all available examples including:
167171

168172
- **Streaming:** Basic streaming, HA mode, metrics monitoring
169-
- **REST API:** Latest reports, historical data, bulk operations, feed management
173+
- **REST API:** Latest reports, historical data, bulk operations, feed management
170174
- **Configuration:** Logging setup, debugging, monitoring integration
171175

172176
## API Reference
@@ -191,7 +195,6 @@ await stream.close();
191195
const metrics = stream.getMetrics();
192196
```
193197
194-
195198
### Stream Options
196199
197200
```typescript
@@ -235,8 +238,9 @@ const decoded = decodeReport(report.fullReport, report.feedID);
235238
### Schema Auto-Detection
236239
237240
The SDK automatically detects and decodes all report versions based on Feed ID patterns:
241+
238242
- **V2**: Feed IDs starting with `0x0002`
239-
- **V3**: Feed IDs starting with `0x0003` (Crypto Streams)
243+
- **V3**: Feed IDs starting with `0x0003` (Crypto Streams)
240244
- **V4**: Feed IDs starting with `0x0004` (Real-World Assets)
241245
- **V5**: Feed IDs starting with `0x0005`
242246
- **V6**: Feed IDs starting with `0x0006` (Multiple Price Values)
@@ -245,14 +249,16 @@ The SDK automatically detects and decodes all report versions based on Feed ID p
245249
- **V9**: Feed IDs starting with `0x0009` (NAV Fund Data)
246250
- **V10**: Feed IDs starting with `0x000a` (Tokenized Equity)
247251
- **V11**: Feed IDs starting with `0x000b` (Deutsche Boerse)
252+
- **V12**: Feed IDs starting with `0x000c` (Nav Fund Data + Next)
248253
- **V13**: Feed IDs starting with `0x000d` (Best Bid/Ask)
249254
250255
### Common Fields
251256
252257
All reports include standard metadata:
258+
253259
```typescript
254260
interface BaseFields {
255-
version: "V2" | "V3" | "V4" | "V5" | "V6" | "V7" | "V8" | "V9" | "V10" | "V11" | "V13";
261+
version: "V2" | "V3" | "V4" | "V5" | "V6" | "V7" | "V8" | "V9" | "V10" | "V11" | "V12" | "V13";
256262
nativeFee: bigint;
257263
linkFee: bigint;
258264
expiresAt: number;
@@ -265,7 +271,7 @@ interface BaseFields {
265271
### Schema-Specific Fields
266272
267273
- **V2/V3/V4**: `price: bigint` - Standard price data
268-
- **V3**: `bid: bigint, ask: bigint` - Crypto bid/ask spreads
274+
- **V3**: `bid: bigint, ask: bigint` - Crypto bid/ask spreads
269275
- **V4**: `marketStatus: MarketStatus` - Real-world asset market status
270276
- **V5**: `rate: bigint, timestamp: number, duration: number` - Interest rate data with observation timestamp and duration
271277
- **V6**: `price: bigint, price2: bigint, price3: bigint, price4: bigint, price5: bigint` - Multiple price values in a single payload
@@ -274,6 +280,7 @@ interface BaseFields {
274280
- **V9**: `navPerShare: bigint, navDate: number, aum: bigint, ripcord: number` - NAV fund data
275281
- **V10**: `price: bigint, lastUpdateTimestamp: number, marketStatus: MarketStatus, currentMultiplier: bigint, newMultiplier: bigint, activationDateTime: number, tokenizedPrice: bigint` - Tokenized equity data
276282
- **V11**: `mid: bigint, LastSeenTimestampNs: number, bid: bigint, vidVolume: number, ask: bigint, askVolume: number, lastTradedPrice: bigint, marketStatus: MarketStatus` - Deutsche Boerse
283+
- **V12**: `navPerShare: bigint, nextNavPerShare: bigint, navDate: number, ripcord: number` - NAV fund data + Next
277284
- **V13**: `bestAsk: bigint, bestBid: bigint, askVolume: number, bidVolume: number, lastTradedPrice: bigint` - Best Bid/Ask
278285
279286
For complete field definitions, see the [documentation](https://docs.chain.link/data-streams/reference/report-schema-v3).
@@ -283,7 +290,7 @@ For complete field definitions, see the [documentation](https://docs.chain.link/
283290
HA mode establishes multiple simultaneous connections for zero-downtime operation:
284291
285292
- **Automatic failover** between connections
286-
- **Report deduplication** across connections
293+
- **Report deduplication** across connections
287294
- **Automatic origin discovery** to find available endpoints
288295
- **Per-connection monitoring** and statistics
289296
@@ -305,22 +312,22 @@ const client = createClient({
305312
306313
### Error Types Overview
307314
308-
| **Error Type** | **When Thrown** | **Key Properties** |
309-
|---|---|---|
310-
| `ValidationError` | Invalid feed IDs, timestamps, parameters | `message` |
311-
| `AuthenticationError` | Invalid credentials, HMAC failures | `message` |
312-
| `APIError` | HTTP 4xx/5xx, network timeouts, rate limits | `statusCode`, `message` |
313-
| `ReportDecodingError` | Corrupted report data, unsupported versions | `message` |
314-
| `WebSocketError` | Connection failures, protocol errors | `message` |
315-
| `OriginDiscoveryError` | HA discovery failures | `cause`, `message` |
316-
| `MultiConnectionError` | All HA connections failed | `message` |
317-
| `PartialConnectionFailureError` | Some HA connections failed | `failedConnections`, `totalConnections` |
318-
| `InsufficientConnectionsError` | HA degraded performance | `availableConnections`, `requiredConnections` |
315+
| **Error Type** | **When Thrown** | **Key Properties** |
316+
|---------------------------------|---------------------------------------------|-----------------------------------------------|
317+
| `ValidationError` | Invalid feed IDs, timestamps, parameters | `message` |
318+
| `AuthenticationError` | Invalid credentials, HMAC failures | `message` |
319+
| `APIError` | HTTP 4xx/5xx, network timeouts, rate limits | `statusCode`, `message` |
320+
| `ReportDecodingError` | Corrupted report data, unsupported versions | `message` |
321+
| `WebSocketError` | Connection failures, protocol errors | `message` |
322+
| `OriginDiscoveryError` | HA discovery failures | `cause`, `message` |
323+
| `MultiConnectionError` | All HA connections failed | `message` |
324+
| `PartialConnectionFailureError` | Some HA connections failed | `failedConnections`, `totalConnections` |
325+
| `InsufficientConnectionsError` | HA degraded performance | `availableConnections`, `requiredConnections` |
319326
320327
### Usage Examples
321328
322329
```typescript
323-
import {
330+
import {
324331
ValidationError,
325332
AuthenticationError,
326333
APIError,
@@ -358,6 +365,7 @@ stream.on('error', (error) => {
358365
```
359366
360367
**Catch-all error handling:**
368+
361369
```typescript
362370
import { DataStreamsError } from './src';
363371

@@ -404,6 +412,7 @@ const client = createClient({
404412
```
405413
406414
Using Pino (structured JSON):
415+
407416
```typescript
408417
import pino from 'pino';
409418
import { createClient, LogLevel } from '@chainlink/data-streams-sdk';
@@ -429,6 +438,7 @@ const client = createClient({
429438
```
430439
431440
Command-line with pretty output:
441+
432442
```bash
433443
PINO_LEVEL=info npx ts-node examples/metrics-monitoring.ts | npx pino-pretty
434444
```
@@ -505,10 +515,10 @@ interface LoggingConfig {
505515
warn?: (message: string, ...args: any[]) => void;
506516
error?: (message: string, ...args: any[]) => void;
507517
};
508-
518+
509519
/** Minimum logging level - filters out lower priority logs */
510520
logLevel?: LogLevel; // DEBUG (0) | INFO (1) | WARN (2) | ERROR (3)
511-
521+
512522
/** Enable WebSocket ping/pong and connection state debugging logs */
513523
enableConnectionDebug?: boolean;
514524
}
@@ -533,6 +543,7 @@ const m = stream.getMetrics();
533543
```
534544
535545
Simple periodic print (example):
546+
536547
```typescript
537548
setInterval(() => {
538549
const m = stream.getMetrics();

typescript/examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ npx ts-node examples/list-feeds.ts
8787

8888
## Feed IDs
8989

90-
The SDK automatically detects and supports all report schema versions (V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V13).
90+
The SDK automatically detects and supports all report schema versions (V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13).
9191

9292
For available feed IDs, see the official [Chainlink documentation](https://docs.chain.link/data-streams/).
9393

typescript/src/decoder/implementation.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ReportDecodingError } from "../types/errors";
33
import {
44
DecodedV10Report,
55
DecodedV11Report,
6+
DecodedV12Report,
67
DecodedV13Report,
78
DecodedV2Report,
89
DecodedV3Report,
@@ -13,6 +14,7 @@ import {
1314
DecodedV8Report,
1415
DecodedV9Report,
1516
MarketStatus,
17+
Ripcord,
1618
} from "../types";
1719

1820
import { SDKLogger } from "../utils/logger";
@@ -147,6 +149,19 @@ const reportSchemaV11 = [
147149
{ type: "uint32", name: "marketStatus" },
148150
];
149151

152+
const reportSchemaV12 = [
153+
{ type: "bytes32", name: "feedId" },
154+
{ type: "uint32", name: "validFromTimestamp" },
155+
{ type: "uint32", name: "observationsTimestamp" },
156+
{ type: "uint192", name: "nativeFee" },
157+
{ type: "uint192", name: "linkFee" },
158+
{ type: "uint32", name: "expiresAt" },
159+
{ type: "int192", name: "navPerShare" },
160+
{ type: "int192", name: "nextNavPerShare" },
161+
{ type: "uint64", name: "navDate" },
162+
{ type: "uint32", name: "ripcord" },
163+
];
164+
150165
const reportSchemaV13 = [
151166
{ type: "bytes32", name: "feedId" },
152167
{ type: "uint32", name: "validFromTimestamp" },
@@ -183,6 +198,7 @@ export function decodeReport(
183198
| DecodedV9Report
184199
| DecodedV10Report
185200
| DecodedV11Report
201+
| DecodedV12Report
186202
| DecodedV13Report {
187203
logger?.debug(`Decoding report for feed ${feedId}`);
188204

@@ -233,6 +249,8 @@ export function decodeReport(
233249
return decodeV10Report(reportBlob);
234250
case "000b":
235251
return decodeV11Report(reportBlob);
252+
case "000c":
253+
return decodeV12Report(reportBlob);
236254
case "000d":
237255
return decodeV13Report(reportBlob);
238256
default:
@@ -430,7 +448,7 @@ function decodeV9Report(reportBlob: string): DecodedV9Report {
430448
);
431449

432450
const ripcord = Number(decoded[9]);
433-
if (ripcord !== 0 && ripcord !== 1) {
451+
if (ripcord !== Ripcord.NORMAL && ripcord !== Ripcord.PAUSED) {
434452
throw new ReportDecodingError(`Invalid ripcord value: ${ripcord}. Must be 0 (normal) or 1 (paused)`);
435453
}
436454

@@ -515,6 +533,36 @@ function decodeV11Report(reportBlob: string): DecodedV11Report {
515533
}
516534
}
517535

536+
function decodeV12Report(reportBlob: string): DecodedV12Report {
537+
try {
538+
const decoded = globalAbiCoder.decode(
539+
reportSchemaV12.map(item => item.type),
540+
getBytes(reportBlob)
541+
);
542+
543+
const ripcord = Number(decoded[9]);
544+
if (ripcord !== Ripcord.NORMAL && ripcord !== Ripcord.PAUSED) {
545+
throw new ReportDecodingError(`Invalid ripcord value: ${ripcord}. Must be 0 (normal) or 1 (paused)`);
546+
}
547+
548+
return {
549+
version: "V12",
550+
nativeFee: decoded[3],
551+
linkFee: decoded[4],
552+
expiresAt: Number(decoded[5]),
553+
navPerShare: decoded[6],
554+
nextNavPerShare: decoded[7],
555+
navDate: Number(decoded[8]),
556+
ripcord,
557+
};
558+
} catch (error) {
559+
throw new ReportDecodingError(
560+
`Failed to decode V12 report: ${error instanceof Error ? error.message : String(error)}`
561+
);
562+
}
563+
}
564+
565+
518566
function decodeV13Report(reportBlob: string): DecodedV13Report {
519567
try {
520568
const decoded = globalAbiCoder.decode(

typescript/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export type {
6060
DecodedV9Report,
6161
DecodedV10Report,
6262
DecodedV11Report,
63+
DecodedV12Report,
6364
DecodedV13Report,
6465
MarketStatus,
6566
} from "./types/report";

typescript/src/types/report.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ export enum MarketStatus {
2828
ACTIVE = 2,
2929
}
3030

31+
export enum Ripcord {
32+
NORMAL = 0,
33+
PAUSED = 1,
34+
}
35+
3136
/**
3237
* Base interface for all reports before decoding
3338
*/
@@ -185,6 +190,20 @@ export interface DecodedV11Report extends DecodedReportFields {
185190
marketStatus: number;
186191
}
187192

193+
/**
194+
* Decoded V12 report format (NAV Data + Next).
195+
*/
196+
export interface DecodedV12Report extends DecodedReportFields {
197+
version: "V12";
198+
/** DON's consensus NAV per share (18 decimal precision) */
199+
navPerShare: bigint;
200+
nextNavPerShare: bigint;
201+
/** Timestamp for the date the NAV report was produced */
202+
navDate: number;
203+
/** Emergency pause flag (0 = normal, 1 = paused - do not consume NAV data) */
204+
ripcord: number;
205+
}
206+
188207
/**
189208
* Decoded V13 report format (Best Bid/Ask).
190209
* Provides the best bid/ask prices, bid/ask volume and last traded price.
@@ -215,6 +234,7 @@ export type DecodedReport = (
215234
| DecodedV9Report
216235
| DecodedV10Report
217236
| DecodedV11Report
237+
| DecodedV12Report
218238
| DecodedV13Report
219239
) & {
220240
/** Feed ID this report belongs to */

typescript/src/utils/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const VALIDATION_REGEX = {
2323
/** Matches valid feed IDs (0x followed by 64 hex characters) */
2424
FEED_ID: /^0x[0-9a-fA-F]{64}$/,
2525
/** Matches valid schema versions (0x0002-0x0009, 0x000a, 0x000d) */
26-
SCHEMA_VERSION: /^0x000([2-9]|a|b|d)$/,
26+
SCHEMA_VERSION: /^0x000([2-9]|a|b|c|d)$/,
2727
} as const;
2828

2929
// Request timeout constants

0 commit comments

Comments
 (0)