@silentswap/sdk 0.0.76 → 0.0.78
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 +1 -0
- package/dist/index.js +1 -0
- package/dist/order-tracking.d.ts +50 -0
- package/dist/order-tracking.js +131 -0
- package/dist/swap-executor.d.ts +30 -0
- package/dist/swap-executor.js +31 -2
- package/package.json +1 -1
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/dist/swap-executor.d.ts
CHANGED
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
* This module provides a simple interface for integrators to execute swaps without
|
|
6
6
|
* needing to understand the underlying protocol details.
|
|
7
7
|
*
|
|
8
|
+
* **Recovery (refund / prove delivery):** Save `result.viewingAuth` and the order ID
|
|
9
|
+
* for tracking, refund, and recovery. The mnemonic used for the facilitator group can
|
|
10
|
+
* be derived from the same entropy that was used to create the group: use
|
|
11
|
+
* `exportSecretMnemonicFromEntropy(entropy)` from this package. Recovery paths are
|
|
12
|
+
* BIP-44 paths of the form `m/44'/${coinType}'/${accountIndex}'/0/${addressIndex}`,
|
|
13
|
+
* built from the group's exported public keys (skip coin type `'*'`). See the docs
|
|
14
|
+
* "Save auth & order ID (refund & recovery)" for full details.
|
|
15
|
+
*
|
|
8
16
|
* @example
|
|
9
17
|
* ```typescript
|
|
10
18
|
* import { executeSolanaSwap, createSwapAccounts } from '@silentswap/sdk';
|
|
@@ -24,6 +32,7 @@
|
|
|
24
32
|
* });
|
|
25
33
|
*
|
|
26
34
|
* console.log('Order ID:', result.orderId);
|
|
35
|
+
* console.log('Viewing auth (save for tracking/refund/recovery):', result.viewingAuth);
|
|
27
36
|
* ```
|
|
28
37
|
*/
|
|
29
38
|
import type { Caip19 } from './types/core.js';
|
|
@@ -90,6 +99,27 @@ export interface SwapResult {
|
|
|
90
99
|
usdcAmount: string;
|
|
91
100
|
/** Actual source amount used (may differ due to bridge optimization) */
|
|
92
101
|
actualSourceAmount: string;
|
|
102
|
+
/**
|
|
103
|
+
* Viewing auth (Base58 EVM address of the facilitator group's viewer) for this order.
|
|
104
|
+
* Save it together with the order ID to track the order, check refund eligibility, or run
|
|
105
|
+
* recovery flows.
|
|
106
|
+
*/
|
|
107
|
+
viewingAuth: string;
|
|
108
|
+
/**
|
|
109
|
+
* Recovery data for refund/recovery: mnemonic (24 words) and BIP-44 derivation paths
|
|
110
|
+
* for the facilitator group. Store securely; do not log or commit.
|
|
111
|
+
*/
|
|
112
|
+
recoveryData: {
|
|
113
|
+
mnemonic: string;
|
|
114
|
+
recoveryPaths: string[];
|
|
115
|
+
};
|
|
116
|
+
/** Wallets used for this swap: EVM signer (refundee) and Solana depositor */
|
|
117
|
+
usedWallet: {
|
|
118
|
+
/** EVM address used for auth and as refundee */
|
|
119
|
+
evmAddress: `0x${string}`;
|
|
120
|
+
/** Solana public key (base58) used for the deposit transaction */
|
|
121
|
+
solanaPublicKey: string;
|
|
122
|
+
};
|
|
93
123
|
}
|
|
94
124
|
/**
|
|
95
125
|
* EVM account for signing
|
package/dist/swap-executor.js
CHANGED
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
* This module provides a simple interface for integrators to execute swaps without
|
|
6
6
|
* needing to understand the underlying protocol details.
|
|
7
7
|
*
|
|
8
|
+
* **Recovery (refund / prove delivery):** Save `result.viewingAuth` and the order ID
|
|
9
|
+
* for tracking, refund, and recovery. The mnemonic used for the facilitator group can
|
|
10
|
+
* be derived from the same entropy that was used to create the group: use
|
|
11
|
+
* `exportSecretMnemonicFromEntropy(entropy)` from this package. Recovery paths are
|
|
12
|
+
* BIP-44 paths of the form `m/44'/${coinType}'/${accountIndex}'/0/${addressIndex}`,
|
|
13
|
+
* built from the group's exported public keys (skip coin type `'*'`). See the docs
|
|
14
|
+
* "Save auth & order ID (refund & recovery)" for full details.
|
|
15
|
+
*
|
|
8
16
|
* @example
|
|
9
17
|
* ```typescript
|
|
10
18
|
* import { executeSolanaSwap, createSwapAccounts } from '@silentswap/sdk';
|
|
@@ -24,14 +32,15 @@
|
|
|
24
32
|
* });
|
|
25
33
|
*
|
|
26
34
|
* console.log('Order ID:', result.orderId);
|
|
35
|
+
* console.log('Viewing auth (save for tracking/refund/recovery):', result.viewingAuth);
|
|
27
36
|
* ```
|
|
28
37
|
*/
|
|
29
38
|
import { encodeFunctionData, erc20Abi } from 'viem';
|
|
30
39
|
import { createSilentSwapClient } from './client.js';
|
|
31
40
|
import { createSignInMessage, createEip712DocForWalletGeneration } from './sdk.js';
|
|
32
|
-
import { createHdFacilitatorGroupFromEntropy } from './hd-facilitator-group.js';
|
|
41
|
+
import { createHdFacilitatorGroupFromEntropy, exportSecretMnemonicFromEntropy, } from './hd-facilitator-group.js';
|
|
33
42
|
import { queryDepositCount } from './wallet.js';
|
|
34
|
-
import { hexToBytes } from './encoding.js';
|
|
43
|
+
import { hexToBytes, hexToBase58 } from './encoding.js';
|
|
35
44
|
import { quoteResponseToEip712Document, DeliveryMethod, FacilitatorKeyType, PublicKeyArgGroups } from './order.js';
|
|
36
45
|
import { solveOptimalUsdcAmount, fetchRelayQuote, getRelayStatus } from './bridge.js';
|
|
37
46
|
import { ENVIRONMENT, N_RELAY_CHAIN_ID_SOLANA, NI_CHAIN_ID_AVALANCHE, SB58_ADDR_SOL_PROGRAM_SYSTEM, SB58_CHAIN_ID_SOLANA_MAINNET, S0X_ADDR_USDC_AVALANCHE, } from './constants.js';
|
|
@@ -491,12 +500,32 @@ export async function executeSolanaSwap(config) {
|
|
|
491
500
|
});
|
|
492
501
|
}
|
|
493
502
|
}
|
|
503
|
+
// Derive viewing auth (viewer EVM address as Base58) for tracking, refund, and recovery
|
|
504
|
+
const viewer = await facilitatorGroup.viewer();
|
|
505
|
+
const viewerEvmSigner = await viewer.evmSigner();
|
|
506
|
+
const viewingAuth = hexToBase58(viewerEvmSigner.address);
|
|
507
|
+
// Build recovery data (mnemonic + paths) for refund/recovery
|
|
508
|
+
const groupPublicKeysForRecovery = await facilitatorGroup.exportPublicKeys(1, [...PublicKeyArgGroups.GENERIC]);
|
|
509
|
+
const recoveryPaths = [];
|
|
510
|
+
for (const pk of groupPublicKeysForRecovery[0] ?? []) {
|
|
511
|
+
if (pk.coinType === '*')
|
|
512
|
+
continue;
|
|
513
|
+
recoveryPaths.push(`m/44'/${pk.coinType}'/${depositCount}'/0/0`);
|
|
514
|
+
}
|
|
515
|
+
const mnemonic = (await exportSecretMnemonicFromEntropy(hexToBytes(entropy))).toString();
|
|
516
|
+
const recoveryData = { mnemonic, recoveryPaths };
|
|
494
517
|
return {
|
|
495
518
|
orderId,
|
|
496
519
|
quoteId: quoteResponse.quoteId,
|
|
497
520
|
depositTxHash: txHash,
|
|
498
521
|
usdcAmount: bridgeResult.usdcAmountOut.toString(),
|
|
499
522
|
actualSourceAmount: bridgeResult.actualAmountIn.toString(),
|
|
523
|
+
viewingAuth,
|
|
524
|
+
recoveryData,
|
|
525
|
+
usedWallet: {
|
|
526
|
+
evmAddress: accounts.evm.address,
|
|
527
|
+
solanaPublicKey: accounts.solana.publicKey,
|
|
528
|
+
},
|
|
500
529
|
};
|
|
501
530
|
}
|
|
502
531
|
// ============================================================================
|