@k256/sdk 0.3.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +112 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +112 -3
- package/dist/index.js.map +1 -1
- package/dist/leader-ws/index.cjs +11 -2
- package/dist/leader-ws/index.cjs.map +1 -1
- package/dist/leader-ws/index.d.cts +11 -3
- package/dist/leader-ws/index.d.ts +11 -3
- package/dist/leader-ws/index.js +11 -2
- package/dist/leader-ws/index.js.map +1 -1
- package/dist/ws/index.cjs +101 -0
- package/dist/ws/index.cjs.map +1 -1
- package/dist/ws/index.d.cts +76 -2
- package/dist/ws/index.d.ts +76 -2
- package/dist/ws/index.js +101 -1
- package/dist/ws/index.js.map +1 -1
- package/package.json +1 -1
- package/src/leader-ws/decoder.ts +13 -2
- package/src/leader-ws/types.ts +11 -3
- package/src/ws/client.ts +78 -3
- package/src/ws/decoder.ts +56 -1
- package/src/ws/index.ts +6 -1
- package/src/ws/types.ts +49 -0
package/src/ws/client.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { decodeMessage, decodePoolUpdateBatch } from './decoder';
|
|
27
|
-
import { MessageType, type DecodedMessage, type PoolUpdateMessage, type BlockStatsMessage } from './types';
|
|
27
|
+
import { MessageType, type DecodedMessage, type PoolUpdateMessage, type BlockStatsMessage, type PriceUpdateMessage, type PriceBatchMessage, type PriceSnapshotMessage } from './types';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* RFC 6455 WebSocket Close Codes
|
|
@@ -105,6 +105,16 @@ export interface SubscribeQuoteOptions {
|
|
|
105
105
|
refreshIntervalMs?: number;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Price subscription options
|
|
110
|
+
*/
|
|
111
|
+
export interface SubscribePriceOptions {
|
|
112
|
+
/** Token mint addresses to track */
|
|
113
|
+
tokens: string[];
|
|
114
|
+
/** Minimum bps change to receive an update (default 10) */
|
|
115
|
+
thresholdBps?: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
108
118
|
/**
|
|
109
119
|
* WebSocket client configuration
|
|
110
120
|
*/
|
|
@@ -163,6 +173,12 @@ export interface K256WebSocketClientConfig {
|
|
|
163
173
|
onQuote?: (data: DecodedMessage & { type: 'quote' }) => void;
|
|
164
174
|
/** Called on quote subscription confirmed */
|
|
165
175
|
onQuoteSubscribed?: (data: DecodedMessage & { type: 'quote_subscribed' }) => void;
|
|
176
|
+
/** Called on single price update */
|
|
177
|
+
onPriceUpdate?: (message: PriceUpdateMessage) => void;
|
|
178
|
+
/** Called on batched price updates */
|
|
179
|
+
onPriceBatch?: (message: PriceBatchMessage) => void;
|
|
180
|
+
/** Called on initial price snapshot (after subscribe) */
|
|
181
|
+
onPriceSnapshot?: (message: PriceSnapshotMessage) => void;
|
|
166
182
|
/** Called on heartbeat */
|
|
167
183
|
onHeartbeat?: (data: DecodedMessage & { type: 'heartbeat' }) => void;
|
|
168
184
|
/** Called on pong response (with round-trip latency) */
|
|
@@ -228,8 +244,8 @@ export class K256WebSocketClient {
|
|
|
228
244
|
private config: Required<Omit<K256WebSocketClientConfig,
|
|
229
245
|
'onStateChange' | 'onConnect' | 'onDisconnect' | 'onReconnecting' | 'onError' |
|
|
230
246
|
'onSubscribed' | 'onPoolUpdate' | 'onPoolUpdateBatch' | 'onFeeMarket' | 'onBlockStats' |
|
|
231
|
-
'onBlockhash' | 'onQuote' | 'onQuoteSubscribed' | '
|
|
232
|
-
'onMessage' | 'onRawMessage'
|
|
247
|
+
'onBlockhash' | 'onQuote' | 'onQuoteSubscribed' | 'onPriceUpdate' | 'onPriceBatch' |
|
|
248
|
+
'onPriceSnapshot' | 'onHeartbeat' | 'onPong' | 'onMessage' | 'onRawMessage'
|
|
233
249
|
>> & K256WebSocketClientConfig;
|
|
234
250
|
|
|
235
251
|
private _state: ConnectionState = 'disconnected';
|
|
@@ -242,6 +258,7 @@ export class K256WebSocketClient {
|
|
|
242
258
|
private lastHeartbeatTime = 0;
|
|
243
259
|
private pendingSubscription: SubscribeOptions | null = null;
|
|
244
260
|
private pendingQuoteSubscription: SubscribeQuoteOptions | null = null;
|
|
261
|
+
private pendingPriceSubscription: SubscribePriceOptions | null = null;
|
|
245
262
|
private isIntentionallyClosed = false;
|
|
246
263
|
|
|
247
264
|
/** Current connection state */
|
|
@@ -351,12 +368,39 @@ export class K256WebSocketClient {
|
|
|
351
368
|
this.ws?.send(msg);
|
|
352
369
|
}
|
|
353
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Subscribe to real-time price updates
|
|
373
|
+
* @param options - Token mints and threshold configuration
|
|
374
|
+
*/
|
|
375
|
+
subscribePrices(options: SubscribePriceOptions): void {
|
|
376
|
+
this.pendingPriceSubscription = options;
|
|
377
|
+
|
|
378
|
+
if (!this.isConnected) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
this.sendPriceSubscription(options);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Unsubscribe from price updates
|
|
387
|
+
*/
|
|
388
|
+
unsubscribePrices(): void {
|
|
389
|
+
this.pendingPriceSubscription = null;
|
|
390
|
+
|
|
391
|
+
if (!this.isConnected) return;
|
|
392
|
+
|
|
393
|
+
const buf = new Uint8Array([MessageType.UnsubscribePrice]);
|
|
394
|
+
this.ws?.send(buf);
|
|
395
|
+
}
|
|
396
|
+
|
|
354
397
|
/**
|
|
355
398
|
* Unsubscribe from all channels
|
|
356
399
|
*/
|
|
357
400
|
unsubscribe(): void {
|
|
358
401
|
this.pendingSubscription = null;
|
|
359
402
|
this.pendingQuoteSubscription = null;
|
|
403
|
+
this.pendingPriceSubscription = null;
|
|
360
404
|
|
|
361
405
|
if (!this.isConnected) return;
|
|
362
406
|
|
|
@@ -427,6 +471,9 @@ export class K256WebSocketClient {
|
|
|
427
471
|
if (this.pendingQuoteSubscription) {
|
|
428
472
|
this.sendQuoteSubscription(this.pendingQuoteSubscription);
|
|
429
473
|
}
|
|
474
|
+
if (this.pendingPriceSubscription) {
|
|
475
|
+
this.sendPriceSubscription(this.pendingPriceSubscription);
|
|
476
|
+
}
|
|
430
477
|
|
|
431
478
|
this.config.onConnect?.();
|
|
432
479
|
resolve();
|
|
@@ -553,6 +600,21 @@ export class K256WebSocketClient {
|
|
|
553
600
|
case 'quote_subscribed':
|
|
554
601
|
this.config.onQuoteSubscribed?.(decoded as DecodedMessage & { type: 'quote_subscribed' });
|
|
555
602
|
break;
|
|
603
|
+
case 'price_update':
|
|
604
|
+
this.config.onPriceUpdate?.(decoded as PriceUpdateMessage);
|
|
605
|
+
break;
|
|
606
|
+
case 'price_batch':
|
|
607
|
+
this.config.onPriceBatch?.(decoded as PriceBatchMessage);
|
|
608
|
+
for (const entry of (decoded as PriceBatchMessage).data) {
|
|
609
|
+
this.config.onPriceUpdate?.({ type: 'price_update', data: entry });
|
|
610
|
+
}
|
|
611
|
+
break;
|
|
612
|
+
case 'price_snapshot':
|
|
613
|
+
this.config.onPriceSnapshot?.(decoded as PriceSnapshotMessage);
|
|
614
|
+
for (const entry of (decoded as PriceSnapshotMessage).data) {
|
|
615
|
+
this.config.onPriceUpdate?.({ type: 'price_update', data: entry });
|
|
616
|
+
}
|
|
617
|
+
break;
|
|
556
618
|
case 'heartbeat':
|
|
557
619
|
this.lastHeartbeatTime = Date.now();
|
|
558
620
|
this.resetHeartbeatTimeout();
|
|
@@ -622,6 +684,19 @@ export class K256WebSocketClient {
|
|
|
622
684
|
this.ws?.send(JSON.stringify(msg));
|
|
623
685
|
}
|
|
624
686
|
|
|
687
|
+
private sendPriceSubscription(options: SubscribePriceOptions): void {
|
|
688
|
+
const json = JSON.stringify({
|
|
689
|
+
tokens: options.tokens,
|
|
690
|
+
thresholdBps: options.thresholdBps ?? 10,
|
|
691
|
+
});
|
|
692
|
+
const encoder = new TextEncoder();
|
|
693
|
+
const jsonBytes = encoder.encode(json);
|
|
694
|
+
const buf = new Uint8Array(1 + jsonBytes.length);
|
|
695
|
+
buf[0] = MessageType.SubscribePrice;
|
|
696
|
+
buf.set(jsonBytes, 1);
|
|
697
|
+
this.ws?.send(buf);
|
|
698
|
+
}
|
|
699
|
+
|
|
625
700
|
private setState(state: ConnectionState): void {
|
|
626
701
|
if (this._state !== state) {
|
|
627
702
|
const prevState = this._state;
|
package/src/ws/decoder.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { base58Encode } from '../utils/base58';
|
|
11
11
|
import type { BlockMiniStats, TrendDirection } from '../types';
|
|
12
|
-
import { MessageType, type DecodedMessage, type PoolUpdateMessage, type FeeMarketMessage } from './types';
|
|
12
|
+
import { MessageType, type DecodedMessage, type PoolUpdateMessage, type FeeMarketMessage, type PriceEntry, type PriceBatchMessage, type PriceSnapshotMessage } from './types';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Decode a binary WebSocket message from K2
|
|
@@ -232,11 +232,66 @@ export function decodeMessage(data: ArrayBuffer): DecodedMessage | null {
|
|
|
232
232
|
};
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
case MessageType.PriceUpdate: {
|
|
236
|
+
// Single price entry (56 bytes, no count prefix):
|
|
237
|
+
// [mint:32B][usd_price:u64 LE][slot:u64 LE][timestamp_ms:u64 LE]
|
|
238
|
+
// K2 currently doesn't send 0x11, but handle for forward compat.
|
|
239
|
+
if (payload.byteLength < 56) return null;
|
|
240
|
+
const mintBytes = new Uint8Array(payload, 0, 32);
|
|
241
|
+
return {
|
|
242
|
+
type: 'price_update',
|
|
243
|
+
data: {
|
|
244
|
+
mint: base58Encode(mintBytes),
|
|
245
|
+
usdPrice: Number(payloadView.getBigUint64(32, true)) / 1e12,
|
|
246
|
+
slot: Number(payloadView.getBigUint64(40, true)),
|
|
247
|
+
timestampMs: Number(payloadView.getBigUint64(48, true)),
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
case MessageType.PriceBatch:
|
|
253
|
+
case MessageType.PriceSnapshot: {
|
|
254
|
+
// Both use identical binary format from serialize_price_message():
|
|
255
|
+
// [count:u16 LE][entry₁:56B]...[entryₙ:56B]
|
|
256
|
+
// Each entry: [mint:32B][usd_price:u64 LE][slot:u64 LE][timestamp_ms:u64 LE]
|
|
257
|
+
if (payload.byteLength < 2) return null;
|
|
258
|
+
const entries = decodePriceEntries(payload);
|
|
259
|
+
const priceType = msgType === MessageType.PriceSnapshot ? 'price_snapshot' : 'price_batch';
|
|
260
|
+
return { type: priceType, data: entries } as PriceBatchMessage | PriceSnapshotMessage;
|
|
261
|
+
}
|
|
262
|
+
|
|
235
263
|
default:
|
|
236
264
|
return null;
|
|
237
265
|
}
|
|
238
266
|
}
|
|
239
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Decode price entries from a PriceBatch or PriceSnapshot payload.
|
|
270
|
+
*
|
|
271
|
+
* Wire format: [count:u16 LE][entry₁:56B]...[entryₙ:56B]
|
|
272
|
+
*/
|
|
273
|
+
export function decodePriceEntries(payload: ArrayBuffer): PriceEntry[] {
|
|
274
|
+
const view = new DataView(payload);
|
|
275
|
+
if (payload.byteLength < 2) return [];
|
|
276
|
+
|
|
277
|
+
const count = view.getUint16(0, true);
|
|
278
|
+
const entries: PriceEntry[] = [];
|
|
279
|
+
let offset = 2;
|
|
280
|
+
|
|
281
|
+
for (let i = 0; i < count && offset + 56 <= payload.byteLength; i++) {
|
|
282
|
+
const mintBytes = new Uint8Array(payload, offset, 32);
|
|
283
|
+
entries.push({
|
|
284
|
+
mint: base58Encode(mintBytes),
|
|
285
|
+
usdPrice: Number(view.getBigUint64(offset + 32, true)) / 1e12,
|
|
286
|
+
slot: Number(view.getBigUint64(offset + 40, true)),
|
|
287
|
+
timestampMs: Number(view.getBigUint64(offset + 48, true)),
|
|
288
|
+
});
|
|
289
|
+
offset += 56;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return entries;
|
|
293
|
+
}
|
|
294
|
+
|
|
240
295
|
/**
|
|
241
296
|
* Decode a batch of pool updates
|
|
242
297
|
*
|
package/src/ws/index.ts
CHANGED
|
@@ -33,10 +33,11 @@ export type {
|
|
|
33
33
|
ConnectionState,
|
|
34
34
|
SubscribeOptions,
|
|
35
35
|
SubscribeQuoteOptions,
|
|
36
|
+
SubscribePriceOptions,
|
|
36
37
|
} from './client';
|
|
37
38
|
|
|
38
39
|
// Decoder (for advanced usage)
|
|
39
|
-
export { decodeMessage, decodePoolUpdateBatch } from './decoder';
|
|
40
|
+
export { decodeMessage, decodePoolUpdateBatch, decodePriceEntries } from './decoder';
|
|
40
41
|
|
|
41
42
|
// Types
|
|
42
43
|
export { MessageType } from './types';
|
|
@@ -48,6 +49,10 @@ export type {
|
|
|
48
49
|
BlockStatsMessage,
|
|
49
50
|
BlockhashMessage,
|
|
50
51
|
QuoteMessage,
|
|
52
|
+
PriceEntry,
|
|
53
|
+
PriceUpdateMessage,
|
|
54
|
+
PriceBatchMessage,
|
|
55
|
+
PriceSnapshotMessage,
|
|
51
56
|
HeartbeatMessage,
|
|
52
57
|
ErrorMessage,
|
|
53
58
|
SubscribedMessage,
|
package/src/ws/types.ts
CHANGED
|
@@ -44,6 +44,16 @@ export const MessageType = {
|
|
|
44
44
|
PoolUpdateBatch: 0x0e,
|
|
45
45
|
/** Block-level statistics (v3) */
|
|
46
46
|
BlockStats: 0x0f,
|
|
47
|
+
/** Subscribe to price updates (JSON) - Client → Server */
|
|
48
|
+
SubscribePrice: 0x10,
|
|
49
|
+
/** Single price update (bincode 56B) - Server → Client */
|
|
50
|
+
PriceUpdate: 0x11,
|
|
51
|
+
/** Batched price updates (bincode [u16 count][entries...]) - Server → Client */
|
|
52
|
+
PriceBatch: 0x12,
|
|
53
|
+
/** Initial price snapshot (same binary format as PriceBatch) - Server → Client */
|
|
54
|
+
PriceSnapshot: 0x13,
|
|
55
|
+
/** Unsubscribe from prices (no payload) - Client → Server */
|
|
56
|
+
UnsubscribePrice: 0x14,
|
|
47
57
|
/** Error message (UTF-8 string) */
|
|
48
58
|
Error: 0xff,
|
|
49
59
|
} as const;
|
|
@@ -214,6 +224,42 @@ export interface QuoteSubscribedMessage {
|
|
|
214
224
|
};
|
|
215
225
|
}
|
|
216
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Single decoded price entry (shared by PriceUpdate, PriceBatch, PriceSnapshot)
|
|
229
|
+
*/
|
|
230
|
+
export interface PriceEntry {
|
|
231
|
+
mint: string;
|
|
232
|
+
usdPrice: number;
|
|
233
|
+
slot: number;
|
|
234
|
+
timestampMs: number;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Single price update from binary message (0x11)
|
|
239
|
+
* Note: K2 currently sends all updates via PriceBatch (0x12)
|
|
240
|
+
*/
|
|
241
|
+
export interface PriceUpdateMessage {
|
|
242
|
+
type: 'price_update';
|
|
243
|
+
data: PriceEntry;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Batched price updates from binary message (0x12)
|
|
248
|
+
*/
|
|
249
|
+
export interface PriceBatchMessage {
|
|
250
|
+
type: 'price_batch';
|
|
251
|
+
data: PriceEntry[];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Initial price snapshot from binary message (0x13)
|
|
256
|
+
* Same binary format as PriceBatch — sent once after subscribe_price
|
|
257
|
+
*/
|
|
258
|
+
export interface PriceSnapshotMessage {
|
|
259
|
+
type: 'price_snapshot';
|
|
260
|
+
data: PriceEntry[];
|
|
261
|
+
}
|
|
262
|
+
|
|
217
263
|
/**
|
|
218
264
|
* Error message from server
|
|
219
265
|
*/
|
|
@@ -243,6 +289,9 @@ export type DecodedMessage =
|
|
|
243
289
|
| BlockStatsMessage
|
|
244
290
|
| BlockhashMessage
|
|
245
291
|
| QuoteMessage
|
|
292
|
+
| PriceUpdateMessage
|
|
293
|
+
| PriceBatchMessage
|
|
294
|
+
| PriceSnapshotMessage
|
|
246
295
|
| HeartbeatMessage
|
|
247
296
|
| SubscribedMessage
|
|
248
297
|
| QuoteSubscribedMessage
|