@metamask/bridge-controller 50.0.0 → 51.0.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.
- package/CHANGELOG.md +24 -1
- package/dist/bridge-controller.cjs +109 -169
- package/dist/bridge-controller.cjs.map +1 -1
- package/dist/bridge-controller.d.cts +5 -4
- package/dist/bridge-controller.d.cts.map +1 -1
- package/dist/bridge-controller.d.mts +5 -4
- package/dist/bridge-controller.d.mts.map +1 -1
- package/dist/bridge-controller.mjs +113 -173
- package/dist/bridge-controller.mjs.map +1 -1
- package/dist/selectors.d.cts +22 -0
- package/dist/selectors.d.cts.map +1 -1
- package/dist/selectors.d.mts +22 -0
- package/dist/selectors.d.mts.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +21 -10
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +21 -10
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils/feature-flags.d.cts +1 -0
- package/dist/utils/feature-flags.d.cts.map +1 -1
- package/dist/utils/feature-flags.d.mts +1 -0
- package/dist/utils/feature-flags.d.mts.map +1 -1
- package/dist/utils/fetch.cjs +97 -19
- package/dist/utils/fetch.cjs.map +1 -1
- package/dist/utils/fetch.d.cts +23 -2
- package/dist/utils/fetch.d.cts.map +1 -1
- package/dist/utils/fetch.d.mts +23 -2
- package/dist/utils/fetch.d.mts.map +1 -1
- package/dist/utils/fetch.mjs +95 -18
- package/dist/utils/fetch.mjs.map +1 -1
- package/dist/utils/quote-fees.cjs +120 -0
- package/dist/utils/quote-fees.cjs.map +1 -0
- package/dist/utils/quote-fees.d.cts +18 -0
- package/dist/utils/quote-fees.d.cts.map +1 -0
- package/dist/utils/quote-fees.d.mts +18 -0
- package/dist/utils/quote-fees.d.mts.map +1 -0
- package/dist/utils/quote-fees.mjs +116 -0
- package/dist/utils/quote-fees.mjs.map +1 -0
- package/dist/utils/quote.cjs +12 -1
- package/dist/utils/quote.cjs.map +1 -1
- package/dist/utils/quote.d.cts +2 -0
- package/dist/utils/quote.d.cts.map +1 -1
- package/dist/utils/quote.d.mts +2 -0
- package/dist/utils/quote.d.mts.map +1 -1
- package/dist/utils/quote.mjs +10 -0
- package/dist/utils/quote.mjs.map +1 -1
- package/dist/utils/snaps.cjs +19 -1
- package/dist/utils/snaps.cjs.map +1 -1
- package/dist/utils/snaps.d.cts +9 -0
- package/dist/utils/snaps.d.cts.map +1 -1
- package/dist/utils/snaps.d.mts +9 -0
- package/dist/utils/snaps.d.mts.map +1 -1
- package/dist/utils/snaps.mjs +17 -0
- package/dist/utils/snaps.mjs.map +1 -1
- package/dist/utils/validators.cjs +1 -0
- package/dist/utils/validators.cjs.map +1 -1
- package/dist/utils/validators.d.cts +3 -0
- package/dist/utils/validators.d.cts.map +1 -1
- package/dist/utils/validators.d.mts +3 -0
- package/dist/utils/validators.d.mts.map +1 -1
- package/dist/utils/validators.mjs +1 -0
- package/dist/utils/validators.mjs.map +1 -1
- package/package.json +2 -1
package/dist/utils/fetch.cjs
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.fetchAssetPrices = exports.fetchBridgeQuotes = exports.fetchBridgeTokens = exports.getClientHeaders = void 0;
|
|
3
|
+
exports.fetchBridgeQuoteStream = exports.fetchAssetPrices = exports.fetchBridgeQuotes = exports.fetchBridgeTokens = exports.getClientHeaders = void 0;
|
|
4
4
|
const superstruct_1 = require("@metamask/superstruct");
|
|
5
|
-
const
|
|
5
|
+
const fetch_event_source_1 = require("@microsoft/fetch-event-source");
|
|
6
6
|
const bridge_1 = require("./bridge.cjs");
|
|
7
7
|
const caip_formatters_1 = require("./caip-formatters.cjs");
|
|
8
8
|
const validators_1 = require("./validators.cjs");
|
|
9
|
-
const CACHE_REFRESH_TEN_MINUTES = 10 * utils_1.Duration.Minute;
|
|
10
9
|
const getClientHeaders = (clientId, clientVersion) => ({
|
|
11
10
|
'X-Client-Id': clientId,
|
|
12
11
|
...(clientVersion ? { 'Client-Version': clientVersion } : {}),
|
|
@@ -30,8 +29,6 @@ async function fetchBridgeTokens(chainId, clientId, fetchFn, bridgeApiBaseUrl, c
|
|
|
30
29
|
// note that the Assets controller won't be able to provide tokens. In extension we fetch+cache the token list from bridge-api to handle this
|
|
31
30
|
const tokens = await fetchFn(url, {
|
|
32
31
|
headers: (0, exports.getClientHeaders)(clientId, clientVersion),
|
|
33
|
-
cacheOptions: { cacheRefreshTime: CACHE_REFRESH_TEN_MINUTES },
|
|
34
|
-
functionName: 'fetchBridgeTokens',
|
|
35
32
|
});
|
|
36
33
|
const transformedTokens = {};
|
|
37
34
|
tokens.forEach((token) => {
|
|
@@ -44,18 +41,11 @@ async function fetchBridgeTokens(chainId, clientId, fetchFn, bridgeApiBaseUrl, c
|
|
|
44
41
|
exports.fetchBridgeTokens = fetchBridgeTokens;
|
|
45
42
|
/**
|
|
46
43
|
* Converts the generic quote request to the type that the bridge-api expects
|
|
47
|
-
* then fetches quotes from the bridge-api
|
|
48
44
|
*
|
|
49
45
|
* @param request - The quote request
|
|
50
|
-
* @
|
|
51
|
-
* @param clientId - The client ID for metrics
|
|
52
|
-
* @param fetchFn - The fetch function to use
|
|
53
|
-
* @param bridgeApiBaseUrl - The base URL for the bridge API
|
|
54
|
-
* @param featureId - The feature ID to append to each quote
|
|
55
|
-
* @param clientVersion - The client version for metrics (optional)
|
|
56
|
-
* @returns A list of bridge tx quotes
|
|
46
|
+
* @returns A URLSearchParams object with the query parameters
|
|
57
47
|
*/
|
|
58
|
-
|
|
48
|
+
const formatQueryParams = (request) => {
|
|
59
49
|
const destWalletAddress = request.destWalletAddress ?? request.walletAddress;
|
|
60
50
|
// Transform the generic quote request into QuoteRequest
|
|
61
51
|
const normalizedRequest = {
|
|
@@ -87,12 +77,26 @@ async function fetchBridgeQuotes(request, signal, clientId, fetchFn, bridgeApiBa
|
|
|
87
77
|
Object.entries(normalizedRequest).forEach(([key, value]) => {
|
|
88
78
|
queryParams.append(key, value.toString());
|
|
89
79
|
});
|
|
80
|
+
return queryParams;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Fetches quotes from the bridge-api's getQuote endpoint
|
|
84
|
+
*
|
|
85
|
+
* @param request - The quote request
|
|
86
|
+
* @param signal - The abort signal
|
|
87
|
+
* @param clientId - The client ID for metrics
|
|
88
|
+
* @param fetchFn - The fetch function to use
|
|
89
|
+
* @param bridgeApiBaseUrl - The base URL for the bridge API
|
|
90
|
+
* @param featureId - The feature ID to append to each quote
|
|
91
|
+
* @param clientVersion - The client version for metrics (optional)
|
|
92
|
+
* @returns A list of bridge tx quotes
|
|
93
|
+
*/
|
|
94
|
+
async function fetchBridgeQuotes(request, signal, clientId, fetchFn, bridgeApiBaseUrl, featureId, clientVersion) {
|
|
95
|
+
const queryParams = formatQueryParams(request);
|
|
90
96
|
const url = `${bridgeApiBaseUrl}/getQuote?${queryParams}`;
|
|
91
97
|
const quotes = await fetchFn(url, {
|
|
92
98
|
headers: (0, exports.getClientHeaders)(clientId, clientVersion),
|
|
93
99
|
signal,
|
|
94
|
-
cacheOptions: { cacheRefreshTime: 0 },
|
|
95
|
-
functionName: 'fetchBridgeQuotes',
|
|
96
100
|
});
|
|
97
101
|
const uniqueValidationFailures = new Set([]);
|
|
98
102
|
const filteredQuotes = quotes
|
|
@@ -134,7 +138,7 @@ async function fetchBridgeQuotes(request, signal, clientId, fetchFn, bridgeApiBa
|
|
|
134
138
|
}
|
|
135
139
|
exports.fetchBridgeQuotes = fetchBridgeQuotes;
|
|
136
140
|
const fetchAssetPricesForCurrency = async (request) => {
|
|
137
|
-
const { currency, assetIds, clientId, clientVersion, fetchFn } = request;
|
|
141
|
+
const { currency, assetIds, clientId, clientVersion, fetchFn, signal } = request;
|
|
138
142
|
const validAssetIds = Array.from(assetIds).filter(Boolean);
|
|
139
143
|
if (validAssetIds.length === 0) {
|
|
140
144
|
return {};
|
|
@@ -146,8 +150,7 @@ const fetchAssetPricesForCurrency = async (request) => {
|
|
|
146
150
|
const url = `https://price.api.cx.metamask.io/v3/spot-prices?${queryParams}`;
|
|
147
151
|
const priceApiResponse = (await fetchFn(url, {
|
|
148
152
|
headers: (0, exports.getClientHeaders)(clientId, clientVersion),
|
|
149
|
-
|
|
150
|
-
functionName: 'fetchAssetExchangeRates',
|
|
153
|
+
signal,
|
|
151
154
|
}));
|
|
152
155
|
if (!priceApiResponse || typeof priceApiResponse !== 'object') {
|
|
153
156
|
return {};
|
|
@@ -193,4 +196,79 @@ const fetchAssetPrices = async (request) => {
|
|
|
193
196
|
return combinedPrices;
|
|
194
197
|
};
|
|
195
198
|
exports.fetchAssetPrices = fetchAssetPrices;
|
|
199
|
+
/**
|
|
200
|
+
* Converts the generic quote request to the type that the bridge-api expects
|
|
201
|
+
* then fetches quotes from the bridge-api
|
|
202
|
+
*
|
|
203
|
+
* @param fetchFn - The fetch function to use
|
|
204
|
+
* @param request - The quote request
|
|
205
|
+
* @param signal - The abort signal
|
|
206
|
+
* @param clientId - The client ID for metrics
|
|
207
|
+
* @param bridgeApiBaseUrl - The base URL for the bridge API
|
|
208
|
+
* @param serverEventHandlers - The server event handlers
|
|
209
|
+
* @param serverEventHandlers.onValidationFailure - The function to handle validation failures
|
|
210
|
+
* @param serverEventHandlers.onValidQuoteReceived - The function to handle valid quotes
|
|
211
|
+
* @param serverEventHandlers.onClose - The function to run when the stream is closed and there are no thrown errors
|
|
212
|
+
* @param clientVersion - The client version for metrics (optional)
|
|
213
|
+
* @returns A list of bridge tx quotes
|
|
214
|
+
*/
|
|
215
|
+
async function fetchBridgeQuoteStream(fetchFn, request, signal, clientId, bridgeApiBaseUrl, serverEventHandlers, clientVersion) {
|
|
216
|
+
const queryParams = formatQueryParams(request);
|
|
217
|
+
const onMessage = (event) => {
|
|
218
|
+
const uniqueValidationFailures = new Set([]);
|
|
219
|
+
if (event.data === '') {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const quoteResponse = JSON.parse(event.data);
|
|
223
|
+
try {
|
|
224
|
+
(0, validators_1.validateQuoteResponse)(quoteResponse);
|
|
225
|
+
// eslint-disable-next-line promise/catch-or-return, @typescript-eslint/no-floating-promises
|
|
226
|
+
serverEventHandlers.onValidQuoteReceived(quoteResponse).then((v) => {
|
|
227
|
+
return v;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
if (error instanceof superstruct_1.StructError) {
|
|
232
|
+
error.failures().forEach(({ branch, path }) => {
|
|
233
|
+
const aggregatorId = branch?.[0]?.quote?.bridgeId ||
|
|
234
|
+
branch?.[0]?.quote?.bridges?.[0] ||
|
|
235
|
+
quoteResponse?.quote?.bridgeId ||
|
|
236
|
+
quoteResponse?.quote?.bridges?.[0] ||
|
|
237
|
+
'unknown';
|
|
238
|
+
const pathString = path?.join('.') || 'unknown';
|
|
239
|
+
uniqueValidationFailures.add([aggregatorId, pathString].join('|'));
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
const validationFailures = Array.from(uniqueValidationFailures);
|
|
243
|
+
if (uniqueValidationFailures.size > 0) {
|
|
244
|
+
console.warn('Quote validation failed', validationFailures);
|
|
245
|
+
serverEventHandlers.onValidationFailure(validationFailures);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// Rethrow any unexpected errors
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
const urlStream = `${bridgeApiBaseUrl}/getQuoteStream?${queryParams}`;
|
|
254
|
+
await (0, fetch_event_source_1.fetchEventSource)(urlStream, {
|
|
255
|
+
headers: {
|
|
256
|
+
...(0, exports.getClientHeaders)(clientId, clientVersion),
|
|
257
|
+
'Content-Type': 'text/event-stream',
|
|
258
|
+
},
|
|
259
|
+
signal,
|
|
260
|
+
onmessage: onMessage,
|
|
261
|
+
onerror: (e) => {
|
|
262
|
+
// Rethrow error to prevent silent fetch failures
|
|
263
|
+
throw new Error(e.toString());
|
|
264
|
+
},
|
|
265
|
+
onclose: () => {
|
|
266
|
+
serverEventHandlers.onClose();
|
|
267
|
+
},
|
|
268
|
+
// Cancels the request when document is hidden, will automatically restart when visible
|
|
269
|
+
openWhenHidden: false,
|
|
270
|
+
fetch: fetchFn,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
exports.fetchBridgeQuoteStream = fetchBridgeQuoteStream;
|
|
196
274
|
//# sourceMappingURL=fetch.cjs.map
|
package/dist/utils/fetch.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.cjs","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":";;;AAAA,uDAAoD;AAEpD,2CAA2C;AAE3C,yCAA4C;AAC5C,2DAG2B;AAE3B,iDAIsB;AAStB,MAAM,yBAAyB,GAAG,EAAE,GAAG,gBAAQ,CAAC,MAAM,CAAC;AAEhD,MAAM,gBAAgB,GAAG,CAAC,QAAgB,EAAE,aAAsB,EAAE,EAAE,CAAC,CAAC;IAC7E,aAAa,EAAE,QAAQ;IACvB,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;CAC9D,CAAC,CAAC;AAHU,QAAA,gBAAgB,oBAG1B;AAEH;;;;;;;;;GASG;AACI,KAAK,UAAU,iBAAiB,CACrC,OAA0B,EAC1B,QAAgB,EAChB,OAAsB,EACtB,gBAAwB,EACxB,aAAsB;IAEtB,8BAA8B;IAC9B,MAAM,GAAG,GAAG,GAAG,gBAAgB,sBAAsB,IAAA,oCAAkB,EAAC,OAAO,CAAC,EAAE,CAAC;IAEnF,uGAAuG;IACvG,uEAAuE;IACvE,6IAA6I;IAC7I,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE;QAChC,OAAO,EAAE,IAAA,wBAAgB,EAAC,QAAQ,EAAE,aAAa,CAAC;QAClD,YAAY,EAAE,EAAE,gBAAgB,EAAE,yBAAyB,EAAE;QAC7D,YAAY,EAAE,mBAAmB;KAClC,CAAC,CAAC;IAEH,MAAM,iBAAiB,GAAgC,EAAE,CAAC;IAC1D,MAAM,CAAC,OAAO,CAAC,CAAC,KAAc,EAAE,EAAE;QAChC,IAAI,IAAA,qCAAwB,EAAC,KAAK,CAAC,EAAE;YACnC,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;SAC1C;IACH,CAAC,CAAC,CAAC;IACH,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AA1BD,8CA0BC;AAED;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,iBAAiB,CACrC,OAA4B,EAC5B,MAA0B,EAC1B,QAAgB,EAChB,OAAsB,EACtB,gBAAwB,EACxB,SAA2B,EAC3B,aAAsB;IAKtB,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,OAAO,CAAC,aAAa,CAAC;IAC7E,wDAAwD;IACxD,MAAM,iBAAiB,GAAiB;QACtC,aAAa,EAAE,IAAA,8CAA4B,EAAC,OAAO,CAAC,aAAa,CAAC;QAClE,iBAAiB,EAAE,IAAA,8CAA4B,EAAC,iBAAiB,CAAC;QAClE,UAAU,EAAE,IAAA,oCAAkB,EAAC,OAAO,CAAC,UAAU,CAAC;QAClD,WAAW,EAAE,IAAA,oCAAkB,EAAC,OAAO,CAAC,WAAW,CAAC;QACpD,eAAe,EAAE,IAAA,8CAA4B,EAAC,OAAO,CAAC,eAAe,CAAC;QACtE,gBAAgB,EAAE,IAAA,8CAA4B,EAAC,OAAO,CAAC,gBAAgB,CAAC;QACxE,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,eAAe,EAAE,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC;QACjD,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAC7C,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC;QACzC,eAAe,EAAE,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC;KAClD,CAAC;IACF,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE;QAClC,iBAAiB,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;KAC/C;IACD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE;QAC/B,iBAAiB,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;KACzC;IACD,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;QAC/C,iBAAiB,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;KAC3C;IACD,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE;QACrD,iBAAiB,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;KACjD;IAED,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;IAC1C,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACzD,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,GAAG,gBAAgB,aAAa,WAAW,EAAE,CAAC;IAC1D,MAAM,MAAM,GAAc,MAAM,OAAO,CAAC,GAAG,EAAE;QAC3C,OAAO,EAAE,IAAA,wBAAgB,EAAC,QAAQ,EAAE,aAAa,CAAC;QAClD,MAAM;QACN,YAAY,EAAE,EAAE,gBAAgB,EAAE,CAAC,EAAE;QACrC,YAAY,EAAE,mBAAmB;KAClC,CAAC,CAAC;IAEH,MAAM,wBAAwB,GAAgB,IAAI,GAAG,CAAS,EAAE,CAAC,CAAC;IAClE,MAAM,cAAc,GAAG,MAAM;SAC1B,MAAM,CAAC,CAAC,aAAsB,EAAkC,EAAE;QACjE,IAAI;YACF,MAAM,cAAc,GAAG,IAAA,yBAAgB,EAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAE5D,IAAI,cAAc,EAAE;gBAClB,OAAO,IAAA,yCAA4B,EAAC,aAAa,CAAC,CAAC;aACpD;YACD,OAAO,IAAA,kCAAqB,EAAC,aAAa,CAAC,CAAC;SAC7C;QAAC,OAAO,KAAK,EAAE;YACd,IAAI,KAAK,YAAY,yBAAW,EAAE;gBAChC,KAAK,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;oBAC5C,MAAM,YAAY,GAChB,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ;wBAC5B,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;wBAC/B,aAA+B,EAAE,KAAK,EAAE,QAAQ;wBAChD,aAA+B,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;wBACrD,SAAS,CAAC;oBACZ,MAAM,UAAU,GAAG,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC;oBAChD,wBAAwB,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBACrE,CAAC,CAAC,CAAC;aACJ;YACD,OAAO,KAAK,CAAC;SACd;IACH,CAAC,CAAC;SACD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACf,GAAG,KAAK;QACR,SAAS,EAAE,SAAS,IAAI,SAAS;KAClC,CAAC,CAAC,CAAC;IAEN,MAAM,kBAAkB,GAAG,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAChE,IAAI,wBAAwB,CAAC,IAAI,GAAG,CAAC,EAAE;QACrC,OAAO,CAAC,IAAI,CAAC,yBAAyB,EAAE,kBAAkB,CAAC,CAAC;KAC7D;IAED,OAAO;QACL,MAAM,EAAE,cAAc;QACtB,kBAAkB;KACnB,CAAC;AACJ,CAAC;AA5FD,8CA4FC;AAED,MAAM,2BAA2B,GAAG,KAAK,EAAE,OAM1C,EAAkE,EAAE;IACnE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACzE,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3D,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE;QAC9B,OAAO,EAAE,CAAC;KACX;IAED,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC;QACtC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QACjD,UAAU,EAAE,QAAQ;KACrB,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,mDAAmD,WAAW,EAAE,CAAC;IAC7E,MAAM,gBAAgB,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,EAAE;QAC3C,OAAO,EAAE,IAAA,wBAAgB,EAAC,QAAQ,EAAE,aAAa,CAAC;QAClD,YAAY,EAAE,EAAE,gBAAgB,EAAE,MAAM,CAAC,gBAAQ,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE;QAChE,YAAY,EAAE,yBAAyB;KACxC,CAAC,CAA0D,CAAC;IAE7D,IAAI,CAAC,gBAAgB,IAAI,OAAO,gBAAgB,KAAK,QAAQ,EAAE;QAC7D,OAAO,EAAE,CAAC;KACX;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAC5C,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,EAAE;QAClC,IAAI,CAAC,eAAe,EAAE;YACpB,OAAO,GAAG,CAAC;SACZ;QACD,IAAI,CAAC,GAAG,CAAC,OAAwB,CAAC,EAAE;YAClC,GAAG,CAAC,OAAwB,CAAC,GAAG,EAAE,CAAC;SACpC;QACD,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE;YAC7B,GAAG,CAAC,OAAwB,CAAC,CAAC,QAAQ,CAAC;gBACrC,eAAe,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;SACxC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,EACD,EAA2D,CAC5D,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;GAKG;AACI,MAAM,gBAAgB,GAAG,KAAK,EACnC,OAEuE,EAGvE,EAAE;IACF,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAExC,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC,UAAU,CAC7C,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CACxB,KAAK,EAAE,QAAQ,EAAE,EAAE,CACjB,MAAM,2BAA2B,CAAC,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC,CAC3D,CACF,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,EAAE;QAC1B,OAAO,gBAAgB,CAAC,MAAM,CAC5B,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;YACd,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE;gBACjC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,EAAE;oBAClE,MAAM,cAAc,GAAG,GAAG,CAAC,OAAwB,CAAC,CAAC;oBACrD,IAAI,CAAC,cAAc,EAAE;wBACnB,GAAG,CAAC,OAAwB,CAAC,GAAG,EAAE,CAAC;qBACpC;oBACD,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE;wBAC5D,GAAG,CAAC,OAAwB,CAAC,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC;oBAClD,CAAC,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;aACJ;YACD,OAAO,GAAG,CAAC;QACb,CAAC,EACD,EAA2D,CAC5D,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,cAAc,CAAC;AACxB,CAAC,CAAC;AAnCW,QAAA,gBAAgB,oBAmC3B","sourcesContent":["import { StructError } from '@metamask/superstruct';\nimport type { CaipAssetType, CaipChainId, Hex } from '@metamask/utils';\nimport { Duration } from '@metamask/utils';\n\nimport { isBitcoinChainId } from './bridge';\nimport {\n formatAddressToCaipReference,\n formatChainIdToDec,\n} from './caip-formatters';\nimport type { FeatureId } from './validators';\nimport {\n validateQuoteResponse,\n validateBitcoinQuoteResponse,\n validateSwapsTokenObject,\n} from './validators';\nimport type {\n QuoteResponse,\n FetchFunction,\n GenericQuoteRequest,\n QuoteRequest,\n BridgeAsset,\n} from '../types';\n\nconst CACHE_REFRESH_TEN_MINUTES = 10 * Duration.Minute;\n\nexport const getClientHeaders = (clientId: string, clientVersion?: string) => ({\n 'X-Client-Id': clientId,\n ...(clientVersion ? { 'Client-Version': clientVersion } : {}),\n});\n\n/**\n * Returns a list of enabled (unblocked) tokens\n *\n * @param chainId - The chain ID to fetch tokens for\n * @param clientId - The client ID for metrics\n * @param fetchFn - The fetch function to use\n * @param bridgeApiBaseUrl - The base URL for the bridge API\n * @param clientVersion - The client version for metrics (optional)\n * @returns A list of enabled (unblocked) tokens\n */\nexport async function fetchBridgeTokens(\n chainId: Hex | CaipChainId,\n clientId: string,\n fetchFn: FetchFunction,\n bridgeApiBaseUrl: string,\n clientVersion?: string,\n): Promise<Record<string, BridgeAsset>> {\n // TODO make token api v2 call\n const url = `${bridgeApiBaseUrl}/getTokens?chainId=${formatChainIdToDec(chainId)}`;\n\n // TODO we will need to cache these. In Extension fetchWithCache is used. This is due to the following:\n // If we allow selecting dest networks which the user has not imported,\n // note that the Assets controller won't be able to provide tokens. In extension we fetch+cache the token list from bridge-api to handle this\n const tokens = await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n cacheOptions: { cacheRefreshTime: CACHE_REFRESH_TEN_MINUTES },\n functionName: 'fetchBridgeTokens',\n });\n\n const transformedTokens: Record<string, BridgeAsset> = {};\n tokens.forEach((token: unknown) => {\n if (validateSwapsTokenObject(token)) {\n transformedTokens[token.address] = token;\n }\n });\n return transformedTokens;\n}\n\n/**\n * Converts the generic quote request to the type that the bridge-api expects\n * then fetches quotes from the bridge-api\n *\n * @param request - The quote request\n * @param signal - The abort signal\n * @param clientId - The client ID for metrics\n * @param fetchFn - The fetch function to use\n * @param bridgeApiBaseUrl - The base URL for the bridge API\n * @param featureId - The feature ID to append to each quote\n * @param clientVersion - The client version for metrics (optional)\n * @returns A list of bridge tx quotes\n */\nexport async function fetchBridgeQuotes(\n request: GenericQuoteRequest,\n signal: AbortSignal | null,\n clientId: string,\n fetchFn: FetchFunction,\n bridgeApiBaseUrl: string,\n featureId: FeatureId | null,\n clientVersion?: string,\n): Promise<{\n quotes: QuoteResponse[];\n validationFailures: string[];\n}> {\n const destWalletAddress = request.destWalletAddress ?? request.walletAddress;\n // Transform the generic quote request into QuoteRequest\n const normalizedRequest: QuoteRequest = {\n walletAddress: formatAddressToCaipReference(request.walletAddress),\n destWalletAddress: formatAddressToCaipReference(destWalletAddress),\n srcChainId: formatChainIdToDec(request.srcChainId),\n destChainId: formatChainIdToDec(request.destChainId),\n srcTokenAddress: formatAddressToCaipReference(request.srcTokenAddress),\n destTokenAddress: formatAddressToCaipReference(request.destTokenAddress),\n srcTokenAmount: request.srcTokenAmount,\n insufficientBal: Boolean(request.insufficientBal),\n resetApproval: Boolean(request.resetApproval),\n gasIncluded: Boolean(request.gasIncluded),\n gasIncluded7702: Boolean(request.gasIncluded7702),\n };\n if (request.slippage !== undefined) {\n normalizedRequest.slippage = request.slippage;\n }\n if (request.noFee !== undefined) {\n normalizedRequest.noFee = request.noFee;\n }\n if (request.aggIds && request.aggIds.length > 0) {\n normalizedRequest.aggIds = request.aggIds;\n }\n if (request.bridgeIds && request.bridgeIds.length > 0) {\n normalizedRequest.bridgeIds = request.bridgeIds;\n }\n\n const queryParams = new URLSearchParams();\n Object.entries(normalizedRequest).forEach(([key, value]) => {\n queryParams.append(key, value.toString());\n });\n const url = `${bridgeApiBaseUrl}/getQuote?${queryParams}`;\n const quotes: unknown[] = await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n signal,\n cacheOptions: { cacheRefreshTime: 0 },\n functionName: 'fetchBridgeQuotes',\n });\n\n const uniqueValidationFailures: Set<string> = new Set<string>([]);\n const filteredQuotes = quotes\n .filter((quoteResponse: unknown): quoteResponse is QuoteResponse => {\n try {\n const isBitcoinQuote = isBitcoinChainId(request.srcChainId);\n\n if (isBitcoinQuote) {\n return validateBitcoinQuoteResponse(quoteResponse);\n }\n return validateQuoteResponse(quoteResponse);\n } catch (error) {\n if (error instanceof StructError) {\n error.failures().forEach(({ branch, path }) => {\n const aggregatorId =\n branch?.[0]?.quote?.bridgeId ||\n branch?.[0]?.quote?.bridges?.[0] ||\n (quoteResponse as QuoteResponse)?.quote?.bridgeId ||\n (quoteResponse as QuoteResponse)?.quote?.bridges?.[0] ||\n 'unknown';\n const pathString = path?.join('.') || 'unknown';\n uniqueValidationFailures.add([aggregatorId, pathString].join('|'));\n });\n }\n return false;\n }\n })\n .map((quote) => ({\n ...quote,\n featureId: featureId ?? undefined,\n }));\n\n const validationFailures = Array.from(uniqueValidationFailures);\n if (uniqueValidationFailures.size > 0) {\n console.warn('Quote validation failed', validationFailures);\n }\n\n return {\n quotes: filteredQuotes,\n validationFailures,\n };\n}\n\nconst fetchAssetPricesForCurrency = async (request: {\n currency: string;\n assetIds: Set<CaipAssetType>;\n clientId: string;\n clientVersion?: string;\n fetchFn: FetchFunction;\n}): Promise<Record<CaipAssetType, { [currency: string]: string }>> => {\n const { currency, assetIds, clientId, clientVersion, fetchFn } = request;\n const validAssetIds = Array.from(assetIds).filter(Boolean);\n if (validAssetIds.length === 0) {\n return {};\n }\n\n const queryParams = new URLSearchParams({\n assetIds: validAssetIds.filter(Boolean).join(','),\n vsCurrency: currency,\n });\n const url = `https://price.api.cx.metamask.io/v3/spot-prices?${queryParams}`;\n const priceApiResponse = (await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n cacheOptions: { cacheRefreshTime: Number(Duration.Second * 30) },\n functionName: 'fetchAssetExchangeRates',\n })) as Record<CaipAssetType, { [currency: string]: number }>;\n\n if (!priceApiResponse || typeof priceApiResponse !== 'object') {\n return {};\n }\n\n return Object.entries(priceApiResponse).reduce(\n (acc, [assetId, currencyToPrice]) => {\n if (!currencyToPrice) {\n return acc;\n }\n if (!acc[assetId as CaipAssetType]) {\n acc[assetId as CaipAssetType] = {};\n }\n if (currencyToPrice[currency]) {\n acc[assetId as CaipAssetType][currency] =\n currencyToPrice[currency].toString();\n }\n return acc;\n },\n {} as Record<CaipAssetType, { [currency: string]: string }>,\n );\n};\n\n/**\n * Fetches the asset prices from the price API for multiple currencies\n *\n * @param request - The request object\n * @returns The asset prices by assetId\n */\nexport const fetchAssetPrices = async (\n request: {\n currencies: Set<string>;\n } & Omit<Parameters<typeof fetchAssetPricesForCurrency>[0], 'currency'>,\n): Promise<\n Record<CaipAssetType, { [currency: string]: string } | undefined>\n> => {\n const { currencies, ...args } = request;\n\n const combinedPrices = await Promise.allSettled(\n Array.from(currencies).map(\n async (currency) =>\n await fetchAssetPricesForCurrency({ ...args, currency }),\n ),\n ).then((priceApiResponse) => {\n return priceApiResponse.reduce(\n (acc, result) => {\n if (result.status === 'fulfilled') {\n Object.entries(result.value).forEach(([assetId, currencyToPrice]) => {\n const existingPrices = acc[assetId as CaipAssetType];\n if (!existingPrices) {\n acc[assetId as CaipAssetType] = {};\n }\n Object.entries(currencyToPrice).forEach(([currency, price]) => {\n acc[assetId as CaipAssetType][currency] = price;\n });\n });\n }\n return acc;\n },\n {} as Record<CaipAssetType, { [currency: string]: string }>,\n );\n });\n\n return combinedPrices;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"fetch.cjs","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"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","sourcesContent":["import { StructError } from '@metamask/superstruct';\nimport type { CaipAssetType, CaipChainId, Hex } from '@metamask/utils';\nimport type { EventSourceMessage } from '@microsoft/fetch-event-source';\nimport { fetchEventSource } from '@microsoft/fetch-event-source';\n\nimport { isBitcoinChainId } from './bridge';\nimport {\n formatAddressToCaipReference,\n formatChainIdToDec,\n} from './caip-formatters';\nimport type { FeatureId } from './validators';\nimport {\n validateQuoteResponse,\n validateBitcoinQuoteResponse,\n validateSwapsTokenObject,\n} from './validators';\nimport type {\n QuoteResponse,\n FetchFunction,\n GenericQuoteRequest,\n QuoteRequest,\n BridgeAsset,\n} from '../types';\n\nexport const getClientHeaders = (clientId: string, clientVersion?: string) => ({\n 'X-Client-Id': clientId,\n ...(clientVersion ? { 'Client-Version': clientVersion } : {}),\n});\n\n/**\n * Returns a list of enabled (unblocked) tokens\n *\n * @param chainId - The chain ID to fetch tokens for\n * @param clientId - The client ID for metrics\n * @param fetchFn - The fetch function to use\n * @param bridgeApiBaseUrl - The base URL for the bridge API\n * @param clientVersion - The client version for metrics (optional)\n * @returns A list of enabled (unblocked) tokens\n */\nexport async function fetchBridgeTokens(\n chainId: Hex | CaipChainId,\n clientId: string,\n fetchFn: FetchFunction,\n bridgeApiBaseUrl: string,\n clientVersion?: string,\n): Promise<Record<string, BridgeAsset>> {\n // TODO make token api v2 call\n const url = `${bridgeApiBaseUrl}/getTokens?chainId=${formatChainIdToDec(chainId)}`;\n\n // TODO we will need to cache these. In Extension fetchWithCache is used. This is due to the following:\n // If we allow selecting dest networks which the user has not imported,\n // note that the Assets controller won't be able to provide tokens. In extension we fetch+cache the token list from bridge-api to handle this\n const tokens = await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n });\n\n const transformedTokens: Record<string, BridgeAsset> = {};\n tokens.forEach((token: unknown) => {\n if (validateSwapsTokenObject(token)) {\n transformedTokens[token.address] = token;\n }\n });\n return transformedTokens;\n}\n\n/**\n * Converts the generic quote request to the type that the bridge-api expects\n *\n * @param request - The quote request\n * @returns A URLSearchParams object with the query parameters\n */\nconst formatQueryParams = (request: GenericQuoteRequest): URLSearchParams => {\n const destWalletAddress = request.destWalletAddress ?? request.walletAddress;\n // Transform the generic quote request into QuoteRequest\n const normalizedRequest: QuoteRequest = {\n walletAddress: formatAddressToCaipReference(request.walletAddress),\n destWalletAddress: formatAddressToCaipReference(destWalletAddress),\n srcChainId: formatChainIdToDec(request.srcChainId),\n destChainId: formatChainIdToDec(request.destChainId),\n srcTokenAddress: formatAddressToCaipReference(request.srcTokenAddress),\n destTokenAddress: formatAddressToCaipReference(request.destTokenAddress),\n srcTokenAmount: request.srcTokenAmount,\n insufficientBal: Boolean(request.insufficientBal),\n resetApproval: Boolean(request.resetApproval),\n gasIncluded: Boolean(request.gasIncluded),\n gasIncluded7702: Boolean(request.gasIncluded7702),\n };\n if (request.slippage !== undefined) {\n normalizedRequest.slippage = request.slippage;\n }\n if (request.noFee !== undefined) {\n normalizedRequest.noFee = request.noFee;\n }\n if (request.aggIds && request.aggIds.length > 0) {\n normalizedRequest.aggIds = request.aggIds;\n }\n if (request.bridgeIds && request.bridgeIds.length > 0) {\n normalizedRequest.bridgeIds = request.bridgeIds;\n }\n\n const queryParams = new URLSearchParams();\n Object.entries(normalizedRequest).forEach(([key, value]) => {\n queryParams.append(key, value.toString());\n });\n return queryParams;\n};\n\n/**\n * Fetches quotes from the bridge-api's getQuote endpoint\n *\n * @param request - The quote request\n * @param signal - The abort signal\n * @param clientId - The client ID for metrics\n * @param fetchFn - The fetch function to use\n * @param bridgeApiBaseUrl - The base URL for the bridge API\n * @param featureId - The feature ID to append to each quote\n * @param clientVersion - The client version for metrics (optional)\n * @returns A list of bridge tx quotes\n */\nexport async function fetchBridgeQuotes(\n request: GenericQuoteRequest,\n signal: AbortSignal | null,\n clientId: string,\n fetchFn: FetchFunction,\n bridgeApiBaseUrl: string,\n featureId: FeatureId | null,\n clientVersion?: string,\n): Promise<{\n quotes: QuoteResponse[];\n validationFailures: string[];\n}> {\n const queryParams = formatQueryParams(request);\n\n const url = `${bridgeApiBaseUrl}/getQuote?${queryParams}`;\n const quotes: unknown[] = await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n signal,\n });\n\n const uniqueValidationFailures: Set<string> = new Set<string>([]);\n const filteredQuotes = quotes\n .filter((quoteResponse: unknown): quoteResponse is QuoteResponse => {\n try {\n const isBitcoinQuote = isBitcoinChainId(request.srcChainId);\n\n if (isBitcoinQuote) {\n return validateBitcoinQuoteResponse(quoteResponse);\n }\n return validateQuoteResponse(quoteResponse);\n } catch (error) {\n if (error instanceof StructError) {\n error.failures().forEach(({ branch, path }) => {\n const aggregatorId =\n branch?.[0]?.quote?.bridgeId ||\n branch?.[0]?.quote?.bridges?.[0] ||\n (quoteResponse as QuoteResponse)?.quote?.bridgeId ||\n (quoteResponse as QuoteResponse)?.quote?.bridges?.[0] ||\n 'unknown';\n const pathString = path?.join('.') || 'unknown';\n uniqueValidationFailures.add([aggregatorId, pathString].join('|'));\n });\n }\n return false;\n }\n })\n .map((quote) => ({\n ...quote,\n featureId: featureId ?? undefined,\n }));\n\n const validationFailures = Array.from(uniqueValidationFailures);\n if (uniqueValidationFailures.size > 0) {\n console.warn('Quote validation failed', validationFailures);\n }\n\n return {\n quotes: filteredQuotes,\n validationFailures,\n };\n}\n\nconst fetchAssetPricesForCurrency = async (request: {\n currency: string;\n assetIds: Set<CaipAssetType>;\n clientId: string;\n clientVersion?: string;\n fetchFn: FetchFunction;\n signal?: AbortSignal;\n}): Promise<Record<CaipAssetType, { [currency: string]: string }>> => {\n const { currency, assetIds, clientId, clientVersion, fetchFn, signal } =\n request;\n const validAssetIds = Array.from(assetIds).filter(Boolean);\n if (validAssetIds.length === 0) {\n return {};\n }\n\n const queryParams = new URLSearchParams({\n assetIds: validAssetIds.filter(Boolean).join(','),\n vsCurrency: currency,\n });\n const url = `https://price.api.cx.metamask.io/v3/spot-prices?${queryParams}`;\n const priceApiResponse = (await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n signal,\n })) as Record<CaipAssetType, { [currency: string]: number }>;\n if (!priceApiResponse || typeof priceApiResponse !== 'object') {\n return {};\n }\n\n return Object.entries(priceApiResponse).reduce(\n (acc, [assetId, currencyToPrice]) => {\n if (!currencyToPrice) {\n return acc;\n }\n if (!acc[assetId as CaipAssetType]) {\n acc[assetId as CaipAssetType] = {};\n }\n if (currencyToPrice[currency]) {\n acc[assetId as CaipAssetType][currency] =\n currencyToPrice[currency].toString();\n }\n return acc;\n },\n {} as Record<CaipAssetType, { [currency: string]: string }>,\n );\n};\n\n/**\n * Fetches the asset prices from the price API for multiple currencies\n *\n * @param request - The request object\n * @returns The asset prices by assetId\n */\nexport const fetchAssetPrices = async (\n request: {\n currencies: Set<string>;\n } & Omit<Parameters<typeof fetchAssetPricesForCurrency>[0], 'currency'>,\n): Promise<\n Record<CaipAssetType, { [currency: string]: string } | undefined>\n> => {\n const { currencies, ...args } = request;\n\n const combinedPrices = await Promise.allSettled(\n Array.from(currencies).map(\n async (currency) =>\n await fetchAssetPricesForCurrency({ ...args, currency }),\n ),\n ).then((priceApiResponse) => {\n return priceApiResponse.reduce(\n (acc, result) => {\n if (result.status === 'fulfilled') {\n Object.entries(result.value).forEach(([assetId, currencyToPrice]) => {\n const existingPrices = acc[assetId as CaipAssetType];\n if (!existingPrices) {\n acc[assetId as CaipAssetType] = {};\n }\n Object.entries(currencyToPrice).forEach(([currency, price]) => {\n acc[assetId as CaipAssetType][currency] = price;\n });\n });\n }\n return acc;\n },\n {} as Record<CaipAssetType, { [currency: string]: string }>,\n );\n });\n\n return combinedPrices;\n};\n\n/**\n * Converts the generic quote request to the type that the bridge-api expects\n * then fetches quotes from the bridge-api\n *\n * @param fetchFn - The fetch function to use\n * @param request - The quote request\n * @param signal - The abort signal\n * @param clientId - The client ID for metrics\n * @param bridgeApiBaseUrl - The base URL for the bridge API\n * @param serverEventHandlers - The server event handlers\n * @param serverEventHandlers.onValidationFailure - The function to handle validation failures\n * @param serverEventHandlers.onValidQuoteReceived - The function to handle valid quotes\n * @param serverEventHandlers.onClose - The function to run when the stream is closed and there are no thrown errors\n * @param clientVersion - The client version for metrics (optional)\n * @returns A list of bridge tx quotes\n */\nexport async function fetchBridgeQuoteStream(\n fetchFn: FetchFunction,\n request: GenericQuoteRequest,\n signal: AbortSignal | undefined,\n clientId: string,\n bridgeApiBaseUrl: string,\n serverEventHandlers: {\n onClose: () => void;\n onValidationFailure: (validationFailures: string[]) => void;\n onValidQuoteReceived: (quotes: QuoteResponse) => Promise<void>;\n },\n clientVersion?: string,\n): Promise<void> {\n const queryParams = formatQueryParams(request);\n\n const onMessage = (event: EventSourceMessage) => {\n const uniqueValidationFailures: Set<string> = new Set<string>([]);\n if (event.data === '') {\n return;\n }\n const quoteResponse = JSON.parse(event.data);\n\n try {\n validateQuoteResponse(quoteResponse);\n // eslint-disable-next-line promise/catch-or-return, @typescript-eslint/no-floating-promises\n serverEventHandlers.onValidQuoteReceived(quoteResponse).then((v) => {\n return v;\n });\n } catch (error) {\n if (error instanceof StructError) {\n error.failures().forEach(({ branch, path }) => {\n const aggregatorId =\n branch?.[0]?.quote?.bridgeId ||\n branch?.[0]?.quote?.bridges?.[0] ||\n (quoteResponse as QuoteResponse)?.quote?.bridgeId ||\n (quoteResponse as QuoteResponse)?.quote?.bridges?.[0] ||\n 'unknown';\n const pathString = path?.join('.') || 'unknown';\n uniqueValidationFailures.add([aggregatorId, pathString].join('|'));\n });\n }\n const validationFailures = Array.from(uniqueValidationFailures);\n if (uniqueValidationFailures.size > 0) {\n console.warn('Quote validation failed', validationFailures);\n serverEventHandlers.onValidationFailure(validationFailures);\n } else {\n // Rethrow any unexpected errors\n throw error;\n }\n }\n };\n\n const urlStream = `${bridgeApiBaseUrl}/getQuoteStream?${queryParams}`;\n await fetchEventSource(urlStream, {\n headers: {\n ...getClientHeaders(clientId, clientVersion),\n 'Content-Type': 'text/event-stream',\n },\n signal,\n onmessage: onMessage,\n onerror: (e) => {\n // Rethrow error to prevent silent fetch failures\n throw new Error(e.toString());\n },\n onclose: () => {\n serverEventHandlers.onClose();\n },\n // Cancels the request when document is hidden, will automatically restart when visible\n openWhenHidden: false,\n fetch: fetchFn,\n });\n}\n"]}
|
package/dist/utils/fetch.d.cts
CHANGED
|
@@ -17,8 +17,7 @@ export declare const getClientHeaders: (clientId: string, clientVersion?: string
|
|
|
17
17
|
*/
|
|
18
18
|
export declare function fetchBridgeTokens(chainId: Hex | CaipChainId, clientId: string, fetchFn: FetchFunction, bridgeApiBaseUrl: string, clientVersion?: string): Promise<Record<string, BridgeAsset>>;
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
* then fetches quotes from the bridge-api
|
|
20
|
+
* Fetches quotes from the bridge-api's getQuote endpoint
|
|
22
21
|
*
|
|
23
22
|
* @param request - The quote request
|
|
24
23
|
* @param signal - The abort signal
|
|
@@ -39,6 +38,7 @@ declare const fetchAssetPricesForCurrency: (request: {
|
|
|
39
38
|
clientId: string;
|
|
40
39
|
clientVersion?: string;
|
|
41
40
|
fetchFn: FetchFunction;
|
|
41
|
+
signal?: AbortSignal;
|
|
42
42
|
}) => Promise<Record<`${string}:${string}/${string}:${string}`, {
|
|
43
43
|
[currency: string]: string;
|
|
44
44
|
}>>;
|
|
@@ -53,5 +53,26 @@ export declare const fetchAssetPrices: (request: {
|
|
|
53
53
|
} & Omit<Parameters<typeof fetchAssetPricesForCurrency>[0], 'currency'>) => Promise<Record<`${string}:${string}/${string}:${string}`, {
|
|
54
54
|
[currency: string]: string;
|
|
55
55
|
} | undefined>>;
|
|
56
|
+
/**
|
|
57
|
+
* Converts the generic quote request to the type that the bridge-api expects
|
|
58
|
+
* then fetches quotes from the bridge-api
|
|
59
|
+
*
|
|
60
|
+
* @param fetchFn - The fetch function to use
|
|
61
|
+
* @param request - The quote request
|
|
62
|
+
* @param signal - The abort signal
|
|
63
|
+
* @param clientId - The client ID for metrics
|
|
64
|
+
* @param bridgeApiBaseUrl - The base URL for the bridge API
|
|
65
|
+
* @param serverEventHandlers - The server event handlers
|
|
66
|
+
* @param serverEventHandlers.onValidationFailure - The function to handle validation failures
|
|
67
|
+
* @param serverEventHandlers.onValidQuoteReceived - The function to handle valid quotes
|
|
68
|
+
* @param serverEventHandlers.onClose - The function to run when the stream is closed and there are no thrown errors
|
|
69
|
+
* @param clientVersion - The client version for metrics (optional)
|
|
70
|
+
* @returns A list of bridge tx quotes
|
|
71
|
+
*/
|
|
72
|
+
export declare function fetchBridgeQuoteStream(fetchFn: FetchFunction, request: GenericQuoteRequest, signal: AbortSignal | undefined, clientId: string, bridgeApiBaseUrl: string, serverEventHandlers: {
|
|
73
|
+
onClose: () => void;
|
|
74
|
+
onValidationFailure: (validationFailures: string[]) => void;
|
|
75
|
+
onValidQuoteReceived: (quotes: QuoteResponse) => Promise<void>;
|
|
76
|
+
}, clientVersion?: string): Promise<void>;
|
|
56
77
|
export {};
|
|
57
78
|
//# sourceMappingURL=fetch.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.cts","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,EAAE,wBAAwB;
|
|
1
|
+
{"version":3,"file":"fetch.d.cts","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,EAAE,wBAAwB;AASvE,OAAO,KAAK,EAAE,SAAS,EAAE,yBAAqB;AAM9C,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,mBAAmB,EAEnB,WAAW,EACZ,qBAAiB;AAElB,eAAO,MAAM,gBAAgB,aAAc,MAAM,kBAAkB,MAAM;;;CAGvE,CAAC;AAEH;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,GAAG,GAAG,WAAW,EAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,EACtB,gBAAgB,EAAE,MAAM,EACxB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAkBtC;AA4CD;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,WAAW,GAAG,IAAI,EAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,EACtB,gBAAgB,EAAE,MAAM,EACxB,SAAS,EAAE,SAAS,GAAG,IAAI,EAC3B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IACT,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B,CAAC,CAiDD;AAED,QAAA,MAAM,2BAA2B,YAAmB;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,IAAI,aAAa,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;;GAqCA,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,YAClB;IACP,UAAU,EAAE,IAAI,MAAM,CAAC,CAAC;CACzB,GAAG,KAAK,WAAW,kCAAkC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC;;eAgCxE,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM,EACxB,mBAAmB,EAAE;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,mBAAmB,EAAE,CAAC,kBAAkB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC5D,oBAAoB,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE,EACD,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CA2Df"}
|
package/dist/utils/fetch.d.mts
CHANGED
|
@@ -17,8 +17,7 @@ export declare const getClientHeaders: (clientId: string, clientVersion?: string
|
|
|
17
17
|
*/
|
|
18
18
|
export declare function fetchBridgeTokens(chainId: Hex | CaipChainId, clientId: string, fetchFn: FetchFunction, bridgeApiBaseUrl: string, clientVersion?: string): Promise<Record<string, BridgeAsset>>;
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
* then fetches quotes from the bridge-api
|
|
20
|
+
* Fetches quotes from the bridge-api's getQuote endpoint
|
|
22
21
|
*
|
|
23
22
|
* @param request - The quote request
|
|
24
23
|
* @param signal - The abort signal
|
|
@@ -39,6 +38,7 @@ declare const fetchAssetPricesForCurrency: (request: {
|
|
|
39
38
|
clientId: string;
|
|
40
39
|
clientVersion?: string;
|
|
41
40
|
fetchFn: FetchFunction;
|
|
41
|
+
signal?: AbortSignal;
|
|
42
42
|
}) => Promise<Record<`${string}:${string}/${string}:${string}`, {
|
|
43
43
|
[currency: string]: string;
|
|
44
44
|
}>>;
|
|
@@ -53,5 +53,26 @@ export declare const fetchAssetPrices: (request: {
|
|
|
53
53
|
} & Omit<Parameters<typeof fetchAssetPricesForCurrency>[0], 'currency'>) => Promise<Record<`${string}:${string}/${string}:${string}`, {
|
|
54
54
|
[currency: string]: string;
|
|
55
55
|
} | undefined>>;
|
|
56
|
+
/**
|
|
57
|
+
* Converts the generic quote request to the type that the bridge-api expects
|
|
58
|
+
* then fetches quotes from the bridge-api
|
|
59
|
+
*
|
|
60
|
+
* @param fetchFn - The fetch function to use
|
|
61
|
+
* @param request - The quote request
|
|
62
|
+
* @param signal - The abort signal
|
|
63
|
+
* @param clientId - The client ID for metrics
|
|
64
|
+
* @param bridgeApiBaseUrl - The base URL for the bridge API
|
|
65
|
+
* @param serverEventHandlers - The server event handlers
|
|
66
|
+
* @param serverEventHandlers.onValidationFailure - The function to handle validation failures
|
|
67
|
+
* @param serverEventHandlers.onValidQuoteReceived - The function to handle valid quotes
|
|
68
|
+
* @param serverEventHandlers.onClose - The function to run when the stream is closed and there are no thrown errors
|
|
69
|
+
* @param clientVersion - The client version for metrics (optional)
|
|
70
|
+
* @returns A list of bridge tx quotes
|
|
71
|
+
*/
|
|
72
|
+
export declare function fetchBridgeQuoteStream(fetchFn: FetchFunction, request: GenericQuoteRequest, signal: AbortSignal | undefined, clientId: string, bridgeApiBaseUrl: string, serverEventHandlers: {
|
|
73
|
+
onClose: () => void;
|
|
74
|
+
onValidationFailure: (validationFailures: string[]) => void;
|
|
75
|
+
onValidQuoteReceived: (quotes: QuoteResponse) => Promise<void>;
|
|
76
|
+
}, clientVersion?: string): Promise<void>;
|
|
56
77
|
export {};
|
|
57
78
|
//# sourceMappingURL=fetch.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.mts","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,EAAE,wBAAwB;
|
|
1
|
+
{"version":3,"file":"fetch.d.mts","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,EAAE,wBAAwB;AASvE,OAAO,KAAK,EAAE,SAAS,EAAE,yBAAqB;AAM9C,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,mBAAmB,EAEnB,WAAW,EACZ,qBAAiB;AAElB,eAAO,MAAM,gBAAgB,aAAc,MAAM,kBAAkB,MAAM;;;CAGvE,CAAC;AAEH;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,GAAG,GAAG,WAAW,EAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,EACtB,gBAAgB,EAAE,MAAM,EACxB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAkBtC;AA4CD;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,WAAW,GAAG,IAAI,EAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,EACtB,gBAAgB,EAAE,MAAM,EACxB,SAAS,EAAE,SAAS,GAAG,IAAI,EAC3B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IACT,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B,CAAC,CAiDD;AAED,QAAA,MAAM,2BAA2B,YAAmB;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,IAAI,aAAa,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;;GAqCA,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,YAClB;IACP,UAAU,EAAE,IAAI,MAAM,CAAC,CAAC;CACzB,GAAG,KAAK,WAAW,kCAAkC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC;;eAgCxE,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM,EACxB,mBAAmB,EAAE;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,mBAAmB,EAAE,CAAC,kBAAkB,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC5D,oBAAoB,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE,EACD,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CA2Df"}
|
package/dist/utils/fetch.mjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { StructError } from "@metamask/superstruct";
|
|
2
|
-
import {
|
|
2
|
+
import { fetchEventSource } from "@microsoft/fetch-event-source";
|
|
3
3
|
import { isBitcoinChainId } from "./bridge.mjs";
|
|
4
4
|
import { formatAddressToCaipReference, formatChainIdToDec } from "./caip-formatters.mjs";
|
|
5
5
|
import { validateQuoteResponse, validateBitcoinQuoteResponse, validateSwapsTokenObject } from "./validators.mjs";
|
|
6
|
-
const CACHE_REFRESH_TEN_MINUTES = 10 * Duration.Minute;
|
|
7
6
|
export const getClientHeaders = (clientId, clientVersion) => ({
|
|
8
7
|
'X-Client-Id': clientId,
|
|
9
8
|
...(clientVersion ? { 'Client-Version': clientVersion } : {}),
|
|
@@ -26,8 +25,6 @@ export async function fetchBridgeTokens(chainId, clientId, fetchFn, bridgeApiBas
|
|
|
26
25
|
// note that the Assets controller won't be able to provide tokens. In extension we fetch+cache the token list from bridge-api to handle this
|
|
27
26
|
const tokens = await fetchFn(url, {
|
|
28
27
|
headers: getClientHeaders(clientId, clientVersion),
|
|
29
|
-
cacheOptions: { cacheRefreshTime: CACHE_REFRESH_TEN_MINUTES },
|
|
30
|
-
functionName: 'fetchBridgeTokens',
|
|
31
28
|
});
|
|
32
29
|
const transformedTokens = {};
|
|
33
30
|
tokens.forEach((token) => {
|
|
@@ -39,18 +36,11 @@ export async function fetchBridgeTokens(chainId, clientId, fetchFn, bridgeApiBas
|
|
|
39
36
|
}
|
|
40
37
|
/**
|
|
41
38
|
* Converts the generic quote request to the type that the bridge-api expects
|
|
42
|
-
* then fetches quotes from the bridge-api
|
|
43
39
|
*
|
|
44
40
|
* @param request - The quote request
|
|
45
|
-
* @
|
|
46
|
-
* @param clientId - The client ID for metrics
|
|
47
|
-
* @param fetchFn - The fetch function to use
|
|
48
|
-
* @param bridgeApiBaseUrl - The base URL for the bridge API
|
|
49
|
-
* @param featureId - The feature ID to append to each quote
|
|
50
|
-
* @param clientVersion - The client version for metrics (optional)
|
|
51
|
-
* @returns A list of bridge tx quotes
|
|
41
|
+
* @returns A URLSearchParams object with the query parameters
|
|
52
42
|
*/
|
|
53
|
-
|
|
43
|
+
const formatQueryParams = (request) => {
|
|
54
44
|
const destWalletAddress = request.destWalletAddress ?? request.walletAddress;
|
|
55
45
|
// Transform the generic quote request into QuoteRequest
|
|
56
46
|
const normalizedRequest = {
|
|
@@ -82,12 +72,26 @@ export async function fetchBridgeQuotes(request, signal, clientId, fetchFn, brid
|
|
|
82
72
|
Object.entries(normalizedRequest).forEach(([key, value]) => {
|
|
83
73
|
queryParams.append(key, value.toString());
|
|
84
74
|
});
|
|
75
|
+
return queryParams;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Fetches quotes from the bridge-api's getQuote endpoint
|
|
79
|
+
*
|
|
80
|
+
* @param request - The quote request
|
|
81
|
+
* @param signal - The abort signal
|
|
82
|
+
* @param clientId - The client ID for metrics
|
|
83
|
+
* @param fetchFn - The fetch function to use
|
|
84
|
+
* @param bridgeApiBaseUrl - The base URL for the bridge API
|
|
85
|
+
* @param featureId - The feature ID to append to each quote
|
|
86
|
+
* @param clientVersion - The client version for metrics (optional)
|
|
87
|
+
* @returns A list of bridge tx quotes
|
|
88
|
+
*/
|
|
89
|
+
export async function fetchBridgeQuotes(request, signal, clientId, fetchFn, bridgeApiBaseUrl, featureId, clientVersion) {
|
|
90
|
+
const queryParams = formatQueryParams(request);
|
|
85
91
|
const url = `${bridgeApiBaseUrl}/getQuote?${queryParams}`;
|
|
86
92
|
const quotes = await fetchFn(url, {
|
|
87
93
|
headers: getClientHeaders(clientId, clientVersion),
|
|
88
94
|
signal,
|
|
89
|
-
cacheOptions: { cacheRefreshTime: 0 },
|
|
90
|
-
functionName: 'fetchBridgeQuotes',
|
|
91
95
|
});
|
|
92
96
|
const uniqueValidationFailures = new Set([]);
|
|
93
97
|
const filteredQuotes = quotes
|
|
@@ -128,7 +132,7 @@ export async function fetchBridgeQuotes(request, signal, clientId, fetchFn, brid
|
|
|
128
132
|
};
|
|
129
133
|
}
|
|
130
134
|
const fetchAssetPricesForCurrency = async (request) => {
|
|
131
|
-
const { currency, assetIds, clientId, clientVersion, fetchFn } = request;
|
|
135
|
+
const { currency, assetIds, clientId, clientVersion, fetchFn, signal } = request;
|
|
132
136
|
const validAssetIds = Array.from(assetIds).filter(Boolean);
|
|
133
137
|
if (validAssetIds.length === 0) {
|
|
134
138
|
return {};
|
|
@@ -140,8 +144,7 @@ const fetchAssetPricesForCurrency = async (request) => {
|
|
|
140
144
|
const url = `https://price.api.cx.metamask.io/v3/spot-prices?${queryParams}`;
|
|
141
145
|
const priceApiResponse = (await fetchFn(url, {
|
|
142
146
|
headers: getClientHeaders(clientId, clientVersion),
|
|
143
|
-
|
|
144
|
-
functionName: 'fetchAssetExchangeRates',
|
|
147
|
+
signal,
|
|
145
148
|
}));
|
|
146
149
|
if (!priceApiResponse || typeof priceApiResponse !== 'object') {
|
|
147
150
|
return {};
|
|
@@ -186,4 +189,78 @@ export const fetchAssetPrices = async (request) => {
|
|
|
186
189
|
});
|
|
187
190
|
return combinedPrices;
|
|
188
191
|
};
|
|
192
|
+
/**
|
|
193
|
+
* Converts the generic quote request to the type that the bridge-api expects
|
|
194
|
+
* then fetches quotes from the bridge-api
|
|
195
|
+
*
|
|
196
|
+
* @param fetchFn - The fetch function to use
|
|
197
|
+
* @param request - The quote request
|
|
198
|
+
* @param signal - The abort signal
|
|
199
|
+
* @param clientId - The client ID for metrics
|
|
200
|
+
* @param bridgeApiBaseUrl - The base URL for the bridge API
|
|
201
|
+
* @param serverEventHandlers - The server event handlers
|
|
202
|
+
* @param serverEventHandlers.onValidationFailure - The function to handle validation failures
|
|
203
|
+
* @param serverEventHandlers.onValidQuoteReceived - The function to handle valid quotes
|
|
204
|
+
* @param serverEventHandlers.onClose - The function to run when the stream is closed and there are no thrown errors
|
|
205
|
+
* @param clientVersion - The client version for metrics (optional)
|
|
206
|
+
* @returns A list of bridge tx quotes
|
|
207
|
+
*/
|
|
208
|
+
export async function fetchBridgeQuoteStream(fetchFn, request, signal, clientId, bridgeApiBaseUrl, serverEventHandlers, clientVersion) {
|
|
209
|
+
const queryParams = formatQueryParams(request);
|
|
210
|
+
const onMessage = (event) => {
|
|
211
|
+
const uniqueValidationFailures = new Set([]);
|
|
212
|
+
if (event.data === '') {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const quoteResponse = JSON.parse(event.data);
|
|
216
|
+
try {
|
|
217
|
+
validateQuoteResponse(quoteResponse);
|
|
218
|
+
// eslint-disable-next-line promise/catch-or-return, @typescript-eslint/no-floating-promises
|
|
219
|
+
serverEventHandlers.onValidQuoteReceived(quoteResponse).then((v) => {
|
|
220
|
+
return v;
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
if (error instanceof StructError) {
|
|
225
|
+
error.failures().forEach(({ branch, path }) => {
|
|
226
|
+
const aggregatorId = branch?.[0]?.quote?.bridgeId ||
|
|
227
|
+
branch?.[0]?.quote?.bridges?.[0] ||
|
|
228
|
+
quoteResponse?.quote?.bridgeId ||
|
|
229
|
+
quoteResponse?.quote?.bridges?.[0] ||
|
|
230
|
+
'unknown';
|
|
231
|
+
const pathString = path?.join('.') || 'unknown';
|
|
232
|
+
uniqueValidationFailures.add([aggregatorId, pathString].join('|'));
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
const validationFailures = Array.from(uniqueValidationFailures);
|
|
236
|
+
if (uniqueValidationFailures.size > 0) {
|
|
237
|
+
console.warn('Quote validation failed', validationFailures);
|
|
238
|
+
serverEventHandlers.onValidationFailure(validationFailures);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
// Rethrow any unexpected errors
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
const urlStream = `${bridgeApiBaseUrl}/getQuoteStream?${queryParams}`;
|
|
247
|
+
await fetchEventSource(urlStream, {
|
|
248
|
+
headers: {
|
|
249
|
+
...getClientHeaders(clientId, clientVersion),
|
|
250
|
+
'Content-Type': 'text/event-stream',
|
|
251
|
+
},
|
|
252
|
+
signal,
|
|
253
|
+
onmessage: onMessage,
|
|
254
|
+
onerror: (e) => {
|
|
255
|
+
// Rethrow error to prevent silent fetch failures
|
|
256
|
+
throw new Error(e.toString());
|
|
257
|
+
},
|
|
258
|
+
onclose: () => {
|
|
259
|
+
serverEventHandlers.onClose();
|
|
260
|
+
},
|
|
261
|
+
// Cancels the request when document is hidden, will automatically restart when visible
|
|
262
|
+
openWhenHidden: false,
|
|
263
|
+
fetch: fetchFn,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
189
266
|
//# sourceMappingURL=fetch.mjs.map
|
package/dist/utils/fetch.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.mjs","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"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","sourcesContent":["import { StructError } from '@metamask/superstruct';\nimport type { CaipAssetType, CaipChainId, Hex } from '@metamask/utils';\nimport { Duration } from '@metamask/utils';\n\nimport { isBitcoinChainId } from './bridge';\nimport {\n formatAddressToCaipReference,\n formatChainIdToDec,\n} from './caip-formatters';\nimport type { FeatureId } from './validators';\nimport {\n validateQuoteResponse,\n validateBitcoinQuoteResponse,\n validateSwapsTokenObject,\n} from './validators';\nimport type {\n QuoteResponse,\n FetchFunction,\n GenericQuoteRequest,\n QuoteRequest,\n BridgeAsset,\n} from '../types';\n\nconst CACHE_REFRESH_TEN_MINUTES = 10 * Duration.Minute;\n\nexport const getClientHeaders = (clientId: string, clientVersion?: string) => ({\n 'X-Client-Id': clientId,\n ...(clientVersion ? { 'Client-Version': clientVersion } : {}),\n});\n\n/**\n * Returns a list of enabled (unblocked) tokens\n *\n * @param chainId - The chain ID to fetch tokens for\n * @param clientId - The client ID for metrics\n * @param fetchFn - The fetch function to use\n * @param bridgeApiBaseUrl - The base URL for the bridge API\n * @param clientVersion - The client version for metrics (optional)\n * @returns A list of enabled (unblocked) tokens\n */\nexport async function fetchBridgeTokens(\n chainId: Hex | CaipChainId,\n clientId: string,\n fetchFn: FetchFunction,\n bridgeApiBaseUrl: string,\n clientVersion?: string,\n): Promise<Record<string, BridgeAsset>> {\n // TODO make token api v2 call\n const url = `${bridgeApiBaseUrl}/getTokens?chainId=${formatChainIdToDec(chainId)}`;\n\n // TODO we will need to cache these. In Extension fetchWithCache is used. This is due to the following:\n // If we allow selecting dest networks which the user has not imported,\n // note that the Assets controller won't be able to provide tokens. In extension we fetch+cache the token list from bridge-api to handle this\n const tokens = await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n cacheOptions: { cacheRefreshTime: CACHE_REFRESH_TEN_MINUTES },\n functionName: 'fetchBridgeTokens',\n });\n\n const transformedTokens: Record<string, BridgeAsset> = {};\n tokens.forEach((token: unknown) => {\n if (validateSwapsTokenObject(token)) {\n transformedTokens[token.address] = token;\n }\n });\n return transformedTokens;\n}\n\n/**\n * Converts the generic quote request to the type that the bridge-api expects\n * then fetches quotes from the bridge-api\n *\n * @param request - The quote request\n * @param signal - The abort signal\n * @param clientId - The client ID for metrics\n * @param fetchFn - The fetch function to use\n * @param bridgeApiBaseUrl - The base URL for the bridge API\n * @param featureId - The feature ID to append to each quote\n * @param clientVersion - The client version for metrics (optional)\n * @returns A list of bridge tx quotes\n */\nexport async function fetchBridgeQuotes(\n request: GenericQuoteRequest,\n signal: AbortSignal | null,\n clientId: string,\n fetchFn: FetchFunction,\n bridgeApiBaseUrl: string,\n featureId: FeatureId | null,\n clientVersion?: string,\n): Promise<{\n quotes: QuoteResponse[];\n validationFailures: string[];\n}> {\n const destWalletAddress = request.destWalletAddress ?? request.walletAddress;\n // Transform the generic quote request into QuoteRequest\n const normalizedRequest: QuoteRequest = {\n walletAddress: formatAddressToCaipReference(request.walletAddress),\n destWalletAddress: formatAddressToCaipReference(destWalletAddress),\n srcChainId: formatChainIdToDec(request.srcChainId),\n destChainId: formatChainIdToDec(request.destChainId),\n srcTokenAddress: formatAddressToCaipReference(request.srcTokenAddress),\n destTokenAddress: formatAddressToCaipReference(request.destTokenAddress),\n srcTokenAmount: request.srcTokenAmount,\n insufficientBal: Boolean(request.insufficientBal),\n resetApproval: Boolean(request.resetApproval),\n gasIncluded: Boolean(request.gasIncluded),\n gasIncluded7702: Boolean(request.gasIncluded7702),\n };\n if (request.slippage !== undefined) {\n normalizedRequest.slippage = request.slippage;\n }\n if (request.noFee !== undefined) {\n normalizedRequest.noFee = request.noFee;\n }\n if (request.aggIds && request.aggIds.length > 0) {\n normalizedRequest.aggIds = request.aggIds;\n }\n if (request.bridgeIds && request.bridgeIds.length > 0) {\n normalizedRequest.bridgeIds = request.bridgeIds;\n }\n\n const queryParams = new URLSearchParams();\n Object.entries(normalizedRequest).forEach(([key, value]) => {\n queryParams.append(key, value.toString());\n });\n const url = `${bridgeApiBaseUrl}/getQuote?${queryParams}`;\n const quotes: unknown[] = await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n signal,\n cacheOptions: { cacheRefreshTime: 0 },\n functionName: 'fetchBridgeQuotes',\n });\n\n const uniqueValidationFailures: Set<string> = new Set<string>([]);\n const filteredQuotes = quotes\n .filter((quoteResponse: unknown): quoteResponse is QuoteResponse => {\n try {\n const isBitcoinQuote = isBitcoinChainId(request.srcChainId);\n\n if (isBitcoinQuote) {\n return validateBitcoinQuoteResponse(quoteResponse);\n }\n return validateQuoteResponse(quoteResponse);\n } catch (error) {\n if (error instanceof StructError) {\n error.failures().forEach(({ branch, path }) => {\n const aggregatorId =\n branch?.[0]?.quote?.bridgeId ||\n branch?.[0]?.quote?.bridges?.[0] ||\n (quoteResponse as QuoteResponse)?.quote?.bridgeId ||\n (quoteResponse as QuoteResponse)?.quote?.bridges?.[0] ||\n 'unknown';\n const pathString = path?.join('.') || 'unknown';\n uniqueValidationFailures.add([aggregatorId, pathString].join('|'));\n });\n }\n return false;\n }\n })\n .map((quote) => ({\n ...quote,\n featureId: featureId ?? undefined,\n }));\n\n const validationFailures = Array.from(uniqueValidationFailures);\n if (uniqueValidationFailures.size > 0) {\n console.warn('Quote validation failed', validationFailures);\n }\n\n return {\n quotes: filteredQuotes,\n validationFailures,\n };\n}\n\nconst fetchAssetPricesForCurrency = async (request: {\n currency: string;\n assetIds: Set<CaipAssetType>;\n clientId: string;\n clientVersion?: string;\n fetchFn: FetchFunction;\n}): Promise<Record<CaipAssetType, { [currency: string]: string }>> => {\n const { currency, assetIds, clientId, clientVersion, fetchFn } = request;\n const validAssetIds = Array.from(assetIds).filter(Boolean);\n if (validAssetIds.length === 0) {\n return {};\n }\n\n const queryParams = new URLSearchParams({\n assetIds: validAssetIds.filter(Boolean).join(','),\n vsCurrency: currency,\n });\n const url = `https://price.api.cx.metamask.io/v3/spot-prices?${queryParams}`;\n const priceApiResponse = (await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n cacheOptions: { cacheRefreshTime: Number(Duration.Second * 30) },\n functionName: 'fetchAssetExchangeRates',\n })) as Record<CaipAssetType, { [currency: string]: number }>;\n\n if (!priceApiResponse || typeof priceApiResponse !== 'object') {\n return {};\n }\n\n return Object.entries(priceApiResponse).reduce(\n (acc, [assetId, currencyToPrice]) => {\n if (!currencyToPrice) {\n return acc;\n }\n if (!acc[assetId as CaipAssetType]) {\n acc[assetId as CaipAssetType] = {};\n }\n if (currencyToPrice[currency]) {\n acc[assetId as CaipAssetType][currency] =\n currencyToPrice[currency].toString();\n }\n return acc;\n },\n {} as Record<CaipAssetType, { [currency: string]: string }>,\n );\n};\n\n/**\n * Fetches the asset prices from the price API for multiple currencies\n *\n * @param request - The request object\n * @returns The asset prices by assetId\n */\nexport const fetchAssetPrices = async (\n request: {\n currencies: Set<string>;\n } & Omit<Parameters<typeof fetchAssetPricesForCurrency>[0], 'currency'>,\n): Promise<\n Record<CaipAssetType, { [currency: string]: string } | undefined>\n> => {\n const { currencies, ...args } = request;\n\n const combinedPrices = await Promise.allSettled(\n Array.from(currencies).map(\n async (currency) =>\n await fetchAssetPricesForCurrency({ ...args, currency }),\n ),\n ).then((priceApiResponse) => {\n return priceApiResponse.reduce(\n (acc, result) => {\n if (result.status === 'fulfilled') {\n Object.entries(result.value).forEach(([assetId, currencyToPrice]) => {\n const existingPrices = acc[assetId as CaipAssetType];\n if (!existingPrices) {\n acc[assetId as CaipAssetType] = {};\n }\n Object.entries(currencyToPrice).forEach(([currency, price]) => {\n acc[assetId as CaipAssetType][currency] = price;\n });\n });\n }\n return acc;\n },\n {} as Record<CaipAssetType, { [currency: string]: string }>,\n );\n });\n\n return combinedPrices;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"fetch.mjs","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"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","sourcesContent":["import { StructError } from '@metamask/superstruct';\nimport type { CaipAssetType, CaipChainId, Hex } from '@metamask/utils';\nimport type { EventSourceMessage } from '@microsoft/fetch-event-source';\nimport { fetchEventSource } from '@microsoft/fetch-event-source';\n\nimport { isBitcoinChainId } from './bridge';\nimport {\n formatAddressToCaipReference,\n formatChainIdToDec,\n} from './caip-formatters';\nimport type { FeatureId } from './validators';\nimport {\n validateQuoteResponse,\n validateBitcoinQuoteResponse,\n validateSwapsTokenObject,\n} from './validators';\nimport type {\n QuoteResponse,\n FetchFunction,\n GenericQuoteRequest,\n QuoteRequest,\n BridgeAsset,\n} from '../types';\n\nexport const getClientHeaders = (clientId: string, clientVersion?: string) => ({\n 'X-Client-Id': clientId,\n ...(clientVersion ? { 'Client-Version': clientVersion } : {}),\n});\n\n/**\n * Returns a list of enabled (unblocked) tokens\n *\n * @param chainId - The chain ID to fetch tokens for\n * @param clientId - The client ID for metrics\n * @param fetchFn - The fetch function to use\n * @param bridgeApiBaseUrl - The base URL for the bridge API\n * @param clientVersion - The client version for metrics (optional)\n * @returns A list of enabled (unblocked) tokens\n */\nexport async function fetchBridgeTokens(\n chainId: Hex | CaipChainId,\n clientId: string,\n fetchFn: FetchFunction,\n bridgeApiBaseUrl: string,\n clientVersion?: string,\n): Promise<Record<string, BridgeAsset>> {\n // TODO make token api v2 call\n const url = `${bridgeApiBaseUrl}/getTokens?chainId=${formatChainIdToDec(chainId)}`;\n\n // TODO we will need to cache these. In Extension fetchWithCache is used. This is due to the following:\n // If we allow selecting dest networks which the user has not imported,\n // note that the Assets controller won't be able to provide tokens. In extension we fetch+cache the token list from bridge-api to handle this\n const tokens = await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n });\n\n const transformedTokens: Record<string, BridgeAsset> = {};\n tokens.forEach((token: unknown) => {\n if (validateSwapsTokenObject(token)) {\n transformedTokens[token.address] = token;\n }\n });\n return transformedTokens;\n}\n\n/**\n * Converts the generic quote request to the type that the bridge-api expects\n *\n * @param request - The quote request\n * @returns A URLSearchParams object with the query parameters\n */\nconst formatQueryParams = (request: GenericQuoteRequest): URLSearchParams => {\n const destWalletAddress = request.destWalletAddress ?? request.walletAddress;\n // Transform the generic quote request into QuoteRequest\n const normalizedRequest: QuoteRequest = {\n walletAddress: formatAddressToCaipReference(request.walletAddress),\n destWalletAddress: formatAddressToCaipReference(destWalletAddress),\n srcChainId: formatChainIdToDec(request.srcChainId),\n destChainId: formatChainIdToDec(request.destChainId),\n srcTokenAddress: formatAddressToCaipReference(request.srcTokenAddress),\n destTokenAddress: formatAddressToCaipReference(request.destTokenAddress),\n srcTokenAmount: request.srcTokenAmount,\n insufficientBal: Boolean(request.insufficientBal),\n resetApproval: Boolean(request.resetApproval),\n gasIncluded: Boolean(request.gasIncluded),\n gasIncluded7702: Boolean(request.gasIncluded7702),\n };\n if (request.slippage !== undefined) {\n normalizedRequest.slippage = request.slippage;\n }\n if (request.noFee !== undefined) {\n normalizedRequest.noFee = request.noFee;\n }\n if (request.aggIds && request.aggIds.length > 0) {\n normalizedRequest.aggIds = request.aggIds;\n }\n if (request.bridgeIds && request.bridgeIds.length > 0) {\n normalizedRequest.bridgeIds = request.bridgeIds;\n }\n\n const queryParams = new URLSearchParams();\n Object.entries(normalizedRequest).forEach(([key, value]) => {\n queryParams.append(key, value.toString());\n });\n return queryParams;\n};\n\n/**\n * Fetches quotes from the bridge-api's getQuote endpoint\n *\n * @param request - The quote request\n * @param signal - The abort signal\n * @param clientId - The client ID for metrics\n * @param fetchFn - The fetch function to use\n * @param bridgeApiBaseUrl - The base URL for the bridge API\n * @param featureId - The feature ID to append to each quote\n * @param clientVersion - The client version for metrics (optional)\n * @returns A list of bridge tx quotes\n */\nexport async function fetchBridgeQuotes(\n request: GenericQuoteRequest,\n signal: AbortSignal | null,\n clientId: string,\n fetchFn: FetchFunction,\n bridgeApiBaseUrl: string,\n featureId: FeatureId | null,\n clientVersion?: string,\n): Promise<{\n quotes: QuoteResponse[];\n validationFailures: string[];\n}> {\n const queryParams = formatQueryParams(request);\n\n const url = `${bridgeApiBaseUrl}/getQuote?${queryParams}`;\n const quotes: unknown[] = await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n signal,\n });\n\n const uniqueValidationFailures: Set<string> = new Set<string>([]);\n const filteredQuotes = quotes\n .filter((quoteResponse: unknown): quoteResponse is QuoteResponse => {\n try {\n const isBitcoinQuote = isBitcoinChainId(request.srcChainId);\n\n if (isBitcoinQuote) {\n return validateBitcoinQuoteResponse(quoteResponse);\n }\n return validateQuoteResponse(quoteResponse);\n } catch (error) {\n if (error instanceof StructError) {\n error.failures().forEach(({ branch, path }) => {\n const aggregatorId =\n branch?.[0]?.quote?.bridgeId ||\n branch?.[0]?.quote?.bridges?.[0] ||\n (quoteResponse as QuoteResponse)?.quote?.bridgeId ||\n (quoteResponse as QuoteResponse)?.quote?.bridges?.[0] ||\n 'unknown';\n const pathString = path?.join('.') || 'unknown';\n uniqueValidationFailures.add([aggregatorId, pathString].join('|'));\n });\n }\n return false;\n }\n })\n .map((quote) => ({\n ...quote,\n featureId: featureId ?? undefined,\n }));\n\n const validationFailures = Array.from(uniqueValidationFailures);\n if (uniqueValidationFailures.size > 0) {\n console.warn('Quote validation failed', validationFailures);\n }\n\n return {\n quotes: filteredQuotes,\n validationFailures,\n };\n}\n\nconst fetchAssetPricesForCurrency = async (request: {\n currency: string;\n assetIds: Set<CaipAssetType>;\n clientId: string;\n clientVersion?: string;\n fetchFn: FetchFunction;\n signal?: AbortSignal;\n}): Promise<Record<CaipAssetType, { [currency: string]: string }>> => {\n const { currency, assetIds, clientId, clientVersion, fetchFn, signal } =\n request;\n const validAssetIds = Array.from(assetIds).filter(Boolean);\n if (validAssetIds.length === 0) {\n return {};\n }\n\n const queryParams = new URLSearchParams({\n assetIds: validAssetIds.filter(Boolean).join(','),\n vsCurrency: currency,\n });\n const url = `https://price.api.cx.metamask.io/v3/spot-prices?${queryParams}`;\n const priceApiResponse = (await fetchFn(url, {\n headers: getClientHeaders(clientId, clientVersion),\n signal,\n })) as Record<CaipAssetType, { [currency: string]: number }>;\n if (!priceApiResponse || typeof priceApiResponse !== 'object') {\n return {};\n }\n\n return Object.entries(priceApiResponse).reduce(\n (acc, [assetId, currencyToPrice]) => {\n if (!currencyToPrice) {\n return acc;\n }\n if (!acc[assetId as CaipAssetType]) {\n acc[assetId as CaipAssetType] = {};\n }\n if (currencyToPrice[currency]) {\n acc[assetId as CaipAssetType][currency] =\n currencyToPrice[currency].toString();\n }\n return acc;\n },\n {} as Record<CaipAssetType, { [currency: string]: string }>,\n );\n};\n\n/**\n * Fetches the asset prices from the price API for multiple currencies\n *\n * @param request - The request object\n * @returns The asset prices by assetId\n */\nexport const fetchAssetPrices = async (\n request: {\n currencies: Set<string>;\n } & Omit<Parameters<typeof fetchAssetPricesForCurrency>[0], 'currency'>,\n): Promise<\n Record<CaipAssetType, { [currency: string]: string } | undefined>\n> => {\n const { currencies, ...args } = request;\n\n const combinedPrices = await Promise.allSettled(\n Array.from(currencies).map(\n async (currency) =>\n await fetchAssetPricesForCurrency({ ...args, currency }),\n ),\n ).then((priceApiResponse) => {\n return priceApiResponse.reduce(\n (acc, result) => {\n if (result.status === 'fulfilled') {\n Object.entries(result.value).forEach(([assetId, currencyToPrice]) => {\n const existingPrices = acc[assetId as CaipAssetType];\n if (!existingPrices) {\n acc[assetId as CaipAssetType] = {};\n }\n Object.entries(currencyToPrice).forEach(([currency, price]) => {\n acc[assetId as CaipAssetType][currency] = price;\n });\n });\n }\n return acc;\n },\n {} as Record<CaipAssetType, { [currency: string]: string }>,\n );\n });\n\n return combinedPrices;\n};\n\n/**\n * Converts the generic quote request to the type that the bridge-api expects\n * then fetches quotes from the bridge-api\n *\n * @param fetchFn - The fetch function to use\n * @param request - The quote request\n * @param signal - The abort signal\n * @param clientId - The client ID for metrics\n * @param bridgeApiBaseUrl - The base URL for the bridge API\n * @param serverEventHandlers - The server event handlers\n * @param serverEventHandlers.onValidationFailure - The function to handle validation failures\n * @param serverEventHandlers.onValidQuoteReceived - The function to handle valid quotes\n * @param serverEventHandlers.onClose - The function to run when the stream is closed and there are no thrown errors\n * @param clientVersion - The client version for metrics (optional)\n * @returns A list of bridge tx quotes\n */\nexport async function fetchBridgeQuoteStream(\n fetchFn: FetchFunction,\n request: GenericQuoteRequest,\n signal: AbortSignal | undefined,\n clientId: string,\n bridgeApiBaseUrl: string,\n serverEventHandlers: {\n onClose: () => void;\n onValidationFailure: (validationFailures: string[]) => void;\n onValidQuoteReceived: (quotes: QuoteResponse) => Promise<void>;\n },\n clientVersion?: string,\n): Promise<void> {\n const queryParams = formatQueryParams(request);\n\n const onMessage = (event: EventSourceMessage) => {\n const uniqueValidationFailures: Set<string> = new Set<string>([]);\n if (event.data === '') {\n return;\n }\n const quoteResponse = JSON.parse(event.data);\n\n try {\n validateQuoteResponse(quoteResponse);\n // eslint-disable-next-line promise/catch-or-return, @typescript-eslint/no-floating-promises\n serverEventHandlers.onValidQuoteReceived(quoteResponse).then((v) => {\n return v;\n });\n } catch (error) {\n if (error instanceof StructError) {\n error.failures().forEach(({ branch, path }) => {\n const aggregatorId =\n branch?.[0]?.quote?.bridgeId ||\n branch?.[0]?.quote?.bridges?.[0] ||\n (quoteResponse as QuoteResponse)?.quote?.bridgeId ||\n (quoteResponse as QuoteResponse)?.quote?.bridges?.[0] ||\n 'unknown';\n const pathString = path?.join('.') || 'unknown';\n uniqueValidationFailures.add([aggregatorId, pathString].join('|'));\n });\n }\n const validationFailures = Array.from(uniqueValidationFailures);\n if (uniqueValidationFailures.size > 0) {\n console.warn('Quote validation failed', validationFailures);\n serverEventHandlers.onValidationFailure(validationFailures);\n } else {\n // Rethrow any unexpected errors\n throw error;\n }\n }\n };\n\n const urlStream = `${bridgeApiBaseUrl}/getQuoteStream?${queryParams}`;\n await fetchEventSource(urlStream, {\n headers: {\n ...getClientHeaders(clientId, clientVersion),\n 'Content-Type': 'text/event-stream',\n },\n signal,\n onmessage: onMessage,\n onerror: (e) => {\n // Rethrow error to prevent silent fetch failures\n throw new Error(e.toString());\n },\n onclose: () => {\n serverEventHandlers.onClose();\n },\n // Cancels the request when document is hidden, will automatically restart when visible\n openWhenHidden: false,\n fetch: fetchFn,\n });\n}\n"]}
|