@k256/sdk 0.1.6 → 0.2.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.
@@ -1,9 +1,12 @@
1
+ import { BlockStats, BlockMiniStats, TrendDirection } from '../types/index.js';
2
+
1
3
  /**
2
4
  * WebSocket message types and interfaces
3
5
  *
4
6
  * Message type constants MUST match the K2 server values.
5
7
  * See: https://github.com/k256-xyz for protocol documentation
6
8
  */
9
+
7
10
  /**
8
11
  * WebSocket message type constants
9
12
  *
@@ -39,6 +42,8 @@ declare const MessageType: {
39
42
  readonly Heartbeat: 13;
40
43
  /** Batched pool updates for high throughput */
41
44
  readonly PoolUpdateBatch: 14;
45
+ /** Block-level statistics (v3) */
46
+ readonly BlockStats: 15;
42
47
  /** Error message (UTF-8 string) */
43
48
  readonly Error: 255;
44
49
  };
@@ -68,37 +73,59 @@ interface PoolUpdateMessage {
68
73
  };
69
74
  }
70
75
  /**
71
- * Decoded priority fees from binary message
76
+ * Decoded fee market from binary message (per-writable-account model)
72
77
  *
73
- * All fields from K2 PriorityFeesWire
78
+ * All fields from K2 FeeMarketWire
74
79
  */
75
- interface PriorityFeesMessage {
76
- type: 'priority_fees';
80
+ interface FeeMarketMessage {
81
+ type: 'fee_market';
77
82
  data: {
78
83
  slot: number;
79
84
  timestampMs: number;
80
- /** Recommended priority fee in micro-lamports */
85
+ /** Recommended priority fee in microlamports/CU (max p75 across hottest accounts) */
81
86
  recommended: number;
82
87
  /** Fee state: 0=low, 1=normal, 2=high, 3=extreme */
83
88
  state: number;
84
89
  /** True if data is stale (no recent samples) */
85
90
  isStale: boolean;
86
- swapP50: number;
87
- swapP75: number;
88
- swapP90: number;
89
- swapP99: number;
90
- /** Number of samples used for swap percentiles */
91
- swapSamples: number;
92
- landingP50Fee: number;
93
- landingP75Fee: number;
94
- landingP90Fee: number;
95
- landingP99Fee: number;
96
- top10Fee: number;
97
- top25Fee: number;
98
- spikeDetected: boolean;
99
- spikeFee: number;
91
+ /** Block utilization percentage (0-100) */
92
+ blockUtilizationPct: number;
93
+ /** Number of blocks in the observation window */
94
+ blocksInWindow: number;
95
+ /** Per-writable-account fee data */
96
+ accounts: {
97
+ /** Account public key (Base58) */
98
+ pubkey: string;
99
+ /** Total transactions touching this account */
100
+ totalTxs: number;
101
+ /** Active slots for this account */
102
+ activeSlots: number;
103
+ /** Total CU consumed */
104
+ cuConsumed: number;
105
+ /** Utilization percentage (0-100) of 12M CU limit */
106
+ utilizationPct: number;
107
+ /** Fee percentiles in microlamports/CU */
108
+ p25: number;
109
+ p50: number;
110
+ p75: number;
111
+ p90: number;
112
+ /** Minimum non-zero fee observed */
113
+ minNonzeroPrice: number;
114
+ }[];
115
+ /** Recent block mini-stats (v3) */
116
+ recentBlocks: BlockMiniStats[];
117
+ /** Fee trend direction (v3) */
118
+ trend: TrendDirection;
100
119
  };
101
120
  }
121
+ /**
122
+ * Decoded block stats from binary message (v3)
123
+ */
124
+ interface BlockStatsMessage {
125
+ type: 'block_stats';
126
+ data: BlockStats;
127
+ receivedAt: number;
128
+ }
102
129
  /**
103
130
  * Decoded blockhash from binary message
104
131
  */
@@ -204,7 +231,7 @@ interface PongMessage {
204
231
  /**
205
232
  * Union of all decoded message types
206
233
  */
207
- type DecodedMessage = PoolUpdateMessage | PriorityFeesMessage | BlockhashMessage | QuoteMessage | HeartbeatMessage | SubscribedMessage | QuoteSubscribedMessage | ErrorMessage | PongMessage;
234
+ type DecodedMessage = PoolUpdateMessage | FeeMarketMessage | BlockStatsMessage | BlockhashMessage | QuoteMessage | HeartbeatMessage | SubscribedMessage | QuoteSubscribedMessage | ErrorMessage | PongMessage;
208
235
 
209
236
  /**
210
237
  * K256 WebSocket Client
@@ -342,10 +369,12 @@ interface K256WebSocketClientConfig {
342
369
  onPoolUpdate?: (update: PoolUpdateMessage) => void;
343
370
  /** Called on batched pool updates (for efficiency) */
344
371
  onPoolUpdateBatch?: (updates: PoolUpdateMessage[]) => void;
345
- /** Called on priority fees update */
346
- onPriorityFees?: (data: DecodedMessage & {
347
- type: 'priority_fees';
372
+ /** Called on fee market update (per-writable-account fees) */
373
+ onFeeMarket?: (data: DecodedMessage & {
374
+ type: 'fee_market';
348
375
  }) => void;
376
+ /** Called on block stats update (v3) */
377
+ onBlockStats?: (message: BlockStatsMessage) => void;
349
378
  /** Called on blockhash update */
350
379
  onBlockhash?: (data: DecodedMessage & {
351
380
  type: 'blockhash';
@@ -512,4 +541,4 @@ declare function decodeMessage(data: ArrayBuffer): DecodedMessage | null;
512
541
  */
513
542
  declare function decodePoolUpdateBatch(payload: ArrayBuffer): PoolUpdateMessage[];
514
543
 
515
- export { type BlockhashMessage, CloseCode, type CloseCodeValue, type ConnectionState, type DecodedMessage, type ErrorMessage, type HeartbeatMessage, type K256ErrorCode, K256WebSocketClient, type K256WebSocketClientConfig, K256WebSocketError, MessageType, type MessageTypeValue, type PongMessage, type PoolUpdateMessage, type PriorityFeesMessage, type QuoteMessage, type QuoteSubscribedMessage, type SubscribeOptions, type SubscribeQuoteOptions, type SubscribedMessage, decodeMessage, decodePoolUpdateBatch };
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 };
package/dist/ws/index.js CHANGED
@@ -51,6 +51,8 @@ var MessageType = {
51
51
  Heartbeat: 13,
52
52
  /** Batched pool updates for high throughput */
53
53
  PoolUpdateBatch: 14,
54
+ /** Block-level statistics (v3) */
55
+ BlockStats: 15,
54
56
  /** Error message (UTF-8 string) */
55
57
  Error: 255
56
58
  };
@@ -91,55 +93,75 @@ function decodeMessage(data) {
91
93
  return { type: "error", data: { message: text } };
92
94
  }
93
95
  case MessageType.PriorityFees: {
94
- if (payload.byteLength < 24) return null;
96
+ if (payload.byteLength < 42) return null;
95
97
  const slot = Number(payloadView.getBigUint64(0, true));
96
98
  const timestampMs = Number(payloadView.getBigUint64(8, true));
97
99
  const recommended = Number(payloadView.getBigUint64(16, true));
98
- const state = payload.byteLength > 24 ? payloadView.getUint8(24) : 1;
99
- const isStale = payload.byteLength > 25 ? payloadView.getUint8(25) !== 0 : false;
100
- let swapP50 = 0, swapP75 = 0, swapP90 = 0, swapP99 = 0;
101
- if (payload.byteLength >= 58) {
102
- swapP50 = Number(payloadView.getBigUint64(26, true));
103
- swapP75 = Number(payloadView.getBigUint64(34, true));
104
- swapP90 = Number(payloadView.getBigUint64(42, true));
105
- swapP99 = Number(payloadView.getBigUint64(50, true));
100
+ const state = payloadView.getUint8(24);
101
+ const isStale = payloadView.getUint8(25) !== 0;
102
+ const blockUtilizationPct = payloadView.getFloat32(26, true);
103
+ const blocksInWindow = payloadView.getUint32(30, true);
104
+ const accountCount = Number(payloadView.getBigUint64(34, true));
105
+ const accounts = [];
106
+ let offset = 42;
107
+ for (let i = 0; i < accountCount && offset + 92 <= payload.byteLength; i++) {
108
+ const pubkeyBytes = new Uint8Array(payload, offset, 32);
109
+ const pubkey = base58Encode(pubkeyBytes);
110
+ const totalTxs = payloadView.getUint32(offset + 32, true);
111
+ const activeSlots = payloadView.getUint32(offset + 36, true);
112
+ const cuConsumed = Number(payloadView.getBigUint64(offset + 40, true));
113
+ const utilizationPct = payloadView.getFloat32(offset + 48, true);
114
+ const p25 = Number(payloadView.getBigUint64(offset + 52, true));
115
+ const p50 = Number(payloadView.getBigUint64(offset + 60, true));
116
+ const p75 = Number(payloadView.getBigUint64(offset + 68, true));
117
+ const p90 = Number(payloadView.getBigUint64(offset + 76, true));
118
+ const minNonzeroPrice = Number(payloadView.getBigUint64(offset + 84, true));
119
+ accounts.push({
120
+ pubkey,
121
+ totalTxs,
122
+ activeSlots,
123
+ cuConsumed,
124
+ utilizationPct,
125
+ p25,
126
+ p50,
127
+ p75,
128
+ p90,
129
+ minNonzeroPrice
130
+ });
131
+ offset += 92;
106
132
  }
107
- let swapSamples = 0;
108
- let landingP50Fee = 0, landingP75Fee = 0, landingP90Fee = 0, landingP99Fee = 0;
109
- let top10Fee = 0, top25Fee = 0;
110
- let spikeDetected = false, spikeFee = 0;
111
- if (payload.byteLength >= 119) {
112
- swapSamples = payloadView.getUint32(58, true);
113
- landingP50Fee = Number(payloadView.getBigUint64(62, true));
114
- landingP75Fee = Number(payloadView.getBigUint64(70, true));
115
- landingP90Fee = Number(payloadView.getBigUint64(78, true));
116
- landingP99Fee = Number(payloadView.getBigUint64(86, true));
117
- top10Fee = Number(payloadView.getBigUint64(94, true));
118
- top25Fee = Number(payloadView.getBigUint64(102, true));
119
- spikeDetected = payloadView.getUint8(110) !== 0;
120
- spikeFee = Number(payloadView.getBigUint64(111, true));
133
+ const recentBlocksCount = Number(payloadView.getBigUint64(offset, true));
134
+ offset += 8;
135
+ const recentBlocks = [];
136
+ for (let i = 0; i < recentBlocksCount; i++) {
137
+ const rbSlot = Number(payloadView.getBigUint64(offset, true));
138
+ offset += 8;
139
+ const rbCuConsumed = Number(payloadView.getBigUint64(offset, true));
140
+ offset += 8;
141
+ const rbTxCount = payloadView.getUint32(offset, true);
142
+ offset += 4;
143
+ const rbUtilizationPct = payloadView.getFloat32(offset, true);
144
+ offset += 4;
145
+ const rbAvgCuPrice = Number(payloadView.getBigUint64(offset, true));
146
+ offset += 8;
147
+ recentBlocks.push({ slot: rbSlot, cuConsumed: rbCuConsumed, txCount: rbTxCount, utilizationPct: rbUtilizationPct, avgCuPrice: rbAvgCuPrice });
121
148
  }
149
+ const trendByte = payloadView.getUint8(offset);
150
+ offset += 1;
151
+ const trend = trendByte === 0 ? "rising" : trendByte === 1 ? "falling" : "stable";
122
152
  return {
123
- type: "priority_fees",
153
+ type: "fee_market",
124
154
  data: {
125
155
  slot,
126
156
  timestampMs,
127
157
  recommended,
128
158
  state,
129
159
  isStale,
130
- swapP50,
131
- swapP75,
132
- swapP90,
133
- swapP99,
134
- swapSamples,
135
- landingP50Fee,
136
- landingP75Fee,
137
- landingP90Fee,
138
- landingP99Fee,
139
- top10Fee,
140
- top25Fee,
141
- spikeDetected,
142
- spikeFee
160
+ blockUtilizationPct,
161
+ blocksInWindow,
162
+ accounts,
163
+ recentBlocks,
164
+ trend
143
165
  }
144
166
  };
145
167
  }
@@ -184,6 +206,51 @@ function decodeMessage(data) {
184
206
  }
185
207
  };
186
208
  }
209
+ case MessageType.BlockStats: {
210
+ let offset = 0;
211
+ const slot = Number(payloadView.getBigUint64(offset, true));
212
+ offset += 8;
213
+ const cuConsumed = Number(payloadView.getBigUint64(offset, true));
214
+ offset += 8;
215
+ const executionCu = Number(payloadView.getBigUint64(offset, true));
216
+ offset += 8;
217
+ const cuLimit = Number(payloadView.getBigUint64(offset, true));
218
+ offset += 8;
219
+ const cuRemaining = Number(payloadView.getBigUint64(offset, true));
220
+ offset += 8;
221
+ const utilizationPct = payloadView.getFloat32(offset, true);
222
+ offset += 4;
223
+ const txCount = payloadView.getUint32(offset, true);
224
+ offset += 4;
225
+ const avgCuPerTx = payloadView.getUint32(offset, true);
226
+ offset += 4;
227
+ const avgCuPrice = Number(payloadView.getBigUint64(offset, true));
228
+ offset += 8;
229
+ const minCuPrice = Number(payloadView.getBigUint64(offset, true));
230
+ offset += 8;
231
+ const maxCuPrice = Number(payloadView.getBigUint64(offset, true));
232
+ offset += 8;
233
+ const timestampMs = Number(payloadView.getBigUint64(offset, true));
234
+ offset += 8;
235
+ return {
236
+ type: "block_stats",
237
+ data: {
238
+ slot,
239
+ cuConsumed,
240
+ executionCu,
241
+ cuLimit,
242
+ cuRemaining,
243
+ utilizationPct,
244
+ txCount,
245
+ avgCuPerTx,
246
+ avgCuPrice,
247
+ minCuPrice,
248
+ maxCuPrice,
249
+ timestampMs
250
+ },
251
+ receivedAt: Date.now()
252
+ };
253
+ }
187
254
  default:
188
255
  return null;
189
256
  }
@@ -265,19 +332,7 @@ function decodePoolUpdate(payload, payloadView) {
265
332
  }
266
333
  };
267
334
  } catch {
268
- return {
269
- type: "pool_update",
270
- data: {
271
- sequence: 0,
272
- slot: 0,
273
- writeVersion: 0,
274
- protocol: "unknown",
275
- poolAddress: "",
276
- tokenMints: [],
277
- tokenBalances: [],
278
- tokenDecimals: []
279
- }
280
- };
335
+ return null;
281
336
  }
282
337
  }
283
338
  function decodeQuote(payload, payloadView) {
@@ -348,25 +403,7 @@ function decodeQuote(payload, payloadView) {
348
403
  }
349
404
  };
350
405
  } catch {
351
- return {
352
- type: "quote",
353
- data: {
354
- topicId: "",
355
- timestampMs: 0,
356
- sequence: 0,
357
- inputMint: "",
358
- outputMint: "",
359
- inAmount: "0",
360
- outAmount: "0",
361
- priceImpactBps: 0,
362
- contextSlot: 0,
363
- algorithm: "",
364
- isImprovement: false,
365
- isCached: false,
366
- isStale: false,
367
- routePlan: null
368
- }
369
- };
406
+ return null;
370
407
  }
371
408
  }
372
409
 
@@ -668,8 +705,11 @@ var K256WebSocketClient = class {
668
705
  case "pool_update":
669
706
  this.config.onPoolUpdate?.(decoded);
670
707
  break;
671
- case "priority_fees":
672
- this.config.onPriorityFees?.(decoded);
708
+ case "fee_market":
709
+ this.config.onFeeMarket?.(decoded);
710
+ break;
711
+ case "block_stats":
712
+ this.config.onBlockStats?.(decoded);
673
713
  break;
674
714
  case "blockhash":
675
715
  this.config.onBlockhash?.(decoded);