@metamask/transaction-controller 48.2.0 → 50.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +45 -1
  2. package/dist/TransactionController.cjs +67 -48
  3. package/dist/TransactionController.cjs.map +1 -1
  4. package/dist/TransactionController.d.cts +5 -8
  5. package/dist/TransactionController.d.cts.map +1 -1
  6. package/dist/TransactionController.d.mts +5 -8
  7. package/dist/TransactionController.d.mts.map +1 -1
  8. package/dist/TransactionController.mjs +68 -49
  9. package/dist/TransactionController.mjs.map +1 -1
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.mts +1 -1
  14. package/dist/index.d.mts.map +1 -1
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/types.cjs +5 -0
  17. package/dist/types.cjs.map +1 -1
  18. package/dist/types.d.cts +36 -13
  19. package/dist/types.d.cts.map +1 -1
  20. package/dist/types.d.mts +36 -13
  21. package/dist/types.d.mts.map +1 -1
  22. package/dist/types.mjs +5 -0
  23. package/dist/types.mjs.map +1 -1
  24. package/dist/utils/batch.cjs +47 -9
  25. package/dist/utils/batch.cjs.map +1 -1
  26. package/dist/utils/batch.d.cts +4 -2
  27. package/dist/utils/batch.d.cts.map +1 -1
  28. package/dist/utils/batch.d.mts +4 -2
  29. package/dist/utils/batch.d.mts.map +1 -1
  30. package/dist/utils/batch.mjs +49 -11
  31. package/dist/utils/batch.mjs.map +1 -1
  32. package/dist/utils/eip7702.cjs +21 -9
  33. package/dist/utils/eip7702.cjs.map +1 -1
  34. package/dist/utils/eip7702.d.cts +10 -1
  35. package/dist/utils/eip7702.d.cts.map +1 -1
  36. package/dist/utils/eip7702.d.mts +10 -1
  37. package/dist/utils/eip7702.d.mts.map +1 -1
  38. package/dist/utils/eip7702.mjs +19 -8
  39. package/dist/utils/eip7702.mjs.map +1 -1
  40. package/dist/utils/feature-flags.cjs +26 -5
  41. package/dist/utils/feature-flags.cjs.map +1 -1
  42. package/dist/utils/feature-flags.d.cts +27 -7
  43. package/dist/utils/feature-flags.d.cts.map +1 -1
  44. package/dist/utils/feature-flags.d.mts +27 -7
  45. package/dist/utils/feature-flags.d.mts.map +1 -1
  46. package/dist/utils/feature-flags.mjs +24 -4
  47. package/dist/utils/feature-flags.mjs.map +1 -1
  48. package/dist/utils/signature.cjs +26 -0
  49. package/dist/utils/signature.cjs.map +1 -0
  50. package/dist/utils/signature.d.cts +11 -0
  51. package/dist/utils/signature.d.cts.map +1 -0
  52. package/dist/utils/signature.d.mts +11 -0
  53. package/dist/utils/signature.d.mts.map +1 -0
  54. package/dist/utils/signature.mjs +22 -0
  55. package/dist/utils/signature.mjs.map +1 -0
  56. package/dist/utils/validation.cjs +5 -1
  57. package/dist/utils/validation.cjs.map +1 -1
  58. package/dist/utils/validation.d.cts +3 -1
  59. package/dist/utils/validation.d.cts.map +1 -1
  60. package/dist/utils/validation.d.mts +3 -1
  61. package/dist/utils/validation.d.mts.map +1 -1
  62. package/dist/utils/validation.mjs +5 -1
  63. package/dist/utils/validation.mjs.map +1 -1
  64. package/package.json +3 -2
@@ -1,19 +1,21 @@
1
1
  import type EthQuery from "@metamask/eth-query";
2
2
  import type { Hex } from "@metamask/utils";
3
- import type { TransactionController, TransactionControllerMessenger } from "../index.mjs";
4
- import { type TransactionBatchRequest, type TransactionBatchResult } from "../types.mjs";
3
+ import { type TransactionBatchRequest, type TransactionController, type TransactionControllerMessenger } from "../index.mjs";
4
+ import { type TransactionBatchResult } from "../types.mjs";
5
5
  type AddTransactionBatchRequest = {
6
6
  addTransaction: TransactionController['addTransaction'];
7
7
  getChainId: (networkClientId: string) => Hex;
8
8
  getEthQuery: (networkClientId: string) => EthQuery;
9
9
  getInternalAccounts: () => Hex[];
10
10
  messenger: TransactionControllerMessenger;
11
+ publicKeyEIP7702?: Hex;
11
12
  request: TransactionBatchRequest;
12
13
  };
13
14
  type IsAtomicBatchSupportedRequest = {
14
15
  address: Hex;
15
16
  getEthQuery: (chainId: Hex) => EthQuery;
16
17
  messenger: TransactionControllerMessenger;
18
+ publicKeyEIP7702?: Hex;
17
19
  };
18
20
  /**
19
21
  * Add a batch transaction.
@@ -1 +1 @@
1
- {"version":3,"file":"batch.d.mts","sourceRoot":"","sources":["../../src/utils/batch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,4BAA4B;AAEhD,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAa3C,OAAO,KAAK,EAAE,qBAAqB,EAAE,8BAA8B,EAAE,qBAAW;AAEhF,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAG5B,qBAAiB;AAElB,KAAK,0BAA0B,GAAG;IAChC,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;IACxD,UAAU,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,GAAG,CAAC;IAC7C,WAAW,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,QAAQ,CAAC;IACnD,mBAAmB,EAAE,MAAM,GAAG,EAAE,CAAC;IACjC,SAAS,EAAE,8BAA8B,CAAC;IAC1C,OAAO,EAAE,uBAAuB,CAAC;CAClC,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACnC,OAAO,EAAE,GAAG,CAAC;IACb,WAAW,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,QAAQ,CAAC;IACxC,SAAS,EAAE,8BAA8B,CAAC;CAC3C,CAAC;AAIF;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,sBAAsB,CAAC,CAgFjC;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,GAAG,EAAE,CAAC,CAwBhB"}
1
+ {"version":3,"file":"batch.d.mts","sourceRoot":"","sources":["../../src/utils/batch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,4BAA4B;AAEhD,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAe3C,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,8BAA8B,EACpC,qBAAW;AAMZ,OAAO,EAEL,KAAK,sBAAsB,EAG5B,qBAAiB;AAElB,KAAK,0BAA0B,GAAG;IAChC,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;IACxD,UAAU,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,GAAG,CAAC;IAC7C,WAAW,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,QAAQ,CAAC;IACnD,mBAAmB,EAAE,MAAM,GAAG,EAAE,CAAC;IACjC,SAAS,EAAE,8BAA8B,CAAC;IAC1C,gBAAgB,CAAC,EAAE,GAAG,CAAC;IACvB,OAAO,EAAE,uBAAuB,CAAC;CAClC,CAAC;AAEF,KAAK,6BAA6B,GAAG;IACnC,OAAO,EAAE,GAAG,CAAC;IACb,WAAW,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,QAAQ,CAAC;IACxC,SAAS,EAAE,8BAA8B,CAAC;IAC1C,gBAAgB,CAAC,EAAE,GAAG,CAAC;CACxB,CAAC;AAIF;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,sBAAsB,CAAC,CAsGjC;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,GAAG,EAAE,CAAC,CAkChB"}
@@ -1,8 +1,10 @@
1
1
  import { rpcErrors } from "@metamask/rpc-errors";
2
- import { createModuleLogger } from "@metamask/utils";
2
+ import { bytesToHex, createModuleLogger } from "@metamask/utils";
3
+ import { parse, v4 } from "uuid";
3
4
  import { doesChainSupportEIP7702, generateEIP7702BatchTransaction, isAccountUpgradedToEIP7702 } from "./eip7702.mjs";
4
- import { getEIP7702SupportedChains, getEIP7702UpgradeContractAddress } from "./feature-flags.mjs";
5
+ import { getBatchSizeLimit, getEIP7702SupportedChains, getEIP7702UpgradeContractAddress } from "./feature-flags.mjs";
5
6
  import { validateBatchRequest } from "./validation.mjs";
7
+ import { determineTransactionType } from "../index.mjs";
6
8
  import { projectLogger } from "../logger.mjs";
7
9
  import { TransactionEnvelopeType, TransactionType } from "../types.mjs";
8
10
  const log = createModuleLogger(projectLogger, 'batch');
@@ -13,12 +15,14 @@ const log = createModuleLogger(projectLogger, 'batch');
13
15
  * @returns The batch result object including the batch ID.
14
16
  */
15
17
  export async function addTransactionBatch(request) {
16
- const { addTransaction, getChainId, getInternalAccounts, messenger, request: userRequest, } = request;
18
+ const { addTransaction, getChainId, getInternalAccounts, messenger, publicKeyEIP7702, request: userRequest, } = request;
19
+ const sizeLimit = getBatchSizeLimit(messenger);
17
20
  validateBatchRequest({
18
21
  internalAccounts: getInternalAccounts(),
19
22
  request: userRequest,
23
+ sizeLimit,
20
24
  });
21
- const { from, networkClientId, requireApproval, transactions } = userRequest;
25
+ const { batchId: batchIdOverride, from, networkClientId, requireApproval, transactions, } = userRequest;
22
26
  log('Adding', userRequest);
23
27
  const chainId = getChainId(networkClientId);
24
28
  const ethQuery = request.getEthQuery(networkClientId);
@@ -27,20 +31,23 @@ export async function addTransactionBatch(request) {
27
31
  log('Chain does not support EIP-7702', chainId);
28
32
  throw rpcErrors.internal('Chain does not support EIP-7702');
29
33
  }
30
- const { delegationAddress, isSupported } = await isAccountUpgradedToEIP7702(from, chainId, messenger, ethQuery);
34
+ if (!publicKeyEIP7702) {
35
+ throw rpcErrors.internal('EIP-7702 public key not specified');
36
+ }
37
+ const { delegationAddress, isSupported } = await isAccountUpgradedToEIP7702(from, chainId, publicKeyEIP7702, messenger, ethQuery);
31
38
  log('Account', { delegationAddress, isSupported });
32
39
  if (!isSupported && delegationAddress) {
33
40
  log('Account upgraded to unsupported contract', from, delegationAddress);
34
41
  throw rpcErrors.internal('Account upgraded to unsupported contract');
35
42
  }
36
- const nestedTransactions = transactions.map((tx) => tx.params);
43
+ const nestedTransactions = await Promise.all(transactions.map((tx) => getNestedTransactionMeta(userRequest, tx, ethQuery)));
37
44
  const batchParams = generateEIP7702BatchTransaction(from, nestedTransactions);
38
45
  const txParams = {
39
46
  from,
40
47
  ...batchParams,
41
48
  };
42
49
  if (!isSupported) {
43
- const upgradeContractAddress = getEIP7702UpgradeContractAddress(chainId, messenger);
50
+ const upgradeContractAddress = getEIP7702UpgradeContractAddress(chainId, messenger, publicKeyEIP7702);
44
51
  if (!upgradeContractAddress) {
45
52
  throw rpcErrors.internal('Upgrade contract address not found');
46
53
  }
@@ -48,13 +55,14 @@ export async function addTransactionBatch(request) {
48
55
  txParams.authorizationList = [{ address: upgradeContractAddress }];
49
56
  }
50
57
  log('Adding batch transaction', txParams, networkClientId);
51
- const { transactionMeta, result } = await addTransaction(txParams, {
58
+ const batchId = batchIdOverride ?? generateBatchId();
59
+ const { result } = await addTransaction(txParams, {
60
+ batchId,
52
61
  nestedTransactions,
53
62
  networkClientId,
54
63
  requireApproval,
55
64
  type: TransactionType.batch,
56
65
  });
57
- const batchId = transactionMeta.id;
58
66
  // Wait for the transaction to be published.
59
67
  await result;
60
68
  return {
@@ -68,12 +76,15 @@ export async function addTransactionBatch(request) {
68
76
  * @returns The chain IDs that support atomic batch transactions.
69
77
  */
70
78
  export async function isAtomicBatchSupported(request) {
71
- const { address, getEthQuery, messenger } = request;
79
+ const { address, getEthQuery, messenger, publicKeyEIP7702: publicKey, } = request;
80
+ if (!publicKey) {
81
+ throw rpcErrors.internal('EIP-7702 public key not specified');
82
+ }
72
83
  const chainIds7702 = getEIP7702SupportedChains(messenger);
73
84
  const chainIds = [];
74
85
  for (const chainId of chainIds7702) {
75
86
  const ethQuery = getEthQuery(chainId);
76
- const { isSupported, delegationAddress } = await isAccountUpgradedToEIP7702(address, chainId, messenger, ethQuery);
87
+ const { isSupported, delegationAddress } = await isAccountUpgradedToEIP7702(address, chainId, publicKey, messenger, ethQuery);
77
88
  if (!delegationAddress || isSupported) {
78
89
  chainIds.push(chainId);
79
90
  }
@@ -81,4 +92,31 @@ export async function isAtomicBatchSupported(request) {
81
92
  log('Atomic batch supported chains', chainIds);
82
93
  return chainIds;
83
94
  }
95
+ /**
96
+ * Generate a tranasction batch ID.
97
+ *
98
+ * @returns A unique batch ID as a hexadecimal string.
99
+ */
100
+ function generateBatchId() {
101
+ const idString = v4();
102
+ const idBytes = new Uint8Array(parse(idString));
103
+ return bytesToHex(idBytes);
104
+ }
105
+ /**
106
+ * Generate the metadata for a nested transaction.
107
+ *
108
+ * @param request - The batch request.
109
+ * @param singleRequest - The request for a single transaction.
110
+ * @param ethQuery - The EthQuery instance used to interact with the Ethereum blockchain.
111
+ * @returns The metadata for the nested transaction.
112
+ */
113
+ async function getNestedTransactionMeta(request, singleRequest, ethQuery) {
114
+ const { from } = request;
115
+ const { params } = singleRequest;
116
+ const { type } = await determineTransactionType({ from, ...params }, ethQuery);
117
+ return {
118
+ ...params,
119
+ type,
120
+ };
121
+ }
84
122
  //# sourceMappingURL=batch.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"batch.mjs","sourceRoot":"","sources":["../../src/utils/batch.ts"],"names":[],"mappings":"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","sourcesContent":["import type EthQuery from '@metamask/eth-query';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\n\nimport {\n doesChainSupportEIP7702,\n generateEIP7702BatchTransaction,\n isAccountUpgradedToEIP7702,\n} from './eip7702';\nimport {\n getEIP7702SupportedChains,\n getEIP7702UpgradeContractAddress,\n} from './feature-flags';\nimport { validateBatchRequest } from './validation';\nimport type { TransactionController, TransactionControllerMessenger } from '..';\nimport { projectLogger } from '../logger';\nimport {\n TransactionEnvelopeType,\n type TransactionBatchRequest,\n type TransactionBatchResult,\n type TransactionParams,\n TransactionType,\n} from '../types';\n\ntype AddTransactionBatchRequest = {\n addTransaction: TransactionController['addTransaction'];\n getChainId: (networkClientId: string) => Hex;\n getEthQuery: (networkClientId: string) => EthQuery;\n getInternalAccounts: () => Hex[];\n messenger: TransactionControllerMessenger;\n request: TransactionBatchRequest;\n};\n\ntype IsAtomicBatchSupportedRequest = {\n address: Hex;\n getEthQuery: (chainId: Hex) => EthQuery;\n messenger: TransactionControllerMessenger;\n};\n\nconst log = createModuleLogger(projectLogger, 'batch');\n\n/**\n * Add a batch transaction.\n *\n * @param request - The request object including the user request and necessary callbacks.\n * @returns The batch result object including the batch ID.\n */\nexport async function addTransactionBatch(\n request: AddTransactionBatchRequest,\n): Promise<TransactionBatchResult> {\n const {\n addTransaction,\n getChainId,\n getInternalAccounts,\n messenger,\n request: userRequest,\n } = request;\n\n validateBatchRequest({\n internalAccounts: getInternalAccounts(),\n request: userRequest,\n });\n\n const { from, networkClientId, requireApproval, transactions } = userRequest;\n\n log('Adding', userRequest);\n\n const chainId = getChainId(networkClientId);\n const ethQuery = request.getEthQuery(networkClientId);\n const isChainSupported = doesChainSupportEIP7702(chainId, messenger);\n\n if (!isChainSupported) {\n log('Chain does not support EIP-7702', chainId);\n throw rpcErrors.internal('Chain does not support EIP-7702');\n }\n\n const { delegationAddress, isSupported } = await isAccountUpgradedToEIP7702(\n from,\n chainId,\n messenger,\n ethQuery,\n );\n\n log('Account', { delegationAddress, isSupported });\n\n if (!isSupported && delegationAddress) {\n log('Account upgraded to unsupported contract', from, delegationAddress);\n throw rpcErrors.internal('Account upgraded to unsupported contract');\n }\n\n const nestedTransactions = transactions.map((tx) => tx.params);\n const batchParams = generateEIP7702BatchTransaction(from, nestedTransactions);\n\n const txParams: TransactionParams = {\n from,\n ...batchParams,\n };\n\n if (!isSupported) {\n const upgradeContractAddress = getEIP7702UpgradeContractAddress(\n chainId,\n messenger,\n );\n\n if (!upgradeContractAddress) {\n throw rpcErrors.internal('Upgrade contract address not found');\n }\n\n txParams.type = TransactionEnvelopeType.setCode;\n txParams.authorizationList = [{ address: upgradeContractAddress }];\n }\n\n log('Adding batch transaction', txParams, networkClientId);\n\n const { transactionMeta, result } = await addTransaction(txParams, {\n nestedTransactions,\n networkClientId,\n requireApproval,\n type: TransactionType.batch,\n });\n\n const batchId = transactionMeta.id;\n\n // Wait for the transaction to be published.\n await result;\n\n return {\n batchId,\n };\n}\n\n/**\n * Determine which chains support atomic batch transactions for the given account.\n *\n * @param request - The request object including the account address and necessary callbacks.\n * @returns The chain IDs that support atomic batch transactions.\n */\nexport async function isAtomicBatchSupported(\n request: IsAtomicBatchSupportedRequest,\n): Promise<Hex[]> {\n const { address, getEthQuery, messenger } = request;\n\n const chainIds7702 = getEIP7702SupportedChains(messenger);\n const chainIds: Hex[] = [];\n\n for (const chainId of chainIds7702) {\n const ethQuery = getEthQuery(chainId);\n\n const { isSupported, delegationAddress } = await isAccountUpgradedToEIP7702(\n address,\n chainId,\n messenger,\n ethQuery,\n );\n\n if (!delegationAddress || isSupported) {\n chainIds.push(chainId);\n }\n }\n\n log('Atomic batch supported chains', chainIds);\n\n return chainIds;\n}\n"]}
1
+ {"version":3,"file":"batch.mjs","sourceRoot":"","sources":["../../src/utils/batch.ts"],"names":[],"mappings":"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","sourcesContent":["import type EthQuery from '@metamask/eth-query';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type { Hex } from '@metamask/utils';\nimport { bytesToHex, createModuleLogger } from '@metamask/utils';\nimport { parse, v4 } from 'uuid';\n\nimport {\n doesChainSupportEIP7702,\n generateEIP7702BatchTransaction,\n isAccountUpgradedToEIP7702,\n} from './eip7702';\nimport {\n getBatchSizeLimit,\n getEIP7702SupportedChains,\n getEIP7702UpgradeContractAddress,\n} from './feature-flags';\nimport { validateBatchRequest } from './validation';\nimport {\n determineTransactionType,\n type TransactionBatchRequest,\n type TransactionController,\n type TransactionControllerMessenger,\n} from '..';\nimport { projectLogger } from '../logger';\nimport type {\n NestedTransactionMetadata,\n TransactionBatchSingleRequest,\n} from '../types';\nimport {\n TransactionEnvelopeType,\n type TransactionBatchResult,\n type TransactionParams,\n TransactionType,\n} from '../types';\n\ntype AddTransactionBatchRequest = {\n addTransaction: TransactionController['addTransaction'];\n getChainId: (networkClientId: string) => Hex;\n getEthQuery: (networkClientId: string) => EthQuery;\n getInternalAccounts: () => Hex[];\n messenger: TransactionControllerMessenger;\n publicKeyEIP7702?: Hex;\n request: TransactionBatchRequest;\n};\n\ntype IsAtomicBatchSupportedRequest = {\n address: Hex;\n getEthQuery: (chainId: Hex) => EthQuery;\n messenger: TransactionControllerMessenger;\n publicKeyEIP7702?: Hex;\n};\n\nconst log = createModuleLogger(projectLogger, 'batch');\n\n/**\n * Add a batch transaction.\n *\n * @param request - The request object including the user request and necessary callbacks.\n * @returns The batch result object including the batch ID.\n */\nexport async function addTransactionBatch(\n request: AddTransactionBatchRequest,\n): Promise<TransactionBatchResult> {\n const {\n addTransaction,\n getChainId,\n getInternalAccounts,\n messenger,\n publicKeyEIP7702,\n request: userRequest,\n } = request;\n\n const sizeLimit = getBatchSizeLimit(messenger);\n\n validateBatchRequest({\n internalAccounts: getInternalAccounts(),\n request: userRequest,\n sizeLimit,\n });\n\n const {\n batchId: batchIdOverride,\n from,\n networkClientId,\n requireApproval,\n transactions,\n } = userRequest;\n\n log('Adding', userRequest);\n\n const chainId = getChainId(networkClientId);\n const ethQuery = request.getEthQuery(networkClientId);\n const isChainSupported = doesChainSupportEIP7702(chainId, messenger);\n\n if (!isChainSupported) {\n log('Chain does not support EIP-7702', chainId);\n throw rpcErrors.internal('Chain does not support EIP-7702');\n }\n\n if (!publicKeyEIP7702) {\n throw rpcErrors.internal('EIP-7702 public key not specified');\n }\n\n const { delegationAddress, isSupported } = await isAccountUpgradedToEIP7702(\n from,\n chainId,\n publicKeyEIP7702,\n messenger,\n ethQuery,\n );\n\n log('Account', { delegationAddress, isSupported });\n\n if (!isSupported && delegationAddress) {\n log('Account upgraded to unsupported contract', from, delegationAddress);\n throw rpcErrors.internal('Account upgraded to unsupported contract');\n }\n\n const nestedTransactions = await Promise.all(\n transactions.map((tx) =>\n getNestedTransactionMeta(userRequest, tx, ethQuery),\n ),\n );\n\n const batchParams = generateEIP7702BatchTransaction(from, nestedTransactions);\n\n const txParams: TransactionParams = {\n from,\n ...batchParams,\n };\n\n if (!isSupported) {\n const upgradeContractAddress = getEIP7702UpgradeContractAddress(\n chainId,\n messenger,\n publicKeyEIP7702,\n );\n\n if (!upgradeContractAddress) {\n throw rpcErrors.internal('Upgrade contract address not found');\n }\n\n txParams.type = TransactionEnvelopeType.setCode;\n txParams.authorizationList = [{ address: upgradeContractAddress }];\n }\n\n log('Adding batch transaction', txParams, networkClientId);\n\n const batchId = batchIdOverride ?? generateBatchId();\n\n const { result } = await addTransaction(txParams, {\n batchId,\n nestedTransactions,\n networkClientId,\n requireApproval,\n type: TransactionType.batch,\n });\n\n // Wait for the transaction to be published.\n await result;\n\n return {\n batchId,\n };\n}\n\n/**\n * Determine which chains support atomic batch transactions for the given account.\n *\n * @param request - The request object including the account address and necessary callbacks.\n * @returns The chain IDs that support atomic batch transactions.\n */\nexport async function isAtomicBatchSupported(\n request: IsAtomicBatchSupportedRequest,\n): Promise<Hex[]> {\n const {\n address,\n getEthQuery,\n messenger,\n publicKeyEIP7702: publicKey,\n } = request;\n\n if (!publicKey) {\n throw rpcErrors.internal('EIP-7702 public key not specified');\n }\n\n const chainIds7702 = getEIP7702SupportedChains(messenger);\n const chainIds: Hex[] = [];\n\n for (const chainId of chainIds7702) {\n const ethQuery = getEthQuery(chainId);\n\n const { isSupported, delegationAddress } = await isAccountUpgradedToEIP7702(\n address,\n chainId,\n publicKey,\n messenger,\n ethQuery,\n );\n\n if (!delegationAddress || isSupported) {\n chainIds.push(chainId);\n }\n }\n\n log('Atomic batch supported chains', chainIds);\n\n return chainIds;\n}\n\n/**\n * Generate a tranasction batch ID.\n *\n * @returns A unique batch ID as a hexadecimal string.\n */\nfunction generateBatchId(): Hex {\n const idString = v4();\n const idBytes = new Uint8Array(parse(idString));\n return bytesToHex(idBytes);\n}\n\n/**\n * Generate the metadata for a nested transaction.\n *\n * @param request - The batch request.\n * @param singleRequest - The request for a single transaction.\n * @param ethQuery - The EthQuery instance used to interact with the Ethereum blockchain.\n * @returns The metadata for the nested transaction.\n */\nasync function getNestedTransactionMeta(\n request: TransactionBatchRequest,\n singleRequest: TransactionBatchSingleRequest,\n ethQuery: EthQuery,\n): Promise<NestedTransactionMetadata> {\n const { from } = request;\n const { params } = singleRequest;\n\n const { type } = await determineTransactionType(\n { from, ...params },\n ethQuery,\n );\n\n return {\n ...params,\n type,\n };\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.signAuthorizationList = exports.generateEIP7702BatchTransaction = exports.isAccountUpgradedToEIP7702 = exports.doesChainSupportEIP7702 = exports.CALLS_SIGNATURE = exports.BATCH_FUNCTION_NAME = exports.DELEGATION_PREFIX = void 0;
3
+ exports.signAuthorizationList = exports.generateEIP7702BatchTransaction = exports.isAccountUpgradedToEIP7702 = exports.getDelegationAddress = exports.doesChainSupportEIP7702 = exports.CALLS_SIGNATURE = exports.BATCH_FUNCTION_NAME = exports.DELEGATION_PREFIX = void 0;
4
4
  const abi_1 = require("@ethersproject/abi");
5
5
  const contracts_1 = require("@ethersproject/contracts");
6
6
  const controller_utils_1 = require("@metamask/controller-utils");
@@ -25,22 +25,34 @@ function doesChainSupportEIP7702(chainId, messenger) {
25
25
  }
26
26
  exports.doesChainSupportEIP7702 = doesChainSupportEIP7702;
27
27
  /**
28
- * Determine if an account has been upgraded to a supported EIP-7702 contract.
28
+ * Retrieve the delegation address for an account.
29
29
  *
30
- * @param address - The EOA address to check.
31
- * @param chainId - The chain ID.
32
- * @param messenger - The messenger instance.
30
+ * @param address - The address to check.
33
31
  * @param ethQuery - The EthQuery instance to communicate with the blockchain.
34
- * @returns An object with the results of the check.
32
+ * @returns The delegation address if it exists.
35
33
  */
36
- async function isAccountUpgradedToEIP7702(address, chainId, messenger, ethQuery) {
37
- const contractAddresses = (0, feature_flags_1.getEIP7702ContractAddresses)(chainId, messenger);
34
+ async function getDelegationAddress(address, ethQuery) {
38
35
  const code = await (0, controller_utils_1.query)(ethQuery, 'eth_getCode', [address]);
39
36
  const normalizedCode = (0, utils_1.add0x)(code?.toLowerCase?.() ?? '');
40
37
  const hasDelegation = code?.length === 48 && normalizedCode.startsWith(exports.DELEGATION_PREFIX);
41
- const delegationAddress = hasDelegation
38
+ return hasDelegation
42
39
  ? (0, utils_1.add0x)(normalizedCode.slice(exports.DELEGATION_PREFIX.length))
43
40
  : undefined;
41
+ }
42
+ exports.getDelegationAddress = getDelegationAddress;
43
+ /**
44
+ * Determine if an account has been upgraded to a supported EIP-7702 contract.
45
+ *
46
+ * @param address - The EOA address to check.
47
+ * @param chainId - The chain ID.
48
+ * @param publicKey - Public key used to validate EIP-7702 contract signatures in feature flags.
49
+ * @param messenger - The messenger instance.
50
+ * @param ethQuery - The EthQuery instance to communicate with the blockchain.
51
+ * @returns An object with the results of the check.
52
+ */
53
+ async function isAccountUpgradedToEIP7702(address, chainId, publicKey, messenger, ethQuery) {
54
+ const contractAddresses = (0, feature_flags_1.getEIP7702ContractAddresses)(chainId, messenger, publicKey);
55
+ const delegationAddress = await getDelegationAddress(address, ethQuery);
44
56
  const isSupported = Boolean(delegationAddress &&
45
57
  contractAddresses.some((contract) => contract.toLowerCase() === delegationAddress.toLowerCase()));
46
58
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"eip7702.cjs","sourceRoot":"","sources":["../../src/utils/eip7702.ts"],"names":[],"mappings":"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","sourcesContent":["import { defaultAbiCoder } from '@ethersproject/abi';\nimport { Contract } from '@ethersproject/contracts';\nimport { query, toHex } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport { createModuleLogger, type Hex, add0x } from '@metamask/utils';\n\nimport {\n getEIP7702ContractAddresses,\n getEIP7702SupportedChains,\n} from './feature-flags';\nimport { ABI_IERC7821 } from '../constants';\nimport { projectLogger } from '../logger';\nimport type { TransactionControllerMessenger } from '../TransactionController';\nimport type {\n BatchTransactionParams,\n Authorization,\n AuthorizationList,\n TransactionMeta,\n} from '../types';\n\nexport const DELEGATION_PREFIX = '0xef0100';\nexport const BATCH_FUNCTION_NAME = 'execute';\nexport const CALLS_SIGNATURE = '(address,uint256,bytes)[]';\n\nconst log = createModuleLogger(projectLogger, 'eip-7702');\n\n/**\n * Determine if a chain supports EIP-7702 using LaunchDarkly feature flag.\n *\n * @param chainId - Hexadecimal ID of the chain.\n * @param messenger - Messenger instance.\n * @returns True if the chain supports EIP-7702.\n */\nexport function doesChainSupportEIP7702(\n chainId: Hex,\n messenger: TransactionControllerMessenger,\n) {\n const supportedChains = getEIP7702SupportedChains(messenger);\n\n return supportedChains.some(\n (supportedChainId) =>\n supportedChainId.toLowerCase() === chainId.toLowerCase(),\n );\n}\n\n/**\n * Determine if an account has been upgraded to a supported EIP-7702 contract.\n *\n * @param address - The EOA address to check.\n * @param chainId - The chain ID.\n * @param messenger - The messenger instance.\n * @param ethQuery - The EthQuery instance to communicate with the blockchain.\n * @returns An object with the results of the check.\n */\nexport async function isAccountUpgradedToEIP7702(\n address: Hex,\n chainId: Hex,\n messenger: TransactionControllerMessenger,\n ethQuery: EthQuery,\n) {\n const contractAddresses = getEIP7702ContractAddresses(chainId, messenger);\n const code = await query(ethQuery, 'eth_getCode', [address]);\n const normalizedCode = add0x(code?.toLowerCase?.() ?? '');\n\n const hasDelegation =\n code?.length === 48 && normalizedCode.startsWith(DELEGATION_PREFIX);\n\n const delegationAddress = hasDelegation\n ? add0x(normalizedCode.slice(DELEGATION_PREFIX.length))\n : undefined;\n\n const isSupported = Boolean(\n delegationAddress &&\n contractAddresses.some(\n (contract) =>\n contract.toLowerCase() === delegationAddress.toLowerCase(),\n ),\n );\n\n return {\n delegationAddress,\n isSupported,\n };\n}\n\n/**\n * Generate an EIP-7702 batch transaction.\n *\n * @param from - The sender address.\n * @param transactions - The transactions to batch.\n * @returns The batch transaction.\n */\nexport function generateEIP7702BatchTransaction(\n from: Hex,\n transactions: BatchTransactionParams[],\n): BatchTransactionParams {\n const erc7821Contract = Contract.getInterface(ABI_IERC7821);\n\n const calls = transactions.map((transaction) => {\n const { data, to, value } = transaction;\n\n return [\n to ?? '0x0000000000000000000000000000000000000000',\n value ?? '0x0',\n data ?? '0x',\n ];\n });\n\n // Single batch mode, no opData.\n const mode = '0x01'.padEnd(66, '0');\n\n const callData = defaultAbiCoder.encode([CALLS_SIGNATURE], [calls]);\n\n const data = erc7821Contract.encodeFunctionData(BATCH_FUNCTION_NAME, [\n mode,\n callData,\n ]) as Hex;\n\n log('Transaction data', data);\n\n return {\n data,\n to: from,\n };\n}\n\n/**\n * Sign an authorization list.\n *\n * @param options - Options bag.\n * @param options.authorizationList - The authorization list to sign.\n * @param options.messenger - The controller messenger.\n * @param options.transactionMeta - The transaction metadata.\n * @returns The signed authorization list.\n */\nexport async function signAuthorizationList({\n authorizationList,\n messenger,\n transactionMeta,\n}: {\n authorizationList?: AuthorizationList;\n messenger: TransactionControllerMessenger;\n transactionMeta: TransactionMeta;\n}): Promise<Required<AuthorizationList | undefined>> {\n if (!authorizationList) {\n return undefined;\n }\n\n const signedAuthorizationList: Required<AuthorizationList> = [];\n let index = 0;\n\n for (const authorization of authorizationList) {\n const signedAuthorization = await signAuthorization(\n authorization,\n transactionMeta,\n messenger,\n index,\n );\n\n signedAuthorizationList.push(signedAuthorization);\n index += 1;\n }\n\n return signedAuthorizationList;\n}\n\n/**\n * Signs an authorization.\n *\n * @param authorization - The authorization to sign.\n * @param transactionMeta - The associated transaction metadata.\n * @param messenger - The messenger to use for signing.\n * @param index - The index of the authorization in the list.\n * @returns The signed authorization.\n */\nasync function signAuthorization(\n authorization: Authorization,\n transactionMeta: TransactionMeta,\n messenger: TransactionControllerMessenger,\n index: number,\n): Promise<Required<Authorization>> {\n const finalAuthorization = prepareAuthorization(\n authorization,\n transactionMeta,\n index,\n );\n\n const { txParams } = transactionMeta;\n const { from } = txParams;\n const { address, chainId, nonce } = finalAuthorization;\n const chainIdDecimal = parseInt(chainId, 16);\n const nonceDecimal = parseInt(nonce, 16);\n\n const signature = await messenger.call(\n 'KeyringController:signEip7702Authorization',\n {\n chainId: chainIdDecimal,\n contractAddress: address,\n from,\n nonce: nonceDecimal,\n },\n );\n\n const r = signature.slice(0, 66) as Hex;\n const s = `0x${signature.slice(66, 130)}` as Hex;\n const v = parseInt(signature.slice(130, 132), 16);\n const yParity = v - 27 === 0 ? '0x' : '0x1';\n const finalNonce = nonceDecimal === 0 ? '0x' : nonce;\n\n const result: Required<Authorization> = {\n address,\n chainId,\n nonce: finalNonce,\n r,\n s,\n yParity,\n };\n\n log('Signed authorization', result);\n\n return result;\n}\n\n/**\n * Prepares an authorization for signing by populating the chainId and nonce.\n *\n * @param authorization - The authorization to prepare.\n * @param transactionMeta - The associated transaction metadata.\n * @param index - The index of the authorization in the list.\n * @returns The prepared authorization.\n */\nfunction prepareAuthorization(\n authorization: Authorization,\n transactionMeta: TransactionMeta,\n index: number,\n): Authorization & { chainId: Hex; nonce: Hex } {\n const { chainId: existingChainId, nonce: existingNonce } = authorization;\n const { txParams, chainId: transactionChainId } = transactionMeta;\n const { nonce: transactionNonce } = txParams;\n\n const chainId = existingChainId ?? transactionChainId;\n let nonce = existingNonce;\n\n if (nonce === undefined) {\n nonce = toHex(parseInt(transactionNonce as string, 16) + 1 + index);\n }\n\n const result = {\n ...authorization,\n chainId,\n nonce,\n };\n\n log('Prepared authorization', result);\n\n return result;\n}\n"]}
1
+ {"version":3,"file":"eip7702.cjs","sourceRoot":"","sources":["../../src/utils/eip7702.ts"],"names":[],"mappings":"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","sourcesContent":["import { defaultAbiCoder } from '@ethersproject/abi';\nimport { Contract } from '@ethersproject/contracts';\nimport { query, toHex } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport { createModuleLogger, type Hex, add0x } from '@metamask/utils';\n\nimport {\n getEIP7702ContractAddresses,\n getEIP7702SupportedChains,\n} from './feature-flags';\nimport { ABI_IERC7821 } from '../constants';\nimport { projectLogger } from '../logger';\nimport type { TransactionControllerMessenger } from '../TransactionController';\nimport type {\n BatchTransactionParams,\n Authorization,\n AuthorizationList,\n TransactionMeta,\n} from '../types';\n\nexport const DELEGATION_PREFIX = '0xef0100';\nexport const BATCH_FUNCTION_NAME = 'execute';\nexport const CALLS_SIGNATURE = '(address,uint256,bytes)[]';\n\nconst log = createModuleLogger(projectLogger, 'eip-7702');\n\n/**\n * Determine if a chain supports EIP-7702 using LaunchDarkly feature flag.\n *\n * @param chainId - Hexadecimal ID of the chain.\n * @param messenger - Messenger instance.\n * @returns True if the chain supports EIP-7702.\n */\nexport function doesChainSupportEIP7702(\n chainId: Hex,\n messenger: TransactionControllerMessenger,\n) {\n const supportedChains = getEIP7702SupportedChains(messenger);\n\n return supportedChains.some(\n (supportedChainId) =>\n supportedChainId.toLowerCase() === chainId.toLowerCase(),\n );\n}\n\n/**\n * Retrieve the delegation address for an account.\n *\n * @param address - The address to check.\n * @param ethQuery - The EthQuery instance to communicate with the blockchain.\n * @returns The delegation address if it exists.\n */\nexport async function getDelegationAddress(\n address: Hex,\n ethQuery: EthQuery,\n): Promise<Hex | undefined> {\n const code = await query(ethQuery, 'eth_getCode', [address]);\n const normalizedCode = add0x(code?.toLowerCase?.() ?? '');\n\n const hasDelegation =\n code?.length === 48 && normalizedCode.startsWith(DELEGATION_PREFIX);\n\n return hasDelegation\n ? add0x(normalizedCode.slice(DELEGATION_PREFIX.length))\n : undefined;\n}\n\n/**\n * Determine if an account has been upgraded to a supported EIP-7702 contract.\n *\n * @param address - The EOA address to check.\n * @param chainId - The chain ID.\n * @param publicKey - Public key used to validate EIP-7702 contract signatures in feature flags.\n * @param messenger - The messenger instance.\n * @param ethQuery - The EthQuery instance to communicate with the blockchain.\n * @returns An object with the results of the check.\n */\nexport async function isAccountUpgradedToEIP7702(\n address: Hex,\n chainId: Hex,\n publicKey: Hex,\n messenger: TransactionControllerMessenger,\n ethQuery: EthQuery,\n) {\n const contractAddresses = getEIP7702ContractAddresses(\n chainId,\n messenger,\n publicKey,\n );\n\n const delegationAddress = await getDelegationAddress(address, ethQuery);\n\n const isSupported = Boolean(\n delegationAddress &&\n contractAddresses.some(\n (contract) =>\n contract.toLowerCase() === delegationAddress.toLowerCase(),\n ),\n );\n\n return {\n delegationAddress,\n isSupported,\n };\n}\n\n/**\n * Generate an EIP-7702 batch transaction.\n *\n * @param from - The sender address.\n * @param transactions - The transactions to batch.\n * @returns The batch transaction.\n */\nexport function generateEIP7702BatchTransaction(\n from: Hex,\n transactions: BatchTransactionParams[],\n): BatchTransactionParams {\n const erc7821Contract = Contract.getInterface(ABI_IERC7821);\n\n const calls = transactions.map((transaction) => {\n const { data, to, value } = transaction;\n\n return [\n to ?? '0x0000000000000000000000000000000000000000',\n value ?? '0x0',\n data ?? '0x',\n ];\n });\n\n // Single batch mode, no opData.\n const mode = '0x01'.padEnd(66, '0');\n\n const callData = defaultAbiCoder.encode([CALLS_SIGNATURE], [calls]);\n\n const data = erc7821Contract.encodeFunctionData(BATCH_FUNCTION_NAME, [\n mode,\n callData,\n ]) as Hex;\n\n log('Transaction data', data);\n\n return {\n data,\n to: from,\n };\n}\n\n/**\n * Sign an authorization list.\n *\n * @param options - Options bag.\n * @param options.authorizationList - The authorization list to sign.\n * @param options.messenger - The controller messenger.\n * @param options.transactionMeta - The transaction metadata.\n * @returns The signed authorization list.\n */\nexport async function signAuthorizationList({\n authorizationList,\n messenger,\n transactionMeta,\n}: {\n authorizationList?: AuthorizationList;\n messenger: TransactionControllerMessenger;\n transactionMeta: TransactionMeta;\n}): Promise<Required<AuthorizationList | undefined>> {\n if (!authorizationList) {\n return undefined;\n }\n\n const signedAuthorizationList: Required<AuthorizationList> = [];\n let index = 0;\n\n for (const authorization of authorizationList) {\n const signedAuthorization = await signAuthorization(\n authorization,\n transactionMeta,\n messenger,\n index,\n );\n\n signedAuthorizationList.push(signedAuthorization);\n index += 1;\n }\n\n return signedAuthorizationList;\n}\n\n/**\n * Signs an authorization.\n *\n * @param authorization - The authorization to sign.\n * @param transactionMeta - The associated transaction metadata.\n * @param messenger - The messenger to use for signing.\n * @param index - The index of the authorization in the list.\n * @returns The signed authorization.\n */\nasync function signAuthorization(\n authorization: Authorization,\n transactionMeta: TransactionMeta,\n messenger: TransactionControllerMessenger,\n index: number,\n): Promise<Required<Authorization>> {\n const finalAuthorization = prepareAuthorization(\n authorization,\n transactionMeta,\n index,\n );\n\n const { txParams } = transactionMeta;\n const { from } = txParams;\n const { address, chainId, nonce } = finalAuthorization;\n const chainIdDecimal = parseInt(chainId, 16);\n const nonceDecimal = parseInt(nonce, 16);\n\n const signature = await messenger.call(\n 'KeyringController:signEip7702Authorization',\n {\n chainId: chainIdDecimal,\n contractAddress: address,\n from,\n nonce: nonceDecimal,\n },\n );\n\n const r = signature.slice(0, 66) as Hex;\n const s = `0x${signature.slice(66, 130)}` as Hex;\n const v = parseInt(signature.slice(130, 132), 16);\n const yParity = v - 27 === 0 ? '0x' : '0x1';\n const finalNonce = nonceDecimal === 0 ? '0x' : nonce;\n\n const result: Required<Authorization> = {\n address,\n chainId,\n nonce: finalNonce,\n r,\n s,\n yParity,\n };\n\n log('Signed authorization', result);\n\n return result;\n}\n\n/**\n * Prepares an authorization for signing by populating the chainId and nonce.\n *\n * @param authorization - The authorization to prepare.\n * @param transactionMeta - The associated transaction metadata.\n * @param index - The index of the authorization in the list.\n * @returns The prepared authorization.\n */\nfunction prepareAuthorization(\n authorization: Authorization,\n transactionMeta: TransactionMeta,\n index: number,\n): Authorization & { chainId: Hex; nonce: Hex } {\n const { chainId: existingChainId, nonce: existingNonce } = authorization;\n const { txParams, chainId: transactionChainId } = transactionMeta;\n const { nonce: transactionNonce } = txParams;\n\n const chainId = existingChainId ?? transactionChainId;\n let nonce = existingNonce;\n\n if (nonce === undefined) {\n nonce = toHex(parseInt(transactionNonce as string, 16) + 1 + index);\n }\n\n const result = {\n ...authorization,\n chainId,\n nonce,\n };\n\n log('Prepared authorization', result);\n\n return result;\n}\n"]}
@@ -13,16 +13,25 @@ export declare const CALLS_SIGNATURE = "(address,uint256,bytes)[]";
13
13
  * @returns True if the chain supports EIP-7702.
14
14
  */
15
15
  export declare function doesChainSupportEIP7702(chainId: Hex, messenger: TransactionControllerMessenger): boolean;
16
+ /**
17
+ * Retrieve the delegation address for an account.
18
+ *
19
+ * @param address - The address to check.
20
+ * @param ethQuery - The EthQuery instance to communicate with the blockchain.
21
+ * @returns The delegation address if it exists.
22
+ */
23
+ export declare function getDelegationAddress(address: Hex, ethQuery: EthQuery): Promise<Hex | undefined>;
16
24
  /**
17
25
  * Determine if an account has been upgraded to a supported EIP-7702 contract.
18
26
  *
19
27
  * @param address - The EOA address to check.
20
28
  * @param chainId - The chain ID.
29
+ * @param publicKey - Public key used to validate EIP-7702 contract signatures in feature flags.
21
30
  * @param messenger - The messenger instance.
22
31
  * @param ethQuery - The EthQuery instance to communicate with the blockchain.
23
32
  * @returns An object with the results of the check.
24
33
  */
25
- export declare function isAccountUpgradedToEIP7702(address: Hex, chainId: Hex, messenger: TransactionControllerMessenger, ethQuery: EthQuery): Promise<{
34
+ export declare function isAccountUpgradedToEIP7702(address: Hex, chainId: Hex, publicKey: Hex, messenger: TransactionControllerMessenger, ethQuery: EthQuery): Promise<{
26
35
  delegationAddress: `0x${string}` | undefined;
27
36
  isSupported: boolean;
28
37
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"eip7702.d.cts","sourceRoot":"","sources":["../../src/utils/eip7702.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,QAAQ,4BAA4B;AAChD,OAAO,EAAsB,KAAK,GAAG,EAAS,wBAAwB;AAQtE,OAAO,KAAK,EAAE,8BAA8B,EAAE,qCAAiC;AAC/E,OAAO,KAAK,EACV,sBAAsB,EAEtB,iBAAiB,EACjB,eAAe,EAChB,qBAAiB;AAElB,eAAO,MAAM,iBAAiB,aAAa,CAAC;AAC5C,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAC7C,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAI3D;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,GAAG,EACZ,SAAS,EAAE,8BAA8B,WAQ1C;AAED;;;;;;;;GAQG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,GAAG,EACZ,SAAS,EAAE,8BAA8B,EACzC,QAAQ,EAAE,QAAQ;;;GAyBnB;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,GAAG,EACT,YAAY,EAAE,sBAAsB,EAAE,GACrC,sBAAsB,CA6BxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,EAC1C,iBAAiB,EACjB,SAAS,EACT,eAAe,GAChB,EAAE;IACD,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,SAAS,EAAE,8BAA8B,CAAC;IAC1C,eAAe,EAAE,eAAe,CAAC;CAClC,GAAG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC,CAqBnD"}
1
+ {"version":3,"file":"eip7702.d.cts","sourceRoot":"","sources":["../../src/utils/eip7702.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,QAAQ,4BAA4B;AAChD,OAAO,EAAsB,KAAK,GAAG,EAAS,wBAAwB;AAQtE,OAAO,KAAK,EAAE,8BAA8B,EAAE,qCAAiC;AAC/E,OAAO,KAAK,EACV,sBAAsB,EAEtB,iBAAiB,EACjB,eAAe,EAChB,qBAAiB;AAElB,eAAO,MAAM,iBAAiB,aAAa,CAAC;AAC5C,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAC7C,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAI3D;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,GAAG,EACZ,SAAS,EAAE,8BAA8B,WAQ1C;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,GAAG,EACZ,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC,CAU1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,GAAG,EACZ,SAAS,EAAE,GAAG,EACd,SAAS,EAAE,8BAA8B,EACzC,QAAQ,EAAE,QAAQ;;;GAsBnB;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,GAAG,EACT,YAAY,EAAE,sBAAsB,EAAE,GACrC,sBAAsB,CA6BxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,EAC1C,iBAAiB,EACjB,SAAS,EACT,eAAe,GAChB,EAAE;IACD,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,SAAS,EAAE,8BAA8B,CAAC;IAC1C,eAAe,EAAE,eAAe,CAAC;CAClC,GAAG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC,CAqBnD"}
@@ -13,16 +13,25 @@ export declare const CALLS_SIGNATURE = "(address,uint256,bytes)[]";
13
13
  * @returns True if the chain supports EIP-7702.
14
14
  */
15
15
  export declare function doesChainSupportEIP7702(chainId: Hex, messenger: TransactionControllerMessenger): boolean;
16
+ /**
17
+ * Retrieve the delegation address for an account.
18
+ *
19
+ * @param address - The address to check.
20
+ * @param ethQuery - The EthQuery instance to communicate with the blockchain.
21
+ * @returns The delegation address if it exists.
22
+ */
23
+ export declare function getDelegationAddress(address: Hex, ethQuery: EthQuery): Promise<Hex | undefined>;
16
24
  /**
17
25
  * Determine if an account has been upgraded to a supported EIP-7702 contract.
18
26
  *
19
27
  * @param address - The EOA address to check.
20
28
  * @param chainId - The chain ID.
29
+ * @param publicKey - Public key used to validate EIP-7702 contract signatures in feature flags.
21
30
  * @param messenger - The messenger instance.
22
31
  * @param ethQuery - The EthQuery instance to communicate with the blockchain.
23
32
  * @returns An object with the results of the check.
24
33
  */
25
- export declare function isAccountUpgradedToEIP7702(address: Hex, chainId: Hex, messenger: TransactionControllerMessenger, ethQuery: EthQuery): Promise<{
34
+ export declare function isAccountUpgradedToEIP7702(address: Hex, chainId: Hex, publicKey: Hex, messenger: TransactionControllerMessenger, ethQuery: EthQuery): Promise<{
26
35
  delegationAddress: `0x${string}` | undefined;
27
36
  isSupported: boolean;
28
37
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"eip7702.d.mts","sourceRoot":"","sources":["../../src/utils/eip7702.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,QAAQ,4BAA4B;AAChD,OAAO,EAAsB,KAAK,GAAG,EAAS,wBAAwB;AAQtE,OAAO,KAAK,EAAE,8BAA8B,EAAE,qCAAiC;AAC/E,OAAO,KAAK,EACV,sBAAsB,EAEtB,iBAAiB,EACjB,eAAe,EAChB,qBAAiB;AAElB,eAAO,MAAM,iBAAiB,aAAa,CAAC;AAC5C,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAC7C,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAI3D;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,GAAG,EACZ,SAAS,EAAE,8BAA8B,WAQ1C;AAED;;;;;;;;GAQG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,GAAG,EACZ,SAAS,EAAE,8BAA8B,EACzC,QAAQ,EAAE,QAAQ;;;GAyBnB;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,GAAG,EACT,YAAY,EAAE,sBAAsB,EAAE,GACrC,sBAAsB,CA6BxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,EAC1C,iBAAiB,EACjB,SAAS,EACT,eAAe,GAChB,EAAE;IACD,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,SAAS,EAAE,8BAA8B,CAAC;IAC1C,eAAe,EAAE,eAAe,CAAC;CAClC,GAAG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC,CAqBnD"}
1
+ {"version":3,"file":"eip7702.d.mts","sourceRoot":"","sources":["../../src/utils/eip7702.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,QAAQ,4BAA4B;AAChD,OAAO,EAAsB,KAAK,GAAG,EAAS,wBAAwB;AAQtE,OAAO,KAAK,EAAE,8BAA8B,EAAE,qCAAiC;AAC/E,OAAO,KAAK,EACV,sBAAsB,EAEtB,iBAAiB,EACjB,eAAe,EAChB,qBAAiB;AAElB,eAAO,MAAM,iBAAiB,aAAa,CAAC;AAC5C,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAC7C,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAI3D;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,GAAG,EACZ,SAAS,EAAE,8BAA8B,WAQ1C;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,GAAG,EACZ,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC,CAU1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,GAAG,EACZ,SAAS,EAAE,GAAG,EACd,SAAS,EAAE,8BAA8B,EACzC,QAAQ,EAAE,QAAQ;;;GAsBnB;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,GAAG,EACT,YAAY,EAAE,sBAAsB,EAAE,GACrC,sBAAsB,CA6BxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,EAC1C,iBAAiB,EACjB,SAAS,EACT,eAAe,GAChB,EAAE;IACD,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,SAAS,EAAE,8BAA8B,CAAC;IAC1C,eAAe,EAAE,eAAe,CAAC;CAClC,GAAG,OAAO,CAAC,QAAQ,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC,CAqBnD"}
@@ -21,22 +21,33 @@ export function doesChainSupportEIP7702(chainId, messenger) {
21
21
  return supportedChains.some((supportedChainId) => supportedChainId.toLowerCase() === chainId.toLowerCase());
22
22
  }
23
23
  /**
24
- * Determine if an account has been upgraded to a supported EIP-7702 contract.
24
+ * Retrieve the delegation address for an account.
25
25
  *
26
- * @param address - The EOA address to check.
27
- * @param chainId - The chain ID.
28
- * @param messenger - The messenger instance.
26
+ * @param address - The address to check.
29
27
  * @param ethQuery - The EthQuery instance to communicate with the blockchain.
30
- * @returns An object with the results of the check.
28
+ * @returns The delegation address if it exists.
31
29
  */
32
- export async function isAccountUpgradedToEIP7702(address, chainId, messenger, ethQuery) {
33
- const contractAddresses = getEIP7702ContractAddresses(chainId, messenger);
30
+ export async function getDelegationAddress(address, ethQuery) {
34
31
  const code = await query(ethQuery, 'eth_getCode', [address]);
35
32
  const normalizedCode = add0x(code?.toLowerCase?.() ?? '');
36
33
  const hasDelegation = code?.length === 48 && normalizedCode.startsWith(DELEGATION_PREFIX);
37
- const delegationAddress = hasDelegation
34
+ return hasDelegation
38
35
  ? add0x(normalizedCode.slice(DELEGATION_PREFIX.length))
39
36
  : undefined;
37
+ }
38
+ /**
39
+ * Determine if an account has been upgraded to a supported EIP-7702 contract.
40
+ *
41
+ * @param address - The EOA address to check.
42
+ * @param chainId - The chain ID.
43
+ * @param publicKey - Public key used to validate EIP-7702 contract signatures in feature flags.
44
+ * @param messenger - The messenger instance.
45
+ * @param ethQuery - The EthQuery instance to communicate with the blockchain.
46
+ * @returns An object with the results of the check.
47
+ */
48
+ export async function isAccountUpgradedToEIP7702(address, chainId, publicKey, messenger, ethQuery) {
49
+ const contractAddresses = getEIP7702ContractAddresses(chainId, messenger, publicKey);
50
+ const delegationAddress = await getDelegationAddress(address, ethQuery);
40
51
  const isSupported = Boolean(delegationAddress &&
41
52
  contractAddresses.some((contract) => contract.toLowerCase() === delegationAddress.toLowerCase()));
42
53
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"eip7702.mjs","sourceRoot":"","sources":["../../src/utils/eip7702.ts"],"names":[],"mappings":"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","sourcesContent":["import { defaultAbiCoder } from '@ethersproject/abi';\nimport { Contract } from '@ethersproject/contracts';\nimport { query, toHex } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport { createModuleLogger, type Hex, add0x } from '@metamask/utils';\n\nimport {\n getEIP7702ContractAddresses,\n getEIP7702SupportedChains,\n} from './feature-flags';\nimport { ABI_IERC7821 } from '../constants';\nimport { projectLogger } from '../logger';\nimport type { TransactionControllerMessenger } from '../TransactionController';\nimport type {\n BatchTransactionParams,\n Authorization,\n AuthorizationList,\n TransactionMeta,\n} from '../types';\n\nexport const DELEGATION_PREFIX = '0xef0100';\nexport const BATCH_FUNCTION_NAME = 'execute';\nexport const CALLS_SIGNATURE = '(address,uint256,bytes)[]';\n\nconst log = createModuleLogger(projectLogger, 'eip-7702');\n\n/**\n * Determine if a chain supports EIP-7702 using LaunchDarkly feature flag.\n *\n * @param chainId - Hexadecimal ID of the chain.\n * @param messenger - Messenger instance.\n * @returns True if the chain supports EIP-7702.\n */\nexport function doesChainSupportEIP7702(\n chainId: Hex,\n messenger: TransactionControllerMessenger,\n) {\n const supportedChains = getEIP7702SupportedChains(messenger);\n\n return supportedChains.some(\n (supportedChainId) =>\n supportedChainId.toLowerCase() === chainId.toLowerCase(),\n );\n}\n\n/**\n * Determine if an account has been upgraded to a supported EIP-7702 contract.\n *\n * @param address - The EOA address to check.\n * @param chainId - The chain ID.\n * @param messenger - The messenger instance.\n * @param ethQuery - The EthQuery instance to communicate with the blockchain.\n * @returns An object with the results of the check.\n */\nexport async function isAccountUpgradedToEIP7702(\n address: Hex,\n chainId: Hex,\n messenger: TransactionControllerMessenger,\n ethQuery: EthQuery,\n) {\n const contractAddresses = getEIP7702ContractAddresses(chainId, messenger);\n const code = await query(ethQuery, 'eth_getCode', [address]);\n const normalizedCode = add0x(code?.toLowerCase?.() ?? '');\n\n const hasDelegation =\n code?.length === 48 && normalizedCode.startsWith(DELEGATION_PREFIX);\n\n const delegationAddress = hasDelegation\n ? add0x(normalizedCode.slice(DELEGATION_PREFIX.length))\n : undefined;\n\n const isSupported = Boolean(\n delegationAddress &&\n contractAddresses.some(\n (contract) =>\n contract.toLowerCase() === delegationAddress.toLowerCase(),\n ),\n );\n\n return {\n delegationAddress,\n isSupported,\n };\n}\n\n/**\n * Generate an EIP-7702 batch transaction.\n *\n * @param from - The sender address.\n * @param transactions - The transactions to batch.\n * @returns The batch transaction.\n */\nexport function generateEIP7702BatchTransaction(\n from: Hex,\n transactions: BatchTransactionParams[],\n): BatchTransactionParams {\n const erc7821Contract = Contract.getInterface(ABI_IERC7821);\n\n const calls = transactions.map((transaction) => {\n const { data, to, value } = transaction;\n\n return [\n to ?? '0x0000000000000000000000000000000000000000',\n value ?? '0x0',\n data ?? '0x',\n ];\n });\n\n // Single batch mode, no opData.\n const mode = '0x01'.padEnd(66, '0');\n\n const callData = defaultAbiCoder.encode([CALLS_SIGNATURE], [calls]);\n\n const data = erc7821Contract.encodeFunctionData(BATCH_FUNCTION_NAME, [\n mode,\n callData,\n ]) as Hex;\n\n log('Transaction data', data);\n\n return {\n data,\n to: from,\n };\n}\n\n/**\n * Sign an authorization list.\n *\n * @param options - Options bag.\n * @param options.authorizationList - The authorization list to sign.\n * @param options.messenger - The controller messenger.\n * @param options.transactionMeta - The transaction metadata.\n * @returns The signed authorization list.\n */\nexport async function signAuthorizationList({\n authorizationList,\n messenger,\n transactionMeta,\n}: {\n authorizationList?: AuthorizationList;\n messenger: TransactionControllerMessenger;\n transactionMeta: TransactionMeta;\n}): Promise<Required<AuthorizationList | undefined>> {\n if (!authorizationList) {\n return undefined;\n }\n\n const signedAuthorizationList: Required<AuthorizationList> = [];\n let index = 0;\n\n for (const authorization of authorizationList) {\n const signedAuthorization = await signAuthorization(\n authorization,\n transactionMeta,\n messenger,\n index,\n );\n\n signedAuthorizationList.push(signedAuthorization);\n index += 1;\n }\n\n return signedAuthorizationList;\n}\n\n/**\n * Signs an authorization.\n *\n * @param authorization - The authorization to sign.\n * @param transactionMeta - The associated transaction metadata.\n * @param messenger - The messenger to use for signing.\n * @param index - The index of the authorization in the list.\n * @returns The signed authorization.\n */\nasync function signAuthorization(\n authorization: Authorization,\n transactionMeta: TransactionMeta,\n messenger: TransactionControllerMessenger,\n index: number,\n): Promise<Required<Authorization>> {\n const finalAuthorization = prepareAuthorization(\n authorization,\n transactionMeta,\n index,\n );\n\n const { txParams } = transactionMeta;\n const { from } = txParams;\n const { address, chainId, nonce } = finalAuthorization;\n const chainIdDecimal = parseInt(chainId, 16);\n const nonceDecimal = parseInt(nonce, 16);\n\n const signature = await messenger.call(\n 'KeyringController:signEip7702Authorization',\n {\n chainId: chainIdDecimal,\n contractAddress: address,\n from,\n nonce: nonceDecimal,\n },\n );\n\n const r = signature.slice(0, 66) as Hex;\n const s = `0x${signature.slice(66, 130)}` as Hex;\n const v = parseInt(signature.slice(130, 132), 16);\n const yParity = v - 27 === 0 ? '0x' : '0x1';\n const finalNonce = nonceDecimal === 0 ? '0x' : nonce;\n\n const result: Required<Authorization> = {\n address,\n chainId,\n nonce: finalNonce,\n r,\n s,\n yParity,\n };\n\n log('Signed authorization', result);\n\n return result;\n}\n\n/**\n * Prepares an authorization for signing by populating the chainId and nonce.\n *\n * @param authorization - The authorization to prepare.\n * @param transactionMeta - The associated transaction metadata.\n * @param index - The index of the authorization in the list.\n * @returns The prepared authorization.\n */\nfunction prepareAuthorization(\n authorization: Authorization,\n transactionMeta: TransactionMeta,\n index: number,\n): Authorization & { chainId: Hex; nonce: Hex } {\n const { chainId: existingChainId, nonce: existingNonce } = authorization;\n const { txParams, chainId: transactionChainId } = transactionMeta;\n const { nonce: transactionNonce } = txParams;\n\n const chainId = existingChainId ?? transactionChainId;\n let nonce = existingNonce;\n\n if (nonce === undefined) {\n nonce = toHex(parseInt(transactionNonce as string, 16) + 1 + index);\n }\n\n const result = {\n ...authorization,\n chainId,\n nonce,\n };\n\n log('Prepared authorization', result);\n\n return result;\n}\n"]}
1
+ {"version":3,"file":"eip7702.mjs","sourceRoot":"","sources":["../../src/utils/eip7702.ts"],"names":[],"mappings":"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","sourcesContent":["import { defaultAbiCoder } from '@ethersproject/abi';\nimport { Contract } from '@ethersproject/contracts';\nimport { query, toHex } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport { createModuleLogger, type Hex, add0x } from '@metamask/utils';\n\nimport {\n getEIP7702ContractAddresses,\n getEIP7702SupportedChains,\n} from './feature-flags';\nimport { ABI_IERC7821 } from '../constants';\nimport { projectLogger } from '../logger';\nimport type { TransactionControllerMessenger } from '../TransactionController';\nimport type {\n BatchTransactionParams,\n Authorization,\n AuthorizationList,\n TransactionMeta,\n} from '../types';\n\nexport const DELEGATION_PREFIX = '0xef0100';\nexport const BATCH_FUNCTION_NAME = 'execute';\nexport const CALLS_SIGNATURE = '(address,uint256,bytes)[]';\n\nconst log = createModuleLogger(projectLogger, 'eip-7702');\n\n/**\n * Determine if a chain supports EIP-7702 using LaunchDarkly feature flag.\n *\n * @param chainId - Hexadecimal ID of the chain.\n * @param messenger - Messenger instance.\n * @returns True if the chain supports EIP-7702.\n */\nexport function doesChainSupportEIP7702(\n chainId: Hex,\n messenger: TransactionControllerMessenger,\n) {\n const supportedChains = getEIP7702SupportedChains(messenger);\n\n return supportedChains.some(\n (supportedChainId) =>\n supportedChainId.toLowerCase() === chainId.toLowerCase(),\n );\n}\n\n/**\n * Retrieve the delegation address for an account.\n *\n * @param address - The address to check.\n * @param ethQuery - The EthQuery instance to communicate with the blockchain.\n * @returns The delegation address if it exists.\n */\nexport async function getDelegationAddress(\n address: Hex,\n ethQuery: EthQuery,\n): Promise<Hex | undefined> {\n const code = await query(ethQuery, 'eth_getCode', [address]);\n const normalizedCode = add0x(code?.toLowerCase?.() ?? '');\n\n const hasDelegation =\n code?.length === 48 && normalizedCode.startsWith(DELEGATION_PREFIX);\n\n return hasDelegation\n ? add0x(normalizedCode.slice(DELEGATION_PREFIX.length))\n : undefined;\n}\n\n/**\n * Determine if an account has been upgraded to a supported EIP-7702 contract.\n *\n * @param address - The EOA address to check.\n * @param chainId - The chain ID.\n * @param publicKey - Public key used to validate EIP-7702 contract signatures in feature flags.\n * @param messenger - The messenger instance.\n * @param ethQuery - The EthQuery instance to communicate with the blockchain.\n * @returns An object with the results of the check.\n */\nexport async function isAccountUpgradedToEIP7702(\n address: Hex,\n chainId: Hex,\n publicKey: Hex,\n messenger: TransactionControllerMessenger,\n ethQuery: EthQuery,\n) {\n const contractAddresses = getEIP7702ContractAddresses(\n chainId,\n messenger,\n publicKey,\n );\n\n const delegationAddress = await getDelegationAddress(address, ethQuery);\n\n const isSupported = Boolean(\n delegationAddress &&\n contractAddresses.some(\n (contract) =>\n contract.toLowerCase() === delegationAddress.toLowerCase(),\n ),\n );\n\n return {\n delegationAddress,\n isSupported,\n };\n}\n\n/**\n * Generate an EIP-7702 batch transaction.\n *\n * @param from - The sender address.\n * @param transactions - The transactions to batch.\n * @returns The batch transaction.\n */\nexport function generateEIP7702BatchTransaction(\n from: Hex,\n transactions: BatchTransactionParams[],\n): BatchTransactionParams {\n const erc7821Contract = Contract.getInterface(ABI_IERC7821);\n\n const calls = transactions.map((transaction) => {\n const { data, to, value } = transaction;\n\n return [\n to ?? '0x0000000000000000000000000000000000000000',\n value ?? '0x0',\n data ?? '0x',\n ];\n });\n\n // Single batch mode, no opData.\n const mode = '0x01'.padEnd(66, '0');\n\n const callData = defaultAbiCoder.encode([CALLS_SIGNATURE], [calls]);\n\n const data = erc7821Contract.encodeFunctionData(BATCH_FUNCTION_NAME, [\n mode,\n callData,\n ]) as Hex;\n\n log('Transaction data', data);\n\n return {\n data,\n to: from,\n };\n}\n\n/**\n * Sign an authorization list.\n *\n * @param options - Options bag.\n * @param options.authorizationList - The authorization list to sign.\n * @param options.messenger - The controller messenger.\n * @param options.transactionMeta - The transaction metadata.\n * @returns The signed authorization list.\n */\nexport async function signAuthorizationList({\n authorizationList,\n messenger,\n transactionMeta,\n}: {\n authorizationList?: AuthorizationList;\n messenger: TransactionControllerMessenger;\n transactionMeta: TransactionMeta;\n}): Promise<Required<AuthorizationList | undefined>> {\n if (!authorizationList) {\n return undefined;\n }\n\n const signedAuthorizationList: Required<AuthorizationList> = [];\n let index = 0;\n\n for (const authorization of authorizationList) {\n const signedAuthorization = await signAuthorization(\n authorization,\n transactionMeta,\n messenger,\n index,\n );\n\n signedAuthorizationList.push(signedAuthorization);\n index += 1;\n }\n\n return signedAuthorizationList;\n}\n\n/**\n * Signs an authorization.\n *\n * @param authorization - The authorization to sign.\n * @param transactionMeta - The associated transaction metadata.\n * @param messenger - The messenger to use for signing.\n * @param index - The index of the authorization in the list.\n * @returns The signed authorization.\n */\nasync function signAuthorization(\n authorization: Authorization,\n transactionMeta: TransactionMeta,\n messenger: TransactionControllerMessenger,\n index: number,\n): Promise<Required<Authorization>> {\n const finalAuthorization = prepareAuthorization(\n authorization,\n transactionMeta,\n index,\n );\n\n const { txParams } = transactionMeta;\n const { from } = txParams;\n const { address, chainId, nonce } = finalAuthorization;\n const chainIdDecimal = parseInt(chainId, 16);\n const nonceDecimal = parseInt(nonce, 16);\n\n const signature = await messenger.call(\n 'KeyringController:signEip7702Authorization',\n {\n chainId: chainIdDecimal,\n contractAddress: address,\n from,\n nonce: nonceDecimal,\n },\n );\n\n const r = signature.slice(0, 66) as Hex;\n const s = `0x${signature.slice(66, 130)}` as Hex;\n const v = parseInt(signature.slice(130, 132), 16);\n const yParity = v - 27 === 0 ? '0x' : '0x1';\n const finalNonce = nonceDecimal === 0 ? '0x' : nonce;\n\n const result: Required<Authorization> = {\n address,\n chainId,\n nonce: finalNonce,\n r,\n s,\n yParity,\n };\n\n log('Signed authorization', result);\n\n return result;\n}\n\n/**\n * Prepares an authorization for signing by populating the chainId and nonce.\n *\n * @param authorization - The authorization to prepare.\n * @param transactionMeta - The associated transaction metadata.\n * @param index - The index of the authorization in the list.\n * @returns The prepared authorization.\n */\nfunction prepareAuthorization(\n authorization: Authorization,\n transactionMeta: TransactionMeta,\n index: number,\n): Authorization & { chainId: Hex; nonce: Hex } {\n const { chainId: existingChainId, nonce: existingNonce } = authorization;\n const { txParams, chainId: transactionChainId } = transactionMeta;\n const { nonce: transactionNonce } = txParams;\n\n const chainId = existingChainId ?? transactionChainId;\n let nonce = existingNonce;\n\n if (nonce === undefined) {\n nonce = toHex(parseInt(transactionNonce as string, 16) + 1 + index);\n }\n\n const result = {\n ...authorization,\n chainId,\n nonce,\n };\n\n log('Prepared authorization', result);\n\n return result;\n}\n"]}
@@ -1,9 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getEIP7702UpgradeContractAddress = exports.getEIP7702ContractAddresses = exports.getEIP7702SupportedChains = exports.FEATURE_FLAG_EIP_7702 = void 0;
3
+ exports.getBatchSizeLimit = exports.getEIP7702UpgradeContractAddress = exports.getEIP7702ContractAddresses = exports.getEIP7702SupportedChains = exports.FEATURE_FLAG_EIP_7702 = exports.FEATURE_FLAG_TRANSACTIONS = void 0;
4
4
  const utils_1 = require("@metamask/utils");
5
+ const signature_1 = require("./signature.cjs");
5
6
  const logger_1 = require("../logger.cjs");
7
+ exports.FEATURE_FLAG_TRANSACTIONS = 'confirmations-transactions';
6
8
  exports.FEATURE_FLAG_EIP_7702 = 'confirmations-eip-7702';
9
+ const DEFAULT_BATCH_SIZE_LIMIT = 10;
7
10
  const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'feature-flags');
8
11
  /**
9
12
  * Retrieves the supported EIP-7702 chains.
@@ -21,11 +24,15 @@ exports.getEIP7702SupportedChains = getEIP7702SupportedChains;
21
24
  *
22
25
  * @param chainId - The chain ID.
23
26
  * @param messenger - The controller messenger instance.
27
+ * @param publicKey - The public key used to validate the contract authenticity.
24
28
  * @returns The supported contract addresses.
25
29
  */
26
- function getEIP7702ContractAddresses(chainId, messenger) {
30
+ function getEIP7702ContractAddresses(chainId, messenger, publicKey) {
27
31
  const featureFlags = getFeatureFlags(messenger);
28
- return (featureFlags?.[exports.FEATURE_FLAG_EIP_7702]?.contractAddresses?.[chainId.toLowerCase()] ?? []);
32
+ const contracts = featureFlags?.[exports.FEATURE_FLAG_EIP_7702]?.contracts?.[chainId.toLowerCase()] ?? [];
33
+ return contracts
34
+ .filter((contract) => (0, signature_1.isValidSignature)([contract.address, chainId], contract.signature, publicKey))
35
+ .map((contract) => contract.address);
29
36
  }
30
37
  exports.getEIP7702ContractAddresses = getEIP7702ContractAddresses;
31
38
  /**
@@ -33,12 +40,26 @@ exports.getEIP7702ContractAddresses = getEIP7702ContractAddresses;
33
40
  *
34
41
  * @param chainId - The chain ID.
35
42
  * @param messenger - The controller messenger instance.
43
+ * @param publicKey - The public key used to validate the contract authenticity.
36
44
  * @returns The upgrade contract address.
37
45
  */
38
- function getEIP7702UpgradeContractAddress(chainId, messenger) {
39
- return getEIP7702ContractAddresses(chainId, messenger)?.[0];
46
+ function getEIP7702UpgradeContractAddress(chainId, messenger, publicKey) {
47
+ return getEIP7702ContractAddresses(chainId, messenger, publicKey)?.[0];
40
48
  }
41
49
  exports.getEIP7702UpgradeContractAddress = getEIP7702UpgradeContractAddress;
50
+ /**
51
+ * Retrieves the batch size limit.
52
+ * Defaults to 10 if not set.
53
+ *
54
+ * @param messenger - The controller messenger instance.
55
+ * @returns The batch size limit.
56
+ */
57
+ function getBatchSizeLimit(messenger) {
58
+ const featureFlags = getFeatureFlags(messenger);
59
+ return (featureFlags?.[exports.FEATURE_FLAG_TRANSACTIONS]?.batchSizeLimit ??
60
+ DEFAULT_BATCH_SIZE_LIMIT);
61
+ }
62
+ exports.getBatchSizeLimit = getBatchSizeLimit;
42
63
  /**
43
64
  * Retrieves the relevant feature flags from the remote feature flag controller.
44
65
  *
@@ -1 +1 @@
1
- {"version":3,"file":"feature-flags.cjs","sourceRoot":"","sources":["../../src/utils/feature-flags.ts"],"names":[],"mappings":";;;AAAA,2CAA+D;AAE/D,0CAA0C;AAG7B,QAAA,qBAAqB,GAAG,wBAAwB,CAAC;AAgB9D,MAAM,GAAG,GAAG,IAAA,0BAAkB,EAAC,sBAAa,EAAE,eAAe,CAAC,CAAC;AAE/D;;;;;GAKG;AACH,SAAgB,yBAAyB,CACvC,SAAyC;IAEzC,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAChD,OAAO,YAAY,EAAE,CAAC,6BAAqB,CAAC,EAAE,eAAe,IAAI,EAAE,CAAC;AACtE,CAAC;AALD,8DAKC;AAED;;;;;;GAMG;AACH,SAAgB,2BAA2B,CACzC,OAAY,EACZ,SAAyC;IAEzC,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAEhD,OAAO,CACL,YAAY,EAAE,CAAC,6BAAqB,CAAC,EAAE,iBAAiB,EAAE,CACxD,OAAO,CAAC,WAAW,EAAS,CAC7B,IAAI,EAAE,CACR,CAAC;AACJ,CAAC;AAXD,kEAWC;AAED;;;;;;GAMG;AACH,SAAgB,gCAAgC,CAC9C,OAAY,EACZ,SAAyC;IAEzC,OAAO,2BAA2B,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AALD,4EAKC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CACtB,SAAyC;IAEzC,MAAM,YAAY,GAAG,SAAS,CAAC,IAAI,CACjC,sCAAsC,CACvC,CAAC,kBAAkB,CAAC;IAErB,GAAG,CAAC,yBAAyB,EAAE,YAAY,CAAC,CAAC;IAE7C,OAAO,YAAiD,CAAC;AAC3D,CAAC","sourcesContent":["import { createModuleLogger, type Hex } from '@metamask/utils';\n\nimport { projectLogger } from '../logger';\nimport type { TransactionControllerMessenger } from '../TransactionController';\n\nexport const FEATURE_FLAG_EIP_7702 = 'confirmations-eip-7702';\n\nexport type TransactionControllerFeatureFlags = {\n [FEATURE_FLAG_EIP_7702]: {\n /**\n * All contract addresses that support EIP-7702 batch transactions.\n * Keyed by chain ID.\n * First address in each array is the contract that standard EOAs will be upgraded to.\n */\n contractAddresses: Record<Hex, Hex[]>;\n\n /** Chains enabled for EIP-7702 batch transactions. */\n supportedChains: Hex[];\n };\n};\n\nconst log = createModuleLogger(projectLogger, 'feature-flags');\n\n/**\n * Retrieves the supported EIP-7702 chains.\n *\n * @param messenger - The controller messenger instance.\n * @returns The supported chains.\n */\nexport function getEIP7702SupportedChains(\n messenger: TransactionControllerMessenger,\n): Hex[] {\n const featureFlags = getFeatureFlags(messenger);\n return featureFlags?.[FEATURE_FLAG_EIP_7702]?.supportedChains ?? [];\n}\n\n/**\n * Retrieves the supported EIP-7702 contract addresses for a given chain ID.\n *\n * @param chainId - The chain ID.\n * @param messenger - The controller messenger instance.\n * @returns The supported contract addresses.\n */\nexport function getEIP7702ContractAddresses(\n chainId: Hex,\n messenger: TransactionControllerMessenger,\n): Hex[] {\n const featureFlags = getFeatureFlags(messenger);\n\n return (\n featureFlags?.[FEATURE_FLAG_EIP_7702]?.contractAddresses?.[\n chainId.toLowerCase() as Hex\n ] ?? []\n );\n}\n\n/**\n * Retrieves the EIP-7702 upgrade contract address.\n *\n * @param chainId - The chain ID.\n * @param messenger - The controller messenger instance.\n * @returns The upgrade contract address.\n */\nexport function getEIP7702UpgradeContractAddress(\n chainId: Hex,\n messenger: TransactionControllerMessenger,\n): Hex | undefined {\n return getEIP7702ContractAddresses(chainId, messenger)?.[0];\n}\n\n/**\n * Retrieves the relevant feature flags from the remote feature flag controller.\n *\n * @param messenger - The messenger instance.\n * @returns The feature flags.\n */\nfunction getFeatureFlags(\n messenger: TransactionControllerMessenger,\n): TransactionControllerFeatureFlags {\n const featureFlags = messenger.call(\n 'RemoteFeatureFlagController:getState',\n ).remoteFeatureFlags;\n\n log('Retrieved feature flags', featureFlags);\n\n return featureFlags as TransactionControllerFeatureFlags;\n}\n"]}
1
+ {"version":3,"file":"feature-flags.cjs","sourceRoot":"","sources":["../../src/utils/feature-flags.ts"],"names":[],"mappings":";;;AAAA,2CAA+D;AAE/D,+CAA+C;AAC/C,0CAA0C;AAG7B,QAAA,yBAAyB,GAAG,4BAA4B,CAAC;AACzD,QAAA,qBAAqB,GAAG,wBAAwB,CAAC;AAE9D,MAAM,wBAAwB,GAAG,EAAE,CAAC;AA8BpC,MAAM,GAAG,GAAG,IAAA,0BAAkB,EAAC,sBAAa,EAAE,eAAe,CAAC,CAAC;AAE/D;;;;;GAKG;AACH,SAAgB,yBAAyB,CACvC,SAAyC;IAEzC,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAChD,OAAO,YAAY,EAAE,CAAC,6BAAqB,CAAC,EAAE,eAAe,IAAI,EAAE,CAAC;AACtE,CAAC;AALD,8DAKC;AAED;;;;;;;GAOG;AACH,SAAgB,2BAA2B,CACzC,OAAY,EACZ,SAAyC,EACzC,SAAc;IAEd,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAEhD,MAAM,SAAS,GACb,YAAY,EAAE,CAAC,6BAAqB,CAAC,EAAE,SAAS,EAAE,CAChD,OAAO,CAAC,WAAW,EAAS,CAC7B,IAAI,EAAE,CAAC;IAEV,OAAO,SAAS;SACb,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CACnB,IAAA,4BAAgB,EACd,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,EAC3B,QAAQ,CAAC,SAAS,EAClB,SAAS,CACV,CACF;SACA,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACzC,CAAC;AArBD,kEAqBC;AAED;;;;;;;GAOG;AACH,SAAgB,gCAAgC,CAC9C,OAAY,EACZ,SAAyC,EACzC,SAAc;IAEd,OAAO,2BAA2B,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACzE,CAAC;AAND,4EAMC;AAED;;;;;;GAMG;AACH,SAAgB,iBAAiB,CAC/B,SAAyC;IAEzC,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAChD,OAAO,CACL,YAAY,EAAE,CAAC,iCAAyB,CAAC,EAAE,cAAc;QACzD,wBAAwB,CACzB,CAAC;AACJ,CAAC;AARD,8CAQC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CACtB,SAAyC;IAEzC,MAAM,YAAY,GAAG,SAAS,CAAC,IAAI,CACjC,sCAAsC,CACvC,CAAC,kBAAkB,CAAC;IAErB,GAAG,CAAC,yBAAyB,EAAE,YAAY,CAAC,CAAC;IAE7C,OAAO,YAAiD,CAAC;AAC3D,CAAC","sourcesContent":["import { createModuleLogger, type Hex } from '@metamask/utils';\n\nimport { isValidSignature } from './signature';\nimport { projectLogger } from '../logger';\nimport type { TransactionControllerMessenger } from '../TransactionController';\n\nexport const FEATURE_FLAG_TRANSACTIONS = 'confirmations-transactions';\nexport const FEATURE_FLAG_EIP_7702 = 'confirmations-eip-7702';\n\nconst DEFAULT_BATCH_SIZE_LIMIT = 10;\n\nexport type TransactionControllerFeatureFlags = {\n [FEATURE_FLAG_EIP_7702]?: {\n /**\n * All contracts that support EIP-7702 batch transactions.\n * Keyed by chain ID.\n * First entry in each array is the contract that standard EOAs will be upgraded to.\n */\n contracts?: Record<\n Hex,\n {\n /** Address of the smart contract. */\n address: Hex;\n\n /** Signature to verify the contract is authentic. */\n signature: Hex;\n }[]\n >;\n\n /** Chains enabled for EIP-7702 batch transactions. */\n supportedChains?: Hex[];\n };\n\n [FEATURE_FLAG_TRANSACTIONS]?: {\n /** Maximum number of transactions that can be in an external batch. */\n batchSizeLimit?: number;\n };\n};\n\nconst log = createModuleLogger(projectLogger, 'feature-flags');\n\n/**\n * Retrieves the supported EIP-7702 chains.\n *\n * @param messenger - The controller messenger instance.\n * @returns The supported chains.\n */\nexport function getEIP7702SupportedChains(\n messenger: TransactionControllerMessenger,\n): Hex[] {\n const featureFlags = getFeatureFlags(messenger);\n return featureFlags?.[FEATURE_FLAG_EIP_7702]?.supportedChains ?? [];\n}\n\n/**\n * Retrieves the supported EIP-7702 contract addresses for a given chain ID.\n *\n * @param chainId - The chain ID.\n * @param messenger - The controller messenger instance.\n * @param publicKey - The public key used to validate the contract authenticity.\n * @returns The supported contract addresses.\n */\nexport function getEIP7702ContractAddresses(\n chainId: Hex,\n messenger: TransactionControllerMessenger,\n publicKey: Hex,\n): Hex[] {\n const featureFlags = getFeatureFlags(messenger);\n\n const contracts =\n featureFlags?.[FEATURE_FLAG_EIP_7702]?.contracts?.[\n chainId.toLowerCase() as Hex\n ] ?? [];\n\n return contracts\n .filter((contract) =>\n isValidSignature(\n [contract.address, chainId],\n contract.signature,\n publicKey,\n ),\n )\n .map((contract) => contract.address);\n}\n\n/**\n * Retrieves the EIP-7702 upgrade contract address.\n *\n * @param chainId - The chain ID.\n * @param messenger - The controller messenger instance.\n * @param publicKey - The public key used to validate the contract authenticity.\n * @returns The upgrade contract address.\n */\nexport function getEIP7702UpgradeContractAddress(\n chainId: Hex,\n messenger: TransactionControllerMessenger,\n publicKey: Hex,\n): Hex | undefined {\n return getEIP7702ContractAddresses(chainId, messenger, publicKey)?.[0];\n}\n\n/**\n * Retrieves the batch size limit.\n * Defaults to 10 if not set.\n *\n * @param messenger - The controller messenger instance.\n * @returns The batch size limit.\n */\nexport function getBatchSizeLimit(\n messenger: TransactionControllerMessenger,\n): number {\n const featureFlags = getFeatureFlags(messenger);\n return (\n featureFlags?.[FEATURE_FLAG_TRANSACTIONS]?.batchSizeLimit ??\n DEFAULT_BATCH_SIZE_LIMIT\n );\n}\n\n/**\n * Retrieves the relevant feature flags from the remote feature flag controller.\n *\n * @param messenger - The messenger instance.\n * @returns The feature flags.\n */\nfunction getFeatureFlags(\n messenger: TransactionControllerMessenger,\n): TransactionControllerFeatureFlags {\n const featureFlags = messenger.call(\n 'RemoteFeatureFlagController:getState',\n ).remoteFeatureFlags;\n\n log('Retrieved feature flags', featureFlags);\n\n return featureFlags as TransactionControllerFeatureFlags;\n}\n"]}