@metamask-previews/transaction-controller 59.0.0-preview-3f5f144a → 59.0.0-preview-848c8e7d

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,10 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ### Fixed
11
-
12
- - Prevent incorrect `simpleSend` classification when contract code lookup fails ([#6206](https://github.com/MetaMask/core/pull/6206))
13
-
14
10
  ## [59.0.0]
15
11
 
16
12
  ### Added
@@ -5,9 +5,7 @@ const abi_1 = require("@ethersproject/abi");
5
5
  const controller_utils_1 = require("@metamask/controller-utils");
6
6
  const metamask_eth_abis_1 = require("@metamask/metamask-eth-abis");
7
7
  const eip7702_1 = require("./eip7702.cjs");
8
- const logger_1 = require("../logger.cjs");
9
8
  const types_1 = require("../types.cjs");
10
- const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, 'transaction-type');
11
9
  exports.ESTIMATE_GAS_ERROR = 'eth_estimateGas rpc method error';
12
10
  const ERC20Interface = new abi_1.Interface(metamask_eth_abis_1.abiERC20);
13
11
  const ERC721Interface = new abi_1.Interface(metamask_eth_abis_1.abiERC721);
@@ -29,14 +27,12 @@ async function determineTransactionType(txParams, ethQuery) {
29
27
  }
30
28
  let getCodeResponse;
31
29
  let isContractAddress = Boolean(data?.length);
32
- let hasReadAddressAsContractFailed = false;
33
30
  if (ethQuery) {
34
31
  const response = await readAddressAsContract(ethQuery, to);
35
32
  getCodeResponse = response.contractCode;
36
33
  isContractAddress = response.isContractAddress;
37
- hasReadAddressAsContractFailed = Boolean(response.error);
38
34
  }
39
- if (!isContractAddress && !hasReadAddressAsContractFailed) {
35
+ if (!isContractAddress) {
40
36
  return { type: types_1.TransactionType.simpleSend, getCodeResponse };
41
37
  }
42
38
  const hasValue = Number(txParams.value ?? '0') !== 0;
@@ -104,18 +100,17 @@ async function readAddressAsContract(ethQuery, address) {
104
100
  let contractCode;
105
101
  try {
106
102
  contractCode = await (0, controller_utils_1.query)(ethQuery, 'getCode', [address]);
103
+ // Not used
104
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
107
105
  }
108
106
  catch (error) {
109
- log(`Error reading contract code for address ${address}:`, error);
110
- return {
111
- contractCode: null,
112
- isContractAddress: false,
113
- error: error,
114
- };
107
+ contractCode = null;
115
108
  }
116
- const isContractAddress = contractCode !== '0x' &&
117
- contractCode !== '0x0' &&
118
- !contractCode.startsWith(eip7702_1.DELEGATION_PREFIX);
119
- return { contractCode, isContractAddress, error: null };
109
+ const isContractAddress = contractCode
110
+ ? contractCode !== '0x' &&
111
+ contractCode !== '0x0' &&
112
+ !contractCode.startsWith(eip7702_1.DELEGATION_PREFIX)
113
+ : false;
114
+ return { contractCode, isContractAddress };
120
115
  }
121
116
  //# sourceMappingURL=transaction-type.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"transaction-type.cjs","sourceRoot":"","sources":["../../src/utils/transaction-type.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { query } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport {\n abiERC721,\n abiERC20,\n abiERC1155,\n abiFiatTokenV2,\n} from '@metamask/metamask-eth-abis';\n\nimport { DELEGATION_PREFIX } from './eip7702';\nimport { createModuleLogger, projectLogger } from '../logger';\nimport type { InferTransactionTypeResult, TransactionParams } from '../types';\nimport { TransactionType } from '../types';\n\nconst log = createModuleLogger(projectLogger, 'transaction-type');\n\nexport const ESTIMATE_GAS_ERROR = 'eth_estimateGas rpc method error';\n\nconst ERC20Interface = new Interface(abiERC20);\nconst ERC721Interface = new Interface(abiERC721);\nconst ERC1155Interface = new Interface(abiERC1155);\nconst USDCInterface = new Interface(abiFiatTokenV2);\n\n/**\n * Determines the type of the transaction by analyzing the txParams.\n * It will never return TRANSACTION_TYPE_CANCEL or TRANSACTION_TYPE_RETRY as these\n * represent specific events that we specify manually at transaction creation.\n *\n * @param txParams - Parameters for the transaction.\n * @param ethQuery - EthQuery instance.\n * @returns A object with the transaction type and the contract code response in Hex.\n */\nexport async function determineTransactionType(\n txParams: TransactionParams,\n ethQuery?: EthQuery,\n): Promise<InferTransactionTypeResult> {\n const { data, to } = txParams;\n\n if (data && !to) {\n return { type: TransactionType.deployContract, getCodeResponse: undefined };\n }\n\n let getCodeResponse;\n let isContractAddress = Boolean(data?.length);\n let hasReadAddressAsContractFailed: boolean = false;\n\n if (ethQuery) {\n const response = await readAddressAsContract(ethQuery, to);\n\n getCodeResponse = response.contractCode;\n isContractAddress = response.isContractAddress;\n hasReadAddressAsContractFailed = Boolean(response.error);\n }\n\n if (!isContractAddress && !hasReadAddressAsContractFailed) {\n return { type: TransactionType.simpleSend, getCodeResponse };\n }\n\n const hasValue = Number(txParams.value ?? '0') !== 0;\n\n const contractInteractionResult = {\n type: TransactionType.contractInteraction,\n getCodeResponse,\n };\n\n if (!data || hasValue) {\n return contractInteractionResult;\n }\n\n const name = getMethodName(data);\n\n if (!name) {\n return contractInteractionResult;\n }\n\n const tokenMethodName = [\n TransactionType.tokenMethodApprove,\n TransactionType.tokenMethodSetApprovalForAll,\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n TransactionType.tokenMethodSafeTransferFrom,\n TransactionType.tokenMethodIncreaseAllowance,\n ].find(\n (methodName) => methodName.toLowerCase() === (name as string).toLowerCase(),\n );\n\n if (tokenMethodName) {\n return { type: tokenMethodName, getCodeResponse };\n }\n\n return contractInteractionResult;\n}\n\n/**\n * Attempts to decode transaction data using ABIs for three different token standards: ERC20, ERC721, ERC1155.\n * The data will decode correctly if the transaction is an interaction with a contract that matches one of these\n * contract standards\n *\n * @param data - Encoded transaction data.\n * @returns A representation of an ethereum contract call.\n */\nfunction getMethodName(data?: string): string | undefined {\n if (!data || data.length < 10) {\n return undefined;\n }\n\n const fourByte = data.substring(0, 10).toLowerCase();\n\n for (const interfaceInstance of [\n ERC20Interface,\n ERC721Interface,\n ERC1155Interface,\n USDCInterface,\n ]) {\n try {\n return interfaceInstance.getFunction(fourByte).name;\n } catch {\n // Intentionally empty\n }\n }\n\n return undefined;\n}\n\n/**\n * Reads an Ethereum address and determines if it is a contract address.\n *\n * @param ethQuery - The Ethereum query object used to interact with the Ethereum blockchain.\n * @param address - The Ethereum address.\n * @returns An object containing the contract code and a boolean indicating if it is a contract address.\n */\nasync function readAddressAsContract(\n ethQuery: EthQuery,\n address?: string,\n): Promise<{\n contractCode: string | null;\n isContractAddress: boolean;\n error: Error | null;\n}> {\n let contractCode;\n try {\n contractCode = await query(ethQuery, 'getCode', [address]);\n } catch (error) {\n log(`Error reading contract code for address ${address}:`, error);\n return {\n contractCode: null,\n isContractAddress: false,\n error: error as Error,\n };\n }\n\n const isContractAddress =\n contractCode !== '0x' &&\n contractCode !== '0x0' &&\n !contractCode.startsWith(DELEGATION_PREFIX);\n return { contractCode, isContractAddress, error: null };\n}\n"]}
1
+ {"version":3,"file":"transaction-type.cjs","sourceRoot":"","sources":["../../src/utils/transaction-type.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { query } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport {\n abiERC721,\n abiERC20,\n abiERC1155,\n abiFiatTokenV2,\n} from '@metamask/metamask-eth-abis';\n\nimport { DELEGATION_PREFIX } from './eip7702';\nimport type { InferTransactionTypeResult, TransactionParams } from '../types';\nimport { TransactionType } from '../types';\n\nexport const ESTIMATE_GAS_ERROR = 'eth_estimateGas rpc method error';\n\nconst ERC20Interface = new Interface(abiERC20);\nconst ERC721Interface = new Interface(abiERC721);\nconst ERC1155Interface = new Interface(abiERC1155);\nconst USDCInterface = new Interface(abiFiatTokenV2);\n\n/**\n * Determines the type of the transaction by analyzing the txParams.\n * It will never return TRANSACTION_TYPE_CANCEL or TRANSACTION_TYPE_RETRY as these\n * represent specific events that we specify manually at transaction creation.\n *\n * @param txParams - Parameters for the transaction.\n * @param ethQuery - EthQuery instance.\n * @returns A object with the transaction type and the contract code response in Hex.\n */\nexport async function determineTransactionType(\n txParams: TransactionParams,\n ethQuery?: EthQuery,\n): Promise<InferTransactionTypeResult> {\n const { data, to } = txParams;\n\n if (data && !to) {\n return { type: TransactionType.deployContract, getCodeResponse: undefined };\n }\n\n let getCodeResponse;\n let isContractAddress = Boolean(data?.length);\n\n if (ethQuery) {\n const response = await readAddressAsContract(ethQuery, to);\n\n getCodeResponse = response.contractCode;\n isContractAddress = response.isContractAddress;\n }\n\n if (!isContractAddress) {\n return { type: TransactionType.simpleSend, getCodeResponse };\n }\n\n const hasValue = Number(txParams.value ?? '0') !== 0;\n\n const contractInteractionResult = {\n type: TransactionType.contractInteraction,\n getCodeResponse,\n };\n\n if (!data || hasValue) {\n return contractInteractionResult;\n }\n\n const name = getMethodName(data);\n\n if (!name) {\n return contractInteractionResult;\n }\n\n const tokenMethodName = [\n TransactionType.tokenMethodApprove,\n TransactionType.tokenMethodSetApprovalForAll,\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n TransactionType.tokenMethodSafeTransferFrom,\n TransactionType.tokenMethodIncreaseAllowance,\n ].find(\n (methodName) => methodName.toLowerCase() === (name as string).toLowerCase(),\n );\n\n if (tokenMethodName) {\n return { type: tokenMethodName, getCodeResponse };\n }\n\n return contractInteractionResult;\n}\n\n/**\n * Attempts to decode transaction data using ABIs for three different token standards: ERC20, ERC721, ERC1155.\n * The data will decode correctly if the transaction is an interaction with a contract that matches one of these\n * contract standards\n *\n * @param data - Encoded transaction data.\n * @returns A representation of an ethereum contract call.\n */\nfunction getMethodName(data?: string): string | undefined {\n if (!data || data.length < 10) {\n return undefined;\n }\n\n const fourByte = data.substring(0, 10).toLowerCase();\n\n for (const interfaceInstance of [\n ERC20Interface,\n ERC721Interface,\n ERC1155Interface,\n USDCInterface,\n ]) {\n try {\n return interfaceInstance.getFunction(fourByte).name;\n } catch {\n // Intentionally empty\n }\n }\n\n return undefined;\n}\n\n/**\n * Reads an Ethereum address and determines if it is a contract address.\n *\n * @param ethQuery - The Ethereum query object used to interact with the Ethereum blockchain.\n * @param address - The Ethereum address.\n * @returns An object containing the contract code and a boolean indicating if it is a contract address.\n */\nasync function readAddressAsContract(\n ethQuery: EthQuery,\n address?: string,\n): Promise<{\n contractCode: string | null;\n isContractAddress: boolean;\n}> {\n let contractCode;\n try {\n contractCode = await query(ethQuery, 'getCode', [address]);\n // Not used\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (error) {\n contractCode = null;\n }\n\n const isContractAddress = contractCode\n ? contractCode !== '0x' &&\n contractCode !== '0x0' &&\n !contractCode.startsWith(DELEGATION_PREFIX)\n : false;\n return { contractCode, isContractAddress };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"transaction-type.d.cts","sourceRoot":"","sources":["../../src/utils/transaction-type.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,4BAA4B;AAUhD,OAAO,KAAK,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,qBAAiB;AAK9E,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAOrE;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,CAAC,EAAE,QAAQ,GAClB,OAAO,CAAC,0BAA0B,CAAC,CAwDrC"}
1
+ {"version":3,"file":"transaction-type.d.cts","sourceRoot":"","sources":["../../src/utils/transaction-type.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,4BAA4B;AAShD,OAAO,KAAK,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,qBAAiB;AAG9E,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAOrE;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,CAAC,EAAE,QAAQ,GAClB,OAAO,CAAC,0BAA0B,CAAC,CAsDrC"}
@@ -1 +1 @@
1
- {"version":3,"file":"transaction-type.d.mts","sourceRoot":"","sources":["../../src/utils/transaction-type.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,4BAA4B;AAUhD,OAAO,KAAK,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,qBAAiB;AAK9E,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAOrE;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,CAAC,EAAE,QAAQ,GAClB,OAAO,CAAC,0BAA0B,CAAC,CAwDrC"}
1
+ {"version":3,"file":"transaction-type.d.mts","sourceRoot":"","sources":["../../src/utils/transaction-type.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,4BAA4B;AAShD,OAAO,KAAK,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,qBAAiB;AAG9E,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAOrE;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,CAAC,EAAE,QAAQ,GAClB,OAAO,CAAC,0BAA0B,CAAC,CAsDrC"}
@@ -2,9 +2,7 @@ import { Interface } from "@ethersproject/abi";
2
2
  import { query } from "@metamask/controller-utils";
3
3
  import { abiERC721, abiERC20, abiERC1155, abiFiatTokenV2 } from "@metamask/metamask-eth-abis";
4
4
  import { DELEGATION_PREFIX } from "./eip7702.mjs";
5
- import { createModuleLogger, projectLogger } from "../logger.mjs";
6
5
  import { TransactionType } from "../types.mjs";
7
- const log = createModuleLogger(projectLogger, 'transaction-type');
8
6
  export const ESTIMATE_GAS_ERROR = 'eth_estimateGas rpc method error';
9
7
  const ERC20Interface = new Interface(abiERC20);
10
8
  const ERC721Interface = new Interface(abiERC721);
@@ -26,14 +24,12 @@ export async function determineTransactionType(txParams, ethQuery) {
26
24
  }
27
25
  let getCodeResponse;
28
26
  let isContractAddress = Boolean(data?.length);
29
- let hasReadAddressAsContractFailed = false;
30
27
  if (ethQuery) {
31
28
  const response = await readAddressAsContract(ethQuery, to);
32
29
  getCodeResponse = response.contractCode;
33
30
  isContractAddress = response.isContractAddress;
34
- hasReadAddressAsContractFailed = Boolean(response.error);
35
31
  }
36
- if (!isContractAddress && !hasReadAddressAsContractFailed) {
32
+ if (!isContractAddress) {
37
33
  return { type: TransactionType.simpleSend, getCodeResponse };
38
34
  }
39
35
  const hasValue = Number(txParams.value ?? '0') !== 0;
@@ -100,18 +96,17 @@ async function readAddressAsContract(ethQuery, address) {
100
96
  let contractCode;
101
97
  try {
102
98
  contractCode = await query(ethQuery, 'getCode', [address]);
99
+ // Not used
100
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
103
101
  }
104
102
  catch (error) {
105
- log(`Error reading contract code for address ${address}:`, error);
106
- return {
107
- contractCode: null,
108
- isContractAddress: false,
109
- error: error,
110
- };
103
+ contractCode = null;
111
104
  }
112
- const isContractAddress = contractCode !== '0x' &&
113
- contractCode !== '0x0' &&
114
- !contractCode.startsWith(DELEGATION_PREFIX);
115
- return { contractCode, isContractAddress, error: null };
105
+ const isContractAddress = contractCode
106
+ ? contractCode !== '0x' &&
107
+ contractCode !== '0x0' &&
108
+ !contractCode.startsWith(DELEGATION_PREFIX)
109
+ : false;
110
+ return { contractCode, isContractAddress };
116
111
  }
117
112
  //# sourceMappingURL=transaction-type.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"transaction-type.mjs","sourceRoot":"","sources":["../../src/utils/transaction-type.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { query } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport {\n abiERC721,\n abiERC20,\n abiERC1155,\n abiFiatTokenV2,\n} from '@metamask/metamask-eth-abis';\n\nimport { DELEGATION_PREFIX } from './eip7702';\nimport { createModuleLogger, projectLogger } from '../logger';\nimport type { InferTransactionTypeResult, TransactionParams } from '../types';\nimport { TransactionType } from '../types';\n\nconst log = createModuleLogger(projectLogger, 'transaction-type');\n\nexport const ESTIMATE_GAS_ERROR = 'eth_estimateGas rpc method error';\n\nconst ERC20Interface = new Interface(abiERC20);\nconst ERC721Interface = new Interface(abiERC721);\nconst ERC1155Interface = new Interface(abiERC1155);\nconst USDCInterface = new Interface(abiFiatTokenV2);\n\n/**\n * Determines the type of the transaction by analyzing the txParams.\n * It will never return TRANSACTION_TYPE_CANCEL or TRANSACTION_TYPE_RETRY as these\n * represent specific events that we specify manually at transaction creation.\n *\n * @param txParams - Parameters for the transaction.\n * @param ethQuery - EthQuery instance.\n * @returns A object with the transaction type and the contract code response in Hex.\n */\nexport async function determineTransactionType(\n txParams: TransactionParams,\n ethQuery?: EthQuery,\n): Promise<InferTransactionTypeResult> {\n const { data, to } = txParams;\n\n if (data && !to) {\n return { type: TransactionType.deployContract, getCodeResponse: undefined };\n }\n\n let getCodeResponse;\n let isContractAddress = Boolean(data?.length);\n let hasReadAddressAsContractFailed: boolean = false;\n\n if (ethQuery) {\n const response = await readAddressAsContract(ethQuery, to);\n\n getCodeResponse = response.contractCode;\n isContractAddress = response.isContractAddress;\n hasReadAddressAsContractFailed = Boolean(response.error);\n }\n\n if (!isContractAddress && !hasReadAddressAsContractFailed) {\n return { type: TransactionType.simpleSend, getCodeResponse };\n }\n\n const hasValue = Number(txParams.value ?? '0') !== 0;\n\n const contractInteractionResult = {\n type: TransactionType.contractInteraction,\n getCodeResponse,\n };\n\n if (!data || hasValue) {\n return contractInteractionResult;\n }\n\n const name = getMethodName(data);\n\n if (!name) {\n return contractInteractionResult;\n }\n\n const tokenMethodName = [\n TransactionType.tokenMethodApprove,\n TransactionType.tokenMethodSetApprovalForAll,\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n TransactionType.tokenMethodSafeTransferFrom,\n TransactionType.tokenMethodIncreaseAllowance,\n ].find(\n (methodName) => methodName.toLowerCase() === (name as string).toLowerCase(),\n );\n\n if (tokenMethodName) {\n return { type: tokenMethodName, getCodeResponse };\n }\n\n return contractInteractionResult;\n}\n\n/**\n * Attempts to decode transaction data using ABIs for three different token standards: ERC20, ERC721, ERC1155.\n * The data will decode correctly if the transaction is an interaction with a contract that matches one of these\n * contract standards\n *\n * @param data - Encoded transaction data.\n * @returns A representation of an ethereum contract call.\n */\nfunction getMethodName(data?: string): string | undefined {\n if (!data || data.length < 10) {\n return undefined;\n }\n\n const fourByte = data.substring(0, 10).toLowerCase();\n\n for (const interfaceInstance of [\n ERC20Interface,\n ERC721Interface,\n ERC1155Interface,\n USDCInterface,\n ]) {\n try {\n return interfaceInstance.getFunction(fourByte).name;\n } catch {\n // Intentionally empty\n }\n }\n\n return undefined;\n}\n\n/**\n * Reads an Ethereum address and determines if it is a contract address.\n *\n * @param ethQuery - The Ethereum query object used to interact with the Ethereum blockchain.\n * @param address - The Ethereum address.\n * @returns An object containing the contract code and a boolean indicating if it is a contract address.\n */\nasync function readAddressAsContract(\n ethQuery: EthQuery,\n address?: string,\n): Promise<{\n contractCode: string | null;\n isContractAddress: boolean;\n error: Error | null;\n}> {\n let contractCode;\n try {\n contractCode = await query(ethQuery, 'getCode', [address]);\n } catch (error) {\n log(`Error reading contract code for address ${address}:`, error);\n return {\n contractCode: null,\n isContractAddress: false,\n error: error as Error,\n };\n }\n\n const isContractAddress =\n contractCode !== '0x' &&\n contractCode !== '0x0' &&\n !contractCode.startsWith(DELEGATION_PREFIX);\n return { contractCode, isContractAddress, error: null };\n}\n"]}
1
+ {"version":3,"file":"transaction-type.mjs","sourceRoot":"","sources":["../../src/utils/transaction-type.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { query } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport {\n abiERC721,\n abiERC20,\n abiERC1155,\n abiFiatTokenV2,\n} from '@metamask/metamask-eth-abis';\n\nimport { DELEGATION_PREFIX } from './eip7702';\nimport type { InferTransactionTypeResult, TransactionParams } from '../types';\nimport { TransactionType } from '../types';\n\nexport const ESTIMATE_GAS_ERROR = 'eth_estimateGas rpc method error';\n\nconst ERC20Interface = new Interface(abiERC20);\nconst ERC721Interface = new Interface(abiERC721);\nconst ERC1155Interface = new Interface(abiERC1155);\nconst USDCInterface = new Interface(abiFiatTokenV2);\n\n/**\n * Determines the type of the transaction by analyzing the txParams.\n * It will never return TRANSACTION_TYPE_CANCEL or TRANSACTION_TYPE_RETRY as these\n * represent specific events that we specify manually at transaction creation.\n *\n * @param txParams - Parameters for the transaction.\n * @param ethQuery - EthQuery instance.\n * @returns A object with the transaction type and the contract code response in Hex.\n */\nexport async function determineTransactionType(\n txParams: TransactionParams,\n ethQuery?: EthQuery,\n): Promise<InferTransactionTypeResult> {\n const { data, to } = txParams;\n\n if (data && !to) {\n return { type: TransactionType.deployContract, getCodeResponse: undefined };\n }\n\n let getCodeResponse;\n let isContractAddress = Boolean(data?.length);\n\n if (ethQuery) {\n const response = await readAddressAsContract(ethQuery, to);\n\n getCodeResponse = response.contractCode;\n isContractAddress = response.isContractAddress;\n }\n\n if (!isContractAddress) {\n return { type: TransactionType.simpleSend, getCodeResponse };\n }\n\n const hasValue = Number(txParams.value ?? '0') !== 0;\n\n const contractInteractionResult = {\n type: TransactionType.contractInteraction,\n getCodeResponse,\n };\n\n if (!data || hasValue) {\n return contractInteractionResult;\n }\n\n const name = getMethodName(data);\n\n if (!name) {\n return contractInteractionResult;\n }\n\n const tokenMethodName = [\n TransactionType.tokenMethodApprove,\n TransactionType.tokenMethodSetApprovalForAll,\n TransactionType.tokenMethodTransfer,\n TransactionType.tokenMethodTransferFrom,\n TransactionType.tokenMethodSafeTransferFrom,\n TransactionType.tokenMethodIncreaseAllowance,\n ].find(\n (methodName) => methodName.toLowerCase() === (name as string).toLowerCase(),\n );\n\n if (tokenMethodName) {\n return { type: tokenMethodName, getCodeResponse };\n }\n\n return contractInteractionResult;\n}\n\n/**\n * Attempts to decode transaction data using ABIs for three different token standards: ERC20, ERC721, ERC1155.\n * The data will decode correctly if the transaction is an interaction with a contract that matches one of these\n * contract standards\n *\n * @param data - Encoded transaction data.\n * @returns A representation of an ethereum contract call.\n */\nfunction getMethodName(data?: string): string | undefined {\n if (!data || data.length < 10) {\n return undefined;\n }\n\n const fourByte = data.substring(0, 10).toLowerCase();\n\n for (const interfaceInstance of [\n ERC20Interface,\n ERC721Interface,\n ERC1155Interface,\n USDCInterface,\n ]) {\n try {\n return interfaceInstance.getFunction(fourByte).name;\n } catch {\n // Intentionally empty\n }\n }\n\n return undefined;\n}\n\n/**\n * Reads an Ethereum address and determines if it is a contract address.\n *\n * @param ethQuery - The Ethereum query object used to interact with the Ethereum blockchain.\n * @param address - The Ethereum address.\n * @returns An object containing the contract code and a boolean indicating if it is a contract address.\n */\nasync function readAddressAsContract(\n ethQuery: EthQuery,\n address?: string,\n): Promise<{\n contractCode: string | null;\n isContractAddress: boolean;\n}> {\n let contractCode;\n try {\n contractCode = await query(ethQuery, 'getCode', [address]);\n // Not used\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (error) {\n contractCode = null;\n }\n\n const isContractAddress = contractCode\n ? contractCode !== '0x' &&\n contractCode !== '0x0' &&\n !contractCode.startsWith(DELEGATION_PREFIX)\n : false;\n return { contractCode, isContractAddress };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/transaction-controller",
3
- "version": "59.0.0-preview-3f5f144a",
3
+ "version": "59.0.0-preview-848c8e7d",
4
4
  "description": "Stores transactions alongside their periodically updated statuses and manages interactions such as approval and cancellation",
5
5
  "keywords": [
6
6
  "MetaMask",