@metamask-previews/transaction-controller 62.20.0-preview-327d86601 → 62.20.0-preview-ccd74c3b

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 CHANGED
@@ -11,10 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  - Add `perpsAcrossDeposit` and `predictAcrossDeposit` transaction types for Across MetaMask Pay submissions ([#7886](https://github.com/MetaMask/core/pull/7886))
13
13
 
14
- ### Fixed
15
-
16
- - Use effective recipient (decoded from tx data for token transfers) when checking for existing transactions in first-time interaction logic ([#8130](https://github.com/MetaMask/core/pull/8130))
17
-
18
14
  ## [62.20.0]
19
15
 
20
16
  ### Changed
@@ -7,25 +7,6 @@ const validation_1 = require("./validation.cjs");
7
7
  const accounts_api_1 = require("../api/accounts-api.cjs");
8
8
  const logger_1 = require("../logger.cjs");
9
9
  const types_1 = require("../types.cjs");
10
- const TOKEN_TRANSFER_TYPES = [
11
- types_1.TransactionType.tokenMethodTransfer,
12
- types_1.TransactionType.tokenMethodTransferFrom,
13
- ];
14
- /**
15
- * Returns the effective recipient for first-time-interaction checks (decoded from data for token transfers).
16
- * Used when comparing existing transactions so we match by actual recipient, not txParams.to (contract for ERC20).
17
- *
18
- * @param tx - Transaction meta with txParams and type
19
- * @returns Effective recipient address, or undefined
20
- */
21
- function getEffectiveRecipient(tx) {
22
- const { data, to } = tx?.txParams ?? {};
23
- if (data && TOKEN_TRANSFER_TYPES.includes(tx?.type)) {
24
- const parsed = (0, transaction_type_1.decodeTransactionData)(data);
25
- return (parsed?.args?._to ?? parsed?.args?.to ?? to);
26
- }
27
- return to;
28
- }
29
10
  /**
30
11
  * Updates the first-time interaction status for a transaction.
31
12
  *
@@ -63,13 +44,12 @@ async function updateFirstTimeInteraction({ existingTransactions, getTransaction
63
44
  from,
64
45
  };
65
46
  (0, validation_1.validateParamTo)(recipient);
66
- const recipientLower = recipient?.toLowerCase();
67
- const existingTransaction = existingTransactions.find((tx) => tx.id !== transactionId &&
68
- tx.chainId === chainId &&
69
- tx.txParams?.from?.toLowerCase() === from?.toLowerCase() &&
70
- getEffectiveRecipient(tx)?.toLowerCase() === recipientLower);
71
- // Skip API call only if we already have a tx with same from and same effective recipient (e.g. duplicate or pending).
72
- // For ERC20, effective recipient is decoded from data; using txParams.to would wrongly match any send of that token.
47
+ const existingTransaction = existingTransactions.find((tx) => tx.chainId === chainId &&
48
+ tx.txParams.from.toLowerCase() === from.toLowerCase() &&
49
+ tx.txParams.to?.toLowerCase() === to?.toLowerCase() &&
50
+ tx.id !== transactionId);
51
+ // Check if there is an existing transaction with the same from, to, and chainId
52
+ // else we continue to check the account address relationship from API
73
53
  if (existingTransaction) {
74
54
  return;
75
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"first-time-interaction.cjs","sourceRoot":"","sources":["../../src/utils/first-time-interaction.ts"],"names":[],"mappings":";;;AAEA,2CAA8C;AAE9C,6DAA2D;AAC3D,iDAA+C;AAC/C,0DAAoE;AAEpE,0CAAiD;AACjD,wCAA2C;AAG3C,MAAM,oBAAoB,GAAG;IAC3B,uBAAe,CAAC,mBAAmB;IACnC,uBAAe,CAAC,uBAAuB;CACxC,CAAC;AAEF;;;;;;GAMG;AACH,SAAS,qBAAqB,CAAC,EAAmB;IAChD,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,QAAQ,IAAI,EAAE,CAAC;IACxC,IAAI,IAAI,IAAI,oBAAoB,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAuB,CAAC,EAAE,CAAC;QACvE,MAAM,MAAM,GAAG,IAAA,wCAAqB,EAAC,IAAI,CAA2B,CAAC;QACrE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAuB,CAAC;IAC7E,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAkBD;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,0BAA0B,CAAC,EAC/C,oBAAoB,EACpB,cAAc,EACd,6BAA6B,EAC7B,KAAK,EACL,YAAY,EACZ,eAAe,EACf,iBAAiB,GACiB;IAClC,IAAI,CAAC,6BAA6B,EAAE,EAAE,CAAC;QACrC,OAAO;IACT,CAAC;IAED,MAAM,EACJ,OAAO,EACP,EAAE,EAAE,aAAa,EACjB,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAC5B,IAAI,GACL,GAAG,eAAe,CAAC;IAEpB,IAAI,SAAS,CAAC;IACd,IACE,IAAI;QACJ;YACE,uBAAe,CAAC,mBAAmB;YACnC,uBAAe,CAAC,uBAAuB;SACxC,CAAC,QAAQ,CAAC,IAAuB,CAAC,EACnC,CAAC;QACD,MAAM,UAAU,GAAG,IAAA,wCAAqB,EAAC,IAAI,CAA2B,CAAC;QACzE,oCAAoC;QACpC,oBAAoB;QACpB,SAAS,GAAG,UAAU,EAAE,IAAI,EAAE,GAAG,IAAI,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC;IAC5D,CAAC;IAED,6EAA6E;IAC7E,SAAS,KAAT,SAAS,GAAK,EAAE,EAAC;IAEjB,MAAM,OAAO,GAAyC;QACpD,OAAO,EAAE,IAAA,mBAAW,EAAC,OAAO,CAAC;QAC7B,EAAE,EAAE,SAAmB;QACvB,IAAI;KACL,CAAC;IAEF,IAAA,4BAAe,EAAC,SAAS,CAAC,CAAC;IAE3B,MAAM,cAAc,GAAG,SAAS,EAAE,WAAW,EAAE,CAAC;IAChD,MAAM,mBAAmB,GAAG,oBAAoB,CAAC,IAAI,CACnD,CAAC,EAAE,EAAE,EAAE,CACL,EAAE,CAAC,EAAE,KAAK,aAAa;QACvB,EAAE,CAAC,OAAO,KAAK,OAAO;QACtB,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,IAAI,EAAE,WAAW,EAAE;QACxD,qBAAqB,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,KAAK,cAAc,CAC9D,CAAC;IAEF,sHAAsH;IACtH,qHAAqH;IACrH,IAAI,mBAAmB,EAAE,CAAC;QACxB,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,KAAK,CAC3B,EAAE,IAAI,EAAE,8BAA8B,EAAE,aAAa,EAAE,YAAY,EAAE,EACrE,GAAG,EAAE,CAAC,IAAA,4CAA6B,EAAC,OAAO,CAAC,CAC7C,CAAC;QAEF,MAAM,sBAAsB,GAC1B,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;QAEhD,MAAM,oBAAoB,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;QAE3D,wBAAwB;QACxB,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC1B,IAAA,sBAAG,EACD,+DAA+D,EAC/D,aAAa,CACd,CAAC;YACF,OAAO;QACT,CAAC;QAED,iBAAiB,CACf;YACE,aAAa;YACb,IAAI,EAAE,8EAA8E;SACrF,EACD,CAAC,MAAM,EAAE,EAAE;YACT,MAAM,CAAC,sBAAsB,GAAG,sBAAsB,CAAC;QACzD,CAAC,CACF,CAAC;QAEF,IAAA,sBAAG,EAAC,gCAAgC,EAAE,aAAa,EAAE;YACnD,sBAAsB;SACvB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAA,sBAAG,EACD,qFAAqF,EACrF,KAAK,CACN,CAAC;IACJ,CAAC;AACH,CAAC;AAnGD,gEAmGC","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: { data, from, to },\n type,\n } = transactionMeta;\n\n let recipient;\n if (\n data &&\n [\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n ].includes(type as TransactionType)\n ) {\n const parsedData = decodeTransactionData(data) as TransactionDescription;\n // _to is for ERC20, ERC721 and USDC\n // to is for ERC1155\n recipient = parsedData?.args?._to ?? parsedData?.args?.to;\n }\n\n // Use as fallback if no recipient is found from decode or no data is present\n recipient ??= to;\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"]}
1
+ {"version":3,"file":"first-time-interaction.cjs","sourceRoot":"","sources":["../../src/utils/first-time-interaction.ts"],"names":[],"mappings":";;;AAEA,2CAA8C;AAE9C,6DAA2D;AAC3D,iDAA+C;AAC/C,0DAAoE;AAEpE,0CAAiD;AACjD,wCAA2C;AAmB3C;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,0BAA0B,CAAC,EAC/C,oBAAoB,EACpB,cAAc,EACd,6BAA6B,EAC7B,KAAK,EACL,YAAY,EACZ,eAAe,EACf,iBAAiB,GACiB;IAClC,IAAI,CAAC,6BAA6B,EAAE,EAAE,CAAC;QACrC,OAAO;IACT,CAAC;IAED,MAAM,EACJ,OAAO,EACP,EAAE,EAAE,aAAa,EACjB,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAC5B,IAAI,GACL,GAAG,eAAe,CAAC;IAEpB,IAAI,SAAS,CAAC;IACd,IACE,IAAI;QACJ;YACE,uBAAe,CAAC,mBAAmB;YACnC,uBAAe,CAAC,uBAAuB;SACxC,CAAC,QAAQ,CAAC,IAAuB,CAAC,EACnC,CAAC;QACD,MAAM,UAAU,GAAG,IAAA,wCAAqB,EAAC,IAAI,CAA2B,CAAC;QACzE,oCAAoC;QACpC,oBAAoB;QACpB,SAAS,GAAG,UAAU,EAAE,IAAI,EAAE,GAAG,IAAI,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC;IAC5D,CAAC;IAED,6EAA6E;IAC7E,SAAS,KAAT,SAAS,GAAK,EAAE,EAAC;IAEjB,MAAM,OAAO,GAAyC;QACpD,OAAO,EAAE,IAAA,mBAAW,EAAC,OAAO,CAAC;QAC7B,EAAE,EAAE,SAAmB;QACvB,IAAI;KACL,CAAC;IAEF,IAAA,4BAAe,EAAC,SAAS,CAAC,CAAC;IAE3B,MAAM,mBAAmB,GAAG,oBAAoB,CAAC,IAAI,CACnD,CAAC,EAAE,EAAE,EAAE,CACL,EAAE,CAAC,OAAO,KAAK,OAAO;QACtB,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE;QACrD,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE;QACnD,EAAE,CAAC,EAAE,KAAK,aAAa,CAC1B,CAAC;IAEF,gFAAgF;IAChF,sEAAsE;IACtE,IAAI,mBAAmB,EAAE,CAAC;QACxB,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,KAAK,CAC3B,EAAE,IAAI,EAAE,8BAA8B,EAAE,aAAa,EAAE,YAAY,EAAE,EACrE,GAAG,EAAE,CAAC,IAAA,4CAA6B,EAAC,OAAO,CAAC,CAC7C,CAAC;QAEF,MAAM,sBAAsB,GAC1B,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;QAEhD,MAAM,oBAAoB,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;QAE3D,wBAAwB;QACxB,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC1B,IAAA,sBAAG,EACD,+DAA+D,EAC/D,aAAa,CACd,CAAC;YACF,OAAO;QACT,CAAC;QAED,iBAAiB,CACf;YACE,aAAa;YACb,IAAI,EAAE,8EAA8E;SACrF,EACD,CAAC,MAAM,EAAE,EAAE;YACT,MAAM,CAAC,sBAAsB,GAAG,sBAAsB,CAAC;QACzD,CAAC,CACF,CAAC;QAEF,IAAA,sBAAG,EAAC,gCAAgC,EAAE,aAAa,EAAE;YACnD,sBAAsB;SACvB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAA,sBAAG,EACD,qFAAqF,EACrF,KAAK,CACN,CAAC;IACJ,CAAC;AACH,CAAC;AAlGD,gEAkGC","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\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: { data, from, to },\n type,\n } = transactionMeta;\n\n let recipient;\n if (\n data &&\n [\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n ].includes(type as TransactionType)\n ) {\n const parsedData = decodeTransactionData(data) as TransactionDescription;\n // _to is for ERC20, ERC721 and USDC\n // to is for ERC1155\n recipient = parsedData?.args?._to ?? parsedData?.args?.to;\n }\n\n // Use as fallback if no recipient is found from decode or no data is present\n recipient ??= to;\n\n const request: GetAccountAddressRelationshipRequest = {\n chainId: hexToNumber(chainId),\n to: recipient as string,\n from,\n };\n\n validateParamTo(recipient);\n\n const existingTransaction = existingTransactions.find(\n (tx) =>\n tx.chainId === chainId &&\n tx.txParams.from.toLowerCase() === from.toLowerCase() &&\n tx.txParams.to?.toLowerCase() === to?.toLowerCase() &&\n tx.id !== transactionId,\n );\n\n // Check if there is an existing transaction with the same from, to, and chainId\n // else we continue to check the account address relationship from API\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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"first-time-interaction.d.cts","sourceRoot":"","sources":["../../src/utils/first-time-interaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,mCAAmC;AAS9E,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAiB;AAuBhD,KAAK,iCAAiC,GAAG;IACvC,oBAAoB,EAAE,eAAe,EAAE,CAAC;IACxC,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;IACvE,6BAA6B,EAAE,MAAM,OAAO,CAAC;IAC7C,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,EAAE,eAAe,CAAC;IACjC,iBAAiB,EAAE,CACjB,YAAY,EAAE;QACZ,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;KACd,EACD,OAAO,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,KACvC,IAAI,CAAC;CACX,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAsB,0BAA0B,CAAC,EAC/C,oBAAoB,EACpB,cAAc,EACd,6BAA6B,EAC7B,KAAK,EACL,YAAY,EACZ,eAAe,EACf,iBAAiB,GAClB,EAAE,iCAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CA2FnD"}
1
+ {"version":3,"file":"first-time-interaction.d.cts","sourceRoot":"","sources":["../../src/utils/first-time-interaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,mCAAmC;AAS9E,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAiB;AAEhD,KAAK,iCAAiC,GAAG;IACvC,oBAAoB,EAAE,eAAe,EAAE,CAAC;IACxC,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;IACvE,6BAA6B,EAAE,MAAM,OAAO,CAAC;IAC7C,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,EAAE,eAAe,CAAC;IACjC,iBAAiB,EAAE,CACjB,YAAY,EAAE;QACZ,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;KACd,EACD,OAAO,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,KACvC,IAAI,CAAC;CACX,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAsB,0BAA0B,CAAC,EAC/C,oBAAoB,EACpB,cAAc,EACd,6BAA6B,EAC7B,KAAK,EACL,YAAY,EACZ,eAAe,EACf,iBAAiB,GAClB,EAAE,iCAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CA0FnD"}
@@ -1 +1 @@
1
- {"version":3,"file":"first-time-interaction.d.mts","sourceRoot":"","sources":["../../src/utils/first-time-interaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,mCAAmC;AAS9E,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAiB;AAuBhD,KAAK,iCAAiC,GAAG;IACvC,oBAAoB,EAAE,eAAe,EAAE,CAAC;IACxC,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;IACvE,6BAA6B,EAAE,MAAM,OAAO,CAAC;IAC7C,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,EAAE,eAAe,CAAC;IACjC,iBAAiB,EAAE,CACjB,YAAY,EAAE;QACZ,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;KACd,EACD,OAAO,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,KACvC,IAAI,CAAC;CACX,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAsB,0BAA0B,CAAC,EAC/C,oBAAoB,EACpB,cAAc,EACd,6BAA6B,EAC7B,KAAK,EACL,YAAY,EACZ,eAAe,EACf,iBAAiB,GAClB,EAAE,iCAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CA2FnD"}
1
+ {"version":3,"file":"first-time-interaction.d.mts","sourceRoot":"","sources":["../../src/utils/first-time-interaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,mCAAmC;AAS9E,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAiB;AAEhD,KAAK,iCAAiC,GAAG;IACvC,oBAAoB,EAAE,eAAe,EAAE,CAAC;IACxC,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;IACvE,6BAA6B,EAAE,MAAM,OAAO,CAAC;IAC7C,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,eAAe,EAAE,eAAe,CAAC;IACjC,iBAAiB,EAAE,CACjB,YAAY,EAAE;QACZ,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;KACd,EACD,OAAO,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,KACvC,IAAI,CAAC;CACX,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAsB,0BAA0B,CAAC,EAC/C,oBAAoB,EACpB,cAAc,EACd,6BAA6B,EAC7B,KAAK,EACL,YAAY,EACZ,eAAe,EACf,iBAAiB,GAClB,EAAE,iCAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CA0FnD"}
@@ -4,25 +4,6 @@ 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
- }
26
7
  /**
27
8
  * Updates the first-time interaction status for a transaction.
28
9
  *
@@ -60,13 +41,12 @@ export async function updateFirstTimeInteraction({ existingTransactions, getTran
60
41
  from,
61
42
  };
62
43
  validateParamTo(recipient);
63
- const recipientLower = recipient?.toLowerCase();
64
- const existingTransaction = existingTransactions.find((tx) => tx.id !== transactionId &&
65
- tx.chainId === chainId &&
66
- tx.txParams?.from?.toLowerCase() === from?.toLowerCase() &&
67
- getEffectiveRecipient(tx)?.toLowerCase() === recipientLower);
68
- // Skip API call only if we already have a tx with same from and same effective recipient (e.g. duplicate or pending).
69
- // For ERC20, effective recipient is decoded from data; using txParams.to would wrongly match any send of that token.
44
+ const existingTransaction = existingTransactions.find((tx) => tx.chainId === chainId &&
45
+ tx.txParams.from.toLowerCase() === from.toLowerCase() &&
46
+ tx.txParams.to?.toLowerCase() === to?.toLowerCase() &&
47
+ tx.id !== transactionId);
48
+ // Check if there is an existing transaction with the same from, to, and chainId
49
+ // else we continue to check the account address relationship from API
70
50
  if (existingTransaction) {
71
51
  return;
72
52
  }
@@ -1 +1 @@
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: { data, from, to },\n type,\n } = transactionMeta;\n\n let recipient;\n if (\n data &&\n [\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n ].includes(type as TransactionType)\n ) {\n const parsedData = decodeTransactionData(data) as TransactionDescription;\n // _to is for ERC20, ERC721 and USDC\n // to is for ERC1155\n recipient = parsedData?.args?._to ?? parsedData?.args?.to;\n }\n\n // Use as fallback if no recipient is found from decode or no data is present\n recipient ??= to;\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"]}
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;AAmB3C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,EAC/C,oBAAoB,EACpB,cAAc,EACd,6BAA6B,EAC7B,KAAK,EACL,YAAY,EACZ,eAAe,EACf,iBAAiB,GACiB;IAClC,IAAI,CAAC,6BAA6B,EAAE,EAAE,CAAC;QACrC,OAAO;IACT,CAAC;IAED,MAAM,EACJ,OAAO,EACP,EAAE,EAAE,aAAa,EACjB,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAC5B,IAAI,GACL,GAAG,eAAe,CAAC;IAEpB,IAAI,SAAS,CAAC;IACd,IACE,IAAI;QACJ;YACE,eAAe,CAAC,mBAAmB;YACnC,eAAe,CAAC,uBAAuB;SACxC,CAAC,QAAQ,CAAC,IAAuB,CAAC,EACnC,CAAC;QACD,MAAM,UAAU,GAAG,qBAAqB,CAAC,IAAI,CAA2B,CAAC;QACzE,oCAAoC;QACpC,oBAAoB;QACpB,SAAS,GAAG,UAAU,EAAE,IAAI,EAAE,GAAG,IAAI,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC;IAC5D,CAAC;IAED,6EAA6E;IAC7E,SAAS,KAAT,SAAS,GAAK,EAAE,EAAC;IAEjB,MAAM,OAAO,GAAyC;QACpD,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC;QAC7B,EAAE,EAAE,SAAmB;QACvB,IAAI;KACL,CAAC;IAEF,eAAe,CAAC,SAAS,CAAC,CAAC;IAE3B,MAAM,mBAAmB,GAAG,oBAAoB,CAAC,IAAI,CACnD,CAAC,EAAE,EAAE,EAAE,CACL,EAAE,CAAC,OAAO,KAAK,OAAO;QACtB,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE;QACrD,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE;QACnD,EAAE,CAAC,EAAE,KAAK,aAAa,CAC1B,CAAC;IAEF,gFAAgF;IAChF,sEAAsE;IACtE,IAAI,mBAAmB,EAAE,CAAC;QACxB,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,KAAK,CAC3B,EAAE,IAAI,EAAE,8BAA8B,EAAE,aAAa,EAAE,YAAY,EAAE,EACrE,GAAG,EAAE,CAAC,6BAA6B,CAAC,OAAO,CAAC,CAC7C,CAAC;QAEF,MAAM,sBAAsB,GAC1B,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;QAEhD,MAAM,oBAAoB,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;QAE3D,wBAAwB;QACxB,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC1B,GAAG,CACD,+DAA+D,EAC/D,aAAa,CACd,CAAC;YACF,OAAO;QACT,CAAC;QAED,iBAAiB,CACf;YACE,aAAa;YACb,IAAI,EAAE,8EAA8E;SACrF,EACD,CAAC,MAAM,EAAE,EAAE;YACT,MAAM,CAAC,sBAAsB,GAAG,sBAAsB,CAAC;QACzD,CAAC,CACF,CAAC;QAEF,GAAG,CAAC,gCAAgC,EAAE,aAAa,EAAE;YACnD,sBAAsB;SACvB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CACD,qFAAqF,EACrF,KAAK,CACN,CAAC;IACJ,CAAC;AACH,CAAC","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\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: { data, from, to },\n type,\n } = transactionMeta;\n\n let recipient;\n if (\n data &&\n [\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n ].includes(type as TransactionType)\n ) {\n const parsedData = decodeTransactionData(data) as TransactionDescription;\n // _to is for ERC20, ERC721 and USDC\n // to is for ERC1155\n recipient = parsedData?.args?._to ?? parsedData?.args?.to;\n }\n\n // Use as fallback if no recipient is found from decode or no data is present\n recipient ??= to;\n\n const request: GetAccountAddressRelationshipRequest = {\n chainId: hexToNumber(chainId),\n to: recipient as string,\n from,\n };\n\n validateParamTo(recipient);\n\n const existingTransaction = existingTransactions.find(\n (tx) =>\n tx.chainId === chainId &&\n tx.txParams.from.toLowerCase() === from.toLowerCase() &&\n tx.txParams.to?.toLowerCase() === to?.toLowerCase() &&\n tx.id !== transactionId,\n );\n\n // Check if there is an existing transaction with the same from, to, and chainId\n // else we continue to check the account address relationship from API\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-previews/transaction-controller",
3
- "version": "62.20.0-preview-327d86601",
3
+ "version": "62.20.0-preview-ccd74c3b",
4
4
  "description": "Stores transactions alongside their periodically updated statuses and manages interactions such as approval and cancellation",
5
5
  "keywords": [
6
6
  "MetaMask",