@metamask-previews/transaction-controller 45.0.0-preview-1adbaffc → 45.1.0-preview-ee98fad1

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 (86) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/dist/TransactionController.cjs +85 -67
  3. package/dist/TransactionController.cjs.map +1 -1
  4. package/dist/TransactionController.d.cts +22 -17
  5. package/dist/TransactionController.d.cts.map +1 -1
  6. package/dist/TransactionController.d.mts +22 -17
  7. package/dist/TransactionController.d.mts.map +1 -1
  8. package/dist/TransactionController.mjs +84 -66
  9. package/dist/TransactionController.mjs.map +1 -1
  10. package/dist/abi/IERC7821.cjs +23 -0
  11. package/dist/abi/IERC7821.cjs.map +1 -0
  12. package/dist/abi/IERC7821.d.cts +16 -0
  13. package/dist/abi/IERC7821.d.cts.map +1 -0
  14. package/dist/abi/IERC7821.d.mts +16 -0
  15. package/dist/abi/IERC7821.d.mts.map +1 -0
  16. package/dist/abi/IERC7821.mjs +20 -0
  17. package/dist/abi/IERC7821.mjs.map +1 -0
  18. package/dist/gas-flows/OracleLayer1GasFeeFlow.cjs +4 -13
  19. package/dist/gas-flows/OracleLayer1GasFeeFlow.cjs.map +1 -1
  20. package/dist/gas-flows/OracleLayer1GasFeeFlow.d.cts.map +1 -1
  21. package/dist/gas-flows/OracleLayer1GasFeeFlow.d.mts.map +1 -1
  22. package/dist/gas-flows/OracleLayer1GasFeeFlow.mjs +4 -13
  23. package/dist/gas-flows/OracleLayer1GasFeeFlow.mjs.map +1 -1
  24. package/dist/index.cjs +3 -2
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +3 -2
  27. package/dist/index.d.cts.map +1 -1
  28. package/dist/index.d.mts +3 -2
  29. package/dist/index.d.mts.map +1 -1
  30. package/dist/index.mjs +2 -1
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/types.cjs +11 -37
  33. package/dist/types.cjs.map +1 -1
  34. package/dist/types.d.cts +98 -1
  35. package/dist/types.d.cts.map +1 -1
  36. package/dist/types.d.mts +98 -1
  37. package/dist/types.d.mts.map +1 -1
  38. package/dist/types.mjs +11 -37
  39. package/dist/types.mjs.map +1 -1
  40. package/dist/utils/batch.cjs +84 -0
  41. package/dist/utils/batch.cjs.map +1 -0
  42. package/dist/utils/batch.d.cts +32 -0
  43. package/dist/utils/batch.d.cts.map +1 -0
  44. package/dist/utils/batch.d.mts +32 -0
  45. package/dist/utils/batch.d.mts.map +1 -0
  46. package/dist/utils/batch.mjs +79 -0
  47. package/dist/utils/batch.mjs.map +1 -0
  48. package/dist/utils/eip7702.cjs +160 -0
  49. package/dist/utils/eip7702.cjs.map +1 -0
  50. package/dist/utils/eip7702.d.cts +63 -0
  51. package/dist/utils/eip7702.d.cts.map +1 -0
  52. package/dist/utils/eip7702.d.mts +63 -0
  53. package/dist/utils/eip7702.d.mts.map +1 -0
  54. package/dist/utils/eip7702.mjs +153 -0
  55. package/dist/utils/eip7702.mjs.map +1 -0
  56. package/dist/utils/feature-flags.cjs +53 -0
  57. package/dist/utils/feature-flags.cjs.map +1 -0
  58. package/dist/utils/feature-flags.d.cts +34 -0
  59. package/dist/utils/feature-flags.d.cts.map +1 -0
  60. package/dist/utils/feature-flags.d.mts +34 -0
  61. package/dist/utils/feature-flags.d.mts.map +1 -0
  62. package/dist/utils/feature-flags.mjs +47 -0
  63. package/dist/utils/feature-flags.mjs.map +1 -0
  64. package/dist/utils/prepare.cjs +49 -0
  65. package/dist/utils/prepare.cjs.map +1 -0
  66. package/dist/utils/prepare.d.cts +21 -0
  67. package/dist/utils/prepare.d.cts.map +1 -0
  68. package/dist/utils/prepare.d.mts +21 -0
  69. package/dist/utils/prepare.d.mts.map +1 -0
  70. package/dist/utils/prepare.mjs +44 -0
  71. package/dist/utils/prepare.mjs.map +1 -0
  72. package/dist/utils/utils.cjs +3 -8
  73. package/dist/utils/utils.cjs.map +1 -1
  74. package/dist/utils/utils.d.cts.map +1 -1
  75. package/dist/utils/utils.d.mts.map +1 -1
  76. package/dist/utils/utils.mjs +3 -8
  77. package/dist/utils/utils.mjs.map +1 -1
  78. package/dist/utils/validation.cjs +102 -31
  79. package/dist/utils/validation.cjs.map +1 -1
  80. package/dist/utils/validation.d.cts +18 -6
  81. package/dist/utils/validation.d.cts.map +1 -1
  82. package/dist/utils/validation.d.mts +18 -6
  83. package/dist/utils/validation.d.mts.map +1 -1
  84. package/dist/utils/validation.mjs +103 -32
  85. package/dist/utils/validation.mjs.map +1 -1
  86. package/package.json +11 -9
@@ -0,0 +1,79 @@
1
+ import { rpcErrors } from "@metamask/rpc-errors";
2
+ import { createModuleLogger } from "@metamask/utils";
3
+ import { doesChainSupportEIP7702, generateEIP7702BatchTransaction, isAccountUpgradedToEIP7702 } from "./eip7702.mjs";
4
+ import { getEIP7702SupportedChains, getEIP7702UpgradeContractAddress } from "./feature-flags.mjs";
5
+ import { projectLogger } from "../logger.mjs";
6
+ import { TransactionEnvelopeType, TransactionType } from "../types.mjs";
7
+ const log = createModuleLogger(projectLogger, 'batch');
8
+ /**
9
+ * Add a batch transaction.
10
+ *
11
+ * @param request - The request object including the user request and necessary callbacks.
12
+ * @returns The batch result object including the batch ID.
13
+ */
14
+ export async function addTransactionBatch(request) {
15
+ const { addTransaction, getChainId, messenger, request: userRequest, } = request;
16
+ const { from, networkClientId, requireApproval, transactions } = userRequest;
17
+ log('Adding', userRequest);
18
+ const chainId = getChainId(networkClientId);
19
+ const ethQuery = request.getEthQuery(networkClientId);
20
+ const isChainSupported = doesChainSupportEIP7702(chainId, messenger);
21
+ if (!isChainSupported) {
22
+ log('Chain does not support EIP-7702', chainId);
23
+ throw rpcErrors.internal('Chain does not support EIP-7702');
24
+ }
25
+ const { delegationAddress, isSupported } = await isAccountUpgradedToEIP7702(from, chainId, messenger, ethQuery);
26
+ log('Account', { delegationAddress, isSupported });
27
+ if (!isSupported && delegationAddress) {
28
+ log('Account upgraded to unsupported contract', from, delegationAddress);
29
+ throw rpcErrors.internal('Account upgraded to unsupported contract');
30
+ }
31
+ const nestedTransactions = transactions.map((tx) => tx.params);
32
+ const batchParams = generateEIP7702BatchTransaction(from, nestedTransactions);
33
+ const txParams = {
34
+ from,
35
+ ...batchParams,
36
+ };
37
+ if (!isSupported) {
38
+ const upgradeContractAddress = getEIP7702UpgradeContractAddress(chainId, messenger);
39
+ if (!upgradeContractAddress) {
40
+ throw rpcErrors.internal('Upgrade contract address not found');
41
+ }
42
+ txParams.type = TransactionEnvelopeType.setCode;
43
+ txParams.authorizationList = [{ address: upgradeContractAddress }];
44
+ }
45
+ log('Adding batch transaction', txParams, networkClientId);
46
+ const { transactionMeta, result } = await addTransaction(txParams, {
47
+ nestedTransactions,
48
+ networkClientId,
49
+ requireApproval,
50
+ type: TransactionType.batch,
51
+ });
52
+ const batchId = transactionMeta.id;
53
+ // Wait for the transaction to be published.
54
+ await result;
55
+ return {
56
+ batchId,
57
+ };
58
+ }
59
+ /**
60
+ * Determine which chains support atomic batch transactions for the given account.
61
+ *
62
+ * @param request - The request object including the account address and necessary callbacks.
63
+ * @returns The chain IDs that support atomic batch transactions.
64
+ */
65
+ export async function isAtomicBatchSupported(request) {
66
+ const { address, getEthQuery, messenger } = request;
67
+ const chainIds7702 = getEIP7702SupportedChains(messenger);
68
+ const chainIds = [];
69
+ for (const chainId of chainIds7702) {
70
+ const ethQuery = getEthQuery(chainId);
71
+ const { isSupported, delegationAddress } = await isAccountUpgradedToEIP7702(address, chainId, messenger, ethQuery);
72
+ if (!delegationAddress || isSupported) {
73
+ chainIds.push(chainId);
74
+ }
75
+ }
76
+ log('Atomic batch supported chains', chainIds);
77
+ return chainIds;
78
+ }
79
+ //# sourceMappingURL=batch.mjs.map
@@ -0,0 +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 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 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 messenger,\n request: userRequest,\n } = request;\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"]}
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.signAuthorizationList = exports.generateEIP7702BatchTransaction = exports.isAccountUpgradedToEIP7702 = exports.doesChainSupportEIP7702 = exports.BATCH_FUNCTION_NAME = exports.DELEGATION_PREFIX = void 0;
4
+ const abi_1 = require("@ethersproject/abi");
5
+ const contracts_1 = require("@ethersproject/contracts");
6
+ const controller_utils_1 = require("@metamask/controller-utils");
7
+ const utils_1 = require("@metamask/utils");
8
+ const feature_flags_1 = require("./feature-flags.cjs");
9
+ const IERC7821_1 = require("../abi/IERC7821.cjs");
10
+ const logger_1 = require("../logger.cjs");
11
+ exports.DELEGATION_PREFIX = '0xef0100';
12
+ exports.BATCH_FUNCTION_NAME = 'execute';
13
+ const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'eip-7702');
14
+ /**
15
+ * Determine if a chain supports EIP-7702 using LaunchDarkly feature flag.
16
+ *
17
+ * @param chainId - Hexadecimal ID of the chain.
18
+ * @param messenger - Messenger instance.
19
+ * @returns True if the chain supports EIP-7702.
20
+ */
21
+ function doesChainSupportEIP7702(chainId, messenger) {
22
+ const supportedChains = (0, feature_flags_1.getEIP7702SupportedChains)(messenger);
23
+ return supportedChains.some((supportedChainId) => supportedChainId.toLowerCase() === chainId.toLowerCase());
24
+ }
25
+ exports.doesChainSupportEIP7702 = doesChainSupportEIP7702;
26
+ /**
27
+ * Determine if an account has been upgraded to a supported EIP-7702 contract.
28
+ *
29
+ * @param address - The EOA address to check.
30
+ * @param chainId - The chain ID.
31
+ * @param messenger - The messenger instance.
32
+ * @param ethQuery - The EthQuery instance to communicate with the blockchain.
33
+ * @returns An object with the results of the check.
34
+ */
35
+ async function isAccountUpgradedToEIP7702(address, chainId, messenger, ethQuery) {
36
+ const contractAddresses = (0, feature_flags_1.getEIP7702ContractAddresses)(chainId, messenger);
37
+ const code = await (0, controller_utils_1.query)(ethQuery, 'eth_getCode', [address]);
38
+ const normalizedCode = (0, utils_1.add0x)(code?.toLowerCase?.() ?? '');
39
+ const hasDelegation = code?.length === 48 && normalizedCode.startsWith(exports.DELEGATION_PREFIX);
40
+ const delegationAddress = hasDelegation
41
+ ? (0, utils_1.add0x)(normalizedCode.slice(exports.DELEGATION_PREFIX.length))
42
+ : undefined;
43
+ const isSupported = Boolean(delegationAddress &&
44
+ contractAddresses.some((contract) => contract.toLowerCase() === delegationAddress.toLowerCase()));
45
+ return {
46
+ delegationAddress,
47
+ isSupported,
48
+ };
49
+ }
50
+ exports.isAccountUpgradedToEIP7702 = isAccountUpgradedToEIP7702;
51
+ /**
52
+ * Generate an EIP-7702 batch transaction.
53
+ *
54
+ * @param from - The sender address.
55
+ * @param transactions - The transactions to batch.
56
+ * @returns The batch transaction.
57
+ */
58
+ function generateEIP7702BatchTransaction(from, transactions) {
59
+ const erc7821Contract = contracts_1.Contract.getInterface(IERC7821_1.ABI_IERC7821);
60
+ const calls = transactions.map((transaction) => {
61
+ const { data, to, value } = transaction;
62
+ return [
63
+ to ?? '0x0000000000000000000000000000000000000000',
64
+ value ?? '0x0',
65
+ data ?? '0x',
66
+ ];
67
+ });
68
+ const mode = '0x01'.padEnd(66, '0');
69
+ const callData = abi_1.defaultAbiCoder.encode(['(address,uint256,bytes)[]'], [calls]);
70
+ const data = erc7821Contract.encodeFunctionData(exports.BATCH_FUNCTION_NAME, [
71
+ mode,
72
+ callData,
73
+ ]);
74
+ log('Transaction data', data);
75
+ return {
76
+ data,
77
+ to: from,
78
+ };
79
+ }
80
+ exports.generateEIP7702BatchTransaction = generateEIP7702BatchTransaction;
81
+ /**
82
+ * Sign an authorization list.
83
+ *
84
+ * @param options - Options bag.
85
+ * @param options.authorizationList - The authorization list to sign.
86
+ * @param options.messenger - The controller messenger.
87
+ * @param options.transactionMeta - The transaction metadata.
88
+ * @returns The signed authorization list.
89
+ */
90
+ async function signAuthorizationList({ authorizationList, messenger, transactionMeta, }) {
91
+ if (!authorizationList) {
92
+ return undefined;
93
+ }
94
+ const signedAuthorizationList = [];
95
+ let index = 0;
96
+ for (const authorization of authorizationList) {
97
+ const signedAuthorization = await signAuthorization(authorization, transactionMeta, messenger, index);
98
+ signedAuthorizationList.push(signedAuthorization);
99
+ index += 1;
100
+ }
101
+ return signedAuthorizationList;
102
+ }
103
+ exports.signAuthorizationList = signAuthorizationList;
104
+ /**
105
+ * Signs an authorization.
106
+ *
107
+ * @param authorization - The authorization to sign.
108
+ * @param transactionMeta - The associated transaction metadata.
109
+ * @param messenger - The messenger to use for signing.
110
+ * @param index - The index of the authorization in the list.
111
+ * @returns The signed authorization.
112
+ */
113
+ async function signAuthorization(authorization, transactionMeta, messenger, index) {
114
+ const finalAuthorization = prepareAuthorization(authorization, transactionMeta, index);
115
+ const { address, chainId, nonce } = finalAuthorization;
116
+ const chainIdDecimal = parseInt(chainId, 16);
117
+ const nonceDecimal = parseInt(nonce, 16);
118
+ const signature = await messenger.call('KeyringController:signAuthorization', [chainIdDecimal, address, nonceDecimal]);
119
+ const r = signature.slice(0, 66);
120
+ const s = `0x${signature.slice(66, 130)}`;
121
+ const v = parseInt(signature.slice(130, 132), 16);
122
+ const yParity = v - 27 === 0 ? '0x' : '0x1';
123
+ const finalNonce = nonceDecimal === 0 ? '0x' : nonce;
124
+ const result = {
125
+ address,
126
+ chainId,
127
+ nonce: finalNonce,
128
+ r,
129
+ s,
130
+ yParity,
131
+ };
132
+ log('Signed authorization', result);
133
+ return result;
134
+ }
135
+ /**
136
+ * Prepares an authorization for signing by populating the chainId and nonce.
137
+ *
138
+ * @param authorization - The authorization to prepare.
139
+ * @param transactionMeta - The associated transaction metadata.
140
+ * @param index - The index of the authorization in the list.
141
+ * @returns The prepared authorization.
142
+ */
143
+ function prepareAuthorization(authorization, transactionMeta, index) {
144
+ const { chainId: existingChainId, nonce: existingNonce } = authorization;
145
+ const { txParams, chainId: transactionChainId } = transactionMeta;
146
+ const { nonce: transactionNonce } = txParams;
147
+ const chainId = existingChainId ?? transactionChainId;
148
+ let nonce = existingNonce;
149
+ if (nonce === undefined) {
150
+ nonce = (0, controller_utils_1.toHex)(parseInt(transactionNonce, 16) + 1 + index);
151
+ }
152
+ const result = {
153
+ ...authorization,
154
+ chainId,
155
+ nonce,
156
+ };
157
+ log('Prepared authorization', result);
158
+ return result;
159
+ }
160
+ //# sourceMappingURL=eip7702.cjs.map
@@ -0,0 +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 '../abi/IERC7821';\nimport { projectLogger } from '../logger';\nimport type { TransactionControllerMessenger } from '../TransactionController';\nimport type {\n BatchTransactionParams,\n Authorization,\n AuthorizationList,\n TransactionMeta,\n} from '../types';\n\nexport type KeyringControllerAuthorization = [\n chainId: number,\n contractAddress: string,\n nonce: number,\n];\n\nexport type KeyringControllerSignAuthorization = {\n type: 'KeyringController:signAuthorization';\n handler: (authorization: KeyringControllerAuthorization) => Promise<string>;\n};\n\nexport const DELEGATION_PREFIX = '0xef0100';\nexport const BATCH_FUNCTION_NAME = 'execute';\n\nexport type FeatureFlagsEIP7702 = {\n contractAddresses?: Record<Hex, Hex[]>;\n supportedChains?: Hex[];\n};\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 const mode = '0x01'.padEnd(66, '0');\n\n const callData = defaultAbiCoder.encode(\n ['(address,uint256,bytes)[]'],\n [calls],\n );\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 { 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:signAuthorization',\n [chainIdDecimal, address, nonceDecimal],\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"]}
@@ -0,0 +1,63 @@
1
+ import type EthQuery from "@metamask/eth-query";
2
+ import { type Hex } from "@metamask/utils";
3
+ import type { TransactionControllerMessenger } from "../TransactionController.cjs";
4
+ import type { BatchTransactionParams, AuthorizationList, TransactionMeta } from "../types.cjs";
5
+ export type KeyringControllerAuthorization = [
6
+ chainId: number,
7
+ contractAddress: string,
8
+ nonce: number
9
+ ];
10
+ export type KeyringControllerSignAuthorization = {
11
+ type: 'KeyringController:signAuthorization';
12
+ handler: (authorization: KeyringControllerAuthorization) => Promise<string>;
13
+ };
14
+ export declare const DELEGATION_PREFIX = "0xef0100";
15
+ export declare const BATCH_FUNCTION_NAME = "execute";
16
+ export type FeatureFlagsEIP7702 = {
17
+ contractAddresses?: Record<Hex, Hex[]>;
18
+ supportedChains?: Hex[];
19
+ };
20
+ /**
21
+ * Determine if a chain supports EIP-7702 using LaunchDarkly feature flag.
22
+ *
23
+ * @param chainId - Hexadecimal ID of the chain.
24
+ * @param messenger - Messenger instance.
25
+ * @returns True if the chain supports EIP-7702.
26
+ */
27
+ export declare function doesChainSupportEIP7702(chainId: Hex, messenger: TransactionControllerMessenger): boolean;
28
+ /**
29
+ * Determine if an account has been upgraded to a supported EIP-7702 contract.
30
+ *
31
+ * @param address - The EOA address to check.
32
+ * @param chainId - The chain ID.
33
+ * @param messenger - The messenger instance.
34
+ * @param ethQuery - The EthQuery instance to communicate with the blockchain.
35
+ * @returns An object with the results of the check.
36
+ */
37
+ export declare function isAccountUpgradedToEIP7702(address: Hex, chainId: Hex, messenger: TransactionControllerMessenger, ethQuery: EthQuery): Promise<{
38
+ delegationAddress: `0x${string}` | undefined;
39
+ isSupported: boolean;
40
+ }>;
41
+ /**
42
+ * Generate an EIP-7702 batch transaction.
43
+ *
44
+ * @param from - The sender address.
45
+ * @param transactions - The transactions to batch.
46
+ * @returns The batch transaction.
47
+ */
48
+ export declare function generateEIP7702BatchTransaction(from: Hex, transactions: BatchTransactionParams[]): BatchTransactionParams;
49
+ /**
50
+ * Sign an authorization list.
51
+ *
52
+ * @param options - Options bag.
53
+ * @param options.authorizationList - The authorization list to sign.
54
+ * @param options.messenger - The controller messenger.
55
+ * @param options.transactionMeta - The transaction metadata.
56
+ * @returns The signed authorization list.
57
+ */
58
+ export declare function signAuthorizationList({ authorizationList, messenger, transactionMeta, }: {
59
+ authorizationList?: AuthorizationList;
60
+ messenger: TransactionControllerMessenger;
61
+ transactionMeta: TransactionMeta;
62
+ }): Promise<Required<AuthorizationList | undefined>>;
63
+ //# sourceMappingURL=eip7702.d.cts.map
@@ -0,0 +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,MAAM,MAAM,8BAA8B,GAAG;IAC3C,OAAO,EAAE,MAAM;IACf,eAAe,EAAE,MAAM;IACvB,KAAK,EAAE,MAAM;CACd,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,IAAI,EAAE,qCAAqC,CAAC;IAC5C,OAAO,EAAE,CAAC,aAAa,EAAE,8BAA8B,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7E,CAAC;AAEF,eAAO,MAAM,iBAAiB,aAAa,CAAC;AAC5C,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAE7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACvC,eAAe,CAAC,EAAE,GAAG,EAAE,CAAC;CACzB,CAAC;AAIF;;;;;;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,CA+BxB;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"}
@@ -0,0 +1,63 @@
1
+ import type EthQuery from "@metamask/eth-query";
2
+ import { type Hex } from "@metamask/utils";
3
+ import type { TransactionControllerMessenger } from "../TransactionController.mjs";
4
+ import type { BatchTransactionParams, AuthorizationList, TransactionMeta } from "../types.mjs";
5
+ export type KeyringControllerAuthorization = [
6
+ chainId: number,
7
+ contractAddress: string,
8
+ nonce: number
9
+ ];
10
+ export type KeyringControllerSignAuthorization = {
11
+ type: 'KeyringController:signAuthorization';
12
+ handler: (authorization: KeyringControllerAuthorization) => Promise<string>;
13
+ };
14
+ export declare const DELEGATION_PREFIX = "0xef0100";
15
+ export declare const BATCH_FUNCTION_NAME = "execute";
16
+ export type FeatureFlagsEIP7702 = {
17
+ contractAddresses?: Record<Hex, Hex[]>;
18
+ supportedChains?: Hex[];
19
+ };
20
+ /**
21
+ * Determine if a chain supports EIP-7702 using LaunchDarkly feature flag.
22
+ *
23
+ * @param chainId - Hexadecimal ID of the chain.
24
+ * @param messenger - Messenger instance.
25
+ * @returns True if the chain supports EIP-7702.
26
+ */
27
+ export declare function doesChainSupportEIP7702(chainId: Hex, messenger: TransactionControllerMessenger): boolean;
28
+ /**
29
+ * Determine if an account has been upgraded to a supported EIP-7702 contract.
30
+ *
31
+ * @param address - The EOA address to check.
32
+ * @param chainId - The chain ID.
33
+ * @param messenger - The messenger instance.
34
+ * @param ethQuery - The EthQuery instance to communicate with the blockchain.
35
+ * @returns An object with the results of the check.
36
+ */
37
+ export declare function isAccountUpgradedToEIP7702(address: Hex, chainId: Hex, messenger: TransactionControllerMessenger, ethQuery: EthQuery): Promise<{
38
+ delegationAddress: `0x${string}` | undefined;
39
+ isSupported: boolean;
40
+ }>;
41
+ /**
42
+ * Generate an EIP-7702 batch transaction.
43
+ *
44
+ * @param from - The sender address.
45
+ * @param transactions - The transactions to batch.
46
+ * @returns The batch transaction.
47
+ */
48
+ export declare function generateEIP7702BatchTransaction(from: Hex, transactions: BatchTransactionParams[]): BatchTransactionParams;
49
+ /**
50
+ * Sign an authorization list.
51
+ *
52
+ * @param options - Options bag.
53
+ * @param options.authorizationList - The authorization list to sign.
54
+ * @param options.messenger - The controller messenger.
55
+ * @param options.transactionMeta - The transaction metadata.
56
+ * @returns The signed authorization list.
57
+ */
58
+ export declare function signAuthorizationList({ authorizationList, messenger, transactionMeta, }: {
59
+ authorizationList?: AuthorizationList;
60
+ messenger: TransactionControllerMessenger;
61
+ transactionMeta: TransactionMeta;
62
+ }): Promise<Required<AuthorizationList | undefined>>;
63
+ //# sourceMappingURL=eip7702.d.mts.map
@@ -0,0 +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,MAAM,MAAM,8BAA8B,GAAG;IAC3C,OAAO,EAAE,MAAM;IACf,eAAe,EAAE,MAAM;IACvB,KAAK,EAAE,MAAM;CACd,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,IAAI,EAAE,qCAAqC,CAAC;IAC5C,OAAO,EAAE,CAAC,aAAa,EAAE,8BAA8B,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7E,CAAC;AAEF,eAAO,MAAM,iBAAiB,aAAa,CAAC;AAC5C,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAE7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACvC,eAAe,CAAC,EAAE,GAAG,EAAE,CAAC;CACzB,CAAC;AAIF;;;;;;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,CA+BxB;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"}
@@ -0,0 +1,153 @@
1
+ import { defaultAbiCoder } from "@ethersproject/abi";
2
+ import { Contract } from "@ethersproject/contracts";
3
+ import { query, toHex } from "@metamask/controller-utils";
4
+ import { createModuleLogger, add0x } from "@metamask/utils";
5
+ import { getEIP7702ContractAddresses, getEIP7702SupportedChains } from "./feature-flags.mjs";
6
+ import { ABI_IERC7821 } from "../abi/IERC7821.mjs";
7
+ import { projectLogger } from "../logger.mjs";
8
+ export const DELEGATION_PREFIX = '0xef0100';
9
+ export const BATCH_FUNCTION_NAME = 'execute';
10
+ const log = createModuleLogger(projectLogger, 'eip-7702');
11
+ /**
12
+ * Determine if a chain supports EIP-7702 using LaunchDarkly feature flag.
13
+ *
14
+ * @param chainId - Hexadecimal ID of the chain.
15
+ * @param messenger - Messenger instance.
16
+ * @returns True if the chain supports EIP-7702.
17
+ */
18
+ export function doesChainSupportEIP7702(chainId, messenger) {
19
+ const supportedChains = getEIP7702SupportedChains(messenger);
20
+ return supportedChains.some((supportedChainId) => supportedChainId.toLowerCase() === chainId.toLowerCase());
21
+ }
22
+ /**
23
+ * Determine if an account has been upgraded to a supported EIP-7702 contract.
24
+ *
25
+ * @param address - The EOA address to check.
26
+ * @param chainId - The chain ID.
27
+ * @param messenger - The messenger instance.
28
+ * @param ethQuery - The EthQuery instance to communicate with the blockchain.
29
+ * @returns An object with the results of the check.
30
+ */
31
+ export async function isAccountUpgradedToEIP7702(address, chainId, messenger, ethQuery) {
32
+ const contractAddresses = getEIP7702ContractAddresses(chainId, messenger);
33
+ const code = await query(ethQuery, 'eth_getCode', [address]);
34
+ const normalizedCode = add0x(code?.toLowerCase?.() ?? '');
35
+ const hasDelegation = code?.length === 48 && normalizedCode.startsWith(DELEGATION_PREFIX);
36
+ const delegationAddress = hasDelegation
37
+ ? add0x(normalizedCode.slice(DELEGATION_PREFIX.length))
38
+ : undefined;
39
+ const isSupported = Boolean(delegationAddress &&
40
+ contractAddresses.some((contract) => contract.toLowerCase() === delegationAddress.toLowerCase()));
41
+ return {
42
+ delegationAddress,
43
+ isSupported,
44
+ };
45
+ }
46
+ /**
47
+ * Generate an EIP-7702 batch transaction.
48
+ *
49
+ * @param from - The sender address.
50
+ * @param transactions - The transactions to batch.
51
+ * @returns The batch transaction.
52
+ */
53
+ export function generateEIP7702BatchTransaction(from, transactions) {
54
+ const erc7821Contract = Contract.getInterface(ABI_IERC7821);
55
+ const calls = transactions.map((transaction) => {
56
+ const { data, to, value } = transaction;
57
+ return [
58
+ to ?? '0x0000000000000000000000000000000000000000',
59
+ value ?? '0x0',
60
+ data ?? '0x',
61
+ ];
62
+ });
63
+ const mode = '0x01'.padEnd(66, '0');
64
+ const callData = defaultAbiCoder.encode(['(address,uint256,bytes)[]'], [calls]);
65
+ const data = erc7821Contract.encodeFunctionData(BATCH_FUNCTION_NAME, [
66
+ mode,
67
+ callData,
68
+ ]);
69
+ log('Transaction data', data);
70
+ return {
71
+ data,
72
+ to: from,
73
+ };
74
+ }
75
+ /**
76
+ * Sign an authorization list.
77
+ *
78
+ * @param options - Options bag.
79
+ * @param options.authorizationList - The authorization list to sign.
80
+ * @param options.messenger - The controller messenger.
81
+ * @param options.transactionMeta - The transaction metadata.
82
+ * @returns The signed authorization list.
83
+ */
84
+ export async function signAuthorizationList({ authorizationList, messenger, transactionMeta, }) {
85
+ if (!authorizationList) {
86
+ return undefined;
87
+ }
88
+ const signedAuthorizationList = [];
89
+ let index = 0;
90
+ for (const authorization of authorizationList) {
91
+ const signedAuthorization = await signAuthorization(authorization, transactionMeta, messenger, index);
92
+ signedAuthorizationList.push(signedAuthorization);
93
+ index += 1;
94
+ }
95
+ return signedAuthorizationList;
96
+ }
97
+ /**
98
+ * Signs an authorization.
99
+ *
100
+ * @param authorization - The authorization to sign.
101
+ * @param transactionMeta - The associated transaction metadata.
102
+ * @param messenger - The messenger to use for signing.
103
+ * @param index - The index of the authorization in the list.
104
+ * @returns The signed authorization.
105
+ */
106
+ async function signAuthorization(authorization, transactionMeta, messenger, index) {
107
+ const finalAuthorization = prepareAuthorization(authorization, transactionMeta, index);
108
+ const { address, chainId, nonce } = finalAuthorization;
109
+ const chainIdDecimal = parseInt(chainId, 16);
110
+ const nonceDecimal = parseInt(nonce, 16);
111
+ const signature = await messenger.call('KeyringController:signAuthorization', [chainIdDecimal, address, nonceDecimal]);
112
+ const r = signature.slice(0, 66);
113
+ const s = `0x${signature.slice(66, 130)}`;
114
+ const v = parseInt(signature.slice(130, 132), 16);
115
+ const yParity = v - 27 === 0 ? '0x' : '0x1';
116
+ const finalNonce = nonceDecimal === 0 ? '0x' : nonce;
117
+ const result = {
118
+ address,
119
+ chainId,
120
+ nonce: finalNonce,
121
+ r,
122
+ s,
123
+ yParity,
124
+ };
125
+ log('Signed authorization', result);
126
+ return result;
127
+ }
128
+ /**
129
+ * Prepares an authorization for signing by populating the chainId and nonce.
130
+ *
131
+ * @param authorization - The authorization to prepare.
132
+ * @param transactionMeta - The associated transaction metadata.
133
+ * @param index - The index of the authorization in the list.
134
+ * @returns The prepared authorization.
135
+ */
136
+ function prepareAuthorization(authorization, transactionMeta, index) {
137
+ const { chainId: existingChainId, nonce: existingNonce } = authorization;
138
+ const { txParams, chainId: transactionChainId } = transactionMeta;
139
+ const { nonce: transactionNonce } = txParams;
140
+ const chainId = existingChainId ?? transactionChainId;
141
+ let nonce = existingNonce;
142
+ if (nonce === undefined) {
143
+ nonce = toHex(parseInt(transactionNonce, 16) + 1 + index);
144
+ }
145
+ const result = {
146
+ ...authorization,
147
+ chainId,
148
+ nonce,
149
+ };
150
+ log('Prepared authorization', result);
151
+ return result;
152
+ }
153
+ //# sourceMappingURL=eip7702.mjs.map
@@ -0,0 +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 '../abi/IERC7821';\nimport { projectLogger } from '../logger';\nimport type { TransactionControllerMessenger } from '../TransactionController';\nimport type {\n BatchTransactionParams,\n Authorization,\n AuthorizationList,\n TransactionMeta,\n} from '../types';\n\nexport type KeyringControllerAuthorization = [\n chainId: number,\n contractAddress: string,\n nonce: number,\n];\n\nexport type KeyringControllerSignAuthorization = {\n type: 'KeyringController:signAuthorization';\n handler: (authorization: KeyringControllerAuthorization) => Promise<string>;\n};\n\nexport const DELEGATION_PREFIX = '0xef0100';\nexport const BATCH_FUNCTION_NAME = 'execute';\n\nexport type FeatureFlagsEIP7702 = {\n contractAddresses?: Record<Hex, Hex[]>;\n supportedChains?: Hex[];\n};\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 const mode = '0x01'.padEnd(66, '0');\n\n const callData = defaultAbiCoder.encode(\n ['(address,uint256,bytes)[]'],\n [calls],\n );\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 { 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:signAuthorization',\n [chainIdDecimal, address, nonceDecimal],\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"]}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getEIP7702UpgradeContractAddress = exports.getEIP7702ContractAddresses = exports.getEIP7702SupportedChains = exports.FEATURE_FLAG_EIP_7702 = void 0;
4
+ const utils_1 = require("@metamask/utils");
5
+ const logger_1 = require("../logger.cjs");
6
+ exports.FEATURE_FLAG_EIP_7702 = 'confirmations-eip-7702';
7
+ const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'feature-flags');
8
+ /**
9
+ * Retrieves the supported EIP-7702 chains.
10
+ *
11
+ * @param messenger - The controller messenger instance.
12
+ * @returns The supported chains.
13
+ */
14
+ function getEIP7702SupportedChains(messenger) {
15
+ const featureFlags = getFeatureFlags(messenger);
16
+ return featureFlags?.[exports.FEATURE_FLAG_EIP_7702]?.supportedChains ?? [];
17
+ }
18
+ exports.getEIP7702SupportedChains = getEIP7702SupportedChains;
19
+ /**
20
+ * Retrieves the supported EIP-7702 contract addresses for a given chain ID.
21
+ *
22
+ * @param chainId - The chain ID.
23
+ * @param messenger - The controller messenger instance.
24
+ * @returns The supported contract addresses.
25
+ */
26
+ function getEIP7702ContractAddresses(chainId, messenger) {
27
+ const featureFlags = getFeatureFlags(messenger);
28
+ return (featureFlags?.[exports.FEATURE_FLAG_EIP_7702]?.contractAddresses?.[chainId.toLowerCase()] ?? []);
29
+ }
30
+ exports.getEIP7702ContractAddresses = getEIP7702ContractAddresses;
31
+ /**
32
+ * Retrieves the EIP-7702 upgrade contract address.
33
+ *
34
+ * @param chainId - The chain ID.
35
+ * @param messenger - The controller messenger instance.
36
+ * @returns The upgrade contract address.
37
+ */
38
+ function getEIP7702UpgradeContractAddress(chainId, messenger) {
39
+ return getEIP7702ContractAddresses(chainId, messenger)?.[0];
40
+ }
41
+ exports.getEIP7702UpgradeContractAddress = getEIP7702UpgradeContractAddress;
42
+ /**
43
+ * Retrieves the relevant feature flags from the remote feature flag controller.
44
+ *
45
+ * @param messenger - The messenger instance.
46
+ * @returns The feature flags.
47
+ */
48
+ function getFeatureFlags(messenger) {
49
+ const featureFlags = messenger.call('RemoteFeatureFlagController:getState').remoteFeatureFlags;
50
+ log('Retrieved feature flags', featureFlags);
51
+ return featureFlags;
52
+ }
53
+ //# sourceMappingURL=feature-flags.cjs.map