@metamask-previews/transaction-pay-controller 31.2.1-preview-906029d47 → 31.2.1-preview-e80304db4

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 (100) hide show
  1. package/CHANGELOG.md +3 -20
  2. package/dist/TransactionPayController.d.ts.map +1 -1
  3. package/dist/TransactionPayController.js +5 -4
  4. package/dist/TransactionPayController.js.map +1 -1
  5. package/dist/actions/update-payment-token.d.ts.map +1 -1
  6. package/dist/actions/update-payment-token.js +22 -51
  7. package/dist/actions/update-payment-token.js.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/strategy/across/AcrossStrategy.d.ts.map +1 -1
  12. package/dist/strategy/across/AcrossStrategy.js +1 -6
  13. package/dist/strategy/across/AcrossStrategy.js.map +1 -1
  14. package/dist/strategy/across/across-quotes.js +4 -7
  15. package/dist/strategy/across/across-quotes.js.map +1 -1
  16. package/dist/strategy/across/across-submit.d.ts.map +1 -1
  17. package/dist/strategy/across/across-submit.js +1 -3
  18. package/dist/strategy/across/across-submit.js.map +1 -1
  19. package/dist/strategy/fiat/FiatStrategy.d.ts +0 -1
  20. package/dist/strategy/fiat/FiatStrategy.d.ts.map +1 -1
  21. package/dist/strategy/fiat/FiatStrategy.js +0 -4
  22. package/dist/strategy/fiat/FiatStrategy.js.map +1 -1
  23. package/dist/strategy/fiat/fiat-direct-musd.d.ts.map +1 -1
  24. package/dist/strategy/fiat/fiat-direct-musd.js +2 -4
  25. package/dist/strategy/fiat/fiat-direct-musd.js.map +1 -1
  26. package/dist/strategy/fiat/fiat-quotes.d.ts.map +1 -1
  27. package/dist/strategy/fiat/fiat-quotes.js +2 -4
  28. package/dist/strategy/fiat/fiat-quotes.js.map +1 -1
  29. package/dist/strategy/fiat/fiat-submit.d.ts.map +1 -1
  30. package/dist/strategy/fiat/fiat-submit.js +2 -5
  31. package/dist/strategy/fiat/fiat-submit.js.map +1 -1
  32. package/dist/strategy/fiat/fiat-test-funding.d.ts.map +1 -1
  33. package/dist/strategy/fiat/fiat-test-funding.js +5 -3
  34. package/dist/strategy/fiat/fiat-test-funding.js.map +1 -1
  35. package/dist/strategy/relay/RelayStrategy.d.ts +0 -4
  36. package/dist/strategy/relay/RelayStrategy.d.ts.map +1 -1
  37. package/dist/strategy/relay/RelayStrategy.js +1 -6
  38. package/dist/strategy/relay/RelayStrategy.js.map +1 -1
  39. package/dist/strategy/relay/relay-quotes.d.ts.map +1 -1
  40. package/dist/strategy/relay/relay-quotes.js +6 -10
  41. package/dist/strategy/relay/relay-quotes.js.map +1 -1
  42. package/dist/strategy/relay/relay-submit-execute.d.ts.map +1 -1
  43. package/dist/strategy/relay/relay-submit-execute.js +1 -3
  44. package/dist/strategy/relay/relay-submit-execute.js.map +1 -1
  45. package/dist/strategy/relay/relay-submit.d.ts.map +1 -1
  46. package/dist/strategy/relay/relay-submit.js +6 -8
  47. package/dist/strategy/relay/relay-submit.js.map +1 -1
  48. package/dist/strategy/relay/relay-validation.d.ts.map +1 -1
  49. package/dist/strategy/relay/relay-validation.js +2 -5
  50. package/dist/strategy/relay/relay-validation.js.map +1 -1
  51. package/dist/strategy/server/ServerStrategy.d.ts.map +1 -1
  52. package/dist/strategy/server/ServerStrategy.js +0 -4
  53. package/dist/strategy/server/ServerStrategy.js.map +1 -1
  54. package/dist/strategy/server/server-quotes.js +4 -7
  55. package/dist/strategy/server/server-quotes.js.map +1 -1
  56. package/dist/strategy/server/server-submit.d.ts.map +1 -1
  57. package/dist/strategy/server/server-submit.js +6 -9
  58. package/dist/strategy/server/server-submit.js.map +1 -1
  59. package/dist/strategy/server/server-validation.d.ts.map +1 -1
  60. package/dist/strategy/server/server-validation.js +1 -3
  61. package/dist/strategy/server/server-validation.js.map +1 -1
  62. package/dist/types.d.ts +25 -60
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/dist/utils/account.d.ts +0 -54
  66. package/dist/utils/account.d.ts.map +1 -1
  67. package/dist/utils/account.js +0 -124
  68. package/dist/utils/account.js.map +1 -1
  69. package/dist/utils/gas-payment.d.ts +3 -12
  70. package/dist/utils/gas-payment.d.ts.map +1 -1
  71. package/dist/utils/gas-payment.js +2 -2
  72. package/dist/utils/gas-payment.js.map +1 -1
  73. package/dist/utils/max-gas-station.d.ts.map +1 -1
  74. package/dist/utils/max-gas-station.js +5 -9
  75. package/dist/utils/max-gas-station.js.map +1 -1
  76. package/dist/utils/max-quote.js +2 -3
  77. package/dist/utils/max-quote.js.map +1 -1
  78. package/dist/utils/no-op-quote.d.ts +3 -3
  79. package/dist/utils/no-op-quote.d.ts.map +1 -1
  80. package/dist/utils/no-op-quote.js +3 -4
  81. package/dist/utils/no-op-quote.js.map +1 -1
  82. package/dist/utils/perps.d.ts.map +1 -1
  83. package/dist/utils/perps.js +2 -5
  84. package/dist/utils/perps.js.map +1 -1
  85. package/dist/utils/post-quote.js +3 -4
  86. package/dist/utils/post-quote.js.map +1 -1
  87. package/dist/utils/quotes.d.ts.map +1 -1
  88. package/dist/utils/quotes.js +25 -99
  89. package/dist/utils/quotes.js.map +1 -1
  90. package/dist/utils/source-amounts.d.ts.map +1 -1
  91. package/dist/utils/source-amounts.js +4 -25
  92. package/dist/utils/source-amounts.js.map +1 -1
  93. package/dist/utils/token.d.ts +1 -84
  94. package/dist/utils/token.d.ts.map +1 -1
  95. package/dist/utils/token.js +13 -144
  96. package/dist/utils/token.js.map +1 -1
  97. package/dist/utils/validation.d.ts.map +1 -1
  98. package/dist/utils/validation.js +11 -14
  99. package/dist/utils/validation.js.map +1 -1
  100. package/package.json +3 -4
@@ -1 +1 @@
1
- {"version":3,"file":"max-quote.js","sourceRoot":"","sources":["../../src/utils/max-quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { BigNumber } from 'bignumber.js';\n\nimport type {\n PayStrategyGetQuotesRequest,\n QuoteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../types.js';\nimport { accountRequiresMfa } from './account.js';\nimport { prefixError } from './error-prefix.js';\nimport { isAtomicMaxEnabled } from './feature-flags.js';\nimport { getMaxGasStationQuote } from './max-gas-station.js';\nimport type { MaxGasStationQuoteDetails } from './max-gas-station.js';\nimport { getEvmQuoteSource, getTokenInfo } from './token.js';\nimport { QuoteError } from './validation.js';\n\nconst ATOMIC_PROMOTION_FAILURE_PREFIX = 'Atomic promotion failed: ';\n\n/**\n * Provider-agnostic view of a quote, supplied by each strategy so the max\n * flow never reads a strategy's original quote shape directly.\n */\nexport type MaxQuoteDetails = MaxGasStationQuoteDetails & {\n /** Whether the route is subsidized, so it exchanges 1:1 without fees. */\n isSubsidized: boolean;\n\n /** Raw amount of the target token the quote delivers. */\n targetAmountRaw: string;\n};\n\n/**\n * Callbacks bridging the max flow to a specific strategy.\n */\nexport type MaxQuotesRequest<OriginalQuote> = {\n /** Full quote context, including the original transaction. */\n fullRequest: PayStrategyGetQuotesRequest;\n\n /** Map a strategy quote to the details the flow needs. */\n getQuoteDetails: (\n quote: TransactionPayQuote<OriginalQuote>,\n ) => MaxQuoteDetails;\n\n /**\n * Fetch quotes from every available provider.\n *\n * The transaction is the original, or a copy whose calls are re-encoded for\n * an atomic max amount. Embedded calls must be built from it.\n */\n getQuotes: (\n request: QuoteRequest,\n transaction: TransactionMeta,\n ) => Promise<TransactionPayQuote<OriginalQuote>[]>;\n\n /** Fetch a quote from the same provider as an existing quote. */\n requote: (\n quote: TransactionPayQuote<OriginalQuote>,\n request: QuoteRequest,\n transaction: TransactionMeta,\n ) => Promise<TransactionPayQuote<OriginalQuote>>;\n\n /** Max-amount quote request. */\n request: QuoteRequest;\n\n /**\n * Validate an atomic quote before it is returned.\n *\n * A failure is treated as a failed atomic promotion, so the next strategy is\n * tried.\n */\n validateAtomicQuote?: (\n quote: TransactionPayQuote<OriginalQuote>,\n ) => Promise<void>;\n};\n\n/**\n * Fetch quotes that spend the whole source balance.\n *\n * Every quote is re-quoted, when needed, so origin gas can be paid from the\n * source token. See {@link getMaxGasStationQuote}.\n *\n * When atomic max is enabled, subsidized routes are instead quoted as an\n * atomic exact-output quote that embeds the transaction's calls re-encoded for\n * the max amount. The request's `atomic` value is a subsidy hint:\n *\n * - Not `false`: quote atomically first, falling back to a non-atomic quote\n * when the route is not subsidized.\n * - `false`: quote non-atomically first, promoting to an atomic quote when the\n * route turns out to be subsidized. A failed promotion is terminal.\n *\n * @param options - Max quotes request.\n * @returns One quote per provider.\n */\nexport async function getMaxQuotes<OriginalQuote>(\n options: MaxQuotesRequest<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote>[]> {\n const { fullRequest, getQuotes, request } = options;\n const { messenger, transaction } = fullRequest;\n\n if (!isAtomicMaxSupported({ messenger, request, transaction })) {\n // Only the atomic flows below may produce an atomic max quote.\n const nonAtomicRequest =\n request.atomic === true ? { ...request, atomic: undefined } : request;\n\n const quotes = await getQuotes(nonAtomicRequest, transaction);\n return Promise.all(quotes.map((quote) => withGasStation(quote, options)));\n }\n\n if (request.atomic === false) {\n return getPromotedQuotes(options);\n }\n\n return getAtomicFirstQuotes(options);\n}\n\n/**\n * Whether a max request may be quoted atomically, embedding the transaction's\n * calls in the quote.\n *\n * Strategies use this to decide whether to embed calls for a max request, so\n * it must agree with the routing in {@link getMaxQuotes}.\n *\n * @param options - Options bag.\n * @param options.messenger - Controller messenger.\n * @param options.request - Quote request.\n * @param options.transaction - Transaction being quoted.\n * @returns Whether atomic max quoting is supported.\n */\nexport function isAtomicMaxSupported({\n messenger,\n request,\n transaction,\n}: {\n messenger: TransactionPayControllerMessenger;\n request: Pick<QuoteRequest, 'isPostQuote'>;\n transaction?: TransactionMeta;\n}): boolean {\n return (\n request.isPostQuote !== true && isAtomicMaxEnabled(messenger, transaction)\n );\n}\n\n/**\n * Whether a quote is an atomic max quote from {@link getMaxQuotes}, so it was\n * already validated by `validateAtomicQuote`.\n *\n * @param quote - Quote to check.\n * @returns Whether the quote is an atomic max quote.\n */\nexport function isAtomicMaxQuote(\n quote: Pick<TransactionPayQuote<unknown>, 'request'>,\n): boolean {\n return quote.request.isMaxAmount === true && quote.request.atomic === true;\n}\n\nasync function getAtomicFirstQuotes<OriginalQuote>(\n options: MaxQuotesRequest<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote>[]> {\n const {\n fullRequest,\n getQuoteDetails,\n getQuotes,\n request,\n requote,\n validateAtomicQuote,\n } = options;\n\n const atomic = await buildAtomicMaxRequest({\n amount: getFixedRateTargetAmount(fullRequest.messenger, request),\n fullRequest,\n request,\n });\n\n const quotes = await getQuotes(atomic.request, atomic.transaction);\n\n return Promise.all(\n quotes.map(async (quote) => {\n if (getQuoteDetails(quote).isSubsidized) {\n return withAtomicPromotionFailure(async () => {\n await validateAtomicQuote?.(quote);\n return quote;\n });\n }\n\n const nonAtomicQuote = await requote(\n quote,\n { ...request, atomic: false },\n fullRequest.transaction,\n );\n\n return withGasStation(nonAtomicQuote, options);\n }),\n );\n}\n\nasync function getPromotedQuotes<OriginalQuote>(\n options: MaxQuotesRequest<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote>[]> {\n const {\n fullRequest,\n getQuoteDetails,\n getQuotes,\n request,\n requote,\n validateAtomicQuote,\n } = options;\n\n const quotes = await getQuotes(request, fullRequest.transaction);\n\n return Promise.all(\n quotes.map(async (discoveryQuote) => {\n const quote = await withGasStation(discoveryQuote, options);\n const details = getQuoteDetails(quote);\n\n if (!details.isSubsidized) {\n return quote;\n }\n\n // A request whose signing keyring requires MFA arrives here already\n // cleared to `atomic: false` by `updateQuotes`. Promotion ignores that\n // hint and sets `atomic: true` regardless, which would embed the target\n // calls again and reintroduce the per-quote delegation signature.\n if (\n accountRequiresMfa(\n fullRequest.messenger,\n fullRequest.transaction.txParams.from,\n )\n ) {\n return quote;\n }\n\n return withAtomicPromotionFailure(async () => {\n const atomic = await buildAtomicMaxRequest({\n amount: details.targetAmountRaw,\n fullRequest,\n request,\n });\n\n const promotedQuote = await requote(\n quote,\n atomic.request,\n atomic.transaction,\n );\n\n if (!getQuoteDetails(promotedQuote).isSubsidized) {\n throw new Error('Promoted quote lost subsidy');\n }\n\n await validateAtomicQuote?.(promotedQuote);\n\n return promotedQuote;\n });\n }),\n );\n}\n\n/**\n * Run an atomic quote step, converting any failure into the terminal atomic\n * promotion error so the next strategy is tried.\n *\n * @param fn - Atomic quote step.\n * @returns The step result.\n */\nasync function withAtomicPromotionFailure<Result>(\n fn: () => Promise<Result>,\n): Promise<Result> {\n try {\n return await fn();\n } catch (error) {\n const prefixed = prefixError(error, ATOMIC_PROMOTION_FAILURE_PREFIX);\n\n throw new QuoteError({\n detail: [prefixed.message],\n message: prefixed.message,\n reason: 'no-quotes',\n });\n }\n}\n\nfunction withGasStation<OriginalQuote>(\n quote: TransactionPayQuote<OriginalQuote>,\n { fullRequest, getQuoteDetails, requote }: MaxQuotesRequest<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote>> {\n return getMaxGasStationQuote({\n getQuoteDetails,\n messenger: fullRequest.messenger,\n quote,\n requote: (adjustedRequest) =>\n requote(quote, adjustedRequest, fullRequest.transaction),\n });\n}\n\n/**\n * Target amount for a subsidized route spending the whole source budget.\n *\n * Fixed-spread subsidized routes exchange stablecoins 1:1 without fees, so the\n * source budget converts directly, rather than the required amount from\n * before Max was selected.\n *\n * @param messenger - Controller messenger.\n * @param request - Max quote request.\n * @returns Raw target amount.\n */\nfunction getFixedRateTargetAmount(\n messenger: TransactionPayControllerMessenger,\n request: QuoteRequest,\n): string {\n const { chainId: sourceChainId, tokenAddress: sourceTokenAddress } =\n getEvmQuoteSource(request);\n\n const sourceToken = getTokenInfo(\n messenger,\n sourceTokenAddress,\n sourceChainId,\n );\n\n const targetToken = getTokenInfo(\n messenger,\n request.targetTokenAddress,\n request.targetChainId,\n );\n\n if (!sourceToken || !targetToken) {\n throw new Error('Token decimals not found for atomic max quote');\n }\n\n return new BigNumber(request.sourceTokenAmount)\n .shiftedBy(targetToken.decimals - sourceToken.decimals)\n .toFixed(0, BigNumber.ROUND_DOWN);\n}\n\n/**\n * Build an atomic exact-output request, with a copy of the transaction whose\n * calls and required asset are re-encoded for the target amount.\n *\n * @param options - Options bag.\n * @param options.amount - Raw target amount.\n * @param options.fullRequest - Full quote context.\n * @param options.request - Max quote request.\n * @returns The atomic request and the transaction to embed.\n */\nasync function buildAtomicMaxRequest({\n amount,\n fullRequest,\n request,\n}: {\n amount: string;\n fullRequest: PayStrategyGetQuotesRequest;\n request: QuoteRequest;\n}): Promise<{ request: QuoteRequest; transaction: TransactionMeta }> {\n const targetAmount = validatePositiveIntegerString(amount);\n\n const transaction = await applyAmountDataUpdates({\n amount: targetAmount,\n messenger: fullRequest.messenger,\n transaction: fullRequest.transaction,\n });\n\n return {\n request: { ...request, atomic: true, targetAmountMinimum: targetAmount },\n transaction,\n };\n}\n\nfunction validatePositiveIntegerString(value: string): string {\n const amount = new BigNumber(value);\n\n if (!amount.isFinite() || !amount.isInteger() || !amount.isGreaterThan(0)) {\n throw new Error(`Invalid target amount: ${value}`);\n }\n\n return amount.toFixed(0);\n}\n\n/**\n * Copy a transaction with its calls and first required asset re-encoded for\n * an amount, leaving the original untouched.\n *\n * @param options - Options bag.\n * @param options.amount - Raw amount to encode.\n * @param options.messenger - Controller messenger.\n * @param options.transaction - Original transaction.\n * @returns The updated copy.\n */\nasync function applyAmountDataUpdates({\n amount,\n messenger,\n transaction,\n}: {\n amount: string;\n messenger: TransactionPayControllerMessenger;\n transaction: TransactionMeta;\n}): Promise<TransactionMeta> {\n // The callback gets its own copy, so the updates below never leak into it.\n const { updates } = await messenger.call(\n 'TransactionPayController:getAmountData',\n { amount, transaction: cloneTransaction(transaction) },\n );\n\n const updatedTransaction = cloneTransaction(transaction);\n const { nestedTransactions, requiredAssets } = updatedTransaction;\n\n if (!updates.length) {\n throw new Error('getAmountData returned no updates for atomic promotion');\n }\n\n if (!nestedTransactions?.length) {\n throw new Error('Missing nested transactions for atomic promotion');\n }\n\n for (const { nestedTransactionIndex, data } of updates) {\n if (!nestedTransactions[nestedTransactionIndex]) {\n throw new Error(\n 'getAmountData returned an unusable nested transaction update',\n );\n }\n\n nestedTransactions[nestedTransactionIndex].data = data;\n }\n\n if (!requiredAssets?.[0]) {\n throw new Error('Missing required assets for atomic promotion');\n }\n\n requiredAssets[0].amount = toHex(BigInt(amount));\n\n return updatedTransaction;\n}\n\nfunction cloneTransaction(transaction: TransactionMeta): TransactionMeta {\n return {\n ...transaction,\n nestedTransactions: transaction.nestedTransactions?.map(\n (nestedTransaction) => ({ ...nestedTransaction }),\n ),\n requiredAssets: transaction.requiredAssets?.map((requiredAsset) => ({\n ...requiredAsset,\n })),\n };\n}\n"]}
1
+ {"version":3,"file":"max-quote.js","sourceRoot":"","sources":["../../src/utils/max-quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { BigNumber } from 'bignumber.js';\n\nimport type {\n PayStrategyGetQuotesRequest,\n QuoteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../types.js';\nimport { accountRequiresMfa } from './account.js';\nimport { prefixError } from './error-prefix.js';\nimport { isAtomicMaxEnabled } from './feature-flags.js';\nimport { getMaxGasStationQuote } from './max-gas-station.js';\nimport type { MaxGasStationQuoteDetails } from './max-gas-station.js';\nimport { getTokenInfo } from './token.js';\nimport { QuoteError } from './validation.js';\n\nconst ATOMIC_PROMOTION_FAILURE_PREFIX = 'Atomic promotion failed: ';\n\n/**\n * Provider-agnostic view of a quote, supplied by each strategy so the max\n * flow never reads a strategy's original quote shape directly.\n */\nexport type MaxQuoteDetails = MaxGasStationQuoteDetails & {\n /** Whether the route is subsidized, so it exchanges 1:1 without fees. */\n isSubsidized: boolean;\n\n /** Raw amount of the target token the quote delivers. */\n targetAmountRaw: string;\n};\n\n/**\n * Callbacks bridging the max flow to a specific strategy.\n */\nexport type MaxQuotesRequest<OriginalQuote> = {\n /** Full quote context, including the original transaction. */\n fullRequest: PayStrategyGetQuotesRequest;\n\n /** Map a strategy quote to the details the flow needs. */\n getQuoteDetails: (\n quote: TransactionPayQuote<OriginalQuote>,\n ) => MaxQuoteDetails;\n\n /**\n * Fetch quotes from every available provider.\n *\n * The transaction is the original, or a copy whose calls are re-encoded for\n * an atomic max amount. Embedded calls must be built from it.\n */\n getQuotes: (\n request: QuoteRequest,\n transaction: TransactionMeta,\n ) => Promise<TransactionPayQuote<OriginalQuote>[]>;\n\n /** Fetch a quote from the same provider as an existing quote. */\n requote: (\n quote: TransactionPayQuote<OriginalQuote>,\n request: QuoteRequest,\n transaction: TransactionMeta,\n ) => Promise<TransactionPayQuote<OriginalQuote>>;\n\n /** Max-amount quote request. */\n request: QuoteRequest;\n\n /**\n * Validate an atomic quote before it is returned.\n *\n * A failure is treated as a failed atomic promotion, so the next strategy is\n * tried.\n */\n validateAtomicQuote?: (\n quote: TransactionPayQuote<OriginalQuote>,\n ) => Promise<void>;\n};\n\n/**\n * Fetch quotes that spend the whole source balance.\n *\n * Every quote is re-quoted, when needed, so origin gas can be paid from the\n * source token. See {@link getMaxGasStationQuote}.\n *\n * When atomic max is enabled, subsidized routes are instead quoted as an\n * atomic exact-output quote that embeds the transaction's calls re-encoded for\n * the max amount. The request's `atomic` value is a subsidy hint:\n *\n * - Not `false`: quote atomically first, falling back to a non-atomic quote\n * when the route is not subsidized.\n * - `false`: quote non-atomically first, promoting to an atomic quote when the\n * route turns out to be subsidized. A failed promotion is terminal.\n *\n * @param options - Max quotes request.\n * @returns One quote per provider.\n */\nexport async function getMaxQuotes<OriginalQuote>(\n options: MaxQuotesRequest<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote>[]> {\n const { fullRequest, getQuotes, request } = options;\n const { messenger, transaction } = fullRequest;\n\n if (!isAtomicMaxSupported({ messenger, request, transaction })) {\n // Only the atomic flows below may produce an atomic max quote.\n const nonAtomicRequest =\n request.atomic === true ? { ...request, atomic: undefined } : request;\n\n const quotes = await getQuotes(nonAtomicRequest, transaction);\n return Promise.all(quotes.map((quote) => withGasStation(quote, options)));\n }\n\n if (request.atomic === false) {\n return getPromotedQuotes(options);\n }\n\n return getAtomicFirstQuotes(options);\n}\n\n/**\n * Whether a max request may be quoted atomically, embedding the transaction's\n * calls in the quote.\n *\n * Strategies use this to decide whether to embed calls for a max request, so\n * it must agree with the routing in {@link getMaxQuotes}.\n *\n * @param options - Options bag.\n * @param options.messenger - Controller messenger.\n * @param options.request - Quote request.\n * @param options.transaction - Transaction being quoted.\n * @returns Whether atomic max quoting is supported.\n */\nexport function isAtomicMaxSupported({\n messenger,\n request,\n transaction,\n}: {\n messenger: TransactionPayControllerMessenger;\n request: Pick<QuoteRequest, 'isPostQuote'>;\n transaction?: TransactionMeta;\n}): boolean {\n return (\n request.isPostQuote !== true && isAtomicMaxEnabled(messenger, transaction)\n );\n}\n\n/**\n * Whether a quote is an atomic max quote from {@link getMaxQuotes}, so it was\n * already validated by `validateAtomicQuote`.\n *\n * @param quote - Quote to check.\n * @returns Whether the quote is an atomic max quote.\n */\nexport function isAtomicMaxQuote(\n quote: Pick<TransactionPayQuote<unknown>, 'request'>,\n): boolean {\n return quote.request.isMaxAmount === true && quote.request.atomic === true;\n}\n\nasync function getAtomicFirstQuotes<OriginalQuote>(\n options: MaxQuotesRequest<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote>[]> {\n const {\n fullRequest,\n getQuoteDetails,\n getQuotes,\n request,\n requote,\n validateAtomicQuote,\n } = options;\n\n const atomic = await buildAtomicMaxRequest({\n amount: getFixedRateTargetAmount(fullRequest.messenger, request),\n fullRequest,\n request,\n });\n\n const quotes = await getQuotes(atomic.request, atomic.transaction);\n\n return Promise.all(\n quotes.map(async (quote) => {\n if (getQuoteDetails(quote).isSubsidized) {\n return withAtomicPromotionFailure(async () => {\n await validateAtomicQuote?.(quote);\n return quote;\n });\n }\n\n const nonAtomicQuote = await requote(\n quote,\n { ...request, atomic: false },\n fullRequest.transaction,\n );\n\n return withGasStation(nonAtomicQuote, options);\n }),\n );\n}\n\nasync function getPromotedQuotes<OriginalQuote>(\n options: MaxQuotesRequest<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote>[]> {\n const {\n fullRequest,\n getQuoteDetails,\n getQuotes,\n request,\n requote,\n validateAtomicQuote,\n } = options;\n\n const quotes = await getQuotes(request, fullRequest.transaction);\n\n return Promise.all(\n quotes.map(async (discoveryQuote) => {\n const quote = await withGasStation(discoveryQuote, options);\n const details = getQuoteDetails(quote);\n\n if (!details.isSubsidized) {\n return quote;\n }\n\n // A request whose signing keyring requires MFA arrives here already\n // cleared to `atomic: false` by `updateQuotes`. Promotion ignores that\n // hint and sets `atomic: true` regardless, which would embed the target\n // calls again and reintroduce the per-quote delegation signature.\n if (\n accountRequiresMfa(\n fullRequest.messenger,\n fullRequest.transaction.txParams.from,\n )\n ) {\n return quote;\n }\n\n return withAtomicPromotionFailure(async () => {\n const atomic = await buildAtomicMaxRequest({\n amount: details.targetAmountRaw,\n fullRequest,\n request,\n });\n\n const promotedQuote = await requote(\n quote,\n atomic.request,\n atomic.transaction,\n );\n\n if (!getQuoteDetails(promotedQuote).isSubsidized) {\n throw new Error('Promoted quote lost subsidy');\n }\n\n await validateAtomicQuote?.(promotedQuote);\n\n return promotedQuote;\n });\n }),\n );\n}\n\n/**\n * Run an atomic quote step, converting any failure into the terminal atomic\n * promotion error so the next strategy is tried.\n *\n * @param fn - Atomic quote step.\n * @returns The step result.\n */\nasync function withAtomicPromotionFailure<Result>(\n fn: () => Promise<Result>,\n): Promise<Result> {\n try {\n return await fn();\n } catch (error) {\n const prefixed = prefixError(error, ATOMIC_PROMOTION_FAILURE_PREFIX);\n\n throw new QuoteError({\n detail: [prefixed.message],\n message: prefixed.message,\n reason: 'no-quotes',\n });\n }\n}\n\nfunction withGasStation<OriginalQuote>(\n quote: TransactionPayQuote<OriginalQuote>,\n { fullRequest, getQuoteDetails, requote }: MaxQuotesRequest<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote>> {\n return getMaxGasStationQuote({\n getQuoteDetails,\n messenger: fullRequest.messenger,\n quote,\n requote: (adjustedRequest) =>\n requote(quote, adjustedRequest, fullRequest.transaction),\n });\n}\n\n/**\n * Target amount for a subsidized route spending the whole source budget.\n *\n * Fixed-spread subsidized routes exchange stablecoins 1:1 without fees, so the\n * source budget converts directly, rather than the required amount from\n * before Max was selected.\n *\n * @param messenger - Controller messenger.\n * @param request - Max quote request.\n * @returns Raw target amount.\n */\nfunction getFixedRateTargetAmount(\n messenger: TransactionPayControllerMessenger,\n request: QuoteRequest,\n): string {\n const sourceToken = getTokenInfo(\n messenger,\n request.sourceTokenAddress,\n request.sourceChainId,\n );\n\n const targetToken = getTokenInfo(\n messenger,\n request.targetTokenAddress,\n request.targetChainId,\n );\n\n if (!sourceToken || !targetToken) {\n throw new Error('Token decimals not found for atomic max quote');\n }\n\n return new BigNumber(request.sourceTokenAmount)\n .shiftedBy(targetToken.decimals - sourceToken.decimals)\n .toFixed(0, BigNumber.ROUND_DOWN);\n}\n\n/**\n * Build an atomic exact-output request, with a copy of the transaction whose\n * calls and required asset are re-encoded for the target amount.\n *\n * @param options - Options bag.\n * @param options.amount - Raw target amount.\n * @param options.fullRequest - Full quote context.\n * @param options.request - Max quote request.\n * @returns The atomic request and the transaction to embed.\n */\nasync function buildAtomicMaxRequest({\n amount,\n fullRequest,\n request,\n}: {\n amount: string;\n fullRequest: PayStrategyGetQuotesRequest;\n request: QuoteRequest;\n}): Promise<{ request: QuoteRequest; transaction: TransactionMeta }> {\n const targetAmount = validatePositiveIntegerString(amount);\n\n const transaction = await applyAmountDataUpdates({\n amount: targetAmount,\n messenger: fullRequest.messenger,\n transaction: fullRequest.transaction,\n });\n\n return {\n request: { ...request, atomic: true, targetAmountMinimum: targetAmount },\n transaction,\n };\n}\n\nfunction validatePositiveIntegerString(value: string): string {\n const amount = new BigNumber(value);\n\n if (!amount.isFinite() || !amount.isInteger() || !amount.isGreaterThan(0)) {\n throw new Error(`Invalid target amount: ${value}`);\n }\n\n return amount.toFixed(0);\n}\n\n/**\n * Copy a transaction with its calls and first required asset re-encoded for\n * an amount, leaving the original untouched.\n *\n * @param options - Options bag.\n * @param options.amount - Raw amount to encode.\n * @param options.messenger - Controller messenger.\n * @param options.transaction - Original transaction.\n * @returns The updated copy.\n */\nasync function applyAmountDataUpdates({\n amount,\n messenger,\n transaction,\n}: {\n amount: string;\n messenger: TransactionPayControllerMessenger;\n transaction: TransactionMeta;\n}): Promise<TransactionMeta> {\n // The callback gets its own copy, so the updates below never leak into it.\n const { updates } = await messenger.call(\n 'TransactionPayController:getAmountData',\n { amount, transaction: cloneTransaction(transaction) },\n );\n\n const updatedTransaction = cloneTransaction(transaction);\n const { nestedTransactions, requiredAssets } = updatedTransaction;\n\n if (!updates.length) {\n throw new Error('getAmountData returned no updates for atomic promotion');\n }\n\n if (!nestedTransactions?.length) {\n throw new Error('Missing nested transactions for atomic promotion');\n }\n\n for (const { nestedTransactionIndex, data } of updates) {\n if (!nestedTransactions[nestedTransactionIndex]) {\n throw new Error(\n 'getAmountData returned an unusable nested transaction update',\n );\n }\n\n nestedTransactions[nestedTransactionIndex].data = data;\n }\n\n if (!requiredAssets?.[0]) {\n throw new Error('Missing required assets for atomic promotion');\n }\n\n requiredAssets[0].amount = toHex(BigInt(amount));\n\n return updatedTransaction;\n}\n\nfunction cloneTransaction(transaction: TransactionMeta): TransactionMeta {\n return {\n ...transaction,\n nestedTransactions: transaction.nestedTransactions?.map(\n (nestedTransaction) => ({ ...nestedTransaction }),\n ),\n requiredAssets: transaction.requiredAssets?.map((requiredAsset) => ({\n ...requiredAsset,\n })),\n };\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { Hex, Json } from '@metamask/utils';
2
- import type { EvmPaymentToken, TransactionPayQuote } from '../types.js';
2
+ import type { TransactionPaymentToken, TransactionPayQuote } from '../types.js';
3
3
  /**
4
4
  * Build a no-op quote for a transaction that needs no conversion.
5
5
  *
@@ -11,8 +11,8 @@ import type { EvmPaymentToken, TransactionPayQuote } from '../types.js';
11
11
  * ignored by fee totals and quote refreshes.
12
12
  *
13
13
  * @param from - Resolved wallet address for the transaction.
14
- * @param paymentToken - Selected EVM payment token.
14
+ * @param paymentToken - Selected payment token.
15
15
  * @returns The no-op quote.
16
16
  */
17
- export declare function buildNoOpQuote(from: Hex, paymentToken: EvmPaymentToken): TransactionPayQuote<Json>;
17
+ export declare function buildNoOpQuote(from: Hex, paymentToken: TransactionPaymentToken): TransactionPayQuote<Json>;
18
18
  //# sourceMappingURL=no-op-quote.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-op-quote.d.ts","sourceRoot":"","sources":["../../src/utils/no-op-quote.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,KAAK,EAEV,eAAe,EAEf,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAWrB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,GAAG,EACT,YAAY,EAAE,eAAe,GAC5B,mBAAmB,CAAC,IAAI,CAAC,CA6B3B"}
1
+ {"version":3,"file":"no-op-quote.d.ts","sourceRoot":"","sources":["../../src/utils/no-op-quote.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,KAAK,EAGV,uBAAuB,EACvB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAUrB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,GAAG,EACT,YAAY,EAAE,uBAAuB,GACpC,mBAAmB,CAAC,IAAI,CAAC,CA6B3B"}
@@ -1,5 +1,4 @@
1
1
  import { TransactionPayStrategy } from '../constants.js';
2
- import { getCaipAccountId } from './account.js';
3
2
  function zeroFiat() {
4
3
  return { fiat: '0', usd: '0' };
5
4
  }
@@ -17,7 +16,7 @@ function zeroAmount() {
17
16
  * ignored by fee totals and quote refreshes.
18
17
  *
19
18
  * @param from - Resolved wallet address for the transaction.
20
- * @param paymentToken - Selected EVM payment token.
19
+ * @param paymentToken - Selected payment token.
21
20
  * @returns The no-op quote.
22
21
  */
23
22
  export function buildNoOpQuote(from, paymentToken) {
@@ -37,9 +36,9 @@ export function buildNoOpQuote(from, paymentToken) {
37
36
  original: null,
38
37
  request: {
39
38
  from,
40
- sourceAccountId: getCaipAccountId(paymentToken.assetId, from),
41
- sourceAssetId: paymentToken.assetId,
42
39
  sourceBalanceRaw: paymentToken.balanceRaw,
40
+ sourceChainId: paymentToken.chainId,
41
+ sourceTokenAddress: paymentToken.address,
43
42
  sourceTokenAmount: '0',
44
43
  targetAmountMinimum: '0',
45
44
  targetChainId: paymentToken.chainId,
@@ -1 +1 @@
1
- {"version":3,"file":"no-op-quote.js","sourceRoot":"","sources":["../../src/utils/no-op-quote.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAOzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,SAAS,QAAQ;IACf,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,EAAE,GAAG,QAAQ,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAS,EACT,YAA6B;IAE7B,OAAO;QACL,IAAI,EAAE,QAAQ,EAAE;QAChB,iBAAiB,EAAE,CAAC;QACpB,IAAI,EAAE;YACJ,QAAQ,EAAE,QAAQ,EAAE;YACpB,QAAQ,EAAE,QAAQ,EAAE;YACpB,aAAa,EAAE;gBACb,QAAQ,EAAE,UAAU,EAAE;gBACtB,GAAG,EAAE,UAAU,EAAE;aAClB;YACD,aAAa,EAAE,QAAQ,EAAE;SAC1B;QACD,YAAY,EAAE,KAAK;QACnB,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE;YACP,IAAI;YACJ,eAAe,EAAE,gBAAgB,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC;YAC7D,aAAa,EAAE,YAAY,CAAC,OAAO;YACnC,gBAAgB,EAAE,YAAY,CAAC,UAAU;YACzC,iBAAiB,EAAE,GAAG;YACtB,mBAAmB,EAAE,GAAG;YACxB,aAAa,EAAE,YAAY,CAAC,OAAO;YACnC,kBAAkB,EAAE,YAAY,CAAC,OAAO;SACzC;QACD,YAAY,EAAE,UAAU,EAAE;QAC1B,QAAQ,EAAE,sBAAsB,CAAC,IAAI;QACrC,YAAY,EAAE,QAAQ,EAAE;KACzB,CAAC;AACJ,CAAC","sourcesContent":["import type { Hex, Json } from '@metamask/utils';\n\nimport { TransactionPayStrategy } from '../constants.js';\nimport type {\n Amount,\n EvmPaymentToken,\n FiatValue,\n TransactionPayQuote,\n} from '../types.js';\nimport { getCaipAccountId } from './account.js';\n\nfunction zeroFiat(): FiatValue {\n return { fiat: '0', usd: '0' };\n}\n\nfunction zeroAmount(): Amount {\n return { ...zeroFiat(), human: '0', raw: '0' };\n}\n\n/**\n * Build a no-op quote for a transaction that needs no conversion.\n *\n * Stored instead of an empty quotes array when a payment token is selected\n * but the route is direct (e.g. same token and chain, or sufficient balance).\n * This gives clients and the publish hook an explicit marker to distinguish\n * \"no quote needed\" from \"quote needed but missing\". No-op quotes use the\n * `TransactionPayStrategy.None` strategy, cannot be executed, and must be\n * ignored by fee totals and quote refreshes.\n *\n * @param from - Resolved wallet address for the transaction.\n * @param paymentToken - Selected EVM payment token.\n * @returns The no-op quote.\n */\nexport function buildNoOpQuote(\n from: Hex,\n paymentToken: EvmPaymentToken,\n): TransactionPayQuote<Json> {\n return {\n dust: zeroFiat(),\n estimatedDuration: 0,\n fees: {\n metaMask: zeroFiat(),\n provider: zeroFiat(),\n sourceNetwork: {\n estimate: zeroAmount(),\n max: zeroAmount(),\n },\n targetNetwork: zeroFiat(),\n },\n isInputBased: false,\n original: null,\n request: {\n from,\n sourceAccountId: getCaipAccountId(paymentToken.assetId, from),\n sourceAssetId: paymentToken.assetId,\n sourceBalanceRaw: paymentToken.balanceRaw,\n sourceTokenAmount: '0',\n targetAmountMinimum: '0',\n targetChainId: paymentToken.chainId,\n targetTokenAddress: paymentToken.address,\n },\n sourceAmount: zeroAmount(),\n strategy: TransactionPayStrategy.None,\n targetAmount: zeroFiat(),\n };\n}\n"]}
1
+ {"version":3,"file":"no-op-quote.js","sourceRoot":"","sources":["../../src/utils/no-op-quote.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAQzD,SAAS,QAAQ;IACf,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,EAAE,GAAG,QAAQ,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAS,EACT,YAAqC;IAErC,OAAO;QACL,IAAI,EAAE,QAAQ,EAAE;QAChB,iBAAiB,EAAE,CAAC;QACpB,IAAI,EAAE;YACJ,QAAQ,EAAE,QAAQ,EAAE;YACpB,QAAQ,EAAE,QAAQ,EAAE;YACpB,aAAa,EAAE;gBACb,QAAQ,EAAE,UAAU,EAAE;gBACtB,GAAG,EAAE,UAAU,EAAE;aAClB;YACD,aAAa,EAAE,QAAQ,EAAE;SAC1B;QACD,YAAY,EAAE,KAAK;QACnB,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE;YACP,IAAI;YACJ,gBAAgB,EAAE,YAAY,CAAC,UAAU;YACzC,aAAa,EAAE,YAAY,CAAC,OAAO;YACnC,kBAAkB,EAAE,YAAY,CAAC,OAAO;YACxC,iBAAiB,EAAE,GAAG;YACtB,mBAAmB,EAAE,GAAG;YACxB,aAAa,EAAE,YAAY,CAAC,OAAO;YACnC,kBAAkB,EAAE,YAAY,CAAC,OAAO;SACzC;QACD,YAAY,EAAE,UAAU,EAAE;QAC1B,QAAQ,EAAE,sBAAsB,CAAC,IAAI;QACrC,YAAY,EAAE,QAAQ,EAAE;KACzB,CAAC;AACJ,CAAC","sourcesContent":["import type { Hex, Json } from '@metamask/utils';\n\nimport { TransactionPayStrategy } from '../constants.js';\nimport type {\n Amount,\n FiatValue,\n TransactionPaymentToken,\n TransactionPayQuote,\n} from '../types.js';\n\nfunction zeroFiat(): FiatValue {\n return { fiat: '0', usd: '0' };\n}\n\nfunction zeroAmount(): Amount {\n return { ...zeroFiat(), human: '0', raw: '0' };\n}\n\n/**\n * Build a no-op quote for a transaction that needs no conversion.\n *\n * Stored instead of an empty quotes array when a payment token is selected\n * but the route is direct (e.g. same token and chain, or sufficient balance).\n * This gives clients and the publish hook an explicit marker to distinguish\n * \"no quote needed\" from \"quote needed but missing\". No-op quotes use the\n * `TransactionPayStrategy.None` strategy, cannot be executed, and must be\n * ignored by fee totals and quote refreshes.\n *\n * @param from - Resolved wallet address for the transaction.\n * @param paymentToken - Selected payment token.\n * @returns The no-op quote.\n */\nexport function buildNoOpQuote(\n from: Hex,\n paymentToken: TransactionPaymentToken,\n): TransactionPayQuote<Json> {\n return {\n dust: zeroFiat(),\n estimatedDuration: 0,\n fees: {\n metaMask: zeroFiat(),\n provider: zeroFiat(),\n sourceNetwork: {\n estimate: zeroAmount(),\n max: zeroAmount(),\n },\n targetNetwork: zeroFiat(),\n },\n isInputBased: false,\n original: null,\n request: {\n from,\n sourceBalanceRaw: paymentToken.balanceRaw,\n sourceChainId: paymentToken.chainId,\n sourceTokenAddress: paymentToken.address,\n sourceTokenAmount: '0',\n targetAmountMinimum: '0',\n targetChainId: paymentToken.chainId,\n targetTokenAddress: paymentToken.address,\n },\n sourceAmount: zeroAmount(),\n strategy: TransactionPayStrategy.None,\n targetAmount: zeroFiat(),\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"perps.d.ts","sourceRoot":"","sources":["../../src/utils/perps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAU3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQhD,KAAK,gBAAgB,GAAG,IAAI,CAC1B,eAAe,EACf,UAAU,GAAG,oBAAoB,CAClC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,gBAAgB,GAAG,SAAS,GACxC,OAAO,CAeT;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,IAAI,CACX,YAAY,EACZ,aAAa,GAAG,eAAe,GAAG,oBAAoB,CACvD,EACD,WAAW,EAAE,gBAAgB,GAAG,SAAS,GACxC,OAAO,CAQT;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,oBAAoB,EAAE,GAAG,EACzB,cAAc,GAAE,MAAgC,GAC/C,YAAY,CAad;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,YAAY,EACrB,oBAAoB,EAAE,GAAG,GACxB,YAAY,CAkBd"}
1
+ {"version":3,"file":"perps.d.ts","sourceRoot":"","sources":["../../src/utils/perps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAU3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD,KAAK,gBAAgB,GAAG,IAAI,CAC1B,eAAe,EACf,UAAU,GAAG,oBAAoB,CAClC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,gBAAgB,GAAG,SAAS,GACxC,OAAO,CAeT;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,IAAI,CACX,YAAY,EACZ,aAAa,GAAG,eAAe,GAAG,oBAAoB,CACvD,EACD,WAAW,EAAE,gBAAgB,GAAG,SAAS,GACxC,OAAO,CAQT;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,oBAAoB,EAAE,GAAG,EACzB,cAAc,GAAE,MAAgC,GAC/C,YAAY,CAad;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,YAAY,EACrB,oBAAoB,EAAE,GAAG,GACxB,YAAY,CAad"}
@@ -1,8 +1,6 @@
1
1
  import { BigNumber } from 'bignumber.js';
2
2
  import { ARBITRUM_USDC_ADDRESS, CHAIN_ID_ARBITRUM, CHAIN_ID_HYPERCORE, HYPERCORE_USDC_DECIMALS, USDC_DECIMALS, } from '../constants.js';
3
- import { getCaipAccountId } from './account.js';
4
3
  import { extractErc20TransferRecipient } from './polymarket/calldata.js';
5
- import { buildCaipAssetType } from './token.js';
6
4
  const HYPERLIQUID_BRIDGE_ADDRESS_LOWER = '0x2df1c51e09aecf9cacb7bc98cb1742757f163df7';
7
5
  /**
8
6
  * Detect whether a transaction is a Hyperliquid perps deposit by inspecting
@@ -89,11 +87,10 @@ export function normalizePerpsWithdrawRequest(request, hypercoreUsdcAddress) {
89
87
  if (!request.isHyperliquidSource) {
90
88
  return request;
91
89
  }
92
- const sourceAssetId = buildCaipAssetType(CHAIN_ID_HYPERCORE, hypercoreUsdcAddress);
93
90
  return {
94
91
  ...request,
95
- sourceAccountId: getCaipAccountId(sourceAssetId, request.from),
96
- sourceAssetId,
92
+ sourceChainId: CHAIN_ID_HYPERCORE,
93
+ sourceTokenAddress: hypercoreUsdcAddress,
97
94
  sourceTokenAmount: request.sourceTokenAmount
98
95
  ? toHypercoreAmount(request.sourceTokenAmount)
99
96
  : request.sourceTokenAmount,
@@ -1 +1 @@
1
- {"version":3,"file":"perps.js","sourceRoot":"","sources":["../../src/utils/perps.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TransactionMeta } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n ARBITRUM_USDC_ADDRESS,\n CHAIN_ID_ARBITRUM,\n CHAIN_ID_HYPERCORE,\n HYPERCORE_USDC_DECIMALS,\n USDC_DECIMALS,\n} from '../constants.js';\nimport type { QuoteRequest } from '../types.js';\nimport { getCaipAccountId } from './account.js';\nimport { extractErc20TransferRecipient } from './polymarket/calldata.js';\nimport { buildCaipAssetType } from './token.js';\n\nconst HYPERLIQUID_BRIDGE_ADDRESS_LOWER =\n '0x2df1c51e09aecf9cacb7bc98cb1742757f163df7';\n\ntype PerpsTransaction = Pick<\n TransactionMeta,\n 'txParams' | 'nestedTransactions'\n>;\n\n/**\n * Detect whether a transaction is a Hyperliquid perps deposit by inspecting\n * the parent transaction (and any nested transactions) for a call to the\n * Hyperliquid bridge or an ERC-20 transfer to it.\n *\n * Transaction type is intentionally NOT consulted so that any caller\n * funnelling a bridge deposit through Pay is supported.\n *\n * @param transaction - Parent transaction whose calls are inspected.\n * @returns Whether the transaction deposits into the Hyperliquid bridge.\n */\nexport function isPerpsDepositTransaction(\n transaction: PerpsTransaction | undefined,\n): boolean {\n if (!transaction) {\n return false;\n }\n\n const calls = [\n transaction.txParams,\n ...(transaction.nestedTransactions ?? []),\n ];\n\n return calls.some(\n (call) =>\n isBridgeAddress(call?.to) ||\n isBridgeAddress(getTransferRecipient(call?.data)),\n );\n}\n\n/**\n * Detect whether a quote request is a Hyperliquid perps deposit, meaning the\n * parent transaction deposits into the Hyperliquid bridge and the request\n * targets the Arbitrum USDC it requires.\n *\n * @param request - Quote request from the transaction-pay controller.\n * @param transaction - Parent transaction whose calls are inspected.\n * @returns Whether the request matches a Hyperliquid perps deposit.\n */\nexport function isPerpsDepositRequest(\n request: Pick<\n QuoteRequest,\n 'isPostQuote' | 'targetChainId' | 'targetTokenAddress'\n >,\n transaction: PerpsTransaction | undefined,\n): boolean {\n return (\n request.isPostQuote !== true &&\n request.targetChainId === CHAIN_ID_ARBITRUM &&\n request.targetTokenAddress.toLowerCase() ===\n ARBITRUM_USDC_ADDRESS.toLowerCase() &&\n isPerpsDepositTransaction(transaction)\n );\n}\n\n/**\n * Rewrite a Hyperliquid perps-deposit quote request into a HyperCore direct\n * deposit.\n *\n * Transaction pay starts from the parent on-chain asset (Arbitrum USDC,\n * 6 decimals), so the target amount is shifted to the decimals the strategy's\n * quote API uses for the HyperCore token. This defaults to the 8 decimals of\n * HyperCore USDC, but providers may represent it differently (e.g. Across\n * quotes USDC-PERPS in 6 decimals).\n *\n * @param request - Quote request from the transaction-pay controller.\n * @param transaction - Parent transaction whose calls are inspected.\n * @param hypercoreUsdcAddress - Strategy-specific HyperCore USDC address to\n * set as the target token.\n * @param targetDecimals - Decimals of the HyperCore token in the strategy's\n * quote API.\n * @returns Normalized request, or the original request if not a perps deposit.\n */\nexport function normalizePerpsDepositRequest(\n request: QuoteRequest,\n transaction: PerpsTransaction | undefined,\n hypercoreUsdcAddress: Hex,\n targetDecimals: number = HYPERCORE_USDC_DECIMALS,\n): QuoteRequest {\n if (!isPerpsDepositRequest(request, transaction)) {\n return request;\n }\n\n return {\n ...request,\n targetAmountMinimum: new BigNumber(request.targetAmountMinimum)\n .shiftedBy(targetDecimals - USDC_DECIMALS)\n .toFixed(0),\n targetChainId: CHAIN_ID_HYPERCORE,\n targetTokenAddress: hypercoreUsdcAddress,\n };\n}\n\n/**\n * Rewrite a Hyperliquid perps-withdraw quote request so the source is\n * HyperCore USDC rather than Arbitrum USDC.\n *\n * The source amount is provided in 6 decimals and shifted to the 8 decimals\n * HyperCore expects.\n *\n * @param request - Quote request from the transaction-pay controller.\n * @param hypercoreUsdcAddress - Strategy-specific HyperCore USDC address to\n * set as the source token.\n * @returns Normalized request, or the original request if not a perps withdraw.\n */\nexport function normalizePerpsWithdrawRequest(\n request: QuoteRequest,\n hypercoreUsdcAddress: Hex,\n): QuoteRequest {\n if (!request.isHyperliquidSource) {\n return request;\n }\n\n const sourceAssetId = buildCaipAssetType(\n CHAIN_ID_HYPERCORE,\n hypercoreUsdcAddress,\n );\n\n return {\n ...request,\n sourceAccountId: getCaipAccountId(sourceAssetId, request.from),\n sourceAssetId,\n sourceTokenAmount: request.sourceTokenAmount\n ? toHypercoreAmount(request.sourceTokenAmount)\n : request.sourceTokenAmount,\n };\n}\n\nfunction getTransferRecipient(data: string | undefined): Hex | undefined {\n if (!data) {\n return undefined;\n }\n\n try {\n return extractErc20TransferRecipient(data as Hex);\n } catch {\n return undefined;\n }\n}\n\nfunction isBridgeAddress(address: string | undefined): boolean {\n return address?.toLowerCase() === HYPERLIQUID_BRIDGE_ADDRESS_LOWER;\n}\n\nfunction toHypercoreAmount(amount: string): string {\n return new BigNumber(amount)\n .shiftedBy(HYPERCORE_USDC_DECIMALS - USDC_DECIMALS)\n .toFixed(0);\n}\n"]}
1
+ {"version":3,"file":"perps.js","sourceRoot":"","sources":["../../src/utils/perps.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TransactionMeta } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n ARBITRUM_USDC_ADDRESS,\n CHAIN_ID_ARBITRUM,\n CHAIN_ID_HYPERCORE,\n HYPERCORE_USDC_DECIMALS,\n USDC_DECIMALS,\n} from '../constants.js';\nimport type { QuoteRequest } from '../types.js';\nimport { extractErc20TransferRecipient } from './polymarket/calldata.js';\n\nconst HYPERLIQUID_BRIDGE_ADDRESS_LOWER =\n '0x2df1c51e09aecf9cacb7bc98cb1742757f163df7';\n\ntype PerpsTransaction = Pick<\n TransactionMeta,\n 'txParams' | 'nestedTransactions'\n>;\n\n/**\n * Detect whether a transaction is a Hyperliquid perps deposit by inspecting\n * the parent transaction (and any nested transactions) for a call to the\n * Hyperliquid bridge or an ERC-20 transfer to it.\n *\n * Transaction type is intentionally NOT consulted so that any caller\n * funnelling a bridge deposit through Pay is supported.\n *\n * @param transaction - Parent transaction whose calls are inspected.\n * @returns Whether the transaction deposits into the Hyperliquid bridge.\n */\nexport function isPerpsDepositTransaction(\n transaction: PerpsTransaction | undefined,\n): boolean {\n if (!transaction) {\n return false;\n }\n\n const calls = [\n transaction.txParams,\n ...(transaction.nestedTransactions ?? []),\n ];\n\n return calls.some(\n (call) =>\n isBridgeAddress(call?.to) ||\n isBridgeAddress(getTransferRecipient(call?.data)),\n );\n}\n\n/**\n * Detect whether a quote request is a Hyperliquid perps deposit, meaning the\n * parent transaction deposits into the Hyperliquid bridge and the request\n * targets the Arbitrum USDC it requires.\n *\n * @param request - Quote request from the transaction-pay controller.\n * @param transaction - Parent transaction whose calls are inspected.\n * @returns Whether the request matches a Hyperliquid perps deposit.\n */\nexport function isPerpsDepositRequest(\n request: Pick<\n QuoteRequest,\n 'isPostQuote' | 'targetChainId' | 'targetTokenAddress'\n >,\n transaction: PerpsTransaction | undefined,\n): boolean {\n return (\n request.isPostQuote !== true &&\n request.targetChainId === CHAIN_ID_ARBITRUM &&\n request.targetTokenAddress.toLowerCase() ===\n ARBITRUM_USDC_ADDRESS.toLowerCase() &&\n isPerpsDepositTransaction(transaction)\n );\n}\n\n/**\n * Rewrite a Hyperliquid perps-deposit quote request into a HyperCore direct\n * deposit.\n *\n * Transaction pay starts from the parent on-chain asset (Arbitrum USDC,\n * 6 decimals), so the target amount is shifted to the decimals the strategy's\n * quote API uses for the HyperCore token. This defaults to the 8 decimals of\n * HyperCore USDC, but providers may represent it differently (e.g. Across\n * quotes USDC-PERPS in 6 decimals).\n *\n * @param request - Quote request from the transaction-pay controller.\n * @param transaction - Parent transaction whose calls are inspected.\n * @param hypercoreUsdcAddress - Strategy-specific HyperCore USDC address to\n * set as the target token.\n * @param targetDecimals - Decimals of the HyperCore token in the strategy's\n * quote API.\n * @returns Normalized request, or the original request if not a perps deposit.\n */\nexport function normalizePerpsDepositRequest(\n request: QuoteRequest,\n transaction: PerpsTransaction | undefined,\n hypercoreUsdcAddress: Hex,\n targetDecimals: number = HYPERCORE_USDC_DECIMALS,\n): QuoteRequest {\n if (!isPerpsDepositRequest(request, transaction)) {\n return request;\n }\n\n return {\n ...request,\n targetAmountMinimum: new BigNumber(request.targetAmountMinimum)\n .shiftedBy(targetDecimals - USDC_DECIMALS)\n .toFixed(0),\n targetChainId: CHAIN_ID_HYPERCORE,\n targetTokenAddress: hypercoreUsdcAddress,\n };\n}\n\n/**\n * Rewrite a Hyperliquid perps-withdraw quote request so the source is\n * HyperCore USDC rather than Arbitrum USDC.\n *\n * The source amount is provided in 6 decimals and shifted to the 8 decimals\n * HyperCore expects.\n *\n * @param request - Quote request from the transaction-pay controller.\n * @param hypercoreUsdcAddress - Strategy-specific HyperCore USDC address to\n * set as the source token.\n * @returns Normalized request, or the original request if not a perps withdraw.\n */\nexport function normalizePerpsWithdrawRequest(\n request: QuoteRequest,\n hypercoreUsdcAddress: Hex,\n): QuoteRequest {\n if (!request.isHyperliquidSource) {\n return request;\n }\n\n return {\n ...request,\n sourceChainId: CHAIN_ID_HYPERCORE,\n sourceTokenAddress: hypercoreUsdcAddress,\n sourceTokenAmount: request.sourceTokenAmount\n ? toHypercoreAmount(request.sourceTokenAmount)\n : request.sourceTokenAmount,\n };\n}\n\nfunction getTransferRecipient(data: string | undefined): Hex | undefined {\n if (!data) {\n return undefined;\n }\n\n try {\n return extractErc20TransferRecipient(data as Hex);\n } catch {\n return undefined;\n }\n}\n\nfunction isBridgeAddress(address: string | undefined): boolean {\n return address?.toLowerCase() === HYPERLIQUID_BRIDGE_ADDRESS_LOWER;\n}\n\nfunction toHypercoreAmount(amount: string): string {\n return new BigNumber(amount)\n .shiftedBy(HYPERCORE_USDC_DECIMALS - USDC_DECIMALS)\n .toFixed(0);\n}\n"]}
@@ -2,7 +2,7 @@ import { createModuleLogger } from '@metamask/utils';
2
2
  import { BigNumber } from 'bignumber.js';
3
3
  import { NATIVE_TOKEN_ADDRESS } from '../constants.js';
4
4
  import { projectLogger } from '../logger.js';
5
- import { getEvmQuoteSource, getNativeToken } from './token.js';
5
+ import { getNativeToken } from './token.js';
6
6
  const log = createModuleLogger(projectLogger, 'post-quote');
7
7
  // Buffer applied to the gas cost when reserving native tokens for gas in
8
8
  // post-quote flows, accounting for gas limit re-estimation variance.
@@ -69,9 +69,8 @@ export async function reservePostQuoteGas({ quote, requote, }) {
69
69
  }
70
70
  }
71
71
  function isNativeSource(request) {
72
- const { chainId, tokenAddress } = getEvmQuoteSource(request);
73
- const sourceToken = tokenAddress.toLowerCase();
74
- return (sourceToken === getNativeToken(chainId).toLowerCase() ||
72
+ const sourceToken = request.sourceTokenAddress.toLowerCase();
73
+ return (sourceToken === getNativeToken(request.sourceChainId).toLowerCase() ||
75
74
  sourceToken === NATIVE_TOKEN_ADDRESS.toLowerCase());
76
75
  }
77
76
  //# sourceMappingURL=post-quote.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"post-quote.js","sourceRoot":"","sources":["../../src/utils/post-quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { NATIVE_TOKEN_ADDRESS } from '../constants.js';\nimport { projectLogger } from '../logger.js';\nimport type { QuoteRequest, TransactionPayQuote } from '../types.js';\nimport { getEvmQuoteSource, getNativeToken } from './token.js';\n\nconst log = createModuleLogger(projectLogger, 'post-quote');\n\n// Buffer applied to the gas cost when reserving native tokens for gas in\n// post-quote flows, accounting for gas limit re-estimation variance.\nconst POST_QUOTE_GAS_BUFFER = 1.1;\n\n/**\n * Re-quote a post-quote flow with the gas cost reserved from the source amount.\n *\n * Gas must come out of the source amount when the balance is fully committed\n * to the quote, either because gas is paid with the source token as a gas fee\n * token or because the source token is itself the native gas token.\n *\n * @param options - Options bag.\n * @param options.quote - Quote for the full source amount.\n * @param options.requote - Fetch a quote for an adjusted request.\n * @returns The re-quoted quote, or the original quote when no reservation is\n * needed or the re-quote is unusable.\n */\nexport async function reservePostQuoteGas<Original>({\n quote,\n requote,\n}: {\n quote: TransactionPayQuote<Original>;\n requote: (request: QuoteRequest) => Promise<TransactionPayQuote<Original>>;\n}): Promise<TransactionPayQuote<Original>> {\n const { request } = quote;\n\n if (!request.isPostQuote) {\n return quote;\n }\n\n if (!quote.fees.isSourceGasFeeToken && !isNativeSource(request)) {\n return quote;\n }\n\n const gasCostRaw = new BigNumber(quote.fees.sourceNetwork.max.raw)\n .multipliedBy(POST_QUOTE_GAS_BUFFER)\n .integerValue(BigNumber.ROUND_UP);\n\n const existingHeadroom = new BigNumber(request.sourceBalanceRaw).minus(\n request.sourceTokenAmount,\n );\n\n if (existingHeadroom.isGreaterThanOrEqualTo(gasCostRaw)) {\n log('Sufficient existing balance for gas, skipping subtraction', {\n existingHeadroom: existingHeadroom.toString(10),\n gasCostRaw: gasCostRaw.toString(10),\n });\n return quote;\n }\n\n const adjustedSourceAmount = new BigNumber(request.sourceTokenAmount)\n .minus(gasCostRaw)\n .integerValue(BigNumber.ROUND_DOWN);\n\n log('Subtracting gas from source for post-quote', {\n adjustedSourceAmount: adjustedSourceAmount.toString(10),\n gasCostRaw,\n originalSourceAmount: request.sourceTokenAmount,\n });\n\n if (!adjustedSourceAmount.isGreaterThan(0)) {\n log('Insufficient balance after gas subtraction, using original quote');\n return quote;\n }\n\n try {\n const adjustedQuote = await requote({\n ...request,\n sourceTokenAmount: adjustedSourceAmount.toFixed(0, BigNumber.ROUND_DOWN),\n });\n\n if (\n quote.fees.isSourceGasFeeToken &&\n !adjustedQuote.fees.isSourceGasFeeToken\n ) {\n log('Re-quote lost gas fee token eligibility, using original quote');\n return quote;\n }\n\n return adjustedQuote;\n } catch (error) {\n log('Re-quote failed, using original quote', { error });\n return quote;\n }\n}\n\nfunction isNativeSource(request: QuoteRequest): boolean {\n const { chainId, tokenAddress } = getEvmQuoteSource(request);\n const sourceToken = tokenAddress.toLowerCase();\n\n return (\n sourceToken === getNativeToken(chainId).toLowerCase() ||\n sourceToken === NATIVE_TOKEN_ADDRESS.toLowerCase()\n );\n}\n"]}
1
+ {"version":3,"file":"post-quote.js","sourceRoot":"","sources":["../../src/utils/post-quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { NATIVE_TOKEN_ADDRESS } from '../constants.js';\nimport { projectLogger } from '../logger.js';\nimport type { QuoteRequest, TransactionPayQuote } from '../types.js';\nimport { getNativeToken } from './token.js';\n\nconst log = createModuleLogger(projectLogger, 'post-quote');\n\n// Buffer applied to the gas cost when reserving native tokens for gas in\n// post-quote flows, accounting for gas limit re-estimation variance.\nconst POST_QUOTE_GAS_BUFFER = 1.1;\n\n/**\n * Re-quote a post-quote flow with the gas cost reserved from the source amount.\n *\n * Gas must come out of the source amount when the balance is fully committed\n * to the quote, either because gas is paid with the source token as a gas fee\n * token or because the source token is itself the native gas token.\n *\n * @param options - Options bag.\n * @param options.quote - Quote for the full source amount.\n * @param options.requote - Fetch a quote for an adjusted request.\n * @returns The re-quoted quote, or the original quote when no reservation is\n * needed or the re-quote is unusable.\n */\nexport async function reservePostQuoteGas<Original>({\n quote,\n requote,\n}: {\n quote: TransactionPayQuote<Original>;\n requote: (request: QuoteRequest) => Promise<TransactionPayQuote<Original>>;\n}): Promise<TransactionPayQuote<Original>> {\n const { request } = quote;\n\n if (!request.isPostQuote) {\n return quote;\n }\n\n if (!quote.fees.isSourceGasFeeToken && !isNativeSource(request)) {\n return quote;\n }\n\n const gasCostRaw = new BigNumber(quote.fees.sourceNetwork.max.raw)\n .multipliedBy(POST_QUOTE_GAS_BUFFER)\n .integerValue(BigNumber.ROUND_UP);\n\n const existingHeadroom = new BigNumber(request.sourceBalanceRaw).minus(\n request.sourceTokenAmount,\n );\n\n if (existingHeadroom.isGreaterThanOrEqualTo(gasCostRaw)) {\n log('Sufficient existing balance for gas, skipping subtraction', {\n existingHeadroom: existingHeadroom.toString(10),\n gasCostRaw: gasCostRaw.toString(10),\n });\n return quote;\n }\n\n const adjustedSourceAmount = new BigNumber(request.sourceTokenAmount)\n .minus(gasCostRaw)\n .integerValue(BigNumber.ROUND_DOWN);\n\n log('Subtracting gas from source for post-quote', {\n adjustedSourceAmount: adjustedSourceAmount.toString(10),\n gasCostRaw,\n originalSourceAmount: request.sourceTokenAmount,\n });\n\n if (!adjustedSourceAmount.isGreaterThan(0)) {\n log('Insufficient balance after gas subtraction, using original quote');\n return quote;\n }\n\n try {\n const adjustedQuote = await requote({\n ...request,\n sourceTokenAmount: adjustedSourceAmount.toFixed(0, BigNumber.ROUND_DOWN),\n });\n\n if (\n quote.fees.isSourceGasFeeToken &&\n !adjustedQuote.fees.isSourceGasFeeToken\n ) {\n log('Re-quote lost gas fee token eligibility, using original quote');\n return quote;\n }\n\n return adjustedQuote;\n } catch (error) {\n log('Re-quote failed, using original quote', { error });\n return quote;\n }\n}\n\nfunction isNativeSource(request: QuoteRequest): boolean {\n const sourceToken = request.sourceTokenAddress.toLowerCase();\n\n return (\n sourceToken === getNativeToken(request.sourceChainId).toLowerCase() ||\n sourceToken === NATIVE_TOKEN_ADDRESS.toLowerCase()\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"quotes.d.ts","sourceRoot":"","sources":["../../src/utils/quotes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAIxE,OAAO,EAAmB,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAE1E,OAAO,KAAK,EAIV,eAAe,EACf,kBAAkB,EAClB,iCAAiC,EAMjC,6BAA6B,EAC9B,MAAM,aAAa,CAAC;AAsCrB,MAAM,MAAM,mBAAmB,GAAG;IAChC,aAAa,EAAE,CAAC,WAAW,EAAE,eAAe,KAAK,sBAAsB,EAAE,CAAC;IAC1E,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,6BAA6B,CAAC;CACtD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAgLlB;AA4ID;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,iCAAiC,EAC5C,qBAAqB,EAAE,6BAA6B,EACpD,aAAa,EAAE,CAAC,WAAW,EAAE,eAAe,KAAK,sBAAsB,EAAE,EACzE,UAAU,CAAC,EAAE,kBAAkB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA+Df"}
1
+ {"version":3,"file":"quotes.d.ts","sourceRoot":"","sources":["../../src/utils/quotes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAIxE,OAAO,EAAmB,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAE1E,OAAO,KAAK,EAGV,eAAe,EACf,kBAAkB,EAClB,iCAAiC,EAMjC,6BAA6B,EAC9B,MAAM,aAAa,CAAC;AAyBrB,MAAM,MAAM,mBAAmB,GAAG;IAChC,aAAa,EAAE,CAAC,WAAW,EAAE,eAAe,KAAK,sBAAsB,EAAE,CAAC;IAC1E,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,6BAA6B,CAAC;CACtD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAgKlB;AAkID;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,iCAAiC,EAC5C,qBAAqB,EAAE,6BAA6B,EACpD,aAAa,EAAE,CAAC,WAAW,EAAE,eAAe,KAAK,sBAAsB,EAAE,EACzE,UAAU,CAAC,EAAE,kBAAkB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA+Df"}
@@ -1,12 +1,12 @@
1
1
  import { TransactionStatus } from '@metamask/transaction-controller';
2
- import { createModuleLogger, parseCaipAssetType } from '@metamask/utils';
2
+ import { createModuleLogger } from '@metamask/utils';
3
3
  import { TransactionPayStrategy } from '../constants.js';
4
4
  import { projectLogger } from '../logger.js';
5
- import { accountRequiresMfa, accountSupports7702, getAssetAccount, getCaipAccountId, getEvmAccountAddress, getEvmAccountOverride, } from './account.js';
5
+ import { accountRequiresMfa, accountSupports7702 } from './account.js';
6
6
  import { buildNoOpQuote } from './no-op-quote.js';
7
7
  import { updateSourceAmounts } from './source-amounts.js';
8
8
  import { checkStrategyQuoteSupport, checkStrategySupport, getStrategiesByName, getStrategyByName, } from './strategy.js';
9
- import { buildCaipAssetType, computeTokenAmounts, getAssetBalance, getAssetFiatRate, getEvmPaymentToken, getLiveTokenBalance, getTokenFiatRate, isNonEvmPaymentToken, } from './token.js';
9
+ import { computeTokenAmounts, getLiveTokenBalance, getTokenFiatRate, } from './token.js';
10
10
  import { calculateTotals } from './totals.js';
11
11
  import { getTransaction, updateTransaction } from './transaction.js';
12
12
  import { isQuoteError } from './validation.js';
@@ -35,8 +35,7 @@ export async function updateQuotes(request) {
35
35
  }
36
36
  log('Updating quotes', { transactionId });
37
37
  const { accountOverride, atomic: requestedAtomic, fiatPayment, isHyperliquidSource, isMaxAmount, isPolymarketDepositWallet, isPostQuote, isQuoteRequired, paymentOverride, paymentToken: originalPaymentToken, refundTo, sourceAmounts, tokens, } = transactionData;
38
- const transactionFrom = transaction.txParams.from;
39
- const from = getFrom(messenger, transaction, transactionData);
38
+ const from = getFrom(transaction, transactionData);
40
39
  const atomic = resolveAtomic(transaction, messenger, requestedAtomic);
41
40
  const controller = abortPreviousAndCreateController(transactionId);
42
41
  const { signal } = controller;
@@ -45,19 +44,11 @@ export async function updateQuotes(request) {
45
44
  data.quoteError = undefined;
46
45
  });
47
46
  try {
48
- const sourceAccount = originalPaymentToken && isNonEvmPaymentToken(originalPaymentToken)
49
- ? getAssetAccount(messenger, {
50
- accountOverride,
51
- assetId: originalPaymentToken.assetId,
52
- from: transactionFrom,
53
- })
54
- : undefined;
55
47
  const paymentToken = await refreshPaymentTokenBalance({
56
48
  // Post-quote payment tokens are received by `accountOverride`.
57
- from: getEvmAccountAddress(messenger, accountOverride, transactionFrom),
49
+ from: accountOverride ?? transaction.txParams.from,
58
50
  messenger,
59
51
  paymentToken: originalPaymentToken,
60
- sourceAccount,
61
52
  signal,
62
53
  transactionId,
63
54
  updateTransactionData,
@@ -73,11 +64,7 @@ export async function updateQuotes(request) {
73
64
  // loaded.
74
65
  const latestSourceAmounts = getSourceAmounts(messenger, transactionId) ?? sourceAmounts;
75
66
  const requests = buildQuoteRequests({
76
- // Post-quote output is received by the EVM account override. Other flows
77
- // send from the EVM account override, which is already `from`.
78
- accountOverride: isPostQuote
79
- ? getEvmAccountOverride(messenger, accountOverride)
80
- : undefined,
67
+ accountOverride,
81
68
  atomic,
82
69
  from,
83
70
  isHyperliquidSource,
@@ -87,7 +74,6 @@ export async function updateQuotes(request) {
87
74
  paymentOverride,
88
75
  paymentToken,
89
76
  refundTo,
90
- sourceAddress: sourceAccount?.address ?? from,
91
77
  sourceAmounts: latestSourceAmounts,
92
78
  tokens,
93
79
  transactionId,
@@ -149,18 +135,17 @@ export async function updateQuotes(request) {
149
135
  /**
150
136
  * Get the account that sends quotes for a transaction.
151
137
  *
152
- * @param messenger - Controller messenger.
153
138
  * @param transaction - Transaction metadata.
154
139
  * @param transactionData - Transaction pay data.
155
140
  * @returns The quote sender address.
156
141
  */
157
- function getFrom(messenger, transaction, transactionData) {
142
+ function getFrom(transaction, transactionData) {
158
143
  const transactionFrom = transaction.txParams.from;
159
144
  // Post-quote output goes to `accountOverride`, so the funds holder sends.
160
145
  if (transactionData.isPostQuote) {
161
146
  return transactionFrom;
162
147
  }
163
- return getEvmAccountAddress(messenger, transactionData.accountOverride, transactionFrom);
148
+ return transactionData.accountOverride ?? transactionFrom;
164
149
  }
165
150
  /**
166
151
  * Resolve whether quotes may execute the target transaction atomically.
@@ -224,17 +209,14 @@ function syncTransaction({ batchTransactions, hasQuotes, isPostQuote, messenger,
224
209
  if (!tx.isGasFeeSponsored) {
225
210
  tx.isExternalSign = hasQuotes;
226
211
  }
227
- // The legacy chain ID and token address only describe EVM payment
228
- // tokens, so they are unset for a non-EVM payment token.
229
- const evmPaymentToken = getEvmPaymentToken(paymentToken);
230
212
  tx.metamaskPay = {
231
213
  bridgeFeeFiat: totals.fees.provider.usd,
232
- chainId: evmPaymentToken?.chainId,
214
+ chainId: paymentToken?.chainId,
233
215
  isPostQuote,
234
216
  networkFeeFiat: totals.fees.sourceNetwork.estimate.usd,
235
217
  strategy,
236
218
  targetFiat: totals.targetAmount.usd,
237
- tokenAddress: evmPaymentToken?.address,
219
+ tokenAddress: paymentToken?.address,
238
220
  totalFiat: totals.total.usd,
239
221
  };
240
222
  });
@@ -264,7 +246,7 @@ export async function refreshQuotes(messenger, updateTransactionData, getStrateg
264
246
  const strategyName = quotes[0].strategy;
265
247
  const strategy = getStrategyByName(strategyName);
266
248
  const refreshInterval = (await strategy.getRefreshInterval?.({
267
- chainId: parseCaipAssetType(quotes[0].request.sourceAssetId).chainId,
249
+ chainId: quotes[0].request.sourceChainId,
268
250
  messenger,
269
251
  transactionId,
270
252
  })) ?? DEFAULT_REFRESH_INTERVAL;
@@ -316,13 +298,7 @@ function getSourceAmounts(messenger, transactionId) {
316
298
  return messenger.call('TransactionPayController:getState').transactionData[transactionId]?.sourceAmounts;
317
299
  }
318
300
  /**
319
- * Build quote requests required to retrieve quotes using an EVM payment token.
320
- *
321
- * Requests are built the same way for every payment token, identifying the
322
- * source by its CAIP-19 asset type and the CAIP-10 account holding it.
323
- * Strategies support or reject them based on the protocol of the source
324
- * asset. Post-quote flows with a non-EVM destination token are not supported
325
- * and return no requests.
301
+ * Build quote requests required to retrieve quotes.
326
302
  *
327
303
  * @param request - Request parameters.
328
304
  * @param request.accountOverride - Optional account receiving post-quote output.
@@ -335,25 +311,20 @@ function getSourceAmounts(messenger, transactionId) {
335
311
  * @param request.paymentOverride - Optional payment override type for the transaction.
336
312
  * @param request.paymentToken - Payment token (source for standard flows, destination for post-quote).
337
313
  * @param request.refundTo - Optional address to receive refunds if the Relay transaction fails.
338
- * @param request.sourceAddress - Address of the account holding the payment token.
339
314
  * @param request.sourceAmounts - Source amounts for the transaction.
340
315
  * @param request.tokens - Required tokens for the transaction.
341
316
  * @param request.transactionId - ID of the transaction.
342
317
  * @returns Array of quote requests.
343
318
  */
344
- function buildQuoteRequests({ accountOverride, atomic, from, isHyperliquidSource, isMaxAmount, isPolymarketDepositWallet, isPostQuote, paymentOverride, paymentToken, refundTo, sourceAddress, sourceAmounts, tokens, transactionId, }) {
319
+ function buildQuoteRequests({ accountOverride, atomic, from, isHyperliquidSource, isMaxAmount, isPolymarketDepositWallet, isPostQuote, paymentOverride, paymentToken, refundTo, sourceAmounts, tokens, transactionId, }) {
345
320
  if (!paymentToken) {
346
321
  return [];
347
322
  }
348
323
  if (isPostQuote) {
349
- const evmPaymentToken = getEvmPaymentToken(paymentToken);
350
- if (!evmPaymentToken) {
351
- return [];
352
- }
353
324
  return buildPostQuoteRequests({
354
325
  accountOverride,
355
326
  atomic,
356
- destinationToken: evmPaymentToken,
327
+ destinationToken: paymentToken,
357
328
  from,
358
329
  isHyperliquidSource,
359
330
  isMaxAmount,
@@ -364,7 +335,6 @@ function buildQuoteRequests({ accountOverride, atomic, from, isHyperliquidSource
364
335
  transactionId,
365
336
  });
366
337
  }
367
- const sourceAccountId = getCaipAccountId(paymentToken.assetId, sourceAddress);
368
338
  // Standard flow: source = paymentToken, target = required tokens
369
339
  const requests = (sourceAmounts ?? []).map((sourceAmount) => {
370
340
  const token = tokens.find((singleToken) => singleToken.address === sourceAmount.targetTokenAddress);
@@ -374,9 +344,9 @@ function buildQuoteRequests({ accountOverride, atomic, from, isHyperliquidSource
374
344
  isMaxAmount,
375
345
  paymentOverride,
376
346
  refundTo,
377
- sourceAccountId,
378
- sourceAssetId: paymentToken.assetId,
379
347
  sourceBalanceRaw: paymentToken.balanceRaw,
348
+ sourceChainId: paymentToken.chainId,
349
+ sourceTokenAddress: paymentToken.address,
380
350
  sourceTokenAmount: sourceAmount.sourceAmountRaw,
381
351
  targetAmountMinimum: token.allowUnderMinimum ? '0' : token.amountRaw,
382
352
  targetChainId: token.chainId,
@@ -420,7 +390,6 @@ function buildPostQuoteRequests({ accountOverride, atomic, destinationToken, fro
420
390
  });
421
391
  return [];
422
392
  }
423
- const sourceAssetId = buildCaipAssetType(sourceAmount.sourceChainId, sourceAmount.sourceTokenAddress);
424
393
  const request = {
425
394
  accountOverride,
426
395
  atomic,
@@ -431,9 +400,9 @@ function buildPostQuoteRequests({ accountOverride, atomic, destinationToken, fro
431
400
  isPostQuote: true,
432
401
  paymentOverride,
433
402
  refundTo,
434
- sourceAccountId: getCaipAccountId(sourceAssetId, from),
435
- sourceAssetId,
436
403
  sourceBalanceRaw: sourceAmount.sourceBalanceRaw,
404
+ sourceChainId: sourceAmount.sourceChainId,
405
+ sourceTokenAddress: sourceAmount.sourceTokenAddress,
437
406
  sourceTokenAmount: sourceAmount.sourceAmountRaw,
438
407
  targetAmountMinimum: '0',
439
408
  targetChainId: destinationToken.chainId,
@@ -444,26 +413,16 @@ function buildPostQuoteRequests({ accountOverride, atomic, destinationToken, fro
444
413
  // Multiple token support would require multiple quotes for each required token.
445
414
  return [request];
446
415
  }
447
- async function refreshPaymentTokenBalance({ from, messenger, paymentToken, signal, sourceAccount, transactionId, updateTransactionData, }) {
416
+ async function refreshPaymentTokenBalance({ from, messenger, paymentToken, signal, transactionId, updateTransactionData, }) {
448
417
  if (!paymentToken) {
449
418
  return undefined;
450
419
  }
451
- if (sourceAccount) {
452
- return refreshNonEvmPaymentTokenBalance({
453
- messenger,
454
- paymentToken,
455
- sourceAccount,
456
- transactionId,
457
- updateTransactionData,
458
- });
459
- }
460
- const evmPaymentToken = getEvmPaymentToken(paymentToken);
461
420
  try {
462
- const fiatRates = getTokenFiatRate(messenger, evmPaymentToken.address, evmPaymentToken.chainId);
421
+ const fiatRates = getTokenFiatRate(messenger, paymentToken.address, paymentToken.chainId);
463
422
  if (!fiatRates) {
464
423
  return paymentToken;
465
424
  }
466
- const liveBalance = await getLiveTokenBalance(messenger, from, evmPaymentToken.chainId, evmPaymentToken.address);
425
+ const liveBalance = await getLiveTokenBalance(messenger, from, paymentToken.chainId, paymentToken.address);
467
426
  if (signal.aborted) {
468
427
  log('Payment token balance refresh aborted', { transactionId });
469
428
  return paymentToken;
@@ -487,43 +446,11 @@ async function refreshPaymentTokenBalance({ from, messenger, paymentToken, signa
487
446
  return paymentToken;
488
447
  }
489
448
  }
490
- /**
491
- * Re-read a non-EVM payment token balance and fiat value from
492
- * `AssetsController` state.
493
- *
494
- * @param request - Request parameters.
495
- * @param request.messenger - Controller messenger.
496
- * @param request.paymentToken - Non-EVM payment token to refresh.
497
- * @param request.sourceAccount - Account holding the payment token.
498
- * @param request.transactionId - ID of the transaction.
499
- * @param request.updateTransactionData - Callback to update transaction data.
500
- * @returns The refreshed payment token, or the original token if no fiat rate is available.
501
- */
502
- function refreshNonEvmPaymentTokenBalance({ messenger, paymentToken, sourceAccount, transactionId, updateTransactionData, }) {
503
- const fiatRates = getAssetFiatRate(messenger, paymentToken.assetId);
504
- if (!fiatRates) {
505
- return paymentToken;
506
- }
507
- const balance = getAssetBalance(messenger, sourceAccount.id, paymentToken.assetId);
508
- const { raw: balanceRaw, human: balanceHuman, usd: balanceUsd, fiat: balanceFiat, } = computeTokenAmounts(balance, paymentToken.decimals, fiatRates);
509
- const updatedToken = {
510
- ...paymentToken,
511
- balanceFiat,
512
- balanceHuman,
513
- balanceRaw,
514
- balanceUsd,
515
- };
516
- updateTransactionData(transactionId, (data) => {
517
- data.paymentToken = updatedToken;
518
- });
519
- log('Refreshed non-EVM payment token balance', { transactionId, balanceRaw });
520
- return updatedToken;
521
- }
522
449
  /**
523
450
  * Retrieve quotes for a transaction.
524
451
  *
525
452
  * @param transaction - Transaction metadata.
526
- * @param from - Sending wallet address (`txParams.from` for post-quote, otherwise the EVM account override or `txParams.from`).
453
+ * @param from - Sending wallet address (`txParams.from` for post-quote, otherwise `accountOverride ?? txParams.from`).
527
454
  * @param requests - Quote requests.
528
455
  * @param paymentToken - Selected payment token, if any.
529
456
  * @param isQuoteRequired - Whether a quote is always required for the transaction.
@@ -542,15 +469,14 @@ async function getQuotes(transaction, from, requests, paymentToken, isQuoteRequi
542
469
  transactionId,
543
470
  });
544
471
  });
545
- if (!requests.length && !fiatPaymentMethod) {
472
+ if (!requests?.length && !fiatPaymentMethod) {
546
473
  // A selected payment token with no conversion requests means the route is
547
474
  // direct. Return an explicit no-op quote so clients and the publish hook
548
475
  // can distinguish "no conversion needed" from "quote needed but missing".
549
476
  // Not applicable when a quote is always required, as an empty requests
550
477
  // list then means the source amounts could not be calculated.
551
- const evmPaymentToken = getEvmPaymentToken(paymentToken);
552
- const noOpQuote = evmPaymentToken && !isQuoteRequired
553
- ? buildNoOpQuote(from, evmPaymentToken)
478
+ const noOpQuote = paymentToken && !isQuoteRequired
479
+ ? buildNoOpQuote(from, paymentToken)
554
480
  : undefined;
555
481
  if (noOpQuote) {
556
482
  log('Built no-op quote for direct route', { transactionId });