@metamask/transaction-pay-controller 22.1.0 → 22.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -1
- package/dist/TransactionPayController.cjs +7 -7
- package/dist/TransactionPayController.cjs.map +1 -1
- package/dist/TransactionPayController.d.cts.map +1 -1
- package/dist/TransactionPayController.d.mts.map +1 -1
- package/dist/TransactionPayController.mjs +7 -7
- package/dist/TransactionPayController.mjs.map +1 -1
- package/dist/constants.cjs +10 -2
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +3 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +3 -0
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +9 -1
- package/dist/constants.mjs.map +1 -1
- package/dist/strategy/across/AcrossStrategy.cjs +6 -5
- package/dist/strategy/across/AcrossStrategy.cjs.map +1 -1
- package/dist/strategy/across/AcrossStrategy.d.cts.map +1 -1
- package/dist/strategy/across/AcrossStrategy.d.mts.map +1 -1
- package/dist/strategy/across/AcrossStrategy.mjs +6 -5
- package/dist/strategy/across/AcrossStrategy.mjs.map +1 -1
- package/dist/strategy/across/authorization-list.cjs +25 -0
- package/dist/strategy/across/authorization-list.cjs.map +1 -0
- package/dist/strategy/across/authorization-list.d.cts +16 -0
- package/dist/strategy/across/authorization-list.d.cts.map +1 -0
- package/dist/strategy/across/authorization-list.d.mts +16 -0
- package/dist/strategy/across/authorization-list.d.mts.map +1 -0
- package/dist/strategy/across/authorization-list.mjs +21 -0
- package/dist/strategy/across/authorization-list.mjs.map +1 -0
- package/dist/strategy/across/requests.cjs +1 -0
- package/dist/strategy/across/requests.cjs.map +1 -1
- package/dist/strategy/across/requests.d.cts.map +1 -1
- package/dist/strategy/across/requests.d.mts.map +1 -1
- package/dist/strategy/across/requests.mjs +1 -0
- package/dist/strategy/across/requests.mjs.map +1 -1
- package/dist/strategy/fiat/constants.cjs +5 -6
- package/dist/strategy/fiat/constants.cjs.map +1 -1
- package/dist/strategy/fiat/constants.d.cts +1 -2
- package/dist/strategy/fiat/constants.d.cts.map +1 -1
- package/dist/strategy/fiat/constants.d.mts +1 -2
- package/dist/strategy/fiat/constants.d.mts.map +1 -1
- package/dist/strategy/fiat/constants.mjs +5 -6
- package/dist/strategy/fiat/constants.mjs.map +1 -1
- package/dist/strategy/fiat/fiat-quotes.cjs +8 -7
- package/dist/strategy/fiat/fiat-quotes.cjs.map +1 -1
- package/dist/strategy/fiat/fiat-quotes.d.cts.map +1 -1
- package/dist/strategy/fiat/fiat-quotes.d.mts.map +1 -1
- package/dist/strategy/fiat/fiat-quotes.mjs +9 -8
- package/dist/strategy/fiat/fiat-quotes.mjs.map +1 -1
- package/dist/strategy/fiat/fiat-submit.cjs +23 -20
- package/dist/strategy/fiat/fiat-submit.cjs.map +1 -1
- package/dist/strategy/fiat/fiat-submit.d.cts.map +1 -1
- package/dist/strategy/fiat/fiat-submit.d.mts.map +1 -1
- package/dist/strategy/fiat/fiat-submit.mjs +23 -20
- package/dist/strategy/fiat/fiat-submit.mjs.map +1 -1
- package/dist/strategy/fiat/utils.cjs +10 -9
- package/dist/strategy/fiat/utils.cjs.map +1 -1
- package/dist/strategy/fiat/utils.d.cts +4 -3
- package/dist/strategy/fiat/utils.d.cts.map +1 -1
- package/dist/strategy/fiat/utils.d.mts +4 -3
- package/dist/strategy/fiat/utils.d.mts.map +1 -1
- package/dist/strategy/fiat/utils.mjs +9 -8
- package/dist/strategy/fiat/utils.mjs.map +1 -1
- package/dist/strategy/relay/relay-quotes.cjs +18 -1
- package/dist/strategy/relay/relay-quotes.cjs.map +1 -1
- package/dist/strategy/relay/relay-quotes.mjs +18 -1
- package/dist/strategy/relay/relay-quotes.mjs.map +1 -1
- package/dist/strategy/relay/relay-submit.cjs +37 -7
- package/dist/strategy/relay/relay-submit.cjs.map +1 -1
- package/dist/strategy/relay/relay-submit.mjs +37 -7
- package/dist/strategy/relay/relay-submit.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +8 -6
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +8 -6
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils/feature-flags.cjs +29 -5
- package/dist/utils/feature-flags.cjs.map +1 -1
- package/dist/utils/feature-flags.d.cts +15 -0
- package/dist/utils/feature-flags.d.cts.map +1 -1
- package/dist/utils/feature-flags.d.mts +15 -0
- package/dist/utils/feature-flags.d.mts.map +1 -1
- package/dist/utils/feature-flags.mjs +23 -0
- package/dist/utils/feature-flags.mjs.map +1 -1
- package/dist/utils/token.cjs +24 -1
- package/dist/utils/token.cjs.map +1 -1
- package/dist/utils/token.d.cts +14 -1
- package/dist/utils/token.d.cts.map +1 -1
- package/dist/utils/token.d.mts +14 -1
- package/dist/utils/token.d.mts.map +1 -1
- package/dist/utils/token.mjs +23 -1
- package/dist/utils/token.mjs.map +1 -1
- package/dist/utils/totals.cjs +8 -2
- package/dist/utils/totals.cjs.map +1 -1
- package/dist/utils/totals.d.cts.map +1 -1
- package/dist/utils/totals.d.mts.map +1 -1
- package/dist/utils/totals.mjs +8 -2
- package/dist/utils/totals.mjs.map +1 -1
- package/package.json +11 -11
|
@@ -5,6 +5,7 @@ const ramps_controller_1 = require("@metamask/ramps-controller");
|
|
|
5
5
|
const utils_1 = require("@metamask/utils");
|
|
6
6
|
const bignumber_js_1 = require("bignumber.js");
|
|
7
7
|
const logger_1 = require("../../logger.cjs");
|
|
8
|
+
const token_1 = require("../../utils/token.cjs");
|
|
8
9
|
const relay_quotes_1 = require("../relay/relay-quotes.cjs");
|
|
9
10
|
const relay_submit_1 = require("../relay/relay-submit.cjs");
|
|
10
11
|
const utils_2 = require("./utils.cjs");
|
|
@@ -36,23 +37,24 @@ async function submitFiatQuotes(request) {
|
|
|
36
37
|
throw new Error('Missing wallet address for fiat submission');
|
|
37
38
|
}
|
|
38
39
|
const state = messenger.call('TransactionPayController:getState');
|
|
39
|
-
const
|
|
40
|
+
const fiatPayment = state.transactionData[transactionId]?.fiatPayment;
|
|
41
|
+
const orderId = fiatPayment?.orderId;
|
|
40
42
|
if (!orderId) {
|
|
41
43
|
throw new Error('Missing order ID for fiat submission');
|
|
42
44
|
}
|
|
43
|
-
const
|
|
44
|
-
if (!
|
|
45
|
-
throw new Error(
|
|
45
|
+
const providerCode = extractProviderCode(fiatPayment?.rampsQuote?.provider);
|
|
46
|
+
if (!providerCode) {
|
|
47
|
+
throw new Error('Missing provider code for fiat submission');
|
|
46
48
|
}
|
|
47
49
|
log('Starting fiat order polling', {
|
|
48
50
|
orderId,
|
|
49
|
-
providerCode
|
|
51
|
+
providerCode,
|
|
50
52
|
transactionId,
|
|
51
53
|
});
|
|
52
54
|
const order = await waitForOrderCompletion({
|
|
53
55
|
messenger,
|
|
54
|
-
orderCode:
|
|
55
|
-
providerCode
|
|
56
|
+
orderCode: orderId,
|
|
57
|
+
providerCode,
|
|
56
58
|
transactionId,
|
|
57
59
|
walletAddress,
|
|
58
60
|
});
|
|
@@ -65,17 +67,17 @@ async function submitFiatQuotes(request) {
|
|
|
65
67
|
}
|
|
66
68
|
exports.submitFiatQuotes = submitFiatQuotes;
|
|
67
69
|
/**
|
|
68
|
-
*
|
|
70
|
+
* Extracts the provider code from a ramps provider string.
|
|
69
71
|
*
|
|
70
|
-
* @param
|
|
71
|
-
* @returns The
|
|
72
|
+
* @param provider - Provider string (e.g. `/providers/transak-native`).
|
|
73
|
+
* @returns The provider code, or `null` if the format is invalid.
|
|
72
74
|
*/
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
if (parts.length < 4 || parts[0] !== 'providers' || parts[2] !== 'orders') {
|
|
75
|
+
function extractProviderCode(provider) {
|
|
76
|
+
if (!provider) {
|
|
76
77
|
return null;
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
+
const parts = provider.split('/').filter(Boolean);
|
|
80
|
+
return parts.length >= 2 && parts[0] === 'providers' ? parts[1] : null;
|
|
79
81
|
}
|
|
80
82
|
/**
|
|
81
83
|
* Converts the order's human-readable crypto amount to a raw token amount.
|
|
@@ -109,7 +111,7 @@ function getRawSourceAmountFromOrder({ cryptoAmount, decimals, }) {
|
|
|
109
111
|
*/
|
|
110
112
|
function validateOrderAsset({ expectedAsset, orderCrypto, transactionId, }) {
|
|
111
113
|
const orderAssetId = orderCrypto?.assetId?.toLowerCase();
|
|
112
|
-
const expectedAssetId = expectedAsset.
|
|
114
|
+
const expectedAssetId = (0, token_1.buildCaipAssetType)(expectedAsset.chainId, expectedAsset.address).toLowerCase();
|
|
113
115
|
const expectedChainId = expectedAssetId.split('/')[0];
|
|
114
116
|
const orderChainId = orderCrypto?.chainId?.toLowerCase();
|
|
115
117
|
if (orderAssetId && orderAssetId !== expectedAssetId) {
|
|
@@ -210,18 +212,19 @@ async function submitRelayAfterFiatCompletion({ order, request, }) {
|
|
|
210
212
|
if (quotes.length > 1) {
|
|
211
213
|
throw new Error('Multiple fiat quotes are not supported for submission');
|
|
212
214
|
}
|
|
213
|
-
const fiatAsset = (0, utils_2.deriveFiatAssetForFiatPayment)(transaction);
|
|
214
|
-
if (!fiatAsset) {
|
|
215
|
-
throw new Error(`Missing fiat asset mapping for transaction type: ${String(transaction.type)}`);
|
|
216
|
-
}
|
|
215
|
+
const fiatAsset = (0, utils_2.deriveFiatAssetForFiatPayment)(transaction, messenger);
|
|
217
216
|
validateOrderAsset({
|
|
218
217
|
expectedAsset: fiatAsset,
|
|
219
218
|
orderCrypto: order.cryptoCurrency,
|
|
220
219
|
transactionId,
|
|
221
220
|
});
|
|
221
|
+
const tokenInfo = (0, token_1.getTokenInfo)(messenger, fiatAsset.address, fiatAsset.chainId);
|
|
222
|
+
if (!tokenInfo) {
|
|
223
|
+
throw new Error(`Unable to resolve token info for fiat asset ${fiatAsset.address} on chain ${fiatAsset.chainId}`);
|
|
224
|
+
}
|
|
222
225
|
const sourceAmountRaw = getRawSourceAmountFromOrder({
|
|
223
226
|
cryptoAmount: order.cryptoAmount,
|
|
224
|
-
decimals:
|
|
227
|
+
decimals: tokenInfo.decimals,
|
|
225
228
|
});
|
|
226
229
|
const baseRequest = quotes[0].request;
|
|
227
230
|
const relayRequest = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fiat-submit.cjs","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RampsOrder,\n RampsOrderCryptoCurrency,\n} from '@metamask/ramps-controller';\nimport { RampsOrderStatus } from '@metamask/ramps-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategy,\n PayStrategyExecuteRequest,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../../types';\nimport { getRelayQuotes } from '../relay/relay-quotes';\nimport { submitRelayQuotes } from '../relay/relay-submit';\nimport type { RelayQuote } from '../relay/types';\nimport type { TransactionPayFiatAsset } from './constants';\nimport type { FiatQuote } from './types';\nimport { deriveFiatAssetForFiatPayment } from './utils';\n\nconst log = createModuleLogger(projectLogger, 'fiat-submit');\n\nconst ORDER_POLL_INTERVAL_MS = 1000;\nconst ORDER_POLL_TIMEOUT_MS = 10 * 60 * 1000;\nconst MAX_SLIPPAGE_PERCENT = 5;\n\nconst TERMINAL_FAILURE_STATUSES: RampsOrderStatus[] = [\n RampsOrderStatus.Cancelled,\n RampsOrderStatus.Failed,\n RampsOrderStatus.IdExpired,\n];\n\n/**\n * Submits fiat strategy quotes by polling the on-ramp order until completion,\n * then re-quoting and submitting the relay leg with the settled crypto amount.\n *\n * @param request - Strategy execute request containing fiat quotes, messenger, and transaction metadata.\n * @param request.messenger - Controller messenger for cross-controller calls.\n * @param request.quotes - Fiat quotes to execute (exactly one expected).\n * @param request.transaction - Original transaction metadata.\n * @param request.isSmartTransaction - Callback to check smart transaction eligibility.\n * @returns An object containing the relay transaction hash if available.\n */\nexport async function submitFiatQuotes(\n request: PayStrategyExecuteRequest<FiatQuote>,\n): ReturnType<PayStrategy<FiatQuote>['execute']> {\n const { messenger, transaction } = request;\n const transactionId = transaction.id;\n const walletAddress = transaction.txParams.from as Hex | undefined;\n\n if (!walletAddress) {\n throw new Error('Missing wallet address for fiat submission');\n }\n\n const state = messenger.call('TransactionPayController:getState');\n const orderId = state.transactionData[transactionId]?.fiatPayment?.orderId;\n\n if (!orderId) {\n throw new Error('Missing order ID for fiat submission');\n }\n\n const parsedOrder = parseOrderId(orderId);\n\n if (!parsedOrder) {\n throw new Error(`Invalid order ID format: ${orderId}`);\n }\n\n log('Starting fiat order polling', {\n orderId,\n providerCode: parsedOrder.providerCode,\n transactionId,\n });\n\n const order = await waitForOrderCompletion({\n messenger,\n orderCode: parsedOrder.orderCode,\n providerCode: parsedOrder.providerCode,\n transactionId,\n walletAddress,\n });\n\n log('Fiat order completed', {\n cryptoAmount: order.cryptoAmount,\n orderId,\n transactionId,\n });\n\n return await submitRelayAfterFiatCompletion({ order, request });\n}\n\n/**\n * Parses a normalized order ID string into its provider and order components.\n *\n * @param orderId - Order ID in `/providers/{providerCode}/orders/{orderCode}` format.\n * @returns The parsed provider and order codes, or `null` if the format is invalid.\n */\nfunction parseOrderId(\n orderId: string,\n): { orderCode: string; providerCode: string } | null {\n const parts = orderId.split('/').filter(Boolean);\n\n if (parts.length < 4 || parts[0] !== 'providers' || parts[2] !== 'orders') {\n return null;\n }\n\n return { orderCode: parts[3], providerCode: parts[1] };\n}\n\n/**\n * Converts the order's human-readable crypto amount to a raw token amount.\n *\n * @param options - The conversion options.\n * @param options.cryptoAmount - Human-readable crypto amount from the completed order.\n * @param options.decimals - Token decimals for the fiat asset.\n * @returns The raw token amount as a string.\n */\nfunction getRawSourceAmountFromOrder({\n cryptoAmount,\n decimals,\n}: {\n cryptoAmount: RampsOrder['cryptoAmount'];\n decimals: number;\n}): string {\n const normalizedAmount = new BigNumber(String(cryptoAmount));\n\n if (!normalizedAmount.isFinite() || normalizedAmount.lte(0)) {\n throw new Error(\n `Invalid fiat order crypto amount: ${String(cryptoAmount)}`,\n );\n }\n\n const rawAmount = normalizedAmount\n .shiftedBy(decimals)\n .decimalPlaces(0, BigNumber.ROUND_DOWN)\n .toFixed(0);\n\n if (!new BigNumber(rawAmount).gt(0)) {\n throw new Error('Computed fiat order source amount is not positive');\n }\n\n return rawAmount;\n}\n\n/**\n * Validates that the completed order's crypto asset matches the expected fiat asset.\n *\n * @param options - The validation options.\n * @param options.expectedAsset - The expected fiat asset derived from the transaction type.\n * @param options.orderCrypto - The crypto currency information from the completed order.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nfunction validateOrderAsset({\n expectedAsset,\n orderCrypto,\n transactionId,\n}: {\n expectedAsset: TransactionPayFiatAsset;\n orderCrypto: RampsOrderCryptoCurrency | undefined;\n transactionId: string;\n}): void {\n const orderAssetId = orderCrypto?.assetId?.toLowerCase();\n const expectedAssetId = expectedAsset.caipAssetId.toLowerCase();\n const expectedChainId = expectedAssetId.split('/')[0];\n const orderChainId = orderCrypto?.chainId?.toLowerCase();\n\n if (orderAssetId && orderAssetId !== expectedAssetId) {\n throw new Error(\n `Fiat order asset mismatch for transaction ${transactionId}: ` +\n `expected ${expectedAssetId}, got ${orderAssetId}`,\n );\n }\n\n if (orderChainId && orderChainId !== expectedChainId) {\n throw new Error(\n `Fiat order chain mismatch for transaction ${transactionId}: ` +\n `expected ${expectedChainId}, got ${orderChainId}`,\n );\n }\n}\n\n/**\n * Validates that the re-quoted relay target output hasn't drifted beyond the\n * acceptable slippage threshold compared to the original quote shown to the user.\n *\n * @param options - The validation options.\n * @param options.originalTargetRaw - Raw target amount from the original relay quote.\n * @param options.reQuotedTargetRaw - Raw target amount from the re-quoted relay.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nfunction validateRelaySlippage({\n originalTargetRaw,\n reQuotedTargetRaw,\n transactionId,\n}: {\n originalTargetRaw: string;\n reQuotedTargetRaw: string;\n transactionId: string;\n}): void {\n const original = new BigNumber(originalTargetRaw);\n const reQuoted = new BigNumber(reQuotedTargetRaw);\n\n if (!original.gt(0) || !reQuoted.gt(0)) {\n return;\n }\n\n const slippagePercent = original\n .minus(reQuoted)\n .dividedBy(original)\n .multipliedBy(100);\n\n log('Relay slippage check', {\n originalTargetRaw,\n reQuotedTargetRaw,\n slippagePercent: slippagePercent.toFixed(2),\n transactionId,\n });\n\n if (slippagePercent.gt(MAX_SLIPPAGE_PERCENT)) {\n throw new Error(\n `Relay re-quote slippage too high for transaction ` +\n `${slippagePercent.toFixed(2)}% exceeds ${MAX_SLIPPAGE_PERCENT}% max`,\n );\n }\n}\n\n/**\n * Polls the on-ramp order until it reaches a terminal status.\n *\n * @param options - The polling options.\n * @param options.messenger - Controller messenger for calling `RampsController:getOrder`.\n * @param options.orderCode - The order identifier within the provider.\n * @param options.providerCode - The on-ramp provider code (e.g. \"transak\").\n * @param options.transactionId - Transaction ID for logging.\n * @param options.walletAddress - Wallet address associated with the order.\n * @returns The completed order data.\n */\nasync function waitForOrderCompletion({\n messenger,\n orderCode,\n providerCode,\n transactionId,\n walletAddress,\n}: {\n messenger: TransactionPayControllerMessenger;\n orderCode: string;\n providerCode: string;\n transactionId: string;\n walletAddress: string;\n}): Promise<RampsOrder> {\n const startTime = Date.now();\n let lastStatus: string | undefined;\n\n while (true) {\n let order: RampsOrder | undefined;\n\n try {\n order = await messenger.call(\n 'RampsController:getOrder',\n providerCode,\n orderCode,\n walletAddress,\n );\n } catch (error) {\n log('Order polling network error', error);\n }\n\n if (order) {\n lastStatus = order.status;\n\n log('Polled fiat order', {\n orderStatus: order.status,\n providerCode,\n transactionId,\n });\n\n if (order.status === RampsOrderStatus.Completed) {\n return order;\n }\n\n if (TERMINAL_FAILURE_STATUSES.includes(order.status)) {\n throw new Error(`Fiat order ${order.status.toLowerCase()}`);\n }\n }\n\n if (Date.now() - startTime >= ORDER_POLL_TIMEOUT_MS) {\n throw new Error(\n `Fiat order polling timed out (last status: ${lastStatus})`,\n );\n }\n\n await new Promise((resolve) => setTimeout(resolve, ORDER_POLL_INTERVAL_MS));\n }\n}\n\n/**\n * Re-quotes and submits the relay leg using the settled amount from a completed fiat order.\n *\n * @param options - The submission options.\n * @param options.order - The completed on-ramp order containing the settled crypto amount.\n * @param options.request - The original fiat strategy execute request.\n * @returns An object containing the relay transaction hash if available.\n */\nasync function submitRelayAfterFiatCompletion({\n order,\n request,\n}: {\n order: RampsOrder;\n request: PayStrategyExecuteRequest<FiatQuote>;\n}): Promise<{ transactionHash?: Hex }> {\n const { messenger, quotes, transaction } = request;\n const transactionId = transaction.id;\n\n if (!quotes.length) {\n throw new Error('Missing fiat quote for relay submission');\n }\n\n if (quotes.length > 1) {\n throw new Error('Multiple fiat quotes are not supported for submission');\n }\n\n const fiatAsset = deriveFiatAssetForFiatPayment(transaction);\n if (!fiatAsset) {\n throw new Error(\n `Missing fiat asset mapping for transaction type: ${String(transaction.type)}`,\n );\n }\n\n validateOrderAsset({\n expectedAsset: fiatAsset,\n orderCrypto: order.cryptoCurrency,\n transactionId,\n });\n\n const sourceAmountRaw = getRawSourceAmountFromOrder({\n cryptoAmount: order.cryptoAmount,\n decimals: fiatAsset.decimals,\n });\n\n const baseRequest = quotes[0].request;\n const relayRequest: QuoteRequest = {\n ...baseRequest,\n isMaxAmount: true,\n isPostQuote: false,\n sourceBalanceRaw: sourceAmountRaw,\n sourceTokenAmount: sourceAmountRaw,\n };\n\n log('Re-quoting relay from completed fiat order', {\n completedOrderAmount: order.cryptoAmount,\n relayRequest,\n sourceAmountRaw,\n transactionId,\n });\n\n const relayQuotes = await getRelayQuotes({\n accountSupports7702: request.accountSupports7702,\n messenger,\n requests: [relayRequest],\n transaction,\n });\n\n if (!relayQuotes.length) {\n throw new Error('No relay quotes returned for completed fiat order');\n }\n\n const originalRelayQuote = quotes[0].original.relayQuote;\n validateRelaySlippage({\n originalTargetRaw: originalRelayQuote.details.currencyOut.amount,\n reQuotedTargetRaw: relayQuotes[0].original.details.currencyOut.amount,\n transactionId,\n });\n\n log('Received relay quotes for completed fiat order', {\n relayQuoteCount: relayQuotes.length,\n transactionId,\n });\n\n const relaySubmitRequest: PayStrategyExecuteRequest<RelayQuote> = {\n accountSupports7702: request.accountSupports7702,\n isSmartTransaction: request.isSmartTransaction,\n messenger,\n quotes: relayQuotes,\n transaction,\n };\n\n const relayResult = await submitRelayQuotes(relaySubmitRequest);\n\n log('Relay submission completed after fiat order', {\n relayResult,\n transactionId,\n });\n\n return relayResult;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fiat-submit.cjs","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RampsOrder,\n RampsOrderCryptoCurrency,\n} from '@metamask/ramps-controller';\nimport { RampsOrderStatus } from '@metamask/ramps-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategy,\n PayStrategyExecuteRequest,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../../types';\nimport { buildCaipAssetType, getTokenInfo } from '../../utils/token';\nimport { getRelayQuotes } from '../relay/relay-quotes';\nimport { submitRelayQuotes } from '../relay/relay-submit';\nimport type { RelayQuote } from '../relay/types';\nimport type { TransactionPayFiatAsset } from './constants';\nimport type { FiatQuote } from './types';\nimport { deriveFiatAssetForFiatPayment } from './utils';\n\nconst log = createModuleLogger(projectLogger, 'fiat-submit');\n\nconst ORDER_POLL_INTERVAL_MS = 1000;\nconst ORDER_POLL_TIMEOUT_MS = 10 * 60 * 1000;\nconst MAX_SLIPPAGE_PERCENT = 5;\n\nconst TERMINAL_FAILURE_STATUSES: RampsOrderStatus[] = [\n RampsOrderStatus.Cancelled,\n RampsOrderStatus.Failed,\n RampsOrderStatus.IdExpired,\n];\n\n/**\n * Submits fiat strategy quotes by polling the on-ramp order until completion,\n * then re-quoting and submitting the relay leg with the settled crypto amount.\n *\n * @param request - Strategy execute request containing fiat quotes, messenger, and transaction metadata.\n * @param request.messenger - Controller messenger for cross-controller calls.\n * @param request.quotes - Fiat quotes to execute (exactly one expected).\n * @param request.transaction - Original transaction metadata.\n * @param request.isSmartTransaction - Callback to check smart transaction eligibility.\n * @returns An object containing the relay transaction hash if available.\n */\nexport async function submitFiatQuotes(\n request: PayStrategyExecuteRequest<FiatQuote>,\n): ReturnType<PayStrategy<FiatQuote>['execute']> {\n const { messenger, transaction } = request;\n const transactionId = transaction.id;\n const walletAddress = transaction.txParams.from as Hex | undefined;\n\n if (!walletAddress) {\n throw new Error('Missing wallet address for fiat submission');\n }\n\n const state = messenger.call('TransactionPayController:getState');\n const fiatPayment = state.transactionData[transactionId]?.fiatPayment;\n const orderId = fiatPayment?.orderId;\n\n if (!orderId) {\n throw new Error('Missing order ID for fiat submission');\n }\n\n const providerCode = extractProviderCode(fiatPayment?.rampsQuote?.provider);\n\n if (!providerCode) {\n throw new Error('Missing provider code for fiat submission');\n }\n\n log('Starting fiat order polling', {\n orderId,\n providerCode,\n transactionId,\n });\n\n const order = await waitForOrderCompletion({\n messenger,\n orderCode: orderId,\n providerCode,\n transactionId,\n walletAddress,\n });\n\n log('Fiat order completed', {\n cryptoAmount: order.cryptoAmount,\n orderId,\n transactionId,\n });\n\n return await submitRelayAfterFiatCompletion({ order, request });\n}\n\n/**\n * Extracts the provider code from a ramps provider string.\n *\n * @param provider - Provider string (e.g. `/providers/transak-native`).\n * @returns The provider code, or `null` if the format is invalid.\n */\nfunction extractProviderCode(provider: string | undefined): string | null {\n if (!provider) {\n return null;\n }\n\n const parts = provider.split('/').filter(Boolean);\n return parts.length >= 2 && parts[0] === 'providers' ? parts[1] : null;\n}\n\n/**\n * Converts the order's human-readable crypto amount to a raw token amount.\n *\n * @param options - The conversion options.\n * @param options.cryptoAmount - Human-readable crypto amount from the completed order.\n * @param options.decimals - Token decimals for the fiat asset.\n * @returns The raw token amount as a string.\n */\nfunction getRawSourceAmountFromOrder({\n cryptoAmount,\n decimals,\n}: {\n cryptoAmount: RampsOrder['cryptoAmount'];\n decimals: number;\n}): string {\n const normalizedAmount = new BigNumber(String(cryptoAmount));\n\n if (!normalizedAmount.isFinite() || normalizedAmount.lte(0)) {\n throw new Error(\n `Invalid fiat order crypto amount: ${String(cryptoAmount)}`,\n );\n }\n\n const rawAmount = normalizedAmount\n .shiftedBy(decimals)\n .decimalPlaces(0, BigNumber.ROUND_DOWN)\n .toFixed(0);\n\n if (!new BigNumber(rawAmount).gt(0)) {\n throw new Error('Computed fiat order source amount is not positive');\n }\n\n return rawAmount;\n}\n\n/**\n * Validates that the completed order's crypto asset matches the expected fiat asset.\n *\n * @param options - The validation options.\n * @param options.expectedAsset - The expected fiat asset derived from the transaction type.\n * @param options.orderCrypto - The crypto currency information from the completed order.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nfunction validateOrderAsset({\n expectedAsset,\n orderCrypto,\n transactionId,\n}: {\n expectedAsset: TransactionPayFiatAsset;\n orderCrypto: RampsOrderCryptoCurrency | undefined;\n transactionId: string;\n}): void {\n const orderAssetId = orderCrypto?.assetId?.toLowerCase();\n const expectedAssetId = buildCaipAssetType(\n expectedAsset.chainId,\n expectedAsset.address,\n ).toLowerCase();\n const expectedChainId = expectedAssetId.split('/')[0];\n const orderChainId = orderCrypto?.chainId?.toLowerCase();\n\n if (orderAssetId && orderAssetId !== expectedAssetId) {\n throw new Error(\n `Fiat order asset mismatch for transaction ${transactionId}: ` +\n `expected ${expectedAssetId}, got ${orderAssetId}`,\n );\n }\n\n if (orderChainId && orderChainId !== expectedChainId) {\n throw new Error(\n `Fiat order chain mismatch for transaction ${transactionId}: ` +\n `expected ${expectedChainId}, got ${orderChainId}`,\n );\n }\n}\n\n/**\n * Validates that the re-quoted relay target output hasn't drifted beyond the\n * acceptable slippage threshold compared to the original quote shown to the user.\n *\n * @param options - The validation options.\n * @param options.originalTargetRaw - Raw target amount from the original relay quote.\n * @param options.reQuotedTargetRaw - Raw target amount from the re-quoted relay.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nfunction validateRelaySlippage({\n originalTargetRaw,\n reQuotedTargetRaw,\n transactionId,\n}: {\n originalTargetRaw: string;\n reQuotedTargetRaw: string;\n transactionId: string;\n}): void {\n const original = new BigNumber(originalTargetRaw);\n const reQuoted = new BigNumber(reQuotedTargetRaw);\n\n if (!original.gt(0) || !reQuoted.gt(0)) {\n return;\n }\n\n const slippagePercent = original\n .minus(reQuoted)\n .dividedBy(original)\n .multipliedBy(100);\n\n log('Relay slippage check', {\n originalTargetRaw,\n reQuotedTargetRaw,\n slippagePercent: slippagePercent.toFixed(2),\n transactionId,\n });\n\n if (slippagePercent.gt(MAX_SLIPPAGE_PERCENT)) {\n throw new Error(\n `Relay re-quote slippage too high for transaction ` +\n `${slippagePercent.toFixed(2)}% exceeds ${MAX_SLIPPAGE_PERCENT}% max`,\n );\n }\n}\n\n/**\n * Polls the on-ramp order until it reaches a terminal status.\n *\n * @param options - The polling options.\n * @param options.messenger - Controller messenger for calling `RampsController:getOrder`.\n * @param options.orderCode - The order identifier within the provider.\n * @param options.providerCode - The on-ramp provider code (e.g. \"transak\").\n * @param options.transactionId - Transaction ID for logging.\n * @param options.walletAddress - Wallet address associated with the order.\n * @returns The completed order data.\n */\nasync function waitForOrderCompletion({\n messenger,\n orderCode,\n providerCode,\n transactionId,\n walletAddress,\n}: {\n messenger: TransactionPayControllerMessenger;\n orderCode: string;\n providerCode: string;\n transactionId: string;\n walletAddress: string;\n}): Promise<RampsOrder> {\n const startTime = Date.now();\n let lastStatus: string | undefined;\n\n while (true) {\n let order: RampsOrder | undefined;\n\n try {\n order = await messenger.call(\n 'RampsController:getOrder',\n providerCode,\n orderCode,\n walletAddress,\n );\n } catch (error) {\n log('Order polling network error', error);\n }\n\n if (order) {\n lastStatus = order.status;\n\n log('Polled fiat order', {\n orderStatus: order.status,\n providerCode,\n transactionId,\n });\n\n if (order.status === RampsOrderStatus.Completed) {\n return order;\n }\n\n if (TERMINAL_FAILURE_STATUSES.includes(order.status)) {\n throw new Error(`Fiat order ${order.status.toLowerCase()}`);\n }\n }\n\n if (Date.now() - startTime >= ORDER_POLL_TIMEOUT_MS) {\n throw new Error(\n `Fiat order polling timed out (last status: ${lastStatus})`,\n );\n }\n\n await new Promise((resolve) => setTimeout(resolve, ORDER_POLL_INTERVAL_MS));\n }\n}\n\n/**\n * Re-quotes and submits the relay leg using the settled amount from a completed fiat order.\n *\n * @param options - The submission options.\n * @param options.order - The completed on-ramp order containing the settled crypto amount.\n * @param options.request - The original fiat strategy execute request.\n * @returns An object containing the relay transaction hash if available.\n */\nasync function submitRelayAfterFiatCompletion({\n order,\n request,\n}: {\n order: RampsOrder;\n request: PayStrategyExecuteRequest<FiatQuote>;\n}): Promise<{ transactionHash?: Hex }> {\n const { messenger, quotes, transaction } = request;\n const transactionId = transaction.id;\n\n if (!quotes.length) {\n throw new Error('Missing fiat quote for relay submission');\n }\n\n if (quotes.length > 1) {\n throw new Error('Multiple fiat quotes are not supported for submission');\n }\n\n const fiatAsset = deriveFiatAssetForFiatPayment(transaction, messenger);\n\n validateOrderAsset({\n expectedAsset: fiatAsset,\n orderCrypto: order.cryptoCurrency,\n transactionId,\n });\n\n const tokenInfo = getTokenInfo(\n messenger,\n fiatAsset.address,\n fiatAsset.chainId,\n );\n\n if (!tokenInfo) {\n throw new Error(\n `Unable to resolve token info for fiat asset ${fiatAsset.address} on chain ${fiatAsset.chainId}`,\n );\n }\n\n const sourceAmountRaw = getRawSourceAmountFromOrder({\n cryptoAmount: order.cryptoAmount,\n decimals: tokenInfo.decimals,\n });\n\n const baseRequest = quotes[0].request;\n const relayRequest: QuoteRequest = {\n ...baseRequest,\n isMaxAmount: true,\n isPostQuote: false,\n sourceBalanceRaw: sourceAmountRaw,\n sourceTokenAmount: sourceAmountRaw,\n };\n\n log('Re-quoting relay from completed fiat order', {\n completedOrderAmount: order.cryptoAmount,\n relayRequest,\n sourceAmountRaw,\n transactionId,\n });\n\n const relayQuotes = await getRelayQuotes({\n accountSupports7702: request.accountSupports7702,\n messenger,\n requests: [relayRequest],\n transaction,\n });\n\n if (!relayQuotes.length) {\n throw new Error('No relay quotes returned for completed fiat order');\n }\n\n const originalRelayQuote = quotes[0].original.relayQuote;\n validateRelaySlippage({\n originalTargetRaw: originalRelayQuote.details.currencyOut.amount,\n reQuotedTargetRaw: relayQuotes[0].original.details.currencyOut.amount,\n transactionId,\n });\n\n log('Received relay quotes for completed fiat order', {\n relayQuoteCount: relayQuotes.length,\n transactionId,\n });\n\n const relaySubmitRequest: PayStrategyExecuteRequest<RelayQuote> = {\n accountSupports7702: request.accountSupports7702,\n isSmartTransaction: request.isSmartTransaction,\n messenger,\n quotes: relayQuotes,\n transaction,\n };\n\n const relayResult = await submitRelayQuotes(relaySubmitRequest);\n\n log('Relay submission completed after fiat order', {\n relayResult,\n transactionId,\n });\n\n return relayResult;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fiat-submit.d.cts","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,WAAW,EACX,yBAAyB,EAG1B,wBAAoB;
|
|
1
|
+
{"version":3,"file":"fiat-submit.d.cts","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,WAAW,EACX,yBAAyB,EAG1B,wBAAoB;AAMrB,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAgB;AAezC;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,yBAAyB,CAAC,SAAS,CAAC,GAC5C,UAAU,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CA4C/C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fiat-submit.d.mts","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,WAAW,EACX,yBAAyB,EAG1B,wBAAoB;
|
|
1
|
+
{"version":3,"file":"fiat-submit.d.mts","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,WAAW,EACX,yBAAyB,EAG1B,wBAAoB;AAMrB,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAgB;AAezC;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,yBAAyB,CAAC,SAAS,CAAC,GAC5C,UAAU,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CA4C/C"}
|
|
@@ -2,6 +2,7 @@ import { RampsOrderStatus } from "@metamask/ramps-controller";
|
|
|
2
2
|
import { createModuleLogger } from "@metamask/utils";
|
|
3
3
|
import { BigNumber } from "bignumber.js";
|
|
4
4
|
import { projectLogger } from "../../logger.mjs";
|
|
5
|
+
import { buildCaipAssetType, getTokenInfo } from "../../utils/token.mjs";
|
|
5
6
|
import { getRelayQuotes } from "../relay/relay-quotes.mjs";
|
|
6
7
|
import { submitRelayQuotes } from "../relay/relay-submit.mjs";
|
|
7
8
|
import { deriveFiatAssetForFiatPayment } from "./utils.mjs";
|
|
@@ -33,23 +34,24 @@ export async function submitFiatQuotes(request) {
|
|
|
33
34
|
throw new Error('Missing wallet address for fiat submission');
|
|
34
35
|
}
|
|
35
36
|
const state = messenger.call('TransactionPayController:getState');
|
|
36
|
-
const
|
|
37
|
+
const fiatPayment = state.transactionData[transactionId]?.fiatPayment;
|
|
38
|
+
const orderId = fiatPayment?.orderId;
|
|
37
39
|
if (!orderId) {
|
|
38
40
|
throw new Error('Missing order ID for fiat submission');
|
|
39
41
|
}
|
|
40
|
-
const
|
|
41
|
-
if (!
|
|
42
|
-
throw new Error(
|
|
42
|
+
const providerCode = extractProviderCode(fiatPayment?.rampsQuote?.provider);
|
|
43
|
+
if (!providerCode) {
|
|
44
|
+
throw new Error('Missing provider code for fiat submission');
|
|
43
45
|
}
|
|
44
46
|
log('Starting fiat order polling', {
|
|
45
47
|
orderId,
|
|
46
|
-
providerCode
|
|
48
|
+
providerCode,
|
|
47
49
|
transactionId,
|
|
48
50
|
});
|
|
49
51
|
const order = await waitForOrderCompletion({
|
|
50
52
|
messenger,
|
|
51
|
-
orderCode:
|
|
52
|
-
providerCode
|
|
53
|
+
orderCode: orderId,
|
|
54
|
+
providerCode,
|
|
53
55
|
transactionId,
|
|
54
56
|
walletAddress,
|
|
55
57
|
});
|
|
@@ -61,17 +63,17 @@ export async function submitFiatQuotes(request) {
|
|
|
61
63
|
return await submitRelayAfterFiatCompletion({ order, request });
|
|
62
64
|
}
|
|
63
65
|
/**
|
|
64
|
-
*
|
|
66
|
+
* Extracts the provider code from a ramps provider string.
|
|
65
67
|
*
|
|
66
|
-
* @param
|
|
67
|
-
* @returns The
|
|
68
|
+
* @param provider - Provider string (e.g. `/providers/transak-native`).
|
|
69
|
+
* @returns The provider code, or `null` if the format is invalid.
|
|
68
70
|
*/
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
if (parts.length < 4 || parts[0] !== 'providers' || parts[2] !== 'orders') {
|
|
71
|
+
function extractProviderCode(provider) {
|
|
72
|
+
if (!provider) {
|
|
72
73
|
return null;
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
+
const parts = provider.split('/').filter(Boolean);
|
|
76
|
+
return parts.length >= 2 && parts[0] === 'providers' ? parts[1] : null;
|
|
75
77
|
}
|
|
76
78
|
/**
|
|
77
79
|
* Converts the order's human-readable crypto amount to a raw token amount.
|
|
@@ -105,7 +107,7 @@ function getRawSourceAmountFromOrder({ cryptoAmount, decimals, }) {
|
|
|
105
107
|
*/
|
|
106
108
|
function validateOrderAsset({ expectedAsset, orderCrypto, transactionId, }) {
|
|
107
109
|
const orderAssetId = orderCrypto?.assetId?.toLowerCase();
|
|
108
|
-
const expectedAssetId = expectedAsset.
|
|
110
|
+
const expectedAssetId = buildCaipAssetType(expectedAsset.chainId, expectedAsset.address).toLowerCase();
|
|
109
111
|
const expectedChainId = expectedAssetId.split('/')[0];
|
|
110
112
|
const orderChainId = orderCrypto?.chainId?.toLowerCase();
|
|
111
113
|
if (orderAssetId && orderAssetId !== expectedAssetId) {
|
|
@@ -206,18 +208,19 @@ async function submitRelayAfterFiatCompletion({ order, request, }) {
|
|
|
206
208
|
if (quotes.length > 1) {
|
|
207
209
|
throw new Error('Multiple fiat quotes are not supported for submission');
|
|
208
210
|
}
|
|
209
|
-
const fiatAsset = deriveFiatAssetForFiatPayment(transaction);
|
|
210
|
-
if (!fiatAsset) {
|
|
211
|
-
throw new Error(`Missing fiat asset mapping for transaction type: ${String(transaction.type)}`);
|
|
212
|
-
}
|
|
211
|
+
const fiatAsset = deriveFiatAssetForFiatPayment(transaction, messenger);
|
|
213
212
|
validateOrderAsset({
|
|
214
213
|
expectedAsset: fiatAsset,
|
|
215
214
|
orderCrypto: order.cryptoCurrency,
|
|
216
215
|
transactionId,
|
|
217
216
|
});
|
|
217
|
+
const tokenInfo = getTokenInfo(messenger, fiatAsset.address, fiatAsset.chainId);
|
|
218
|
+
if (!tokenInfo) {
|
|
219
|
+
throw new Error(`Unable to resolve token info for fiat asset ${fiatAsset.address} on chain ${fiatAsset.chainId}`);
|
|
220
|
+
}
|
|
218
221
|
const sourceAmountRaw = getRawSourceAmountFromOrder({
|
|
219
222
|
cryptoAmount: order.cryptoAmount,
|
|
220
|
-
decimals:
|
|
223
|
+
decimals: tokenInfo.decimals,
|
|
221
224
|
});
|
|
222
225
|
const baseRequest = quotes[0].request;
|
|
223
226
|
const relayRequest = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fiat-submit.mjs","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,mCAAmC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,wBAAwB;AACrD,OAAO,EAAE,SAAS,EAAE,qBAAqB;AAEzC,OAAO,EAAE,aAAa,EAAE,yBAAqB;AAO7C,OAAO,EAAE,cAAc,EAAE,kCAA8B;AACvD,OAAO,EAAE,iBAAiB,EAAE,kCAA8B;AAI1D,OAAO,EAAE,6BAA6B,EAAE,oBAAgB;AAExD,MAAM,GAAG,GAAG,kBAAkB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AAE7D,MAAM,sBAAsB,GAAG,IAAI,CAAC;AACpC,MAAM,qBAAqB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC7C,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAE/B,MAAM,yBAAyB,GAAuB;IACpD,gBAAgB,CAAC,SAAS;IAC1B,gBAAgB,CAAC,MAAM;IACvB,gBAAgB,CAAC,SAAS;CAC3B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAA6C;IAE7C,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAC3C,MAAM,aAAa,GAAG,WAAW,CAAC,EAAE,CAAC;IACrC,MAAM,aAAa,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAuB,CAAC;IAEnE,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC;IAE3E,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IAE1C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,GAAG,CAAC,6BAA6B,EAAE;QACjC,OAAO;QACP,YAAY,EAAE,WAAW,CAAC,YAAY;QACtC,aAAa;KACd,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,MAAM,sBAAsB,CAAC;QACzC,SAAS;QACT,SAAS,EAAE,WAAW,CAAC,SAAS;QAChC,YAAY,EAAE,WAAW,CAAC,YAAY;QACtC,aAAa;QACb,aAAa;KACd,CAAC,CAAC;IAEH,GAAG,CAAC,sBAAsB,EAAE;QAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,OAAO;QACP,aAAa;KACd,CAAC,CAAC;IAEH,OAAO,MAAM,8BAA8B,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;AAClE,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CACnB,OAAe;IAEf,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEjD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,WAAW,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACzD,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,2BAA2B,CAAC,EACnC,YAAY,EACZ,QAAQ,GAIT;IACC,MAAM,gBAAgB,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;IAE7D,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CACb,qCAAqC,MAAM,CAAC,YAAY,CAAC,EAAE,CAC5D,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,gBAAgB;SAC/B,SAAS,CAAC,QAAQ,CAAC;SACnB,aAAa,CAAC,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC;SACtC,OAAO,CAAC,CAAC,CAAC,CAAC;IAEd,IAAI,CAAC,IAAI,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,kBAAkB,CAAC,EAC1B,aAAa,EACb,WAAW,EACX,aAAa,GAKd;IACC,MAAM,YAAY,GAAG,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IACzD,MAAM,eAAe,GAAG,aAAa,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;IAChE,MAAM,eAAe,GAAG,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,YAAY,GAAG,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAEzD,IAAI,YAAY,IAAI,YAAY,KAAK,eAAe,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,6CAA6C,aAAa,IAAI;YAC5D,YAAY,eAAe,SAAS,YAAY,EAAE,CACrD,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,IAAI,YAAY,KAAK,eAAe,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,6CAA6C,aAAa,IAAI;YAC5D,YAAY,eAAe,SAAS,YAAY,EAAE,CACrD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,qBAAqB,CAAC,EAC7B,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,GAKd;IACC,MAAM,QAAQ,GAAG,IAAI,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,IAAI,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAElD,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,OAAO;IACT,CAAC;IAED,MAAM,eAAe,GAAG,QAAQ;SAC7B,KAAK,CAAC,QAAQ,CAAC;SACf,SAAS,CAAC,QAAQ,CAAC;SACnB,YAAY,CAAC,GAAG,CAAC,CAAC;IAErB,GAAG,CAAC,sBAAsB,EAAE;QAC1B,iBAAiB;QACjB,iBAAiB;QACjB,eAAe,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3C,aAAa;KACd,CAAC,CAAC;IAEH,IAAI,eAAe,CAAC,EAAE,CAAC,oBAAoB,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACb,mDAAmD;YACjD,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,oBAAoB,OAAO,CACxE,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,KAAK,UAAU,sBAAsB,CAAC,EACpC,SAAS,EACT,SAAS,EACT,YAAY,EACZ,aAAa,EACb,aAAa,GAOd;IACC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,UAA8B,CAAC;IAEnC,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,KAA6B,CAAC;QAElC,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,CAC1B,0BAA0B,EAC1B,YAAY,EACZ,SAAS,EACT,aAAa,CACd,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,KAAK,EAAE,CAAC;YACV,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC;YAE1B,GAAG,CAAC,mBAAmB,EAAE;gBACvB,WAAW,EAAE,KAAK,CAAC,MAAM;gBACzB,YAAY;gBACZ,aAAa;aACd,CAAC,CAAC;YAEH,IAAI,KAAK,CAAC,MAAM,KAAK,gBAAgB,CAAC,SAAS,EAAE,CAAC;gBAChD,OAAO,KAAK,CAAC;YACf,CAAC;YAED,IAAI,yBAAyB,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrD,MAAM,IAAI,KAAK,CAAC,cAAc,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;QAED,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,IAAI,qBAAqB,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CACb,8CAA8C,UAAU,GAAG,CAC5D,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,8BAA8B,CAAC,EAC5C,KAAK,EACL,OAAO,GAIR;IACC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IACnD,MAAM,aAAa,GAAG,WAAW,CAAC,EAAE,CAAC;IAErC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,SAAS,GAAG,6BAA6B,CAAC,WAAW,CAAC,CAAC;IAC7D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,oDAAoD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAC/E,CAAC;IACJ,CAAC;IAED,kBAAkB,CAAC;QACjB,aAAa,EAAE,SAAS;QACxB,WAAW,EAAE,KAAK,CAAC,cAAc;QACjC,aAAa;KACd,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,2BAA2B,CAAC;QAClD,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,QAAQ,EAAE,SAAS,CAAC,QAAQ;KAC7B,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACtC,MAAM,YAAY,GAAiB;QACjC,GAAG,WAAW;QACd,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,KAAK;QAClB,gBAAgB,EAAE,eAAe;QACjC,iBAAiB,EAAE,eAAe;KACnC,CAAC;IAEF,GAAG,CAAC,4CAA4C,EAAE;QAChD,oBAAoB,EAAE,KAAK,CAAC,YAAY;QACxC,YAAY;QACZ,eAAe;QACf,aAAa;KACd,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC;QACvC,mBAAmB,EAAE,OAAO,CAAC,mBAAmB;QAChD,SAAS;QACT,QAAQ,EAAE,CAAC,YAAY,CAAC;QACxB,WAAW;KACZ,CAAC,CAAC;IAEH,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,kBAAkB,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;IACzD,qBAAqB,CAAC;QACpB,iBAAiB,EAAE,kBAAkB,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM;QAChE,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM;QACrE,aAAa;KACd,CAAC,CAAC;IAEH,GAAG,CAAC,gDAAgD,EAAE;QACpD,eAAe,EAAE,WAAW,CAAC,MAAM;QACnC,aAAa;KACd,CAAC,CAAC;IAEH,MAAM,kBAAkB,GAA0C;QAChE,mBAAmB,EAAE,OAAO,CAAC,mBAAmB;QAChD,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,SAAS;QACT,MAAM,EAAE,WAAW;QACnB,WAAW;KACZ,CAAC;IAEF,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;IAEhE,GAAG,CAAC,6CAA6C,EAAE;QACjD,WAAW;QACX,aAAa;KACd,CAAC,CAAC;IAEH,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["import type {\n RampsOrder,\n RampsOrderCryptoCurrency,\n} from '@metamask/ramps-controller';\nimport { RampsOrderStatus } from '@metamask/ramps-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategy,\n PayStrategyExecuteRequest,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../../types';\nimport { getRelayQuotes } from '../relay/relay-quotes';\nimport { submitRelayQuotes } from '../relay/relay-submit';\nimport type { RelayQuote } from '../relay/types';\nimport type { TransactionPayFiatAsset } from './constants';\nimport type { FiatQuote } from './types';\nimport { deriveFiatAssetForFiatPayment } from './utils';\n\nconst log = createModuleLogger(projectLogger, 'fiat-submit');\n\nconst ORDER_POLL_INTERVAL_MS = 1000;\nconst ORDER_POLL_TIMEOUT_MS = 10 * 60 * 1000;\nconst MAX_SLIPPAGE_PERCENT = 5;\n\nconst TERMINAL_FAILURE_STATUSES: RampsOrderStatus[] = [\n RampsOrderStatus.Cancelled,\n RampsOrderStatus.Failed,\n RampsOrderStatus.IdExpired,\n];\n\n/**\n * Submits fiat strategy quotes by polling the on-ramp order until completion,\n * then re-quoting and submitting the relay leg with the settled crypto amount.\n *\n * @param request - Strategy execute request containing fiat quotes, messenger, and transaction metadata.\n * @param request.messenger - Controller messenger for cross-controller calls.\n * @param request.quotes - Fiat quotes to execute (exactly one expected).\n * @param request.transaction - Original transaction metadata.\n * @param request.isSmartTransaction - Callback to check smart transaction eligibility.\n * @returns An object containing the relay transaction hash if available.\n */\nexport async function submitFiatQuotes(\n request: PayStrategyExecuteRequest<FiatQuote>,\n): ReturnType<PayStrategy<FiatQuote>['execute']> {\n const { messenger, transaction } = request;\n const transactionId = transaction.id;\n const walletAddress = transaction.txParams.from as Hex | undefined;\n\n if (!walletAddress) {\n throw new Error('Missing wallet address for fiat submission');\n }\n\n const state = messenger.call('TransactionPayController:getState');\n const orderId = state.transactionData[transactionId]?.fiatPayment?.orderId;\n\n if (!orderId) {\n throw new Error('Missing order ID for fiat submission');\n }\n\n const parsedOrder = parseOrderId(orderId);\n\n if (!parsedOrder) {\n throw new Error(`Invalid order ID format: ${orderId}`);\n }\n\n log('Starting fiat order polling', {\n orderId,\n providerCode: parsedOrder.providerCode,\n transactionId,\n });\n\n const order = await waitForOrderCompletion({\n messenger,\n orderCode: parsedOrder.orderCode,\n providerCode: parsedOrder.providerCode,\n transactionId,\n walletAddress,\n });\n\n log('Fiat order completed', {\n cryptoAmount: order.cryptoAmount,\n orderId,\n transactionId,\n });\n\n return await submitRelayAfterFiatCompletion({ order, request });\n}\n\n/**\n * Parses a normalized order ID string into its provider and order components.\n *\n * @param orderId - Order ID in `/providers/{providerCode}/orders/{orderCode}` format.\n * @returns The parsed provider and order codes, or `null` if the format is invalid.\n */\nfunction parseOrderId(\n orderId: string,\n): { orderCode: string; providerCode: string } | null {\n const parts = orderId.split('/').filter(Boolean);\n\n if (parts.length < 4 || parts[0] !== 'providers' || parts[2] !== 'orders') {\n return null;\n }\n\n return { orderCode: parts[3], providerCode: parts[1] };\n}\n\n/**\n * Converts the order's human-readable crypto amount to a raw token amount.\n *\n * @param options - The conversion options.\n * @param options.cryptoAmount - Human-readable crypto amount from the completed order.\n * @param options.decimals - Token decimals for the fiat asset.\n * @returns The raw token amount as a string.\n */\nfunction getRawSourceAmountFromOrder({\n cryptoAmount,\n decimals,\n}: {\n cryptoAmount: RampsOrder['cryptoAmount'];\n decimals: number;\n}): string {\n const normalizedAmount = new BigNumber(String(cryptoAmount));\n\n if (!normalizedAmount.isFinite() || normalizedAmount.lte(0)) {\n throw new Error(\n `Invalid fiat order crypto amount: ${String(cryptoAmount)}`,\n );\n }\n\n const rawAmount = normalizedAmount\n .shiftedBy(decimals)\n .decimalPlaces(0, BigNumber.ROUND_DOWN)\n .toFixed(0);\n\n if (!new BigNumber(rawAmount).gt(0)) {\n throw new Error('Computed fiat order source amount is not positive');\n }\n\n return rawAmount;\n}\n\n/**\n * Validates that the completed order's crypto asset matches the expected fiat asset.\n *\n * @param options - The validation options.\n * @param options.expectedAsset - The expected fiat asset derived from the transaction type.\n * @param options.orderCrypto - The crypto currency information from the completed order.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nfunction validateOrderAsset({\n expectedAsset,\n orderCrypto,\n transactionId,\n}: {\n expectedAsset: TransactionPayFiatAsset;\n orderCrypto: RampsOrderCryptoCurrency | undefined;\n transactionId: string;\n}): void {\n const orderAssetId = orderCrypto?.assetId?.toLowerCase();\n const expectedAssetId = expectedAsset.caipAssetId.toLowerCase();\n const expectedChainId = expectedAssetId.split('/')[0];\n const orderChainId = orderCrypto?.chainId?.toLowerCase();\n\n if (orderAssetId && orderAssetId !== expectedAssetId) {\n throw new Error(\n `Fiat order asset mismatch for transaction ${transactionId}: ` +\n `expected ${expectedAssetId}, got ${orderAssetId}`,\n );\n }\n\n if (orderChainId && orderChainId !== expectedChainId) {\n throw new Error(\n `Fiat order chain mismatch for transaction ${transactionId}: ` +\n `expected ${expectedChainId}, got ${orderChainId}`,\n );\n }\n}\n\n/**\n * Validates that the re-quoted relay target output hasn't drifted beyond the\n * acceptable slippage threshold compared to the original quote shown to the user.\n *\n * @param options - The validation options.\n * @param options.originalTargetRaw - Raw target amount from the original relay quote.\n * @param options.reQuotedTargetRaw - Raw target amount from the re-quoted relay.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nfunction validateRelaySlippage({\n originalTargetRaw,\n reQuotedTargetRaw,\n transactionId,\n}: {\n originalTargetRaw: string;\n reQuotedTargetRaw: string;\n transactionId: string;\n}): void {\n const original = new BigNumber(originalTargetRaw);\n const reQuoted = new BigNumber(reQuotedTargetRaw);\n\n if (!original.gt(0) || !reQuoted.gt(0)) {\n return;\n }\n\n const slippagePercent = original\n .minus(reQuoted)\n .dividedBy(original)\n .multipliedBy(100);\n\n log('Relay slippage check', {\n originalTargetRaw,\n reQuotedTargetRaw,\n slippagePercent: slippagePercent.toFixed(2),\n transactionId,\n });\n\n if (slippagePercent.gt(MAX_SLIPPAGE_PERCENT)) {\n throw new Error(\n `Relay re-quote slippage too high for transaction ` +\n `${slippagePercent.toFixed(2)}% exceeds ${MAX_SLIPPAGE_PERCENT}% max`,\n );\n }\n}\n\n/**\n * Polls the on-ramp order until it reaches a terminal status.\n *\n * @param options - The polling options.\n * @param options.messenger - Controller messenger for calling `RampsController:getOrder`.\n * @param options.orderCode - The order identifier within the provider.\n * @param options.providerCode - The on-ramp provider code (e.g. \"transak\").\n * @param options.transactionId - Transaction ID for logging.\n * @param options.walletAddress - Wallet address associated with the order.\n * @returns The completed order data.\n */\nasync function waitForOrderCompletion({\n messenger,\n orderCode,\n providerCode,\n transactionId,\n walletAddress,\n}: {\n messenger: TransactionPayControllerMessenger;\n orderCode: string;\n providerCode: string;\n transactionId: string;\n walletAddress: string;\n}): Promise<RampsOrder> {\n const startTime = Date.now();\n let lastStatus: string | undefined;\n\n while (true) {\n let order: RampsOrder | undefined;\n\n try {\n order = await messenger.call(\n 'RampsController:getOrder',\n providerCode,\n orderCode,\n walletAddress,\n );\n } catch (error) {\n log('Order polling network error', error);\n }\n\n if (order) {\n lastStatus = order.status;\n\n log('Polled fiat order', {\n orderStatus: order.status,\n providerCode,\n transactionId,\n });\n\n if (order.status === RampsOrderStatus.Completed) {\n return order;\n }\n\n if (TERMINAL_FAILURE_STATUSES.includes(order.status)) {\n throw new Error(`Fiat order ${order.status.toLowerCase()}`);\n }\n }\n\n if (Date.now() - startTime >= ORDER_POLL_TIMEOUT_MS) {\n throw new Error(\n `Fiat order polling timed out (last status: ${lastStatus})`,\n );\n }\n\n await new Promise((resolve) => setTimeout(resolve, ORDER_POLL_INTERVAL_MS));\n }\n}\n\n/**\n * Re-quotes and submits the relay leg using the settled amount from a completed fiat order.\n *\n * @param options - The submission options.\n * @param options.order - The completed on-ramp order containing the settled crypto amount.\n * @param options.request - The original fiat strategy execute request.\n * @returns An object containing the relay transaction hash if available.\n */\nasync function submitRelayAfterFiatCompletion({\n order,\n request,\n}: {\n order: RampsOrder;\n request: PayStrategyExecuteRequest<FiatQuote>;\n}): Promise<{ transactionHash?: Hex }> {\n const { messenger, quotes, transaction } = request;\n const transactionId = transaction.id;\n\n if (!quotes.length) {\n throw new Error('Missing fiat quote for relay submission');\n }\n\n if (quotes.length > 1) {\n throw new Error('Multiple fiat quotes are not supported for submission');\n }\n\n const fiatAsset = deriveFiatAssetForFiatPayment(transaction);\n if (!fiatAsset) {\n throw new Error(\n `Missing fiat asset mapping for transaction type: ${String(transaction.type)}`,\n );\n }\n\n validateOrderAsset({\n expectedAsset: fiatAsset,\n orderCrypto: order.cryptoCurrency,\n transactionId,\n });\n\n const sourceAmountRaw = getRawSourceAmountFromOrder({\n cryptoAmount: order.cryptoAmount,\n decimals: fiatAsset.decimals,\n });\n\n const baseRequest = quotes[0].request;\n const relayRequest: QuoteRequest = {\n ...baseRequest,\n isMaxAmount: true,\n isPostQuote: false,\n sourceBalanceRaw: sourceAmountRaw,\n sourceTokenAmount: sourceAmountRaw,\n };\n\n log('Re-quoting relay from completed fiat order', {\n completedOrderAmount: order.cryptoAmount,\n relayRequest,\n sourceAmountRaw,\n transactionId,\n });\n\n const relayQuotes = await getRelayQuotes({\n accountSupports7702: request.accountSupports7702,\n messenger,\n requests: [relayRequest],\n transaction,\n });\n\n if (!relayQuotes.length) {\n throw new Error('No relay quotes returned for completed fiat order');\n }\n\n const originalRelayQuote = quotes[0].original.relayQuote;\n validateRelaySlippage({\n originalTargetRaw: originalRelayQuote.details.currencyOut.amount,\n reQuotedTargetRaw: relayQuotes[0].original.details.currencyOut.amount,\n transactionId,\n });\n\n log('Received relay quotes for completed fiat order', {\n relayQuoteCount: relayQuotes.length,\n transactionId,\n });\n\n const relaySubmitRequest: PayStrategyExecuteRequest<RelayQuote> = {\n accountSupports7702: request.accountSupports7702,\n isSmartTransaction: request.isSmartTransaction,\n messenger,\n quotes: relayQuotes,\n transaction,\n };\n\n const relayResult = await submitRelayQuotes(relaySubmitRequest);\n\n log('Relay submission completed after fiat order', {\n relayResult,\n transactionId,\n });\n\n return relayResult;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fiat-submit.mjs","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RampsOrder,\n RampsOrderCryptoCurrency,\n} from '@metamask/ramps-controller';\nimport { RampsOrderStatus } from '@metamask/ramps-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategy,\n PayStrategyExecuteRequest,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../../types';\nimport { buildCaipAssetType, getTokenInfo } from '../../utils/token';\nimport { getRelayQuotes } from '../relay/relay-quotes';\nimport { submitRelayQuotes } from '../relay/relay-submit';\nimport type { RelayQuote } from '../relay/types';\nimport type { TransactionPayFiatAsset } from './constants';\nimport type { FiatQuote } from './types';\nimport { deriveFiatAssetForFiatPayment } from './utils';\n\nconst log = createModuleLogger(projectLogger, 'fiat-submit');\n\nconst ORDER_POLL_INTERVAL_MS = 1000;\nconst ORDER_POLL_TIMEOUT_MS = 10 * 60 * 1000;\nconst MAX_SLIPPAGE_PERCENT = 5;\n\nconst TERMINAL_FAILURE_STATUSES: RampsOrderStatus[] = [\n RampsOrderStatus.Cancelled,\n RampsOrderStatus.Failed,\n RampsOrderStatus.IdExpired,\n];\n\n/**\n * Submits fiat strategy quotes by polling the on-ramp order until completion,\n * then re-quoting and submitting the relay leg with the settled crypto amount.\n *\n * @param request - Strategy execute request containing fiat quotes, messenger, and transaction metadata.\n * @param request.messenger - Controller messenger for cross-controller calls.\n * @param request.quotes - Fiat quotes to execute (exactly one expected).\n * @param request.transaction - Original transaction metadata.\n * @param request.isSmartTransaction - Callback to check smart transaction eligibility.\n * @returns An object containing the relay transaction hash if available.\n */\nexport async function submitFiatQuotes(\n request: PayStrategyExecuteRequest<FiatQuote>,\n): ReturnType<PayStrategy<FiatQuote>['execute']> {\n const { messenger, transaction } = request;\n const transactionId = transaction.id;\n const walletAddress = transaction.txParams.from as Hex | undefined;\n\n if (!walletAddress) {\n throw new Error('Missing wallet address for fiat submission');\n }\n\n const state = messenger.call('TransactionPayController:getState');\n const fiatPayment = state.transactionData[transactionId]?.fiatPayment;\n const orderId = fiatPayment?.orderId;\n\n if (!orderId) {\n throw new Error('Missing order ID for fiat submission');\n }\n\n const providerCode = extractProviderCode(fiatPayment?.rampsQuote?.provider);\n\n if (!providerCode) {\n throw new Error('Missing provider code for fiat submission');\n }\n\n log('Starting fiat order polling', {\n orderId,\n providerCode,\n transactionId,\n });\n\n const order = await waitForOrderCompletion({\n messenger,\n orderCode: orderId,\n providerCode,\n transactionId,\n walletAddress,\n });\n\n log('Fiat order completed', {\n cryptoAmount: order.cryptoAmount,\n orderId,\n transactionId,\n });\n\n return await submitRelayAfterFiatCompletion({ order, request });\n}\n\n/**\n * Extracts the provider code from a ramps provider string.\n *\n * @param provider - Provider string (e.g. `/providers/transak-native`).\n * @returns The provider code, or `null` if the format is invalid.\n */\nfunction extractProviderCode(provider: string | undefined): string | null {\n if (!provider) {\n return null;\n }\n\n const parts = provider.split('/').filter(Boolean);\n return parts.length >= 2 && parts[0] === 'providers' ? parts[1] : null;\n}\n\n/**\n * Converts the order's human-readable crypto amount to a raw token amount.\n *\n * @param options - The conversion options.\n * @param options.cryptoAmount - Human-readable crypto amount from the completed order.\n * @param options.decimals - Token decimals for the fiat asset.\n * @returns The raw token amount as a string.\n */\nfunction getRawSourceAmountFromOrder({\n cryptoAmount,\n decimals,\n}: {\n cryptoAmount: RampsOrder['cryptoAmount'];\n decimals: number;\n}): string {\n const normalizedAmount = new BigNumber(String(cryptoAmount));\n\n if (!normalizedAmount.isFinite() || normalizedAmount.lte(0)) {\n throw new Error(\n `Invalid fiat order crypto amount: ${String(cryptoAmount)}`,\n );\n }\n\n const rawAmount = normalizedAmount\n .shiftedBy(decimals)\n .decimalPlaces(0, BigNumber.ROUND_DOWN)\n .toFixed(0);\n\n if (!new BigNumber(rawAmount).gt(0)) {\n throw new Error('Computed fiat order source amount is not positive');\n }\n\n return rawAmount;\n}\n\n/**\n * Validates that the completed order's crypto asset matches the expected fiat asset.\n *\n * @param options - The validation options.\n * @param options.expectedAsset - The expected fiat asset derived from the transaction type.\n * @param options.orderCrypto - The crypto currency information from the completed order.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nfunction validateOrderAsset({\n expectedAsset,\n orderCrypto,\n transactionId,\n}: {\n expectedAsset: TransactionPayFiatAsset;\n orderCrypto: RampsOrderCryptoCurrency | undefined;\n transactionId: string;\n}): void {\n const orderAssetId = orderCrypto?.assetId?.toLowerCase();\n const expectedAssetId = buildCaipAssetType(\n expectedAsset.chainId,\n expectedAsset.address,\n ).toLowerCase();\n const expectedChainId = expectedAssetId.split('/')[0];\n const orderChainId = orderCrypto?.chainId?.toLowerCase();\n\n if (orderAssetId && orderAssetId !== expectedAssetId) {\n throw new Error(\n `Fiat order asset mismatch for transaction ${transactionId}: ` +\n `expected ${expectedAssetId}, got ${orderAssetId}`,\n );\n }\n\n if (orderChainId && orderChainId !== expectedChainId) {\n throw new Error(\n `Fiat order chain mismatch for transaction ${transactionId}: ` +\n `expected ${expectedChainId}, got ${orderChainId}`,\n );\n }\n}\n\n/**\n * Validates that the re-quoted relay target output hasn't drifted beyond the\n * acceptable slippage threshold compared to the original quote shown to the user.\n *\n * @param options - The validation options.\n * @param options.originalTargetRaw - Raw target amount from the original relay quote.\n * @param options.reQuotedTargetRaw - Raw target amount from the re-quoted relay.\n * @param options.transactionId - Transaction ID for error reporting.\n */\nfunction validateRelaySlippage({\n originalTargetRaw,\n reQuotedTargetRaw,\n transactionId,\n}: {\n originalTargetRaw: string;\n reQuotedTargetRaw: string;\n transactionId: string;\n}): void {\n const original = new BigNumber(originalTargetRaw);\n const reQuoted = new BigNumber(reQuotedTargetRaw);\n\n if (!original.gt(0) || !reQuoted.gt(0)) {\n return;\n }\n\n const slippagePercent = original\n .minus(reQuoted)\n .dividedBy(original)\n .multipliedBy(100);\n\n log('Relay slippage check', {\n originalTargetRaw,\n reQuotedTargetRaw,\n slippagePercent: slippagePercent.toFixed(2),\n transactionId,\n });\n\n if (slippagePercent.gt(MAX_SLIPPAGE_PERCENT)) {\n throw new Error(\n `Relay re-quote slippage too high for transaction ` +\n `${slippagePercent.toFixed(2)}% exceeds ${MAX_SLIPPAGE_PERCENT}% max`,\n );\n }\n}\n\n/**\n * Polls the on-ramp order until it reaches a terminal status.\n *\n * @param options - The polling options.\n * @param options.messenger - Controller messenger for calling `RampsController:getOrder`.\n * @param options.orderCode - The order identifier within the provider.\n * @param options.providerCode - The on-ramp provider code (e.g. \"transak\").\n * @param options.transactionId - Transaction ID for logging.\n * @param options.walletAddress - Wallet address associated with the order.\n * @returns The completed order data.\n */\nasync function waitForOrderCompletion({\n messenger,\n orderCode,\n providerCode,\n transactionId,\n walletAddress,\n}: {\n messenger: TransactionPayControllerMessenger;\n orderCode: string;\n providerCode: string;\n transactionId: string;\n walletAddress: string;\n}): Promise<RampsOrder> {\n const startTime = Date.now();\n let lastStatus: string | undefined;\n\n while (true) {\n let order: RampsOrder | undefined;\n\n try {\n order = await messenger.call(\n 'RampsController:getOrder',\n providerCode,\n orderCode,\n walletAddress,\n );\n } catch (error) {\n log('Order polling network error', error);\n }\n\n if (order) {\n lastStatus = order.status;\n\n log('Polled fiat order', {\n orderStatus: order.status,\n providerCode,\n transactionId,\n });\n\n if (order.status === RampsOrderStatus.Completed) {\n return order;\n }\n\n if (TERMINAL_FAILURE_STATUSES.includes(order.status)) {\n throw new Error(`Fiat order ${order.status.toLowerCase()}`);\n }\n }\n\n if (Date.now() - startTime >= ORDER_POLL_TIMEOUT_MS) {\n throw new Error(\n `Fiat order polling timed out (last status: ${lastStatus})`,\n );\n }\n\n await new Promise((resolve) => setTimeout(resolve, ORDER_POLL_INTERVAL_MS));\n }\n}\n\n/**\n * Re-quotes and submits the relay leg using the settled amount from a completed fiat order.\n *\n * @param options - The submission options.\n * @param options.order - The completed on-ramp order containing the settled crypto amount.\n * @param options.request - The original fiat strategy execute request.\n * @returns An object containing the relay transaction hash if available.\n */\nasync function submitRelayAfterFiatCompletion({\n order,\n request,\n}: {\n order: RampsOrder;\n request: PayStrategyExecuteRequest<FiatQuote>;\n}): Promise<{ transactionHash?: Hex }> {\n const { messenger, quotes, transaction } = request;\n const transactionId = transaction.id;\n\n if (!quotes.length) {\n throw new Error('Missing fiat quote for relay submission');\n }\n\n if (quotes.length > 1) {\n throw new Error('Multiple fiat quotes are not supported for submission');\n }\n\n const fiatAsset = deriveFiatAssetForFiatPayment(transaction, messenger);\n\n validateOrderAsset({\n expectedAsset: fiatAsset,\n orderCrypto: order.cryptoCurrency,\n transactionId,\n });\n\n const tokenInfo = getTokenInfo(\n messenger,\n fiatAsset.address,\n fiatAsset.chainId,\n );\n\n if (!tokenInfo) {\n throw new Error(\n `Unable to resolve token info for fiat asset ${fiatAsset.address} on chain ${fiatAsset.chainId}`,\n );\n }\n\n const sourceAmountRaw = getRawSourceAmountFromOrder({\n cryptoAmount: order.cryptoAmount,\n decimals: tokenInfo.decimals,\n });\n\n const baseRequest = quotes[0].request;\n const relayRequest: QuoteRequest = {\n ...baseRequest,\n isMaxAmount: true,\n isPostQuote: false,\n sourceBalanceRaw: sourceAmountRaw,\n sourceTokenAmount: sourceAmountRaw,\n };\n\n log('Re-quoting relay from completed fiat order', {\n completedOrderAmount: order.cryptoAmount,\n relayRequest,\n sourceAmountRaw,\n transactionId,\n });\n\n const relayQuotes = await getRelayQuotes({\n accountSupports7702: request.accountSupports7702,\n messenger,\n requests: [relayRequest],\n transaction,\n });\n\n if (!relayQuotes.length) {\n throw new Error('No relay quotes returned for completed fiat order');\n }\n\n const originalRelayQuote = quotes[0].original.relayQuote;\n validateRelaySlippage({\n originalTargetRaw: originalRelayQuote.details.currencyOut.amount,\n reQuotedTargetRaw: relayQuotes[0].original.details.currencyOut.amount,\n transactionId,\n });\n\n log('Received relay quotes for completed fiat order', {\n relayQuoteCount: relayQuotes.length,\n transactionId,\n });\n\n const relaySubmitRequest: PayStrategyExecuteRequest<RelayQuote> = {\n accountSupports7702: request.accountSupports7702,\n isSmartTransaction: request.isSmartTransaction,\n messenger,\n quotes: relayQuotes,\n transaction,\n };\n\n const relayResult = await submitRelayQuotes(relaySubmitRequest);\n\n log('Relay submission completed after fiat order', {\n relayResult,\n transactionId,\n });\n\n return relayResult;\n}\n"]}
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.deriveFiatAssetForFiatPayment = void 0;
|
|
4
4
|
const transaction_controller_1 = require("@metamask/transaction-controller");
|
|
5
|
+
const feature_flags_1 = require("../../utils/feature-flags.cjs");
|
|
5
6
|
const constants_1 = require("./constants.cjs");
|
|
6
|
-
function deriveFiatAssetForFiatPayment(transaction) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const firstMatchingType = transaction.nestedTransactions?.[0]?.type;
|
|
10
|
-
if (firstMatchingType) {
|
|
11
|
-
return constants_1.FIAT_ASSET_ID_BY_TX_TYPE[firstMatchingType];
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
return constants_1.FIAT_ASSET_ID_BY_TX_TYPE[transactionType];
|
|
7
|
+
function deriveFiatAssetForFiatPayment(transaction, messenger) {
|
|
8
|
+
const txType = resolveTransactionType(transaction);
|
|
9
|
+
return (0, feature_flags_1.getFiatAssetPerTransactionType)(messenger, txType);
|
|
15
10
|
}
|
|
16
11
|
exports.deriveFiatAssetForFiatPayment = deriveFiatAssetForFiatPayment;
|
|
12
|
+
function resolveTransactionType(transaction) {
|
|
13
|
+
if (transaction.type !== transaction_controller_1.TransactionType.batch) {
|
|
14
|
+
return transaction.type;
|
|
15
|
+
}
|
|
16
|
+
return transaction.nestedTransactions?.find((tx) => tx.type && constants_1.FIAT_ASSET_ID_BY_TX_TYPE[tx.type] !== undefined)?.type;
|
|
17
|
+
}
|
|
17
18
|
//# sourceMappingURL=utils.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.cjs","sourceRoot":"","sources":["../../../src/strategy/fiat/utils.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"utils.cjs","sourceRoot":"","sources":["../../../src/strategy/fiat/utils.ts"],"names":[],"mappings":";;;AACA,6EAAmE;AAGnE,iEAA2E;AAE3E,+CAAuD;AAEvD,SAAgB,6BAA6B,CAC3C,WAA4B,EAC5B,SAA4C;IAE5C,MAAM,MAAM,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;IAEnD,OAAO,IAAA,8CAA8B,EAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAC3D,CAAC;AAPD,sEAOC;AAED,SAAS,sBAAsB,CAC7B,WAA4B;IAE5B,IAAI,WAAW,CAAC,IAAI,KAAK,wCAAe,CAAC,KAAK,EAAE,CAAC;QAC/C,OAAO,WAAW,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED,OAAO,WAAW,CAAC,kBAAkB,EAAE,IAAI,CACzC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,IAAI,oCAAwB,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,SAAS,CACnE,EAAE,IAAI,CAAC;AACV,CAAC","sourcesContent":["import type { TransactionMeta } from '@metamask/transaction-controller';\nimport { TransactionType } from '@metamask/transaction-controller';\n\nimport type { TransactionPayControllerMessenger } from '../../types';\nimport { getFiatAssetPerTransactionType } from '../../utils/feature-flags';\nimport type { TransactionPayFiatAsset } from './constants';\nimport { FIAT_ASSET_ID_BY_TX_TYPE } from './constants';\n\nexport function deriveFiatAssetForFiatPayment(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayFiatAsset {\n const txType = resolveTransactionType(transaction);\n\n return getFiatAssetPerTransactionType(messenger, txType);\n}\n\nfunction resolveTransactionType(\n transaction: TransactionMeta,\n): TransactionType | undefined {\n if (transaction.type !== TransactionType.batch) {\n return transaction.type;\n }\n\n return transaction.nestedTransactions?.find(\n (tx) => tx.type && FIAT_ASSET_ID_BY_TX_TYPE[tx.type] !== undefined,\n )?.type;\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { TransactionMeta } from "@metamask/transaction-controller";
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import type { TransactionMeta } from "@metamask/transaction-controller";
|
|
2
|
+
import type { TransactionPayControllerMessenger } from "../../types.cjs";
|
|
3
|
+
import type { TransactionPayFiatAsset } from "./constants.cjs";
|
|
4
|
+
export declare function deriveFiatAssetForFiatPayment(transaction: TransactionMeta, messenger: TransactionPayControllerMessenger): TransactionPayFiatAsset;
|
|
4
5
|
//# sourceMappingURL=utils.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.cts","sourceRoot":"","sources":["../../../src/strategy/fiat/utils.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"utils.d.cts","sourceRoot":"","sources":["../../../src/strategy/fiat/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,yCAAyC;AAGxE,OAAO,KAAK,EAAE,iCAAiC,EAAE,wBAAoB;AAErE,OAAO,KAAK,EAAE,uBAAuB,EAAE,wBAAoB;AAG3D,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,eAAe,EAC5B,SAAS,EAAE,iCAAiC,GAC3C,uBAAuB,CAIzB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { TransactionMeta } from "@metamask/transaction-controller";
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import type { TransactionMeta } from "@metamask/transaction-controller";
|
|
2
|
+
import type { TransactionPayControllerMessenger } from "../../types.mjs";
|
|
3
|
+
import type { TransactionPayFiatAsset } from "./constants.mjs";
|
|
4
|
+
export declare function deriveFiatAssetForFiatPayment(transaction: TransactionMeta, messenger: TransactionPayControllerMessenger): TransactionPayFiatAsset;
|
|
4
5
|
//# sourceMappingURL=utils.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.mts","sourceRoot":"","sources":["../../../src/strategy/fiat/utils.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"utils.d.mts","sourceRoot":"","sources":["../../../src/strategy/fiat/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,yCAAyC;AAGxE,OAAO,KAAK,EAAE,iCAAiC,EAAE,wBAAoB;AAErE,OAAO,KAAK,EAAE,uBAAuB,EAAE,wBAAoB;AAG3D,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,eAAe,EAC5B,SAAS,EAAE,iCAAiC,GAC3C,uBAAuB,CAIzB"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { TransactionType } from "@metamask/transaction-controller";
|
|
2
|
+
import { getFiatAssetPerTransactionType } from "../../utils/feature-flags.mjs";
|
|
2
3
|
import { FIAT_ASSET_ID_BY_TX_TYPE } from "./constants.mjs";
|
|
3
|
-
export function deriveFiatAssetForFiatPayment(transaction) {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export function deriveFiatAssetForFiatPayment(transaction, messenger) {
|
|
5
|
+
const txType = resolveTransactionType(transaction);
|
|
6
|
+
return getFiatAssetPerTransactionType(messenger, txType);
|
|
7
|
+
}
|
|
8
|
+
function resolveTransactionType(transaction) {
|
|
9
|
+
if (transaction.type !== TransactionType.batch) {
|
|
10
|
+
return transaction.type;
|
|
10
11
|
}
|
|
11
|
-
return FIAT_ASSET_ID_BY_TX_TYPE[
|
|
12
|
+
return transaction.nestedTransactions?.find((tx) => tx.type && FIAT_ASSET_ID_BY_TX_TYPE[tx.type] !== undefined)?.type;
|
|
12
13
|
}
|
|
13
14
|
//# sourceMappingURL=utils.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","sourceRoot":"","sources":["../../../src/strategy/fiat/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.mjs","sourceRoot":"","sources":["../../../src/strategy/fiat/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,yCAAyC;AAGnE,OAAO,EAAE,8BAA8B,EAAE,sCAAkC;AAE3E,OAAO,EAAE,wBAAwB,EAAE,wBAAoB;AAEvD,MAAM,UAAU,6BAA6B,CAC3C,WAA4B,EAC5B,SAA4C;IAE5C,MAAM,MAAM,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;IAEnD,OAAO,8BAA8B,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,sBAAsB,CAC7B,WAA4B;IAE5B,IAAI,WAAW,CAAC,IAAI,KAAK,eAAe,CAAC,KAAK,EAAE,CAAC;QAC/C,OAAO,WAAW,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED,OAAO,WAAW,CAAC,kBAAkB,EAAE,IAAI,CACzC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,IAAI,wBAAwB,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,SAAS,CACnE,EAAE,IAAI,CAAC;AACV,CAAC","sourcesContent":["import type { TransactionMeta } from '@metamask/transaction-controller';\nimport { TransactionType } from '@metamask/transaction-controller';\n\nimport type { TransactionPayControllerMessenger } from '../../types';\nimport { getFiatAssetPerTransactionType } from '../../utils/feature-flags';\nimport type { TransactionPayFiatAsset } from './constants';\nimport { FIAT_ASSET_ID_BY_TX_TYPE } from './constants';\n\nexport function deriveFiatAssetForFiatPayment(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayFiatAsset {\n const txType = resolveTransactionType(transaction);\n\n return getFiatAssetPerTransactionType(messenger, txType);\n}\n\nfunction resolveTransactionType(\n transaction: TransactionMeta,\n): TransactionType | undefined {\n if (transaction.type !== TransactionType.batch) {\n return transaction.type;\n }\n\n return transaction.nestedTransactions?.find(\n (tx) => tx.type && FIAT_ASSET_ID_BY_TX_TYPE[tx.type] !== undefined,\n )?.type;\n}\n"]}
|
|
@@ -414,7 +414,17 @@ async function calculateSourceNetworkCost(quote, messenger, request, transaction
|
|
|
414
414
|
.map((item) => item.data);
|
|
415
415
|
const { chainId, data, maxFeePerGas, maxPriorityFeePerGas, to, value } = relayParams[0];
|
|
416
416
|
const isPredictWithdraw = request.isPostQuote && (0, transaction_1.isPredictWithdrawTransaction)(transaction);
|
|
417
|
-
|
|
417
|
+
// `fromOverride = Safe proxy` is only valid for deposit-style Relay routes
|
|
418
|
+
// where the deposit contract reads the user's source-token balance directly.
|
|
419
|
+
// Same-chain destinations route through DEX swap aggregators that frequently
|
|
420
|
+
// reject contract callers (anti-MEV `msg.sender == tx.origin` checks,
|
|
421
|
+
// ERC777-style callback interfaces, native wrap/unwrap requiring caller
|
|
422
|
+
// native balance). Simulating those from the Safe proxy reverts and breaks
|
|
423
|
+
// gas estimation. For swap-only routes, fall back to the relay params'
|
|
424
|
+
// EOA `from` so simulation succeeds.
|
|
425
|
+
const hasDepositStep = quote.steps.some((step) => step.id === 'deposit');
|
|
426
|
+
const useFromOverride = isPredictWithdraw && hasDepositStep;
|
|
427
|
+
const fromOverride = useFromOverride ? request.refundTo : undefined;
|
|
418
428
|
const relayOnlyGas = await calculateSourceNetworkGasLimit(relayParams, messenger, fromOverride);
|
|
419
429
|
const { gasLimits, is7702, totalGasEstimate, totalGasLimit } = request.isPostQuote
|
|
420
430
|
? combinePostQuoteGas(relayOnlyGas, transaction)
|
|
@@ -462,6 +472,13 @@ async function calculateSourceNetworkCost(quote, messenger, request, transaction
|
|
|
462
472
|
nativeBalance,
|
|
463
473
|
max: max.raw,
|
|
464
474
|
});
|
|
475
|
+
// Gas-fee-token lookup must use the Safe proxy for ALL Predict withdraws,
|
|
476
|
+
// not only deposit-style routes. The user's source token (pUSD) lives in
|
|
477
|
+
// the Safe; the EOA is empty until the Safe.execTransaction sub-call runs
|
|
478
|
+
// mid-batch. Querying the EOA for gas-fee-token availability would always
|
|
479
|
+
// return nothing and force users to hold POL.
|
|
480
|
+
// (`useFromOverride` only governs the gas-estimation `from` address, where
|
|
481
|
+
// swap-style routes need EOA because DEX routers reject contract callers.)
|
|
465
482
|
if (isPredictWithdraw && request.refundTo) {
|
|
466
483
|
log('Using proxy address for predict withdraw gas station simulation', {
|
|
467
484
|
proxyAddress: request.refundTo,
|