@cowprotocol/sdk-bridging 0.3.2-beta.0 → 0.3.2
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/README.md +265 -0
- package/dist/index.d.mts +128 -24
- package/dist/index.d.ts +128 -24
- package/dist/index.js +546 -170
- package/dist/index.mjs +546 -170
- package/package.json +13 -13
package/dist/index.mjs
CHANGED
|
@@ -76,6 +76,83 @@ function isAppDoc(appData) {
|
|
|
76
76
|
return typeof appData === "object" && appData !== null && "version" in appData && "metadata" in appData;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
// src/const.ts
|
|
80
|
+
import { RAW_FILES_PATH } from "@cowprotocol/sdk-config";
|
|
81
|
+
var RAW_PROVIDERS_FILES_PATH = `${RAW_FILES_PATH}/src/bridging/providers`;
|
|
82
|
+
var DEFAULT_GAS_COST_FOR_HOOK_ESTIMATION = 24e4;
|
|
83
|
+
var DEFAULT_EXTRA_GAS_FOR_HOOK_ESTIMATION = 2e5;
|
|
84
|
+
var COW_SHED_PROXY_CREATION_GAS = 36e4;
|
|
85
|
+
var DEFAULT_EXTRA_GAS_PROXY_CREATION = 4e5;
|
|
86
|
+
var HOOK_DAPP_BRIDGE_PROVIDER_PREFIX = "cow-sdk://bridging/providers";
|
|
87
|
+
|
|
88
|
+
// src/BridgingSdk/findBridgeProviderFromHook.ts
|
|
89
|
+
function findBridgeProviderFromHook(fullAppData, providers) {
|
|
90
|
+
const postHooks = getPostHooks(fullAppData);
|
|
91
|
+
const bridgingHook = postHooks.find((hook) => {
|
|
92
|
+
return hook.dappId?.startsWith(HOOK_DAPP_BRIDGE_PROVIDER_PREFIX);
|
|
93
|
+
});
|
|
94
|
+
if (!bridgingHook) {
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
const bridgeProviderDappId = bridgingHook.dappId;
|
|
98
|
+
return providers.find((provider) => provider.info.dappId === bridgeProviderDappId);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/BridgingSdk/getCrossChainOrder.ts
|
|
102
|
+
async function getCrossChainOrder(params) {
|
|
103
|
+
const { chainId, orderId, orderBookApi, providers, env } = params;
|
|
104
|
+
const chainContext = { chainId, env };
|
|
105
|
+
const order = await orderBookApi.getOrder(orderId, chainContext);
|
|
106
|
+
const provider = order.fullAppData && findBridgeProviderFromHook(order.fullAppData, providers);
|
|
107
|
+
if (!provider) {
|
|
108
|
+
throw new BridgeOrderParsingError(
|
|
109
|
+
`Unknown Bridge provider in order ${order.uid}. Add provider to the SDK config to be able to decode the order`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const trades = await orderBookApi.getTrades({ orderUid: order.uid }, chainContext);
|
|
113
|
+
if (trades.length > 0) {
|
|
114
|
+
const firstTrade = trades[0];
|
|
115
|
+
const tradeTxHash = firstTrade?.txHash;
|
|
116
|
+
if (!tradeTxHash) {
|
|
117
|
+
throw new BridgeOrderParsingError(
|
|
118
|
+
`No tx hash found for order ${orderId} . First trade, with log index ${firstTrade?.logIndex}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
const { params: bridgingParams, status: statusResult } = await provider.getBridgingParams(chainId, orderId, tradeTxHash) || {};
|
|
122
|
+
if (!bridgingParams || !statusResult) {
|
|
123
|
+
throw new BridgeOrderParsingError(`Bridging params cannot be derived from transaction: ${tradeTxHash}`);
|
|
124
|
+
}
|
|
125
|
+
const state = {
|
|
126
|
+
provider,
|
|
127
|
+
chainId,
|
|
128
|
+
order,
|
|
129
|
+
statusResult: {
|
|
130
|
+
status: "unknown" /* UNKNOWN */
|
|
131
|
+
},
|
|
132
|
+
bridgingParams,
|
|
133
|
+
tradeTxHash
|
|
134
|
+
};
|
|
135
|
+
try {
|
|
136
|
+
const explorerUrl = provider.getExplorerUrl(bridgingParams.bridgingId);
|
|
137
|
+
return {
|
|
138
|
+
...state,
|
|
139
|
+
statusResult,
|
|
140
|
+
explorerUrl
|
|
141
|
+
};
|
|
142
|
+
} catch (e) {
|
|
143
|
+
console.error("Cannot get bridging status", e);
|
|
144
|
+
return state;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/BridgingSdk/BridgingSdk.ts
|
|
151
|
+
import { TradingSdk } from "@cowprotocol/sdk-trading";
|
|
152
|
+
import { OrderBookApi } from "@cowprotocol/sdk-order-book";
|
|
153
|
+
import { ALL_SUPPORTED_CHAINS } from "@cowprotocol/sdk-config";
|
|
154
|
+
import { enableLogging, setGlobalAdapter } from "@cowprotocol/sdk-common";
|
|
155
|
+
|
|
79
156
|
// src/BridgingSdk/getQuoteWithoutBridge.ts
|
|
80
157
|
import { jsonWithBigintReplacer, log } from "@cowprotocol/sdk-common";
|
|
81
158
|
function getQuoteWithoutBridge(params) {
|
|
@@ -126,15 +203,6 @@ async function getBridgeSignedHook(bridgeRequest, { provider, signer, hookGasLim
|
|
|
126
203
|
};
|
|
127
204
|
}
|
|
128
205
|
|
|
129
|
-
// src/const.ts
|
|
130
|
-
import { RAW_FILES_PATH } from "@cowprotocol/sdk-config";
|
|
131
|
-
var RAW_PROVIDERS_FILES_PATH = `${RAW_FILES_PATH}/src/bridging/providers`;
|
|
132
|
-
var DEFAULT_GAS_COST_FOR_HOOK_ESTIMATION = 24e4;
|
|
133
|
-
var DEFAULT_EXTRA_GAS_FOR_HOOK_ESTIMATION = 2e5;
|
|
134
|
-
var COW_SHED_PROXY_CREATION_GAS = 36e4;
|
|
135
|
-
var DEFAULT_EXTRA_GAS_PROXY_CREATION = 4e5;
|
|
136
|
-
var HOOK_DAPP_BRIDGE_PROVIDER_PREFIX = "cow-sdk://bridging/providers";
|
|
137
|
-
|
|
138
206
|
// src/BridgingSdk/getQuoteWithBridge.ts
|
|
139
207
|
import { OrderKind } from "@cowprotocol/sdk-order-book";
|
|
140
208
|
import { getGlobalAdapter as getGlobalAdapter2, jsonWithBigintReplacer as jsonWithBigintReplacer2, log as log2 } from "@cowprotocol/sdk-common";
|
|
@@ -362,73 +430,274 @@ async function getBridgeResult(context) {
|
|
|
362
430
|
return { bridgeResult, bridgeHook, appDataInfo };
|
|
363
431
|
}
|
|
364
432
|
|
|
365
|
-
// src/BridgingSdk/
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
433
|
+
// src/BridgingSdk/strategies/SingleQuoteStrategy.ts
|
|
434
|
+
var SingleQuoteStrategyImpl = class {
|
|
435
|
+
strategyName = "SingleQuoteStrategy";
|
|
436
|
+
async execute(request, config) {
|
|
437
|
+
const { quoteBridgeRequest, advancedSettings } = request;
|
|
438
|
+
const { sellTokenChainId, buyTokenChainId } = quoteBridgeRequest;
|
|
439
|
+
const { tradingSdk, providers } = config;
|
|
440
|
+
if (sellTokenChainId !== buyTokenChainId) {
|
|
441
|
+
const provider = providers[0];
|
|
442
|
+
if (!provider) {
|
|
443
|
+
throw new Error("No provider found for cross-chain swap");
|
|
444
|
+
}
|
|
445
|
+
return getQuoteWithBridge({
|
|
446
|
+
swapAndBridgeRequest: quoteBridgeRequest,
|
|
447
|
+
advancedSettings,
|
|
448
|
+
tradingSdk,
|
|
449
|
+
provider,
|
|
450
|
+
bridgeHookSigner: advancedSettings?.quoteSigner
|
|
451
|
+
});
|
|
452
|
+
} else {
|
|
453
|
+
return getQuoteWithoutBridge({
|
|
454
|
+
quoteBridgeRequest,
|
|
455
|
+
advancedSettings,
|
|
456
|
+
tradingSdk
|
|
457
|
+
});
|
|
458
|
+
}
|
|
373
459
|
}
|
|
374
|
-
|
|
375
|
-
return providers.find((provider) => provider.info.dappId === bridgeProviderDappId);
|
|
376
|
-
}
|
|
460
|
+
};
|
|
377
461
|
|
|
378
|
-
// src/BridgingSdk/
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (!provider) {
|
|
385
|
-
throw new BridgeOrderParsingError(
|
|
386
|
-
`Unknown Bridge provider in order ${order.uid}. Add provider to the SDK config to be able to decode the order`
|
|
462
|
+
// src/BridgingSdk/utils.ts
|
|
463
|
+
function validateCrossChainRequest(sellTokenChainId, buyTokenChainId) {
|
|
464
|
+
if (sellTokenChainId === buyTokenChainId) {
|
|
465
|
+
throw new BridgeProviderError(
|
|
466
|
+
"getMultiQuotes() and getBestQuote() are only for cross-chain bridging. For single-chain swaps, use getQuote() instead.",
|
|
467
|
+
{ sellTokenChainId, buyTokenChainId }
|
|
387
468
|
);
|
|
388
469
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
470
|
+
}
|
|
471
|
+
function createBridgeQuoteTimeoutPromise(timeoutMs, prefix) {
|
|
472
|
+
return new Promise((_, reject) => {
|
|
473
|
+
setTimeout(() => {
|
|
474
|
+
reject(new BridgeProviderError(`${prefix} timeout after ${timeoutMs}ms`, {}));
|
|
475
|
+
}, timeoutMs);
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
function safeCallProgressiveCallback(onQuoteResult, result) {
|
|
479
|
+
if (!onQuoteResult) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
try {
|
|
483
|
+
onQuoteResult(result);
|
|
484
|
+
} catch (callbackError) {
|
|
485
|
+
console.warn("Error in onQuoteResult callback:", callbackError);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
function fillTimeoutResults(results, providersToQuery) {
|
|
489
|
+
for (let i = 0; i < providersToQuery.length; i++) {
|
|
490
|
+
const provider = providersToQuery[i];
|
|
491
|
+
if (!results[i] && provider) {
|
|
492
|
+
results[i] = {
|
|
493
|
+
providerDappId: provider.info.dappId,
|
|
494
|
+
quote: null,
|
|
495
|
+
error: new BridgeProviderError("Provider request timed out", {})
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
async function executeProviderQuotes(promises, timeout, config) {
|
|
501
|
+
try {
|
|
502
|
+
await Promise.race([
|
|
503
|
+
Promise.allSettled(promises),
|
|
504
|
+
createBridgeQuoteTimeoutPromise(timeout, `Multi-quote with ${config.providers.length}`)
|
|
505
|
+
]);
|
|
506
|
+
} catch {
|
|
507
|
+
console.warn("getMultiQuotes timeout occurred, returning partial results");
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function isBetterQuote(quote1, quote2) {
|
|
511
|
+
if (!quote2 || !quote2.quote) {
|
|
512
|
+
return !!quote1.quote;
|
|
513
|
+
}
|
|
514
|
+
if (!quote1.quote) {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
const quote1BuyAmount = quote1.quote.bridge.amountsAndCosts.afterSlippage.buyAmount;
|
|
518
|
+
const quote2BuyAmount = quote2.quote.bridge.amountsAndCosts.afterSlippage.buyAmount;
|
|
519
|
+
return quote1BuyAmount > quote2BuyAmount;
|
|
520
|
+
}
|
|
521
|
+
function safeCallBestQuoteCallback(onQuoteResult, result) {
|
|
522
|
+
if (!onQuoteResult) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
try {
|
|
526
|
+
onQuoteResult(result);
|
|
527
|
+
} catch (callbackError) {
|
|
528
|
+
console.warn("Error in onQuoteResult callback:", callbackError);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
function resolveProvidersToQuery(providerDappIds, providers) {
|
|
532
|
+
if (!providerDappIds) {
|
|
533
|
+
return providers;
|
|
534
|
+
}
|
|
535
|
+
return providerDappIds.map((dappId) => {
|
|
536
|
+
const provider = providers.find((p) => p.info.dappId === dappId);
|
|
537
|
+
if (!provider) {
|
|
538
|
+
throw new BridgeProviderError(
|
|
539
|
+
`Provider with dappId '${dappId}' not found. Available providers: ${providers.map((p) => p.info.dappId).join(", ")}`,
|
|
540
|
+
{ providers }
|
|
396
541
|
);
|
|
397
542
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
543
|
+
return provider;
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/BridgingSdk/strategies/MultiQuoteStrategy.ts
|
|
548
|
+
var DEFAULT_TOTAL_TIMEOUT_MS = 4e4;
|
|
549
|
+
var DEFAULT_PROVIDER_TIMEOUT_MS = 2e4;
|
|
550
|
+
var MultiQuoteStrategyImpl = class {
|
|
551
|
+
strategyName = "MultiQuoteStrategy";
|
|
552
|
+
async execute(request, config) {
|
|
553
|
+
const { quoteBridgeRequest, providerDappIds, advancedSettings, options } = request;
|
|
554
|
+
const { sellTokenChainId, buyTokenChainId } = quoteBridgeRequest;
|
|
555
|
+
validateCrossChainRequest(sellTokenChainId, buyTokenChainId);
|
|
556
|
+
const providersToQuery = resolveProvidersToQuery(providerDappIds, config.providers);
|
|
557
|
+
const {
|
|
558
|
+
onQuoteResult,
|
|
559
|
+
totalTimeout = DEFAULT_TOTAL_TIMEOUT_MS,
|
|
560
|
+
providerTimeout = DEFAULT_PROVIDER_TIMEOUT_MS
|
|
561
|
+
} = options || {};
|
|
562
|
+
const results = [];
|
|
563
|
+
const promises = [];
|
|
564
|
+
for (let i = 0; i < providersToQuery.length; i++) {
|
|
565
|
+
const provider = providersToQuery[i];
|
|
566
|
+
if (!provider) {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
const context = {
|
|
570
|
+
provider,
|
|
571
|
+
quoteBridgeRequest,
|
|
572
|
+
advancedSettings,
|
|
573
|
+
providerTimeout,
|
|
574
|
+
onQuoteResult,
|
|
575
|
+
results,
|
|
576
|
+
index: i
|
|
577
|
+
};
|
|
578
|
+
const promise = this.createProviderQuotePromise(context, config);
|
|
579
|
+
promises.push(promise);
|
|
401
580
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
581
|
+
await executeProviderQuotes(promises, totalTimeout, config);
|
|
582
|
+
fillTimeoutResults(results, providersToQuery);
|
|
583
|
+
results.sort((a, b) => {
|
|
584
|
+
if (isBetterQuote(a, b))
|
|
585
|
+
return -1;
|
|
586
|
+
if (isBetterQuote(b, a))
|
|
587
|
+
return 1;
|
|
588
|
+
return 0;
|
|
589
|
+
});
|
|
590
|
+
return results;
|
|
591
|
+
}
|
|
592
|
+
createProviderQuotePromise(context, config) {
|
|
593
|
+
const { provider, quoteBridgeRequest, advancedSettings, providerTimeout, onQuoteResult, results, index } = context;
|
|
594
|
+
return (async () => {
|
|
595
|
+
try {
|
|
596
|
+
const quote = await Promise.race([
|
|
597
|
+
getQuoteWithBridge({
|
|
598
|
+
swapAndBridgeRequest: quoteBridgeRequest,
|
|
599
|
+
advancedSettings,
|
|
600
|
+
tradingSdk: config.tradingSdk,
|
|
601
|
+
provider,
|
|
602
|
+
bridgeHookSigner: advancedSettings?.quoteSigner
|
|
603
|
+
}),
|
|
604
|
+
createBridgeQuoteTimeoutPromise(providerTimeout, `Provider ${provider.info.dappId}`)
|
|
605
|
+
]);
|
|
606
|
+
const result = {
|
|
607
|
+
providerDappId: provider.info.dappId,
|
|
608
|
+
quote,
|
|
609
|
+
error: void 0
|
|
610
|
+
};
|
|
611
|
+
results[index] = result;
|
|
612
|
+
safeCallProgressiveCallback(onQuoteResult, result);
|
|
613
|
+
} catch (error) {
|
|
614
|
+
const result = {
|
|
615
|
+
providerDappId: provider.info.dappId,
|
|
616
|
+
quote: null,
|
|
617
|
+
error: error instanceof Error ? error : new BridgeProviderError(String(error), {})
|
|
618
|
+
};
|
|
619
|
+
results[index] = result;
|
|
620
|
+
safeCallProgressiveCallback(onQuoteResult, result);
|
|
621
|
+
}
|
|
622
|
+
})();
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
// src/BridgingSdk/strategies/BestQuoteStrategy.ts
|
|
627
|
+
var DEFAULT_TOTAL_TIMEOUT_MS2 = 4e4;
|
|
628
|
+
var DEFAULT_PROVIDER_TIMEOUT_MS2 = 2e4;
|
|
629
|
+
var BestQuoteStrategyImpl = class {
|
|
630
|
+
strategyName = "BestQuoteStrategy";
|
|
631
|
+
async execute(request, config) {
|
|
632
|
+
const { quoteBridgeRequest, providerDappIds, advancedSettings, options } = request;
|
|
633
|
+
const { sellTokenChainId, buyTokenChainId } = quoteBridgeRequest;
|
|
634
|
+
validateCrossChainRequest(sellTokenChainId, buyTokenChainId);
|
|
635
|
+
const providersToQuery = resolveProvidersToQuery(providerDappIds, config.providers);
|
|
636
|
+
const {
|
|
637
|
+
onQuoteResult,
|
|
638
|
+
totalTimeout = DEFAULT_TOTAL_TIMEOUT_MS2,
|
|
639
|
+
providerTimeout = DEFAULT_PROVIDER_TIMEOUT_MS2
|
|
640
|
+
} = options || {};
|
|
641
|
+
const bestResult = { current: null };
|
|
642
|
+
const firstError = { current: null };
|
|
643
|
+
const promises = [];
|
|
644
|
+
for (const provider of providersToQuery) {
|
|
645
|
+
const context = {
|
|
646
|
+
provider,
|
|
647
|
+
quoteBridgeRequest,
|
|
648
|
+
advancedSettings,
|
|
649
|
+
providerTimeout,
|
|
650
|
+
onQuoteResult,
|
|
651
|
+
bestResult,
|
|
652
|
+
firstError
|
|
418
653
|
};
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
return state;
|
|
654
|
+
const promise = this.createBestQuoteProviderPromise(context, config);
|
|
655
|
+
promises.push(promise);
|
|
422
656
|
}
|
|
657
|
+
await executeProviderQuotes(promises, totalTimeout, config);
|
|
658
|
+
return bestResult.current || firstError.current;
|
|
423
659
|
}
|
|
424
|
-
|
|
425
|
-
}
|
|
660
|
+
createBestQuoteProviderPromise(context, config) {
|
|
661
|
+
const { provider, quoteBridgeRequest, advancedSettings, providerTimeout, onQuoteResult, bestResult, firstError } = context;
|
|
662
|
+
return (async () => {
|
|
663
|
+
try {
|
|
664
|
+
const quote = await Promise.race([
|
|
665
|
+
getQuoteWithBridge({
|
|
666
|
+
swapAndBridgeRequest: quoteBridgeRequest,
|
|
667
|
+
advancedSettings,
|
|
668
|
+
tradingSdk: config.tradingSdk,
|
|
669
|
+
provider,
|
|
670
|
+
bridgeHookSigner: advancedSettings?.quoteSigner
|
|
671
|
+
}),
|
|
672
|
+
createBridgeQuoteTimeoutPromise(providerTimeout, `Provider ${provider.info.dappId}`)
|
|
673
|
+
]);
|
|
674
|
+
const result = {
|
|
675
|
+
providerDappId: provider.info.dappId,
|
|
676
|
+
quote,
|
|
677
|
+
error: void 0
|
|
678
|
+
};
|
|
679
|
+
if (isBetterQuote(result, bestResult.current)) {
|
|
680
|
+
bestResult.current = result;
|
|
681
|
+
safeCallBestQuoteCallback(onQuoteResult, result);
|
|
682
|
+
}
|
|
683
|
+
} catch (error) {
|
|
684
|
+
const errorResult = {
|
|
685
|
+
providerDappId: provider.info.dappId,
|
|
686
|
+
quote: null,
|
|
687
|
+
error: error instanceof Error ? error : new BridgeProviderError(String(error), {})
|
|
688
|
+
};
|
|
689
|
+
if (!firstError.current) {
|
|
690
|
+
firstError.current = errorResult;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
})();
|
|
694
|
+
}
|
|
695
|
+
};
|
|
426
696
|
|
|
427
697
|
// src/BridgingSdk/BridgingSdk.ts
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
import { enableLogging, setGlobalAdapter } from "@cowprotocol/sdk-common";
|
|
698
|
+
var singleQuoteStrategy = new SingleQuoteStrategyImpl();
|
|
699
|
+
var multiQuoteStrategy = new MultiQuoteStrategyImpl();
|
|
700
|
+
var bestQuoteStrategy = new BestQuoteStrategyImpl();
|
|
432
701
|
var BridgingSdk = class {
|
|
433
702
|
constructor(options, adapter) {
|
|
434
703
|
this.options = options;
|
|
@@ -436,8 +705,8 @@ var BridgingSdk = class {
|
|
|
436
705
|
setGlobalAdapter(adapter);
|
|
437
706
|
}
|
|
438
707
|
const { providers, ...restOptions } = options;
|
|
439
|
-
if (!providers || providers.length
|
|
440
|
-
throw new Error("
|
|
708
|
+
if (!providers || providers.length === 0) {
|
|
709
|
+
throw new Error("At least one bridge provider is required");
|
|
441
710
|
}
|
|
442
711
|
if (options.enableLogging !== void 0) {
|
|
443
712
|
enableLogging(options.enableLogging);
|
|
@@ -501,23 +770,47 @@ var BridgingSdk = class {
|
|
|
501
770
|
* @throws Error if no path is found
|
|
502
771
|
*/
|
|
503
772
|
async getQuote(quoteBridgeRequest, advancedSettings) {
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
773
|
+
const request = {
|
|
774
|
+
quoteBridgeRequest,
|
|
775
|
+
advancedSettings
|
|
776
|
+
};
|
|
777
|
+
return singleQuoteStrategy.execute(request, this.config);
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Get quotes from multiple bridge providers in parallel with progressive results.
|
|
781
|
+
*
|
|
782
|
+
* This method is specifically for cross-chain bridging quotes. For single-chain swaps, use getQuote() instead.
|
|
783
|
+
*
|
|
784
|
+
* Features:
|
|
785
|
+
* - Progressive results: Use the `onQuoteResult` callback to receive quotes as soon as each provider responds
|
|
786
|
+
* - Timeout support: Configure maximum wait time for all providers and individual provider timeouts
|
|
787
|
+
* - Parallel execution: All providers are queried simultaneously for best performance
|
|
788
|
+
*
|
|
789
|
+
* @param request - The multi-quote request containing quote parameters, provider dappIds, and options
|
|
790
|
+
* @returns Array of results, one for each provider (successful quotes or errors)
|
|
791
|
+
* @throws Error if the request is for a single-chain swap (sellTokenChainId === buyTokenChainId)
|
|
792
|
+
* ```
|
|
793
|
+
*/
|
|
794
|
+
async getMultiQuotes(request) {
|
|
795
|
+
return multiQuoteStrategy.execute(request, this.config);
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Get the best quote from multiple bridge providers with progressive updates.
|
|
799
|
+
*
|
|
800
|
+
* This method is specifically for cross-chain bridging quotes. For single-chain swaps, use getQuote() instead.
|
|
801
|
+
*
|
|
802
|
+
* Features:
|
|
803
|
+
* - Returns only the best quote based on buyAmount after slippage
|
|
804
|
+
* - Progressive updates: Use the `onQuoteResult` callback to receive updates whenever a better quote is found
|
|
805
|
+
* - Timeout support: Configure maximum wait time for all providers and individual provider timeouts
|
|
806
|
+
* - Parallel execution: All providers are queried simultaneously for best performance
|
|
807
|
+
*
|
|
808
|
+
* @param request - The best quote request containing quote parameters, provider dappIds, and options
|
|
809
|
+
* @returns The best quote result found, or null if no successful quotes were obtained
|
|
810
|
+
* @throws Error if the request is for a single-chain swap (sellTokenChainId === buyTokenChainId)
|
|
811
|
+
*/
|
|
812
|
+
async getBestQuote(request) {
|
|
813
|
+
return bestQuoteStrategy.execute(request, this.config);
|
|
521
814
|
}
|
|
522
815
|
async getOrder(params) {
|
|
523
816
|
const { orderBookApi } = this.config;
|
|
@@ -536,6 +829,9 @@ var BridgingSdk = class {
|
|
|
536
829
|
getProviderFromAppData(fullAppData) {
|
|
537
830
|
return findBridgeProviderFromHook(fullAppData, this.getProviders());
|
|
538
831
|
}
|
|
832
|
+
getProviderByDappId(dappId) {
|
|
833
|
+
return this.config.providers.find((provider) => provider.info.dappId === dappId);
|
|
834
|
+
}
|
|
539
835
|
};
|
|
540
836
|
|
|
541
837
|
// src/providers/across/AcrossApi.ts
|
|
@@ -3395,9 +3691,13 @@ var BUNGEE_APPROVE_AND_BRIDGE_V1_ABI = [
|
|
|
3395
3691
|
|
|
3396
3692
|
// src/providers/bungee/BungeeApi.ts
|
|
3397
3693
|
import { getGlobalAdapter as getGlobalAdapter8, log as log5 } from "@cowprotocol/sdk-common";
|
|
3694
|
+
|
|
3695
|
+
// src/providers/bungee/consts.ts
|
|
3696
|
+
var BUNGEE_API_PATH = "/api/v1/bungee";
|
|
3697
|
+
var BUNGEE_MANUAL_API_PATH = "/api/v1/bungee-manual";
|
|
3398
3698
|
var BUNGEE_BASE_URL = "https://public-backend.bungee.exchange";
|
|
3399
|
-
var BUNGEE_API_URL = `${BUNGEE_BASE_URL}
|
|
3400
|
-
var BUNGEE_MANUAL_API_URL = `${BUNGEE_BASE_URL}
|
|
3699
|
+
var BUNGEE_API_URL = `${BUNGEE_BASE_URL}${BUNGEE_API_PATH}`;
|
|
3700
|
+
var BUNGEE_MANUAL_API_URL = `${BUNGEE_BASE_URL}${BUNGEE_MANUAL_API_PATH}`;
|
|
3401
3701
|
var BUNGEE_EVENTS_API_URL = "https://microservices.socket.tech/loki";
|
|
3402
3702
|
var ACROSS_API_URL2 = "https://app.across.to/api";
|
|
3403
3703
|
var SUPPORTED_BRIDGES = ["across", "cctp", "gnosis-native-bridge"];
|
|
@@ -3407,16 +3707,98 @@ var errorMessageMap = {
|
|
|
3407
3707
|
across: "Across Api Error",
|
|
3408
3708
|
"bungee-manual": "Bungee Manual Api Error"
|
|
3409
3709
|
};
|
|
3710
|
+
var DEFAULT_API_OPTIONS = {
|
|
3711
|
+
apiBaseUrl: BUNGEE_API_URL,
|
|
3712
|
+
eventsApiBaseUrl: BUNGEE_EVENTS_API_URL,
|
|
3713
|
+
acrossApiBaseUrl: ACROSS_API_URL2,
|
|
3714
|
+
manualApiBaseUrl: BUNGEE_MANUAL_API_URL
|
|
3715
|
+
};
|
|
3716
|
+
var BUNGEE_API_FALLBACK_TIMEOUT = 3e5;
|
|
3717
|
+
|
|
3718
|
+
// src/providers/bungee/apiUtils.ts
|
|
3719
|
+
function isValidQuoteResponse(response) {
|
|
3720
|
+
if (typeof response !== "object" || response === null) {
|
|
3721
|
+
return false;
|
|
3722
|
+
}
|
|
3723
|
+
const resp = response;
|
|
3724
|
+
if (!("success" in resp) || !("statusCode" in resp) || !("result" in resp) || typeof resp.success !== "boolean" || typeof resp.statusCode !== "number") {
|
|
3725
|
+
return false;
|
|
3726
|
+
}
|
|
3727
|
+
const result = resp.result;
|
|
3728
|
+
if (typeof result !== "object" || result === null) {
|
|
3729
|
+
return false;
|
|
3730
|
+
}
|
|
3731
|
+
const res = result;
|
|
3732
|
+
if (!("originChainId" in res) || !("destinationChainId" in res) || !("userAddress" in res) || !("receiverAddress" in res) || !("manualRoutes" in res) || !Array.isArray(res.manualRoutes)) {
|
|
3733
|
+
return false;
|
|
3734
|
+
}
|
|
3735
|
+
return res.manualRoutes.every((route) => {
|
|
3736
|
+
if (typeof route !== "object" || route === null) {
|
|
3737
|
+
return false;
|
|
3738
|
+
}
|
|
3739
|
+
const r = route;
|
|
3740
|
+
if (!("routeDetails" in r) || typeof r.routeDetails !== "object" || r.routeDetails === null) {
|
|
3741
|
+
return false;
|
|
3742
|
+
}
|
|
3743
|
+
if (!("routeFee" in r.routeDetails)) {
|
|
3744
|
+
return false;
|
|
3745
|
+
}
|
|
3746
|
+
const routeFee = r.routeDetails.routeFee;
|
|
3747
|
+
if (typeof routeFee !== "object" || routeFee === null) {
|
|
3748
|
+
return false;
|
|
3749
|
+
}
|
|
3750
|
+
if (!("amount" in routeFee)) {
|
|
3751
|
+
return false;
|
|
3752
|
+
}
|
|
3753
|
+
return "quoteId" in r && "quoteExpiry" in r && "output" in r && "gasFee" in r && "slippage" in r && "estimatedTime" in r && "routeDetails" in r;
|
|
3754
|
+
});
|
|
3755
|
+
}
|
|
3756
|
+
function isValidBungeeEventsResponse(response) {
|
|
3757
|
+
if (typeof response !== "object" || response === null) {
|
|
3758
|
+
return false;
|
|
3759
|
+
}
|
|
3760
|
+
const resp = response;
|
|
3761
|
+
if (!("success" in resp) || !("result" in resp) || typeof resp.success !== "boolean" || !Array.isArray(resp.result)) {
|
|
3762
|
+
return false;
|
|
3763
|
+
}
|
|
3764
|
+
return resp.result.every((event) => {
|
|
3765
|
+
if (typeof event !== "object" || event === null) {
|
|
3766
|
+
return false;
|
|
3767
|
+
}
|
|
3768
|
+
const e = event;
|
|
3769
|
+
return "identifier" in e && "bridgeName" in e && "fromChainId" in e && "isCowswapTrade" in e && "orderId" in e && // 'recipient' in e &&
|
|
3770
|
+
"sender" in e && "srcTxStatus" in e && "destTxStatus" in e;
|
|
3771
|
+
});
|
|
3772
|
+
}
|
|
3773
|
+
function isValidAcrossStatusResponse(response) {
|
|
3774
|
+
if (typeof response !== "object" || response === null) {
|
|
3775
|
+
return false;
|
|
3776
|
+
}
|
|
3777
|
+
const resp = response;
|
|
3778
|
+
if (!("status" in resp)) {
|
|
3779
|
+
return false;
|
|
3780
|
+
}
|
|
3781
|
+
return true;
|
|
3782
|
+
}
|
|
3783
|
+
function isInfrastructureError(status) {
|
|
3784
|
+
return status >= 500 || status === 429;
|
|
3785
|
+
}
|
|
3786
|
+
function isClientFetchError(error) {
|
|
3787
|
+
return error instanceof TypeError || error instanceof Error && error.message?.includes("fetch");
|
|
3788
|
+
}
|
|
3789
|
+
function resolveApiEndpointFromOptions(key, options, useFallback, customUrl) {
|
|
3790
|
+
return useFallback ? DEFAULT_API_OPTIONS[key] : customUrl || options[key] || DEFAULT_API_OPTIONS[key];
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
// src/providers/bungee/BungeeApi.ts
|
|
3410
3794
|
var BungeeApi = class {
|
|
3411
|
-
constructor(options = {
|
|
3412
|
-
apiBaseUrl: BUNGEE_API_URL,
|
|
3413
|
-
eventsApiBaseUrl: BUNGEE_EVENTS_API_URL,
|
|
3414
|
-
acrossApiBaseUrl: ACROSS_API_URL2,
|
|
3415
|
-
includeBridges: SUPPORTED_BRIDGES
|
|
3416
|
-
}) {
|
|
3795
|
+
constructor(options = DEFAULT_API_OPTIONS) {
|
|
3417
3796
|
this.options = options;
|
|
3418
3797
|
this.validateBridges(this.getSupportedBridges());
|
|
3798
|
+
this.fallbackTimeoutMs = this.options.fallbackTimeoutMs ?? BUNGEE_API_FALLBACK_TIMEOUT;
|
|
3419
3799
|
}
|
|
3800
|
+
fallbackStates = /* @__PURE__ */ new Map();
|
|
3801
|
+
fallbackTimeoutMs;
|
|
3420
3802
|
// TODO: why do we need options.includeBridges then? Practically, you cannot add more bridges dynamically
|
|
3421
3803
|
validateBridges(includeBridges) {
|
|
3422
3804
|
if (includeBridges?.some((bridge) => !SUPPORTED_BRIDGES.includes(bridge))) {
|
|
@@ -3644,100 +4026,94 @@ var BungeeApi = class {
|
|
|
3644
4026
|
getSupportedBridges(bridges) {
|
|
3645
4027
|
return bridges ?? this.options.includeBridges ?? SUPPORTED_BRIDGES;
|
|
3646
4028
|
}
|
|
4029
|
+
isBungeeApi(apiType) {
|
|
4030
|
+
return apiType === "bungee" || apiType === "bungee-manual";
|
|
4031
|
+
}
|
|
4032
|
+
shouldAddApiKey(apiType) {
|
|
4033
|
+
return this.isBungeeApi(apiType) && !!this.options.apiKey && !!this.options.customApiBaseUrl;
|
|
4034
|
+
}
|
|
4035
|
+
shouldUseFallback(apiType) {
|
|
4036
|
+
const fallbackState = this.fallbackStates.get(apiType);
|
|
4037
|
+
if (!fallbackState)
|
|
4038
|
+
return false;
|
|
4039
|
+
const now = Date.now();
|
|
4040
|
+
if (now > fallbackState.fallbackExpiresAt) {
|
|
4041
|
+
this.fallbackStates.delete(apiType);
|
|
4042
|
+
return false;
|
|
4043
|
+
}
|
|
4044
|
+
return fallbackState.isUsingFallback;
|
|
4045
|
+
}
|
|
4046
|
+
enableFallback(apiType) {
|
|
4047
|
+
const now = Date.now();
|
|
4048
|
+
this.fallbackStates.set(apiType, {
|
|
4049
|
+
isUsingFallback: true,
|
|
4050
|
+
fallbackExpiresAt: now + this.fallbackTimeoutMs
|
|
4051
|
+
});
|
|
4052
|
+
}
|
|
3647
4053
|
shouldAddAffiliate(apiType, baseUrl) {
|
|
3648
|
-
|
|
3649
|
-
|
|
4054
|
+
if (!this.isBungeeApi(apiType))
|
|
4055
|
+
return false;
|
|
4056
|
+
const defaultHost = new URL(BUNGEE_BASE_URL).host;
|
|
4057
|
+
const baseHost = new URL(baseUrl).host;
|
|
4058
|
+
return this.shouldAddApiKey(apiType) || baseHost !== defaultHost;
|
|
3650
4059
|
}
|
|
3651
4060
|
async makeApiCall(apiType, path, params, isValidResponse) {
|
|
4061
|
+
const useFallback = this.shouldUseFallback(apiType);
|
|
4062
|
+
const customApiBaseUrl = this.options.apiKey ? this.options.customApiBaseUrl : void 0;
|
|
3652
4063
|
const baseUrlMap = {
|
|
3653
|
-
bungee:
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
4064
|
+
bungee: resolveApiEndpointFromOptions(
|
|
4065
|
+
"apiBaseUrl",
|
|
4066
|
+
this.options,
|
|
4067
|
+
useFallback,
|
|
4068
|
+
customApiBaseUrl ? `${customApiBaseUrl}${BUNGEE_API_PATH}` : void 0
|
|
4069
|
+
),
|
|
4070
|
+
"bungee-manual": resolveApiEndpointFromOptions(
|
|
4071
|
+
"manualApiBaseUrl",
|
|
4072
|
+
this.options,
|
|
4073
|
+
useFallback,
|
|
4074
|
+
customApiBaseUrl ? `${customApiBaseUrl}${BUNGEE_MANUAL_API_PATH}` : void 0
|
|
4075
|
+
),
|
|
4076
|
+
events: resolveApiEndpointFromOptions("eventsApiBaseUrl", this.options, useFallback),
|
|
4077
|
+
across: resolveApiEndpointFromOptions("acrossApiBaseUrl", this.options, useFallback)
|
|
3657
4078
|
};
|
|
3658
4079
|
const baseUrl = baseUrlMap[apiType];
|
|
3659
4080
|
const url = `${baseUrl}${path}?${new URLSearchParams(params).toString()}`;
|
|
3660
4081
|
const headers = {};
|
|
4082
|
+
if (this.shouldAddApiKey(apiType) && this.options.apiKey) {
|
|
4083
|
+
headers["x-api-key"] = this.options.apiKey;
|
|
4084
|
+
}
|
|
3661
4085
|
if (this.shouldAddAffiliate(apiType, baseUrl) && this.options.affiliate) {
|
|
3662
4086
|
headers["affiliate"] = this.options.affiliate;
|
|
3663
4087
|
}
|
|
3664
4088
|
log5(`Fetching ${apiType} API: GET ${url}. Params: ${JSON.stringify(params)}`);
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
4089
|
+
try {
|
|
4090
|
+
const response = await fetch(url, { method: "GET", headers });
|
|
4091
|
+
if (!response.ok) {
|
|
4092
|
+
if (isInfrastructureError(response.status) && !useFallback) {
|
|
4093
|
+
this.enableFallback(apiType);
|
|
4094
|
+
log5(
|
|
4095
|
+
`Infrastructure error (${response.status}) detected for ${apiType} API. Enabling fallback for ${this.fallbackTimeoutMs}ms`
|
|
4096
|
+
);
|
|
4097
|
+
return this.makeApiCall(apiType, path, params, isValidResponse);
|
|
4098
|
+
}
|
|
4099
|
+
const errorBody = await response.json();
|
|
4100
|
+
throw new BridgeProviderQuoteError("API_ERROR" /* API_ERROR */, { errorBody, type: errorMessageMap[apiType] });
|
|
4101
|
+
}
|
|
4102
|
+
const json = await response.json();
|
|
4103
|
+
if (isValidResponse && !isValidResponse(json)) {
|
|
4104
|
+
throw new BridgeProviderQuoteError("INVALID_API_JSON_RESPONSE" /* INVALID_API_JSON_RESPONSE */, { json, apiType, params });
|
|
4105
|
+
}
|
|
4106
|
+
return json;
|
|
4107
|
+
} catch (error) {
|
|
4108
|
+
if (!useFallback && isClientFetchError(error)) {
|
|
4109
|
+
this.enableFallback(apiType);
|
|
4110
|
+
log5(`Network error detected for ${apiType} API. Enabling fallback for ${this.fallbackTimeoutMs}ms`);
|
|
4111
|
+
return this.makeApiCall(apiType, path, params, isValidResponse);
|
|
4112
|
+
}
|
|
4113
|
+
throw error;
|
|
3673
4114
|
}
|
|
3674
|
-
return json;
|
|
3675
4115
|
}
|
|
3676
4116
|
};
|
|
3677
|
-
function isValidQuoteResponse(response) {
|
|
3678
|
-
if (typeof response !== "object" || response === null) {
|
|
3679
|
-
return false;
|
|
3680
|
-
}
|
|
3681
|
-
const resp = response;
|
|
3682
|
-
if (!("success" in resp) || !("statusCode" in resp) || !("result" in resp) || typeof resp.success !== "boolean" || typeof resp.statusCode !== "number") {
|
|
3683
|
-
return false;
|
|
3684
|
-
}
|
|
3685
|
-
const result = resp.result;
|
|
3686
|
-
if (typeof result !== "object" || result === null) {
|
|
3687
|
-
return false;
|
|
3688
|
-
}
|
|
3689
|
-
const res = result;
|
|
3690
|
-
if (!("originChainId" in res) || !("destinationChainId" in res) || !("userAddress" in res) || !("receiverAddress" in res) || !("manualRoutes" in res) || !Array.isArray(res.manualRoutes)) {
|
|
3691
|
-
return false;
|
|
3692
|
-
}
|
|
3693
|
-
return res.manualRoutes.every((route) => {
|
|
3694
|
-
if (typeof route !== "object" || route === null) {
|
|
3695
|
-
return false;
|
|
3696
|
-
}
|
|
3697
|
-
const r = route;
|
|
3698
|
-
if (!("routeDetails" in r) || typeof r.routeDetails !== "object" || r.routeDetails === null) {
|
|
3699
|
-
return false;
|
|
3700
|
-
}
|
|
3701
|
-
if (!("routeFee" in r.routeDetails)) {
|
|
3702
|
-
return false;
|
|
3703
|
-
}
|
|
3704
|
-
const routeFee = r.routeDetails.routeFee;
|
|
3705
|
-
if (typeof routeFee !== "object" || routeFee === null) {
|
|
3706
|
-
return false;
|
|
3707
|
-
}
|
|
3708
|
-
if (!("amount" in routeFee)) {
|
|
3709
|
-
return false;
|
|
3710
|
-
}
|
|
3711
|
-
return "quoteId" in r && "quoteExpiry" in r && "output" in r && "gasFee" in r && "slippage" in r && "estimatedTime" in r && "routeDetails" in r;
|
|
3712
|
-
});
|
|
3713
|
-
}
|
|
3714
|
-
function isValidBungeeEventsResponse(response) {
|
|
3715
|
-
if (typeof response !== "object" || response === null) {
|
|
3716
|
-
return false;
|
|
3717
|
-
}
|
|
3718
|
-
const resp = response;
|
|
3719
|
-
if (!("success" in resp) || !("result" in resp) || typeof resp.success !== "boolean" || !Array.isArray(resp.result)) {
|
|
3720
|
-
return false;
|
|
3721
|
-
}
|
|
3722
|
-
return resp.result.every((event) => {
|
|
3723
|
-
if (typeof event !== "object" || event === null) {
|
|
3724
|
-
return false;
|
|
3725
|
-
}
|
|
3726
|
-
const e = event;
|
|
3727
|
-
return "identifier" in e && "bridgeName" in e && "fromChainId" in e && "isCowswapTrade" in e && "orderId" in e && // 'recipient' in e &&
|
|
3728
|
-
"sender" in e && "srcTxStatus" in e && "destTxStatus" in e;
|
|
3729
|
-
});
|
|
3730
|
-
}
|
|
3731
|
-
function isValidAcrossStatusResponse(response) {
|
|
3732
|
-
if (typeof response !== "object" || response === null) {
|
|
3733
|
-
return false;
|
|
3734
|
-
}
|
|
3735
|
-
const resp = response;
|
|
3736
|
-
if (!("status" in resp)) {
|
|
3737
|
-
return false;
|
|
3738
|
-
}
|
|
3739
|
-
return true;
|
|
3740
|
-
}
|
|
3741
4117
|
|
|
3742
4118
|
// src/providers/bungee/createBungeeDepositCall.ts
|
|
3743
4119
|
import { ETH_ADDRESS } from "@cowprotocol/sdk-config";
|