@silentswap/sdk 0.0.68 → 0.0.69
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/bridge.js +22 -3
- package/dist/quote-utils.d.ts +6 -2
- package/dist/quote-utils.js +70 -15
- package/package.json +1 -1
package/dist/bridge.js
CHANGED
|
@@ -247,10 +247,29 @@ export async function fetchRelayQuote(params, signal) {
|
|
|
247
247
|
});
|
|
248
248
|
if (!response.ok) {
|
|
249
249
|
const text = await response.text();
|
|
250
|
-
throw
|
|
250
|
+
throw parseQuoteApiErrorResponse(response.status, text);
|
|
251
251
|
}
|
|
252
252
|
return response.json();
|
|
253
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Parse quote API error response (relay.link or deBridge) and return a user-friendly Error.
|
|
256
|
+
* Handles INSUFFICIENT_FUNDS / "No UTXOs available to spend" and other known error shapes.
|
|
257
|
+
*/
|
|
258
|
+
function parseQuoteApiErrorResponse(status, text) {
|
|
259
|
+
try {
|
|
260
|
+
const body = JSON.parse(text);
|
|
261
|
+
if (body.errorCode === 'INSUFFICIENT_FUNDS' || (body.message && body.message.includes('UTXO'))) {
|
|
262
|
+
return new Error('Insufficient funds');
|
|
263
|
+
}
|
|
264
|
+
if (body.message && typeof body.message === 'string') {
|
|
265
|
+
return new Error(body.message);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
// not JSON or invalid
|
|
270
|
+
}
|
|
271
|
+
return new Error(`HTTP ${status}: ${text}`);
|
|
272
|
+
}
|
|
254
273
|
/**
|
|
255
274
|
* Fetch a deBridge order with detailed response
|
|
256
275
|
* Uses create-tx endpoint for cross-chain swaps
|
|
@@ -294,7 +313,7 @@ export async function fetchDebridgeOrder(params, signal) {
|
|
|
294
313
|
const response = await fetch(`https://deswap.debridge.finance/v1.0/dln/order/create-tx?${queryParams}`, { signal });
|
|
295
314
|
if (!response.ok) {
|
|
296
315
|
const text = await response.text();
|
|
297
|
-
throw
|
|
316
|
+
throw parseQuoteApiErrorResponse(response.status, text);
|
|
298
317
|
}
|
|
299
318
|
return response.json();
|
|
300
319
|
}
|
|
@@ -323,7 +342,7 @@ async function fetchDebridgeSingleChainOrder(params, signal) {
|
|
|
323
342
|
const response = await fetch(`https://deswap.debridge.finance/v1.0/chain/transaction?${queryParams}`, { signal });
|
|
324
343
|
if (!response.ok) {
|
|
325
344
|
const text = await response.text();
|
|
326
|
-
throw
|
|
345
|
+
throw parseQuoteApiErrorResponse(response.status, text);
|
|
327
346
|
}
|
|
328
347
|
const data = await response.json();
|
|
329
348
|
// Normalize single-chain response to match DeBridgeOrderResponse structure
|
package/dist/quote-utils.d.ts
CHANGED
|
@@ -66,9 +66,13 @@ export declare function calculateRelayMetrics(relayQuote: RelayQuoteResponse): Q
|
|
|
66
66
|
*/
|
|
67
67
|
export declare function calculateDebridgeMetrics(debridgeQuote: DeBridgeOrderResponse): QuoteMetrics;
|
|
68
68
|
/**
|
|
69
|
-
* Select the best quote from multiple providers based on retention rate
|
|
69
|
+
* Select the best quote from multiple providers based on retention rate.
|
|
70
|
+
* A quote is viable if it has transaction data (for execution) or estimation data (for display).
|
|
71
|
+
* When both providers return estimation but no tx, we still pick the best so the app can show
|
|
72
|
+
* output and refetch on amount change instead of throwing and blocking further quotes.
|
|
73
|
+
* When both fail, uses relayRejection/debridgeRejection to throw a specific message (e.g. "Insufficient funds").
|
|
70
74
|
*/
|
|
71
|
-
export declare function selectBestQuote(relayQuote: RelayQuoteResponse | null, debridgeQuote: DeBridgeOrderResponse | null, srcAmount: string): BridgeQuoteResult;
|
|
75
|
+
export declare function selectBestQuote(relayQuote: RelayQuoteResponse | null, debridgeQuote: DeBridgeOrderResponse | null, srcAmount: string, relayRejection?: unknown, debridgeRejection?: unknown): BridgeQuoteResult;
|
|
72
76
|
/**
|
|
73
77
|
* Interpolate retention rate from samples
|
|
74
78
|
*/
|
package/dist/quote-utils.js
CHANGED
|
@@ -113,18 +113,74 @@ function debridgeHasTransactionData(quote) {
|
|
|
113
113
|
return !!(quote.tx && (quote.tx.to || quote.tx.data));
|
|
114
114
|
}
|
|
115
115
|
/**
|
|
116
|
-
*
|
|
116
|
+
* Check if a relay quote has estimation data (output amount) even without tx.
|
|
117
|
+
* Allows selecting a quote for display when API returned estimation but no tx.
|
|
117
118
|
*/
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
function relayHasEstimationData(quote) {
|
|
120
|
+
return !!(quote.details?.currencyOut?.amount != null && quote.details?.currencyOut?.amount !== '');
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Check if a deBridge quote has estimation data (output amount) even without tx.
|
|
124
|
+
*/
|
|
125
|
+
function debridgeHasEstimationData(quote) {
|
|
126
|
+
return !!(quote.estimation?.dstChainTokenOut?.amount != null &&
|
|
127
|
+
quote.estimation.dstChainTokenOut.amount !== '');
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Derive a user-facing message from quote provider rejection reasons.
|
|
131
|
+
* When any provider returns INSUFFICIENT_FUNDS / "No UTXOs available to spend", return "Insufficient funds".
|
|
132
|
+
*/
|
|
133
|
+
function getQuoteProviderErrorMessage(relayRejection, debridgeRejection) {
|
|
134
|
+
const check = (reason) => {
|
|
135
|
+
if (reason instanceof Error) {
|
|
136
|
+
const msg = reason.message;
|
|
137
|
+
if (msg.includes('Insufficient funds') ||
|
|
138
|
+
msg.includes('INSUFFICIENT_FUNDS') ||
|
|
139
|
+
msg.includes('No UTXOs available to spend')) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
// Parse JSON from "HTTP 400: {...}" style message
|
|
143
|
+
const jsonMatch = msg.match(/\{[\s\S]*\}/);
|
|
144
|
+
if (jsonMatch) {
|
|
145
|
+
try {
|
|
146
|
+
const body = JSON.parse(jsonMatch[0]);
|
|
147
|
+
if (body.errorCode === 'INSUFFICIENT_FUNDS' || (body.message && body.message.includes('UTXO'))) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// ignore
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
};
|
|
158
|
+
if (check(relayRejection) || check(debridgeRejection)) {
|
|
159
|
+
return 'Insufficient funds';
|
|
160
|
+
}
|
|
161
|
+
return 'All quote providers failed';
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Select the best quote from multiple providers based on retention rate.
|
|
165
|
+
* A quote is viable if it has transaction data (for execution) or estimation data (for display).
|
|
166
|
+
* When both providers return estimation but no tx, we still pick the best so the app can show
|
|
167
|
+
* output and refetch on amount change instead of throwing and blocking further quotes.
|
|
168
|
+
* When both fail, uses relayRejection/debridgeRejection to throw a specific message (e.g. "Insufficient funds").
|
|
169
|
+
*/
|
|
170
|
+
export function selectBestQuote(relayQuote, debridgeQuote, srcAmount, relayRejection, debridgeRejection) {
|
|
171
|
+
const hasRelayTx = relayQuote && relayHasTransactionData(relayQuote);
|
|
172
|
+
const hasRelayEst = relayQuote && relayHasEstimationData(relayQuote);
|
|
173
|
+
const hasDebridgeTx = debridgeQuote && debridgeHasTransactionData(debridgeQuote);
|
|
174
|
+
const hasDebridgeEst = debridgeQuote && debridgeHasEstimationData(debridgeQuote);
|
|
175
|
+
const viableRelay = (hasRelayTx || hasRelayEst) ? relayQuote : null;
|
|
176
|
+
const viableDebridge = (hasDebridgeTx || hasDebridgeEst) ? debridgeQuote : null;
|
|
122
177
|
// Calculate metrics only for viable providers
|
|
123
178
|
const relayMetrics = viableRelay ? calculateRelayMetrics(viableRelay) : null;
|
|
124
179
|
const debridgeMetrics = viableDebridge ? calculateDebridgeMetrics(viableDebridge) : null;
|
|
125
|
-
// Both providers failed or have no
|
|
180
|
+
// Both providers failed or have no usable response (no tx and no estimation)
|
|
126
181
|
if (!viableRelay && !viableDebridge) {
|
|
127
|
-
|
|
182
|
+
const message = getQuoteProviderErrorMessage(relayRejection, debridgeRejection);
|
|
183
|
+
throw new Error(message);
|
|
128
184
|
}
|
|
129
185
|
// Determine best provider based on retention rate
|
|
130
186
|
let bestProvider;
|
|
@@ -228,9 +284,6 @@ export async function getBridgeQuote(srcChainId, srcToken, srcAmount, dstChainId
|
|
|
228
284
|
signal, recipientAddress, // Optional recipient address (required for Solana destinations)
|
|
229
285
|
sourceAddress, // Optional source chain address (if different from userAddress)
|
|
230
286
|
forceProvider) {
|
|
231
|
-
console.log('recipientAddress', recipientAddress);
|
|
232
|
-
console.log('sourceAddress', sourceAddress);
|
|
233
|
-
console.log('userAddress', userAddress);
|
|
234
287
|
// Normalize token addresses for bridge API (handles both EVM and Solana)
|
|
235
288
|
const srcTokenNorm = normalizeTokenAddressForQuote(srcChainId, srcToken);
|
|
236
289
|
const dstTokenNorm = normalizeTokenAddressForQuote(dstChainId, dstToken);
|
|
@@ -412,7 +465,8 @@ forceProvider) {
|
|
|
412
465
|
if (err instanceof Error && err.name !== 'AbortError') {
|
|
413
466
|
console.warn('Relay quote failed:', err);
|
|
414
467
|
}
|
|
415
|
-
|
|
468
|
+
// Rethrow so Promise.allSettled captures the reason (e.g. "Insufficient funds")
|
|
469
|
+
throw err;
|
|
416
470
|
}
|
|
417
471
|
})(),
|
|
418
472
|
// deBridge quote (skipped when forceProvider === 'relay')
|
|
@@ -494,15 +548,16 @@ forceProvider) {
|
|
|
494
548
|
if (err instanceof Error && err.name !== 'AbortError') {
|
|
495
549
|
console.warn('DeBridge quote failed:', err);
|
|
496
550
|
}
|
|
497
|
-
|
|
551
|
+
// Rethrow so Promise.allSettled captures the reason (e.g. "Insufficient funds")
|
|
552
|
+
throw err;
|
|
498
553
|
}
|
|
499
554
|
})(),
|
|
500
555
|
]);
|
|
501
556
|
const relayQuote = relayResult.status === 'fulfilled' ? relayResult.value : null;
|
|
502
557
|
const debridgeQuote = debridgeResult.status === 'fulfilled' ? debridgeResult.value : null;
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
return
|
|
558
|
+
const relayRejection = relayResult.status === 'rejected' ? relayResult.reason : undefined;
|
|
559
|
+
const debridgeRejection = debridgeResult.status === 'rejected' ? debridgeResult.reason : undefined;
|
|
560
|
+
return selectBestQuote(relayQuote, debridgeQuote, srcAmount, relayRejection, debridgeRejection);
|
|
506
561
|
}
|
|
507
562
|
/**
|
|
508
563
|
* Convert BridgeQuoteResult to BridgeQuote with transaction data
|