@metamask/transaction-controller 38.2.0 → 39.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [39.0.0]
11
+
12
+ ### Changed
13
+
14
+ - **BREAKING:** Bump peer dependency `@metamask/accounts-controller` from `^18.0.0` to `^19.0.0` ([#4915](https://github.com/MetaMask/core/pull/4915))
15
+ - Bump `@metamask/controller-utils` from `^11.4.2` to `^11.4.3` ([#4915](https://github.com/MetaMask/core/pull/4915))
16
+
17
+ ## [38.3.0]
18
+
19
+ ### Added
20
+
21
+ - Validate gas fee properties to ensure they are valid hexadecimal strings ([#4854](https://github.com/MetaMask/core/pull/4854))
22
+
23
+ ### Fixed
24
+
25
+ - Fix gas limit estimation on new transactions and via `estimateGas` and `estimateGasBuffered` methods ([#4897](https://github.com/MetaMask/core/pull/4897))
26
+
10
27
  ## [38.2.0]
11
28
 
12
29
  ### Added
@@ -1103,7 +1120,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1103
1120
 
1104
1121
  All changes listed after this point were applied to this package following the monorepo conversion.
1105
1122
 
1106
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@38.2.0...HEAD
1123
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@39.0.0...HEAD
1124
+ [39.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@38.3.0...@metamask/transaction-controller@39.0.0
1125
+ [38.3.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@38.2.0...@metamask/transaction-controller@38.3.0
1107
1126
  [38.2.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@38.1.0...@metamask/transaction-controller@38.2.0
1108
1127
  [38.1.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@38.0.0...@metamask/transaction-controller@38.1.0
1109
1128
  [38.0.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-controller@37.3.0...@metamask/transaction-controller@38.0.0
@@ -493,13 +493,13 @@ export declare class TransactionController extends BaseController<typeof control
493
493
  * @returns The gas and gas price.
494
494
  */
495
495
  estimateGas(transaction: TransactionParams, networkClientId?: NetworkClientId): Promise<{
496
- gas: string;
496
+ gas: `0x${string}`;
497
497
  simulationFails: {
498
- reason: any;
499
- errorKey: any;
498
+ reason?: string | undefined;
499
+ errorKey?: string | undefined;
500
500
  debug: {
501
- blockNumber: string;
502
- blockGasLimit: string;
501
+ blockNumber?: string | undefined;
502
+ blockGasLimit?: string | undefined;
503
503
  };
504
504
  } | undefined;
505
505
  }>;
@@ -513,11 +513,11 @@ export declare class TransactionController extends BaseController<typeof control
513
513
  estimateGasBuffered(transaction: TransactionParams, multiplier: number, networkClientId?: NetworkClientId): Promise<{
514
514
  gas: `0x${string}`;
515
515
  simulationFails: {
516
- reason: any;
517
- errorKey: any;
516
+ reason?: string | undefined;
517
+ errorKey?: string | undefined;
518
518
  debug: {
519
- blockNumber: string;
520
- blockGasLimit: string;
519
+ blockNumber?: string | undefined;
520
+ blockGasLimit?: string | undefined;
521
521
  };
522
522
  } | undefined;
523
523
  }>;
@@ -493,13 +493,13 @@ export declare class TransactionController extends BaseController<typeof control
493
493
  * @returns The gas and gas price.
494
494
  */
495
495
  estimateGas(transaction: TransactionParams, networkClientId?: NetworkClientId): Promise<{
496
- gas: string;
496
+ gas: `0x${string}`;
497
497
  simulationFails: {
498
- reason: any;
499
- errorKey: any;
498
+ reason?: string | undefined;
499
+ errorKey?: string | undefined;
500
500
  debug: {
501
- blockNumber: string;
502
- blockGasLimit: string;
501
+ blockNumber?: string | undefined;
502
+ blockGasLimit?: string | undefined;
503
503
  };
504
504
  } | undefined;
505
505
  }>;
@@ -513,11 +513,11 @@ export declare class TransactionController extends BaseController<typeof control
513
513
  estimateGasBuffered(transaction: TransactionParams, multiplier: number, networkClientId?: NetworkClientId): Promise<{
514
514
  gas: `0x${string}`;
515
515
  simulationFails: {
516
- reason: any;
517
- errorKey: any;
516
+ reason?: string | undefined;
517
+ errorKey?: string | undefined;
518
518
  debug: {
519
- blockNumber: string;
520
- blockGasLimit: string;
519
+ blockNumber?: string | undefined;
520
+ blockGasLimit?: string | undefined;
521
521
  };
522
522
  } | undefined;
523
523
  }>;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  /* eslint-disable jsdoc/require-jsdoc */
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.addGasBuffer = exports.estimateGas = exports.updateGas = exports.GAS_ESTIMATE_FALLBACK_MULTIPLIER = exports.DEFAULT_GAS_MULTIPLIER = exports.FIXED_GAS = exports.log = void 0;
4
+ exports.addGasBuffer = exports.estimateGas = exports.updateGas = exports.MAX_GAS_BLOCK_PERCENT = exports.GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT = exports.DEFAULT_GAS_MULTIPLIER = exports.FIXED_GAS = exports.log = void 0;
5
5
  const controller_utils_1 = require("@metamask/controller-utils");
6
6
  const utils_1 = require("@metamask/utils");
7
7
  const constants_1 = require("../constants.cjs");
@@ -9,7 +9,8 @@ const logger_1 = require("../logger.cjs");
9
9
  exports.log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'gas');
10
10
  exports.FIXED_GAS = '0x5208';
11
11
  exports.DEFAULT_GAS_MULTIPLIER = 1.5;
12
- exports.GAS_ESTIMATE_FALLBACK_MULTIPLIER = 0.35;
12
+ exports.GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT = 35;
13
+ exports.MAX_GAS_BLOCK_PERCENT = 90;
13
14
  async function updateGas(request) {
14
15
  const { txMeta } = request;
15
16
  const initialParams = { ...txMeta.txParams };
@@ -28,16 +29,18 @@ exports.updateGas = updateGas;
28
29
  async function estimateGas(txParams, ethQuery) {
29
30
  const request = { ...txParams };
30
31
  const { data, value } = request;
31
- const { gasLimit: gasLimitHex, number: blockNumber } = await getLatestBlock(ethQuery);
32
- const gasLimitBN = (0, controller_utils_1.hexToBN)(gasLimitHex);
32
+ const { gasLimit: blockGasLimit, number: blockNumber } = await getLatestBlock(ethQuery);
33
+ const blockGasLimitBN = (0, controller_utils_1.hexToBN)(blockGasLimit);
34
+ const fallback = (0, controller_utils_1.BNToHex)((0, controller_utils_1.fractionBN)(blockGasLimitBN, exports.GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT, 100));
33
35
  request.data = data ? (0, utils_1.add0x)(data) : data;
34
- request.gas = (0, controller_utils_1.BNToHex)(gasLimitBN.muln(exports.GAS_ESTIMATE_FALLBACK_MULTIPLIER));
35
36
  request.value = value || '0x0';
36
- let estimatedGas = request.gas;
37
+ delete request.gasPrice;
38
+ delete request.maxFeePerGas;
39
+ delete request.maxPriorityFeePerGas;
40
+ let estimatedGas = fallback;
37
41
  let simulationFails;
38
42
  try {
39
43
  estimatedGas = await (0, controller_utils_1.query)(ethQuery, 'estimateGas', [request]);
40
- // TODO: Replace `any` with type
41
44
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
45
  }
43
46
  catch (error) {
@@ -46,13 +49,13 @@ async function estimateGas(txParams, ethQuery) {
46
49
  errorKey: error.errorKey,
47
50
  debug: {
48
51
  blockNumber,
49
- blockGasLimit: gasLimitHex,
52
+ blockGasLimit,
50
53
  },
51
54
  };
52
- (0, exports.log)('Estimation failed', { ...simulationFails, fallback: estimateGas });
55
+ (0, exports.log)('Estimation failed', { ...simulationFails, fallback });
53
56
  }
54
57
  return {
55
- blockGasLimit: gasLimitHex,
58
+ blockGasLimit,
56
59
  estimatedGas,
57
60
  simulationFails,
58
61
  };
@@ -60,8 +63,8 @@ async function estimateGas(txParams, ethQuery) {
60
63
  exports.estimateGas = estimateGas;
61
64
  function addGasBuffer(estimatedGas, blockGasLimit, multiplier) {
62
65
  const estimatedGasBN = (0, controller_utils_1.hexToBN)(estimatedGas);
63
- const maxGasBN = (0, controller_utils_1.hexToBN)(blockGasLimit).muln(0.9);
64
- const paddedGasBN = estimatedGasBN.muln(multiplier);
66
+ const maxGasBN = (0, controller_utils_1.fractionBN)((0, controller_utils_1.hexToBN)(blockGasLimit), exports.MAX_GAS_BLOCK_PERCENT, 100);
67
+ const paddedGasBN = (0, controller_utils_1.fractionBN)(estimatedGasBN, multiplier * 100, 100);
65
68
  if (estimatedGasBN.gt(maxGasBN)) {
66
69
  const estimatedGasHex = (0, utils_1.add0x)(estimatedGas);
67
70
  (0, exports.log)('Using estimated value', estimatedGasHex);
@@ -1 +1 @@
1
- {"version":3,"file":"gas.cjs","sourceRoot":"","sources":["../../src/utils/gas.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable jsdoc/require-jsdoc */\n\nimport { BNToHex, hexToBN, query } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport type { Hex } from '@metamask/utils';\nimport { add0x, createModuleLogger } from '@metamask/utils';\n\nimport { GAS_BUFFER_CHAIN_OVERRIDES } from '../constants';\nimport { projectLogger } from '../logger';\nimport type { TransactionMeta, TransactionParams } from '../types';\n\nexport type UpdateGasRequest = {\n ethQuery: EthQuery;\n isCustomNetwork: boolean;\n chainId: Hex;\n txMeta: TransactionMeta;\n};\n\nexport const log = createModuleLogger(projectLogger, 'gas');\n\nexport const FIXED_GAS = '0x5208';\nexport const DEFAULT_GAS_MULTIPLIER = 1.5;\nexport const GAS_ESTIMATE_FALLBACK_MULTIPLIER = 0.35;\n\nexport async function updateGas(request: UpdateGasRequest) {\n const { txMeta } = request;\n const initialParams = { ...txMeta.txParams };\n\n const [gas, simulationFails] = await getGas(request);\n\n txMeta.txParams.gas = gas;\n txMeta.simulationFails = simulationFails;\n\n if (!initialParams.gas) {\n txMeta.originalGasEstimate = txMeta.txParams.gas;\n }\n\n if (!txMeta.defaultGasEstimates) {\n txMeta.defaultGasEstimates = {};\n }\n\n txMeta.defaultGasEstimates.gas = txMeta.txParams.gas;\n}\n\nexport async function estimateGas(\n txParams: TransactionParams,\n ethQuery: EthQuery,\n) {\n const request = { ...txParams };\n const { data, value } = request;\n\n const { gasLimit: gasLimitHex, number: blockNumber } = await getLatestBlock(\n ethQuery,\n );\n\n const gasLimitBN = hexToBN(gasLimitHex);\n\n request.data = data ? add0x(data) : data;\n request.gas = BNToHex(gasLimitBN.muln(GAS_ESTIMATE_FALLBACK_MULTIPLIER));\n request.value = value || '0x0';\n\n let estimatedGas = request.gas;\n let simulationFails;\n\n try {\n estimatedGas = await query(ethQuery, 'estimateGas', [request]);\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n simulationFails = {\n reason: error.message,\n errorKey: error.errorKey,\n debug: {\n blockNumber,\n blockGasLimit: gasLimitHex,\n },\n };\n\n log('Estimation failed', { ...simulationFails, fallback: estimateGas });\n }\n\n return {\n blockGasLimit: gasLimitHex,\n estimatedGas,\n simulationFails,\n };\n}\n\nexport function addGasBuffer(\n estimatedGas: string,\n blockGasLimit: string,\n multiplier: number,\n) {\n const estimatedGasBN = hexToBN(estimatedGas);\n const maxGasBN = hexToBN(blockGasLimit).muln(0.9);\n const paddedGasBN = estimatedGasBN.muln(multiplier);\n\n if (estimatedGasBN.gt(maxGasBN)) {\n const estimatedGasHex = add0x(estimatedGas);\n log('Using estimated value', estimatedGasHex);\n return estimatedGasHex;\n }\n\n if (paddedGasBN.lt(maxGasBN)) {\n const paddedHex = add0x(BNToHex(paddedGasBN));\n log('Using padded estimate', paddedHex, multiplier);\n return paddedHex;\n }\n\n const maxHex = add0x(BNToHex(maxGasBN));\n log('Using 90% of block gas limit', maxHex);\n return maxHex;\n}\n\nasync function getGas(\n request: UpdateGasRequest,\n): Promise<[string, TransactionMeta['simulationFails']?]> {\n const { isCustomNetwork, chainId, txMeta } = request;\n\n if (txMeta.txParams.gas) {\n log('Using value from request', txMeta.txParams.gas);\n return [txMeta.txParams.gas];\n }\n\n if (await requiresFixedGas(request)) {\n log('Using fixed value', FIXED_GAS);\n return [FIXED_GAS];\n }\n\n const { blockGasLimit, estimatedGas, simulationFails } = await estimateGas(\n txMeta.txParams,\n request.ethQuery,\n );\n\n if (isCustomNetwork || simulationFails) {\n log(\n isCustomNetwork\n ? 'Using original estimate as custom network'\n : 'Using original fallback estimate as simulation failed',\n );\n return [estimatedGas, simulationFails];\n }\n\n const bufferMultiplier =\n GAS_BUFFER_CHAIN_OVERRIDES[\n chainId as keyof typeof GAS_BUFFER_CHAIN_OVERRIDES\n ] ?? DEFAULT_GAS_MULTIPLIER;\n\n const bufferedGas = addGasBuffer(\n estimatedGas,\n blockGasLimit,\n bufferMultiplier,\n );\n\n return [bufferedGas, simulationFails];\n}\n\nasync function requiresFixedGas({\n ethQuery,\n txMeta,\n isCustomNetwork,\n}: UpdateGasRequest): Promise<boolean> {\n const {\n txParams: { to, data },\n } = txMeta;\n\n if (isCustomNetwork || !to || data) {\n return false;\n }\n\n const code = await getCode(ethQuery, to);\n\n return !code || code === '0x';\n}\n\nasync function getCode(\n ethQuery: EthQuery,\n address: string,\n): Promise<string | undefined> {\n return await query(ethQuery, 'getCode', [address]);\n}\n\nasync function getLatestBlock(\n ethQuery: EthQuery,\n): Promise<{ gasLimit: string; number: string }> {\n return await query(ethQuery, 'getBlockByNumber', ['latest', false]);\n}\n"]}
1
+ {"version":3,"file":"gas.cjs","sourceRoot":"","sources":["../../src/utils/gas.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable jsdoc/require-jsdoc */\n\nimport {\n BNToHex,\n fractionBN,\n hexToBN,\n query,\n} from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport type { Hex } from '@metamask/utils';\nimport { add0x, createModuleLogger } from '@metamask/utils';\n\nimport { GAS_BUFFER_CHAIN_OVERRIDES } from '../constants';\nimport { projectLogger } from '../logger';\nimport type { TransactionMeta, TransactionParams } from '../types';\n\nexport type UpdateGasRequest = {\n ethQuery: EthQuery;\n isCustomNetwork: boolean;\n chainId: Hex;\n txMeta: TransactionMeta;\n};\n\nexport const log = createModuleLogger(projectLogger, 'gas');\n\nexport const FIXED_GAS = '0x5208';\nexport const DEFAULT_GAS_MULTIPLIER = 1.5;\nexport const GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT = 35;\nexport const MAX_GAS_BLOCK_PERCENT = 90;\n\nexport async function updateGas(request: UpdateGasRequest) {\n const { txMeta } = request;\n const initialParams = { ...txMeta.txParams };\n\n const [gas, simulationFails] = await getGas(request);\n\n txMeta.txParams.gas = gas;\n txMeta.simulationFails = simulationFails;\n\n if (!initialParams.gas) {\n txMeta.originalGasEstimate = txMeta.txParams.gas;\n }\n\n if (!txMeta.defaultGasEstimates) {\n txMeta.defaultGasEstimates = {};\n }\n\n txMeta.defaultGasEstimates.gas = txMeta.txParams.gas;\n}\n\nexport async function estimateGas(\n txParams: TransactionParams,\n ethQuery: EthQuery,\n) {\n const request = { ...txParams };\n const { data, value } = request;\n\n const { gasLimit: blockGasLimit, number: blockNumber } = await getLatestBlock(\n ethQuery,\n );\n\n const blockGasLimitBN = hexToBN(blockGasLimit);\n\n const fallback = BNToHex(\n fractionBN(blockGasLimitBN, GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT, 100),\n );\n\n request.data = data ? add0x(data) : data;\n request.value = value || '0x0';\n\n delete request.gasPrice;\n delete request.maxFeePerGas;\n delete request.maxPriorityFeePerGas;\n\n let estimatedGas = fallback;\n let simulationFails: TransactionMeta['simulationFails'];\n\n try {\n estimatedGas = await query(ethQuery, 'estimateGas', [request]);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n simulationFails = {\n reason: error.message,\n errorKey: error.errorKey,\n debug: {\n blockNumber,\n blockGasLimit,\n },\n };\n\n log('Estimation failed', { ...simulationFails, fallback });\n }\n\n return {\n blockGasLimit,\n estimatedGas,\n simulationFails,\n };\n}\n\nexport function addGasBuffer(\n estimatedGas: string,\n blockGasLimit: string,\n multiplier: number,\n) {\n const estimatedGasBN = hexToBN(estimatedGas);\n\n const maxGasBN = fractionBN(\n hexToBN(blockGasLimit),\n MAX_GAS_BLOCK_PERCENT,\n 100,\n );\n\n const paddedGasBN = fractionBN(estimatedGasBN, multiplier * 100, 100);\n\n if (estimatedGasBN.gt(maxGasBN)) {\n const estimatedGasHex = add0x(estimatedGas);\n log('Using estimated value', estimatedGasHex);\n return estimatedGasHex;\n }\n\n if (paddedGasBN.lt(maxGasBN)) {\n const paddedHex = add0x(BNToHex(paddedGasBN));\n log('Using padded estimate', paddedHex, multiplier);\n return paddedHex;\n }\n\n const maxHex = add0x(BNToHex(maxGasBN));\n log('Using 90% of block gas limit', maxHex);\n return maxHex;\n}\n\nasync function getGas(\n request: UpdateGasRequest,\n): Promise<[string, TransactionMeta['simulationFails']?]> {\n const { isCustomNetwork, chainId, txMeta } = request;\n\n if (txMeta.txParams.gas) {\n log('Using value from request', txMeta.txParams.gas);\n return [txMeta.txParams.gas];\n }\n\n if (await requiresFixedGas(request)) {\n log('Using fixed value', FIXED_GAS);\n return [FIXED_GAS];\n }\n\n const { blockGasLimit, estimatedGas, simulationFails } = await estimateGas(\n txMeta.txParams,\n request.ethQuery,\n );\n\n if (isCustomNetwork || simulationFails) {\n log(\n isCustomNetwork\n ? 'Using original estimate as custom network'\n : 'Using original fallback estimate as simulation failed',\n );\n return [estimatedGas, simulationFails];\n }\n\n const bufferMultiplier =\n GAS_BUFFER_CHAIN_OVERRIDES[\n chainId as keyof typeof GAS_BUFFER_CHAIN_OVERRIDES\n ] ?? DEFAULT_GAS_MULTIPLIER;\n\n const bufferedGas = addGasBuffer(\n estimatedGas,\n blockGasLimit,\n bufferMultiplier,\n );\n\n return [bufferedGas, simulationFails];\n}\n\nasync function requiresFixedGas({\n ethQuery,\n txMeta,\n isCustomNetwork,\n}: UpdateGasRequest): Promise<boolean> {\n const {\n txParams: { to, data },\n } = txMeta;\n\n if (isCustomNetwork || !to || data) {\n return false;\n }\n\n const code = await getCode(ethQuery, to);\n\n return !code || code === '0x';\n}\n\nasync function getCode(\n ethQuery: EthQuery,\n address: string,\n): Promise<string | undefined> {\n return await query(ethQuery, 'getCode', [address]);\n}\n\nasync function getLatestBlock(\n ethQuery: EthQuery,\n): Promise<{ gasLimit: string; number: string }> {\n return await query(ethQuery, 'getBlockByNumber', ['latest', false]);\n}\n"]}
@@ -11,17 +11,18 @@ export type UpdateGasRequest = {
11
11
  export declare const log: import("debug").Debugger;
12
12
  export declare const FIXED_GAS = "0x5208";
13
13
  export declare const DEFAULT_GAS_MULTIPLIER = 1.5;
14
- export declare const GAS_ESTIMATE_FALLBACK_MULTIPLIER = 0.35;
14
+ export declare const GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT = 35;
15
+ export declare const MAX_GAS_BLOCK_PERCENT = 90;
15
16
  export declare function updateGas(request: UpdateGasRequest): Promise<void>;
16
17
  export declare function estimateGas(txParams: TransactionParams, ethQuery: EthQuery): Promise<{
17
18
  blockGasLimit: string;
18
- estimatedGas: string;
19
+ estimatedGas: `0x${string}`;
19
20
  simulationFails: {
20
- reason: any;
21
- errorKey: any;
21
+ reason?: string | undefined;
22
+ errorKey?: string | undefined;
22
23
  debug: {
23
- blockNumber: string;
24
- blockGasLimit: string;
24
+ blockNumber?: string | undefined;
25
+ blockGasLimit?: string | undefined;
25
26
  };
26
27
  } | undefined;
27
28
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"gas.d.cts","sourceRoot":"","sources":["../../src/utils/gas.ts"],"names":[],"mappings":";AAGA,OAAO,KAAK,QAAQ,4BAA4B;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,qBAAiB;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,GAAG,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,GAAG,0BAA2C,CAAC;AAE5D,eAAO,MAAM,SAAS,WAAW,CAAC;AAClC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,iBAkBxD;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,EAAE,QAAQ;;;;;;;;;;;GAwCnB;AAED,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,iBAqBnB"}
1
+ {"version":3,"file":"gas.d.cts","sourceRoot":"","sources":["../../src/utils/gas.ts"],"names":[],"mappings":";AAQA,OAAO,KAAK,QAAQ,4BAA4B;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,qBAAiB;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,GAAG,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,GAAG,0BAA2C,CAAC;AAE5D,eAAO,MAAM,SAAS,WAAW,CAAC;AAClC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,mCAAmC,KAAK,CAAC;AACtD,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,iBAkBxD;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,EAAE,QAAQ;;;;;;;;;;;GA8CnB;AAED,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,iBA2BnB"}
@@ -11,17 +11,18 @@ export type UpdateGasRequest = {
11
11
  export declare const log: import("debug").Debugger;
12
12
  export declare const FIXED_GAS = "0x5208";
13
13
  export declare const DEFAULT_GAS_MULTIPLIER = 1.5;
14
- export declare const GAS_ESTIMATE_FALLBACK_MULTIPLIER = 0.35;
14
+ export declare const GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT = 35;
15
+ export declare const MAX_GAS_BLOCK_PERCENT = 90;
15
16
  export declare function updateGas(request: UpdateGasRequest): Promise<void>;
16
17
  export declare function estimateGas(txParams: TransactionParams, ethQuery: EthQuery): Promise<{
17
18
  blockGasLimit: string;
18
- estimatedGas: string;
19
+ estimatedGas: `0x${string}`;
19
20
  simulationFails: {
20
- reason: any;
21
- errorKey: any;
21
+ reason?: string | undefined;
22
+ errorKey?: string | undefined;
22
23
  debug: {
23
- blockNumber: string;
24
- blockGasLimit: string;
24
+ blockNumber?: string | undefined;
25
+ blockGasLimit?: string | undefined;
25
26
  };
26
27
  } | undefined;
27
28
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"gas.d.mts","sourceRoot":"","sources":["../../src/utils/gas.ts"],"names":[],"mappings":";AAGA,OAAO,KAAK,QAAQ,4BAA4B;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,qBAAiB;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,GAAG,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,GAAG,0BAA2C,CAAC;AAE5D,eAAO,MAAM,SAAS,WAAW,CAAC;AAClC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,iBAkBxD;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,EAAE,QAAQ;;;;;;;;;;;GAwCnB;AAED,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,iBAqBnB"}
1
+ {"version":3,"file":"gas.d.mts","sourceRoot":"","sources":["../../src/utils/gas.ts"],"names":[],"mappings":";AAQA,OAAO,KAAK,QAAQ,4BAA4B;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,qBAAiB;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,GAAG,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,GAAG,0BAA2C,CAAC;AAE5D,eAAO,MAAM,SAAS,WAAW,CAAC;AAClC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,mCAAmC,KAAK,CAAC;AACtD,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,iBAkBxD;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,iBAAiB,EAC3B,QAAQ,EAAE,QAAQ;;;;;;;;;;;GA8CnB;AAED,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,iBA2BnB"}
@@ -1,12 +1,13 @@
1
1
  /* eslint-disable jsdoc/require-jsdoc */
2
- import { BNToHex, hexToBN, query } from "@metamask/controller-utils";
2
+ import { BNToHex, fractionBN, hexToBN, query } from "@metamask/controller-utils";
3
3
  import { add0x, createModuleLogger } from "@metamask/utils";
4
4
  import { GAS_BUFFER_CHAIN_OVERRIDES } from "../constants.mjs";
5
5
  import { projectLogger } from "../logger.mjs";
6
6
  export const log = createModuleLogger(projectLogger, 'gas');
7
7
  export const FIXED_GAS = '0x5208';
8
8
  export const DEFAULT_GAS_MULTIPLIER = 1.5;
9
- export const GAS_ESTIMATE_FALLBACK_MULTIPLIER = 0.35;
9
+ export const GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT = 35;
10
+ export const MAX_GAS_BLOCK_PERCENT = 90;
10
11
  export async function updateGas(request) {
11
12
  const { txMeta } = request;
12
13
  const initialParams = { ...txMeta.txParams };
@@ -24,16 +25,18 @@ export async function updateGas(request) {
24
25
  export async function estimateGas(txParams, ethQuery) {
25
26
  const request = { ...txParams };
26
27
  const { data, value } = request;
27
- const { gasLimit: gasLimitHex, number: blockNumber } = await getLatestBlock(ethQuery);
28
- const gasLimitBN = hexToBN(gasLimitHex);
28
+ const { gasLimit: blockGasLimit, number: blockNumber } = await getLatestBlock(ethQuery);
29
+ const blockGasLimitBN = hexToBN(blockGasLimit);
30
+ const fallback = BNToHex(fractionBN(blockGasLimitBN, GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT, 100));
29
31
  request.data = data ? add0x(data) : data;
30
- request.gas = BNToHex(gasLimitBN.muln(GAS_ESTIMATE_FALLBACK_MULTIPLIER));
31
32
  request.value = value || '0x0';
32
- let estimatedGas = request.gas;
33
+ delete request.gasPrice;
34
+ delete request.maxFeePerGas;
35
+ delete request.maxPriorityFeePerGas;
36
+ let estimatedGas = fallback;
33
37
  let simulationFails;
34
38
  try {
35
39
  estimatedGas = await query(ethQuery, 'estimateGas', [request]);
36
- // TODO: Replace `any` with type
37
40
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
41
  }
39
42
  catch (error) {
@@ -42,21 +45,21 @@ export async function estimateGas(txParams, ethQuery) {
42
45
  errorKey: error.errorKey,
43
46
  debug: {
44
47
  blockNumber,
45
- blockGasLimit: gasLimitHex,
48
+ blockGasLimit,
46
49
  },
47
50
  };
48
- log('Estimation failed', { ...simulationFails, fallback: estimateGas });
51
+ log('Estimation failed', { ...simulationFails, fallback });
49
52
  }
50
53
  return {
51
- blockGasLimit: gasLimitHex,
54
+ blockGasLimit,
52
55
  estimatedGas,
53
56
  simulationFails,
54
57
  };
55
58
  }
56
59
  export function addGasBuffer(estimatedGas, blockGasLimit, multiplier) {
57
60
  const estimatedGasBN = hexToBN(estimatedGas);
58
- const maxGasBN = hexToBN(blockGasLimit).muln(0.9);
59
- const paddedGasBN = estimatedGasBN.muln(multiplier);
61
+ const maxGasBN = fractionBN(hexToBN(blockGasLimit), MAX_GAS_BLOCK_PERCENT, 100);
62
+ const paddedGasBN = fractionBN(estimatedGasBN, multiplier * 100, 100);
60
63
  if (estimatedGasBN.gt(maxGasBN)) {
61
64
  const estimatedGasHex = add0x(estimatedGas);
62
65
  log('Using estimated value', estimatedGasHex);
@@ -1 +1 @@
1
- {"version":3,"file":"gas.mjs","sourceRoot":"","sources":["../../src/utils/gas.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable jsdoc/require-jsdoc */\n\nimport { BNToHex, hexToBN, query } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport type { Hex } from '@metamask/utils';\nimport { add0x, createModuleLogger } from '@metamask/utils';\n\nimport { GAS_BUFFER_CHAIN_OVERRIDES } from '../constants';\nimport { projectLogger } from '../logger';\nimport type { TransactionMeta, TransactionParams } from '../types';\n\nexport type UpdateGasRequest = {\n ethQuery: EthQuery;\n isCustomNetwork: boolean;\n chainId: Hex;\n txMeta: TransactionMeta;\n};\n\nexport const log = createModuleLogger(projectLogger, 'gas');\n\nexport const FIXED_GAS = '0x5208';\nexport const DEFAULT_GAS_MULTIPLIER = 1.5;\nexport const GAS_ESTIMATE_FALLBACK_MULTIPLIER = 0.35;\n\nexport async function updateGas(request: UpdateGasRequest) {\n const { txMeta } = request;\n const initialParams = { ...txMeta.txParams };\n\n const [gas, simulationFails] = await getGas(request);\n\n txMeta.txParams.gas = gas;\n txMeta.simulationFails = simulationFails;\n\n if (!initialParams.gas) {\n txMeta.originalGasEstimate = txMeta.txParams.gas;\n }\n\n if (!txMeta.defaultGasEstimates) {\n txMeta.defaultGasEstimates = {};\n }\n\n txMeta.defaultGasEstimates.gas = txMeta.txParams.gas;\n}\n\nexport async function estimateGas(\n txParams: TransactionParams,\n ethQuery: EthQuery,\n) {\n const request = { ...txParams };\n const { data, value } = request;\n\n const { gasLimit: gasLimitHex, number: blockNumber } = await getLatestBlock(\n ethQuery,\n );\n\n const gasLimitBN = hexToBN(gasLimitHex);\n\n request.data = data ? add0x(data) : data;\n request.gas = BNToHex(gasLimitBN.muln(GAS_ESTIMATE_FALLBACK_MULTIPLIER));\n request.value = value || '0x0';\n\n let estimatedGas = request.gas;\n let simulationFails;\n\n try {\n estimatedGas = await query(ethQuery, 'estimateGas', [request]);\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n simulationFails = {\n reason: error.message,\n errorKey: error.errorKey,\n debug: {\n blockNumber,\n blockGasLimit: gasLimitHex,\n },\n };\n\n log('Estimation failed', { ...simulationFails, fallback: estimateGas });\n }\n\n return {\n blockGasLimit: gasLimitHex,\n estimatedGas,\n simulationFails,\n };\n}\n\nexport function addGasBuffer(\n estimatedGas: string,\n blockGasLimit: string,\n multiplier: number,\n) {\n const estimatedGasBN = hexToBN(estimatedGas);\n const maxGasBN = hexToBN(blockGasLimit).muln(0.9);\n const paddedGasBN = estimatedGasBN.muln(multiplier);\n\n if (estimatedGasBN.gt(maxGasBN)) {\n const estimatedGasHex = add0x(estimatedGas);\n log('Using estimated value', estimatedGasHex);\n return estimatedGasHex;\n }\n\n if (paddedGasBN.lt(maxGasBN)) {\n const paddedHex = add0x(BNToHex(paddedGasBN));\n log('Using padded estimate', paddedHex, multiplier);\n return paddedHex;\n }\n\n const maxHex = add0x(BNToHex(maxGasBN));\n log('Using 90% of block gas limit', maxHex);\n return maxHex;\n}\n\nasync function getGas(\n request: UpdateGasRequest,\n): Promise<[string, TransactionMeta['simulationFails']?]> {\n const { isCustomNetwork, chainId, txMeta } = request;\n\n if (txMeta.txParams.gas) {\n log('Using value from request', txMeta.txParams.gas);\n return [txMeta.txParams.gas];\n }\n\n if (await requiresFixedGas(request)) {\n log('Using fixed value', FIXED_GAS);\n return [FIXED_GAS];\n }\n\n const { blockGasLimit, estimatedGas, simulationFails } = await estimateGas(\n txMeta.txParams,\n request.ethQuery,\n );\n\n if (isCustomNetwork || simulationFails) {\n log(\n isCustomNetwork\n ? 'Using original estimate as custom network'\n : 'Using original fallback estimate as simulation failed',\n );\n return [estimatedGas, simulationFails];\n }\n\n const bufferMultiplier =\n GAS_BUFFER_CHAIN_OVERRIDES[\n chainId as keyof typeof GAS_BUFFER_CHAIN_OVERRIDES\n ] ?? DEFAULT_GAS_MULTIPLIER;\n\n const bufferedGas = addGasBuffer(\n estimatedGas,\n blockGasLimit,\n bufferMultiplier,\n );\n\n return [bufferedGas, simulationFails];\n}\n\nasync function requiresFixedGas({\n ethQuery,\n txMeta,\n isCustomNetwork,\n}: UpdateGasRequest): Promise<boolean> {\n const {\n txParams: { to, data },\n } = txMeta;\n\n if (isCustomNetwork || !to || data) {\n return false;\n }\n\n const code = await getCode(ethQuery, to);\n\n return !code || code === '0x';\n}\n\nasync function getCode(\n ethQuery: EthQuery,\n address: string,\n): Promise<string | undefined> {\n return await query(ethQuery, 'getCode', [address]);\n}\n\nasync function getLatestBlock(\n ethQuery: EthQuery,\n): Promise<{ gasLimit: string; number: string }> {\n return await query(ethQuery, 'getBlockByNumber', ['latest', false]);\n}\n"]}
1
+ {"version":3,"file":"gas.mjs","sourceRoot":"","sources":["../../src/utils/gas.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable jsdoc/require-jsdoc */\n\nimport {\n BNToHex,\n fractionBN,\n hexToBN,\n query,\n} from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport type { Hex } from '@metamask/utils';\nimport { add0x, createModuleLogger } from '@metamask/utils';\n\nimport { GAS_BUFFER_CHAIN_OVERRIDES } from '../constants';\nimport { projectLogger } from '../logger';\nimport type { TransactionMeta, TransactionParams } from '../types';\n\nexport type UpdateGasRequest = {\n ethQuery: EthQuery;\n isCustomNetwork: boolean;\n chainId: Hex;\n txMeta: TransactionMeta;\n};\n\nexport const log = createModuleLogger(projectLogger, 'gas');\n\nexport const FIXED_GAS = '0x5208';\nexport const DEFAULT_GAS_MULTIPLIER = 1.5;\nexport const GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT = 35;\nexport const MAX_GAS_BLOCK_PERCENT = 90;\n\nexport async function updateGas(request: UpdateGasRequest) {\n const { txMeta } = request;\n const initialParams = { ...txMeta.txParams };\n\n const [gas, simulationFails] = await getGas(request);\n\n txMeta.txParams.gas = gas;\n txMeta.simulationFails = simulationFails;\n\n if (!initialParams.gas) {\n txMeta.originalGasEstimate = txMeta.txParams.gas;\n }\n\n if (!txMeta.defaultGasEstimates) {\n txMeta.defaultGasEstimates = {};\n }\n\n txMeta.defaultGasEstimates.gas = txMeta.txParams.gas;\n}\n\nexport async function estimateGas(\n txParams: TransactionParams,\n ethQuery: EthQuery,\n) {\n const request = { ...txParams };\n const { data, value } = request;\n\n const { gasLimit: blockGasLimit, number: blockNumber } = await getLatestBlock(\n ethQuery,\n );\n\n const blockGasLimitBN = hexToBN(blockGasLimit);\n\n const fallback = BNToHex(\n fractionBN(blockGasLimitBN, GAS_ESTIMATE_FALLBACK_BLOCK_PERCENT, 100),\n );\n\n request.data = data ? add0x(data) : data;\n request.value = value || '0x0';\n\n delete request.gasPrice;\n delete request.maxFeePerGas;\n delete request.maxPriorityFeePerGas;\n\n let estimatedGas = fallback;\n let simulationFails: TransactionMeta['simulationFails'];\n\n try {\n estimatedGas = await query(ethQuery, 'estimateGas', [request]);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n simulationFails = {\n reason: error.message,\n errorKey: error.errorKey,\n debug: {\n blockNumber,\n blockGasLimit,\n },\n };\n\n log('Estimation failed', { ...simulationFails, fallback });\n }\n\n return {\n blockGasLimit,\n estimatedGas,\n simulationFails,\n };\n}\n\nexport function addGasBuffer(\n estimatedGas: string,\n blockGasLimit: string,\n multiplier: number,\n) {\n const estimatedGasBN = hexToBN(estimatedGas);\n\n const maxGasBN = fractionBN(\n hexToBN(blockGasLimit),\n MAX_GAS_BLOCK_PERCENT,\n 100,\n );\n\n const paddedGasBN = fractionBN(estimatedGasBN, multiplier * 100, 100);\n\n if (estimatedGasBN.gt(maxGasBN)) {\n const estimatedGasHex = add0x(estimatedGas);\n log('Using estimated value', estimatedGasHex);\n return estimatedGasHex;\n }\n\n if (paddedGasBN.lt(maxGasBN)) {\n const paddedHex = add0x(BNToHex(paddedGasBN));\n log('Using padded estimate', paddedHex, multiplier);\n return paddedHex;\n }\n\n const maxHex = add0x(BNToHex(maxGasBN));\n log('Using 90% of block gas limit', maxHex);\n return maxHex;\n}\n\nasync function getGas(\n request: UpdateGasRequest,\n): Promise<[string, TransactionMeta['simulationFails']?]> {\n const { isCustomNetwork, chainId, txMeta } = request;\n\n if (txMeta.txParams.gas) {\n log('Using value from request', txMeta.txParams.gas);\n return [txMeta.txParams.gas];\n }\n\n if (await requiresFixedGas(request)) {\n log('Using fixed value', FIXED_GAS);\n return [FIXED_GAS];\n }\n\n const { blockGasLimit, estimatedGas, simulationFails } = await estimateGas(\n txMeta.txParams,\n request.ethQuery,\n );\n\n if (isCustomNetwork || simulationFails) {\n log(\n isCustomNetwork\n ? 'Using original estimate as custom network'\n : 'Using original fallback estimate as simulation failed',\n );\n return [estimatedGas, simulationFails];\n }\n\n const bufferMultiplier =\n GAS_BUFFER_CHAIN_OVERRIDES[\n chainId as keyof typeof GAS_BUFFER_CHAIN_OVERRIDES\n ] ?? DEFAULT_GAS_MULTIPLIER;\n\n const bufferedGas = addGasBuffer(\n estimatedGas,\n blockGasLimit,\n bufferMultiplier,\n );\n\n return [bufferedGas, simulationFails];\n}\n\nasync function requiresFixedGas({\n ethQuery,\n txMeta,\n isCustomNetwork,\n}: UpdateGasRequest): Promise<boolean> {\n const {\n txParams: { to, data },\n } = txMeta;\n\n if (isCustomNetwork || !to || data) {\n return false;\n }\n\n const code = await getCode(ethQuery, to);\n\n return !code || code === '0x';\n}\n\nasync function getCode(\n ethQuery: EthQuery,\n address: string,\n): Promise<string | undefined> {\n return await query(ethQuery, 'getCode', [address]);\n}\n\nasync function getLatestBlock(\n ethQuery: EthQuery,\n): Promise<{ gasLimit: string; number: string }> {\n return await query(ethQuery, 'getBlockByNumber', ['latest', false]);\n}\n"]}
@@ -5,8 +5,9 @@ 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 rpc_errors_1 = require("@metamask/rpc-errors");
8
+ const utils_1 = require("@metamask/utils");
8
9
  const types_1 = require("../types.cjs");
9
- const utils_1 = require("./utils.cjs");
10
+ const utils_2 = require("./utils.cjs");
10
11
  /**
11
12
  * Validates whether a transaction initiated by a specific 'from' address is permitted by the origin.
12
13
  *
@@ -75,7 +76,7 @@ function validateEnvelopeType(type) {
75
76
  * @throws Throws invalid params if the transaction specifies EIP-1559 but the network does not support it.
76
77
  */
77
78
  function validateEIP1559Compatibility(txParams, isEIP1559Compatible) {
78
- if ((0, utils_1.isEIP1559Transaction)(txParams) && !isEIP1559Compatible) {
79
+ if ((0, utils_2.isEIP1559Transaction)(txParams) && !isEIP1559Compatible) {
79
80
  throw rpc_errors_1.rpcErrors.invalidParams('Invalid transaction params: params specify an EIP-1559 transaction but the current network does not support EIP-1559');
80
81
  }
81
82
  }
@@ -193,17 +194,17 @@ function validateGasFeeParams(txParams) {
193
194
  ensureProperTransactionEnvelopeTypeProvided(txParams, 'gasPrice');
194
195
  ensureMutuallyExclusiveFieldsNotProvided(txParams, 'gasPrice', 'maxFeePerGas');
195
196
  ensureMutuallyExclusiveFieldsNotProvided(txParams, 'gasPrice', 'maxPriorityFeePerGas');
196
- ensureFieldIsString(txParams, 'gasPrice');
197
+ ensureFieldIsValidHex(txParams, 'gasPrice');
197
198
  }
198
199
  if (txParams.maxFeePerGas) {
199
200
  ensureProperTransactionEnvelopeTypeProvided(txParams, 'maxFeePerGas');
200
201
  ensureMutuallyExclusiveFieldsNotProvided(txParams, 'maxFeePerGas', 'gasPrice');
201
- ensureFieldIsString(txParams, 'maxFeePerGas');
202
+ ensureFieldIsValidHex(txParams, 'maxFeePerGas');
202
203
  }
203
204
  if (txParams.maxPriorityFeePerGas) {
204
205
  ensureProperTransactionEnvelopeTypeProvided(txParams, 'maxPriorityFeePerGas');
205
206
  ensureMutuallyExclusiveFieldsNotProvided(txParams, 'maxPriorityFeePerGas', 'gasPrice');
206
- ensureFieldIsString(txParams, 'maxPriorityFeePerGas');
207
+ ensureFieldIsValidHex(txParams, 'maxPriorityFeePerGas');
207
208
  }
208
209
  }
209
210
  /**
@@ -249,19 +250,17 @@ function ensureMutuallyExclusiveFieldsNotProvided(txParams, fieldBeingValidated,
249
250
  }
250
251
  }
251
252
  /**
252
- * Ensures that the provided value for field is a string, throws an
253
- * invalidParams error if field is not a string.
253
+ * Ensures that the provided value for field is a valid hexadecimal.
254
+ * Throws an invalidParams error if field is not a valid hexadecimal.
254
255
  *
255
256
  * @param txParams - The transaction parameters object
256
257
  * @param field - The current field being validated
257
- * @throws {rpcErrors.invalidParams} Throws if field is not a string
258
+ * @throws {rpcErrors.invalidParams} Throws if field is not a valid hexadecimal
258
259
  */
259
- function ensureFieldIsString(txParams, field) {
260
- if (typeof txParams[field] !== 'string') {
261
- throw rpc_errors_1.rpcErrors.invalidParams(
262
- // TODO: Either fix this lint violation or explain why it's necessary to ignore.
263
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
264
- `Invalid transaction params: ${field} is not a string. got: (${txParams[field]})`);
260
+ function ensureFieldIsValidHex(txParams, field) {
261
+ const value = txParams[field];
262
+ if (typeof value !== 'string' || !(0, utils_1.isStrictHexString)(value)) {
263
+ throw rpc_errors_1.rpcErrors.invalidParams(`Invalid transaction params: ${field} is not a valid hexadecimal. got: (${String(value)})`);
265
264
  }
266
265
  }
267
266
  //# sourceMappingURL=validation.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.cjs","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { ORIGIN_METAMASK, isValidHexAddress } from '@metamask/controller-utils';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport { providerErrors, rpcErrors } from '@metamask/rpc-errors';\n\nimport { TransactionEnvelopeType, type TransactionParams } from '../types';\nimport { isEIP1559Transaction } from './utils';\n\ntype GasFieldsToValidate = 'gasPrice' | 'maxFeePerGas' | 'maxPriorityFeePerGas';\n\n/**\n * Validates whether a transaction initiated by a specific 'from' address is permitted by the origin.\n *\n * @param permittedAddresses - The permitted accounts for the given origin.\n * @param selectedAddress - The currently selected Ethereum address in the wallet.\n * @param from - The address from which the transaction is initiated.\n * @param origin - The origin or source of the transaction.\n * @throws Throws an error if the transaction is not permitted.\n */\nexport async function validateTransactionOrigin(\n permittedAddresses: string[],\n selectedAddress: string,\n from: string,\n origin: string,\n) {\n if (origin === ORIGIN_METAMASK) {\n // Ensure the 'from' address matches the currently selected address\n if (from !== selectedAddress) {\n throw rpcErrors.internal({\n message: `Internally initiated transaction is using invalid account.`,\n data: {\n origin,\n fromAddress: from,\n selectedAddress,\n },\n });\n }\n return;\n }\n\n // Check if the origin has permissions to initiate transactions from the specified address\n if (!permittedAddresses.includes(from)) {\n throw providerErrors.unauthorized({ data: { origin } });\n }\n}\n\n/**\n * Validates the transaction params for required properties and throws in\n * the event of any validation error.\n *\n * @param txParams - Transaction params object to validate.\n * @param isEIP1559Compatible - whether or not the current network supports EIP-1559 transactions.\n */\nexport function validateTxParams(\n txParams: TransactionParams,\n isEIP1559Compatible = true,\n) {\n validateEnvelopeType(txParams.type);\n validateEIP1559Compatibility(txParams, isEIP1559Compatible);\n validateParamFrom(txParams.from);\n validateParamRecipient(txParams);\n validateParamValue(txParams.value);\n validateParamData(txParams.data);\n validateParamChainId(txParams.chainId);\n validateGasFeeParams(txParams);\n}\n\n/**\n * Validates the `type` property, ensuring that if it is specified, it is a valid transaction envelope type.\n *\n * @param type - The transaction envelope type to validate.\n * @throws Throws invalid params if the type is not a valid transaction envelope type.\n */\nfunction validateEnvelopeType(type: string | undefined) {\n if (\n type &&\n !Object.values(TransactionEnvelopeType).includes(\n type as TransactionEnvelopeType,\n )\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: \"${type}\". Must be one of: ${Object.values(\n TransactionEnvelopeType,\n ).join(', ')}`,\n );\n }\n}\n\n/**\n * Validates EIP-1559 compatibility for transaction creation.\n *\n * @param txParams - The transaction parameters to validate.\n * @param isEIP1559Compatible - Indicates if the current network supports EIP-1559.\n * @throws Throws invalid params if the transaction specifies EIP-1559 but the network does not support it.\n */\nfunction validateEIP1559Compatibility(\n txParams: TransactionParams,\n isEIP1559Compatible: boolean,\n) {\n if (isEIP1559Transaction(txParams) && !isEIP1559Compatible) {\n throw rpcErrors.invalidParams(\n 'Invalid transaction params: params specify an EIP-1559 transaction but the current network does not support EIP-1559',\n );\n }\n}\n\n/**\n * Validates value property, ensuring it is a valid positive integer number\n * denominated in wei.\n *\n * @param value - The value to validate, expressed as a string.\n * @throws Throws an error if the value is not a valid positive integer\n * number denominated in wei.\n * - If the value contains a hyphen (-), it is considered invalid.\n * - If the value contains a decimal point (.), it is considered invalid.\n * - If the value is not a finite number, is NaN, or is not a safe integer, it is considered invalid.\n */\nfunction validateParamValue(value?: string) {\n if (value !== undefined) {\n if (value.includes('-')) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value \"${value}\": not a positive number.`,\n );\n }\n\n if (value.includes('.')) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value \"${value}\": number must be in wei.`,\n );\n }\n const intValue = parseInt(value, 10);\n const isValid =\n Number.isFinite(intValue) &&\n !Number.isNaN(intValue) &&\n !isNaN(Number(value)) &&\n Number.isSafeInteger(intValue);\n if (!isValid) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value ${value}: number must be a valid number.`,\n );\n }\n }\n}\n\n/**\n * Validates the recipient address in a transaction's parameters.\n *\n * @param txParams - The transaction parameters object to validate.\n * @throws Throws an error if the recipient address is invalid:\n * - If the recipient address is an empty string ('0x') or undefined and the transaction contains data,\n * the \"to\" field is removed from the transaction parameters.\n * - If the recipient address is not a valid hexadecimal Ethereum address, an error is thrown.\n */\nfunction validateParamRecipient(txParams: TransactionParams) {\n if (txParams.to === '0x' || txParams.to === undefined) {\n if (txParams.data) {\n delete txParams.to;\n } else {\n throw rpcErrors.invalidParams(`Invalid \"to\" address.`);\n }\n } else if (txParams.to !== undefined && !isValidHexAddress(txParams.to)) {\n throw rpcErrors.invalidParams(`Invalid \"to\" address.`);\n }\n}\n\n/**\n * Validates the recipient address in a transaction's parameters.\n *\n * @param from - The from property to validate.\n * @throws Throws an error if the recipient address is invalid:\n * - If the recipient address is an empty string ('0x') or undefined and the transaction contains data,\n * the \"to\" field is removed from the transaction parameters.\n * - If the recipient address is not a valid hexadecimal Ethereum address, an error is thrown.\n */\nfunction validateParamFrom(from: string) {\n if (!from || typeof from !== 'string') {\n throw rpcErrors.invalidParams(\n `Invalid \"from\" address ${from}: not a string.`,\n );\n }\n if (!isValidHexAddress(from)) {\n throw rpcErrors.invalidParams('Invalid \"from\" address.');\n }\n}\n\n/**\n * Validates input data for transactions.\n *\n * @param value - The input data to validate.\n * @throws Throws invalid params if the input data is invalid.\n */\nfunction validateParamData(value?: string) {\n if (value) {\n const ERC20Interface = new Interface(abiERC20);\n try {\n ERC20Interface.parseTransaction({ data: value });\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.message.match(/BUFFER_OVERRUN/u)) {\n throw rpcErrors.invalidParams(\n 'Invalid transaction params: data out-of-bounds, BUFFER_OVERRUN.',\n );\n }\n }\n }\n}\n\n/**\n * Validates chainId type.\n *\n * @param chainId - The chainId to validate.\n */\nfunction validateParamChainId(chainId: number | string | undefined) {\n if (\n chainId !== undefined &&\n typeof chainId !== 'number' &&\n typeof chainId !== 'string'\n ) {\n throw rpcErrors.invalidParams(\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `Invalid transaction params: chainId is not a Number or hex string. got: (${chainId})`,\n );\n }\n}\n\n/**\n * Validates gas values.\n *\n * @param txParams - The transaction parameters to validate.\n */\nfunction validateGasFeeParams(txParams: TransactionParams) {\n if (txParams.gasPrice) {\n ensureProperTransactionEnvelopeTypeProvided(txParams, 'gasPrice');\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'gasPrice',\n 'maxFeePerGas',\n );\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'gasPrice',\n 'maxPriorityFeePerGas',\n );\n ensureFieldIsString(txParams, 'gasPrice');\n }\n\n if (txParams.maxFeePerGas) {\n ensureProperTransactionEnvelopeTypeProvided(txParams, 'maxFeePerGas');\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'maxFeePerGas',\n 'gasPrice',\n );\n ensureFieldIsString(txParams, 'maxFeePerGas');\n }\n\n if (txParams.maxPriorityFeePerGas) {\n ensureProperTransactionEnvelopeTypeProvided(\n txParams,\n 'maxPriorityFeePerGas',\n );\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'maxPriorityFeePerGas',\n 'gasPrice',\n );\n ensureFieldIsString(txParams, 'maxPriorityFeePerGas');\n }\n}\n\n/**\n * Ensures that the provided txParams has the proper 'type' specified for the\n * given field, if it is provided. If types do not match throws an\n * invalidParams error.\n *\n * @param txParams - The transaction parameters object\n * @param field - The current field being validated\n * @throws {ethErrors.rpc.invalidParams} Throws if type does not match the\n * expectations for provided field.\n */\nfunction ensureProperTransactionEnvelopeTypeProvided(\n txParams: TransactionParams,\n field: GasFieldsToValidate,\n) {\n switch (field) {\n case 'maxFeePerGas':\n case 'maxPriorityFeePerGas':\n if (\n txParams.type &&\n txParams.type !== TransactionEnvelopeType.feeMarket\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: specified type \"${txParams.type}\" but including maxFeePerGas and maxPriorityFeePerGas requires type: \"${TransactionEnvelopeType.feeMarket}\"`,\n );\n }\n break;\n case 'gasPrice':\n default:\n if (\n txParams.type &&\n txParams.type === TransactionEnvelopeType.feeMarket\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: specified type \"${txParams.type}\" but included a gasPrice instead of maxFeePerGas and maxPriorityFeePerGas`,\n );\n }\n }\n}\n\n/**\n * Given two fields, ensure that the second field is not included in txParams,\n * and if it is throw an invalidParams error.\n *\n * @param txParams - The transaction parameters object\n * @param fieldBeingValidated - The current field being validated\n * @param mutuallyExclusiveField - The field to ensure is not provided\n * @throws {ethErrors.rpc.invalidParams} Throws if mutuallyExclusiveField is\n * present in txParams.\n */\nfunction ensureMutuallyExclusiveFieldsNotProvided(\n txParams: TransactionParams,\n fieldBeingValidated: GasFieldsToValidate,\n mutuallyExclusiveField: GasFieldsToValidate,\n) {\n if (typeof txParams[mutuallyExclusiveField] !== 'undefined') {\n throw rpcErrors.invalidParams(\n `Invalid transaction params: specified ${fieldBeingValidated} but also included ${mutuallyExclusiveField}, these cannot be mixed`,\n );\n }\n}\n\n/**\n * Ensures that the provided value for field is a string, throws an\n * invalidParams error if field is not a string.\n *\n * @param txParams - The transaction parameters object\n * @param field - The current field being validated\n * @throws {rpcErrors.invalidParams} Throws if field is not a string\n */\nfunction ensureFieldIsString(\n txParams: TransactionParams,\n field: GasFieldsToValidate,\n) {\n if (typeof txParams[field] !== 'string') {\n throw rpcErrors.invalidParams(\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `Invalid transaction params: ${field} is not a string. got: (${txParams[field]})`,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"validation.cjs","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { ORIGIN_METAMASK, isValidHexAddress } from '@metamask/controller-utils';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport { providerErrors, rpcErrors } from '@metamask/rpc-errors';\nimport { isStrictHexString } from '@metamask/utils';\n\nimport { TransactionEnvelopeType, type TransactionParams } from '../types';\nimport { isEIP1559Transaction } from './utils';\n\ntype GasFieldsToValidate = 'gasPrice' | 'maxFeePerGas' | 'maxPriorityFeePerGas';\n\n/**\n * Validates whether a transaction initiated by a specific 'from' address is permitted by the origin.\n *\n * @param permittedAddresses - The permitted accounts for the given origin.\n * @param selectedAddress - The currently selected Ethereum address in the wallet.\n * @param from - The address from which the transaction is initiated.\n * @param origin - The origin or source of the transaction.\n * @throws Throws an error if the transaction is not permitted.\n */\nexport async function validateTransactionOrigin(\n permittedAddresses: string[],\n selectedAddress: string,\n from: string,\n origin: string,\n) {\n if (origin === ORIGIN_METAMASK) {\n // Ensure the 'from' address matches the currently selected address\n if (from !== selectedAddress) {\n throw rpcErrors.internal({\n message: `Internally initiated transaction is using invalid account.`,\n data: {\n origin,\n fromAddress: from,\n selectedAddress,\n },\n });\n }\n return;\n }\n\n // Check if the origin has permissions to initiate transactions from the specified address\n if (!permittedAddresses.includes(from)) {\n throw providerErrors.unauthorized({ data: { origin } });\n }\n}\n\n/**\n * Validates the transaction params for required properties and throws in\n * the event of any validation error.\n *\n * @param txParams - Transaction params object to validate.\n * @param isEIP1559Compatible - whether or not the current network supports EIP-1559 transactions.\n */\nexport function validateTxParams(\n txParams: TransactionParams,\n isEIP1559Compatible = true,\n) {\n validateEnvelopeType(txParams.type);\n validateEIP1559Compatibility(txParams, isEIP1559Compatible);\n validateParamFrom(txParams.from);\n validateParamRecipient(txParams);\n validateParamValue(txParams.value);\n validateParamData(txParams.data);\n validateParamChainId(txParams.chainId);\n validateGasFeeParams(txParams);\n}\n\n/**\n * Validates the `type` property, ensuring that if it is specified, it is a valid transaction envelope type.\n *\n * @param type - The transaction envelope type to validate.\n * @throws Throws invalid params if the type is not a valid transaction envelope type.\n */\nfunction validateEnvelopeType(type: string | undefined) {\n if (\n type &&\n !Object.values(TransactionEnvelopeType).includes(\n type as TransactionEnvelopeType,\n )\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: \"${type}\". Must be one of: ${Object.values(\n TransactionEnvelopeType,\n ).join(', ')}`,\n );\n }\n}\n\n/**\n * Validates EIP-1559 compatibility for transaction creation.\n *\n * @param txParams - The transaction parameters to validate.\n * @param isEIP1559Compatible - Indicates if the current network supports EIP-1559.\n * @throws Throws invalid params if the transaction specifies EIP-1559 but the network does not support it.\n */\nfunction validateEIP1559Compatibility(\n txParams: TransactionParams,\n isEIP1559Compatible: boolean,\n) {\n if (isEIP1559Transaction(txParams) && !isEIP1559Compatible) {\n throw rpcErrors.invalidParams(\n 'Invalid transaction params: params specify an EIP-1559 transaction but the current network does not support EIP-1559',\n );\n }\n}\n\n/**\n * Validates value property, ensuring it is a valid positive integer number\n * denominated in wei.\n *\n * @param value - The value to validate, expressed as a string.\n * @throws Throws an error if the value is not a valid positive integer\n * number denominated in wei.\n * - If the value contains a hyphen (-), it is considered invalid.\n * - If the value contains a decimal point (.), it is considered invalid.\n * - If the value is not a finite number, is NaN, or is not a safe integer, it is considered invalid.\n */\nfunction validateParamValue(value?: string) {\n if (value !== undefined) {\n if (value.includes('-')) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value \"${value}\": not a positive number.`,\n );\n }\n\n if (value.includes('.')) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value \"${value}\": number must be in wei.`,\n );\n }\n const intValue = parseInt(value, 10);\n const isValid =\n Number.isFinite(intValue) &&\n !Number.isNaN(intValue) &&\n !isNaN(Number(value)) &&\n Number.isSafeInteger(intValue);\n if (!isValid) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value ${value}: number must be a valid number.`,\n );\n }\n }\n}\n\n/**\n * Validates the recipient address in a transaction's parameters.\n *\n * @param txParams - The transaction parameters object to validate.\n * @throws Throws an error if the recipient address is invalid:\n * - If the recipient address is an empty string ('0x') or undefined and the transaction contains data,\n * the \"to\" field is removed from the transaction parameters.\n * - If the recipient address is not a valid hexadecimal Ethereum address, an error is thrown.\n */\nfunction validateParamRecipient(txParams: TransactionParams) {\n if (txParams.to === '0x' || txParams.to === undefined) {\n if (txParams.data) {\n delete txParams.to;\n } else {\n throw rpcErrors.invalidParams(`Invalid \"to\" address.`);\n }\n } else if (txParams.to !== undefined && !isValidHexAddress(txParams.to)) {\n throw rpcErrors.invalidParams(`Invalid \"to\" address.`);\n }\n}\n\n/**\n * Validates the recipient address in a transaction's parameters.\n *\n * @param from - The from property to validate.\n * @throws Throws an error if the recipient address is invalid:\n * - If the recipient address is an empty string ('0x') or undefined and the transaction contains data,\n * the \"to\" field is removed from the transaction parameters.\n * - If the recipient address is not a valid hexadecimal Ethereum address, an error is thrown.\n */\nfunction validateParamFrom(from: string) {\n if (!from || typeof from !== 'string') {\n throw rpcErrors.invalidParams(\n `Invalid \"from\" address ${from}: not a string.`,\n );\n }\n if (!isValidHexAddress(from)) {\n throw rpcErrors.invalidParams('Invalid \"from\" address.');\n }\n}\n\n/**\n * Validates input data for transactions.\n *\n * @param value - The input data to validate.\n * @throws Throws invalid params if the input data is invalid.\n */\nfunction validateParamData(value?: string) {\n if (value) {\n const ERC20Interface = new Interface(abiERC20);\n try {\n ERC20Interface.parseTransaction({ data: value });\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.message.match(/BUFFER_OVERRUN/u)) {\n throw rpcErrors.invalidParams(\n 'Invalid transaction params: data out-of-bounds, BUFFER_OVERRUN.',\n );\n }\n }\n }\n}\n\n/**\n * Validates chainId type.\n *\n * @param chainId - The chainId to validate.\n */\nfunction validateParamChainId(chainId: number | string | undefined) {\n if (\n chainId !== undefined &&\n typeof chainId !== 'number' &&\n typeof chainId !== 'string'\n ) {\n throw rpcErrors.invalidParams(\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `Invalid transaction params: chainId is not a Number or hex string. got: (${chainId})`,\n );\n }\n}\n\n/**\n * Validates gas values.\n *\n * @param txParams - The transaction parameters to validate.\n */\nfunction validateGasFeeParams(txParams: TransactionParams) {\n if (txParams.gasPrice) {\n ensureProperTransactionEnvelopeTypeProvided(txParams, 'gasPrice');\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'gasPrice',\n 'maxFeePerGas',\n );\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'gasPrice',\n 'maxPriorityFeePerGas',\n );\n ensureFieldIsValidHex(txParams, 'gasPrice');\n }\n\n if (txParams.maxFeePerGas) {\n ensureProperTransactionEnvelopeTypeProvided(txParams, 'maxFeePerGas');\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'maxFeePerGas',\n 'gasPrice',\n );\n ensureFieldIsValidHex(txParams, 'maxFeePerGas');\n }\n\n if (txParams.maxPriorityFeePerGas) {\n ensureProperTransactionEnvelopeTypeProvided(\n txParams,\n 'maxPriorityFeePerGas',\n );\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'maxPriorityFeePerGas',\n 'gasPrice',\n );\n ensureFieldIsValidHex(txParams, 'maxPriorityFeePerGas');\n }\n}\n\n/**\n * Ensures that the provided txParams has the proper 'type' specified for the\n * given field, if it is provided. If types do not match throws an\n * invalidParams error.\n *\n * @param txParams - The transaction parameters object\n * @param field - The current field being validated\n * @throws {ethErrors.rpc.invalidParams} Throws if type does not match the\n * expectations for provided field.\n */\nfunction ensureProperTransactionEnvelopeTypeProvided(\n txParams: TransactionParams,\n field: GasFieldsToValidate,\n) {\n switch (field) {\n case 'maxFeePerGas':\n case 'maxPriorityFeePerGas':\n if (\n txParams.type &&\n txParams.type !== TransactionEnvelopeType.feeMarket\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: specified type \"${txParams.type}\" but including maxFeePerGas and maxPriorityFeePerGas requires type: \"${TransactionEnvelopeType.feeMarket}\"`,\n );\n }\n break;\n case 'gasPrice':\n default:\n if (\n txParams.type &&\n txParams.type === TransactionEnvelopeType.feeMarket\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: specified type \"${txParams.type}\" but included a gasPrice instead of maxFeePerGas and maxPriorityFeePerGas`,\n );\n }\n }\n}\n\n/**\n * Given two fields, ensure that the second field is not included in txParams,\n * and if it is throw an invalidParams error.\n *\n * @param txParams - The transaction parameters object\n * @param fieldBeingValidated - The current field being validated\n * @param mutuallyExclusiveField - The field to ensure is not provided\n * @throws {ethErrors.rpc.invalidParams} Throws if mutuallyExclusiveField is\n * present in txParams.\n */\nfunction ensureMutuallyExclusiveFieldsNotProvided(\n txParams: TransactionParams,\n fieldBeingValidated: GasFieldsToValidate,\n mutuallyExclusiveField: GasFieldsToValidate,\n) {\n if (typeof txParams[mutuallyExclusiveField] !== 'undefined') {\n throw rpcErrors.invalidParams(\n `Invalid transaction params: specified ${fieldBeingValidated} but also included ${mutuallyExclusiveField}, these cannot be mixed`,\n );\n }\n}\n\n/**\n * Ensures that the provided value for field is a valid hexadecimal.\n * Throws an invalidParams error if field is not a valid hexadecimal.\n *\n * @param txParams - The transaction parameters object\n * @param field - The current field being validated\n * @throws {rpcErrors.invalidParams} Throws if field is not a valid hexadecimal\n */\nfunction ensureFieldIsValidHex(\n txParams: TransactionParams,\n field: GasFieldsToValidate,\n) {\n const value = txParams[field];\n if (typeof value !== 'string' || !isStrictHexString(value)) {\n throw rpcErrors.invalidParams(\n `Invalid transaction params: ${field} is not a valid hexadecimal. got: (${String(\n value,\n )})`,\n );\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.cts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAKA,OAAO,EAA2B,KAAK,iBAAiB,EAAE,qBAAiB;AAK3E;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,kBAAkB,EAAE,MAAM,EAAE,EAC5B,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,iBAqBf;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,iBAAiB,EAC3B,mBAAmB,UAAO,QAU3B"}
1
+ {"version":3,"file":"validation.d.cts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAMA,OAAO,EAA2B,KAAK,iBAAiB,EAAE,qBAAiB;AAK3E;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,kBAAkB,EAAE,MAAM,EAAE,EAC5B,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,iBAqBf;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,iBAAiB,EAC3B,mBAAmB,UAAO,QAU3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.mts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAKA,OAAO,EAA2B,KAAK,iBAAiB,EAAE,qBAAiB;AAK3E;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,kBAAkB,EAAE,MAAM,EAAE,EAC5B,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,iBAqBf;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,iBAAiB,EAC3B,mBAAmB,UAAO,QAU3B"}
1
+ {"version":3,"file":"validation.d.mts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAMA,OAAO,EAA2B,KAAK,iBAAiB,EAAE,qBAAiB;AAK3E;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,kBAAkB,EAAE,MAAM,EAAE,EAC5B,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,iBAqBf;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,iBAAiB,EAC3B,mBAAmB,UAAO,QAU3B"}
@@ -2,6 +2,7 @@ import { Interface } from "@ethersproject/abi";
2
2
  import { ORIGIN_METAMASK, isValidHexAddress } from "@metamask/controller-utils";
3
3
  import { abiERC20 } from "@metamask/metamask-eth-abis";
4
4
  import { providerErrors, rpcErrors } from "@metamask/rpc-errors";
5
+ import { isStrictHexString } from "@metamask/utils";
5
6
  import { TransactionEnvelopeType } from "../types.mjs";
6
7
  import { isEIP1559Transaction } from "./utils.mjs";
7
8
  /**
@@ -188,17 +189,17 @@ function validateGasFeeParams(txParams) {
188
189
  ensureProperTransactionEnvelopeTypeProvided(txParams, 'gasPrice');
189
190
  ensureMutuallyExclusiveFieldsNotProvided(txParams, 'gasPrice', 'maxFeePerGas');
190
191
  ensureMutuallyExclusiveFieldsNotProvided(txParams, 'gasPrice', 'maxPriorityFeePerGas');
191
- ensureFieldIsString(txParams, 'gasPrice');
192
+ ensureFieldIsValidHex(txParams, 'gasPrice');
192
193
  }
193
194
  if (txParams.maxFeePerGas) {
194
195
  ensureProperTransactionEnvelopeTypeProvided(txParams, 'maxFeePerGas');
195
196
  ensureMutuallyExclusiveFieldsNotProvided(txParams, 'maxFeePerGas', 'gasPrice');
196
- ensureFieldIsString(txParams, 'maxFeePerGas');
197
+ ensureFieldIsValidHex(txParams, 'maxFeePerGas');
197
198
  }
198
199
  if (txParams.maxPriorityFeePerGas) {
199
200
  ensureProperTransactionEnvelopeTypeProvided(txParams, 'maxPriorityFeePerGas');
200
201
  ensureMutuallyExclusiveFieldsNotProvided(txParams, 'maxPriorityFeePerGas', 'gasPrice');
201
- ensureFieldIsString(txParams, 'maxPriorityFeePerGas');
202
+ ensureFieldIsValidHex(txParams, 'maxPriorityFeePerGas');
202
203
  }
203
204
  }
204
205
  /**
@@ -244,19 +245,17 @@ function ensureMutuallyExclusiveFieldsNotProvided(txParams, fieldBeingValidated,
244
245
  }
245
246
  }
246
247
  /**
247
- * Ensures that the provided value for field is a string, throws an
248
- * invalidParams error if field is not a string.
248
+ * Ensures that the provided value for field is a valid hexadecimal.
249
+ * Throws an invalidParams error if field is not a valid hexadecimal.
249
250
  *
250
251
  * @param txParams - The transaction parameters object
251
252
  * @param field - The current field being validated
252
- * @throws {rpcErrors.invalidParams} Throws if field is not a string
253
+ * @throws {rpcErrors.invalidParams} Throws if field is not a valid hexadecimal
253
254
  */
254
- function ensureFieldIsString(txParams, field) {
255
- if (typeof txParams[field] !== 'string') {
256
- throw rpcErrors.invalidParams(
257
- // TODO: Either fix this lint violation or explain why it's necessary to ignore.
258
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
259
- `Invalid transaction params: ${field} is not a string. got: (${txParams[field]})`);
255
+ function ensureFieldIsValidHex(txParams, field) {
256
+ const value = txParams[field];
257
+ if (typeof value !== 'string' || !isStrictHexString(value)) {
258
+ throw rpcErrors.invalidParams(`Invalid transaction params: ${field} is not a valid hexadecimal. got: (${String(value)})`);
260
259
  }
261
260
  }
262
261
  //# sourceMappingURL=validation.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.mjs","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { ORIGIN_METAMASK, isValidHexAddress } from '@metamask/controller-utils';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport { providerErrors, rpcErrors } from '@metamask/rpc-errors';\n\nimport { TransactionEnvelopeType, type TransactionParams } from '../types';\nimport { isEIP1559Transaction } from './utils';\n\ntype GasFieldsToValidate = 'gasPrice' | 'maxFeePerGas' | 'maxPriorityFeePerGas';\n\n/**\n * Validates whether a transaction initiated by a specific 'from' address is permitted by the origin.\n *\n * @param permittedAddresses - The permitted accounts for the given origin.\n * @param selectedAddress - The currently selected Ethereum address in the wallet.\n * @param from - The address from which the transaction is initiated.\n * @param origin - The origin or source of the transaction.\n * @throws Throws an error if the transaction is not permitted.\n */\nexport async function validateTransactionOrigin(\n permittedAddresses: string[],\n selectedAddress: string,\n from: string,\n origin: string,\n) {\n if (origin === ORIGIN_METAMASK) {\n // Ensure the 'from' address matches the currently selected address\n if (from !== selectedAddress) {\n throw rpcErrors.internal({\n message: `Internally initiated transaction is using invalid account.`,\n data: {\n origin,\n fromAddress: from,\n selectedAddress,\n },\n });\n }\n return;\n }\n\n // Check if the origin has permissions to initiate transactions from the specified address\n if (!permittedAddresses.includes(from)) {\n throw providerErrors.unauthorized({ data: { origin } });\n }\n}\n\n/**\n * Validates the transaction params for required properties and throws in\n * the event of any validation error.\n *\n * @param txParams - Transaction params object to validate.\n * @param isEIP1559Compatible - whether or not the current network supports EIP-1559 transactions.\n */\nexport function validateTxParams(\n txParams: TransactionParams,\n isEIP1559Compatible = true,\n) {\n validateEnvelopeType(txParams.type);\n validateEIP1559Compatibility(txParams, isEIP1559Compatible);\n validateParamFrom(txParams.from);\n validateParamRecipient(txParams);\n validateParamValue(txParams.value);\n validateParamData(txParams.data);\n validateParamChainId(txParams.chainId);\n validateGasFeeParams(txParams);\n}\n\n/**\n * Validates the `type` property, ensuring that if it is specified, it is a valid transaction envelope type.\n *\n * @param type - The transaction envelope type to validate.\n * @throws Throws invalid params if the type is not a valid transaction envelope type.\n */\nfunction validateEnvelopeType(type: string | undefined) {\n if (\n type &&\n !Object.values(TransactionEnvelopeType).includes(\n type as TransactionEnvelopeType,\n )\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: \"${type}\". Must be one of: ${Object.values(\n TransactionEnvelopeType,\n ).join(', ')}`,\n );\n }\n}\n\n/**\n * Validates EIP-1559 compatibility for transaction creation.\n *\n * @param txParams - The transaction parameters to validate.\n * @param isEIP1559Compatible - Indicates if the current network supports EIP-1559.\n * @throws Throws invalid params if the transaction specifies EIP-1559 but the network does not support it.\n */\nfunction validateEIP1559Compatibility(\n txParams: TransactionParams,\n isEIP1559Compatible: boolean,\n) {\n if (isEIP1559Transaction(txParams) && !isEIP1559Compatible) {\n throw rpcErrors.invalidParams(\n 'Invalid transaction params: params specify an EIP-1559 transaction but the current network does not support EIP-1559',\n );\n }\n}\n\n/**\n * Validates value property, ensuring it is a valid positive integer number\n * denominated in wei.\n *\n * @param value - The value to validate, expressed as a string.\n * @throws Throws an error if the value is not a valid positive integer\n * number denominated in wei.\n * - If the value contains a hyphen (-), it is considered invalid.\n * - If the value contains a decimal point (.), it is considered invalid.\n * - If the value is not a finite number, is NaN, or is not a safe integer, it is considered invalid.\n */\nfunction validateParamValue(value?: string) {\n if (value !== undefined) {\n if (value.includes('-')) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value \"${value}\": not a positive number.`,\n );\n }\n\n if (value.includes('.')) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value \"${value}\": number must be in wei.`,\n );\n }\n const intValue = parseInt(value, 10);\n const isValid =\n Number.isFinite(intValue) &&\n !Number.isNaN(intValue) &&\n !isNaN(Number(value)) &&\n Number.isSafeInteger(intValue);\n if (!isValid) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value ${value}: number must be a valid number.`,\n );\n }\n }\n}\n\n/**\n * Validates the recipient address in a transaction's parameters.\n *\n * @param txParams - The transaction parameters object to validate.\n * @throws Throws an error if the recipient address is invalid:\n * - If the recipient address is an empty string ('0x') or undefined and the transaction contains data,\n * the \"to\" field is removed from the transaction parameters.\n * - If the recipient address is not a valid hexadecimal Ethereum address, an error is thrown.\n */\nfunction validateParamRecipient(txParams: TransactionParams) {\n if (txParams.to === '0x' || txParams.to === undefined) {\n if (txParams.data) {\n delete txParams.to;\n } else {\n throw rpcErrors.invalidParams(`Invalid \"to\" address.`);\n }\n } else if (txParams.to !== undefined && !isValidHexAddress(txParams.to)) {\n throw rpcErrors.invalidParams(`Invalid \"to\" address.`);\n }\n}\n\n/**\n * Validates the recipient address in a transaction's parameters.\n *\n * @param from - The from property to validate.\n * @throws Throws an error if the recipient address is invalid:\n * - If the recipient address is an empty string ('0x') or undefined and the transaction contains data,\n * the \"to\" field is removed from the transaction parameters.\n * - If the recipient address is not a valid hexadecimal Ethereum address, an error is thrown.\n */\nfunction validateParamFrom(from: string) {\n if (!from || typeof from !== 'string') {\n throw rpcErrors.invalidParams(\n `Invalid \"from\" address ${from}: not a string.`,\n );\n }\n if (!isValidHexAddress(from)) {\n throw rpcErrors.invalidParams('Invalid \"from\" address.');\n }\n}\n\n/**\n * Validates input data for transactions.\n *\n * @param value - The input data to validate.\n * @throws Throws invalid params if the input data is invalid.\n */\nfunction validateParamData(value?: string) {\n if (value) {\n const ERC20Interface = new Interface(abiERC20);\n try {\n ERC20Interface.parseTransaction({ data: value });\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.message.match(/BUFFER_OVERRUN/u)) {\n throw rpcErrors.invalidParams(\n 'Invalid transaction params: data out-of-bounds, BUFFER_OVERRUN.',\n );\n }\n }\n }\n}\n\n/**\n * Validates chainId type.\n *\n * @param chainId - The chainId to validate.\n */\nfunction validateParamChainId(chainId: number | string | undefined) {\n if (\n chainId !== undefined &&\n typeof chainId !== 'number' &&\n typeof chainId !== 'string'\n ) {\n throw rpcErrors.invalidParams(\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `Invalid transaction params: chainId is not a Number or hex string. got: (${chainId})`,\n );\n }\n}\n\n/**\n * Validates gas values.\n *\n * @param txParams - The transaction parameters to validate.\n */\nfunction validateGasFeeParams(txParams: TransactionParams) {\n if (txParams.gasPrice) {\n ensureProperTransactionEnvelopeTypeProvided(txParams, 'gasPrice');\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'gasPrice',\n 'maxFeePerGas',\n );\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'gasPrice',\n 'maxPriorityFeePerGas',\n );\n ensureFieldIsString(txParams, 'gasPrice');\n }\n\n if (txParams.maxFeePerGas) {\n ensureProperTransactionEnvelopeTypeProvided(txParams, 'maxFeePerGas');\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'maxFeePerGas',\n 'gasPrice',\n );\n ensureFieldIsString(txParams, 'maxFeePerGas');\n }\n\n if (txParams.maxPriorityFeePerGas) {\n ensureProperTransactionEnvelopeTypeProvided(\n txParams,\n 'maxPriorityFeePerGas',\n );\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'maxPriorityFeePerGas',\n 'gasPrice',\n );\n ensureFieldIsString(txParams, 'maxPriorityFeePerGas');\n }\n}\n\n/**\n * Ensures that the provided txParams has the proper 'type' specified for the\n * given field, if it is provided. If types do not match throws an\n * invalidParams error.\n *\n * @param txParams - The transaction parameters object\n * @param field - The current field being validated\n * @throws {ethErrors.rpc.invalidParams} Throws if type does not match the\n * expectations for provided field.\n */\nfunction ensureProperTransactionEnvelopeTypeProvided(\n txParams: TransactionParams,\n field: GasFieldsToValidate,\n) {\n switch (field) {\n case 'maxFeePerGas':\n case 'maxPriorityFeePerGas':\n if (\n txParams.type &&\n txParams.type !== TransactionEnvelopeType.feeMarket\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: specified type \"${txParams.type}\" but including maxFeePerGas and maxPriorityFeePerGas requires type: \"${TransactionEnvelopeType.feeMarket}\"`,\n );\n }\n break;\n case 'gasPrice':\n default:\n if (\n txParams.type &&\n txParams.type === TransactionEnvelopeType.feeMarket\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: specified type \"${txParams.type}\" but included a gasPrice instead of maxFeePerGas and maxPriorityFeePerGas`,\n );\n }\n }\n}\n\n/**\n * Given two fields, ensure that the second field is not included in txParams,\n * and if it is throw an invalidParams error.\n *\n * @param txParams - The transaction parameters object\n * @param fieldBeingValidated - The current field being validated\n * @param mutuallyExclusiveField - The field to ensure is not provided\n * @throws {ethErrors.rpc.invalidParams} Throws if mutuallyExclusiveField is\n * present in txParams.\n */\nfunction ensureMutuallyExclusiveFieldsNotProvided(\n txParams: TransactionParams,\n fieldBeingValidated: GasFieldsToValidate,\n mutuallyExclusiveField: GasFieldsToValidate,\n) {\n if (typeof txParams[mutuallyExclusiveField] !== 'undefined') {\n throw rpcErrors.invalidParams(\n `Invalid transaction params: specified ${fieldBeingValidated} but also included ${mutuallyExclusiveField}, these cannot be mixed`,\n );\n }\n}\n\n/**\n * Ensures that the provided value for field is a string, throws an\n * invalidParams error if field is not a string.\n *\n * @param txParams - The transaction parameters object\n * @param field - The current field being validated\n * @throws {rpcErrors.invalidParams} Throws if field is not a string\n */\nfunction ensureFieldIsString(\n txParams: TransactionParams,\n field: GasFieldsToValidate,\n) {\n if (typeof txParams[field] !== 'string') {\n throw rpcErrors.invalidParams(\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `Invalid transaction params: ${field} is not a string. got: (${txParams[field]})`,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"validation.mjs","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,2BAA2B;AAC/C,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,mCAAmC;AAChF,OAAO,EAAE,QAAQ,EAAE,oCAAoC;AACvD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,6BAA6B;AACjE,OAAO,EAAE,iBAAiB,EAAE,wBAAwB;AAEpD,OAAO,EAAE,uBAAuB,EAA0B,qBAAiB;AAC3E,OAAO,EAAE,oBAAoB,EAAE,oBAAgB;AAI/C;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,kBAA4B,EAC5B,eAAuB,EACvB,IAAY,EACZ,MAAc;IAEd,IAAI,MAAM,KAAK,eAAe,EAAE;QAC9B,mEAAmE;QACnE,IAAI,IAAI,KAAK,eAAe,EAAE;YAC5B,MAAM,SAAS,CAAC,QAAQ,CAAC;gBACvB,OAAO,EAAE,4DAA4D;gBACrE,IAAI,EAAE;oBACJ,MAAM;oBACN,WAAW,EAAE,IAAI;oBACjB,eAAe;iBAChB;aACF,CAAC,CAAC;SACJ;QACD,OAAO;KACR;IAED,0FAA0F;IAC1F,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;QACtC,MAAM,cAAc,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;KACzD;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAA2B,EAC3B,mBAAmB,GAAG,IAAI;IAE1B,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACpC,4BAA4B,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;IAC5D,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjC,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACjC,kBAAkB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjC,oBAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED;;;;;GAKG;AACH,SAAS,oBAAoB,CAAC,IAAwB;IACpD,IACE,IAAI;QACJ,CAAC,MAAM,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,QAAQ,CAC9C,IAA+B,CAChC,EACD;QACA,MAAM,SAAS,CAAC,aAAa,CAC3B,uCAAuC,IAAI,sBAAsB,MAAM,CAAC,MAAM,CAC5E,uBAAuB,CACxB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACf,CAAC;KACH;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,4BAA4B,CACnC,QAA2B,EAC3B,mBAA4B;IAE5B,IAAI,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,mBAAmB,EAAE;QAC1D,MAAM,SAAS,CAAC,aAAa,CAC3B,sHAAsH,CACvH,CAAC;KACH;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,kBAAkB,CAAC,KAAc;IACxC,IAAI,KAAK,KAAK,SAAS,EAAE;QACvB,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;YACvB,MAAM,SAAS,CAAC,aAAa,CAC3B,8BAA8B,KAAK,2BAA2B,CAC/D,CAAC;SACH;QAED,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;YACvB,MAAM,SAAS,CAAC,aAAa,CAC3B,8BAA8B,KAAK,2BAA2B,CAC/D,CAAC;SACH;QACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,OAAO,GACX,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACzB,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;YACvB,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACrB,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,CAAC,OAAO,EAAE;YACZ,MAAM,SAAS,CAAC,aAAa,CAC3B,6BAA6B,KAAK,kCAAkC,CACrE,CAAC;SACH;KACF;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,sBAAsB,CAAC,QAA2B;IACzD,IAAI,QAAQ,CAAC,EAAE,KAAK,IAAI,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE;QACrD,IAAI,QAAQ,CAAC,IAAI,EAAE;YACjB,OAAO,QAAQ,CAAC,EAAE,CAAC;SACpB;aAAM;YACL,MAAM,SAAS,CAAC,aAAa,CAAC,uBAAuB,CAAC,CAAC;SACxD;KACF;SAAM,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;QACvE,MAAM,SAAS,CAAC,aAAa,CAAC,uBAAuB,CAAC,CAAC;KACxD;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QACrC,MAAM,SAAS,CAAC,aAAa,CAC3B,0BAA0B,IAAI,iBAAiB,CAChD,CAAC;KACH;IACD,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE;QAC5B,MAAM,SAAS,CAAC,aAAa,CAAC,yBAAyB,CAAC,CAAC;KAC1D;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,KAAc;IACvC,IAAI,KAAK,EAAE;QACT,MAAM,cAAc,GAAG,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI;YACF,cAAc,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACjD,gCAAgC;YAChC,8DAA8D;SAC/D;QAAC,OAAO,KAAU,EAAE;YACnB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE;gBAC1C,MAAM,SAAS,CAAC,aAAa,CAC3B,iEAAiE,CAClE,CAAC;aACH;SACF;KACF;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAAC,OAAoC;IAChE,IACE,OAAO,KAAK,SAAS;QACrB,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO,OAAO,KAAK,QAAQ,EAC3B;QACA,MAAM,SAAS,CAAC,aAAa;QAC3B,gFAAgF;QAChF,4EAA4E;QAC5E,4EAA4E,OAAO,GAAG,CACvF,CAAC;KACH;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAAC,QAA2B;IACvD,IAAI,QAAQ,CAAC,QAAQ,EAAE;QACrB,2CAA2C,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QAClE,wCAAwC,CACtC,QAAQ,EACR,UAAU,EACV,cAAc,CACf,CAAC;QACF,wCAAwC,CACtC,QAAQ,EACR,UAAU,EACV,sBAAsB,CACvB,CAAC;QACF,qBAAqB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;KAC7C;IAED,IAAI,QAAQ,CAAC,YAAY,EAAE;QACzB,2CAA2C,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QACtE,wCAAwC,CACtC,QAAQ,EACR,cAAc,EACd,UAAU,CACX,CAAC;QACF,qBAAqB,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;KACjD;IAED,IAAI,QAAQ,CAAC,oBAAoB,EAAE;QACjC,2CAA2C,CACzC,QAAQ,EACR,sBAAsB,CACvB,CAAC;QACF,wCAAwC,CACtC,QAAQ,EACR,sBAAsB,EACtB,UAAU,CACX,CAAC;QACF,qBAAqB,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;KACzD;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,2CAA2C,CAClD,QAA2B,EAC3B,KAA0B;IAE1B,QAAQ,KAAK,EAAE;QACb,KAAK,cAAc,CAAC;QACpB,KAAK,sBAAsB;YACzB,IACE,QAAQ,CAAC,IAAI;gBACb,QAAQ,CAAC,IAAI,KAAK,uBAAuB,CAAC,SAAS,EACnD;gBACA,MAAM,SAAS,CAAC,aAAa,CAC3B,sDAAsD,QAAQ,CAAC,IAAI,yEAAyE,uBAAuB,CAAC,SAAS,GAAG,CACjL,CAAC;aACH;YACD,MAAM;QACR,KAAK,UAAU,CAAC;QAChB;YACE,IACE,QAAQ,CAAC,IAAI;gBACb,QAAQ,CAAC,IAAI,KAAK,uBAAuB,CAAC,SAAS,EACnD;gBACA,MAAM,SAAS,CAAC,aAAa,CAC3B,sDAAsD,QAAQ,CAAC,IAAI,4EAA4E,CAChJ,CAAC;aACH;KACJ;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,wCAAwC,CAC/C,QAA2B,EAC3B,mBAAwC,EACxC,sBAA2C;IAE3C,IAAI,OAAO,QAAQ,CAAC,sBAAsB,CAAC,KAAK,WAAW,EAAE;QAC3D,MAAM,SAAS,CAAC,aAAa,CAC3B,yCAAyC,mBAAmB,sBAAsB,sBAAsB,yBAAyB,CAClI,CAAC;KACH;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,qBAAqB,CAC5B,QAA2B,EAC3B,KAA0B;IAE1B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE;QAC1D,MAAM,SAAS,CAAC,aAAa,CAC3B,+BAA+B,KAAK,sCAAsC,MAAM,CAC9E,KAAK,CACN,GAAG,CACL,CAAC;KACH;AACH,CAAC","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { ORIGIN_METAMASK, isValidHexAddress } from '@metamask/controller-utils';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport { providerErrors, rpcErrors } from '@metamask/rpc-errors';\nimport { isStrictHexString } from '@metamask/utils';\n\nimport { TransactionEnvelopeType, type TransactionParams } from '../types';\nimport { isEIP1559Transaction } from './utils';\n\ntype GasFieldsToValidate = 'gasPrice' | 'maxFeePerGas' | 'maxPriorityFeePerGas';\n\n/**\n * Validates whether a transaction initiated by a specific 'from' address is permitted by the origin.\n *\n * @param permittedAddresses - The permitted accounts for the given origin.\n * @param selectedAddress - The currently selected Ethereum address in the wallet.\n * @param from - The address from which the transaction is initiated.\n * @param origin - The origin or source of the transaction.\n * @throws Throws an error if the transaction is not permitted.\n */\nexport async function validateTransactionOrigin(\n permittedAddresses: string[],\n selectedAddress: string,\n from: string,\n origin: string,\n) {\n if (origin === ORIGIN_METAMASK) {\n // Ensure the 'from' address matches the currently selected address\n if (from !== selectedAddress) {\n throw rpcErrors.internal({\n message: `Internally initiated transaction is using invalid account.`,\n data: {\n origin,\n fromAddress: from,\n selectedAddress,\n },\n });\n }\n return;\n }\n\n // Check if the origin has permissions to initiate transactions from the specified address\n if (!permittedAddresses.includes(from)) {\n throw providerErrors.unauthorized({ data: { origin } });\n }\n}\n\n/**\n * Validates the transaction params for required properties and throws in\n * the event of any validation error.\n *\n * @param txParams - Transaction params object to validate.\n * @param isEIP1559Compatible - whether or not the current network supports EIP-1559 transactions.\n */\nexport function validateTxParams(\n txParams: TransactionParams,\n isEIP1559Compatible = true,\n) {\n validateEnvelopeType(txParams.type);\n validateEIP1559Compatibility(txParams, isEIP1559Compatible);\n validateParamFrom(txParams.from);\n validateParamRecipient(txParams);\n validateParamValue(txParams.value);\n validateParamData(txParams.data);\n validateParamChainId(txParams.chainId);\n validateGasFeeParams(txParams);\n}\n\n/**\n * Validates the `type` property, ensuring that if it is specified, it is a valid transaction envelope type.\n *\n * @param type - The transaction envelope type to validate.\n * @throws Throws invalid params if the type is not a valid transaction envelope type.\n */\nfunction validateEnvelopeType(type: string | undefined) {\n if (\n type &&\n !Object.values(TransactionEnvelopeType).includes(\n type as TransactionEnvelopeType,\n )\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: \"${type}\". Must be one of: ${Object.values(\n TransactionEnvelopeType,\n ).join(', ')}`,\n );\n }\n}\n\n/**\n * Validates EIP-1559 compatibility for transaction creation.\n *\n * @param txParams - The transaction parameters to validate.\n * @param isEIP1559Compatible - Indicates if the current network supports EIP-1559.\n * @throws Throws invalid params if the transaction specifies EIP-1559 but the network does not support it.\n */\nfunction validateEIP1559Compatibility(\n txParams: TransactionParams,\n isEIP1559Compatible: boolean,\n) {\n if (isEIP1559Transaction(txParams) && !isEIP1559Compatible) {\n throw rpcErrors.invalidParams(\n 'Invalid transaction params: params specify an EIP-1559 transaction but the current network does not support EIP-1559',\n );\n }\n}\n\n/**\n * Validates value property, ensuring it is a valid positive integer number\n * denominated in wei.\n *\n * @param value - The value to validate, expressed as a string.\n * @throws Throws an error if the value is not a valid positive integer\n * number denominated in wei.\n * - If the value contains a hyphen (-), it is considered invalid.\n * - If the value contains a decimal point (.), it is considered invalid.\n * - If the value is not a finite number, is NaN, or is not a safe integer, it is considered invalid.\n */\nfunction validateParamValue(value?: string) {\n if (value !== undefined) {\n if (value.includes('-')) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value \"${value}\": not a positive number.`,\n );\n }\n\n if (value.includes('.')) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value \"${value}\": number must be in wei.`,\n );\n }\n const intValue = parseInt(value, 10);\n const isValid =\n Number.isFinite(intValue) &&\n !Number.isNaN(intValue) &&\n !isNaN(Number(value)) &&\n Number.isSafeInteger(intValue);\n if (!isValid) {\n throw rpcErrors.invalidParams(\n `Invalid transaction value ${value}: number must be a valid number.`,\n );\n }\n }\n}\n\n/**\n * Validates the recipient address in a transaction's parameters.\n *\n * @param txParams - The transaction parameters object to validate.\n * @throws Throws an error if the recipient address is invalid:\n * - If the recipient address is an empty string ('0x') or undefined and the transaction contains data,\n * the \"to\" field is removed from the transaction parameters.\n * - If the recipient address is not a valid hexadecimal Ethereum address, an error is thrown.\n */\nfunction validateParamRecipient(txParams: TransactionParams) {\n if (txParams.to === '0x' || txParams.to === undefined) {\n if (txParams.data) {\n delete txParams.to;\n } else {\n throw rpcErrors.invalidParams(`Invalid \"to\" address.`);\n }\n } else if (txParams.to !== undefined && !isValidHexAddress(txParams.to)) {\n throw rpcErrors.invalidParams(`Invalid \"to\" address.`);\n }\n}\n\n/**\n * Validates the recipient address in a transaction's parameters.\n *\n * @param from - The from property to validate.\n * @throws Throws an error if the recipient address is invalid:\n * - If the recipient address is an empty string ('0x') or undefined and the transaction contains data,\n * the \"to\" field is removed from the transaction parameters.\n * - If the recipient address is not a valid hexadecimal Ethereum address, an error is thrown.\n */\nfunction validateParamFrom(from: string) {\n if (!from || typeof from !== 'string') {\n throw rpcErrors.invalidParams(\n `Invalid \"from\" address ${from}: not a string.`,\n );\n }\n if (!isValidHexAddress(from)) {\n throw rpcErrors.invalidParams('Invalid \"from\" address.');\n }\n}\n\n/**\n * Validates input data for transactions.\n *\n * @param value - The input data to validate.\n * @throws Throws invalid params if the input data is invalid.\n */\nfunction validateParamData(value?: string) {\n if (value) {\n const ERC20Interface = new Interface(abiERC20);\n try {\n ERC20Interface.parseTransaction({ data: value });\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (error.message.match(/BUFFER_OVERRUN/u)) {\n throw rpcErrors.invalidParams(\n 'Invalid transaction params: data out-of-bounds, BUFFER_OVERRUN.',\n );\n }\n }\n }\n}\n\n/**\n * Validates chainId type.\n *\n * @param chainId - The chainId to validate.\n */\nfunction validateParamChainId(chainId: number | string | undefined) {\n if (\n chainId !== undefined &&\n typeof chainId !== 'number' &&\n typeof chainId !== 'string'\n ) {\n throw rpcErrors.invalidParams(\n // TODO: Either fix this lint violation or explain why it's necessary to ignore.\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n `Invalid transaction params: chainId is not a Number or hex string. got: (${chainId})`,\n );\n }\n}\n\n/**\n * Validates gas values.\n *\n * @param txParams - The transaction parameters to validate.\n */\nfunction validateGasFeeParams(txParams: TransactionParams) {\n if (txParams.gasPrice) {\n ensureProperTransactionEnvelopeTypeProvided(txParams, 'gasPrice');\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'gasPrice',\n 'maxFeePerGas',\n );\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'gasPrice',\n 'maxPriorityFeePerGas',\n );\n ensureFieldIsValidHex(txParams, 'gasPrice');\n }\n\n if (txParams.maxFeePerGas) {\n ensureProperTransactionEnvelopeTypeProvided(txParams, 'maxFeePerGas');\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'maxFeePerGas',\n 'gasPrice',\n );\n ensureFieldIsValidHex(txParams, 'maxFeePerGas');\n }\n\n if (txParams.maxPriorityFeePerGas) {\n ensureProperTransactionEnvelopeTypeProvided(\n txParams,\n 'maxPriorityFeePerGas',\n );\n ensureMutuallyExclusiveFieldsNotProvided(\n txParams,\n 'maxPriorityFeePerGas',\n 'gasPrice',\n );\n ensureFieldIsValidHex(txParams, 'maxPriorityFeePerGas');\n }\n}\n\n/**\n * Ensures that the provided txParams has the proper 'type' specified for the\n * given field, if it is provided. If types do not match throws an\n * invalidParams error.\n *\n * @param txParams - The transaction parameters object\n * @param field - The current field being validated\n * @throws {ethErrors.rpc.invalidParams} Throws if type does not match the\n * expectations for provided field.\n */\nfunction ensureProperTransactionEnvelopeTypeProvided(\n txParams: TransactionParams,\n field: GasFieldsToValidate,\n) {\n switch (field) {\n case 'maxFeePerGas':\n case 'maxPriorityFeePerGas':\n if (\n txParams.type &&\n txParams.type !== TransactionEnvelopeType.feeMarket\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: specified type \"${txParams.type}\" but including maxFeePerGas and maxPriorityFeePerGas requires type: \"${TransactionEnvelopeType.feeMarket}\"`,\n );\n }\n break;\n case 'gasPrice':\n default:\n if (\n txParams.type &&\n txParams.type === TransactionEnvelopeType.feeMarket\n ) {\n throw rpcErrors.invalidParams(\n `Invalid transaction envelope type: specified type \"${txParams.type}\" but included a gasPrice instead of maxFeePerGas and maxPriorityFeePerGas`,\n );\n }\n }\n}\n\n/**\n * Given two fields, ensure that the second field is not included in txParams,\n * and if it is throw an invalidParams error.\n *\n * @param txParams - The transaction parameters object\n * @param fieldBeingValidated - The current field being validated\n * @param mutuallyExclusiveField - The field to ensure is not provided\n * @throws {ethErrors.rpc.invalidParams} Throws if mutuallyExclusiveField is\n * present in txParams.\n */\nfunction ensureMutuallyExclusiveFieldsNotProvided(\n txParams: TransactionParams,\n fieldBeingValidated: GasFieldsToValidate,\n mutuallyExclusiveField: GasFieldsToValidate,\n) {\n if (typeof txParams[mutuallyExclusiveField] !== 'undefined') {\n throw rpcErrors.invalidParams(\n `Invalid transaction params: specified ${fieldBeingValidated} but also included ${mutuallyExclusiveField}, these cannot be mixed`,\n );\n }\n}\n\n/**\n * Ensures that the provided value for field is a valid hexadecimal.\n * Throws an invalidParams error if field is not a valid hexadecimal.\n *\n * @param txParams - The transaction parameters object\n * @param field - The current field being validated\n * @throws {rpcErrors.invalidParams} Throws if field is not a valid hexadecimal\n */\nfunction ensureFieldIsValidHex(\n txParams: TransactionParams,\n field: GasFieldsToValidate,\n) {\n const value = txParams[field];\n if (typeof value !== 'string' || !isStrictHexString(value)) {\n throw rpcErrors.invalidParams(\n `Invalid transaction params: ${field} is not a valid hexadecimal. got: (${String(\n value,\n )})`,\n );\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask/transaction-controller",
3
- "version": "38.2.0",
3
+ "version": "39.0.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,7 +54,7 @@
54
54
  "@ethersproject/contracts": "^5.7.0",
55
55
  "@ethersproject/providers": "^5.7.0",
56
56
  "@metamask/base-controller": "^7.0.2",
57
- "@metamask/controller-utils": "^11.4.2",
57
+ "@metamask/controller-utils": "^11.4.3",
58
58
  "@metamask/eth-query": "^4.0.0",
59
59
  "@metamask/metamask-eth-abis": "^3.1.1",
60
60
  "@metamask/nonce-tracker": "^6.0.0",
@@ -69,14 +69,14 @@
69
69
  },
70
70
  "devDependencies": {
71
71
  "@babel/runtime": "^7.23.9",
72
- "@metamask/accounts-controller": "^18.2.3",
72
+ "@metamask/accounts-controller": "^19.0.0",
73
73
  "@metamask/approval-controller": "^7.1.1",
74
74
  "@metamask/auto-changelog": "^3.4.4",
75
75
  "@metamask/eth-json-rpc-provider": "^4.1.6",
76
76
  "@metamask/ethjs-provider-http": "^0.3.0",
77
- "@metamask/gas-fee-controller": "^22.0.0",
77
+ "@metamask/gas-fee-controller": "^22.0.1",
78
78
  "@metamask/keyring-api": "^8.1.3",
79
- "@metamask/network-controller": "^22.0.1",
79
+ "@metamask/network-controller": "^22.0.2",
80
80
  "@types/bn.js": "^5.1.5",
81
81
  "@types/jest": "^27.4.1",
82
82
  "@types/node": "^16.18.54",
@@ -92,7 +92,7 @@
92
92
  },
93
93
  "peerDependencies": {
94
94
  "@babel/runtime": "^7.23.9",
95
- "@metamask/accounts-controller": "^18.0.0",
95
+ "@metamask/accounts-controller": "^19.0.0",
96
96
  "@metamask/approval-controller": "^7.0.0",
97
97
  "@metamask/gas-fee-controller": "^22.0.0",
98
98
  "@metamask/network-controller": "^22.0.0"