@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 CHANGED
@@ -111,6 +111,16 @@ var MessageType = {
111
111
  PoolUpdateBatch: 14,
112
112
  /** Block-level statistics (v3) */
113
113
  BlockStats: 15,
114
+ /** Subscribe to price updates (JSON) - Client → Server */
115
+ SubscribePrice: 16,
116
+ /** Single price update (bincode 56B) - Server → Client */
117
+ PriceUpdate: 17,
118
+ /** Batched price updates (bincode [u16 count][entries...]) - Server → Client */
119
+ PriceBatch: 18,
120
+ /** Initial price snapshot (same binary format as PriceBatch) - Server → Client */
121
+ PriceSnapshot: 19,
122
+ /** Unsubscribe from prices (no payload) - Client → Server */
123
+ UnsubscribePrice: 20,
114
124
  /** Error message (UTF-8 string) */
115
125
  Error: 255
116
126
  };
@@ -309,10 +319,48 @@ function decodeMessage(data) {
309
319
  receivedAt: Date.now()
310
320
  };
311
321
  }
322
+ case MessageType.PriceUpdate: {
323
+ if (payload.byteLength < 56) return null;
324
+ const mintBytes = new Uint8Array(payload, 0, 32);
325
+ return {
326
+ type: "price_update",
327
+ data: {
328
+ mint: base58Encode(mintBytes),
329
+ usdPrice: Number(payloadView.getBigUint64(32, true)) / 1e12,
330
+ slot: Number(payloadView.getBigUint64(40, true)),
331
+ timestampMs: Number(payloadView.getBigUint64(48, true))
332
+ }
333
+ };
334
+ }
335
+ case MessageType.PriceBatch:
336
+ case MessageType.PriceSnapshot: {
337
+ if (payload.byteLength < 2) return null;
338
+ const entries = decodePriceEntries(payload);
339
+ const priceType = msgType === MessageType.PriceSnapshot ? "price_snapshot" : "price_batch";
340
+ return { type: priceType, data: entries };
341
+ }
312
342
  default:
313
343
  return null;
314
344
  }
315
345
  }
346
+ function decodePriceEntries(payload) {
347
+ const view = new DataView(payload);
348
+ if (payload.byteLength < 2) return [];
349
+ const count = view.getUint16(0, true);
350
+ const entries = [];
351
+ let offset = 2;
352
+ for (let i = 0; i < count && offset + 56 <= payload.byteLength; i++) {
353
+ const mintBytes = new Uint8Array(payload, offset, 32);
354
+ entries.push({
355
+ mint: base58Encode(mintBytes),
356
+ usdPrice: Number(view.getBigUint64(offset + 32, true)) / 1e12,
357
+ slot: Number(view.getBigUint64(offset + 40, true)),
358
+ timestampMs: Number(view.getBigUint64(offset + 48, true))
359
+ });
360
+ offset += 56;
361
+ }
362
+ return entries;
363
+ }
316
364
  function decodePoolUpdateBatch(payload) {
317
365
  const view = new DataView(payload);
318
366
  if (payload.byteLength < 2) return [];
@@ -535,6 +583,7 @@ var K256WebSocketClient = class {
535
583
  lastHeartbeatTime = 0;
536
584
  pendingSubscription = null;
537
585
  pendingQuoteSubscription = null;
586
+ pendingPriceSubscription = null;
538
587
  isIntentionallyClosed = false;
539
588
  /** Current connection state */
540
589
  get state() {
@@ -624,12 +673,33 @@ var K256WebSocketClient = class {
624
673
  const msg = JSON.stringify({ type: "unsubscribe_quote", topicId });
625
674
  this.ws?.send(msg);
626
675
  }
676
+ /**
677
+ * Subscribe to real-time price updates
678
+ * @param options - Token mints and threshold configuration
679
+ */
680
+ subscribePrices(options) {
681
+ this.pendingPriceSubscription = options;
682
+ if (!this.isConnected) {
683
+ return;
684
+ }
685
+ this.sendPriceSubscription(options);
686
+ }
687
+ /**
688
+ * Unsubscribe from price updates
689
+ */
690
+ unsubscribePrices() {
691
+ this.pendingPriceSubscription = null;
692
+ if (!this.isConnected) return;
693
+ const buf = new Uint8Array([MessageType.UnsubscribePrice]);
694
+ this.ws?.send(buf);
695
+ }
627
696
  /**
628
697
  * Unsubscribe from all channels
629
698
  */
630
699
  unsubscribe() {
631
700
  this.pendingSubscription = null;
632
701
  this.pendingQuoteSubscription = null;
702
+ this.pendingPriceSubscription = null;
633
703
  if (!this.isConnected) return;
634
704
  this.ws?.send(JSON.stringify({ type: "unsubscribe" }));
635
705
  }
@@ -680,6 +750,9 @@ var K256WebSocketClient = class {
680
750
  if (this.pendingQuoteSubscription) {
681
751
  this.sendQuoteSubscription(this.pendingQuoteSubscription);
682
752
  }
753
+ if (this.pendingPriceSubscription) {
754
+ this.sendPriceSubscription(this.pendingPriceSubscription);
755
+ }
683
756
  this.config.onConnect?.();
684
757
  resolve();
685
758
  };
@@ -779,6 +852,21 @@ var K256WebSocketClient = class {
779
852
  case "quote_subscribed":
780
853
  this.config.onQuoteSubscribed?.(decoded);
781
854
  break;
855
+ case "price_update":
856
+ this.config.onPriceUpdate?.(decoded);
857
+ break;
858
+ case "price_batch":
859
+ this.config.onPriceBatch?.(decoded);
860
+ for (const entry of decoded.data) {
861
+ this.config.onPriceUpdate?.({ type: "price_update", data: entry });
862
+ }
863
+ break;
864
+ case "price_snapshot":
865
+ this.config.onPriceSnapshot?.(decoded);
866
+ for (const entry of decoded.data) {
867
+ this.config.onPriceUpdate?.({ type: "price_update", data: entry });
868
+ }
869
+ break;
782
870
  case "heartbeat":
783
871
  this.lastHeartbeatTime = Date.now();
784
872
  this.resetHeartbeatTimeout();
@@ -839,6 +927,18 @@ var K256WebSocketClient = class {
839
927
  };
840
928
  this.ws?.send(JSON.stringify(msg));
841
929
  }
930
+ sendPriceSubscription(options) {
931
+ const json = JSON.stringify({
932
+ tokens: options.tokens,
933
+ thresholdBps: options.thresholdBps ?? 10
934
+ });
935
+ const encoder = new TextEncoder();
936
+ const jsonBytes = encoder.encode(json);
937
+ const buf = new Uint8Array(1 + jsonBytes.length);
938
+ buf[0] = MessageType.SubscribePrice;
939
+ buf.set(jsonBytes, 1);
940
+ this.ws?.send(buf);
941
+ }
842
942
  setState(state) {
843
943
  if (this._state !== state) {
844
944
  const prevState = this._state;
@@ -1009,6 +1109,11 @@ function readU16(view, o) {
1009
1109
  o.v += 2;
1010
1110
  return val;
1011
1111
  }
1112
+ function readF64(view, o) {
1113
+ const val = view.getFloat64(o.v, true);
1114
+ o.v += 8;
1115
+ return val;
1116
+ }
1012
1117
  function readU8(view, o) {
1013
1118
  const val = view.getUint8(o.v);
1014
1119
  o.v += 1;
@@ -1067,12 +1172,16 @@ function readGossipPeer(view, data, o) {
1067
1172
  lastVote: readU64(view, o),
1068
1173
  rootSlot: readU64(view, o),
1069
1174
  wallclock: readU64(view, o),
1070
- // Geo/ASN enrichment from IPinfo Lite MMDB (server-side)
1175
+ // Geo/ASN enrichment from MaxMind GeoLite2 (server-side)
1071
1176
  countryCode: readVecU8AsString(view, data, o),
1072
1177
  continentCode: readVecU8AsString(view, data, o),
1073
1178
  asn: readVecU8AsString(view, data, o),
1074
1179
  asName: readVecU8AsString(view, data, o),
1075
- asDomain: readVecU8AsString(view, data, o)
1180
+ city: readVecU8AsString(view, data, o),
1181
+ region: readVecU8AsString(view, data, o),
1182
+ latitude: readF64(view, o),
1183
+ longitude: readF64(view, o),
1184
+ timezone: readVecU8AsString(view, data, o)
1076
1185
  };
1077
1186
  }
1078
1187
  function readGossipPeerVec(view, data, o) {
@@ -1503,6 +1612,7 @@ exports.base58Encode = base58Encode;
1503
1612
  exports.decodeLeaderMessage = decodeLeaderMessage;
1504
1613
  exports.decodeMessage = decodeMessage;
1505
1614
  exports.decodePoolUpdateBatch = decodePoolUpdateBatch;
1615
+ exports.decodePriceEntries = decodePriceEntries;
1506
1616
  exports.isValidPubkey = isValidPubkey;
1507
1617
  //# sourceMappingURL=index.cjs.map
1508
1618
  //# sourceMappingURL=index.cjs.map