@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.
- package/CHANGELOG.md +3 -20
- package/dist/TransactionPayController.d.ts.map +1 -1
- package/dist/TransactionPayController.js +5 -4
- package/dist/TransactionPayController.js.map +1 -1
- package/dist/actions/update-payment-token.d.ts.map +1 -1
- package/dist/actions/update-payment-token.js +22 -51
- package/dist/actions/update-payment-token.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/strategy/across/AcrossStrategy.d.ts.map +1 -1
- package/dist/strategy/across/AcrossStrategy.js +1 -6
- package/dist/strategy/across/AcrossStrategy.js.map +1 -1
- package/dist/strategy/across/across-quotes.js +4 -7
- package/dist/strategy/across/across-quotes.js.map +1 -1
- package/dist/strategy/across/across-submit.d.ts.map +1 -1
- package/dist/strategy/across/across-submit.js +1 -3
- package/dist/strategy/across/across-submit.js.map +1 -1
- package/dist/strategy/fiat/FiatStrategy.d.ts +0 -1
- package/dist/strategy/fiat/FiatStrategy.d.ts.map +1 -1
- package/dist/strategy/fiat/FiatStrategy.js +0 -4
- package/dist/strategy/fiat/FiatStrategy.js.map +1 -1
- package/dist/strategy/fiat/fiat-direct-musd.d.ts.map +1 -1
- package/dist/strategy/fiat/fiat-direct-musd.js +2 -4
- package/dist/strategy/fiat/fiat-direct-musd.js.map +1 -1
- package/dist/strategy/fiat/fiat-quotes.d.ts.map +1 -1
- package/dist/strategy/fiat/fiat-quotes.js +2 -4
- package/dist/strategy/fiat/fiat-quotes.js.map +1 -1
- package/dist/strategy/fiat/fiat-submit.d.ts.map +1 -1
- package/dist/strategy/fiat/fiat-submit.js +2 -5
- package/dist/strategy/fiat/fiat-submit.js.map +1 -1
- package/dist/strategy/fiat/fiat-test-funding.d.ts.map +1 -1
- package/dist/strategy/fiat/fiat-test-funding.js +5 -3
- package/dist/strategy/fiat/fiat-test-funding.js.map +1 -1
- package/dist/strategy/relay/RelayStrategy.d.ts +0 -4
- package/dist/strategy/relay/RelayStrategy.d.ts.map +1 -1
- package/dist/strategy/relay/RelayStrategy.js +1 -6
- package/dist/strategy/relay/RelayStrategy.js.map +1 -1
- package/dist/strategy/relay/relay-quotes.d.ts.map +1 -1
- package/dist/strategy/relay/relay-quotes.js +6 -10
- package/dist/strategy/relay/relay-quotes.js.map +1 -1
- package/dist/strategy/relay/relay-submit-execute.d.ts.map +1 -1
- package/dist/strategy/relay/relay-submit-execute.js +1 -3
- package/dist/strategy/relay/relay-submit-execute.js.map +1 -1
- package/dist/strategy/relay/relay-submit.d.ts.map +1 -1
- package/dist/strategy/relay/relay-submit.js +6 -8
- package/dist/strategy/relay/relay-submit.js.map +1 -1
- package/dist/strategy/relay/relay-validation.d.ts.map +1 -1
- package/dist/strategy/relay/relay-validation.js +2 -5
- package/dist/strategy/relay/relay-validation.js.map +1 -1
- package/dist/strategy/server/ServerStrategy.d.ts.map +1 -1
- package/dist/strategy/server/ServerStrategy.js +0 -4
- package/dist/strategy/server/ServerStrategy.js.map +1 -1
- package/dist/strategy/server/server-quotes.js +4 -7
- package/dist/strategy/server/server-quotes.js.map +1 -1
- package/dist/strategy/server/server-submit.d.ts.map +1 -1
- package/dist/strategy/server/server-submit.js +6 -9
- package/dist/strategy/server/server-submit.js.map +1 -1
- package/dist/strategy/server/server-validation.d.ts.map +1 -1
- package/dist/strategy/server/server-validation.js +1 -3
- package/dist/strategy/server/server-validation.js.map +1 -1
- package/dist/types.d.ts +25 -60
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/account.d.ts +0 -54
- package/dist/utils/account.d.ts.map +1 -1
- package/dist/utils/account.js +0 -124
- package/dist/utils/account.js.map +1 -1
- package/dist/utils/gas-payment.d.ts +3 -12
- package/dist/utils/gas-payment.d.ts.map +1 -1
- package/dist/utils/gas-payment.js +2 -2
- package/dist/utils/gas-payment.js.map +1 -1
- package/dist/utils/max-gas-station.d.ts.map +1 -1
- package/dist/utils/max-gas-station.js +5 -9
- package/dist/utils/max-gas-station.js.map +1 -1
- package/dist/utils/max-quote.js +2 -3
- package/dist/utils/max-quote.js.map +1 -1
- package/dist/utils/no-op-quote.d.ts +3 -3
- package/dist/utils/no-op-quote.d.ts.map +1 -1
- package/dist/utils/no-op-quote.js +3 -4
- package/dist/utils/no-op-quote.js.map +1 -1
- package/dist/utils/perps.d.ts.map +1 -1
- package/dist/utils/perps.js +2 -5
- package/dist/utils/perps.js.map +1 -1
- package/dist/utils/post-quote.js +3 -4
- package/dist/utils/post-quote.js.map +1 -1
- package/dist/utils/quotes.d.ts.map +1 -1
- package/dist/utils/quotes.js +25 -99
- package/dist/utils/quotes.js.map +1 -1
- package/dist/utils/source-amounts.d.ts.map +1 -1
- package/dist/utils/source-amounts.js +4 -25
- package/dist/utils/source-amounts.js.map +1 -1
- package/dist/utils/token.d.ts +1 -84
- package/dist/utils/token.d.ts.map +1 -1
- package/dist/utils/token.js +13 -144
- package/dist/utils/token.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +11 -14
- package/dist/utils/validation.js.map +1 -1
- 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 {
|
|
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
|
|
14
|
+
* @param paymentToken - Selected payment token.
|
|
15
15
|
* @returns The no-op quote.
|
|
16
16
|
*/
|
|
17
|
-
export declare function buildNoOpQuote(from: Hex, paymentToken:
|
|
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,
|
|
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
|
|
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;
|
|
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;
|
|
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"}
|
package/dist/utils/perps.js
CHANGED
|
@@ -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
|
-
|
|
96
|
-
|
|
92
|
+
sourceChainId: CHAIN_ID_HYPERCORE,
|
|
93
|
+
sourceTokenAddress: hypercoreUsdcAddress,
|
|
97
94
|
sourceTokenAmount: request.sourceTokenAmount
|
|
98
95
|
? toHypercoreAmount(request.sourceTokenAmount)
|
|
99
96
|
: request.sourceTokenAmount,
|
package/dist/utils/perps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"perps.js","sourceRoot":"","sources":["../../src/utils/perps.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,EACvB,aAAa,GACd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,
|
|
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"]}
|
package/dist/utils/post-quote.js
CHANGED
|
@@ -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 {
|
|
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
|
|
73
|
-
|
|
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":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,
|
|
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,
|
|
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"}
|
package/dist/utils/quotes.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { TransactionStatus } from '@metamask/transaction-controller';
|
|
2
|
-
import { createModuleLogger
|
|
2
|
+
import { createModuleLogger } from '@metamask/utils';
|
|
3
3
|
import { TransactionPayStrategy } from '../constants.js';
|
|
4
4
|
import { projectLogger } from '../logger.js';
|
|
5
|
-
import { accountRequiresMfa, accountSupports7702
|
|
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 {
|
|
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
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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,
|
|
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:
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
552
|
-
|
|
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 });
|