@metamask-previews/transaction-controller 65.2.0-preview-2b316ced3 → 65.2.0-preview-902014de8

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
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Changed
11
+
12
+ - Trigger the first-time-interaction warning correctly for `safeTransferFrom` token transfers by including `TransactionType.tokenMethodSafeTransferFrom` in the effective-recipient decoding logic ([#8723](https://github.com/MetaMask/core/pull/8723))
13
+
10
14
  ## [65.2.0]
11
15
 
12
16
  ### Added
@@ -10,10 +10,12 @@ const validation_1 = require("./validation.cjs");
10
10
  const TOKEN_TRANSFER_TYPES = [
11
11
  types_1.TransactionType.tokenMethodTransfer,
12
12
  types_1.TransactionType.tokenMethodTransferFrom,
13
+ types_1.TransactionType.tokenMethodSafeTransferFrom,
13
14
  ];
14
15
  /**
15
16
  * 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
+ * Used when comparing existing transactions so we match by actual recipient, not txParams.to (the token
18
+ * contract for ERC20/ERC721/ERC1155 transfer methods).
17
19
  *
18
20
  * @param tx - Transaction meta with txParams and type
19
21
  * @returns Effective recipient address, or undefined
@@ -57,7 +59,8 @@ async function updateFirstTimeInteraction({ existingTransactions, getTransaction
57
59
  tx.txParams?.from?.toLowerCase() === from?.toLowerCase() &&
58
60
  getEffectiveRecipient(tx)?.toLowerCase() === recipientLower);
59
61
  // Skip API call only if we already have a tx with same from and same effective recipient (e.g. duplicate or pending).
60
- // For ERC20, effective recipient is decoded from data; using txParams.to would wrongly match any send of that token.
62
+ // For token transfers (ERC20/ERC721/ERC1155), effective recipient is decoded from data; using txParams.to
63
+ // would wrongly match any send of the same token contract.
61
64
  if (existingTransaction) {
62
65
  return;
63
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"first-time-interaction.cjs","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 { 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';\nimport { decodeTransactionData } from './transaction-type';\nimport { validateParamTo } from './validation';\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"]}
1
+ {"version":3,"file":"first-time-interaction.cjs","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 { 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';\nimport { decodeTransactionData } from './transaction-type';\nimport { validateParamTo } from './validation';\n\nconst TOKEN_TRANSFER_TYPES = [\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n TransactionType.tokenMethodSafeTransferFrom,\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 (the token\n * contract for ERC20/ERC721/ERC1155 transfer methods).\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 token transfers (ERC20/ERC721/ERC1155), effective recipient is decoded from data; using txParams.to\n // would wrongly match any send of the same token contract.\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;AAO9E,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAiB;AAyBhD,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,CA2EnD"}
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;AAO9E,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAiB;AA2BhD,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,CA4EnD"}
@@ -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;AAO9E,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAiB;AAyBhD,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,CA2EnD"}
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;AAO9E,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAiB;AA2BhD,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,CA4EnD"}
@@ -7,10 +7,12 @@ import { validateParamTo } from "./validation.mjs";
7
7
  const TOKEN_TRANSFER_TYPES = [
8
8
  TransactionType.tokenMethodTransfer,
9
9
  TransactionType.tokenMethodTransferFrom,
10
+ TransactionType.tokenMethodSafeTransferFrom,
10
11
  ];
11
12
  /**
12
13
  * 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
+ * Used when comparing existing transactions so we match by actual recipient, not txParams.to (the token
15
+ * contract for ERC20/ERC721/ERC1155 transfer methods).
14
16
  *
15
17
  * @param tx - Transaction meta with txParams and type
16
18
  * @returns Effective recipient address, or undefined
@@ -54,7 +56,8 @@ export async function updateFirstTimeInteraction({ existingTransactions, getTran
54
56
  tx.txParams?.from?.toLowerCase() === from?.toLowerCase() &&
55
57
  getEffectiveRecipient(tx)?.toLowerCase() === recipientLower);
56
58
  // 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.
59
+ // For token transfers (ERC20/ERC721/ERC1155), effective recipient is decoded from data; using txParams.to
60
+ // would wrongly match any send of the same token contract.
58
61
  if (existingTransaction) {
59
62
  return;
60
63
  }
@@ -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 { 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';\nimport { decodeTransactionData } from './transaction-type';\nimport { validateParamTo } from './validation';\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"]}
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 { 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';\nimport { decodeTransactionData } from './transaction-type';\nimport { validateParamTo } from './validation';\n\nconst TOKEN_TRANSFER_TYPES = [\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n TransactionType.tokenMethodSafeTransferFrom,\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 (the token\n * contract for ERC20/ERC721/ERC1155 transfer methods).\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 token transfers (ERC20/ERC721/ERC1155), effective recipient is decoded from data; using txParams.to\n // would wrongly match any send of the same token contract.\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": "65.2.0-preview-2b316ced3",
3
+ "version": "65.2.0-preview-902014de8",
4
4
  "description": "Stores transactions alongside their periodically updated statuses and manages interactions such as approval and cancellation",
5
5
  "keywords": [
6
6
  "Ethereum",