@k256/sdk 0.4.0 → 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.
@@ -44,6 +44,16 @@ declare const MessageType: {
44
44
  readonly PoolUpdateBatch: 14;
45
45
  /** Block-level statistics (v3) */
46
46
  readonly BlockStats: 15;
47
+ /** Subscribe to price updates (JSON) - Client → Server */
48
+ readonly SubscribePrice: 16;
49
+ /** Single price update (bincode 56B) - Server → Client */
50
+ readonly PriceUpdate: 17;
51
+ /** Batched price updates (bincode [u16 count][entries...]) - Server → Client */
52
+ readonly PriceBatch: 18;
53
+ /** Initial price snapshot (same binary format as PriceBatch) - Server → Client */
54
+ readonly PriceSnapshot: 19;
55
+ /** Unsubscribe from prices (no payload) - Client → Server */
56
+ readonly UnsubscribePrice: 20;
47
57
  /** Error message (UTF-8 string) */
48
58
  readonly Error: 255;
49
59
  };
@@ -210,6 +220,38 @@ interface QuoteSubscribedMessage {
210
220
  timestampMs: number;
211
221
  };
212
222
  }
223
+ /**
224
+ * Single decoded price entry (shared by PriceUpdate, PriceBatch, PriceSnapshot)
225
+ */
226
+ interface PriceEntry {
227
+ mint: string;
228
+ usdPrice: number;
229
+ slot: number;
230
+ timestampMs: number;
231
+ }
232
+ /**
233
+ * Single price update from binary message (0x11)
234
+ * Note: K2 currently sends all updates via PriceBatch (0x12)
235
+ */
236
+ interface PriceUpdateMessage {
237
+ type: 'price_update';
238
+ data: PriceEntry;
239
+ }
240
+ /**
241
+ * Batched price updates from binary message (0x12)
242
+ */
243
+ interface PriceBatchMessage {
244
+ type: 'price_batch';
245
+ data: PriceEntry[];
246
+ }
247
+ /**
248
+ * Initial price snapshot from binary message (0x13)
249
+ * Same binary format as PriceBatch — sent once after subscribe_price
250
+ */
251
+ interface PriceSnapshotMessage {
252
+ type: 'price_snapshot';
253
+ data: PriceEntry[];
254
+ }
213
255
  /**
214
256
  * Error message from server
215
257
  */
@@ -231,7 +273,7 @@ interface PongMessage {
231
273
  /**
232
274
  * Union of all decoded message types
233
275
  */
234
- type DecodedMessage = PoolUpdateMessage | FeeMarketMessage | BlockStatsMessage | BlockhashMessage | QuoteMessage | HeartbeatMessage | SubscribedMessage | QuoteSubscribedMessage | ErrorMessage | PongMessage;
276
+ type DecodedMessage = PoolUpdateMessage | FeeMarketMessage | BlockStatsMessage | BlockhashMessage | QuoteMessage | PriceUpdateMessage | PriceBatchMessage | PriceSnapshotMessage | HeartbeatMessage | SubscribedMessage | QuoteSubscribedMessage | ErrorMessage | PongMessage;
235
277
 
236
278
  /**
237
279
  * K256 WebSocket Client
@@ -327,6 +369,15 @@ interface SubscribeQuoteOptions {
327
369
  /** How often to refresh the quote (ms) */
328
370
  refreshIntervalMs?: number;
329
371
  }
372
+ /**
373
+ * Price subscription options
374
+ */
375
+ interface SubscribePriceOptions {
376
+ /** Token mint addresses to track */
377
+ tokens: string[];
378
+ /** Minimum bps change to receive an update (default 10) */
379
+ thresholdBps?: number;
380
+ }
330
381
  /**
331
382
  * WebSocket client configuration
332
383
  */
@@ -387,6 +438,12 @@ interface K256WebSocketClientConfig {
387
438
  onQuoteSubscribed?: (data: DecodedMessage & {
388
439
  type: 'quote_subscribed';
389
440
  }) => void;
441
+ /** Called on single price update */
442
+ onPriceUpdate?: (message: PriceUpdateMessage) => void;
443
+ /** Called on batched price updates */
444
+ onPriceBatch?: (message: PriceBatchMessage) => void;
445
+ /** Called on initial price snapshot (after subscribe) */
446
+ onPriceSnapshot?: (message: PriceSnapshotMessage) => void;
390
447
  /** Called on heartbeat */
391
448
  onHeartbeat?: (data: DecodedMessage & {
392
449
  type: 'heartbeat';
@@ -432,6 +489,7 @@ declare class K256WebSocketClient {
432
489
  private lastHeartbeatTime;
433
490
  private pendingSubscription;
434
491
  private pendingQuoteSubscription;
492
+ private pendingPriceSubscription;
435
493
  private isIntentionallyClosed;
436
494
  /** Current connection state */
437
495
  get state(): ConnectionState;
@@ -466,6 +524,15 @@ declare class K256WebSocketClient {
466
524
  * @param topicId - Topic ID from quote_subscribed response
467
525
  */
468
526
  unsubscribeQuote(topicId: string): void;
527
+ /**
528
+ * Subscribe to real-time price updates
529
+ * @param options - Token mints and threshold configuration
530
+ */
531
+ subscribePrices(options: SubscribePriceOptions): void;
532
+ /**
533
+ * Unsubscribe from price updates
534
+ */
535
+ unsubscribePrices(): void;
469
536
  /**
470
537
  * Unsubscribe from all channels
471
538
  */
@@ -478,6 +545,7 @@ declare class K256WebSocketClient {
478
545
  private handleMessage;
479
546
  private sendSubscription;
480
547
  private sendQuoteSubscription;
548
+ private sendPriceSubscription;
481
549
  private setState;
482
550
  private handleError;
483
551
  private cleanup;
@@ -520,6 +588,12 @@ declare class K256WebSocketClient {
520
588
  * ```
521
589
  */
522
590
  declare function decodeMessage(data: ArrayBuffer): DecodedMessage | null;
591
+ /**
592
+ * Decode price entries from a PriceBatch or PriceSnapshot payload.
593
+ *
594
+ * Wire format: [count:u16 LE][entry₁:56B]...[entryₙ:56B]
595
+ */
596
+ declare function decodePriceEntries(payload: ArrayBuffer): PriceEntry[];
523
597
  /**
524
598
  * Decode a batch of pool updates
525
599
  *
@@ -541,4 +615,4 @@ declare function decodeMessage(data: ArrayBuffer): DecodedMessage | null;
541
615
  */
542
616
  declare function decodePoolUpdateBatch(payload: ArrayBuffer): PoolUpdateMessage[];
543
617
 
544
- export { type BlockStatsMessage, type BlockhashMessage, CloseCode, type CloseCodeValue, type ConnectionState, type DecodedMessage, type ErrorMessage, type FeeMarketMessage, type HeartbeatMessage, type K256ErrorCode, K256WebSocketClient, type K256WebSocketClientConfig, K256WebSocketError, MessageType, type MessageTypeValue, type PongMessage, type PoolUpdateMessage, type QuoteMessage, type QuoteSubscribedMessage, type SubscribeOptions, type SubscribeQuoteOptions, type SubscribedMessage, decodeMessage, decodePoolUpdateBatch };
618
+ export { type BlockStatsMessage, type BlockhashMessage, CloseCode, type CloseCodeValue, type ConnectionState, type DecodedMessage, type ErrorMessage, type FeeMarketMessage, type HeartbeatMessage, type K256ErrorCode, K256WebSocketClient, type K256WebSocketClientConfig, K256WebSocketError, MessageType, type MessageTypeValue, type PongMessage, type PoolUpdateMessage, type PriceBatchMessage, type PriceEntry, type PriceSnapshotMessage, type PriceUpdateMessage, type QuoteMessage, type QuoteSubscribedMessage, type SubscribeOptions, type SubscribePriceOptions, type SubscribeQuoteOptions, type SubscribedMessage, decodeMessage, decodePoolUpdateBatch, decodePriceEntries };
@@ -44,6 +44,16 @@ declare const MessageType: {
44
44
  readonly PoolUpdateBatch: 14;
45
45
  /** Block-level statistics (v3) */
46
46
  readonly BlockStats: 15;
47
+ /** Subscribe to price updates (JSON) - Client → Server */
48
+ readonly SubscribePrice: 16;
49
+ /** Single price update (bincode 56B) - Server → Client */
50
+ readonly PriceUpdate: 17;
51
+ /** Batched price updates (bincode [u16 count][entries...]) - Server → Client */
52
+ readonly PriceBatch: 18;
53
+ /** Initial price snapshot (same binary format as PriceBatch) - Server → Client */
54
+ readonly PriceSnapshot: 19;
55
+ /** Unsubscribe from prices (no payload) - Client → Server */
56
+ readonly UnsubscribePrice: 20;
47
57
  /** Error message (UTF-8 string) */
48
58
  readonly Error: 255;
49
59
  };
@@ -210,6 +220,38 @@ interface QuoteSubscribedMessage {
210
220
  timestampMs: number;
211
221
  };
212
222
  }
223
+ /**
224
+ * Single decoded price entry (shared by PriceUpdate, PriceBatch, PriceSnapshot)
225
+ */
226
+ interface PriceEntry {
227
+ mint: string;
228
+ usdPrice: number;
229
+ slot: number;
230
+ timestampMs: number;
231
+ }
232
+ /**
233
+ * Single price update from binary message (0x11)
234
+ * Note: K2 currently sends all updates via PriceBatch (0x12)
235
+ */
236
+ interface PriceUpdateMessage {
237
+ type: 'price_update';
238
+ data: PriceEntry;
239
+ }
240
+ /**
241
+ * Batched price updates from binary message (0x12)
242
+ */
243
+ interface PriceBatchMessage {
244
+ type: 'price_batch';
245
+ data: PriceEntry[];
246
+ }
247
+ /**
248
+ * Initial price snapshot from binary message (0x13)
249
+ * Same binary format as PriceBatch — sent once after subscribe_price
250
+ */
251
+ interface PriceSnapshotMessage {
252
+ type: 'price_snapshot';
253
+ data: PriceEntry[];
254
+ }
213
255
  /**
214
256
  * Error message from server
215
257
  */
@@ -231,7 +273,7 @@ interface PongMessage {
231
273
  /**
232
274
  * Union of all decoded message types
233
275
  */
234
- type DecodedMessage = PoolUpdateMessage | FeeMarketMessage | BlockStatsMessage | BlockhashMessage | QuoteMessage | HeartbeatMessage | SubscribedMessage | QuoteSubscribedMessage | ErrorMessage | PongMessage;
276
+ type DecodedMessage = PoolUpdateMessage | FeeMarketMessage | BlockStatsMessage | BlockhashMessage | QuoteMessage | PriceUpdateMessage | PriceBatchMessage | PriceSnapshotMessage | HeartbeatMessage | SubscribedMessage | QuoteSubscribedMessage | ErrorMessage | PongMessage;
235
277
 
236
278
  /**
237
279
  * K256 WebSocket Client
@@ -327,6 +369,15 @@ interface SubscribeQuoteOptions {
327
369
  /** How often to refresh the quote (ms) */
328
370
  refreshIntervalMs?: number;
329
371
  }
372
+ /**
373
+ * Price subscription options
374
+ */
375
+ interface SubscribePriceOptions {
376
+ /** Token mint addresses to track */
377
+ tokens: string[];
378
+ /** Minimum bps change to receive an update (default 10) */
379
+ thresholdBps?: number;
380
+ }
330
381
  /**
331
382
  * WebSocket client configuration
332
383
  */
@@ -387,6 +438,12 @@ interface K256WebSocketClientConfig {
387
438
  onQuoteSubscribed?: (data: DecodedMessage & {
388
439
  type: 'quote_subscribed';
389
440
  }) => void;
441
+ /** Called on single price update */
442
+ onPriceUpdate?: (message: PriceUpdateMessage) => void;
443
+ /** Called on batched price updates */
444
+ onPriceBatch?: (message: PriceBatchMessage) => void;
445
+ /** Called on initial price snapshot (after subscribe) */
446
+ onPriceSnapshot?: (message: PriceSnapshotMessage) => void;
390
447
  /** Called on heartbeat */
391
448
  onHeartbeat?: (data: DecodedMessage & {
392
449
  type: 'heartbeat';
@@ -432,6 +489,7 @@ declare class K256WebSocketClient {
432
489
  private lastHeartbeatTime;
433
490
  private pendingSubscription;
434
491
  private pendingQuoteSubscription;
492
+ private pendingPriceSubscription;
435
493
  private isIntentionallyClosed;
436
494
  /** Current connection state */
437
495
  get state(): ConnectionState;
@@ -466,6 +524,15 @@ declare class K256WebSocketClient {
466
524
  * @param topicId - Topic ID from quote_subscribed response
467
525
  */
468
526
  unsubscribeQuote(topicId: string): void;
527
+ /**
528
+ * Subscribe to real-time price updates
529
+ * @param options - Token mints and threshold configuration
530
+ */
531
+ subscribePrices(options: SubscribePriceOptions): void;
532
+ /**
533
+ * Unsubscribe from price updates
534
+ */
535
+ unsubscribePrices(): void;
469
536
  /**
470
537
  * Unsubscribe from all channels
471
538
  */
@@ -478,6 +545,7 @@ declare class K256WebSocketClient {
478
545
  private handleMessage;
479
546
  private sendSubscription;
480
547
  private sendQuoteSubscription;
548
+ private sendPriceSubscription;
481
549
  private setState;
482
550
  private handleError;
483
551
  private cleanup;
@@ -520,6 +588,12 @@ declare class K256WebSocketClient {
520
588
  * ```
521
589
  */
522
590
  declare function decodeMessage(data: ArrayBuffer): DecodedMessage | null;
591
+ /**
592
+ * Decode price entries from a PriceBatch or PriceSnapshot payload.
593
+ *
594
+ * Wire format: [count:u16 LE][entry₁:56B]...[entryₙ:56B]
595
+ */
596
+ declare function decodePriceEntries(payload: ArrayBuffer): PriceEntry[];
523
597
  /**
524
598
  * Decode a batch of pool updates
525
599
  *
@@ -541,4 +615,4 @@ declare function decodeMessage(data: ArrayBuffer): DecodedMessage | null;
541
615
  */
542
616
  declare function decodePoolUpdateBatch(payload: ArrayBuffer): PoolUpdateMessage[];
543
617
 
544
- export { type BlockStatsMessage, type BlockhashMessage, CloseCode, type CloseCodeValue, type ConnectionState, type DecodedMessage, type ErrorMessage, type FeeMarketMessage, type HeartbeatMessage, type K256ErrorCode, K256WebSocketClient, type K256WebSocketClientConfig, K256WebSocketError, MessageType, type MessageTypeValue, type PongMessage, type PoolUpdateMessage, type QuoteMessage, type QuoteSubscribedMessage, type SubscribeOptions, type SubscribeQuoteOptions, type SubscribedMessage, decodeMessage, decodePoolUpdateBatch };
618
+ export { type BlockStatsMessage, type BlockhashMessage, CloseCode, type CloseCodeValue, type ConnectionState, type DecodedMessage, type ErrorMessage, type FeeMarketMessage, type HeartbeatMessage, type K256ErrorCode, K256WebSocketClient, type K256WebSocketClientConfig, K256WebSocketError, MessageType, type MessageTypeValue, type PongMessage, type PoolUpdateMessage, type PriceBatchMessage, type PriceEntry, type PriceSnapshotMessage, type PriceUpdateMessage, type QuoteMessage, type QuoteSubscribedMessage, type SubscribeOptions, type SubscribePriceOptions, type SubscribeQuoteOptions, type SubscribedMessage, decodeMessage, decodePoolUpdateBatch, decodePriceEntries };
package/dist/ws/index.js CHANGED
@@ -53,6 +53,16 @@ var MessageType = {
53
53
  PoolUpdateBatch: 14,
54
54
  /** Block-level statistics (v3) */
55
55
  BlockStats: 15,
56
+ /** Subscribe to price updates (JSON) - Client → Server */
57
+ SubscribePrice: 16,
58
+ /** Single price update (bincode 56B) - Server → Client */
59
+ PriceUpdate: 17,
60
+ /** Batched price updates (bincode [u16 count][entries...]) - Server → Client */
61
+ PriceBatch: 18,
62
+ /** Initial price snapshot (same binary format as PriceBatch) - Server → Client */
63
+ PriceSnapshot: 19,
64
+ /** Unsubscribe from prices (no payload) - Client → Server */
65
+ UnsubscribePrice: 20,
56
66
  /** Error message (UTF-8 string) */
57
67
  Error: 255
58
68
  };
@@ -251,10 +261,48 @@ function decodeMessage(data) {
251
261
  receivedAt: Date.now()
252
262
  };
253
263
  }
264
+ case MessageType.PriceUpdate: {
265
+ if (payload.byteLength < 56) return null;
266
+ const mintBytes = new Uint8Array(payload, 0, 32);
267
+ return {
268
+ type: "price_update",
269
+ data: {
270
+ mint: base58Encode(mintBytes),
271
+ usdPrice: Number(payloadView.getBigUint64(32, true)) / 1e12,
272
+ slot: Number(payloadView.getBigUint64(40, true)),
273
+ timestampMs: Number(payloadView.getBigUint64(48, true))
274
+ }
275
+ };
276
+ }
277
+ case MessageType.PriceBatch:
278
+ case MessageType.PriceSnapshot: {
279
+ if (payload.byteLength < 2) return null;
280
+ const entries = decodePriceEntries(payload);
281
+ const priceType = msgType === MessageType.PriceSnapshot ? "price_snapshot" : "price_batch";
282
+ return { type: priceType, data: entries };
283
+ }
254
284
  default:
255
285
  return null;
256
286
  }
257
287
  }
288
+ function decodePriceEntries(payload) {
289
+ const view = new DataView(payload);
290
+ if (payload.byteLength < 2) return [];
291
+ const count = view.getUint16(0, true);
292
+ const entries = [];
293
+ let offset = 2;
294
+ for (let i = 0; i < count && offset + 56 <= payload.byteLength; i++) {
295
+ const mintBytes = new Uint8Array(payload, offset, 32);
296
+ entries.push({
297
+ mint: base58Encode(mintBytes),
298
+ usdPrice: Number(view.getBigUint64(offset + 32, true)) / 1e12,
299
+ slot: Number(view.getBigUint64(offset + 40, true)),
300
+ timestampMs: Number(view.getBigUint64(offset + 48, true))
301
+ });
302
+ offset += 56;
303
+ }
304
+ return entries;
305
+ }
258
306
  function decodePoolUpdateBatch(payload) {
259
307
  const view = new DataView(payload);
260
308
  if (payload.byteLength < 2) return [];
@@ -477,6 +525,7 @@ var K256WebSocketClient = class {
477
525
  lastHeartbeatTime = 0;
478
526
  pendingSubscription = null;
479
527
  pendingQuoteSubscription = null;
528
+ pendingPriceSubscription = null;
480
529
  isIntentionallyClosed = false;
481
530
  /** Current connection state */
482
531
  get state() {
@@ -566,12 +615,33 @@ var K256WebSocketClient = class {
566
615
  const msg = JSON.stringify({ type: "unsubscribe_quote", topicId });
567
616
  this.ws?.send(msg);
568
617
  }
618
+ /**
619
+ * Subscribe to real-time price updates
620
+ * @param options - Token mints and threshold configuration
621
+ */
622
+ subscribePrices(options) {
623
+ this.pendingPriceSubscription = options;
624
+ if (!this.isConnected) {
625
+ return;
626
+ }
627
+ this.sendPriceSubscription(options);
628
+ }
629
+ /**
630
+ * Unsubscribe from price updates
631
+ */
632
+ unsubscribePrices() {
633
+ this.pendingPriceSubscription = null;
634
+ if (!this.isConnected) return;
635
+ const buf = new Uint8Array([MessageType.UnsubscribePrice]);
636
+ this.ws?.send(buf);
637
+ }
569
638
  /**
570
639
  * Unsubscribe from all channels
571
640
  */
572
641
  unsubscribe() {
573
642
  this.pendingSubscription = null;
574
643
  this.pendingQuoteSubscription = null;
644
+ this.pendingPriceSubscription = null;
575
645
  if (!this.isConnected) return;
576
646
  this.ws?.send(JSON.stringify({ type: "unsubscribe" }));
577
647
  }
@@ -622,6 +692,9 @@ var K256WebSocketClient = class {
622
692
  if (this.pendingQuoteSubscription) {
623
693
  this.sendQuoteSubscription(this.pendingQuoteSubscription);
624
694
  }
695
+ if (this.pendingPriceSubscription) {
696
+ this.sendPriceSubscription(this.pendingPriceSubscription);
697
+ }
625
698
  this.config.onConnect?.();
626
699
  resolve();
627
700
  };
@@ -721,6 +794,21 @@ var K256WebSocketClient = class {
721
794
  case "quote_subscribed":
722
795
  this.config.onQuoteSubscribed?.(decoded);
723
796
  break;
797
+ case "price_update":
798
+ this.config.onPriceUpdate?.(decoded);
799
+ break;
800
+ case "price_batch":
801
+ this.config.onPriceBatch?.(decoded);
802
+ for (const entry of decoded.data) {
803
+ this.config.onPriceUpdate?.({ type: "price_update", data: entry });
804
+ }
805
+ break;
806
+ case "price_snapshot":
807
+ this.config.onPriceSnapshot?.(decoded);
808
+ for (const entry of decoded.data) {
809
+ this.config.onPriceUpdate?.({ type: "price_update", data: entry });
810
+ }
811
+ break;
724
812
  case "heartbeat":
725
813
  this.lastHeartbeatTime = Date.now();
726
814
  this.resetHeartbeatTimeout();
@@ -781,6 +869,18 @@ var K256WebSocketClient = class {
781
869
  };
782
870
  this.ws?.send(JSON.stringify(msg));
783
871
  }
872
+ sendPriceSubscription(options) {
873
+ const json = JSON.stringify({
874
+ tokens: options.tokens,
875
+ thresholdBps: options.thresholdBps ?? 10
876
+ });
877
+ const encoder = new TextEncoder();
878
+ const jsonBytes = encoder.encode(json);
879
+ const buf = new Uint8Array(1 + jsonBytes.length);
880
+ buf[0] = MessageType.SubscribePrice;
881
+ buf.set(jsonBytes, 1);
882
+ this.ws?.send(buf);
883
+ }
784
884
  setState(state) {
785
885
  if (this._state !== state) {
786
886
  const prevState = this._state;
@@ -921,6 +1021,6 @@ var K256WebSocketClient = class {
921
1021
  }
922
1022
  };
923
1023
 
924
- export { CloseCode, K256WebSocketClient, K256WebSocketError, MessageType, decodeMessage, decodePoolUpdateBatch };
1024
+ export { CloseCode, K256WebSocketClient, K256WebSocketError, MessageType, decodeMessage, decodePoolUpdateBatch, decodePriceEntries };
925
1025
  //# sourceMappingURL=index.js.map
926
1026
  //# sourceMappingURL=index.js.map