@toruslabs/ethereum-controllers 8.16.0 → 8.17.1

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 (58) hide show
  1. package/dist/ethereumControllers.umd.min.js +1 -1
  2. package/dist/lib.cjs/Eip5792/walletGetCallsStatus.js +75 -0
  3. package/dist/lib.cjs/Eip5792/walletGetCapabilities.js +64 -0
  4. package/dist/lib.cjs/Eip5792/walletSendCalls.js +163 -0
  5. package/dist/lib.cjs/Eip7702/eip7702Utils.js +82 -0
  6. package/dist/lib.cjs/Eip7702/walletGetUpgradeStatus.js +35 -0
  7. package/dist/lib.cjs/Eip7702/walletUpgradeAccount.js +58 -0
  8. package/dist/lib.cjs/Keyring/KeyringController.js +50 -14
  9. package/dist/lib.cjs/Network/createEthereumMiddleware.js +114 -1
  10. package/dist/lib.cjs/Transaction/TransactionController.js +117 -6
  11. package/dist/lib.cjs/Transaction/TransactionGasUtil.js +13 -0
  12. package/dist/lib.cjs/Transaction/TransactionStateManager.js +9 -0
  13. package/dist/lib.cjs/Transaction/TransactionUtils.js +103 -10
  14. package/dist/lib.cjs/index.js +35 -0
  15. package/dist/lib.cjs/types/Eip5792/index.d.ts +3 -0
  16. package/dist/lib.cjs/types/Eip5792/walletGetCallsStatus.d.ts +19 -0
  17. package/dist/lib.cjs/types/Eip5792/walletGetCapabilities.d.ts +17 -0
  18. package/dist/lib.cjs/types/Eip5792/walletSendCalls.d.ts +45 -0
  19. package/dist/lib.cjs/types/Eip7702/eip7702Utils.d.ts +27 -0
  20. package/dist/lib.cjs/types/Eip7702/walletGetUpgradeStatus.d.ts +11 -0
  21. package/dist/lib.cjs/types/Eip7702/walletUpgradeAccount.d.ts +13 -0
  22. package/dist/lib.cjs/types/Keyring/KeyringController.d.ts +2 -0
  23. package/dist/lib.cjs/types/Network/createEthereumMiddleware.d.ts +53 -1
  24. package/dist/lib.cjs/types/Transaction/TransactionController.d.ts +9 -1
  25. package/dist/lib.cjs/types/Transaction/TransactionStateManager.d.ts +1 -0
  26. package/dist/lib.cjs/types/Transaction/TransactionUtils.d.ts +38 -1
  27. package/dist/lib.cjs/types/index.d.ts +4 -0
  28. package/dist/lib.cjs/types/utils/abis.d.ts +15 -0
  29. package/dist/lib.cjs/types/utils/constants.d.ts +2 -0
  30. package/dist/lib.cjs/types/utils/eip5792Types.d.ts +154 -0
  31. package/dist/lib.cjs/types/utils/eip7702Types.d.ts +60 -0
  32. package/dist/lib.cjs/types/utils/interfaces.d.ts +22 -2
  33. package/dist/lib.cjs/utils/abis.js +30 -0
  34. package/dist/lib.cjs/utils/constants.js +4 -1
  35. package/dist/lib.cjs/utils/eip5792Types.js +39 -0
  36. package/dist/lib.cjs/utils/eip7702Types.js +14 -0
  37. package/dist/lib.cjs/utils/interfaces.js +8 -0
  38. package/dist/lib.esm/Eip5792/walletGetCallsStatus.js +76 -0
  39. package/dist/lib.esm/Eip5792/walletGetCapabilities.js +63 -0
  40. package/dist/lib.esm/Eip5792/walletSendCalls.js +168 -0
  41. package/dist/lib.esm/Eip7702/eip7702Utils.js +81 -0
  42. package/dist/lib.esm/Eip7702/walletGetUpgradeStatus.js +33 -0
  43. package/dist/lib.esm/Eip7702/walletUpgradeAccount.js +56 -0
  44. package/dist/lib.esm/Keyring/KeyringController.js +53 -15
  45. package/dist/lib.esm/Message/utils.js +1 -1
  46. package/dist/lib.esm/Network/createEthereumMiddleware.js +119 -4
  47. package/dist/lib.esm/Transaction/TransactionController.js +124 -10
  48. package/dist/lib.esm/Transaction/TransactionGasUtil.js +13 -0
  49. package/dist/lib.esm/Transaction/TransactionStateManager.js +9 -0
  50. package/dist/lib.esm/Transaction/TransactionUtils.js +106 -14
  51. package/dist/lib.esm/index.js +11 -4
  52. package/dist/lib.esm/utils/abis.js +30 -1
  53. package/dist/lib.esm/utils/constants.js +4 -2
  54. package/dist/lib.esm/utils/eip5792Types.js +104 -0
  55. package/dist/lib.esm/utils/eip7702Types.js +15 -0
  56. package/dist/lib.esm/utils/helpers.js +2 -2
  57. package/dist/lib.esm/utils/interfaces.js +28 -0
  58. package/package.json +7 -4
@@ -0,0 +1,60 @@
1
+ import { Hex } from "viem";
2
+ import { TransactionParams } from "./interfaces";
3
+ export declare const EIP_7702_METHODS: {
4
+ readonly WALLET_UPGRADE_ACCOUNT: "wallet_upgradeAccount";
5
+ readonly WALLET_GET_ACCOUNT_UPGRADE_STATUS: "wallet_getAccountUpgradeStatus";
6
+ };
7
+ export declare const EIP_7702_PREFIX = "0xef0100";
8
+ export declare const EIP_7702_REVOKE_ADDRESS = "0x0000000000000000000000000000000000000000";
9
+ export declare const DUMMY_AUTHORIZATION_SIGNATURE = "0x1111111111111111111111111111111111111111111111111111111111111111";
10
+ /**
11
+ * An authorization to be included in a `setCode` transaction.
12
+ * Specifies code to be added to the authorization signer's EOA account.
13
+ * Introduced in EIP-7702.
14
+ */
15
+ export type Authorization = {
16
+ /** Address of a smart contract that contains the code to be set. */
17
+ address: Hex;
18
+ /**
19
+ * Specific chain the authorization applies to.
20
+ * If not provided, defaults to the chain ID of the transaction.
21
+ */
22
+ chainId: Hex;
23
+ /**
24
+ * Nonce at which the authorization will be valid.
25
+ * If not provided, defaults to the nonce following the transaction's nonce.
26
+ */
27
+ nonce?: Hex;
28
+ /** R component of the signature. */
29
+ r?: Hex;
30
+ /** S component of the signature. */
31
+ s?: Hex;
32
+ /** Y parity generated from the signature. */
33
+ yParity?: Hex;
34
+ };
35
+ export type UnsignedAuthorization = Omit<Required<Authorization>, "r" | "s" | "yParity">;
36
+ export type SignedAuthorization = Required<Authorization>;
37
+ export interface Eip7702Params extends TransactionParams {
38
+ /**
39
+ * Address of the account to upgrade or get upgrade status for.
40
+ */
41
+ account: Hex;
42
+ /**
43
+ * Chain ID of the account to upgrade.
44
+ */
45
+ chainId: Hex;
46
+ /**
47
+ * Optional contract address for delegation.
48
+ */
49
+ implementation?: Hex;
50
+ }
51
+ export interface Eip7702WalletGetUpgradeStatusResponse {
52
+ /**
53
+ * Whether the account is upgraded.
54
+ */
55
+ isUpgraded: boolean;
56
+ /**
57
+ * The implementation address of the contract that the account is delegated to.
58
+ */
59
+ implementation: Hex;
60
+ }
@@ -7,6 +7,10 @@ import { ToBiconomySmartAccountParameters, toEcdsaKernelSmartAccount, ToLightSma
7
7
  import { Client, Hex, WalletClient } from "viem";
8
8
  import { createBundlerClient, createPaymasterClient, SmartAccount, UserOperationReceipt, WebAuthnAccount } from "viem/account-abstraction";
9
9
  import { METHOD_TYPES, SMART_ACCOUNT, TRANSACTION_ENVELOPE_TYPES } from "./constants";
10
+ import { NestedTransactionMetadata } from "./eip5792Types";
11
+ import { Authorization } from "./eip7702Types";
12
+ export type { BatchTransactionParams, NestedTransactionMetadata, TransactionBatchRequest, TransactionBatchSingleRequest } from "./eip5792Types";
13
+ export type { Authorization, Eip7702Params, Eip7702WalletGetUpgradeStatusResponse, SignedAuthorization } from "./eip7702Types";
10
14
  export type CustomTokenInfo = BaseTokenInfo & {
11
15
  erc20: boolean;
12
16
  customTokenId?: string;
@@ -167,6 +171,12 @@ export interface TransactionParams extends BaseRequestParams {
167
171
  * The amount of gas to allocate for the Paymaster post-operation code
168
172
  */
169
173
  paymasterPostOpGasLimit?: string;
174
+ /**
175
+ * Array of authorizations to set code on EOA accounts.
176
+ * Only supported in `setCode` transactions.
177
+ * Introduced in EIP-7702.
178
+ */
179
+ authorizationList?: Authorization[];
170
180
  }
171
181
  export type Nonce = {
172
182
  name: string;
@@ -275,6 +285,11 @@ export interface DappSuggestedGasFees {
275
285
  maxFeePerGas?: string;
276
286
  gas?: string;
277
287
  }
288
+ export declare const TRANSACTION_CATEGORY_EIP7702: {
289
+ readonly BATCH: "batch";
290
+ readonly REVOKE_DELEGATION: "revokeDelegation";
291
+ };
292
+ export type TransactionCategoryWithBatch = TRANSACTION_TYPE | (typeof TRANSACTION_CATEGORY_EIP7702)[keyof typeof TRANSACTION_CATEGORY_EIP7702];
278
293
  export interface EthereumTransactionMeta extends TransactionMeta<TransactionParams> {
279
294
  r?: string;
280
295
  s?: string;
@@ -287,7 +302,7 @@ export interface EthereumTransactionMeta extends TransactionMeta<TransactionPara
287
302
  retryCount?: number;
288
303
  simulationFails?: Record<string, unknown>;
289
304
  loadingDefaults?: boolean;
290
- transactionCategory?: TRANSACTION_TYPE;
305
+ transactionCategory?: TransactionCategoryWithBatch;
291
306
  contractType?: string;
292
307
  nonceDetails?: NonceDetails;
293
308
  methodParams?: unknown[];
@@ -295,6 +310,8 @@ export interface EthereumTransactionMeta extends TransactionMeta<TransactionPara
295
310
  isUserOperation?: boolean;
296
311
  userOpHash?: string;
297
312
  userOpReceipt?: UserOperationReceipt;
313
+ batchId?: string;
314
+ nestedTransactions?: NestedTransactionMetadata[];
298
315
  }
299
316
  export interface TransactionPayload {
300
317
  created_at: Date;
@@ -483,4 +500,7 @@ export interface UserOperationGas {
483
500
  paymasterVerificationGasLimit?: string;
484
501
  paymasterPostOpGasLimit?: string;
485
502
  }
486
- export {};
503
+ /**
504
+ * Type Def for function to query the deployment bytecode of an address. (eth_getCode)
505
+ */
506
+ export type GetEthCodeFn = (address: `0x${string}`, chainId: `0x${string}`) => Promise<`0x${string}`>;
@@ -503,8 +503,38 @@ const singleBalanceCheckerAbi = [{
503
503
  stateMutability: "view",
504
504
  type: "function"
505
505
  }];
506
+ const erc7821Abi = [{
507
+ type: "function",
508
+ name: "execute",
509
+ inputs: [{
510
+ name: "mode",
511
+ type: "bytes32",
512
+ internalType: "ModeCode"
513
+ }, {
514
+ name: "executionData",
515
+ type: "bytes",
516
+ internalType: "bytes"
517
+ }],
518
+ outputs: [],
519
+ stateMutability: "payable"
520
+ }, {
521
+ type: "function",
522
+ name: "supportsExecutionMode",
523
+ inputs: [{
524
+ name: "mode",
525
+ type: "bytes32",
526
+ internalType: "ModeCode"
527
+ }],
528
+ outputs: [{
529
+ name: "",
530
+ type: "bool",
531
+ internalType: "bool"
532
+ }],
533
+ stateMutability: "view"
534
+ }];
506
535
 
507
536
  exports.erc1155Abi = erc1155Abi;
508
537
  exports.erc20Abi = erc20Abi;
509
538
  exports.erc721Abi = erc721Abi;
539
+ exports.erc7821Abi = erc7821Abi;
510
540
  exports.singleBalanceCheckerAbi = singleBalanceCheckerAbi;
@@ -7,6 +7,7 @@ const CONTRACT_TYPE_ETH = "eth";
7
7
  const CONTRACT_TYPE_ERC20 = "erc20";
8
8
  const CONTRACT_TYPE_ERC721 = "erc721";
9
9
  const CONTRACT_TYPE_ERC1155 = "erc1155";
10
+ const CONTRACT_TYPE_ERC7821 = "erc7821";
10
11
  const ERC1155_INTERFACE_ID = "0xd9b67a26";
11
12
  const ERC721_INTERFACE_ID = "0x80ac58cd";
12
13
  const ERC721_METADATA_INTERFACE_ID = "0x5b5e139f";
@@ -278,7 +279,8 @@ const PAYMASTER_METHOD_TYPES = [METHOD_TYPES.ETH_GET_PAYMASTER_DATA, METHOD_TYPE
278
279
  const TRANSACTION_ENVELOPE_TYPES = {
279
280
  LEGACY: "0x0",
280
281
  ACCESS_LIST: "0x1",
281
- FEE_MARKET: "0x2"
282
+ FEE_MARKET: "0x2",
283
+ SET_CODE: "0x4" // Type 4 transaction
282
284
  };
283
285
  const GAS_ESTIMATE_TYPES = {
284
286
  // Fee Market describes the way gas is set after the london hardfork, and was
@@ -381,6 +383,7 @@ exports.COINGECKO_SUPPORTED_CURRENCIES = COINGECKO_SUPPORTED_CURRENCIES;
381
383
  exports.CONTRACT_TYPE_ERC1155 = CONTRACT_TYPE_ERC1155;
382
384
  exports.CONTRACT_TYPE_ERC20 = CONTRACT_TYPE_ERC20;
383
385
  exports.CONTRACT_TYPE_ERC721 = CONTRACT_TYPE_ERC721;
386
+ exports.CONTRACT_TYPE_ERC7821 = CONTRACT_TYPE_ERC7821;
384
387
  exports.CONTRACT_TYPE_ETH = CONTRACT_TYPE_ETH;
385
388
  exports.ERC1155_INTERFACE_ID = ERC1155_INTERFACE_ID;
386
389
  exports.ERC721_ENUMERABLE_INTERFACE_ID = ERC721_ENUMERABLE_INTERFACE_ID;
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ // ============================================================================
4
+ // EIP-5792 Method Names
5
+ // ============================================================================
6
+ const EIP_5792_METHODS = {
7
+ WALLET_GET_CAPABILITIES: "wallet_getCapabilities",
8
+ WALLET_SEND_CALLS: "wallet_sendCalls",
9
+ WALLET_GET_CALLS_STATUS: "wallet_getCallsStatus",
10
+ WALLET_SHOW_CALLS_STATUS: "wallet_showCallsStatus"
11
+ };
12
+ const EIP5792ErrorCode = {
13
+ UnsupportedNonOptionalCapability: 5700,
14
+ UnsupportedChainId: 5710,
15
+ UnknownBundleId: 5730,
16
+ RejectedUpgrade: 5750
17
+ };
18
+ const GetCallsStatusCode = {
19
+ PENDING: 100,
20
+ CONFIRMED: 200,
21
+ FAILED_OFFCHAIN: 400,
22
+ REVERTED: 500,
23
+ REVERTED_PARTIAL: 600
24
+ };
25
+ /**
26
+ * EIP-5792 atomic status (EIP-5792)
27
+ *
28
+ * Reference: https://eips.ethereum.org/EIPS/eip-5792#wallet_getcapabilities
29
+ */
30
+ const Eip5792AtomicStatus = {
31
+ SUPPORTED: "supported",
32
+ READY: "ready",
33
+ UNSUPPORTED: "unsupported"
34
+ };
35
+
36
+ exports.EIP5792ErrorCode = EIP5792ErrorCode;
37
+ exports.EIP_5792_METHODS = EIP_5792_METHODS;
38
+ exports.Eip5792AtomicStatus = Eip5792AtomicStatus;
39
+ exports.GetCallsStatusCode = GetCallsStatusCode;
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ const EIP_7702_METHODS = {
4
+ WALLET_UPGRADE_ACCOUNT: "wallet_upgradeAccount",
5
+ WALLET_GET_ACCOUNT_UPGRADE_STATUS: "wallet_getAccountUpgradeStatus"
6
+ };
7
+ const EIP_7702_PREFIX = "0xef0100";
8
+ const EIP_7702_REVOKE_ADDRESS = "0x0000000000000000000000000000000000000000";
9
+ const DUMMY_AUTHORIZATION_SIGNATURE = "0x1111111111111111111111111111111111111111111111111111111111111111";
10
+
11
+ exports.DUMMY_AUTHORIZATION_SIGNATURE = DUMMY_AUTHORIZATION_SIGNATURE;
12
+ exports.EIP_7702_METHODS = EIP_7702_METHODS;
13
+ exports.EIP_7702_PREFIX = EIP_7702_PREFIX;
14
+ exports.EIP_7702_REVOKE_ADDRESS = EIP_7702_REVOKE_ADDRESS;
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ const TRANSACTION_CATEGORY_EIP7702 = {
4
+ BATCH: "batch",
5
+ REVOKE_DELEGATION: "revokeDelegation"
6
+ };
7
+
8
+ exports.TRANSACTION_CATEGORY_EIP7702 = TRANSACTION_CATEGORY_EIP7702;
@@ -0,0 +1,76 @@
1
+ import { addHexPrefix } from '@ethereumjs/util';
2
+ import { TransactionStatus } from '@toruslabs/base-controllers';
3
+ import { rpcErrors, JsonRpcError } from '@web3auth/auth';
4
+ import { GetCallsStatusCode, EIP_5792_METHODS, EIP5792ErrorCode } from '../utils/eip5792Types.js';
5
+
6
+ /**
7
+ * Maps a TransactionStatus to an EIP-5792 GetCallsStatusCode.
8
+ * @param status - The transaction status.
9
+ * @returns The corresponding EIP-5792 status code.
10
+ */
11
+ function mapTransactionStatusToEip5792Status(status) {
12
+ switch (status) {
13
+ // Pending states - batch is still being processed
14
+ case TransactionStatus.unapproved:
15
+ case TransactionStatus.approved:
16
+ case TransactionStatus.signed:
17
+ case TransactionStatus.submitted:
18
+ case TransactionStatus.pending:
19
+ case TransactionStatus.cancelling:
20
+ return GetCallsStatusCode.PENDING;
21
+
22
+ // Confirmed states - batch completed successfully
23
+ case TransactionStatus.confirmed:
24
+ case TransactionStatus.finalized:
25
+ case TransactionStatus.processed:
26
+ return GetCallsStatusCode.CONFIRMED;
27
+
28
+ // Failed offchain - rejected/cancelled before reaching the chain
29
+ case TransactionStatus.rejected:
30
+ case TransactionStatus.cancelled:
31
+ case TransactionStatus.expired:
32
+ case TransactionStatus.dropped:
33
+ return GetCallsStatusCode.FAILED_OFFCHAIN;
34
+
35
+ // Reverted - transaction was submitted but failed on chain
36
+ case TransactionStatus.failed:
37
+ return GetCallsStatusCode.REVERTED;
38
+ default:
39
+ return GetCallsStatusCode.PENDING;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Handler for wallet_getCallsStatus (EIP-5792).
45
+ * Returns the status of a batch of calls.
46
+ *
47
+ * @param request - The JRPC request with batch ID parameter.
48
+ * @param context - Context containing required functions.
49
+ * @returns The batch status and receipts.
50
+ */
51
+ function walletGetCallsStatus(request, getTransactionByBatchId) {
52
+ const {
53
+ method,
54
+ params
55
+ } = request;
56
+ if (method !== EIP_5792_METHODS.WALLET_GET_CALLS_STATUS) {
57
+ return;
58
+ }
59
+ const batchId = Array.isArray(params) ? params[0] : params;
60
+ if (!batchId) {
61
+ throw rpcErrors.invalidParams("wallet_getCallsStatus: batch ID is required");
62
+ }
63
+ const batchTx = getTransactionByBatchId(batchId);
64
+ if (!batchTx) {
65
+ throw new JsonRpcError(EIP5792ErrorCode.UnknownBundleId, `wallet_getCallsStatus: Batch not found: ${batchId}`);
66
+ }
67
+ return {
68
+ status: mapTransactionStatusToEip5792Status(batchTx.status),
69
+ id: batchId,
70
+ chainId: addHexPrefix(batchTx.chainId),
71
+ atomic: true,
72
+ receipts: batchTx.txReceipt ? [batchTx.txReceipt] : []
73
+ };
74
+ }
75
+
76
+ export { mapTransactionStatusToEip5792Status, walletGetCallsStatus };
@@ -0,0 +1,63 @@
1
+ import { rpcErrors } from '@web3auth/auth';
2
+ import { getIsEip7702UpgradeSupported } from '../Eip7702/eip7702Utils.js';
3
+ import { EIP_5792_METHODS, Eip5792AtomicStatus } from '../utils/eip5792Types.js';
4
+
5
+ /**
6
+ * Handler for wallet_getCapabilities (EIP-5792).
7
+ * Returns capabilities per chain, indicating atomicBatch support for upgraded accounts.
8
+ *
9
+ * @param request - The JRPC request with wallet address parameter.
10
+ * @param context - Context containing required functions.
11
+ * @returns Capabilities per chain.
12
+ */
13
+ async function walletGetCapabilities(request, getEthCode, context) {
14
+ var _context$getCachedDel;
15
+ const {
16
+ method,
17
+ params
18
+ } = request;
19
+ if (method !== EIP_5792_METHODS.WALLET_GET_CAPABILITIES) {
20
+ return;
21
+ }
22
+ if (!Array.isArray(params)) {
23
+ throw rpcErrors.invalidParams("wallet_getCapabilities: parameters are required");
24
+ }
25
+ const walletAddress = params[0];
26
+ if (!walletAddress) {
27
+ throw rpcErrors.invalidParams("wallet_getCapabilities: wallet address is required");
28
+ }
29
+ const chainsOverride = params[1];
30
+ const supportedChains = chainsOverride || context.getSupportedChains();
31
+ const cachedDelegations = ((_context$getCachedDel = context.getCachedDelegations) === null || _context$getCachedDel === void 0 ? void 0 : _context$getCachedDel.call(context)) || {};
32
+ const capabilities = {};
33
+ for (const chainId of supportedChains) {
34
+ const cacheKey = `${walletAddress.toLowerCase()}-${chainId.toLowerCase()}`;
35
+ let delegationAddress = cachedDelegations[cacheKey];
36
+ let atomicStatus = Eip5792AtomicStatus.SUPPORTED;
37
+
38
+ // If not cached, try to fetch
39
+ if (delegationAddress === undefined && getEthCode) {
40
+ try {
41
+ const eip7702UpgradeSupported = await getIsEip7702UpgradeSupported(walletAddress, chainId, getEthCode);
42
+ delegationAddress = eip7702UpgradeSupported.delegationAddress;
43
+ if (delegationAddress === null) {
44
+ atomicStatus = eip7702UpgradeSupported.isSupported ? Eip5792AtomicStatus.READY : Eip5792AtomicStatus.UNSUPPORTED;
45
+ } else {
46
+ var _context$updateDelega;
47
+ // account already upgraded, update the cache
48
+ (_context$updateDelega = context.updateDelegationCache) === null || _context$updateDelega === void 0 || _context$updateDelega.call(context, walletAddress, chainId, delegationAddress);
49
+ }
50
+ } catch {
51
+ atomicStatus = Eip5792AtomicStatus.UNSUPPORTED;
52
+ }
53
+ }
54
+ capabilities[chainId] = {
55
+ atomic: {
56
+ status: atomicStatus
57
+ }
58
+ };
59
+ }
60
+ return capabilities;
61
+ }
62
+
63
+ export { walletGetCapabilities };
@@ -0,0 +1,168 @@
1
+ import { isValidAddress } from '@ethereumjs/util';
2
+ import { rpcErrors } from '@web3auth/auth';
3
+ import { isHexString } from 'ethers';
4
+ import { v4, parse } from 'uuid';
5
+ import { getIsEip7702UpgradeSupported } from '../Eip7702/eip7702Utils.js';
6
+ import { EIP_5792_METHODS } from '../utils/eip5792Types.js';
7
+
8
+ /**
9
+ * Generates a unique batch ID for EIP-5792 calls.
10
+ * @returns A unique hex string batch ID.
11
+ */
12
+ function generateBatchId() {
13
+ const idString = v4();
14
+ const idBytes = new Uint8Array(parse(idString));
15
+ const hexString = Array.from(idBytes).map(byte => byte.toString(16).padStart(2, "0")).join("");
16
+ return `0x${hexString}`;
17
+ }
18
+
19
+ /**
20
+ * Validates a single call in the batch.
21
+ * @param call - The call to validate.
22
+ * @param index - The index of the call in the batch (for error messages).
23
+ */
24
+ function validateCall(call, index) {
25
+ // Validate 'to' address
26
+ if (!call.to || !isHexString(call.to) || !isValidAddress(call.to)) {
27
+ throw rpcErrors.invalidParams(`Invalid 'to' address in call at index ${index}`);
28
+ }
29
+
30
+ // Validate 'value' if present
31
+ if (call.value !== undefined) {
32
+ if (!isHexString(call.value)) {
33
+ throw rpcErrors.invalidParams(`Invalid 'value' in call at index ${index}: must be a valid non-negative hex string`);
34
+ }
35
+ }
36
+
37
+ // Validate 'data' if present
38
+ if (call.data !== undefined) {
39
+ if (!isHexString(call.data)) {
40
+ throw rpcErrors.invalidParams(`Invalid 'data' in call at index ${index}: must be a valid hex string`);
41
+ }
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Validates the parameters for wallet_sendCalls (EIP-5792).
47
+ * @param sendCallsParams - The parameters to validate.
48
+ */
49
+ function validateSendCallsParams(sendCallsParams) {
50
+ // Basic structure validation
51
+ if (!sendCallsParams) {
52
+ throw rpcErrors.invalidParams("Missing send calls parameters");
53
+ }
54
+
55
+ // Validate version format
56
+ if (!sendCallsParams.version || typeof sendCallsParams.version !== "string") {
57
+ throw rpcErrors.invalidParams(`Invalid version: expected a string, got "${sendCallsParams.version || "undefined"}"`);
58
+ }
59
+
60
+ // Validate chainId format
61
+ if (!sendCallsParams.chainId || !isHexString(sendCallsParams.chainId)) {
62
+ throw rpcErrors.invalidParams("Invalid chainId: must be a valid hex string");
63
+ }
64
+
65
+ // Validate 'from' address
66
+ if (!sendCallsParams.from) {
67
+ throw rpcErrors.invalidParams("Missing 'from' address");
68
+ }
69
+ if (!isHexString(sendCallsParams.from) || !isValidAddress(sendCallsParams.from)) {
70
+ throw rpcErrors.invalidParams("Invalid 'from' address: must be a valid hex address");
71
+ }
72
+
73
+ // Validate calls array
74
+ if (!Array.isArray(sendCallsParams.calls) || sendCallsParams.calls.length === 0) {
75
+ throw rpcErrors.invalidParams("Invalid calls: must be a non-empty array");
76
+ }
77
+
78
+ // Validate each call
79
+ sendCallsParams.calls.forEach((call, index) => {
80
+ validateCall(call, index);
81
+ });
82
+ }
83
+ async function processMultipleTransactions({
84
+ from,
85
+ transactions,
86
+ request,
87
+ chainId,
88
+ getEthCode,
89
+ processTransactionBatch
90
+ }) {
91
+ // check if the account is upgraded with EIP-7702 for the given chain
92
+ const {
93
+ isSupported,
94
+ upgradeContractAddress,
95
+ delegationAddress
96
+ } = await getIsEip7702UpgradeSupported(from, chainId, getEthCode);
97
+ if (!isSupported) {
98
+ throw rpcErrors.methodNotSupported(`EIP-7702 upgrade is not supported for the given chain, ${chainId}`);
99
+ }
100
+ const hasAccountAlreadyUpgraded = Boolean(delegationAddress);
101
+ const batchRequest = {
102
+ batchId: generateBatchId(),
103
+ transactions,
104
+ eip7702UpgradeContractAddress: upgradeContractAddress,
105
+ requiredEip7702Upgrade: !hasAccountAlreadyUpgraded
106
+ };
107
+
108
+ // prepare batch transactions
109
+ return processTransactionBatch(batchRequest, request);
110
+ }
111
+ async function processSingleTransaction({
112
+ transactions,
113
+ request,
114
+ processTransactionBatch
115
+ }) {
116
+ const batchRequestWithSingleCall = {
117
+ batchId: generateBatchId(),
118
+ transactions
119
+ };
120
+ return processTransactionBatch(batchRequestWithSingleCall, request);
121
+ }
122
+
123
+ /**
124
+ * Handler for wallet_sendCalls (EIP-5792).
125
+ * Sends a batch of calls for an EIP-7702 upgraded account.
126
+ *
127
+ * @param request - The JRPC request with send calls parameters.
128
+ * @param getEthCode - Function to get the code at an address.
129
+ * @param context - Context containing required functions.
130
+ * @returns The batch ID.
131
+ */
132
+ async function walletSendCalls(request, getEthCode, context) {
133
+ const {
134
+ method,
135
+ params
136
+ } = request;
137
+ if (method !== EIP_5792_METHODS.WALLET_SEND_CALLS) {
138
+ return;
139
+ }
140
+ const sendCallsParams = Array.isArray(params) ? params[0] : params;
141
+ validateSendCallsParams(sendCallsParams);
142
+ const {
143
+ chainId,
144
+ from,
145
+ calls
146
+ } = sendCallsParams;
147
+ const transactions = calls.map(call => ({
148
+ params: call
149
+ }));
150
+ const isBatchCalls = Object.keys(transactions).length > 1;
151
+ if (!isBatchCalls) {
152
+ return processSingleTransaction({
153
+ transactions,
154
+ request,
155
+ processTransactionBatch: context.processTransactionBatch
156
+ });
157
+ }
158
+ return processMultipleTransactions({
159
+ from: from,
160
+ transactions,
161
+ request,
162
+ chainId,
163
+ getEthCode,
164
+ processTransactionBatch: context.processTransactionBatch
165
+ });
166
+ }
167
+
168
+ export { generateBatchId, processMultipleTransactions, processSingleTransaction, validateSendCallsParams, walletSendCalls };
@@ -0,0 +1,81 @@
1
+ import { addHexPrefix } from '@ethereumjs/util';
2
+ import { zeroAddress, encodeAbiParameters, parseAbiParameters, encodeFunctionData } from 'viem';
3
+ import { erc7821Abi } from '../utils/abis.js';
4
+ import { SUPPORTED_NETWORKS } from '../utils/constants.js';
5
+ import { EIP_7702_PREFIX } from '../utils/eip7702Types.js';
6
+
7
+ /**
8
+ * The MetaMask EIP-7702 Stateless Delegator contract address.
9
+ * Used as the delegation target for all supported networks.
10
+ */
11
+ const MetaMask_EIP7702_Stateless_Delegator = "0x63c0c19a282a1B52b07dD5a65b58948A07DAE32B";
12
+ async function getDelegationAddress(walletAddress, chainId, getEthCode) {
13
+ // query eth_getCode
14
+ const code = await getEthCode(walletAddress, chainId);
15
+ const normalizedCode = code ? addHexPrefix(code).toLowerCase() : "0x0";
16
+ const hasDelegation = normalizedCode.length === 48 && normalizedCode.startsWith(EIP_7702_PREFIX);
17
+ if (hasDelegation) {
18
+ const delegationAddress = addHexPrefix(normalizedCode.slice(EIP_7702_PREFIX.length));
19
+ return delegationAddress;
20
+ }
21
+ return null;
22
+ }
23
+
24
+ /**
25
+ * Check if the wallet is supported for EIP-7702 on provided chain.
26
+ */
27
+ async function getIsEip7702UpgradeSupported(address, chainId, getEthCode) {
28
+ if (!SUPPORTED_NETWORKS[chainId]) {
29
+ return {
30
+ isSupported: false,
31
+ delegationAddress: null
32
+ };
33
+ }
34
+ const delegationAddress = await getDelegationAddress(address, chainId, getEthCode);
35
+ return {
36
+ isSupported: true,
37
+ upgradeContractAddress: MetaMask_EIP7702_Stateless_Delegator,
38
+ delegationAddress
39
+ };
40
+ }
41
+
42
+ /**
43
+ * Generate an EIP-7702 batch transaction.
44
+ *
45
+ * Reference: {@link https://github.com/MetaMask/core/blob/main/packages/transaction-controller/src/utils/eip7702.ts#L119}
46
+ *
47
+ * @param from - The sender address.
48
+ * @param transactions - The transactions to batch.
49
+ * @returns The batch transaction.
50
+ */
51
+ function generateEIP7702BatchTransaction(from, transactions) {
52
+ const calls = transactions.map(transaction => {
53
+ const {
54
+ data,
55
+ to,
56
+ value
57
+ } = transaction;
58
+ return {
59
+ target: to !== null && to !== void 0 ? to : zeroAddress,
60
+ value: BigInt(value !== null && value !== void 0 ? value : "0x0"),
61
+ callData: data !== null && data !== void 0 ? data : "0x"
62
+ };
63
+ });
64
+
65
+ // Single batch mode, no opData.
66
+ const mode = "0x01".padEnd(66, "0");
67
+
68
+ // Encode the calls array as (address target, uint256 value, bytes callData)[]
69
+ const executionData = encodeAbiParameters(parseAbiParameters("(address target, uint256 value, bytes callData)[]"), [calls]);
70
+ const data = encodeFunctionData({
71
+ abi: erc7821Abi,
72
+ functionName: "execute",
73
+ args: [mode, executionData]
74
+ });
75
+ return {
76
+ data,
77
+ to: from
78
+ };
79
+ }
80
+
81
+ export { MetaMask_EIP7702_Stateless_Delegator, generateEIP7702BatchTransaction, getDelegationAddress, getIsEip7702UpgradeSupported };
@@ -0,0 +1,33 @@
1
+ import { rpcErrors } from '@web3auth/auth';
2
+ import { getDelegationAddress } from './eip7702Utils.js';
3
+
4
+ /**
5
+ * Get the EIP7702 upgrade status of an EOA.
6
+ *
7
+ * @param request - The request object.
8
+ * @param getEthCode - The function to get the code of an account.
9
+ * @returns The upgrade status.
10
+ */
11
+ async function walletGetUpgradeStatus(request, getEthCode) {
12
+ const {
13
+ method,
14
+ params
15
+ } = request;
16
+ if (method !== "wallet_getAccountUpgradeStatus") {
17
+ return;
18
+ }
19
+ const getUpgradeStatusParam = Array.isArray(params) ? params[0] : params;
20
+ const account = getUpgradeStatusParam.account;
21
+ const chainId = getUpgradeStatusParam.chainId;
22
+ if (!account || !chainId) {
23
+ throw rpcErrors.invalidParams("wallet_getAccountUpgradeStatus: account and chainId are required");
24
+ }
25
+ const delegationAddress = await getDelegationAddress(account, chainId, getEthCode);
26
+ const isUpgraded = delegationAddress !== null;
27
+ return {
28
+ isUpgraded,
29
+ implementation: delegationAddress !== null && delegationAddress !== void 0 ? delegationAddress : "0x0"
30
+ };
31
+ }
32
+
33
+ export { walletGetUpgradeStatus };