@metamask/transaction-pay-controller 12.2.0 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -1
- package/dist/TransactionPayController.cjs +15 -4
- package/dist/TransactionPayController.cjs.map +1 -1
- package/dist/TransactionPayController.d.cts +2 -2
- package/dist/TransactionPayController.d.cts.map +1 -1
- package/dist/TransactionPayController.d.mts +2 -2
- package/dist/TransactionPayController.d.mts.map +1 -1
- package/dist/TransactionPayController.mjs +15 -4
- package/dist/TransactionPayController.mjs.map +1 -1
- package/dist/actions/update-payment-token.cjs +1 -1
- package/dist/actions/update-payment-token.cjs.map +1 -1
- package/dist/actions/update-payment-token.d.cts +1 -1
- package/dist/actions/update-payment-token.d.mts +1 -1
- package/dist/actions/update-payment-token.mjs +1 -1
- package/dist/actions/update-payment-token.mjs.map +1 -1
- package/dist/constants.cjs +19 -1
- 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 +18 -0
- package/dist/constants.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/strategy/bridge/bridge-quotes.cjs +2 -1
- package/dist/strategy/bridge/bridge-quotes.cjs.map +1 -1
- package/dist/strategy/bridge/bridge-quotes.mjs +2 -1
- package/dist/strategy/bridge/bridge-quotes.mjs.map +1 -1
- package/dist/strategy/relay/constants.cjs +1 -2
- package/dist/strategy/relay/constants.cjs.map +1 -1
- package/dist/strategy/relay/constants.d.cts +0 -1
- package/dist/strategy/relay/constants.d.cts.map +1 -1
- package/dist/strategy/relay/constants.d.mts +0 -1
- package/dist/strategy/relay/constants.d.mts.map +1 -1
- package/dist/strategy/relay/constants.mjs +0 -1
- package/dist/strategy/relay/constants.mjs.map +1 -1
- package/dist/strategy/relay/relay-quotes.cjs +50 -15
- package/dist/strategy/relay/relay-quotes.cjs.map +1 -1
- package/dist/strategy/relay/relay-quotes.d.cts.map +1 -1
- package/dist/strategy/relay/relay-quotes.d.mts.map +1 -1
- package/dist/strategy/relay/relay-quotes.mjs +50 -15
- package/dist/strategy/relay/relay-quotes.mjs.map +1 -1
- package/dist/strategy/relay/relay-submit.cjs +68 -21
- package/dist/strategy/relay/relay-submit.cjs.map +1 -1
- package/dist/strategy/relay/relay-submit.d.cts.map +1 -1
- package/dist/strategy/relay/relay-submit.d.mts.map +1 -1
- package/dist/strategy/relay/relay-submit.mjs +68 -21
- package/dist/strategy/relay/relay-submit.mjs.map +1 -1
- package/dist/strategy/relay/types.cjs.map +1 -1
- package/dist/strategy/relay/types.d.cts +11 -0
- package/dist/strategy/relay/types.d.cts.map +1 -1
- package/dist/strategy/relay/types.d.mts +11 -0
- package/dist/strategy/relay/types.d.mts.map +1 -1
- package/dist/strategy/relay/types.mjs.map +1 -1
- package/dist/strategy/test/TestStrategy.cjs +0 -2
- package/dist/strategy/test/TestStrategy.cjs.map +1 -1
- package/dist/strategy/test/TestStrategy.d.cts.map +1 -1
- package/dist/strategy/test/TestStrategy.d.mts.map +1 -1
- package/dist/strategy/test/TestStrategy.mjs +0 -2
- package/dist/strategy/test/TestStrategy.mjs.map +1 -1
- package/dist/tests/messenger-mock.d.cts +3 -3
- package/dist/tests/messenger-mock.d.mts +3 -3
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +50 -11
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +50 -11
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils/quotes.cjs +67 -5
- package/dist/utils/quotes.cjs.map +1 -1
- package/dist/utils/quotes.d.cts.map +1 -1
- package/dist/utils/quotes.d.mts.map +1 -1
- package/dist/utils/quotes.mjs +67 -5
- package/dist/utils/quotes.mjs.map +1 -1
- package/dist/utils/required-tokens.cjs +1 -1
- package/dist/utils/required-tokens.cjs.map +1 -1
- package/dist/utils/required-tokens.mjs +1 -1
- package/dist/utils/required-tokens.mjs.map +1 -1
- package/dist/utils/source-amounts.cjs +47 -4
- package/dist/utils/source-amounts.cjs.map +1 -1
- package/dist/utils/source-amounts.d.cts.map +1 -1
- package/dist/utils/source-amounts.d.mts.map +1 -1
- package/dist/utils/source-amounts.mjs +48 -5
- package/dist/utils/source-amounts.mjs.map +1 -1
- package/dist/utils/token.cjs +18 -6
- package/dist/utils/token.cjs.map +1 -1
- package/dist/utils/token.d.cts +18 -0
- package/dist/utils/token.d.cts.map +1 -1
- package/dist/utils/token.d.mts +18 -0
- package/dist/utils/token.d.mts.map +1 -1
- package/dist/utils/token.mjs +16 -5
- package/dist/utils/token.mjs.map +1 -1
- package/dist/utils/totals.cjs +5 -1
- 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 +5 -1
- package/dist/utils/totals.mjs.map +1 -1
- package/package.json +9 -9
package/dist/utils/quotes.cjs
CHANGED
|
@@ -25,10 +25,11 @@ async function updateQuotes(request) {
|
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
27
|
log('Updating quotes', { transactionId });
|
|
28
|
-
const { isMaxAmount, paymentToken, sourceAmounts, tokens } = transactionData;
|
|
28
|
+
const { isMaxAmount, isPostQuote, paymentToken, sourceAmounts, tokens } = transactionData;
|
|
29
29
|
const requests = buildQuoteRequests({
|
|
30
30
|
from: transaction.txParams.from,
|
|
31
31
|
isMaxAmount: isMaxAmount ?? false,
|
|
32
|
+
isPostQuote,
|
|
32
33
|
paymentToken,
|
|
33
34
|
sourceAmounts,
|
|
34
35
|
tokens,
|
|
@@ -49,6 +50,7 @@ async function updateQuotes(request) {
|
|
|
49
50
|
log('Calculated totals', { transactionId, totals });
|
|
50
51
|
syncTransaction({
|
|
51
52
|
batchTransactions,
|
|
53
|
+
isPostQuote,
|
|
52
54
|
messenger: messenger,
|
|
53
55
|
paymentToken,
|
|
54
56
|
totals,
|
|
@@ -73,12 +75,13 @@ exports.updateQuotes = updateQuotes;
|
|
|
73
75
|
*
|
|
74
76
|
* @param request - Request object.
|
|
75
77
|
* @param request.batchTransactions - Batch transactions to sync.
|
|
78
|
+
* @param request.isPostQuote - Whether this is a post-quote flow.
|
|
76
79
|
* @param request.messenger - Messenger instance.
|
|
77
|
-
* @param request.paymentToken - Payment token
|
|
80
|
+
* @param request.paymentToken - Payment token (source for standard flows, destination for post-quote).
|
|
78
81
|
* @param request.totals - Calculated totals.
|
|
79
82
|
* @param request.transactionId - ID of the transaction to sync.
|
|
80
83
|
*/
|
|
81
|
-
function syncTransaction({ batchTransactions, messenger, paymentToken, totals, transactionId, }) {
|
|
84
|
+
function syncTransaction({ batchTransactions, isPostQuote, messenger, paymentToken, totals, transactionId, }) {
|
|
82
85
|
if (!paymentToken) {
|
|
83
86
|
return;
|
|
84
87
|
}
|
|
@@ -92,6 +95,7 @@ function syncTransaction({ batchTransactions, messenger, paymentToken, totals, t
|
|
|
92
95
|
tx.metamaskPay = {
|
|
93
96
|
bridgeFeeFiat: totals.fees.provider.usd,
|
|
94
97
|
chainId: paymentToken.chainId,
|
|
98
|
+
isPostQuote,
|
|
95
99
|
networkFeeFiat: totals.fees.sourceNetwork.estimate.usd,
|
|
96
100
|
targetFiat: totals.targetAmount.usd,
|
|
97
101
|
tokenAddress: paymentToken.address,
|
|
@@ -142,16 +146,29 @@ exports.refreshQuotes = refreshQuotes;
|
|
|
142
146
|
* @param request - Request parameters.
|
|
143
147
|
* @param request.from - Address from which the transaction is sent.
|
|
144
148
|
* @param request.isMaxAmount - Whether the transaction is a maximum amount transaction.
|
|
145
|
-
* @param request.
|
|
149
|
+
* @param request.isPostQuote - Whether this is a post-quote flow.
|
|
150
|
+
* @param request.paymentToken - Payment token (source for standard flows, destination for post-quote).
|
|
146
151
|
* @param request.sourceAmounts - Source amounts for the transaction.
|
|
147
152
|
* @param request.tokens - Required tokens for the transaction.
|
|
148
153
|
* @param request.transactionId - ID of the transaction.
|
|
149
154
|
* @returns Array of quote requests.
|
|
150
155
|
*/
|
|
151
|
-
function buildQuoteRequests({ from, isMaxAmount, paymentToken, sourceAmounts, tokens, transactionId, }) {
|
|
156
|
+
function buildQuoteRequests({ from, isMaxAmount, isPostQuote, paymentToken, sourceAmounts, tokens, transactionId, }) {
|
|
152
157
|
if (!paymentToken) {
|
|
153
158
|
return [];
|
|
154
159
|
}
|
|
160
|
+
if (isPostQuote) {
|
|
161
|
+
// Post-quote flow: source = transaction's required token, target = paymentToken (destination)
|
|
162
|
+
// The user wants to receive the transaction output in paymentToken
|
|
163
|
+
return buildPostQuoteRequests({
|
|
164
|
+
from,
|
|
165
|
+
isMaxAmount,
|
|
166
|
+
destinationToken: paymentToken,
|
|
167
|
+
sourceAmounts,
|
|
168
|
+
transactionId,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
// Standard flow: source = paymentToken, target = required tokens
|
|
155
172
|
const requests = (sourceAmounts ?? []).map((sourceAmount) => {
|
|
156
173
|
const token = tokens.find((singleToken) => singleToken.address === sourceAmount.targetTokenAddress);
|
|
157
174
|
return {
|
|
@@ -171,6 +188,51 @@ function buildQuoteRequests({ from, isMaxAmount, paymentToken, sourceAmounts, to
|
|
|
171
188
|
}
|
|
172
189
|
return requests;
|
|
173
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Build quote requests for post-quote flows.
|
|
193
|
+
* In this flow, the source is the transaction's required token,
|
|
194
|
+
* and the target is the user's selected destination token (paymentToken).
|
|
195
|
+
*
|
|
196
|
+
* @param request - Request parameters.
|
|
197
|
+
* @param request.from - Address from which the transaction is sent.
|
|
198
|
+
* @param request.isMaxAmount - Whether the transaction is a maximum amount transaction.
|
|
199
|
+
* @param request.destinationToken - Destination token (paymentToken in post-quote mode).
|
|
200
|
+
* @param request.sourceAmounts - Source amounts for the transaction (includes source token info).
|
|
201
|
+
* @param request.transactionId - ID of the transaction.
|
|
202
|
+
* @returns Array of quote requests for post-quote flow.
|
|
203
|
+
*/
|
|
204
|
+
function buildPostQuoteRequests({ from, isMaxAmount, destinationToken, sourceAmounts, transactionId, }) {
|
|
205
|
+
// Find the source amount where targetTokenAddress matches the destination token
|
|
206
|
+
const sourceAmount = sourceAmounts?.find((amount) => amount.targetTokenAddress.toLowerCase() ===
|
|
207
|
+
destinationToken.address.toLowerCase());
|
|
208
|
+
// Same-token-same-chain cases are already filtered in source-amounts.ts
|
|
209
|
+
if (!sourceAmount?.sourceBalanceRaw ||
|
|
210
|
+
!sourceAmount.sourceChainId ||
|
|
211
|
+
!sourceAmount.sourceTokenAddress) {
|
|
212
|
+
log('No valid source amount found for post-quote request', {
|
|
213
|
+
transactionId,
|
|
214
|
+
});
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
const request = {
|
|
218
|
+
from,
|
|
219
|
+
isMaxAmount,
|
|
220
|
+
isPostQuote: true,
|
|
221
|
+
sourceBalanceRaw: sourceAmount.sourceBalanceRaw,
|
|
222
|
+
sourceTokenAmount: sourceAmount.sourceAmountRaw,
|
|
223
|
+
sourceChainId: sourceAmount.sourceChainId,
|
|
224
|
+
sourceTokenAddress: sourceAmount.sourceTokenAddress,
|
|
225
|
+
// For post-quote flows, use EXACT_INPUT - user specifies how much to send,
|
|
226
|
+
// and we show them how much they'll receive after fees
|
|
227
|
+
targetAmountMinimum: '0',
|
|
228
|
+
targetChainId: destinationToken.chainId,
|
|
229
|
+
targetTokenAddress: destinationToken.address,
|
|
230
|
+
};
|
|
231
|
+
log('Post-quote request built', { transactionId, request });
|
|
232
|
+
// Currently only single token post-quote flows are supported.
|
|
233
|
+
// Multiple token support would require multiple quotes for each required token.
|
|
234
|
+
return [request];
|
|
235
|
+
}
|
|
174
236
|
/**
|
|
175
237
|
* Retrieve quotes for a transaction.
|
|
176
238
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotes.cjs","sourceRoot":"","sources":["../../src/utils/quotes.ts"],"names":[],"mappings":"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","sourcesContent":["import { TransactionStatus } from '@metamask/transaction-controller';\nimport type { BatchTransaction } from '@metamask/transaction-controller';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport type { Hex, Json } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\n\nimport { getStrategy, getStrategyByName } from './strategy';\nimport { calculateTotals } from './totals';\nimport { getTransaction, updateTransaction } from './transaction';\nimport { projectLogger } from '../logger';\nimport type {\n QuoteRequest,\n TransactionData,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n TransactionPayRequiredToken,\n TransactionPaySourceAmount,\n TransactionPayTotals,\n TransactionPaymentToken,\n UpdateTransactionDataCallback,\n} from '../types';\n\nconst DEFAULT_REFRESH_INTERVAL = 30 * 1000; // 30 Seconds\n\nconst log = createModuleLogger(projectLogger, 'quotes');\n\nexport type UpdateQuotesRequest = {\n messenger: TransactionPayControllerMessenger;\n transactionData: TransactionData | undefined;\n transactionId: string;\n updateTransactionData: UpdateTransactionDataCallback;\n};\n\n/**\n * Update the quotes for a specific transaction.\n *\n * @param request - Request parameters.\n * @returns Boolean indicating if the quotes were updated.\n */\nexport async function updateQuotes(\n request: UpdateQuotesRequest,\n): Promise<boolean> {\n const { messenger, transactionData, transactionId, updateTransactionData } =\n request;\n\n const transaction = getTransaction(transactionId, messenger);\n\n if (!transaction || !transactionData) {\n throw new Error('Transaction not found');\n }\n\n if (transaction?.status !== TransactionStatus.unapproved) {\n return false;\n }\n\n log('Updating quotes', { transactionId });\n\n const { isMaxAmount, paymentToken, sourceAmounts, tokens } = transactionData;\n\n const requests = buildQuoteRequests({\n from: transaction.txParams.from as Hex,\n isMaxAmount: isMaxAmount ?? false,\n paymentToken,\n sourceAmounts,\n tokens,\n transactionId,\n });\n\n updateTransactionData(transactionId, (data) => {\n data.isLoading = true;\n });\n\n try {\n const { batchTransactions, quotes } = await getQuotes(\n transaction,\n requests,\n messenger,\n );\n\n const totals = calculateTotals({\n isMaxAmount,\n messenger,\n quotes: quotes as TransactionPayQuote<unknown>[],\n tokens,\n transaction,\n });\n\n log('Calculated totals', { transactionId, totals });\n\n syncTransaction({\n batchTransactions,\n messenger: messenger as never,\n paymentToken,\n totals,\n transactionId,\n });\n\n updateTransactionData(transactionId, (data) => {\n data.quotes = quotes as never;\n data.quotesLastUpdated = Date.now();\n data.totals = totals;\n });\n } finally {\n updateTransactionData(transactionId, (data) => {\n data.isLoading = false;\n });\n }\n\n return true;\n}\n\n/**\n * Sync batch transactions to the transaction meta.\n *\n * @param request - Request object.\n * @param request.batchTransactions - Batch transactions to sync.\n * @param request.messenger - Messenger instance.\n * @param request.paymentToken - Payment token used.\n * @param request.totals - Calculated totals.\n * @param request.transactionId - ID of the transaction to sync.\n */\nfunction syncTransaction({\n batchTransactions,\n messenger,\n paymentToken,\n totals,\n transactionId,\n}: {\n batchTransactions: BatchTransaction[];\n messenger: TransactionPayControllerMessenger;\n paymentToken: TransactionPaymentToken | undefined;\n totals: TransactionPayTotals;\n transactionId: string;\n}): void {\n if (!paymentToken) {\n return;\n }\n\n updateTransaction(\n {\n transactionId,\n messenger: messenger as never,\n note: 'Update transaction pay data',\n },\n (tx: TransactionMeta) => {\n tx.batchTransactions = batchTransactions;\n tx.batchTransactionsOptions = {};\n\n tx.metamaskPay = {\n bridgeFeeFiat: totals.fees.provider.usd,\n chainId: paymentToken.chainId,\n networkFeeFiat: totals.fees.sourceNetwork.estimate.usd,\n targetFiat: totals.targetAmount.usd,\n tokenAddress: paymentToken.address,\n totalFiat: totals.total.usd,\n };\n },\n );\n}\n\n/**\n * Refresh quotes for all transactions if expired.\n *\n * @param messenger - Messenger instance.\n * @param updateTransactionData - Callback to update transaction data.\n */\nexport async function refreshQuotes(\n messenger: TransactionPayControllerMessenger,\n updateTransactionData: UpdateTransactionDataCallback,\n): Promise<void> {\n const state = messenger.call('TransactionPayController:getState');\n const transactionIds = Object.keys(state.transactionData);\n\n for (const transactionId of transactionIds) {\n const transactionData = state.transactionData[transactionId];\n const { isLoading, quotes, quotesLastUpdated } = transactionData;\n\n if (isLoading || !quotes?.length) {\n continue;\n }\n\n const strategyName = quotes[0].strategy;\n const strategy = getStrategyByName(strategyName);\n\n const refreshInterval =\n (await strategy.getRefreshInterval?.({\n chainId: quotes[0].request.sourceChainId,\n messenger,\n })) ?? DEFAULT_REFRESH_INTERVAL;\n\n const isExpired = Date.now() - (quotesLastUpdated ?? 0) > refreshInterval;\n\n if (!isExpired) {\n continue;\n }\n\n const isUpdated = await updateQuotes({\n messenger,\n transactionData,\n transactionId,\n updateTransactionData,\n });\n\n if (isUpdated) {\n log('Refreshed quotes', { transactionId, strategy: strategyName });\n }\n }\n}\n\n/**\n * Build quote requests required to retrieve quotes.\n *\n * @param request - Request parameters.\n * @param request.from - Address from which the transaction is sent.\n * @param request.isMaxAmount - Whether the transaction is a maximum amount transaction.\n * @param request.paymentToken - Payment token used for the transaction.\n * @param request.sourceAmounts - Source amounts for the transaction.\n * @param request.tokens - Required tokens for the transaction.\n * @param request.transactionId - ID of the transaction.\n * @returns Array of quote requests.\n */\nfunction buildQuoteRequests({\n from,\n isMaxAmount,\n paymentToken,\n sourceAmounts,\n tokens,\n transactionId,\n}: {\n from: Hex;\n isMaxAmount: boolean;\n paymentToken: TransactionPaymentToken | undefined;\n sourceAmounts: TransactionPaySourceAmount[] | undefined;\n tokens: TransactionPayRequiredToken[];\n transactionId: string;\n}): QuoteRequest[] {\n if (!paymentToken) {\n return [];\n }\n\n const requests = (sourceAmounts ?? []).map((sourceAmount) => {\n const token = tokens.find(\n (singleToken) => singleToken.address === sourceAmount.targetTokenAddress,\n ) as TransactionPayRequiredToken;\n\n return {\n from,\n isMaxAmount,\n sourceBalanceRaw: paymentToken.balanceRaw,\n sourceTokenAmount: sourceAmount.sourceAmountRaw,\n sourceChainId: paymentToken.chainId,\n sourceTokenAddress: paymentToken.address,\n targetAmountMinimum: token.allowUnderMinimum ? '0' : token.amountRaw,\n targetChainId: token.chainId,\n targetTokenAddress: token.address,\n };\n });\n\n if (!requests.length) {\n log('No quote requests', { transactionId });\n }\n\n return requests;\n}\n\n/**\n * Retrieve quotes for a transaction.\n *\n * @param transaction - Transaction metadata.\n * @param requests - Quote requests.\n * @param messenger - Controller messenger.\n * @returns An object containing batch transactions and quotes.\n */\nasync function getQuotes(\n transaction: TransactionMeta,\n requests: QuoteRequest[],\n messenger: TransactionPayControllerMessenger,\n): Promise<{\n batchTransactions: BatchTransaction[];\n quotes: TransactionPayQuote<Json>[];\n}> {\n const { id: transactionId } = transaction;\n const strategy = getStrategy(messenger as never, transaction);\n let quotes: TransactionPayQuote<Json>[] | undefined = [];\n\n try {\n quotes = requests?.length\n ? ((await strategy.getQuotes({\n messenger,\n requests,\n transaction,\n })) as TransactionPayQuote<Json>[])\n : [];\n } catch (error) {\n log('Error fetching quotes', { error, transactionId });\n }\n\n log('Updated', { transactionId, quotes });\n\n const batchTransactions =\n quotes?.length && strategy.getBatchTransactions\n ? await strategy.getBatchTransactions({\n messenger,\n quotes,\n })\n : [];\n\n log('Batch transactions', { transactionId, batchTransactions });\n\n return {\n batchTransactions,\n quotes,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"quotes.cjs","sourceRoot":"","sources":["../../src/utils/quotes.ts"],"names":[],"mappings":"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","sourcesContent":["import { TransactionStatus } from '@metamask/transaction-controller';\nimport type { BatchTransaction } from '@metamask/transaction-controller';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport type { Hex, Json } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\n\nimport { getStrategy, getStrategyByName } from './strategy';\nimport { calculateTotals } from './totals';\nimport { getTransaction, updateTransaction } from './transaction';\nimport { projectLogger } from '../logger';\nimport type {\n QuoteRequest,\n TransactionData,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n TransactionPayRequiredToken,\n TransactionPaySourceAmount,\n TransactionPayTotals,\n TransactionPaymentToken,\n UpdateTransactionDataCallback,\n} from '../types';\n\nconst DEFAULT_REFRESH_INTERVAL = 30 * 1000; // 30 Seconds\n\nconst log = createModuleLogger(projectLogger, 'quotes');\n\nexport type UpdateQuotesRequest = {\n messenger: TransactionPayControllerMessenger;\n transactionData: TransactionData | undefined;\n transactionId: string;\n updateTransactionData: UpdateTransactionDataCallback;\n};\n\n/**\n * Update the quotes for a specific transaction.\n *\n * @param request - Request parameters.\n * @returns Boolean indicating if the quotes were updated.\n */\nexport async function updateQuotes(\n request: UpdateQuotesRequest,\n): Promise<boolean> {\n const { messenger, transactionData, transactionId, updateTransactionData } =\n request;\n\n const transaction = getTransaction(transactionId, messenger);\n\n if (!transaction || !transactionData) {\n throw new Error('Transaction not found');\n }\n\n if (transaction?.status !== TransactionStatus.unapproved) {\n return false;\n }\n\n log('Updating quotes', { transactionId });\n\n const { isMaxAmount, isPostQuote, paymentToken, sourceAmounts, tokens } =\n transactionData;\n\n const requests = buildQuoteRequests({\n from: transaction.txParams.from as Hex,\n isMaxAmount: isMaxAmount ?? false,\n isPostQuote,\n paymentToken,\n sourceAmounts,\n tokens,\n transactionId,\n });\n\n updateTransactionData(transactionId, (data) => {\n data.isLoading = true;\n });\n\n try {\n const { batchTransactions, quotes } = await getQuotes(\n transaction,\n requests,\n messenger,\n );\n\n const totals = calculateTotals({\n isMaxAmount,\n messenger,\n quotes: quotes as TransactionPayQuote<unknown>[],\n tokens,\n transaction,\n });\n\n log('Calculated totals', { transactionId, totals });\n\n syncTransaction({\n batchTransactions,\n isPostQuote,\n messenger: messenger as never,\n paymentToken,\n totals,\n transactionId,\n });\n\n updateTransactionData(transactionId, (data) => {\n data.quotes = quotes as never;\n data.quotesLastUpdated = Date.now();\n data.totals = totals;\n });\n } finally {\n updateTransactionData(transactionId, (data) => {\n data.isLoading = false;\n });\n }\n\n return true;\n}\n\n/**\n * Sync batch transactions to the transaction meta.\n *\n * @param request - Request object.\n * @param request.batchTransactions - Batch transactions to sync.\n * @param request.isPostQuote - Whether this is a post-quote flow.\n * @param request.messenger - Messenger instance.\n * @param request.paymentToken - Payment token (source for standard flows, destination for post-quote).\n * @param request.totals - Calculated totals.\n * @param request.transactionId - ID of the transaction to sync.\n */\nfunction syncTransaction({\n batchTransactions,\n isPostQuote,\n messenger,\n paymentToken,\n totals,\n transactionId,\n}: {\n batchTransactions: BatchTransaction[];\n isPostQuote?: boolean;\n messenger: TransactionPayControllerMessenger;\n paymentToken: TransactionPaymentToken | undefined;\n totals: TransactionPayTotals;\n transactionId: string;\n}): void {\n if (!paymentToken) {\n return;\n }\n\n updateTransaction(\n {\n transactionId,\n messenger: messenger as never,\n note: 'Update transaction pay data',\n },\n (tx: TransactionMeta) => {\n tx.batchTransactions = batchTransactions;\n tx.batchTransactionsOptions = {};\n\n tx.metamaskPay = {\n bridgeFeeFiat: totals.fees.provider.usd,\n chainId: paymentToken.chainId,\n isPostQuote,\n networkFeeFiat: totals.fees.sourceNetwork.estimate.usd,\n targetFiat: totals.targetAmount.usd,\n tokenAddress: paymentToken.address,\n totalFiat: totals.total.usd,\n };\n },\n );\n}\n\n/**\n * Refresh quotes for all transactions if expired.\n *\n * @param messenger - Messenger instance.\n * @param updateTransactionData - Callback to update transaction data.\n */\nexport async function refreshQuotes(\n messenger: TransactionPayControllerMessenger,\n updateTransactionData: UpdateTransactionDataCallback,\n): Promise<void> {\n const state = messenger.call('TransactionPayController:getState');\n const transactionIds = Object.keys(state.transactionData);\n\n for (const transactionId of transactionIds) {\n const transactionData = state.transactionData[transactionId];\n const { isLoading, quotes, quotesLastUpdated } = transactionData;\n\n if (isLoading || !quotes?.length) {\n continue;\n }\n\n const strategyName = quotes[0].strategy;\n const strategy = getStrategyByName(strategyName);\n\n const refreshInterval =\n (await strategy.getRefreshInterval?.({\n chainId: quotes[0].request.sourceChainId,\n messenger,\n })) ?? DEFAULT_REFRESH_INTERVAL;\n\n const isExpired = Date.now() - (quotesLastUpdated ?? 0) > refreshInterval;\n\n if (!isExpired) {\n continue;\n }\n\n const isUpdated = await updateQuotes({\n messenger,\n transactionData,\n transactionId,\n updateTransactionData,\n });\n\n if (isUpdated) {\n log('Refreshed quotes', { transactionId, strategy: strategyName });\n }\n }\n}\n\n/**\n * Build quote requests required to retrieve quotes.\n *\n * @param request - Request parameters.\n * @param request.from - Address from which the transaction is sent.\n * @param request.isMaxAmount - Whether the transaction is a maximum amount transaction.\n * @param request.isPostQuote - Whether this is a post-quote flow.\n * @param request.paymentToken - Payment token (source for standard flows, destination for post-quote).\n * @param request.sourceAmounts - Source amounts for the transaction.\n * @param request.tokens - Required tokens for the transaction.\n * @param request.transactionId - ID of the transaction.\n * @returns Array of quote requests.\n */\nfunction buildQuoteRequests({\n from,\n isMaxAmount,\n isPostQuote,\n paymentToken,\n sourceAmounts,\n tokens,\n transactionId,\n}: {\n from: Hex;\n isMaxAmount: boolean;\n isPostQuote?: boolean;\n paymentToken: TransactionPaymentToken | undefined;\n sourceAmounts: TransactionPaySourceAmount[] | undefined;\n tokens: TransactionPayRequiredToken[];\n transactionId: string;\n}): QuoteRequest[] {\n if (!paymentToken) {\n return [];\n }\n\n if (isPostQuote) {\n // Post-quote flow: source = transaction's required token, target = paymentToken (destination)\n // The user wants to receive the transaction output in paymentToken\n return buildPostQuoteRequests({\n from,\n isMaxAmount,\n destinationToken: paymentToken,\n sourceAmounts,\n transactionId,\n });\n }\n\n // Standard flow: source = paymentToken, target = required tokens\n const requests = (sourceAmounts ?? []).map((sourceAmount) => {\n const token = tokens.find(\n (singleToken) => singleToken.address === sourceAmount.targetTokenAddress,\n ) as TransactionPayRequiredToken;\n\n return {\n from,\n isMaxAmount,\n sourceBalanceRaw: paymentToken.balanceRaw,\n sourceTokenAmount: sourceAmount.sourceAmountRaw,\n sourceChainId: paymentToken.chainId,\n sourceTokenAddress: paymentToken.address,\n targetAmountMinimum: token.allowUnderMinimum ? '0' : token.amountRaw,\n targetChainId: token.chainId,\n targetTokenAddress: token.address,\n };\n });\n\n if (!requests.length) {\n log('No quote requests', { transactionId });\n }\n\n return requests;\n}\n\n/**\n * Build quote requests for post-quote flows.\n * In this flow, the source is the transaction's required token,\n * and the target is the user's selected destination token (paymentToken).\n *\n * @param request - Request parameters.\n * @param request.from - Address from which the transaction is sent.\n * @param request.isMaxAmount - Whether the transaction is a maximum amount transaction.\n * @param request.destinationToken - Destination token (paymentToken in post-quote mode).\n * @param request.sourceAmounts - Source amounts for the transaction (includes source token info).\n * @param request.transactionId - ID of the transaction.\n * @returns Array of quote requests for post-quote flow.\n */\nfunction buildPostQuoteRequests({\n from,\n isMaxAmount,\n destinationToken,\n sourceAmounts,\n transactionId,\n}: {\n from: Hex;\n isMaxAmount: boolean;\n destinationToken: TransactionPaymentToken;\n sourceAmounts: TransactionPaySourceAmount[] | undefined;\n transactionId: string;\n}): QuoteRequest[] {\n // Find the source amount where targetTokenAddress matches the destination token\n const sourceAmount = sourceAmounts?.find(\n (amount) =>\n amount.targetTokenAddress.toLowerCase() ===\n destinationToken.address.toLowerCase(),\n );\n\n // Same-token-same-chain cases are already filtered in source-amounts.ts\n if (\n !sourceAmount?.sourceBalanceRaw ||\n !sourceAmount.sourceChainId ||\n !sourceAmount.sourceTokenAddress\n ) {\n log('No valid source amount found for post-quote request', {\n transactionId,\n });\n return [];\n }\n\n const request: QuoteRequest = {\n from,\n isMaxAmount,\n isPostQuote: true,\n sourceBalanceRaw: sourceAmount.sourceBalanceRaw,\n sourceTokenAmount: sourceAmount.sourceAmountRaw,\n sourceChainId: sourceAmount.sourceChainId,\n sourceTokenAddress: sourceAmount.sourceTokenAddress,\n // For post-quote flows, use EXACT_INPUT - user specifies how much to send,\n // and we show them how much they'll receive after fees\n targetAmountMinimum: '0',\n targetChainId: destinationToken.chainId,\n targetTokenAddress: destinationToken.address,\n };\n\n log('Post-quote request built', { transactionId, request });\n\n // Currently only single token post-quote flows are supported.\n // Multiple token support would require multiple quotes for each required token.\n return [request];\n}\n\n/**\n * Retrieve quotes for a transaction.\n *\n * @param transaction - Transaction metadata.\n * @param requests - Quote requests.\n * @param messenger - Controller messenger.\n * @returns An object containing batch transactions and quotes.\n */\nasync function getQuotes(\n transaction: TransactionMeta,\n requests: QuoteRequest[],\n messenger: TransactionPayControllerMessenger,\n): Promise<{\n batchTransactions: BatchTransaction[];\n quotes: TransactionPayQuote<Json>[];\n}> {\n const { id: transactionId } = transaction;\n const strategy = getStrategy(messenger as never, transaction);\n let quotes: TransactionPayQuote<Json>[] | undefined = [];\n\n try {\n quotes = requests?.length\n ? ((await strategy.getQuotes({\n messenger,\n requests,\n transaction,\n })) as TransactionPayQuote<Json>[])\n : [];\n } catch (error) {\n log('Error fetching quotes', { error, transactionId });\n }\n\n log('Updated', { transactionId, quotes });\n\n const batchTransactions =\n quotes?.length && strategy.getBatchTransactions\n ? await strategy.getBatchTransactions({\n messenger,\n quotes,\n })\n : [];\n\n log('Batch transactions', { transactionId, batchTransactions });\n\n return {\n batchTransactions,\n quotes,\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotes.d.cts","sourceRoot":"","sources":["../../src/utils/quotes.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,eAAe,EACf,iCAAiC,EAMjC,6BAA6B,EAC9B,qBAAiB;AAMlB,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,6BAA6B,CAAC;CACtD,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"quotes.d.cts","sourceRoot":"","sources":["../../src/utils/quotes.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,eAAe,EACf,iCAAiC,EAMjC,6BAA6B,EAC9B,qBAAiB;AAMlB,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,6BAA6B,CAAC;CACtD,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAuElB;AAuDD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,iCAAiC,EAC5C,qBAAqB,EAAE,6BAA6B,GACnD,OAAO,CAAC,IAAI,CAAC,CAsCf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotes.d.mts","sourceRoot":"","sources":["../../src/utils/quotes.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,eAAe,EACf,iCAAiC,EAMjC,6BAA6B,EAC9B,qBAAiB;AAMlB,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,6BAA6B,CAAC;CACtD,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"quotes.d.mts","sourceRoot":"","sources":["../../src/utils/quotes.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,eAAe,EACf,iCAAiC,EAMjC,6BAA6B,EAC9B,qBAAiB;AAMlB,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;IAC7C,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,6BAA6B,CAAC;CACtD,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,OAAO,CAAC,CAuElB;AAuDD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,iCAAiC,EAC5C,qBAAqB,EAAE,6BAA6B,GACnD,OAAO,CAAC,IAAI,CAAC,CAsCf"}
|
package/dist/utils/quotes.mjs
CHANGED
|
@@ -22,10 +22,11 @@ export async function updateQuotes(request) {
|
|
|
22
22
|
return false;
|
|
23
23
|
}
|
|
24
24
|
log('Updating quotes', { transactionId });
|
|
25
|
-
const { isMaxAmount, paymentToken, sourceAmounts, tokens } = transactionData;
|
|
25
|
+
const { isMaxAmount, isPostQuote, paymentToken, sourceAmounts, tokens } = transactionData;
|
|
26
26
|
const requests = buildQuoteRequests({
|
|
27
27
|
from: transaction.txParams.from,
|
|
28
28
|
isMaxAmount: isMaxAmount ?? false,
|
|
29
|
+
isPostQuote,
|
|
29
30
|
paymentToken,
|
|
30
31
|
sourceAmounts,
|
|
31
32
|
tokens,
|
|
@@ -46,6 +47,7 @@ export async function updateQuotes(request) {
|
|
|
46
47
|
log('Calculated totals', { transactionId, totals });
|
|
47
48
|
syncTransaction({
|
|
48
49
|
batchTransactions,
|
|
50
|
+
isPostQuote,
|
|
49
51
|
messenger: messenger,
|
|
50
52
|
paymentToken,
|
|
51
53
|
totals,
|
|
@@ -69,12 +71,13 @@ export async function updateQuotes(request) {
|
|
|
69
71
|
*
|
|
70
72
|
* @param request - Request object.
|
|
71
73
|
* @param request.batchTransactions - Batch transactions to sync.
|
|
74
|
+
* @param request.isPostQuote - Whether this is a post-quote flow.
|
|
72
75
|
* @param request.messenger - Messenger instance.
|
|
73
|
-
* @param request.paymentToken - Payment token
|
|
76
|
+
* @param request.paymentToken - Payment token (source for standard flows, destination for post-quote).
|
|
74
77
|
* @param request.totals - Calculated totals.
|
|
75
78
|
* @param request.transactionId - ID of the transaction to sync.
|
|
76
79
|
*/
|
|
77
|
-
function syncTransaction({ batchTransactions, messenger, paymentToken, totals, transactionId, }) {
|
|
80
|
+
function syncTransaction({ batchTransactions, isPostQuote, messenger, paymentToken, totals, transactionId, }) {
|
|
78
81
|
if (!paymentToken) {
|
|
79
82
|
return;
|
|
80
83
|
}
|
|
@@ -88,6 +91,7 @@ function syncTransaction({ batchTransactions, messenger, paymentToken, totals, t
|
|
|
88
91
|
tx.metamaskPay = {
|
|
89
92
|
bridgeFeeFiat: totals.fees.provider.usd,
|
|
90
93
|
chainId: paymentToken.chainId,
|
|
94
|
+
isPostQuote,
|
|
91
95
|
networkFeeFiat: totals.fees.sourceNetwork.estimate.usd,
|
|
92
96
|
targetFiat: totals.targetAmount.usd,
|
|
93
97
|
tokenAddress: paymentToken.address,
|
|
@@ -137,16 +141,29 @@ export async function refreshQuotes(messenger, updateTransactionData) {
|
|
|
137
141
|
* @param request - Request parameters.
|
|
138
142
|
* @param request.from - Address from which the transaction is sent.
|
|
139
143
|
* @param request.isMaxAmount - Whether the transaction is a maximum amount transaction.
|
|
140
|
-
* @param request.
|
|
144
|
+
* @param request.isPostQuote - Whether this is a post-quote flow.
|
|
145
|
+
* @param request.paymentToken - Payment token (source for standard flows, destination for post-quote).
|
|
141
146
|
* @param request.sourceAmounts - Source amounts for the transaction.
|
|
142
147
|
* @param request.tokens - Required tokens for the transaction.
|
|
143
148
|
* @param request.transactionId - ID of the transaction.
|
|
144
149
|
* @returns Array of quote requests.
|
|
145
150
|
*/
|
|
146
|
-
function buildQuoteRequests({ from, isMaxAmount, paymentToken, sourceAmounts, tokens, transactionId, }) {
|
|
151
|
+
function buildQuoteRequests({ from, isMaxAmount, isPostQuote, paymentToken, sourceAmounts, tokens, transactionId, }) {
|
|
147
152
|
if (!paymentToken) {
|
|
148
153
|
return [];
|
|
149
154
|
}
|
|
155
|
+
if (isPostQuote) {
|
|
156
|
+
// Post-quote flow: source = transaction's required token, target = paymentToken (destination)
|
|
157
|
+
// The user wants to receive the transaction output in paymentToken
|
|
158
|
+
return buildPostQuoteRequests({
|
|
159
|
+
from,
|
|
160
|
+
isMaxAmount,
|
|
161
|
+
destinationToken: paymentToken,
|
|
162
|
+
sourceAmounts,
|
|
163
|
+
transactionId,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
// Standard flow: source = paymentToken, target = required tokens
|
|
150
167
|
const requests = (sourceAmounts ?? []).map((sourceAmount) => {
|
|
151
168
|
const token = tokens.find((singleToken) => singleToken.address === sourceAmount.targetTokenAddress);
|
|
152
169
|
return {
|
|
@@ -166,6 +183,51 @@ function buildQuoteRequests({ from, isMaxAmount, paymentToken, sourceAmounts, to
|
|
|
166
183
|
}
|
|
167
184
|
return requests;
|
|
168
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Build quote requests for post-quote flows.
|
|
188
|
+
* In this flow, the source is the transaction's required token,
|
|
189
|
+
* and the target is the user's selected destination token (paymentToken).
|
|
190
|
+
*
|
|
191
|
+
* @param request - Request parameters.
|
|
192
|
+
* @param request.from - Address from which the transaction is sent.
|
|
193
|
+
* @param request.isMaxAmount - Whether the transaction is a maximum amount transaction.
|
|
194
|
+
* @param request.destinationToken - Destination token (paymentToken in post-quote mode).
|
|
195
|
+
* @param request.sourceAmounts - Source amounts for the transaction (includes source token info).
|
|
196
|
+
* @param request.transactionId - ID of the transaction.
|
|
197
|
+
* @returns Array of quote requests for post-quote flow.
|
|
198
|
+
*/
|
|
199
|
+
function buildPostQuoteRequests({ from, isMaxAmount, destinationToken, sourceAmounts, transactionId, }) {
|
|
200
|
+
// Find the source amount where targetTokenAddress matches the destination token
|
|
201
|
+
const sourceAmount = sourceAmounts?.find((amount) => amount.targetTokenAddress.toLowerCase() ===
|
|
202
|
+
destinationToken.address.toLowerCase());
|
|
203
|
+
// Same-token-same-chain cases are already filtered in source-amounts.ts
|
|
204
|
+
if (!sourceAmount?.sourceBalanceRaw ||
|
|
205
|
+
!sourceAmount.sourceChainId ||
|
|
206
|
+
!sourceAmount.sourceTokenAddress) {
|
|
207
|
+
log('No valid source amount found for post-quote request', {
|
|
208
|
+
transactionId,
|
|
209
|
+
});
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
const request = {
|
|
213
|
+
from,
|
|
214
|
+
isMaxAmount,
|
|
215
|
+
isPostQuote: true,
|
|
216
|
+
sourceBalanceRaw: sourceAmount.sourceBalanceRaw,
|
|
217
|
+
sourceTokenAmount: sourceAmount.sourceAmountRaw,
|
|
218
|
+
sourceChainId: sourceAmount.sourceChainId,
|
|
219
|
+
sourceTokenAddress: sourceAmount.sourceTokenAddress,
|
|
220
|
+
// For post-quote flows, use EXACT_INPUT - user specifies how much to send,
|
|
221
|
+
// and we show them how much they'll receive after fees
|
|
222
|
+
targetAmountMinimum: '0',
|
|
223
|
+
targetChainId: destinationToken.chainId,
|
|
224
|
+
targetTokenAddress: destinationToken.address,
|
|
225
|
+
};
|
|
226
|
+
log('Post-quote request built', { transactionId, request });
|
|
227
|
+
// Currently only single token post-quote flows are supported.
|
|
228
|
+
// Multiple token support would require multiple quotes for each required token.
|
|
229
|
+
return [request];
|
|
230
|
+
}
|
|
169
231
|
/**
|
|
170
232
|
* Retrieve quotes for a transaction.
|
|
171
233
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotes.mjs","sourceRoot":"","sources":["../../src/utils/quotes.ts"],"names":[],"mappings":"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","sourcesContent":["import { TransactionStatus } from '@metamask/transaction-controller';\nimport type { BatchTransaction } from '@metamask/transaction-controller';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport type { Hex, Json } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\n\nimport { getStrategy, getStrategyByName } from './strategy';\nimport { calculateTotals } from './totals';\nimport { getTransaction, updateTransaction } from './transaction';\nimport { projectLogger } from '../logger';\nimport type {\n QuoteRequest,\n TransactionData,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n TransactionPayRequiredToken,\n TransactionPaySourceAmount,\n TransactionPayTotals,\n TransactionPaymentToken,\n UpdateTransactionDataCallback,\n} from '../types';\n\nconst DEFAULT_REFRESH_INTERVAL = 30 * 1000; // 30 Seconds\n\nconst log = createModuleLogger(projectLogger, 'quotes');\n\nexport type UpdateQuotesRequest = {\n messenger: TransactionPayControllerMessenger;\n transactionData: TransactionData | undefined;\n transactionId: string;\n updateTransactionData: UpdateTransactionDataCallback;\n};\n\n/**\n * Update the quotes for a specific transaction.\n *\n * @param request - Request parameters.\n * @returns Boolean indicating if the quotes were updated.\n */\nexport async function updateQuotes(\n request: UpdateQuotesRequest,\n): Promise<boolean> {\n const { messenger, transactionData, transactionId, updateTransactionData } =\n request;\n\n const transaction = getTransaction(transactionId, messenger);\n\n if (!transaction || !transactionData) {\n throw new Error('Transaction not found');\n }\n\n if (transaction?.status !== TransactionStatus.unapproved) {\n return false;\n }\n\n log('Updating quotes', { transactionId });\n\n const { isMaxAmount, paymentToken, sourceAmounts, tokens } = transactionData;\n\n const requests = buildQuoteRequests({\n from: transaction.txParams.from as Hex,\n isMaxAmount: isMaxAmount ?? false,\n paymentToken,\n sourceAmounts,\n tokens,\n transactionId,\n });\n\n updateTransactionData(transactionId, (data) => {\n data.isLoading = true;\n });\n\n try {\n const { batchTransactions, quotes } = await getQuotes(\n transaction,\n requests,\n messenger,\n );\n\n const totals = calculateTotals({\n isMaxAmount,\n messenger,\n quotes: quotes as TransactionPayQuote<unknown>[],\n tokens,\n transaction,\n });\n\n log('Calculated totals', { transactionId, totals });\n\n syncTransaction({\n batchTransactions,\n messenger: messenger as never,\n paymentToken,\n totals,\n transactionId,\n });\n\n updateTransactionData(transactionId, (data) => {\n data.quotes = quotes as never;\n data.quotesLastUpdated = Date.now();\n data.totals = totals;\n });\n } finally {\n updateTransactionData(transactionId, (data) => {\n data.isLoading = false;\n });\n }\n\n return true;\n}\n\n/**\n * Sync batch transactions to the transaction meta.\n *\n * @param request - Request object.\n * @param request.batchTransactions - Batch transactions to sync.\n * @param request.messenger - Messenger instance.\n * @param request.paymentToken - Payment token used.\n * @param request.totals - Calculated totals.\n * @param request.transactionId - ID of the transaction to sync.\n */\nfunction syncTransaction({\n batchTransactions,\n messenger,\n paymentToken,\n totals,\n transactionId,\n}: {\n batchTransactions: BatchTransaction[];\n messenger: TransactionPayControllerMessenger;\n paymentToken: TransactionPaymentToken | undefined;\n totals: TransactionPayTotals;\n transactionId: string;\n}): void {\n if (!paymentToken) {\n return;\n }\n\n updateTransaction(\n {\n transactionId,\n messenger: messenger as never,\n note: 'Update transaction pay data',\n },\n (tx: TransactionMeta) => {\n tx.batchTransactions = batchTransactions;\n tx.batchTransactionsOptions = {};\n\n tx.metamaskPay = {\n bridgeFeeFiat: totals.fees.provider.usd,\n chainId: paymentToken.chainId,\n networkFeeFiat: totals.fees.sourceNetwork.estimate.usd,\n targetFiat: totals.targetAmount.usd,\n tokenAddress: paymentToken.address,\n totalFiat: totals.total.usd,\n };\n },\n );\n}\n\n/**\n * Refresh quotes for all transactions if expired.\n *\n * @param messenger - Messenger instance.\n * @param updateTransactionData - Callback to update transaction data.\n */\nexport async function refreshQuotes(\n messenger: TransactionPayControllerMessenger,\n updateTransactionData: UpdateTransactionDataCallback,\n): Promise<void> {\n const state = messenger.call('TransactionPayController:getState');\n const transactionIds = Object.keys(state.transactionData);\n\n for (const transactionId of transactionIds) {\n const transactionData = state.transactionData[transactionId];\n const { isLoading, quotes, quotesLastUpdated } = transactionData;\n\n if (isLoading || !quotes?.length) {\n continue;\n }\n\n const strategyName = quotes[0].strategy;\n const strategy = getStrategyByName(strategyName);\n\n const refreshInterval =\n (await strategy.getRefreshInterval?.({\n chainId: quotes[0].request.sourceChainId,\n messenger,\n })) ?? DEFAULT_REFRESH_INTERVAL;\n\n const isExpired = Date.now() - (quotesLastUpdated ?? 0) > refreshInterval;\n\n if (!isExpired) {\n continue;\n }\n\n const isUpdated = await updateQuotes({\n messenger,\n transactionData,\n transactionId,\n updateTransactionData,\n });\n\n if (isUpdated) {\n log('Refreshed quotes', { transactionId, strategy: strategyName });\n }\n }\n}\n\n/**\n * Build quote requests required to retrieve quotes.\n *\n * @param request - Request parameters.\n * @param request.from - Address from which the transaction is sent.\n * @param request.isMaxAmount - Whether the transaction is a maximum amount transaction.\n * @param request.paymentToken - Payment token used for the transaction.\n * @param request.sourceAmounts - Source amounts for the transaction.\n * @param request.tokens - Required tokens for the transaction.\n * @param request.transactionId - ID of the transaction.\n * @returns Array of quote requests.\n */\nfunction buildQuoteRequests({\n from,\n isMaxAmount,\n paymentToken,\n sourceAmounts,\n tokens,\n transactionId,\n}: {\n from: Hex;\n isMaxAmount: boolean;\n paymentToken: TransactionPaymentToken | undefined;\n sourceAmounts: TransactionPaySourceAmount[] | undefined;\n tokens: TransactionPayRequiredToken[];\n transactionId: string;\n}): QuoteRequest[] {\n if (!paymentToken) {\n return [];\n }\n\n const requests = (sourceAmounts ?? []).map((sourceAmount) => {\n const token = tokens.find(\n (singleToken) => singleToken.address === sourceAmount.targetTokenAddress,\n ) as TransactionPayRequiredToken;\n\n return {\n from,\n isMaxAmount,\n sourceBalanceRaw: paymentToken.balanceRaw,\n sourceTokenAmount: sourceAmount.sourceAmountRaw,\n sourceChainId: paymentToken.chainId,\n sourceTokenAddress: paymentToken.address,\n targetAmountMinimum: token.allowUnderMinimum ? '0' : token.amountRaw,\n targetChainId: token.chainId,\n targetTokenAddress: token.address,\n };\n });\n\n if (!requests.length) {\n log('No quote requests', { transactionId });\n }\n\n return requests;\n}\n\n/**\n * Retrieve quotes for a transaction.\n *\n * @param transaction - Transaction metadata.\n * @param requests - Quote requests.\n * @param messenger - Controller messenger.\n * @returns An object containing batch transactions and quotes.\n */\nasync function getQuotes(\n transaction: TransactionMeta,\n requests: QuoteRequest[],\n messenger: TransactionPayControllerMessenger,\n): Promise<{\n batchTransactions: BatchTransaction[];\n quotes: TransactionPayQuote<Json>[];\n}> {\n const { id: transactionId } = transaction;\n const strategy = getStrategy(messenger as never, transaction);\n let quotes: TransactionPayQuote<Json>[] | undefined = [];\n\n try {\n quotes = requests?.length\n ? ((await strategy.getQuotes({\n messenger,\n requests,\n transaction,\n })) as TransactionPayQuote<Json>[])\n : [];\n } catch (error) {\n log('Error fetching quotes', { error, transactionId });\n }\n\n log('Updated', { transactionId, quotes });\n\n const batchTransactions =\n quotes?.length && strategy.getBatchTransactions\n ? await strategy.getBatchTransactions({\n messenger,\n quotes,\n })\n : [];\n\n log('Batch transactions', { transactionId, batchTransactions });\n\n return {\n batchTransactions,\n quotes,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"quotes.mjs","sourceRoot":"","sources":["../../src/utils/quotes.ts"],"names":[],"mappings":"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","sourcesContent":["import { TransactionStatus } from '@metamask/transaction-controller';\nimport type { BatchTransaction } from '@metamask/transaction-controller';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport type { Hex, Json } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\n\nimport { getStrategy, getStrategyByName } from './strategy';\nimport { calculateTotals } from './totals';\nimport { getTransaction, updateTransaction } from './transaction';\nimport { projectLogger } from '../logger';\nimport type {\n QuoteRequest,\n TransactionData,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n TransactionPayRequiredToken,\n TransactionPaySourceAmount,\n TransactionPayTotals,\n TransactionPaymentToken,\n UpdateTransactionDataCallback,\n} from '../types';\n\nconst DEFAULT_REFRESH_INTERVAL = 30 * 1000; // 30 Seconds\n\nconst log = createModuleLogger(projectLogger, 'quotes');\n\nexport type UpdateQuotesRequest = {\n messenger: TransactionPayControllerMessenger;\n transactionData: TransactionData | undefined;\n transactionId: string;\n updateTransactionData: UpdateTransactionDataCallback;\n};\n\n/**\n * Update the quotes for a specific transaction.\n *\n * @param request - Request parameters.\n * @returns Boolean indicating if the quotes were updated.\n */\nexport async function updateQuotes(\n request: UpdateQuotesRequest,\n): Promise<boolean> {\n const { messenger, transactionData, transactionId, updateTransactionData } =\n request;\n\n const transaction = getTransaction(transactionId, messenger);\n\n if (!transaction || !transactionData) {\n throw new Error('Transaction not found');\n }\n\n if (transaction?.status !== TransactionStatus.unapproved) {\n return false;\n }\n\n log('Updating quotes', { transactionId });\n\n const { isMaxAmount, isPostQuote, paymentToken, sourceAmounts, tokens } =\n transactionData;\n\n const requests = buildQuoteRequests({\n from: transaction.txParams.from as Hex,\n isMaxAmount: isMaxAmount ?? false,\n isPostQuote,\n paymentToken,\n sourceAmounts,\n tokens,\n transactionId,\n });\n\n updateTransactionData(transactionId, (data) => {\n data.isLoading = true;\n });\n\n try {\n const { batchTransactions, quotes } = await getQuotes(\n transaction,\n requests,\n messenger,\n );\n\n const totals = calculateTotals({\n isMaxAmount,\n messenger,\n quotes: quotes as TransactionPayQuote<unknown>[],\n tokens,\n transaction,\n });\n\n log('Calculated totals', { transactionId, totals });\n\n syncTransaction({\n batchTransactions,\n isPostQuote,\n messenger: messenger as never,\n paymentToken,\n totals,\n transactionId,\n });\n\n updateTransactionData(transactionId, (data) => {\n data.quotes = quotes as never;\n data.quotesLastUpdated = Date.now();\n data.totals = totals;\n });\n } finally {\n updateTransactionData(transactionId, (data) => {\n data.isLoading = false;\n });\n }\n\n return true;\n}\n\n/**\n * Sync batch transactions to the transaction meta.\n *\n * @param request - Request object.\n * @param request.batchTransactions - Batch transactions to sync.\n * @param request.isPostQuote - Whether this is a post-quote flow.\n * @param request.messenger - Messenger instance.\n * @param request.paymentToken - Payment token (source for standard flows, destination for post-quote).\n * @param request.totals - Calculated totals.\n * @param request.transactionId - ID of the transaction to sync.\n */\nfunction syncTransaction({\n batchTransactions,\n isPostQuote,\n messenger,\n paymentToken,\n totals,\n transactionId,\n}: {\n batchTransactions: BatchTransaction[];\n isPostQuote?: boolean;\n messenger: TransactionPayControllerMessenger;\n paymentToken: TransactionPaymentToken | undefined;\n totals: TransactionPayTotals;\n transactionId: string;\n}): void {\n if (!paymentToken) {\n return;\n }\n\n updateTransaction(\n {\n transactionId,\n messenger: messenger as never,\n note: 'Update transaction pay data',\n },\n (tx: TransactionMeta) => {\n tx.batchTransactions = batchTransactions;\n tx.batchTransactionsOptions = {};\n\n tx.metamaskPay = {\n bridgeFeeFiat: totals.fees.provider.usd,\n chainId: paymentToken.chainId,\n isPostQuote,\n networkFeeFiat: totals.fees.sourceNetwork.estimate.usd,\n targetFiat: totals.targetAmount.usd,\n tokenAddress: paymentToken.address,\n totalFiat: totals.total.usd,\n };\n },\n );\n}\n\n/**\n * Refresh quotes for all transactions if expired.\n *\n * @param messenger - Messenger instance.\n * @param updateTransactionData - Callback to update transaction data.\n */\nexport async function refreshQuotes(\n messenger: TransactionPayControllerMessenger,\n updateTransactionData: UpdateTransactionDataCallback,\n): Promise<void> {\n const state = messenger.call('TransactionPayController:getState');\n const transactionIds = Object.keys(state.transactionData);\n\n for (const transactionId of transactionIds) {\n const transactionData = state.transactionData[transactionId];\n const { isLoading, quotes, quotesLastUpdated } = transactionData;\n\n if (isLoading || !quotes?.length) {\n continue;\n }\n\n const strategyName = quotes[0].strategy;\n const strategy = getStrategyByName(strategyName);\n\n const refreshInterval =\n (await strategy.getRefreshInterval?.({\n chainId: quotes[0].request.sourceChainId,\n messenger,\n })) ?? DEFAULT_REFRESH_INTERVAL;\n\n const isExpired = Date.now() - (quotesLastUpdated ?? 0) > refreshInterval;\n\n if (!isExpired) {\n continue;\n }\n\n const isUpdated = await updateQuotes({\n messenger,\n transactionData,\n transactionId,\n updateTransactionData,\n });\n\n if (isUpdated) {\n log('Refreshed quotes', { transactionId, strategy: strategyName });\n }\n }\n}\n\n/**\n * Build quote requests required to retrieve quotes.\n *\n * @param request - Request parameters.\n * @param request.from - Address from which the transaction is sent.\n * @param request.isMaxAmount - Whether the transaction is a maximum amount transaction.\n * @param request.isPostQuote - Whether this is a post-quote flow.\n * @param request.paymentToken - Payment token (source for standard flows, destination for post-quote).\n * @param request.sourceAmounts - Source amounts for the transaction.\n * @param request.tokens - Required tokens for the transaction.\n * @param request.transactionId - ID of the transaction.\n * @returns Array of quote requests.\n */\nfunction buildQuoteRequests({\n from,\n isMaxAmount,\n isPostQuote,\n paymentToken,\n sourceAmounts,\n tokens,\n transactionId,\n}: {\n from: Hex;\n isMaxAmount: boolean;\n isPostQuote?: boolean;\n paymentToken: TransactionPaymentToken | undefined;\n sourceAmounts: TransactionPaySourceAmount[] | undefined;\n tokens: TransactionPayRequiredToken[];\n transactionId: string;\n}): QuoteRequest[] {\n if (!paymentToken) {\n return [];\n }\n\n if (isPostQuote) {\n // Post-quote flow: source = transaction's required token, target = paymentToken (destination)\n // The user wants to receive the transaction output in paymentToken\n return buildPostQuoteRequests({\n from,\n isMaxAmount,\n destinationToken: paymentToken,\n sourceAmounts,\n transactionId,\n });\n }\n\n // Standard flow: source = paymentToken, target = required tokens\n const requests = (sourceAmounts ?? []).map((sourceAmount) => {\n const token = tokens.find(\n (singleToken) => singleToken.address === sourceAmount.targetTokenAddress,\n ) as TransactionPayRequiredToken;\n\n return {\n from,\n isMaxAmount,\n sourceBalanceRaw: paymentToken.balanceRaw,\n sourceTokenAmount: sourceAmount.sourceAmountRaw,\n sourceChainId: paymentToken.chainId,\n sourceTokenAddress: paymentToken.address,\n targetAmountMinimum: token.allowUnderMinimum ? '0' : token.amountRaw,\n targetChainId: token.chainId,\n targetTokenAddress: token.address,\n };\n });\n\n if (!requests.length) {\n log('No quote requests', { transactionId });\n }\n\n return requests;\n}\n\n/**\n * Build quote requests for post-quote flows.\n * In this flow, the source is the transaction's required token,\n * and the target is the user's selected destination token (paymentToken).\n *\n * @param request - Request parameters.\n * @param request.from - Address from which the transaction is sent.\n * @param request.isMaxAmount - Whether the transaction is a maximum amount transaction.\n * @param request.destinationToken - Destination token (paymentToken in post-quote mode).\n * @param request.sourceAmounts - Source amounts for the transaction (includes source token info).\n * @param request.transactionId - ID of the transaction.\n * @returns Array of quote requests for post-quote flow.\n */\nfunction buildPostQuoteRequests({\n from,\n isMaxAmount,\n destinationToken,\n sourceAmounts,\n transactionId,\n}: {\n from: Hex;\n isMaxAmount: boolean;\n destinationToken: TransactionPaymentToken;\n sourceAmounts: TransactionPaySourceAmount[] | undefined;\n transactionId: string;\n}): QuoteRequest[] {\n // Find the source amount where targetTokenAddress matches the destination token\n const sourceAmount = sourceAmounts?.find(\n (amount) =>\n amount.targetTokenAddress.toLowerCase() ===\n destinationToken.address.toLowerCase(),\n );\n\n // Same-token-same-chain cases are already filtered in source-amounts.ts\n if (\n !sourceAmount?.sourceBalanceRaw ||\n !sourceAmount.sourceChainId ||\n !sourceAmount.sourceTokenAddress\n ) {\n log('No valid source amount found for post-quote request', {\n transactionId,\n });\n return [];\n }\n\n const request: QuoteRequest = {\n from,\n isMaxAmount,\n isPostQuote: true,\n sourceBalanceRaw: sourceAmount.sourceBalanceRaw,\n sourceTokenAmount: sourceAmount.sourceAmountRaw,\n sourceChainId: sourceAmount.sourceChainId,\n sourceTokenAddress: sourceAmount.sourceTokenAddress,\n // For post-quote flows, use EXACT_INPUT - user specifies how much to send,\n // and we show them how much they'll receive after fees\n targetAmountMinimum: '0',\n targetChainId: destinationToken.chainId,\n targetTokenAddress: destinationToken.address,\n };\n\n log('Post-quote request built', { transactionId, request });\n\n // Currently only single token post-quote flows are supported.\n // Multiple token support would require multiple quotes for each required token.\n return [request];\n}\n\n/**\n * Retrieve quotes for a transaction.\n *\n * @param transaction - Transaction metadata.\n * @param requests - Quote requests.\n * @param messenger - Controller messenger.\n * @returns An object containing batch transactions and quotes.\n */\nasync function getQuotes(\n transaction: TransactionMeta,\n requests: QuoteRequest[],\n messenger: TransactionPayControllerMessenger,\n): Promise<{\n batchTransactions: BatchTransaction[];\n quotes: TransactionPayQuote<Json>[];\n}> {\n const { id: transactionId } = transaction;\n const strategy = getStrategy(messenger as never, transaction);\n let quotes: TransactionPayQuote<Json>[] | undefined = [];\n\n try {\n quotes = requests?.length\n ? ((await strategy.getQuotes({\n messenger,\n requests,\n transaction,\n })) as TransactionPayQuote<Json>[])\n : [];\n } catch (error) {\n log('Error fetching quotes', { error, transactionId });\n }\n\n log('Updated', { transactionId, quotes });\n\n const batchTransactions =\n quotes?.length && strategy.getBatchTransactions\n ? await strategy.getBatchTransactions({\n messenger,\n quotes,\n })\n : [];\n\n log('Batch transactions', { transactionId, batchTransactions });\n\n return {\n batchTransactions,\n quotes,\n };\n}\n"]}
|
|
@@ -174,7 +174,7 @@ function getTokenTransferData(transactionMeta) {
|
|
|
174
174
|
return { data: singleData, to: singleTo, index: undefined };
|
|
175
175
|
}
|
|
176
176
|
const nestedCallIndex = nestedTransactions?.findIndex((call) => call.data?.startsWith(FOUR_BYTE_TOKEN_TRANSFER));
|
|
177
|
-
const nestedCall = nestedCallIndex === undefined
|
|
177
|
+
const nestedCall = nestedCallIndex === undefined || nestedCallIndex === -1
|
|
178
178
|
? undefined
|
|
179
179
|
: nestedTransactions?.[nestedCallIndex];
|
|
180
180
|
if (nestedCall?.data && nestedCall.to) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"required-tokens.cjs","sourceRoot":"","sources":["../../src/utils/required-tokens.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { toHex } from '@metamask/controller-utils';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { add0x } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n getNativeToken,\n getTokenBalance,\n getTokenFiatRate,\n getTokenInfo,\n} from './token';\nimport type {\n FiatRates,\n TransactionPayControllerMessenger,\n TransactionPayRequiredToken,\n} from '../types';\n\nconst FOUR_BYTE_TOKEN_TRANSFER = '0xa9059cbb';\n\n/**\n * Parse required tokens from a transaction.\n *\n * If the transaction has `requiredAssets`, those are used to determine required tokens.\n * Otherwise, falls back to parsing the transaction data for token transfers.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns An array of required tokens.\n */\nexport function parseRequiredTokens(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken[] {\n const { requiredAssets } = transaction;\n\n if (requiredAssets?.length) {\n const assetTokens = requiredAssets\n .map((asset) =>\n buildRequiredToken(transaction, asset.address, asset.amount, messenger),\n )\n .filter(Boolean) as TransactionPayRequiredToken[];\n\n return assetTokens;\n }\n\n return [\n getTokenTransferToken(transaction, messenger),\n getGasFeeToken(transaction, messenger),\n ].filter(Boolean) as TransactionPayRequiredToken[];\n}\n\n/**\n * Parse a required token from a token transfer.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns The required token or undefined if the transaction is not a token transfer.\n */\nfunction getTokenTransferToken(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { data, to } = getTokenTransferData(transaction) ?? {};\n\n if (!to || !data) {\n return undefined;\n }\n\n let transferAmount: Hex | undefined;\n\n try {\n const result = new Interface(abiERC20).decodeFunctionData('transfer', data);\n transferAmount = toHex(result._value);\n } catch {\n // Intentionally empty\n }\n\n if (transferAmount === undefined) {\n return undefined;\n }\n\n return buildRequiredToken(transaction, to, transferAmount, messenger);\n}\n\n/**\n * Get the gas fee token required for a transaction.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns The gas fee token or undefined if it could not be determined.\n */\nfunction getGasFeeToken(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { chainId, txParams } = transaction;\n const { gas, maxFeePerGas } = txParams;\n const nativeTokenAddress = getNativeToken(chainId);\n\n const maxGasCostRawHex = add0x(\n new BigNumber(gas ?? '0x0')\n .multipliedBy(new BigNumber(maxFeePerGas ?? '0x0'))\n .toString(16),\n );\n\n const token = buildRequiredToken(\n transaction,\n nativeTokenAddress,\n maxGasCostRawHex,\n messenger,\n );\n\n if (!token) {\n return undefined;\n }\n\n const amountUsdValue = new BigNumber(token.amountUsd);\n\n const hasBalance = new BigNumber(token.balanceRaw).isGreaterThanOrEqualTo(\n token.amountRaw,\n );\n\n if (hasBalance || amountUsdValue.isGreaterThanOrEqualTo(1)) {\n return {\n ...token,\n allowUnderMinimum: true,\n skipIfBalance: true,\n };\n }\n\n const fiatRates = getTokenFiatRate(\n messenger,\n nativeTokenAddress,\n chainId,\n ) as FiatRates;\n\n const oneDollarRawHex = add0x(\n new BigNumber(1).dividedBy(fiatRates.usdRate).shiftedBy(18).toString(16),\n );\n\n const oneDollarToken = buildRequiredToken(\n transaction,\n nativeTokenAddress,\n oneDollarRawHex,\n messenger,\n );\n\n /* istanbul ignore next */\n if (!oneDollarToken) {\n return undefined;\n }\n\n return {\n ...oneDollarToken,\n allowUnderMinimum: true,\n skipIfBalance: true,\n };\n}\n\n/**\n * Get the full token properties for a specific token and amount.\n *\n * @param transaction - Transaction metadata.\n * @param tokenAddress - Token address.\n * @param amountRawHex - Raw token amount in hexadecimal format.\n * @param messenger - Controller messenger.\n * @returns The full token properties or undefined if the token data could not be retrieved.\n */\nfunction buildRequiredToken(\n transaction: TransactionMeta,\n tokenAddress: Hex,\n amountRawHex: Hex,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { chainId, txParams } = transaction;\n const from = txParams.from as Hex;\n\n const { decimals: tokenDecimals, symbol } =\n getTokenInfo(messenger, tokenAddress, chainId) ?? {};\n\n const fiatRates = getTokenFiatRate(messenger, tokenAddress, chainId);\n const tokenBalance = getTokenBalance(messenger, from, chainId, tokenAddress);\n\n if (tokenDecimals === undefined || !symbol || fiatRates === undefined) {\n return undefined;\n }\n\n const {\n amountHuman: balanceHuman,\n amountRaw: balanceRaw,\n amountFiat: balanceFiat,\n amountUsd: balanceUsd,\n } = calculateAmounts(tokenBalance, tokenDecimals, fiatRates);\n\n const { amountHuman, amountRaw, amountFiat, amountUsd } = calculateAmounts(\n amountRawHex,\n tokenDecimals,\n fiatRates,\n );\n\n return {\n address: tokenAddress,\n allowUnderMinimum: false,\n amountFiat,\n amountHuman,\n amountRaw,\n amountUsd,\n balanceFiat,\n balanceHuman,\n balanceRaw,\n balanceUsd,\n chainId,\n decimals: tokenDecimals,\n skipIfBalance: false,\n symbol,\n };\n}\n\n/**\n * Calculates the various amount representations for a token value.\n *\n * @param amountRawInput - Raw amount.\n * @param decimals - Number of decimals for the token.\n * @param fiatRates - Fiat rates for the token.\n * @returns Object containing amount in fiat, human-readable, raw, and USD formats.\n */\nfunction calculateAmounts(\n amountRawInput: BigNumber.Value,\n decimals: number,\n fiatRates: FiatRates,\n): {\n amountFiat: string;\n amountHuman: string;\n amountRaw: string;\n amountUsd: string;\n} {\n const amountRawValue = new BigNumber(amountRawInput);\n const amountHumanValue = amountRawValue.shiftedBy(-decimals);\n\n const amountFiat = amountHumanValue\n .multipliedBy(fiatRates.fiatRate)\n .toString(10);\n\n const amountUsd = amountHumanValue\n .multipliedBy(fiatRates.usdRate)\n .toString(10);\n\n const amountRaw = amountRawValue.toFixed(0);\n const amountHuman = amountHumanValue.toString(10);\n\n return {\n amountFiat,\n amountHuman,\n amountRaw,\n amountUsd,\n };\n}\n\n/**\n * Find token transfer data in a transaction.\n *\n * @param transactionMeta - Transaction metadata.\n * @returns - Token transfer data or undefined if not found.\n */\nfunction getTokenTransferData(transactionMeta: TransactionMeta):\n | {\n data: Hex;\n to: Hex;\n index?: number;\n }\n | undefined {\n const { nestedTransactions, txParams } = transactionMeta;\n const { data: singleData } = txParams;\n const singleTo = txParams?.to as Hex | undefined;\n\n if (singleData?.startsWith(FOUR_BYTE_TOKEN_TRANSFER) && singleTo) {\n return { data: singleData as Hex, to: singleTo, index: undefined };\n }\n\n const nestedCallIndex = nestedTransactions?.findIndex((call) =>\n call.data?.startsWith(FOUR_BYTE_TOKEN_TRANSFER),\n );\n\n const nestedCall =\n nestedCallIndex === undefined\n ? undefined\n : nestedTransactions?.[nestedCallIndex];\n\n if (nestedCall?.data && nestedCall.to) {\n return {\n data: nestedCall.data,\n to: nestedCall.to,\n index: nestedCallIndex,\n };\n }\n\n return undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"required-tokens.cjs","sourceRoot":"","sources":["../../src/utils/required-tokens.ts"],"names":[],"mappings":";;;AAAA,4CAA+C;AAC/C,iEAAmD;AACnD,mEAAuD;AAEvD,2CAAwC;AAExC,+CAAyC;AAEzC,uCAKiB;AAOjB,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,SAAgB,mBAAmB,CACjC,WAA4B,EAC5B,SAA4C;IAE5C,MAAM,EAAE,cAAc,EAAE,GAAG,WAAW,CAAC;IAEvC,IAAI,cAAc,EAAE,MAAM,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,cAAc;aAC/B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACb,kBAAkB,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CACxE;aACA,MAAM,CAAC,OAAO,CAAkC,CAAC;QAEpD,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,OAAO;QACL,qBAAqB,CAAC,WAAW,EAAE,SAAS,CAAC;QAC7C,cAAc,CAAC,WAAW,EAAE,SAAS,CAAC;KACvC,CAAC,MAAM,CAAC,OAAO,CAAkC,CAAC;AACrD,CAAC;AApBD,kDAoBC;AAED;;;;;;GAMG;AACH,SAAS,qBAAqB,CAC5B,WAA4B,EAC5B,SAA4C;IAE5C,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,oBAAoB,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IAE7D,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,cAA+B,CAAC;IAEpC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,eAAS,CAAC,4BAAQ,CAAC,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAC5E,cAAc,GAAG,IAAA,wBAAK,EAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,sBAAsB;IACxB,CAAC;IAED,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,kBAAkB,CAAC,WAAW,EAAE,EAAE,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CACrB,WAA4B,EAC5B,SAA4C;IAE5C,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC;IAC1C,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,QAAQ,CAAC;IACvC,MAAM,kBAAkB,GAAG,IAAA,sBAAc,EAAC,OAAO,CAAC,CAAC;IAEnD,MAAM,gBAAgB,GAAG,IAAA,aAAK,EAC5B,IAAI,wBAAS,CAAC,GAAG,IAAI,KAAK,CAAC;SACxB,YAAY,CAAC,IAAI,wBAAS,CAAC,YAAY,IAAI,KAAK,CAAC,CAAC;SAClD,QAAQ,CAAC,EAAE,CAAC,CAChB,CAAC;IAEF,MAAM,KAAK,GAAG,kBAAkB,CAC9B,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,CACV,CAAC;IAEF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,wBAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAEtD,MAAM,UAAU,GAAG,IAAI,wBAAS,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,sBAAsB,CACvE,KAAK,CAAC,SAAS,CAChB,CAAC;IAEF,IAAI,UAAU,IAAI,cAAc,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,OAAO;YACL,GAAG,KAAK;YACR,iBAAiB,EAAE,IAAI;YACvB,aAAa,EAAE,IAAI;SACpB,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,IAAA,wBAAgB,EAChC,SAAS,EACT,kBAAkB,EAClB,OAAO,CACK,CAAC;IAEf,MAAM,eAAe,GAAG,IAAA,aAAK,EAC3B,IAAI,wBAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CACzE,CAAC;IAEF,MAAM,cAAc,GAAG,kBAAkB,CACvC,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,SAAS,CACV,CAAC;IAEF,0BAA0B;IAC1B,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,GAAG,cAAc;QACjB,iBAAiB,EAAE,IAAI;QACvB,aAAa,EAAE,IAAI;KACpB,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,kBAAkB,CACzB,WAA4B,EAC5B,YAAiB,EACjB,YAAiB,EACjB,SAA4C;IAE5C,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC;IAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAW,CAAC;IAElC,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,GACvC,IAAA,oBAAY,EAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;IAEvD,MAAM,SAAS,GAAG,IAAA,wBAAgB,EAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,IAAA,uBAAe,EAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IAE7E,IAAI,aAAa,KAAK,SAAS,IAAI,CAAC,MAAM,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACtE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,EACJ,WAAW,EAAE,YAAY,EACzB,SAAS,EAAE,UAAU,EACrB,UAAU,EAAE,WAAW,EACvB,SAAS,EAAE,UAAU,GACtB,GAAG,gBAAgB,CAAC,YAAY,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;IAE7D,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,gBAAgB,CACxE,YAAY,EACZ,aAAa,EACb,SAAS,CACV,CAAC;IAEF,OAAO;QACL,OAAO,EAAE,YAAY;QACrB,iBAAiB,EAAE,KAAK;QACxB,UAAU;QACV,WAAW;QACX,SAAS;QACT,SAAS;QACT,WAAW;QACX,YAAY;QACZ,UAAU;QACV,UAAU;QACV,OAAO;QACP,QAAQ,EAAE,aAAa;QACvB,aAAa,EAAE,KAAK;QACpB,MAAM;KACP,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,gBAAgB,CACvB,cAA+B,EAC/B,QAAgB,EAChB,SAAoB;IAOpB,MAAM,cAAc,GAAG,IAAI,wBAAS,CAAC,cAAc,CAAC,CAAC;IACrD,MAAM,gBAAgB,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC;IAE7D,MAAM,UAAU,GAAG,gBAAgB;SAChC,YAAY,CAAC,SAAS,CAAC,QAAQ,CAAC;SAChC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,SAAS,GAAG,gBAAgB;SAC/B,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC;SAC/B,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEhB,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAElD,OAAO;QACL,UAAU;QACV,WAAW;QACX,SAAS;QACT,SAAS;KACV,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,oBAAoB,CAAC,eAAgC;IAO5D,MAAM,EAAE,kBAAkB,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC;IACzD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,QAAQ,CAAC;IACtC,MAAM,QAAQ,GAAG,QAAQ,EAAE,EAAqB,CAAC;IAEjD,IAAI,UAAU,EAAE,UAAU,CAAC,wBAAwB,CAAC,IAAI,QAAQ,EAAE,CAAC;QACjE,OAAO,EAAE,IAAI,EAAE,UAAiB,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IACrE,CAAC;IAED,MAAM,eAAe,GAAG,kBAAkB,EAAE,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAC7D,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,wBAAwB,CAAC,CAChD,CAAC;IAEF,MAAM,UAAU,GACd,eAAe,KAAK,SAAS,IAAI,eAAe,KAAK,CAAC,CAAC;QACrD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,kBAAkB,EAAE,CAAC,eAAe,CAAC,CAAC;IAE5C,IAAI,UAAU,EAAE,IAAI,IAAI,UAAU,CAAC,EAAE,EAAE,CAAC;QACtC,OAAO;YACL,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,EAAE,EAAE,UAAU,CAAC,EAAE;YACjB,KAAK,EAAE,eAAe;SACvB,CAAC;IACJ,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { toHex } from '@metamask/controller-utils';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { add0x } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n getNativeToken,\n getTokenBalance,\n getTokenFiatRate,\n getTokenInfo,\n} from './token';\nimport type {\n FiatRates,\n TransactionPayControllerMessenger,\n TransactionPayRequiredToken,\n} from '../types';\n\nconst FOUR_BYTE_TOKEN_TRANSFER = '0xa9059cbb';\n\n/**\n * Parse required tokens from a transaction.\n *\n * If the transaction has `requiredAssets`, those are used to determine required tokens.\n * Otherwise, falls back to parsing the transaction data for token transfers.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns An array of required tokens.\n */\nexport function parseRequiredTokens(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken[] {\n const { requiredAssets } = transaction;\n\n if (requiredAssets?.length) {\n const assetTokens = requiredAssets\n .map((asset) =>\n buildRequiredToken(transaction, asset.address, asset.amount, messenger),\n )\n .filter(Boolean) as TransactionPayRequiredToken[];\n\n return assetTokens;\n }\n\n return [\n getTokenTransferToken(transaction, messenger),\n getGasFeeToken(transaction, messenger),\n ].filter(Boolean) as TransactionPayRequiredToken[];\n}\n\n/**\n * Parse a required token from a token transfer.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns The required token or undefined if the transaction is not a token transfer.\n */\nfunction getTokenTransferToken(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { data, to } = getTokenTransferData(transaction) ?? {};\n\n if (!to || !data) {\n return undefined;\n }\n\n let transferAmount: Hex | undefined;\n\n try {\n const result = new Interface(abiERC20).decodeFunctionData('transfer', data);\n transferAmount = toHex(result._value);\n } catch {\n // Intentionally empty\n }\n\n if (transferAmount === undefined) {\n return undefined;\n }\n\n return buildRequiredToken(transaction, to, transferAmount, messenger);\n}\n\n/**\n * Get the gas fee token required for a transaction.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns The gas fee token or undefined if it could not be determined.\n */\nfunction getGasFeeToken(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { chainId, txParams } = transaction;\n const { gas, maxFeePerGas } = txParams;\n const nativeTokenAddress = getNativeToken(chainId);\n\n const maxGasCostRawHex = add0x(\n new BigNumber(gas ?? '0x0')\n .multipliedBy(new BigNumber(maxFeePerGas ?? '0x0'))\n .toString(16),\n );\n\n const token = buildRequiredToken(\n transaction,\n nativeTokenAddress,\n maxGasCostRawHex,\n messenger,\n );\n\n if (!token) {\n return undefined;\n }\n\n const amountUsdValue = new BigNumber(token.amountUsd);\n\n const hasBalance = new BigNumber(token.balanceRaw).isGreaterThanOrEqualTo(\n token.amountRaw,\n );\n\n if (hasBalance || amountUsdValue.isGreaterThanOrEqualTo(1)) {\n return {\n ...token,\n allowUnderMinimum: true,\n skipIfBalance: true,\n };\n }\n\n const fiatRates = getTokenFiatRate(\n messenger,\n nativeTokenAddress,\n chainId,\n ) as FiatRates;\n\n const oneDollarRawHex = add0x(\n new BigNumber(1).dividedBy(fiatRates.usdRate).shiftedBy(18).toString(16),\n );\n\n const oneDollarToken = buildRequiredToken(\n transaction,\n nativeTokenAddress,\n oneDollarRawHex,\n messenger,\n );\n\n /* istanbul ignore next */\n if (!oneDollarToken) {\n return undefined;\n }\n\n return {\n ...oneDollarToken,\n allowUnderMinimum: true,\n skipIfBalance: true,\n };\n}\n\n/**\n * Get the full token properties for a specific token and amount.\n *\n * @param transaction - Transaction metadata.\n * @param tokenAddress - Token address.\n * @param amountRawHex - Raw token amount in hexadecimal format.\n * @param messenger - Controller messenger.\n * @returns The full token properties or undefined if the token data could not be retrieved.\n */\nfunction buildRequiredToken(\n transaction: TransactionMeta,\n tokenAddress: Hex,\n amountRawHex: Hex,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { chainId, txParams } = transaction;\n const from = txParams.from as Hex;\n\n const { decimals: tokenDecimals, symbol } =\n getTokenInfo(messenger, tokenAddress, chainId) ?? {};\n\n const fiatRates = getTokenFiatRate(messenger, tokenAddress, chainId);\n const tokenBalance = getTokenBalance(messenger, from, chainId, tokenAddress);\n\n if (tokenDecimals === undefined || !symbol || fiatRates === undefined) {\n return undefined;\n }\n\n const {\n amountHuman: balanceHuman,\n amountRaw: balanceRaw,\n amountFiat: balanceFiat,\n amountUsd: balanceUsd,\n } = calculateAmounts(tokenBalance, tokenDecimals, fiatRates);\n\n const { amountHuman, amountRaw, amountFiat, amountUsd } = calculateAmounts(\n amountRawHex,\n tokenDecimals,\n fiatRates,\n );\n\n return {\n address: tokenAddress,\n allowUnderMinimum: false,\n amountFiat,\n amountHuman,\n amountRaw,\n amountUsd,\n balanceFiat,\n balanceHuman,\n balanceRaw,\n balanceUsd,\n chainId,\n decimals: tokenDecimals,\n skipIfBalance: false,\n symbol,\n };\n}\n\n/**\n * Calculates the various amount representations for a token value.\n *\n * @param amountRawInput - Raw amount.\n * @param decimals - Number of decimals for the token.\n * @param fiatRates - Fiat rates for the token.\n * @returns Object containing amount in fiat, human-readable, raw, and USD formats.\n */\nfunction calculateAmounts(\n amountRawInput: BigNumber.Value,\n decimals: number,\n fiatRates: FiatRates,\n): {\n amountFiat: string;\n amountHuman: string;\n amountRaw: string;\n amountUsd: string;\n} {\n const amountRawValue = new BigNumber(amountRawInput);\n const amountHumanValue = amountRawValue.shiftedBy(-decimals);\n\n const amountFiat = amountHumanValue\n .multipliedBy(fiatRates.fiatRate)\n .toString(10);\n\n const amountUsd = amountHumanValue\n .multipliedBy(fiatRates.usdRate)\n .toString(10);\n\n const amountRaw = amountRawValue.toFixed(0);\n const amountHuman = amountHumanValue.toString(10);\n\n return {\n amountFiat,\n amountHuman,\n amountRaw,\n amountUsd,\n };\n}\n\n/**\n * Find token transfer data in a transaction.\n *\n * @param transactionMeta - Transaction metadata.\n * @returns - Token transfer data or undefined if not found.\n */\nfunction getTokenTransferData(transactionMeta: TransactionMeta):\n | {\n data: Hex;\n to: Hex;\n index?: number;\n }\n | undefined {\n const { nestedTransactions, txParams } = transactionMeta;\n const { data: singleData } = txParams;\n const singleTo = txParams?.to as Hex | undefined;\n\n if (singleData?.startsWith(FOUR_BYTE_TOKEN_TRANSFER) && singleTo) {\n return { data: singleData as Hex, to: singleTo, index: undefined };\n }\n\n const nestedCallIndex = nestedTransactions?.findIndex((call) =>\n call.data?.startsWith(FOUR_BYTE_TOKEN_TRANSFER),\n );\n\n const nestedCall =\n nestedCallIndex === undefined || nestedCallIndex === -1\n ? undefined\n : nestedTransactions?.[nestedCallIndex];\n\n if (nestedCall?.data && nestedCall.to) {\n return {\n data: nestedCall.data,\n to: nestedCall.to,\n index: nestedCallIndex,\n };\n }\n\n return undefined;\n}\n"]}
|
|
@@ -170,7 +170,7 @@ function getTokenTransferData(transactionMeta) {
|
|
|
170
170
|
return { data: singleData, to: singleTo, index: undefined };
|
|
171
171
|
}
|
|
172
172
|
const nestedCallIndex = nestedTransactions?.findIndex((call) => call.data?.startsWith(FOUR_BYTE_TOKEN_TRANSFER));
|
|
173
|
-
const nestedCall = nestedCallIndex === undefined
|
|
173
|
+
const nestedCall = nestedCallIndex === undefined || nestedCallIndex === -1
|
|
174
174
|
? undefined
|
|
175
175
|
: nestedTransactions?.[nestedCallIndex];
|
|
176
176
|
if (nestedCall?.data && nestedCall.to) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"required-tokens.mjs","sourceRoot":"","sources":["../../src/utils/required-tokens.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { toHex } from '@metamask/controller-utils';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { add0x } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n getNativeToken,\n getTokenBalance,\n getTokenFiatRate,\n getTokenInfo,\n} from './token';\nimport type {\n FiatRates,\n TransactionPayControllerMessenger,\n TransactionPayRequiredToken,\n} from '../types';\n\nconst FOUR_BYTE_TOKEN_TRANSFER = '0xa9059cbb';\n\n/**\n * Parse required tokens from a transaction.\n *\n * If the transaction has `requiredAssets`, those are used to determine required tokens.\n * Otherwise, falls back to parsing the transaction data for token transfers.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns An array of required tokens.\n */\nexport function parseRequiredTokens(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken[] {\n const { requiredAssets } = transaction;\n\n if (requiredAssets?.length) {\n const assetTokens = requiredAssets\n .map((asset) =>\n buildRequiredToken(transaction, asset.address, asset.amount, messenger),\n )\n .filter(Boolean) as TransactionPayRequiredToken[];\n\n return assetTokens;\n }\n\n return [\n getTokenTransferToken(transaction, messenger),\n getGasFeeToken(transaction, messenger),\n ].filter(Boolean) as TransactionPayRequiredToken[];\n}\n\n/**\n * Parse a required token from a token transfer.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns The required token or undefined if the transaction is not a token transfer.\n */\nfunction getTokenTransferToken(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { data, to } = getTokenTransferData(transaction) ?? {};\n\n if (!to || !data) {\n return undefined;\n }\n\n let transferAmount: Hex | undefined;\n\n try {\n const result = new Interface(abiERC20).decodeFunctionData('transfer', data);\n transferAmount = toHex(result._value);\n } catch {\n // Intentionally empty\n }\n\n if (transferAmount === undefined) {\n return undefined;\n }\n\n return buildRequiredToken(transaction, to, transferAmount, messenger);\n}\n\n/**\n * Get the gas fee token required for a transaction.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns The gas fee token or undefined if it could not be determined.\n */\nfunction getGasFeeToken(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { chainId, txParams } = transaction;\n const { gas, maxFeePerGas } = txParams;\n const nativeTokenAddress = getNativeToken(chainId);\n\n const maxGasCostRawHex = add0x(\n new BigNumber(gas ?? '0x0')\n .multipliedBy(new BigNumber(maxFeePerGas ?? '0x0'))\n .toString(16),\n );\n\n const token = buildRequiredToken(\n transaction,\n nativeTokenAddress,\n maxGasCostRawHex,\n messenger,\n );\n\n if (!token) {\n return undefined;\n }\n\n const amountUsdValue = new BigNumber(token.amountUsd);\n\n const hasBalance = new BigNumber(token.balanceRaw).isGreaterThanOrEqualTo(\n token.amountRaw,\n );\n\n if (hasBalance || amountUsdValue.isGreaterThanOrEqualTo(1)) {\n return {\n ...token,\n allowUnderMinimum: true,\n skipIfBalance: true,\n };\n }\n\n const fiatRates = getTokenFiatRate(\n messenger,\n nativeTokenAddress,\n chainId,\n ) as FiatRates;\n\n const oneDollarRawHex = add0x(\n new BigNumber(1).dividedBy(fiatRates.usdRate).shiftedBy(18).toString(16),\n );\n\n const oneDollarToken = buildRequiredToken(\n transaction,\n nativeTokenAddress,\n oneDollarRawHex,\n messenger,\n );\n\n /* istanbul ignore next */\n if (!oneDollarToken) {\n return undefined;\n }\n\n return {\n ...oneDollarToken,\n allowUnderMinimum: true,\n skipIfBalance: true,\n };\n}\n\n/**\n * Get the full token properties for a specific token and amount.\n *\n * @param transaction - Transaction metadata.\n * @param tokenAddress - Token address.\n * @param amountRawHex - Raw token amount in hexadecimal format.\n * @param messenger - Controller messenger.\n * @returns The full token properties or undefined if the token data could not be retrieved.\n */\nfunction buildRequiredToken(\n transaction: TransactionMeta,\n tokenAddress: Hex,\n amountRawHex: Hex,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { chainId, txParams } = transaction;\n const from = txParams.from as Hex;\n\n const { decimals: tokenDecimals, symbol } =\n getTokenInfo(messenger, tokenAddress, chainId) ?? {};\n\n const fiatRates = getTokenFiatRate(messenger, tokenAddress, chainId);\n const tokenBalance = getTokenBalance(messenger, from, chainId, tokenAddress);\n\n if (tokenDecimals === undefined || !symbol || fiatRates === undefined) {\n return undefined;\n }\n\n const {\n amountHuman: balanceHuman,\n amountRaw: balanceRaw,\n amountFiat: balanceFiat,\n amountUsd: balanceUsd,\n } = calculateAmounts(tokenBalance, tokenDecimals, fiatRates);\n\n const { amountHuman, amountRaw, amountFiat, amountUsd } = calculateAmounts(\n amountRawHex,\n tokenDecimals,\n fiatRates,\n );\n\n return {\n address: tokenAddress,\n allowUnderMinimum: false,\n amountFiat,\n amountHuman,\n amountRaw,\n amountUsd,\n balanceFiat,\n balanceHuman,\n balanceRaw,\n balanceUsd,\n chainId,\n decimals: tokenDecimals,\n skipIfBalance: false,\n symbol,\n };\n}\n\n/**\n * Calculates the various amount representations for a token value.\n *\n * @param amountRawInput - Raw amount.\n * @param decimals - Number of decimals for the token.\n * @param fiatRates - Fiat rates for the token.\n * @returns Object containing amount in fiat, human-readable, raw, and USD formats.\n */\nfunction calculateAmounts(\n amountRawInput: BigNumber.Value,\n decimals: number,\n fiatRates: FiatRates,\n): {\n amountFiat: string;\n amountHuman: string;\n amountRaw: string;\n amountUsd: string;\n} {\n const amountRawValue = new BigNumber(amountRawInput);\n const amountHumanValue = amountRawValue.shiftedBy(-decimals);\n\n const amountFiat = amountHumanValue\n .multipliedBy(fiatRates.fiatRate)\n .toString(10);\n\n const amountUsd = amountHumanValue\n .multipliedBy(fiatRates.usdRate)\n .toString(10);\n\n const amountRaw = amountRawValue.toFixed(0);\n const amountHuman = amountHumanValue.toString(10);\n\n return {\n amountFiat,\n amountHuman,\n amountRaw,\n amountUsd,\n };\n}\n\n/**\n * Find token transfer data in a transaction.\n *\n * @param transactionMeta - Transaction metadata.\n * @returns - Token transfer data or undefined if not found.\n */\nfunction getTokenTransferData(transactionMeta: TransactionMeta):\n | {\n data: Hex;\n to: Hex;\n index?: number;\n }\n | undefined {\n const { nestedTransactions, txParams } = transactionMeta;\n const { data: singleData } = txParams;\n const singleTo = txParams?.to as Hex | undefined;\n\n if (singleData?.startsWith(FOUR_BYTE_TOKEN_TRANSFER) && singleTo) {\n return { data: singleData as Hex, to: singleTo, index: undefined };\n }\n\n const nestedCallIndex = nestedTransactions?.findIndex((call) =>\n call.data?.startsWith(FOUR_BYTE_TOKEN_TRANSFER),\n );\n\n const nestedCall =\n nestedCallIndex === undefined\n ? undefined\n : nestedTransactions?.[nestedCallIndex];\n\n if (nestedCall?.data && nestedCall.to) {\n return {\n data: nestedCall.data,\n to: nestedCall.to,\n index: nestedCallIndex,\n };\n }\n\n return undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"required-tokens.mjs","sourceRoot":"","sources":["../../src/utils/required-tokens.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { toHex } from '@metamask/controller-utils';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type { TransactionMeta } from '@metamask/transaction-controller';\nimport { add0x } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n getNativeToken,\n getTokenBalance,\n getTokenFiatRate,\n getTokenInfo,\n} from './token';\nimport type {\n FiatRates,\n TransactionPayControllerMessenger,\n TransactionPayRequiredToken,\n} from '../types';\n\nconst FOUR_BYTE_TOKEN_TRANSFER = '0xa9059cbb';\n\n/**\n * Parse required tokens from a transaction.\n *\n * If the transaction has `requiredAssets`, those are used to determine required tokens.\n * Otherwise, falls back to parsing the transaction data for token transfers.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns An array of required tokens.\n */\nexport function parseRequiredTokens(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken[] {\n const { requiredAssets } = transaction;\n\n if (requiredAssets?.length) {\n const assetTokens = requiredAssets\n .map((asset) =>\n buildRequiredToken(transaction, asset.address, asset.amount, messenger),\n )\n .filter(Boolean) as TransactionPayRequiredToken[];\n\n return assetTokens;\n }\n\n return [\n getTokenTransferToken(transaction, messenger),\n getGasFeeToken(transaction, messenger),\n ].filter(Boolean) as TransactionPayRequiredToken[];\n}\n\n/**\n * Parse a required token from a token transfer.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns The required token or undefined if the transaction is not a token transfer.\n */\nfunction getTokenTransferToken(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { data, to } = getTokenTransferData(transaction) ?? {};\n\n if (!to || !data) {\n return undefined;\n }\n\n let transferAmount: Hex | undefined;\n\n try {\n const result = new Interface(abiERC20).decodeFunctionData('transfer', data);\n transferAmount = toHex(result._value);\n } catch {\n // Intentionally empty\n }\n\n if (transferAmount === undefined) {\n return undefined;\n }\n\n return buildRequiredToken(transaction, to, transferAmount, messenger);\n}\n\n/**\n * Get the gas fee token required for a transaction.\n *\n * @param transaction - Transaction metadata.\n * @param messenger - Controller messenger.\n * @returns The gas fee token or undefined if it could not be determined.\n */\nfunction getGasFeeToken(\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { chainId, txParams } = transaction;\n const { gas, maxFeePerGas } = txParams;\n const nativeTokenAddress = getNativeToken(chainId);\n\n const maxGasCostRawHex = add0x(\n new BigNumber(gas ?? '0x0')\n .multipliedBy(new BigNumber(maxFeePerGas ?? '0x0'))\n .toString(16),\n );\n\n const token = buildRequiredToken(\n transaction,\n nativeTokenAddress,\n maxGasCostRawHex,\n messenger,\n );\n\n if (!token) {\n return undefined;\n }\n\n const amountUsdValue = new BigNumber(token.amountUsd);\n\n const hasBalance = new BigNumber(token.balanceRaw).isGreaterThanOrEqualTo(\n token.amountRaw,\n );\n\n if (hasBalance || amountUsdValue.isGreaterThanOrEqualTo(1)) {\n return {\n ...token,\n allowUnderMinimum: true,\n skipIfBalance: true,\n };\n }\n\n const fiatRates = getTokenFiatRate(\n messenger,\n nativeTokenAddress,\n chainId,\n ) as FiatRates;\n\n const oneDollarRawHex = add0x(\n new BigNumber(1).dividedBy(fiatRates.usdRate).shiftedBy(18).toString(16),\n );\n\n const oneDollarToken = buildRequiredToken(\n transaction,\n nativeTokenAddress,\n oneDollarRawHex,\n messenger,\n );\n\n /* istanbul ignore next */\n if (!oneDollarToken) {\n return undefined;\n }\n\n return {\n ...oneDollarToken,\n allowUnderMinimum: true,\n skipIfBalance: true,\n };\n}\n\n/**\n * Get the full token properties for a specific token and amount.\n *\n * @param transaction - Transaction metadata.\n * @param tokenAddress - Token address.\n * @param amountRawHex - Raw token amount in hexadecimal format.\n * @param messenger - Controller messenger.\n * @returns The full token properties or undefined if the token data could not be retrieved.\n */\nfunction buildRequiredToken(\n transaction: TransactionMeta,\n tokenAddress: Hex,\n amountRawHex: Hex,\n messenger: TransactionPayControllerMessenger,\n): TransactionPayRequiredToken | undefined {\n const { chainId, txParams } = transaction;\n const from = txParams.from as Hex;\n\n const { decimals: tokenDecimals, symbol } =\n getTokenInfo(messenger, tokenAddress, chainId) ?? {};\n\n const fiatRates = getTokenFiatRate(messenger, tokenAddress, chainId);\n const tokenBalance = getTokenBalance(messenger, from, chainId, tokenAddress);\n\n if (tokenDecimals === undefined || !symbol || fiatRates === undefined) {\n return undefined;\n }\n\n const {\n amountHuman: balanceHuman,\n amountRaw: balanceRaw,\n amountFiat: balanceFiat,\n amountUsd: balanceUsd,\n } = calculateAmounts(tokenBalance, tokenDecimals, fiatRates);\n\n const { amountHuman, amountRaw, amountFiat, amountUsd } = calculateAmounts(\n amountRawHex,\n tokenDecimals,\n fiatRates,\n );\n\n return {\n address: tokenAddress,\n allowUnderMinimum: false,\n amountFiat,\n amountHuman,\n amountRaw,\n amountUsd,\n balanceFiat,\n balanceHuman,\n balanceRaw,\n balanceUsd,\n chainId,\n decimals: tokenDecimals,\n skipIfBalance: false,\n symbol,\n };\n}\n\n/**\n * Calculates the various amount representations for a token value.\n *\n * @param amountRawInput - Raw amount.\n * @param decimals - Number of decimals for the token.\n * @param fiatRates - Fiat rates for the token.\n * @returns Object containing amount in fiat, human-readable, raw, and USD formats.\n */\nfunction calculateAmounts(\n amountRawInput: BigNumber.Value,\n decimals: number,\n fiatRates: FiatRates,\n): {\n amountFiat: string;\n amountHuman: string;\n amountRaw: string;\n amountUsd: string;\n} {\n const amountRawValue = new BigNumber(amountRawInput);\n const amountHumanValue = amountRawValue.shiftedBy(-decimals);\n\n const amountFiat = amountHumanValue\n .multipliedBy(fiatRates.fiatRate)\n .toString(10);\n\n const amountUsd = amountHumanValue\n .multipliedBy(fiatRates.usdRate)\n .toString(10);\n\n const amountRaw = amountRawValue.toFixed(0);\n const amountHuman = amountHumanValue.toString(10);\n\n return {\n amountFiat,\n amountHuman,\n amountRaw,\n amountUsd,\n };\n}\n\n/**\n * Find token transfer data in a transaction.\n *\n * @param transactionMeta - Transaction metadata.\n * @returns - Token transfer data or undefined if not found.\n */\nfunction getTokenTransferData(transactionMeta: TransactionMeta):\n | {\n data: Hex;\n to: Hex;\n index?: number;\n }\n | undefined {\n const { nestedTransactions, txParams } = transactionMeta;\n const { data: singleData } = txParams;\n const singleTo = txParams?.to as Hex | undefined;\n\n if (singleData?.startsWith(FOUR_BYTE_TOKEN_TRANSFER) && singleTo) {\n return { data: singleData as Hex, to: singleTo, index: undefined };\n }\n\n const nestedCallIndex = nestedTransactions?.findIndex((call) =>\n call.data?.startsWith(FOUR_BYTE_TOKEN_TRANSFER),\n );\n\n const nestedCall =\n nestedCallIndex === undefined || nestedCallIndex === -1\n ? undefined\n : nestedTransactions?.[nestedCallIndex];\n\n if (nestedCall?.data && nestedCall.to) {\n return {\n data: nestedCall.data,\n to: nestedCall.to,\n index: nestedCallIndex,\n };\n }\n\n return undefined;\n}\n"]}
|