@metamask/transaction-controller 62.20.0 → 62.22.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 +30 -1
- package/dist/TransactionController.cjs +9 -1
- package/dist/TransactionController.cjs.map +1 -1
- package/dist/TransactionController.d.cts.map +1 -1
- package/dist/TransactionController.d.mts.map +1 -1
- package/dist/TransactionController.mjs +9 -1
- package/dist/TransactionController.mjs.map +1 -1
- package/dist/types.cjs +12 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +14 -0
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +14 -0
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +12 -0
- package/dist/types.mjs.map +1 -1
- package/dist/utils/first-time-interaction.cjs +28 -20
- package/dist/utils/first-time-interaction.cjs.map +1 -1
- package/dist/utils/first-time-interaction.d.cts.map +1 -1
- package/dist/utils/first-time-interaction.d.mts.map +1 -1
- package/dist/utils/first-time-interaction.mjs +28 -20
- package/dist/utils/first-time-interaction.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -4,6 +4,25 @@ import { validateParamTo } from "./validation.mjs";
|
|
|
4
4
|
import { getAccountAddressRelationship } from "../api/accounts-api.mjs";
|
|
5
5
|
import { projectLogger as log } from "../logger.mjs";
|
|
6
6
|
import { TransactionType } from "../types.mjs";
|
|
7
|
+
const TOKEN_TRANSFER_TYPES = [
|
|
8
|
+
TransactionType.tokenMethodTransfer,
|
|
9
|
+
TransactionType.tokenMethodTransferFrom,
|
|
10
|
+
];
|
|
11
|
+
/**
|
|
12
|
+
* Returns the effective recipient for first-time-interaction checks (decoded from data for token transfers).
|
|
13
|
+
* Used when comparing existing transactions so we match by actual recipient, not txParams.to (contract for ERC20).
|
|
14
|
+
*
|
|
15
|
+
* @param tx - Transaction meta with txParams and type
|
|
16
|
+
* @returns Effective recipient address, or undefined
|
|
17
|
+
*/
|
|
18
|
+
function getEffectiveRecipient(tx) {
|
|
19
|
+
const { data, to } = tx?.txParams ?? {};
|
|
20
|
+
if (data && TOKEN_TRANSFER_TYPES.includes(tx?.type)) {
|
|
21
|
+
const parsed = decodeTransactionData(data);
|
|
22
|
+
return (parsed?.args?._to ?? parsed?.args?.to ?? to);
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
}
|
|
7
26
|
/**
|
|
8
27
|
* Updates the first-time interaction status for a transaction.
|
|
9
28
|
*
|
|
@@ -21,32 +40,21 @@ export async function updateFirstTimeInteraction({ existingTransactions, getTran
|
|
|
21
40
|
if (!isFirstTimeInteractionEnabled()) {
|
|
22
41
|
return;
|
|
23
42
|
}
|
|
24
|
-
const { chainId, id: transactionId, txParams: {
|
|
25
|
-
|
|
26
|
-
if (data &&
|
|
27
|
-
[
|
|
28
|
-
TransactionType.tokenMethodTransfer,
|
|
29
|
-
TransactionType.tokenMethodTransferFrom,
|
|
30
|
-
].includes(type)) {
|
|
31
|
-
const parsedData = decodeTransactionData(data);
|
|
32
|
-
// _to is for ERC20, ERC721 and USDC
|
|
33
|
-
// to is for ERC1155
|
|
34
|
-
recipient = parsedData?.args?._to ?? parsedData?.args?.to;
|
|
35
|
-
}
|
|
36
|
-
// Use as fallback if no recipient is found from decode or no data is present
|
|
37
|
-
recipient ?? (recipient = to);
|
|
43
|
+
const { chainId, id: transactionId, txParams: { from }, } = transactionMeta;
|
|
44
|
+
const recipient = getEffectiveRecipient(transactionMeta);
|
|
38
45
|
const request = {
|
|
39
46
|
chainId: hexToNumber(chainId),
|
|
40
47
|
to: recipient,
|
|
41
48
|
from,
|
|
42
49
|
};
|
|
43
50
|
validateParamTo(recipient);
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
tx.
|
|
47
|
-
tx.
|
|
48
|
-
|
|
49
|
-
//
|
|
51
|
+
const recipientLower = recipient?.toLowerCase();
|
|
52
|
+
const existingTransaction = existingTransactions.find((tx) => tx.id !== transactionId &&
|
|
53
|
+
tx.chainId === chainId &&
|
|
54
|
+
tx.txParams?.from?.toLowerCase() === from?.toLowerCase() &&
|
|
55
|
+
getEffectiveRecipient(tx)?.toLowerCase() === recipientLower);
|
|
56
|
+
// Skip API call only if we already have a tx with same from and same effective recipient (e.g. duplicate or pending).
|
|
57
|
+
// For ERC20, effective recipient is decoded from data; using txParams.to would wrongly match any send of that token.
|
|
50
58
|
if (existingTransaction) {
|
|
51
59
|
return;
|
|
52
60
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"first-time-interaction.mjs","sourceRoot":"","sources":["../../src/utils/first-time-interaction.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,wBAAwB;AAE9C,OAAO,EAAE,qBAAqB,EAAE,+BAA2B;AAC3D,OAAO,EAAE,eAAe,EAAE,yBAAqB;AAC/C,OAAO,EAAE,6BAA6B,EAAE,gCAA4B;AAEpE,OAAO,EAAE,aAAa,IAAI,GAAG,EAAE,sBAAkB;AACjD,OAAO,EAAE,eAAe,EAAE,qBAAiB;
|
|
1
|
+
{"version":3,"file":"first-time-interaction.mjs","sourceRoot":"","sources":["../../src/utils/first-time-interaction.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TransactionDescription } from '@ethersproject/abi';\nimport type { TraceContext, TraceCallback } from '@metamask/controller-utils';\nimport { hexToNumber } from '@metamask/utils';\n\nimport { decodeTransactionData } from './transaction-type';\nimport { validateParamTo } from './validation';\nimport { getAccountAddressRelationship } from '../api/accounts-api';\nimport type { GetAccountAddressRelationshipRequest } from '../api/accounts-api';\nimport { projectLogger as log } from '../logger';\nimport { TransactionType } from '../types';\nimport type { TransactionMeta } from '../types';\n\nconst TOKEN_TRANSFER_TYPES = [\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n];\n\n/**\n * Returns the effective recipient for first-time-interaction checks (decoded from data for token transfers).\n * Used when comparing existing transactions so we match by actual recipient, not txParams.to (contract for ERC20).\n *\n * @param tx - Transaction meta with txParams and type\n * @returns Effective recipient address, or undefined\n */\nfunction getEffectiveRecipient(tx: TransactionMeta): string | undefined {\n const { data, to } = tx?.txParams ?? {};\n if (data && TOKEN_TRANSFER_TYPES.includes(tx?.type as TransactionType)) {\n const parsed = decodeTransactionData(data) as TransactionDescription;\n return (parsed?.args?._to ?? parsed?.args?.to ?? to) as string | undefined;\n }\n return to;\n}\n\ntype UpdateFirstTimeInteractionRequest = {\n existingTransactions: TransactionMeta[];\n getTransaction: (transactionId: string) => TransactionMeta | undefined;\n isFirstTimeInteractionEnabled: () => boolean;\n trace: TraceCallback;\n traceContext?: TraceContext;\n transactionMeta: TransactionMeta;\n updateTransaction: (\n updateParams: {\n transactionId: string;\n note: string;\n },\n updater: (txMeta: TransactionMeta) => void,\n ) => void;\n};\n\n/**\n * Updates the first-time interaction status for a transaction.\n *\n * @param params - The parameters for updating first time interaction.\n * @param params.existingTransactions - The existing transactions.\n * @param params.getTransaction - Function to get a transaction by ID.\n * @param params.isFirstTimeInteractionEnabled - The function to check if first time interaction is enabled.\n * @param params.trace - The trace callback.\n * @param params.traceContext - The trace context.\n * @param params.transactionMeta - The transaction meta object.\n * @param params.updateTransaction - Function to update transaction internal state.\n * @returns Promise that resolves when the update is complete.\n */\nexport async function updateFirstTimeInteraction({\n existingTransactions,\n getTransaction,\n isFirstTimeInteractionEnabled,\n trace,\n traceContext,\n transactionMeta,\n updateTransaction,\n}: UpdateFirstTimeInteractionRequest): Promise<void> {\n if (!isFirstTimeInteractionEnabled()) {\n return;\n }\n\n const {\n chainId,\n id: transactionId,\n txParams: { from },\n } = transactionMeta;\n\n const recipient = getEffectiveRecipient(transactionMeta);\n\n const request: GetAccountAddressRelationshipRequest = {\n chainId: hexToNumber(chainId),\n to: recipient as string,\n from,\n };\n\n validateParamTo(recipient);\n\n const recipientLower = recipient?.toLowerCase();\n const existingTransaction = existingTransactions.find(\n (tx) =>\n tx.id !== transactionId &&\n tx.chainId === chainId &&\n tx.txParams?.from?.toLowerCase() === from?.toLowerCase() &&\n getEffectiveRecipient(tx)?.toLowerCase() === recipientLower,\n );\n\n // Skip API call only if we already have a tx with same from and same effective recipient (e.g. duplicate or pending).\n // For ERC20, effective recipient is decoded from data; using txParams.to would wrongly match any send of that token.\n if (existingTransaction) {\n return;\n }\n\n try {\n const { count } = await trace(\n { name: 'Account Address Relationship', parentContext: traceContext },\n () => getAccountAddressRelationship(request),\n );\n\n const isFirstTimeInteraction =\n count === undefined ? undefined : count === 0;\n\n const finalTransactionMeta = getTransaction(transactionId);\n\n /* istanbul ignore if */\n if (!finalTransactionMeta) {\n log(\n 'Cannot update first time interaction as transaction not found',\n transactionId,\n );\n return;\n }\n\n updateTransaction(\n {\n transactionId,\n note: 'TransactionController#updateFirstInteraction - Update first time interaction',\n },\n (txMeta) => {\n txMeta.isFirstTimeInteraction = isFirstTimeInteraction;\n },\n );\n\n log('Updated first time interaction', transactionId, {\n isFirstTimeInteraction,\n });\n } catch (error) {\n log(\n 'Error fetching account address relationship, skipping first time interaction update',\n error,\n );\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask/transaction-controller",
|
|
3
|
-
"version": "62.
|
|
3
|
+
"version": "62.22.0",
|
|
4
4
|
"description": "Stores transactions alongside their periodically updated statuses and manages interactions such as approval and cancellation",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"MetaMask",
|
|
@@ -54,11 +54,11 @@
|
|
|
54
54
|
"@ethersproject/contracts": "^5.7.0",
|
|
55
55
|
"@ethersproject/providers": "^5.7.0",
|
|
56
56
|
"@ethersproject/wallet": "^5.7.0",
|
|
57
|
-
"@metamask/accounts-controller": "^
|
|
57
|
+
"@metamask/accounts-controller": "^37.0.0",
|
|
58
58
|
"@metamask/approval-controller": "^8.0.0",
|
|
59
59
|
"@metamask/base-controller": "^9.0.0",
|
|
60
60
|
"@metamask/controller-utils": "^11.19.0",
|
|
61
|
-
"@metamask/core-backend": "^6.
|
|
61
|
+
"@metamask/core-backend": "^6.1.1",
|
|
62
62
|
"@metamask/eth-query": "^4.0.0",
|
|
63
63
|
"@metamask/gas-fee-controller": "^26.0.3",
|
|
64
64
|
"@metamask/messenger": "^0.3.0",
|