@silentswap/sdk 0.0.76 → 0.0.77

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.d.ts CHANGED
@@ -21,6 +21,7 @@ export * from './rpc.js';
21
21
  export * from './assets.js';
22
22
  export * from './encoding.js';
23
23
  export * from './refund.js';
24
+ export { trackOrderViaWebSocket, getOrderTrackingWebSocketUrl, type TrackOrderOptions, type OrderStatusUpdate, } from './order-tracking.js';
24
25
  export type { NaiveBase58, NaiveBase93, NaiveHexLower, NaiveHexUpper } from './belt-utils.js';
25
26
  export { bytes_to_base58, base58_to_bytes, bytes_to_base93, base93_to_bytes, bytes_to_hex, hex_to_bytes } from './belt-utils.js';
26
27
  export * from './address.js';
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ export * from './rpc.js';
17
17
  export * from './assets.js';
18
18
  export * from './encoding.js';
19
19
  export * from './refund.js';
20
+ export { trackOrderViaWebSocket, getOrderTrackingWebSocketUrl, } from './order-tracking.js';
20
21
  export { bytes_to_base58, base58_to_bytes, bytes_to_base93, base93_to_bytes, bytes_to_hex, hex_to_bytes } from './belt-utils.js';
21
22
  export * from './address.js';
22
23
  // Export asset-utils functions (AssetInfo-based wrappers)
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Order tracking via WebSocket
3
+ *
4
+ * Subscribe to order status updates from the SilentSwap API until the order
5
+ * is fully finalized or the connection times out.
6
+ */
7
+ import type { OrderStatus, OutputStage } from './swap-executor.js';
8
+ /**
9
+ * Incremental status update from the WebSocket stream
10
+ */
11
+ export interface OrderStatusUpdate {
12
+ type: 'deposit' | 'stage' | 'transaction' | 'error';
13
+ data: {
14
+ stage?: OutputStage;
15
+ index?: number;
16
+ kind?: string;
17
+ chain?: string;
18
+ txId?: string;
19
+ message?: string;
20
+ amount?: string;
21
+ orderId?: string;
22
+ };
23
+ }
24
+ /**
25
+ * Options for trackOrderViaWebSocket
26
+ */
27
+ export interface TrackOrderOptions {
28
+ /** WebSocket URL (default: derived from mainnet API) */
29
+ wsUrl?: string;
30
+ /** Timeout in milliseconds (default: 10 * 60 * 1000 = 10 minutes) */
31
+ timeoutMs?: number;
32
+ /** Callback for each status update */
33
+ onStatus?: (update: OrderStatusUpdate) => void;
34
+ }
35
+ /**
36
+ * Derive WebSocket URL from REST API base URL
37
+ * e.g. https://api.silentswap.com -> wss://api.silentswap.com/websocket
38
+ */
39
+ export declare function getOrderTrackingWebSocketUrl(baseUrl: string): string;
40
+ /**
41
+ * Track an order until all outputs are finalized via the SilentSwap WebSocket API.
42
+ * Uses the global WebSocket (available in Node 18+ and browsers).
43
+ *
44
+ * @param orderId - Base58 order ID returned when placing the order
45
+ * @param viewingAuth - Viewing authorization (Base58) from the facilitator group
46
+ * @param options - Optional wsUrl, timeoutMs, and onStatus callback
47
+ * @returns Final order status when all outputs are FINALIZED
48
+ * @throws Error on WebSocket error, invalid response, or timeout
49
+ */
50
+ export declare function trackOrderViaWebSocket(orderId: string, viewingAuth: string, options?: TrackOrderOptions): Promise<OrderStatus>;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Order tracking via WebSocket
3
+ *
4
+ * Subscribe to order status updates from the SilentSwap API until the order
5
+ * is fully finalized or the connection times out.
6
+ */
7
+ /** Stage name used to detect completion */
8
+ const STAGE_FINALIZED = 'FINALIZED';
9
+ const DEFAULT_WS_URL = 'wss://api.silentswap.com/websocket';
10
+ const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
11
+ /**
12
+ * Derive WebSocket URL from REST API base URL
13
+ * e.g. https://api.silentswap.com -> wss://api.silentswap.com/websocket
14
+ */
15
+ export function getOrderTrackingWebSocketUrl(baseUrl) {
16
+ const url = baseUrl.replace(/^http:\/\//i, 'ws://').replace(/^https:\/\//i, 'wss://');
17
+ const trimmed = url.replace(/\/+$/, '');
18
+ return `${trimmed}/websocket`;
19
+ }
20
+ /**
21
+ * Track an order until all outputs are finalized via the SilentSwap WebSocket API.
22
+ * Uses the global WebSocket (available in Node 18+ and browsers).
23
+ *
24
+ * @param orderId - Base58 order ID returned when placing the order
25
+ * @param viewingAuth - Viewing authorization (Base58) from the facilitator group
26
+ * @param options - Optional wsUrl, timeoutMs, and onStatus callback
27
+ * @returns Final order status when all outputs are FINALIZED
28
+ * @throws Error on WebSocket error, invalid response, or timeout
29
+ */
30
+ export function trackOrderViaWebSocket(orderId, viewingAuth, options = {}) {
31
+ const wsUrl = options.wsUrl ?? DEFAULT_WS_URL;
32
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
33
+ const onStatus = options.onStatus;
34
+ return new Promise((resolve, reject) => {
35
+ if (typeof WebSocket === 'undefined') {
36
+ reject(new Error('WebSocket is not available. Use Node 18+ or a browser.'));
37
+ return;
38
+ }
39
+ const ws = new WebSocket(wsUrl);
40
+ let requestId = 0;
41
+ let finalStatus = null;
42
+ let heartbeatInterval;
43
+ const cleanup = () => {
44
+ if (heartbeatInterval) {
45
+ clearInterval(heartbeatInterval);
46
+ heartbeatInterval = undefined;
47
+ }
48
+ try {
49
+ if (ws.readyState === 1)
50
+ ws.close();
51
+ }
52
+ catch {
53
+ // ignore
54
+ }
55
+ };
56
+ const tryResolve = () => {
57
+ if (finalStatus?.outputs?.every((o) => o.stage === STAGE_FINALIZED)) {
58
+ cleanup();
59
+ resolve(finalStatus);
60
+ }
61
+ };
62
+ ws.onopen = () => {
63
+ ws.send(JSON.stringify({
64
+ jsonrpc: '2.0',
65
+ id: requestId++,
66
+ method: 'connect',
67
+ params: { auth: { orderId, viewingAuth } },
68
+ }));
69
+ heartbeatInterval = setInterval(() => {
70
+ if (ws.readyState === 1) {
71
+ const withPing = ws;
72
+ try {
73
+ withPing.ping?.();
74
+ }
75
+ catch {
76
+ // ignore (browser WebSocket has no ping)
77
+ }
78
+ }
79
+ }, 30000);
80
+ };
81
+ ws.onmessage = (event) => {
82
+ try {
83
+ const msg = JSON.parse(typeof event.data === 'string' ? event.data : String(event.data));
84
+ if (msg.error) {
85
+ if (msg.error.code === -32000)
86
+ return; // order not found yet
87
+ cleanup();
88
+ reject(new Error(msg.error.message ?? 'WebSocket error'));
89
+ return;
90
+ }
91
+ if (msg.result) {
92
+ if (msg.result.priority) {
93
+ finalStatus = msg.result;
94
+ onStatus?.({ type: 'deposit', data: { orderId: finalStatus.deposit?.orderId, amount: finalStatus.deposit?.amount } });
95
+ tryResolve();
96
+ }
97
+ else if (msg.result.type) {
98
+ const update = msg.result;
99
+ onStatus?.(update);
100
+ if (update.type === 'stage' && update.data.stage === STAGE_FINALIZED && finalStatus?.outputs?.[update.data.index ?? -1]) {
101
+ finalStatus.outputs[update.data.index].stage = STAGE_FINALIZED;
102
+ tryResolve();
103
+ }
104
+ }
105
+ }
106
+ }
107
+ catch {
108
+ // ignore parse errors
109
+ }
110
+ };
111
+ ws.onerror = (event) => {
112
+ cleanup();
113
+ reject(new Error('WebSocket error'));
114
+ };
115
+ ws.onclose = () => {
116
+ cleanup();
117
+ if (finalStatus)
118
+ resolve(finalStatus);
119
+ };
120
+ setTimeout(() => {
121
+ if (finalStatus) {
122
+ cleanup();
123
+ resolve(finalStatus);
124
+ }
125
+ else {
126
+ cleanup();
127
+ reject(new Error('Order tracking timeout'));
128
+ }
129
+ }, timeoutMs);
130
+ });
131
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@silentswap/sdk",
3
3
  "type": "module",
4
- "version": "0.0.76",
4
+ "version": "0.0.77",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
7
7
  "files": [