@metamask/transaction-pay-controller 18.0.0 → 18.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/CHANGELOG.md +37 -1
  2. package/dist/TransactionPayController-method-action-types.cjs +1 -1
  3. package/dist/TransactionPayController-method-action-types.cjs.map +1 -1
  4. package/dist/TransactionPayController-method-action-types.d.cts +1 -1
  5. package/dist/TransactionPayController-method-action-types.d.mts +1 -1
  6. package/dist/TransactionPayController-method-action-types.mjs +1 -1
  7. package/dist/TransactionPayController-method-action-types.mjs.map +1 -1
  8. package/dist/TransactionPayController.cjs +2 -0
  9. package/dist/TransactionPayController.cjs.map +1 -1
  10. package/dist/TransactionPayController.d.cts.map +1 -1
  11. package/dist/TransactionPayController.d.mts.map +1 -1
  12. package/dist/TransactionPayController.mjs +2 -0
  13. package/dist/TransactionPayController.mjs.map +1 -1
  14. package/dist/constants.cjs +6 -2
  15. package/dist/constants.cjs.map +1 -1
  16. package/dist/constants.d.cts +4 -0
  17. package/dist/constants.d.cts.map +1 -1
  18. package/dist/constants.d.mts +4 -0
  19. package/dist/constants.d.mts.map +1 -1
  20. package/dist/constants.mjs +5 -1
  21. package/dist/constants.mjs.map +1 -1
  22. package/dist/strategy/across/across-actions.cjs +185 -0
  23. package/dist/strategy/across/across-actions.cjs.map +1 -0
  24. package/dist/strategy/across/across-actions.d.cts +21 -0
  25. package/dist/strategy/across/across-actions.d.cts.map +1 -0
  26. package/dist/strategy/across/across-actions.d.mts +21 -0
  27. package/dist/strategy/across/across-actions.d.mts.map +1 -0
  28. package/dist/strategy/across/across-actions.mjs +178 -0
  29. package/dist/strategy/across/across-actions.mjs.map +1 -0
  30. package/dist/strategy/across/across-quotes.cjs +2 -72
  31. package/dist/strategy/across/across-quotes.cjs.map +1 -1
  32. package/dist/strategy/across/across-quotes.d.cts.map +1 -1
  33. package/dist/strategy/across/across-quotes.d.mts.map +1 -1
  34. package/dist/strategy/across/across-quotes.mjs +1 -71
  35. package/dist/strategy/across/across-quotes.mjs.map +1 -1
  36. package/dist/strategy/fiat/FiatStrategy.cjs +15 -0
  37. package/dist/strategy/fiat/FiatStrategy.cjs.map +1 -0
  38. package/dist/strategy/fiat/FiatStrategy.d.cts +7 -0
  39. package/dist/strategy/fiat/FiatStrategy.d.cts.map +1 -0
  40. package/dist/strategy/fiat/FiatStrategy.d.mts +7 -0
  41. package/dist/strategy/fiat/FiatStrategy.d.mts.map +1 -0
  42. package/dist/strategy/fiat/FiatStrategy.mjs +11 -0
  43. package/dist/strategy/fiat/FiatStrategy.mjs.map +1 -0
  44. package/dist/strategy/fiat/constants.cjs +24 -0
  45. package/dist/strategy/fiat/constants.cjs.map +1 -0
  46. package/dist/strategy/fiat/constants.d.cts +10 -0
  47. package/dist/strategy/fiat/constants.d.cts.map +1 -0
  48. package/dist/strategy/fiat/constants.d.mts +10 -0
  49. package/dist/strategy/fiat/constants.d.mts.map +1 -0
  50. package/dist/strategy/fiat/constants.mjs +21 -0
  51. package/dist/strategy/fiat/constants.mjs.map +1 -0
  52. package/dist/strategy/fiat/fiat-quotes.cjs +214 -0
  53. package/dist/strategy/fiat/fiat-quotes.cjs.map +1 -0
  54. package/dist/strategy/fiat/fiat-quotes.d.cts +17 -0
  55. package/dist/strategy/fiat/fiat-quotes.d.cts.map +1 -0
  56. package/dist/strategy/fiat/fiat-quotes.d.mts +17 -0
  57. package/dist/strategy/fiat/fiat-quotes.d.mts.map +1 -0
  58. package/dist/strategy/fiat/fiat-quotes.mjs +210 -0
  59. package/dist/strategy/fiat/fiat-quotes.mjs.map +1 -0
  60. package/dist/strategy/fiat/fiat-submit.cjs +14 -0
  61. package/dist/strategy/fiat/fiat-submit.cjs.map +1 -0
  62. package/dist/strategy/fiat/fiat-submit.d.cts +10 -0
  63. package/dist/strategy/fiat/fiat-submit.d.cts.map +1 -0
  64. package/dist/strategy/fiat/fiat-submit.d.mts +10 -0
  65. package/dist/strategy/fiat/fiat-submit.d.mts.map +1 -0
  66. package/dist/strategy/fiat/fiat-submit.mjs +10 -0
  67. package/dist/strategy/fiat/fiat-submit.mjs.map +1 -0
  68. package/dist/strategy/fiat/types.cjs +3 -0
  69. package/dist/strategy/fiat/types.cjs.map +1 -0
  70. package/dist/strategy/fiat/types.d.cts +8 -0
  71. package/dist/strategy/fiat/types.d.cts.map +1 -0
  72. package/dist/strategy/fiat/types.d.mts +8 -0
  73. package/dist/strategy/fiat/types.d.mts.map +1 -0
  74. package/dist/strategy/fiat/types.mjs +2 -0
  75. package/dist/strategy/fiat/types.mjs.map +1 -0
  76. package/dist/strategy/fiat/utils.cjs +23 -0
  77. package/dist/strategy/fiat/utils.cjs.map +1 -0
  78. package/dist/strategy/fiat/utils.d.cts +6 -0
  79. package/dist/strategy/fiat/utils.d.cts.map +1 -0
  80. package/dist/strategy/fiat/utils.d.mts +6 -0
  81. package/dist/strategy/fiat/utils.d.mts.map +1 -0
  82. package/dist/strategy/fiat/utils.mjs +18 -0
  83. package/dist/strategy/fiat/utils.mjs.map +1 -0
  84. package/dist/strategy/relay/relay-max-gas-station.cjs +2 -1
  85. package/dist/strategy/relay/relay-max-gas-station.cjs.map +1 -1
  86. package/dist/strategy/relay/relay-max-gas-station.d.cts.map +1 -1
  87. package/dist/strategy/relay/relay-max-gas-station.d.mts.map +1 -1
  88. package/dist/strategy/relay/relay-max-gas-station.mjs +2 -1
  89. package/dist/strategy/relay/relay-max-gas-station.mjs.map +1 -1
  90. package/dist/strategy/relay/relay-quotes.cjs +35 -4
  91. package/dist/strategy/relay/relay-quotes.cjs.map +1 -1
  92. package/dist/strategy/relay/relay-quotes.d.cts.map +1 -1
  93. package/dist/strategy/relay/relay-quotes.d.mts.map +1 -1
  94. package/dist/strategy/relay/relay-quotes.mjs +36 -5
  95. package/dist/strategy/relay/relay-quotes.mjs.map +1 -1
  96. package/dist/strategy/relay/relay-submit.cjs +4 -2
  97. package/dist/strategy/relay/relay-submit.cjs.map +1 -1
  98. package/dist/strategy/relay/relay-submit.d.cts.map +1 -1
  99. package/dist/strategy/relay/relay-submit.d.mts.map +1 -1
  100. package/dist/strategy/relay/relay-submit.mjs +4 -2
  101. package/dist/strategy/relay/relay-submit.mjs.map +1 -1
  102. package/dist/strategy/relay/types.cjs.map +1 -1
  103. package/dist/strategy/relay/types.d.cts +66 -18
  104. package/dist/strategy/relay/types.d.cts.map +1 -1
  105. package/dist/strategy/relay/types.d.mts +66 -18
  106. package/dist/strategy/relay/types.d.mts.map +1 -1
  107. package/dist/strategy/relay/types.mjs.map +1 -1
  108. package/dist/tests/messenger-mock.d.cts +1 -1
  109. package/dist/tests/messenger-mock.d.mts +1 -1
  110. package/dist/types.cjs.map +1 -1
  111. package/dist/types.d.cts +16 -1
  112. package/dist/types.d.cts.map +1 -1
  113. package/dist/types.d.mts +16 -1
  114. package/dist/types.d.mts.map +1 -1
  115. package/dist/types.mjs.map +1 -1
  116. package/dist/utils/quotes.cjs +12 -9
  117. package/dist/utils/quotes.cjs.map +1 -1
  118. package/dist/utils/quotes.d.cts.map +1 -1
  119. package/dist/utils/quotes.d.mts.map +1 -1
  120. package/dist/utils/quotes.mjs +12 -9
  121. package/dist/utils/quotes.mjs.map +1 -1
  122. package/dist/utils/strategy.cjs +3 -0
  123. package/dist/utils/strategy.cjs.map +1 -1
  124. package/dist/utils/strategy.d.cts.map +1 -1
  125. package/dist/utils/strategy.d.mts.map +1 -1
  126. package/dist/utils/strategy.mjs +3 -0
  127. package/dist/utils/strategy.mjs.map +1 -1
  128. package/dist/utils/token.cjs +26 -1
  129. package/dist/utils/token.cjs.map +1 -1
  130. package/dist/utils/token.d.cts +10 -0
  131. package/dist/utils/token.d.cts.map +1 -1
  132. package/dist/utils/token.d.mts +10 -0
  133. package/dist/utils/token.d.mts.map +1 -1
  134. package/dist/utils/token.mjs +24 -0
  135. package/dist/utils/token.mjs.map +1 -1
  136. package/dist/utils/totals.cjs +2 -0
  137. package/dist/utils/totals.cjs.map +1 -1
  138. package/dist/utils/totals.d.cts.map +1 -1
  139. package/dist/utils/totals.d.mts.map +1 -1
  140. package/dist/utils/totals.mjs +2 -0
  141. package/dist/utils/totals.mjs.map +1 -1
  142. package/package.json +12 -11
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFiatQuotes = void 0;
4
+ const utils_1 = require("@metamask/utils");
5
+ const bignumber_js_1 = require("bignumber.js");
6
+ const utils_2 = require("./utils.cjs");
7
+ const constants_1 = require("../../constants.cjs");
8
+ const logger_1 = require("../../logger.cjs");
9
+ const token_1 = require("../../utils/token.cjs");
10
+ const relay_quotes_1 = require("../relay/relay-quotes.cjs");
11
+ const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'fiat-strategy');
12
+ /**
13
+ * Fetches MM Pay fiat strategy quotes using a relay-first estimation flow.
14
+ *
15
+ * @param request - Strategy quotes request.
16
+ * @returns A single combined fiat strategy quote, or an empty array when inputs/quotes are unavailable.
17
+ * @remarks
18
+ * Flow summary:
19
+ * 1. Read `amountFiat` and selected payment method from transaction pay state.
20
+ * 2. Build a synthetic relay request from `amountFiat` using source token USD rate.
21
+ * 3. Fetch relay quote and compute total relay fee (`provider + source network + target network + MetaMask`).
22
+ * 4. Call ramps quotes with `adjustedAmountFiat = amountFiat + relayTotalFeeUsd`.
23
+ * 5. Pick the configured ramps provider quote and combine it with relay quote into one fiat strategy quote.
24
+ */
25
+ async function getFiatQuotes(request) {
26
+ const { fiatPaymentMethod, messenger, transaction } = request;
27
+ const transactionId = transaction.id;
28
+ const state = messenger.call('TransactionPayController:getState');
29
+ const transactionData = state.transactionData[transactionId];
30
+ const amountFiat = transactionData?.fiatPayment?.amountFiat;
31
+ const walletAddress = transaction.txParams.from;
32
+ const requiredTokens = getRequiredTokens(transactionData?.tokens);
33
+ const fiatAsset = (0, utils_2.deriveFiatAssetForFiatPayment)(transaction);
34
+ if (!amountFiat ||
35
+ !fiatPaymentMethod ||
36
+ !requiredTokens.length ||
37
+ !fiatAsset) {
38
+ return [];
39
+ }
40
+ try {
41
+ if (requiredTokens.length > 1) {
42
+ throw new Error('Multiple required tokens not supported for fiat strategy');
43
+ }
44
+ const requiredToken = requiredTokens[0];
45
+ const relayRequest = buildRelayRequestFromAmountFiat({
46
+ amountFiat,
47
+ fiatAsset,
48
+ messenger,
49
+ requiredToken,
50
+ walletAddress,
51
+ });
52
+ if (!relayRequest) {
53
+ throw new Error('Failed to build relay request from fiat amount');
54
+ }
55
+ const relayQuotes = await (0, relay_quotes_1.getRelayQuotes)({
56
+ messenger,
57
+ requests: [relayRequest],
58
+ transaction,
59
+ });
60
+ const relayQuote = relayQuotes[0];
61
+ if (!relayQuote) {
62
+ throw new Error('No relay quote available for fiat estimation');
63
+ }
64
+ const relayTotalFeeUsd = getRelayTotalFeeUsd(relayQuote);
65
+ const adjustedAmountFiat = new bignumber_js_1.BigNumber(amountFiat).plus(relayTotalFeeUsd);
66
+ if (!adjustedAmountFiat.isFinite() ||
67
+ !adjustedAmountFiat.gt(0) ||
68
+ !relayTotalFeeUsd.isFinite() ||
69
+ !relayTotalFeeUsd.gte(0)) {
70
+ throw new Error('Invalid fiat amount after relay fee adjustment');
71
+ }
72
+ const adjustedAmount = adjustedAmountFiat.toNumber();
73
+ if (!Number.isFinite(adjustedAmount) || adjustedAmount <= 0) {
74
+ throw new Error('Invalid fiat amount after relay fee adjustment');
75
+ }
76
+ log('Fiat quote flow', {
77
+ adjustedAmountFiat: adjustedAmountFiat.toString(10),
78
+ amountFiat,
79
+ paymentMethods: [fiatPaymentMethod],
80
+ relayTotalFeeUsd: relayTotalFeeUsd.toString(10),
81
+ sourceAmountRaw: relayRequest.sourceTokenAmount,
82
+ transactionId,
83
+ });
84
+ const quotes = await messenger.call('RampsController:getQuotes', {
85
+ amount: adjustedAmount,
86
+ paymentMethods: [fiatPaymentMethod],
87
+ walletAddress,
88
+ });
89
+ log('Fetched ramps quotes', {
90
+ rampsQuotesCount: quotes.success?.length ?? 0,
91
+ transactionId,
92
+ });
93
+ const fiatQuote = (0, utils_2.pickBestFiatQuote)(quotes);
94
+ if (!fiatQuote) {
95
+ throw new Error('No matching ramps quote found for selected provider');
96
+ }
97
+ return [
98
+ combineQuotes({
99
+ adjustedAmountFiat: adjustedAmountFiat.toString(10),
100
+ amountFiat,
101
+ fiatQuote,
102
+ relayQuote,
103
+ }),
104
+ ];
105
+ }
106
+ catch (error) {
107
+ log('Failed to fetch fiat quotes', { error, transactionId });
108
+ }
109
+ return [];
110
+ }
111
+ exports.getFiatQuotes = getFiatQuotes;
112
+ function getRequiredTokens(tokens) {
113
+ return tokens?.filter((token) => !token.skipIfBalance) ?? [];
114
+ }
115
+ function buildRelayRequestFromAmountFiat({ amountFiat, fiatAsset, messenger, requiredToken, walletAddress, }) {
116
+ const sourceFiatRate = (0, token_1.getTokenFiatRate)(messenger, fiatAsset.address, fiatAsset.chainId);
117
+ if (!sourceFiatRate) {
118
+ return undefined;
119
+ }
120
+ const sourceAmountRaw = (0, token_1.computeRawFromFiatAmount)(amountFiat, fiatAsset.decimals, sourceFiatRate.usdRate);
121
+ if (!sourceAmountRaw) {
122
+ return undefined;
123
+ }
124
+ return {
125
+ from: walletAddress,
126
+ // Force EXACT_INPUT mode: source amount is pre-calculated from the fiat
127
+ // amount, so the relay should treat it as a fixed input rather than
128
+ // computing it from the target.
129
+ isPostQuote: true,
130
+ sourceBalanceRaw: sourceAmountRaw,
131
+ sourceChainId: fiatAsset.chainId,
132
+ sourceTokenAddress: fiatAsset.address,
133
+ sourceTokenAmount: sourceAmountRaw,
134
+ targetAmountMinimum: requiredToken.amountRaw,
135
+ targetChainId: requiredToken.chainId,
136
+ targetTokenAddress: requiredToken.address,
137
+ };
138
+ }
139
+ /**
140
+ * Combines fiat and relay legs into a single MM Pay fiat strategy quote.
141
+ *
142
+ * @param params - Combined quote inputs.
143
+ * @param params.adjustedAmountFiat - Fiat amount sent to ramps after adding relay fee estimate.
144
+ * @param params.amountFiat - User-entered fiat amount.
145
+ * @param params.fiatQuote - Selected ramps quote.
146
+ * @param params.relayQuote - Estimated relay quote.
147
+ * @returns A single fiat strategy quote with split fee buckets.
148
+ * @remarks
149
+ * Fee mapping contract for MM Pay Fiat strategy:
150
+ * - `fees.provider`: Total provider fee (relay provider/swap fee + ramps provider/network fee).
151
+ * Consumed by UI transaction fee row and tooltip provider fee.
152
+ * - `fees.providerFiat`: Fiat on-ramp provider fees only (`providerFee + networkFee` from ramps quote).
153
+ * Optional breakdown; client can derive relay portion via `provider - providerFiat`.
154
+ * - `fees.sourceNetwork` / `fees.targetNetwork`: Relay settlement network fees.
155
+ * Consumed by UI transaction fee row and tooltip network fee.
156
+ * - `fees.metaMask`: MM Pay fee (currently 100 bps over `amountFiat + adjustedAmountFiat`).
157
+ * Consumed by UI transaction fee row and tooltip MetaMask fee.
158
+ * - `totals.total` should represent Amount + Transaction Fee using the totals pipeline.
159
+ */
160
+ function combineQuotes({ adjustedAmountFiat, amountFiat, fiatQuote, relayQuote, }) {
161
+ const rampsProviderFee = getRampsProviderFee(fiatQuote);
162
+ const totalProviderFee = new bignumber_js_1.BigNumber(relayQuote.fees.provider.usd)
163
+ .plus(rampsProviderFee)
164
+ .toString(10);
165
+ const rampsProviderFeeStr = rampsProviderFee.toString(10);
166
+ const metaMaskFee = getMetaMaskFee({
167
+ adjustedAmountFiat,
168
+ amountFiat,
169
+ }).toString(10);
170
+ return {
171
+ ...relayQuote,
172
+ fees: {
173
+ ...relayQuote.fees,
174
+ metaMask: {
175
+ fiat: metaMaskFee,
176
+ usd: metaMaskFee,
177
+ },
178
+ provider: {
179
+ fiat: totalProviderFee,
180
+ usd: totalProviderFee,
181
+ },
182
+ providerFiat: {
183
+ fiat: rampsProviderFeeStr,
184
+ usd: rampsProviderFeeStr,
185
+ },
186
+ },
187
+ original: {
188
+ rampsQuote: fiatQuote,
189
+ relayQuote: relayQuote.original,
190
+ },
191
+ strategy: constants_1.TransactionPayStrategy.Fiat,
192
+ };
193
+ }
194
+ /**
195
+ * Ramps providers handle network gas fees themselves but report them separately
196
+ * as `networkFee` alongside their `providerFee`. We combine both into a single
197
+ * ramps provider fee for the `providerFiat` breakdown.
198
+ *
199
+ * @param fiatQuote - The ramps quote containing provider and network fees.
200
+ * @returns Combined ramps provider fee as a BigNumber.
201
+ */
202
+ function getRampsProviderFee(fiatQuote) {
203
+ return new bignumber_js_1.BigNumber(fiatQuote.quote.providerFee ?? 0).plus(fiatQuote.quote.networkFee ?? 0);
204
+ }
205
+ function getRelayTotalFeeUsd(relayQuote) {
206
+ return new bignumber_js_1.BigNumber(relayQuote.fees.provider.usd)
207
+ .plus(relayQuote.fees.sourceNetwork.estimate.usd)
208
+ .plus(relayQuote.fees.targetNetwork.usd)
209
+ .plus(relayQuote.fees.metaMask.usd);
210
+ }
211
+ function getMetaMaskFee({ adjustedAmountFiat, amountFiat, }) {
212
+ return new bignumber_js_1.BigNumber(amountFiat).plus(adjustedAmountFiat).dividedBy(100);
213
+ }
214
+ //# sourceMappingURL=fiat-quotes.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fiat-quotes.cjs","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-quotes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Quote as RampsQuote } from '@metamask/ramps-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport type { FiatQuote } from './types';\nimport { deriveFiatAssetForFiatPayment, pickBestFiatQuote } from './utils';\nimport { TransactionPayStrategy } from '../../constants';\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategyGetQuotesRequest,\n QuoteRequest,\n TransactionPayRequiredToken,\n TransactionPayQuote,\n} from '../../types';\nimport { computeRawFromFiatAmount, getTokenFiatRate } from '../../utils/token';\nimport { getRelayQuotes } from '../relay/relay-quotes';\nimport type { RelayQuote } from '../relay/types';\n\nconst log = createModuleLogger(projectLogger, 'fiat-strategy');\n\n/**\n * Fetches MM Pay fiat strategy quotes using a relay-first estimation flow.\n *\n * @param request - Strategy quotes request.\n * @returns A single combined fiat strategy quote, or an empty array when inputs/quotes are unavailable.\n * @remarks\n * Flow summary:\n * 1. Read `amountFiat` and selected payment method from transaction pay state.\n * 2. Build a synthetic relay request from `amountFiat` using source token USD rate.\n * 3. Fetch relay quote and compute total relay fee (`provider + source network + target network + MetaMask`).\n * 4. Call ramps quotes with `adjustedAmountFiat = amountFiat + relayTotalFeeUsd`.\n * 5. Pick the configured ramps provider quote and combine it with relay quote into one fiat strategy quote.\n */\nexport async function getFiatQuotes(\n request: PayStrategyGetQuotesRequest,\n): Promise<TransactionPayQuote<FiatQuote>[]> {\n const { fiatPaymentMethod, messenger, transaction } = request;\n const transactionId = transaction.id;\n\n const state = messenger.call('TransactionPayController:getState');\n const transactionData = state.transactionData[transactionId];\n const amountFiat = transactionData?.fiatPayment?.amountFiat;\n const walletAddress = transaction.txParams.from as Hex;\n const requiredTokens = getRequiredTokens(transactionData?.tokens);\n const fiatAsset = deriveFiatAssetForFiatPayment(transaction);\n\n if (\n !amountFiat ||\n !fiatPaymentMethod ||\n !requiredTokens.length ||\n !fiatAsset\n ) {\n return [];\n }\n\n try {\n if (requiredTokens.length > 1) {\n throw new Error(\n 'Multiple required tokens not supported for fiat strategy',\n );\n }\n\n const requiredToken = requiredTokens[0];\n\n const relayRequest = buildRelayRequestFromAmountFiat({\n amountFiat,\n fiatAsset,\n messenger,\n requiredToken,\n walletAddress,\n });\n\n if (!relayRequest) {\n throw new Error('Failed to build relay request from fiat amount');\n }\n\n const relayQuotes = await getRelayQuotes({\n messenger,\n requests: [relayRequest],\n transaction,\n });\n\n const relayQuote = relayQuotes[0];\n if (!relayQuote) {\n throw new Error('No relay quote available for fiat estimation');\n }\n\n const relayTotalFeeUsd = getRelayTotalFeeUsd(relayQuote);\n const adjustedAmountFiat = new BigNumber(amountFiat).plus(relayTotalFeeUsd);\n\n if (\n !adjustedAmountFiat.isFinite() ||\n !adjustedAmountFiat.gt(0) ||\n !relayTotalFeeUsd.isFinite() ||\n !relayTotalFeeUsd.gte(0)\n ) {\n throw new Error('Invalid fiat amount after relay fee adjustment');\n }\n\n const adjustedAmount = adjustedAmountFiat.toNumber();\n\n if (!Number.isFinite(adjustedAmount) || adjustedAmount <= 0) {\n throw new Error('Invalid fiat amount after relay fee adjustment');\n }\n\n log('Fiat quote flow', {\n adjustedAmountFiat: adjustedAmountFiat.toString(10),\n amountFiat,\n paymentMethods: [fiatPaymentMethod],\n relayTotalFeeUsd: relayTotalFeeUsd.toString(10),\n sourceAmountRaw: relayRequest.sourceTokenAmount,\n transactionId,\n });\n\n const quotes = await messenger.call('RampsController:getQuotes', {\n amount: adjustedAmount,\n paymentMethods: [fiatPaymentMethod],\n walletAddress,\n });\n\n log('Fetched ramps quotes', {\n rampsQuotesCount: quotes.success?.length ?? 0,\n transactionId,\n });\n\n const fiatQuote = pickBestFiatQuote(quotes);\n\n if (!fiatQuote) {\n throw new Error('No matching ramps quote found for selected provider');\n }\n\n return [\n combineQuotes({\n adjustedAmountFiat: adjustedAmountFiat.toString(10),\n amountFiat,\n fiatQuote,\n relayQuote,\n }),\n ];\n } catch (error) {\n log('Failed to fetch fiat quotes', { error, transactionId });\n }\n\n return [];\n}\n\nfunction getRequiredTokens(\n tokens?: TransactionPayRequiredToken[],\n): TransactionPayRequiredToken[] {\n return tokens?.filter((token) => !token.skipIfBalance) ?? [];\n}\n\nfunction buildRelayRequestFromAmountFiat({\n amountFiat,\n fiatAsset,\n messenger,\n requiredToken,\n walletAddress,\n}: {\n amountFiat: string;\n fiatAsset: {\n address: Hex;\n chainId: Hex;\n decimals: number;\n };\n messenger: PayStrategyGetQuotesRequest['messenger'];\n requiredToken: TransactionPayRequiredToken;\n walletAddress: Hex;\n}): QuoteRequest | undefined {\n const sourceFiatRate = getTokenFiatRate(\n messenger,\n fiatAsset.address,\n fiatAsset.chainId,\n );\n\n if (!sourceFiatRate) {\n return undefined;\n }\n\n const sourceAmountRaw = computeRawFromFiatAmount(\n amountFiat,\n fiatAsset.decimals,\n sourceFiatRate.usdRate,\n );\n\n if (!sourceAmountRaw) {\n return undefined;\n }\n\n return {\n from: walletAddress,\n // Force EXACT_INPUT mode: source amount is pre-calculated from the fiat\n // amount, so the relay should treat it as a fixed input rather than\n // computing it from the target.\n isPostQuote: true,\n sourceBalanceRaw: sourceAmountRaw,\n sourceChainId: fiatAsset.chainId,\n sourceTokenAddress: fiatAsset.address,\n sourceTokenAmount: sourceAmountRaw,\n targetAmountMinimum: requiredToken.amountRaw,\n targetChainId: requiredToken.chainId,\n targetTokenAddress: requiredToken.address,\n };\n}\n\n/**\n * Combines fiat and relay legs into a single MM Pay fiat strategy quote.\n *\n * @param params - Combined quote inputs.\n * @param params.adjustedAmountFiat - Fiat amount sent to ramps after adding relay fee estimate.\n * @param params.amountFiat - User-entered fiat amount.\n * @param params.fiatQuote - Selected ramps quote.\n * @param params.relayQuote - Estimated relay quote.\n * @returns A single fiat strategy quote with split fee buckets.\n * @remarks\n * Fee mapping contract for MM Pay Fiat strategy:\n * - `fees.provider`: Total provider fee (relay provider/swap fee + ramps provider/network fee).\n * Consumed by UI transaction fee row and tooltip provider fee.\n * - `fees.providerFiat`: Fiat on-ramp provider fees only (`providerFee + networkFee` from ramps quote).\n * Optional breakdown; client can derive relay portion via `provider - providerFiat`.\n * - `fees.sourceNetwork` / `fees.targetNetwork`: Relay settlement network fees.\n * Consumed by UI transaction fee row and tooltip network fee.\n * - `fees.metaMask`: MM Pay fee (currently 100 bps over `amountFiat + adjustedAmountFiat`).\n * Consumed by UI transaction fee row and tooltip MetaMask fee.\n * - `totals.total` should represent Amount + Transaction Fee using the totals pipeline.\n */\nfunction combineQuotes({\n adjustedAmountFiat,\n amountFiat,\n fiatQuote,\n relayQuote,\n}: {\n adjustedAmountFiat: string;\n amountFiat: string;\n fiatQuote: RampsQuote;\n relayQuote: TransactionPayQuote<RelayQuote>;\n}): TransactionPayQuote<FiatQuote> {\n const rampsProviderFee = getRampsProviderFee(fiatQuote);\n const totalProviderFee = new BigNumber(relayQuote.fees.provider.usd)\n .plus(rampsProviderFee)\n .toString(10);\n const rampsProviderFeeStr = rampsProviderFee.toString(10);\n const metaMaskFee = getMetaMaskFee({\n adjustedAmountFiat,\n amountFiat,\n }).toString(10);\n\n return {\n ...relayQuote,\n fees: {\n ...relayQuote.fees,\n metaMask: {\n fiat: metaMaskFee,\n usd: metaMaskFee,\n },\n provider: {\n fiat: totalProviderFee,\n usd: totalProviderFee,\n },\n providerFiat: {\n fiat: rampsProviderFeeStr,\n usd: rampsProviderFeeStr,\n },\n },\n original: {\n rampsQuote: fiatQuote,\n relayQuote: relayQuote.original,\n },\n strategy: TransactionPayStrategy.Fiat,\n };\n}\n\n/**\n * Ramps providers handle network gas fees themselves but report them separately\n * as `networkFee` alongside their `providerFee`. We combine both into a single\n * ramps provider fee for the `providerFiat` breakdown.\n *\n * @param fiatQuote - The ramps quote containing provider and network fees.\n * @returns Combined ramps provider fee as a BigNumber.\n */\nfunction getRampsProviderFee(fiatQuote: RampsQuote): BigNumber {\n return new BigNumber(fiatQuote.quote.providerFee ?? 0).plus(\n fiatQuote.quote.networkFee ?? 0,\n );\n}\n\nfunction getRelayTotalFeeUsd(\n relayQuote: TransactionPayQuote<RelayQuote>,\n): BigNumber {\n return new BigNumber(relayQuote.fees.provider.usd)\n .plus(relayQuote.fees.sourceNetwork.estimate.usd)\n .plus(relayQuote.fees.targetNetwork.usd)\n .plus(relayQuote.fees.metaMask.usd);\n}\n\nfunction getMetaMaskFee({\n adjustedAmountFiat,\n amountFiat,\n}: {\n adjustedAmountFiat: BigNumber.Value;\n amountFiat: BigNumber.Value;\n}): BigNumber {\n return new BigNumber(amountFiat).plus(adjustedAmountFiat).dividedBy(100);\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import type { FiatQuote } from "./types.cjs";
2
+ import type { PayStrategyGetQuotesRequest, TransactionPayQuote } from "../../types.cjs";
3
+ /**
4
+ * Fetches MM Pay fiat strategy quotes using a relay-first estimation flow.
5
+ *
6
+ * @param request - Strategy quotes request.
7
+ * @returns A single combined fiat strategy quote, or an empty array when inputs/quotes are unavailable.
8
+ * @remarks
9
+ * Flow summary:
10
+ * 1. Read `amountFiat` and selected payment method from transaction pay state.
11
+ * 2. Build a synthetic relay request from `amountFiat` using source token USD rate.
12
+ * 3. Fetch relay quote and compute total relay fee (`provider + source network + target network + MetaMask`).
13
+ * 4. Call ramps quotes with `adjustedAmountFiat = amountFiat + relayTotalFeeUsd`.
14
+ * 5. Pick the configured ramps provider quote and combine it with relay quote into one fiat strategy quote.
15
+ */
16
+ export declare function getFiatQuotes(request: PayStrategyGetQuotesRequest): Promise<TransactionPayQuote<FiatQuote>[]>;
17
+ //# sourceMappingURL=fiat-quotes.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fiat-quotes.d.cts","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-quotes.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAgB;AAIzC,OAAO,KAAK,EACV,2BAA2B,EAG3B,mBAAmB,EACpB,wBAAoB;AAOrB;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,CAAC,CA6G3C"}
@@ -0,0 +1,17 @@
1
+ import type { FiatQuote } from "./types.mjs";
2
+ import type { PayStrategyGetQuotesRequest, TransactionPayQuote } from "../../types.mjs";
3
+ /**
4
+ * Fetches MM Pay fiat strategy quotes using a relay-first estimation flow.
5
+ *
6
+ * @param request - Strategy quotes request.
7
+ * @returns A single combined fiat strategy quote, or an empty array when inputs/quotes are unavailable.
8
+ * @remarks
9
+ * Flow summary:
10
+ * 1. Read `amountFiat` and selected payment method from transaction pay state.
11
+ * 2. Build a synthetic relay request from `amountFiat` using source token USD rate.
12
+ * 3. Fetch relay quote and compute total relay fee (`provider + source network + target network + MetaMask`).
13
+ * 4. Call ramps quotes with `adjustedAmountFiat = amountFiat + relayTotalFeeUsd`.
14
+ * 5. Pick the configured ramps provider quote and combine it with relay quote into one fiat strategy quote.
15
+ */
16
+ export declare function getFiatQuotes(request: PayStrategyGetQuotesRequest): Promise<TransactionPayQuote<FiatQuote>[]>;
17
+ //# sourceMappingURL=fiat-quotes.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fiat-quotes.d.mts","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-quotes.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAgB;AAIzC,OAAO,KAAK,EACV,2BAA2B,EAG3B,mBAAmB,EACpB,wBAAoB;AAOrB;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,CAAC,CA6G3C"}
@@ -0,0 +1,210 @@
1
+ import { createModuleLogger } from "@metamask/utils";
2
+ import { BigNumber } from "bignumber.js";
3
+ import { deriveFiatAssetForFiatPayment, pickBestFiatQuote } from "./utils.mjs";
4
+ import { TransactionPayStrategy } from "../../constants.mjs";
5
+ import { projectLogger } from "../../logger.mjs";
6
+ import { computeRawFromFiatAmount, getTokenFiatRate } from "../../utils/token.mjs";
7
+ import { getRelayQuotes } from "../relay/relay-quotes.mjs";
8
+ const log = createModuleLogger(projectLogger, 'fiat-strategy');
9
+ /**
10
+ * Fetches MM Pay fiat strategy quotes using a relay-first estimation flow.
11
+ *
12
+ * @param request - Strategy quotes request.
13
+ * @returns A single combined fiat strategy quote, or an empty array when inputs/quotes are unavailable.
14
+ * @remarks
15
+ * Flow summary:
16
+ * 1. Read `amountFiat` and selected payment method from transaction pay state.
17
+ * 2. Build a synthetic relay request from `amountFiat` using source token USD rate.
18
+ * 3. Fetch relay quote and compute total relay fee (`provider + source network + target network + MetaMask`).
19
+ * 4. Call ramps quotes with `adjustedAmountFiat = amountFiat + relayTotalFeeUsd`.
20
+ * 5. Pick the configured ramps provider quote and combine it with relay quote into one fiat strategy quote.
21
+ */
22
+ export async function getFiatQuotes(request) {
23
+ const { fiatPaymentMethod, messenger, transaction } = request;
24
+ const transactionId = transaction.id;
25
+ const state = messenger.call('TransactionPayController:getState');
26
+ const transactionData = state.transactionData[transactionId];
27
+ const amountFiat = transactionData?.fiatPayment?.amountFiat;
28
+ const walletAddress = transaction.txParams.from;
29
+ const requiredTokens = getRequiredTokens(transactionData?.tokens);
30
+ const fiatAsset = deriveFiatAssetForFiatPayment(transaction);
31
+ if (!amountFiat ||
32
+ !fiatPaymentMethod ||
33
+ !requiredTokens.length ||
34
+ !fiatAsset) {
35
+ return [];
36
+ }
37
+ try {
38
+ if (requiredTokens.length > 1) {
39
+ throw new Error('Multiple required tokens not supported for fiat strategy');
40
+ }
41
+ const requiredToken = requiredTokens[0];
42
+ const relayRequest = buildRelayRequestFromAmountFiat({
43
+ amountFiat,
44
+ fiatAsset,
45
+ messenger,
46
+ requiredToken,
47
+ walletAddress,
48
+ });
49
+ if (!relayRequest) {
50
+ throw new Error('Failed to build relay request from fiat amount');
51
+ }
52
+ const relayQuotes = await getRelayQuotes({
53
+ messenger,
54
+ requests: [relayRequest],
55
+ transaction,
56
+ });
57
+ const relayQuote = relayQuotes[0];
58
+ if (!relayQuote) {
59
+ throw new Error('No relay quote available for fiat estimation');
60
+ }
61
+ const relayTotalFeeUsd = getRelayTotalFeeUsd(relayQuote);
62
+ const adjustedAmountFiat = new BigNumber(amountFiat).plus(relayTotalFeeUsd);
63
+ if (!adjustedAmountFiat.isFinite() ||
64
+ !adjustedAmountFiat.gt(0) ||
65
+ !relayTotalFeeUsd.isFinite() ||
66
+ !relayTotalFeeUsd.gte(0)) {
67
+ throw new Error('Invalid fiat amount after relay fee adjustment');
68
+ }
69
+ const adjustedAmount = adjustedAmountFiat.toNumber();
70
+ if (!Number.isFinite(adjustedAmount) || adjustedAmount <= 0) {
71
+ throw new Error('Invalid fiat amount after relay fee adjustment');
72
+ }
73
+ log('Fiat quote flow', {
74
+ adjustedAmountFiat: adjustedAmountFiat.toString(10),
75
+ amountFiat,
76
+ paymentMethods: [fiatPaymentMethod],
77
+ relayTotalFeeUsd: relayTotalFeeUsd.toString(10),
78
+ sourceAmountRaw: relayRequest.sourceTokenAmount,
79
+ transactionId,
80
+ });
81
+ const quotes = await messenger.call('RampsController:getQuotes', {
82
+ amount: adjustedAmount,
83
+ paymentMethods: [fiatPaymentMethod],
84
+ walletAddress,
85
+ });
86
+ log('Fetched ramps quotes', {
87
+ rampsQuotesCount: quotes.success?.length ?? 0,
88
+ transactionId,
89
+ });
90
+ const fiatQuote = pickBestFiatQuote(quotes);
91
+ if (!fiatQuote) {
92
+ throw new Error('No matching ramps quote found for selected provider');
93
+ }
94
+ return [
95
+ combineQuotes({
96
+ adjustedAmountFiat: adjustedAmountFiat.toString(10),
97
+ amountFiat,
98
+ fiatQuote,
99
+ relayQuote,
100
+ }),
101
+ ];
102
+ }
103
+ catch (error) {
104
+ log('Failed to fetch fiat quotes', { error, transactionId });
105
+ }
106
+ return [];
107
+ }
108
+ function getRequiredTokens(tokens) {
109
+ return tokens?.filter((token) => !token.skipIfBalance) ?? [];
110
+ }
111
+ function buildRelayRequestFromAmountFiat({ amountFiat, fiatAsset, messenger, requiredToken, walletAddress, }) {
112
+ const sourceFiatRate = getTokenFiatRate(messenger, fiatAsset.address, fiatAsset.chainId);
113
+ if (!sourceFiatRate) {
114
+ return undefined;
115
+ }
116
+ const sourceAmountRaw = computeRawFromFiatAmount(amountFiat, fiatAsset.decimals, sourceFiatRate.usdRate);
117
+ if (!sourceAmountRaw) {
118
+ return undefined;
119
+ }
120
+ return {
121
+ from: walletAddress,
122
+ // Force EXACT_INPUT mode: source amount is pre-calculated from the fiat
123
+ // amount, so the relay should treat it as a fixed input rather than
124
+ // computing it from the target.
125
+ isPostQuote: true,
126
+ sourceBalanceRaw: sourceAmountRaw,
127
+ sourceChainId: fiatAsset.chainId,
128
+ sourceTokenAddress: fiatAsset.address,
129
+ sourceTokenAmount: sourceAmountRaw,
130
+ targetAmountMinimum: requiredToken.amountRaw,
131
+ targetChainId: requiredToken.chainId,
132
+ targetTokenAddress: requiredToken.address,
133
+ };
134
+ }
135
+ /**
136
+ * Combines fiat and relay legs into a single MM Pay fiat strategy quote.
137
+ *
138
+ * @param params - Combined quote inputs.
139
+ * @param params.adjustedAmountFiat - Fiat amount sent to ramps after adding relay fee estimate.
140
+ * @param params.amountFiat - User-entered fiat amount.
141
+ * @param params.fiatQuote - Selected ramps quote.
142
+ * @param params.relayQuote - Estimated relay quote.
143
+ * @returns A single fiat strategy quote with split fee buckets.
144
+ * @remarks
145
+ * Fee mapping contract for MM Pay Fiat strategy:
146
+ * - `fees.provider`: Total provider fee (relay provider/swap fee + ramps provider/network fee).
147
+ * Consumed by UI transaction fee row and tooltip provider fee.
148
+ * - `fees.providerFiat`: Fiat on-ramp provider fees only (`providerFee + networkFee` from ramps quote).
149
+ * Optional breakdown; client can derive relay portion via `provider - providerFiat`.
150
+ * - `fees.sourceNetwork` / `fees.targetNetwork`: Relay settlement network fees.
151
+ * Consumed by UI transaction fee row and tooltip network fee.
152
+ * - `fees.metaMask`: MM Pay fee (currently 100 bps over `amountFiat + adjustedAmountFiat`).
153
+ * Consumed by UI transaction fee row and tooltip MetaMask fee.
154
+ * - `totals.total` should represent Amount + Transaction Fee using the totals pipeline.
155
+ */
156
+ function combineQuotes({ adjustedAmountFiat, amountFiat, fiatQuote, relayQuote, }) {
157
+ const rampsProviderFee = getRampsProviderFee(fiatQuote);
158
+ const totalProviderFee = new BigNumber(relayQuote.fees.provider.usd)
159
+ .plus(rampsProviderFee)
160
+ .toString(10);
161
+ const rampsProviderFeeStr = rampsProviderFee.toString(10);
162
+ const metaMaskFee = getMetaMaskFee({
163
+ adjustedAmountFiat,
164
+ amountFiat,
165
+ }).toString(10);
166
+ return {
167
+ ...relayQuote,
168
+ fees: {
169
+ ...relayQuote.fees,
170
+ metaMask: {
171
+ fiat: metaMaskFee,
172
+ usd: metaMaskFee,
173
+ },
174
+ provider: {
175
+ fiat: totalProviderFee,
176
+ usd: totalProviderFee,
177
+ },
178
+ providerFiat: {
179
+ fiat: rampsProviderFeeStr,
180
+ usd: rampsProviderFeeStr,
181
+ },
182
+ },
183
+ original: {
184
+ rampsQuote: fiatQuote,
185
+ relayQuote: relayQuote.original,
186
+ },
187
+ strategy: TransactionPayStrategy.Fiat,
188
+ };
189
+ }
190
+ /**
191
+ * Ramps providers handle network gas fees themselves but report them separately
192
+ * as `networkFee` alongside their `providerFee`. We combine both into a single
193
+ * ramps provider fee for the `providerFiat` breakdown.
194
+ *
195
+ * @param fiatQuote - The ramps quote containing provider and network fees.
196
+ * @returns Combined ramps provider fee as a BigNumber.
197
+ */
198
+ function getRampsProviderFee(fiatQuote) {
199
+ return new BigNumber(fiatQuote.quote.providerFee ?? 0).plus(fiatQuote.quote.networkFee ?? 0);
200
+ }
201
+ function getRelayTotalFeeUsd(relayQuote) {
202
+ return new BigNumber(relayQuote.fees.provider.usd)
203
+ .plus(relayQuote.fees.sourceNetwork.estimate.usd)
204
+ .plus(relayQuote.fees.targetNetwork.usd)
205
+ .plus(relayQuote.fees.metaMask.usd);
206
+ }
207
+ function getMetaMaskFee({ adjustedAmountFiat, amountFiat, }) {
208
+ return new BigNumber(amountFiat).plus(adjustedAmountFiat).dividedBy(100);
209
+ }
210
+ //# sourceMappingURL=fiat-quotes.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fiat-quotes.mjs","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-quotes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Quote as RampsQuote } from '@metamask/ramps-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport type { FiatQuote } from './types';\nimport { deriveFiatAssetForFiatPayment, pickBestFiatQuote } from './utils';\nimport { TransactionPayStrategy } from '../../constants';\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategyGetQuotesRequest,\n QuoteRequest,\n TransactionPayRequiredToken,\n TransactionPayQuote,\n} from '../../types';\nimport { computeRawFromFiatAmount, getTokenFiatRate } from '../../utils/token';\nimport { getRelayQuotes } from '../relay/relay-quotes';\nimport type { RelayQuote } from '../relay/types';\n\nconst log = createModuleLogger(projectLogger, 'fiat-strategy');\n\n/**\n * Fetches MM Pay fiat strategy quotes using a relay-first estimation flow.\n *\n * @param request - Strategy quotes request.\n * @returns A single combined fiat strategy quote, or an empty array when inputs/quotes are unavailable.\n * @remarks\n * Flow summary:\n * 1. Read `amountFiat` and selected payment method from transaction pay state.\n * 2. Build a synthetic relay request from `amountFiat` using source token USD rate.\n * 3. Fetch relay quote and compute total relay fee (`provider + source network + target network + MetaMask`).\n * 4. Call ramps quotes with `adjustedAmountFiat = amountFiat + relayTotalFeeUsd`.\n * 5. Pick the configured ramps provider quote and combine it with relay quote into one fiat strategy quote.\n */\nexport async function getFiatQuotes(\n request: PayStrategyGetQuotesRequest,\n): Promise<TransactionPayQuote<FiatQuote>[]> {\n const { fiatPaymentMethod, messenger, transaction } = request;\n const transactionId = transaction.id;\n\n const state = messenger.call('TransactionPayController:getState');\n const transactionData = state.transactionData[transactionId];\n const amountFiat = transactionData?.fiatPayment?.amountFiat;\n const walletAddress = transaction.txParams.from as Hex;\n const requiredTokens = getRequiredTokens(transactionData?.tokens);\n const fiatAsset = deriveFiatAssetForFiatPayment(transaction);\n\n if (\n !amountFiat ||\n !fiatPaymentMethod ||\n !requiredTokens.length ||\n !fiatAsset\n ) {\n return [];\n }\n\n try {\n if (requiredTokens.length > 1) {\n throw new Error(\n 'Multiple required tokens not supported for fiat strategy',\n );\n }\n\n const requiredToken = requiredTokens[0];\n\n const relayRequest = buildRelayRequestFromAmountFiat({\n amountFiat,\n fiatAsset,\n messenger,\n requiredToken,\n walletAddress,\n });\n\n if (!relayRequest) {\n throw new Error('Failed to build relay request from fiat amount');\n }\n\n const relayQuotes = await getRelayQuotes({\n messenger,\n requests: [relayRequest],\n transaction,\n });\n\n const relayQuote = relayQuotes[0];\n if (!relayQuote) {\n throw new Error('No relay quote available for fiat estimation');\n }\n\n const relayTotalFeeUsd = getRelayTotalFeeUsd(relayQuote);\n const adjustedAmountFiat = new BigNumber(amountFiat).plus(relayTotalFeeUsd);\n\n if (\n !adjustedAmountFiat.isFinite() ||\n !adjustedAmountFiat.gt(0) ||\n !relayTotalFeeUsd.isFinite() ||\n !relayTotalFeeUsd.gte(0)\n ) {\n throw new Error('Invalid fiat amount after relay fee adjustment');\n }\n\n const adjustedAmount = adjustedAmountFiat.toNumber();\n\n if (!Number.isFinite(adjustedAmount) || adjustedAmount <= 0) {\n throw new Error('Invalid fiat amount after relay fee adjustment');\n }\n\n log('Fiat quote flow', {\n adjustedAmountFiat: adjustedAmountFiat.toString(10),\n amountFiat,\n paymentMethods: [fiatPaymentMethod],\n relayTotalFeeUsd: relayTotalFeeUsd.toString(10),\n sourceAmountRaw: relayRequest.sourceTokenAmount,\n transactionId,\n });\n\n const quotes = await messenger.call('RampsController:getQuotes', {\n amount: adjustedAmount,\n paymentMethods: [fiatPaymentMethod],\n walletAddress,\n });\n\n log('Fetched ramps quotes', {\n rampsQuotesCount: quotes.success?.length ?? 0,\n transactionId,\n });\n\n const fiatQuote = pickBestFiatQuote(quotes);\n\n if (!fiatQuote) {\n throw new Error('No matching ramps quote found for selected provider');\n }\n\n return [\n combineQuotes({\n adjustedAmountFiat: adjustedAmountFiat.toString(10),\n amountFiat,\n fiatQuote,\n relayQuote,\n }),\n ];\n } catch (error) {\n log('Failed to fetch fiat quotes', { error, transactionId });\n }\n\n return [];\n}\n\nfunction getRequiredTokens(\n tokens?: TransactionPayRequiredToken[],\n): TransactionPayRequiredToken[] {\n return tokens?.filter((token) => !token.skipIfBalance) ?? [];\n}\n\nfunction buildRelayRequestFromAmountFiat({\n amountFiat,\n fiatAsset,\n messenger,\n requiredToken,\n walletAddress,\n}: {\n amountFiat: string;\n fiatAsset: {\n address: Hex;\n chainId: Hex;\n decimals: number;\n };\n messenger: PayStrategyGetQuotesRequest['messenger'];\n requiredToken: TransactionPayRequiredToken;\n walletAddress: Hex;\n}): QuoteRequest | undefined {\n const sourceFiatRate = getTokenFiatRate(\n messenger,\n fiatAsset.address,\n fiatAsset.chainId,\n );\n\n if (!sourceFiatRate) {\n return undefined;\n }\n\n const sourceAmountRaw = computeRawFromFiatAmount(\n amountFiat,\n fiatAsset.decimals,\n sourceFiatRate.usdRate,\n );\n\n if (!sourceAmountRaw) {\n return undefined;\n }\n\n return {\n from: walletAddress,\n // Force EXACT_INPUT mode: source amount is pre-calculated from the fiat\n // amount, so the relay should treat it as a fixed input rather than\n // computing it from the target.\n isPostQuote: true,\n sourceBalanceRaw: sourceAmountRaw,\n sourceChainId: fiatAsset.chainId,\n sourceTokenAddress: fiatAsset.address,\n sourceTokenAmount: sourceAmountRaw,\n targetAmountMinimum: requiredToken.amountRaw,\n targetChainId: requiredToken.chainId,\n targetTokenAddress: requiredToken.address,\n };\n}\n\n/**\n * Combines fiat and relay legs into a single MM Pay fiat strategy quote.\n *\n * @param params - Combined quote inputs.\n * @param params.adjustedAmountFiat - Fiat amount sent to ramps after adding relay fee estimate.\n * @param params.amountFiat - User-entered fiat amount.\n * @param params.fiatQuote - Selected ramps quote.\n * @param params.relayQuote - Estimated relay quote.\n * @returns A single fiat strategy quote with split fee buckets.\n * @remarks\n * Fee mapping contract for MM Pay Fiat strategy:\n * - `fees.provider`: Total provider fee (relay provider/swap fee + ramps provider/network fee).\n * Consumed by UI transaction fee row and tooltip provider fee.\n * - `fees.providerFiat`: Fiat on-ramp provider fees only (`providerFee + networkFee` from ramps quote).\n * Optional breakdown; client can derive relay portion via `provider - providerFiat`.\n * - `fees.sourceNetwork` / `fees.targetNetwork`: Relay settlement network fees.\n * Consumed by UI transaction fee row and tooltip network fee.\n * - `fees.metaMask`: MM Pay fee (currently 100 bps over `amountFiat + adjustedAmountFiat`).\n * Consumed by UI transaction fee row and tooltip MetaMask fee.\n * - `totals.total` should represent Amount + Transaction Fee using the totals pipeline.\n */\nfunction combineQuotes({\n adjustedAmountFiat,\n amountFiat,\n fiatQuote,\n relayQuote,\n}: {\n adjustedAmountFiat: string;\n amountFiat: string;\n fiatQuote: RampsQuote;\n relayQuote: TransactionPayQuote<RelayQuote>;\n}): TransactionPayQuote<FiatQuote> {\n const rampsProviderFee = getRampsProviderFee(fiatQuote);\n const totalProviderFee = new BigNumber(relayQuote.fees.provider.usd)\n .plus(rampsProviderFee)\n .toString(10);\n const rampsProviderFeeStr = rampsProviderFee.toString(10);\n const metaMaskFee = getMetaMaskFee({\n adjustedAmountFiat,\n amountFiat,\n }).toString(10);\n\n return {\n ...relayQuote,\n fees: {\n ...relayQuote.fees,\n metaMask: {\n fiat: metaMaskFee,\n usd: metaMaskFee,\n },\n provider: {\n fiat: totalProviderFee,\n usd: totalProviderFee,\n },\n providerFiat: {\n fiat: rampsProviderFeeStr,\n usd: rampsProviderFeeStr,\n },\n },\n original: {\n rampsQuote: fiatQuote,\n relayQuote: relayQuote.original,\n },\n strategy: TransactionPayStrategy.Fiat,\n };\n}\n\n/**\n * Ramps providers handle network gas fees themselves but report them separately\n * as `networkFee` alongside their `providerFee`. We combine both into a single\n * ramps provider fee for the `providerFiat` breakdown.\n *\n * @param fiatQuote - The ramps quote containing provider and network fees.\n * @returns Combined ramps provider fee as a BigNumber.\n */\nfunction getRampsProviderFee(fiatQuote: RampsQuote): BigNumber {\n return new BigNumber(fiatQuote.quote.providerFee ?? 0).plus(\n fiatQuote.quote.networkFee ?? 0,\n );\n}\n\nfunction getRelayTotalFeeUsd(\n relayQuote: TransactionPayQuote<RelayQuote>,\n): BigNumber {\n return new BigNumber(relayQuote.fees.provider.usd)\n .plus(relayQuote.fees.sourceNetwork.estimate.usd)\n .plus(relayQuote.fees.targetNetwork.usd)\n .plus(relayQuote.fees.metaMask.usd);\n}\n\nfunction getMetaMaskFee({\n adjustedAmountFiat,\n amountFiat,\n}: {\n adjustedAmountFiat: BigNumber.Value;\n amountFiat: BigNumber.Value;\n}): BigNumber {\n return new BigNumber(amountFiat).plus(adjustedAmountFiat).dividedBy(100);\n}\n"]}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.submitFiatQuotes = void 0;
4
+ /**
5
+ * Submit Fiat quotes.
6
+ *
7
+ * @param _request - Strategy execute request.
8
+ * @returns Empty transaction hash until fiat submit implementation is added.
9
+ */
10
+ async function submitFiatQuotes(_request) {
11
+ return { transactionHash: undefined };
12
+ }
13
+ exports.submitFiatQuotes = submitFiatQuotes;
14
+ //# sourceMappingURL=fiat-submit.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fiat-submit.cjs","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":";;;AAGA;;;;;GAKG;AACI,KAAK,UAAU,gBAAgB,CACpC,QAA8C;IAE9C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC;AACxC,CAAC;AAJD,4CAIC","sourcesContent":["import type { FiatQuote } from './types';\nimport type { PayStrategy, PayStrategyExecuteRequest } from '../../types';\n\n/**\n * Submit Fiat quotes.\n *\n * @param _request - Strategy execute request.\n * @returns Empty transaction hash until fiat submit implementation is added.\n */\nexport async function submitFiatQuotes(\n _request: PayStrategyExecuteRequest<FiatQuote>,\n): ReturnType<PayStrategy<FiatQuote>['execute']> {\n return { transactionHash: undefined };\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { FiatQuote } from "./types.cjs";
2
+ import type { PayStrategy, PayStrategyExecuteRequest } from "../../types.cjs";
3
+ /**
4
+ * Submit Fiat quotes.
5
+ *
6
+ * @param _request - Strategy execute request.
7
+ * @returns Empty transaction hash until fiat submit implementation is added.
8
+ */
9
+ export declare function submitFiatQuotes(_request: PayStrategyExecuteRequest<FiatQuote>): ReturnType<PayStrategy<FiatQuote>['execute']>;
10
+ //# sourceMappingURL=fiat-submit.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fiat-submit.d.cts","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAgB;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,yBAAyB,EAAE,wBAAoB;AAE1E;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,yBAAyB,CAAC,SAAS,CAAC,GAC7C,UAAU,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAE/C"}
@@ -0,0 +1,10 @@
1
+ import type { FiatQuote } from "./types.mjs";
2
+ import type { PayStrategy, PayStrategyExecuteRequest } from "../../types.mjs";
3
+ /**
4
+ * Submit Fiat quotes.
5
+ *
6
+ * @param _request - Strategy execute request.
7
+ * @returns Empty transaction hash until fiat submit implementation is added.
8
+ */
9
+ export declare function submitFiatQuotes(_request: PayStrategyExecuteRequest<FiatQuote>): ReturnType<PayStrategy<FiatQuote>['execute']>;
10
+ //# sourceMappingURL=fiat-submit.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fiat-submit.d.mts","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAgB;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,yBAAyB,EAAE,wBAAoB;AAE1E;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,yBAAyB,CAAC,SAAS,CAAC,GAC7C,UAAU,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAE/C"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Submit Fiat quotes.
3
+ *
4
+ * @param _request - Strategy execute request.
5
+ * @returns Empty transaction hash until fiat submit implementation is added.
6
+ */
7
+ export async function submitFiatQuotes(_request) {
8
+ return { transactionHash: undefined };
9
+ }
10
+ //# sourceMappingURL=fiat-submit.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fiat-submit.mjs","sourceRoot":"","sources":["../../../src/strategy/fiat/fiat-submit.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAA8C;IAE9C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC;AACxC,CAAC","sourcesContent":["import type { FiatQuote } from './types';\nimport type { PayStrategy, PayStrategyExecuteRequest } from '../../types';\n\n/**\n * Submit Fiat quotes.\n *\n * @param _request - Strategy execute request.\n * @returns Empty transaction hash until fiat submit implementation is added.\n */\nexport async function submitFiatQuotes(\n _request: PayStrategyExecuteRequest<FiatQuote>,\n): ReturnType<PayStrategy<FiatQuote>['execute']> {\n return { transactionHash: undefined };\n}\n"]}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.cjs","sourceRoot":"","sources":["../../../src/strategy/fiat/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Quote, QuotesResponse } from '@metamask/ramps-controller';\n\nimport type { RelayQuote } from '../relay/types';\n\nexport type FiatQuote = {\n rampsQuote: Quote;\n relayQuote: RelayQuote;\n};\n\nexport type FiatQuotesResponse = QuotesResponse;\n"]}
@@ -0,0 +1,8 @@
1
+ import type { Quote, QuotesResponse } from "@metamask/ramps-controller";
2
+ import type { RelayQuote } from "../relay/types.cjs";
3
+ export type FiatQuote = {
4
+ rampsQuote: Quote;
5
+ relayQuote: RelayQuote;
6
+ };
7
+ export type FiatQuotesResponse = QuotesResponse;
8
+ //# sourceMappingURL=types.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.cts","sourceRoot":"","sources":["../../../src/strategy/fiat/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,mCAAmC;AAExE,OAAO,KAAK,EAAE,UAAU,EAAE,2BAAuB;AAEjD,MAAM,MAAM,SAAS,GAAG;IACtB,UAAU,EAAE,KAAK,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Quote, QuotesResponse } from "@metamask/ramps-controller";
2
+ import type { RelayQuote } from "../relay/types.mjs";
3
+ export type FiatQuote = {
4
+ rampsQuote: Quote;
5
+ relayQuote: RelayQuote;
6
+ };
7
+ export type FiatQuotesResponse = QuotesResponse;
8
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../../../src/strategy/fiat/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,mCAAmC;AAExE,OAAO,KAAK,EAAE,UAAU,EAAE,2BAAuB;AAEjD,MAAM,MAAM,SAAS,GAAG;IACtB,UAAU,EAAE,KAAK,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.mjs.map