@metamask/bridge-controller 49.0.1 → 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.
Files changed (64) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/dist/bridge-controller.cjs +109 -169
  3. package/dist/bridge-controller.cjs.map +1 -1
  4. package/dist/bridge-controller.d.cts +5 -4
  5. package/dist/bridge-controller.d.cts.map +1 -1
  6. package/dist/bridge-controller.d.mts +5 -4
  7. package/dist/bridge-controller.d.mts.map +1 -1
  8. package/dist/bridge-controller.mjs +113 -173
  9. package/dist/bridge-controller.mjs.map +1 -1
  10. package/dist/selectors.d.cts +100 -78
  11. package/dist/selectors.d.cts.map +1 -1
  12. package/dist/selectors.d.mts +100 -78
  13. package/dist/selectors.d.mts.map +1 -1
  14. package/dist/types.cjs.map +1 -1
  15. package/dist/types.d.cts +21 -10
  16. package/dist/types.d.cts.map +1 -1
  17. package/dist/types.d.mts +21 -10
  18. package/dist/types.d.mts.map +1 -1
  19. package/dist/types.mjs.map +1 -1
  20. package/dist/utils/feature-flags.d.cts +1 -0
  21. package/dist/utils/feature-flags.d.cts.map +1 -1
  22. package/dist/utils/feature-flags.d.mts +1 -0
  23. package/dist/utils/feature-flags.d.mts.map +1 -1
  24. package/dist/utils/fetch.cjs +97 -19
  25. package/dist/utils/fetch.cjs.map +1 -1
  26. package/dist/utils/fetch.d.cts +23 -2
  27. package/dist/utils/fetch.d.cts.map +1 -1
  28. package/dist/utils/fetch.d.mts +23 -2
  29. package/dist/utils/fetch.d.mts.map +1 -1
  30. package/dist/utils/fetch.mjs +95 -18
  31. package/dist/utils/fetch.mjs.map +1 -1
  32. package/dist/utils/quote-fees.cjs +120 -0
  33. package/dist/utils/quote-fees.cjs.map +1 -0
  34. package/dist/utils/quote-fees.d.cts +18 -0
  35. package/dist/utils/quote-fees.d.cts.map +1 -0
  36. package/dist/utils/quote-fees.d.mts +18 -0
  37. package/dist/utils/quote-fees.d.mts.map +1 -0
  38. package/dist/utils/quote-fees.mjs +116 -0
  39. package/dist/utils/quote-fees.mjs.map +1 -0
  40. package/dist/utils/quote.cjs +12 -1
  41. package/dist/utils/quote.cjs.map +1 -1
  42. package/dist/utils/quote.d.cts +2 -0
  43. package/dist/utils/quote.d.cts.map +1 -1
  44. package/dist/utils/quote.d.mts +2 -0
  45. package/dist/utils/quote.d.mts.map +1 -1
  46. package/dist/utils/quote.mjs +10 -0
  47. package/dist/utils/quote.mjs.map +1 -1
  48. package/dist/utils/snaps.cjs +19 -1
  49. package/dist/utils/snaps.cjs.map +1 -1
  50. package/dist/utils/snaps.d.cts +9 -0
  51. package/dist/utils/snaps.d.cts.map +1 -1
  52. package/dist/utils/snaps.d.mts +9 -0
  53. package/dist/utils/snaps.d.mts.map +1 -1
  54. package/dist/utils/snaps.mjs +17 -0
  55. package/dist/utils/snaps.mjs.map +1 -1
  56. package/dist/utils/validators.cjs +1 -0
  57. package/dist/utils/validators.cjs.map +1 -1
  58. package/dist/utils/validators.d.cts +10 -7
  59. package/dist/utils/validators.d.cts.map +1 -1
  60. package/dist/utils/validators.d.mts +10 -7
  61. package/dist/utils/validators.d.mts.map +1 -1
  62. package/dist/utils/validators.mjs +1 -0
  63. package/dist/utils/validators.mjs.map +1 -1
  64. package/package.json +4 -3
@@ -1 +1 @@
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 { 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"]}
@@ -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
- * Converts the generic quote request to the type that the bridge-api expects
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;AAQvE,OAAO,KAAK,EAAE,SAAS,EAAE,yBAAqB;AAM9C,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,mBAAmB,EAEnB,WAAW,EACZ,qBAAiB;AAIlB,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,CAoBtC;AAED;;;;;;;;;;;;GAYG;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,CAiFD;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;CACxB;;GAsCA,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"}
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"}
@@ -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
- * Converts the generic quote request to the type that the bridge-api expects
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;AAQvE,OAAO,KAAK,EAAE,SAAS,EAAE,yBAAqB;AAM9C,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,mBAAmB,EAEnB,WAAW,EACZ,qBAAiB;AAIlB,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,CAoBtC;AAED;;;;;;;;;;;;GAYG;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,CAiFD;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;CACxB;;GAsCA,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"}
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"}
@@ -1,9 +1,8 @@
1
1
  import { StructError } from "@metamask/superstruct";
2
- import { Duration } from "@metamask/utils";
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
- * @param signal - The abort signal
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
- export async function fetchBridgeQuotes(request, signal, clientId, fetchFn, bridgeApiBaseUrl, featureId, clientVersion) {
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
- cacheOptions: { cacheRefreshTime: Number(Duration.Second * 30) },
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
@@ -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"]}
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.appendFeesToQuotes = void 0;
4
+ const utils_1 = require("@metamask/utils");
5
+ const bridge_1 = require("./bridge.cjs");
6
+ const caip_formatters_1 = require("./caip-formatters.cjs");
7
+ const snaps_1 = require("./snaps.cjs");
8
+ const chains_1 = require("../constants/chains.cjs");
9
+ /**
10
+ * Appends transaction fees for EVM chains to quotes
11
+ *
12
+ * @param quotes - Array of quote responses to append fees to
13
+ * @param getLayer1GasFee - The function to use to get the layer 1 gas fee
14
+ * @returns Array of quotes with fees appended, or undefined if quotes are for non-EVM chains
15
+ */
16
+ const appendL1GasFees = async (quotes, getLayer1GasFee) => {
17
+ // Indicates whether some of the quotes are not for optimism or base
18
+ const hasInvalidQuotes = quotes.some(({ quote }) => {
19
+ const chainId = (0, caip_formatters_1.formatChainIdToCaip)(quote.srcChainId);
20
+ return ![chains_1.CHAIN_IDS.OPTIMISM, chains_1.CHAIN_IDS.BASE]
21
+ .map(caip_formatters_1.formatChainIdToCaip)
22
+ .includes(chainId);
23
+ });
24
+ // Only append L1 gas fees if all quotes are for either optimism or base
25
+ if (hasInvalidQuotes) {
26
+ return undefined;
27
+ }
28
+ const l1GasFeePromises = Promise.allSettled(quotes.map(async (quoteResponse) => {
29
+ const { quote, trade, approval } = quoteResponse;
30
+ const chainId = (0, utils_1.numberToHex)(quote.srcChainId);
31
+ const getTxParams = (txData) => ({
32
+ from: txData.from,
33
+ to: txData.to,
34
+ value: txData.value,
35
+ data: txData.data,
36
+ gasLimit: txData.gasLimit?.toString(),
37
+ });
38
+ const approvalL1GasFees = approval
39
+ ? await getLayer1GasFee({
40
+ transactionParams: getTxParams(approval),
41
+ chainId,
42
+ })
43
+ : '0x0';
44
+ const tradeL1GasFees = await getLayer1GasFee({
45
+ transactionParams: getTxParams(trade),
46
+ chainId,
47
+ });
48
+ if (approvalL1GasFees === undefined || tradeL1GasFees === undefined) {
49
+ return undefined;
50
+ }
51
+ return {
52
+ ...quoteResponse,
53
+ l1GasFeesInHexWei: (0, bridge_1.sumHexes)(approvalL1GasFees, tradeL1GasFees),
54
+ };
55
+ }));
56
+ const quotesWithL1GasFees = (await l1GasFeePromises).reduce((acc, result) => {
57
+ if (result.status === 'fulfilled' && result.value) {
58
+ acc.push(result.value);
59
+ }
60
+ else if (result.status === 'rejected') {
61
+ console.error('Error calculating L1 gas fees for quote', result.reason);
62
+ }
63
+ return acc;
64
+ }, []);
65
+ return quotesWithL1GasFees;
66
+ };
67
+ /**
68
+ * Appends transaction fees for non-EVM chains to quotes
69
+ *
70
+ * @param quotes - Array of quote responses to append fees to
71
+ * @param messenger - The messaging system to use to call the snap controller
72
+ * @param selectedAccount - The selected account for which the quotes were requested
73
+ * @returns Array of quotes with fees appended, or undefined if quotes are for EVM chains
74
+ */
75
+ const appendNonEvmFees = async (quotes, messenger, selectedAccount) => {
76
+ if (quotes.some(({ quote: { srcChainId } }) => !(0, bridge_1.isNonEvmChainId)(srcChainId))) {
77
+ return undefined;
78
+ }
79
+ const nonEvmFeePromises = Promise.allSettled(quotes.map(async (quoteResponse) => {
80
+ const { trade, quote } = quoteResponse;
81
+ if (selectedAccount?.metadata?.snap?.id && typeof trade === 'string') {
82
+ const scope = (0, caip_formatters_1.formatChainIdToCaip)(quote.srcChainId);
83
+ const response = (await messenger.call('SnapController:handleRequest', (0, snaps_1.computeFeeRequest)(selectedAccount.metadata.snap?.id, trade, selectedAccount.id, scope)));
84
+ const baseFee = response?.find((fee) => fee.type === 'base');
85
+ // Store fees in native units as returned by the snap (e.g., SOL, BTC)
86
+ const feeInNative = baseFee?.asset?.amount || '0';
87
+ return {
88
+ ...quoteResponse,
89
+ nonEvmFeesInNative: feeInNative,
90
+ };
91
+ }
92
+ return quoteResponse;
93
+ }));
94
+ const quotesWithNonEvmFees = (await nonEvmFeePromises).reduce((acc, result) => {
95
+ if (result.status === 'fulfilled' && result.value) {
96
+ acc.push(result.value);
97
+ }
98
+ else if (result.status === 'rejected') {
99
+ console.error('Error calculating non-EVM fees for quote', result.reason);
100
+ }
101
+ return acc;
102
+ }, []);
103
+ return quotesWithNonEvmFees;
104
+ };
105
+ /**
106
+ * Appends transaction fees to quotes
107
+ *
108
+ * @param quotes - Array of quote responses to append fees to
109
+ * @param messenger - The bridge controller to use to call the snap controller
110
+ * @param getLayer1GasFee - The function to use to get the layer 1 gas fee
111
+ * @param selectedAccount - The selected account for which the quotes were requested
112
+ * @returns Array of quotes with fees appended, or undefined if quotes are for EVM chains
113
+ */
114
+ const appendFeesToQuotes = async (quotes, messenger, getLayer1GasFee, selectedAccount) => {
115
+ const quotesWithL1GasFees = await appendL1GasFees(quotes, getLayer1GasFee);
116
+ const quotesWithNonEvmFees = await appendNonEvmFees(quotes, messenger, selectedAccount);
117
+ return quotesWithL1GasFees ?? quotesWithNonEvmFees ?? quotes;
118
+ };
119
+ exports.appendFeesToQuotes = appendFeesToQuotes;
120
+ //# sourceMappingURL=quote-fees.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-fees.cjs","sourceRoot":"","sources":["../../src/utils/quote-fees.ts"],"names":[],"mappings":"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","sourcesContent":["import type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { TransactionController } from '@metamask/transaction-controller';\nimport { numberToHex } from '@metamask/utils';\n\nimport { isNonEvmChainId, sumHexes } from './bridge';\nimport { formatChainIdToCaip } from './caip-formatters';\nimport { computeFeeRequest } from './snaps';\nimport { CHAIN_IDS } from '../constants/chains';\nimport type {\n QuoteResponse,\n L1GasFees,\n NonEvmFees,\n TxData,\n BridgeControllerMessenger,\n} from '../types';\n\n/**\n * Appends transaction fees for EVM chains to quotes\n *\n * @param quotes - Array of quote responses to append fees to\n * @param getLayer1GasFee - The function to use to get the layer 1 gas fee\n * @returns Array of quotes with fees appended, or undefined if quotes are for non-EVM chains\n */\nconst appendL1GasFees = async (\n quotes: QuoteResponse[],\n getLayer1GasFee: typeof TransactionController.prototype.getLayer1GasFee,\n): Promise<(QuoteResponse & L1GasFees)[] | undefined> => {\n // Indicates whether some of the quotes are not for optimism or base\n const hasInvalidQuotes = quotes.some(({ quote }) => {\n const chainId = formatChainIdToCaip(quote.srcChainId);\n return ![CHAIN_IDS.OPTIMISM, CHAIN_IDS.BASE]\n .map(formatChainIdToCaip)\n .includes(chainId);\n });\n\n // Only append L1 gas fees if all quotes are for either optimism or base\n if (hasInvalidQuotes) {\n return undefined;\n }\n\n const l1GasFeePromises = Promise.allSettled(\n quotes.map(async (quoteResponse) => {\n const { quote, trade, approval } = quoteResponse;\n const chainId = numberToHex(quote.srcChainId);\n\n const getTxParams = (txData: TxData) => ({\n from: txData.from,\n to: txData.to,\n value: txData.value,\n data: txData.data,\n gasLimit: txData.gasLimit?.toString(),\n });\n const approvalL1GasFees = approval\n ? await getLayer1GasFee({\n transactionParams: getTxParams(approval),\n chainId,\n })\n : '0x0';\n const tradeL1GasFees = await getLayer1GasFee({\n transactionParams: getTxParams(trade),\n chainId,\n });\n\n if (approvalL1GasFees === undefined || tradeL1GasFees === undefined) {\n return undefined;\n }\n\n return {\n ...quoteResponse,\n l1GasFeesInHexWei: sumHexes(approvalL1GasFees, tradeL1GasFees),\n };\n }),\n );\n\n const quotesWithL1GasFees = (await l1GasFeePromises).reduce<\n (QuoteResponse & L1GasFees)[]\n >((acc, result) => {\n if (result.status === 'fulfilled' && result.value) {\n acc.push(result.value);\n } else if (result.status === 'rejected') {\n console.error('Error calculating L1 gas fees for quote', result.reason);\n }\n return acc;\n }, []);\n\n return quotesWithL1GasFees;\n};\n\n/**\n * Appends transaction fees for non-EVM chains to quotes\n *\n * @param quotes - Array of quote responses to append fees to\n * @param messenger - The messaging system to use to call the snap controller\n * @param selectedAccount - The selected account for which the quotes were requested\n * @returns Array of quotes with fees appended, or undefined if quotes are for EVM chains\n */\nconst appendNonEvmFees = async (\n quotes: QuoteResponse[],\n messenger: BridgeControllerMessenger,\n selectedAccount: InternalAccount,\n): Promise<(QuoteResponse & NonEvmFees)[] | undefined> => {\n if (\n quotes.some(({ quote: { srcChainId } }) => !isNonEvmChainId(srcChainId))\n ) {\n return undefined;\n }\n\n const nonEvmFeePromises = Promise.allSettled(\n quotes.map(async (quoteResponse) => {\n const { trade, quote } = quoteResponse;\n\n if (selectedAccount?.metadata?.snap?.id && typeof trade === 'string') {\n const scope = formatChainIdToCaip(quote.srcChainId);\n\n const response = (await messenger.call(\n 'SnapController:handleRequest',\n computeFeeRequest(\n selectedAccount.metadata.snap?.id,\n trade,\n selectedAccount.id,\n scope,\n ),\n )) as {\n type: 'base' | 'priority';\n asset: {\n unit: string;\n type: string;\n amount: string;\n fungible: true;\n };\n }[];\n\n const baseFee = response?.find((fee) => fee.type === 'base');\n // Store fees in native units as returned by the snap (e.g., SOL, BTC)\n const feeInNative = baseFee?.asset?.amount || '0';\n\n return {\n ...quoteResponse,\n nonEvmFeesInNative: feeInNative,\n };\n }\n return quoteResponse;\n }),\n );\n\n const quotesWithNonEvmFees = (await nonEvmFeePromises).reduce<\n (QuoteResponse & NonEvmFees)[]\n >((acc, result) => {\n if (result.status === 'fulfilled' && result.value) {\n acc.push(result.value);\n } else if (result.status === 'rejected') {\n console.error('Error calculating non-EVM fees for quote', result.reason);\n }\n return acc;\n }, []);\n\n return quotesWithNonEvmFees;\n};\n\n/**\n * Appends transaction fees to quotes\n *\n * @param quotes - Array of quote responses to append fees to\n * @param messenger - The bridge controller to use to call the snap controller\n * @param getLayer1GasFee - The function to use to get the layer 1 gas fee\n * @param selectedAccount - The selected account for which the quotes were requested\n * @returns Array of quotes with fees appended, or undefined if quotes are for EVM chains\n */\nexport const appendFeesToQuotes = async (\n quotes: QuoteResponse[],\n messenger: BridgeControllerMessenger,\n getLayer1GasFee: typeof TransactionController.prototype.getLayer1GasFee,\n selectedAccount: InternalAccount,\n): Promise<(QuoteResponse & L1GasFees & NonEvmFees)[]> => {\n const quotesWithL1GasFees = await appendL1GasFees(quotes, getLayer1GasFee);\n const quotesWithNonEvmFees = await appendNonEvmFees(\n quotes,\n messenger,\n selectedAccount,\n );\n\n return quotesWithL1GasFees ?? quotesWithNonEvmFees ?? quotes;\n};\n"]}
@@ -0,0 +1,18 @@
1
+ import type { InternalAccount } from "@metamask/keyring-internal-api";
2
+ import type { TransactionController } from "@metamask/transaction-controller";
3
+ import type { QuoteResponse, L1GasFees, NonEvmFees, BridgeControllerMessenger } from "../types.cjs";
4
+ /**
5
+ * Appends transaction fees to quotes
6
+ *
7
+ * @param quotes - Array of quote responses to append fees to
8
+ * @param messenger - The bridge controller to use to call the snap controller
9
+ * @param getLayer1GasFee - The function to use to get the layer 1 gas fee
10
+ * @param selectedAccount - The selected account for which the quotes were requested
11
+ * @returns Array of quotes with fees appended, or undefined if quotes are for EVM chains
12
+ */
13
+ export declare const appendFeesToQuotes: (quotes: QuoteResponse[], messenger: BridgeControllerMessenger, getLayer1GasFee: ({ transactionParams, chainId, networkClientId, }: {
14
+ transactionParams: import("@metamask/transaction-controller").TransactionParams;
15
+ chainId?: `0x${string}` | undefined;
16
+ networkClientId?: string | undefined;
17
+ }) => Promise<`0x${string}` | undefined>, selectedAccount: InternalAccount) => Promise<(QuoteResponse & L1GasFees & NonEvmFees)[]>;
18
+ //# sourceMappingURL=quote-fees.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quote-fees.d.cts","sourceRoot":"","sources":["../../src/utils/quote-fees.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAE,qBAAqB,EAAE,yCAAyC;AAO9E,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,UAAU,EAEV,yBAAyB,EAC1B,qBAAiB;AAiJlB;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,WACrB,aAAa,EAAE;;;;2DAGN,eAAe,KAC/B,QAAQ,CAAC,aAAa,GAAG,SAAS,GAAG,UAAU,CAAC,EAAE,CASpD,CAAC"}