@metamask/money-account-upgrade-controller 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.2]
11
+
12
+ ### Changed
13
+
14
+ - Bump `@metamask/keyring-controller` from `^25.4.0` to `^25.5.0` ([#8722](https://github.com/MetaMask/core/pull/8722))
15
+ - Bump `@metamask/chomp-api-service` from `^3.0.0` to `^3.0.1` ([#8755](https://github.com/MetaMask/core/pull/8755))
16
+ - Bump `@metamask/network-controller` from `^30.1.0` to `^31.0.0` ([#8755](https://github.com/MetaMask/core/pull/8755))
17
+
18
+ ## [1.3.1]
19
+
20
+ ### Changed
21
+
22
+ - Bump `@metamask/keyring-controller` from `^25.3.0` to `^25.4.0` ([#8665](https://github.com/MetaMask/core/pull/8665))
23
+
24
+ ### Fixed
25
+
26
+ - Fix the ChompApiService:createUpgrade call in the EIP-7702 auth step, pasing correct arguments ([#8657](https://github.com/MetaMask/core/pull/8657))
27
+
10
28
  ## [1.3.0]
11
29
 
12
30
  ### Changed
@@ -42,7 +60,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
42
60
 
43
61
  - Add `MoneyAccountUpgradeController` with `upgradeAccount` method ([#8426](https://github.com/MetaMask/core/pull/8426))
44
62
 
45
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/money-account-upgrade-controller@1.3.0...HEAD
63
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/money-account-upgrade-controller@1.3.2...HEAD
64
+ [1.3.2]: https://github.com/MetaMask/core/compare/@metamask/money-account-upgrade-controller@1.3.1...@metamask/money-account-upgrade-controller@1.3.2
65
+ [1.3.1]: https://github.com/MetaMask/core/compare/@metamask/money-account-upgrade-controller@1.3.0...@metamask/money-account-upgrade-controller@1.3.1
46
66
  [1.3.0]: https://github.com/MetaMask/core/compare/@metamask/money-account-upgrade-controller@1.2.0...@metamask/money-account-upgrade-controller@1.3.0
47
67
  [1.2.0]: https://github.com/MetaMask/core/compare/@metamask/money-account-upgrade-controller@1.1.0...@metamask/money-account-upgrade-controller@1.2.0
48
68
  [1.1.0]: https://github.com/MetaMask/core/compare/@metamask/money-account-upgrade-controller@1.0.0...@metamask/money-account-upgrade-controller@1.1.0
@@ -58,9 +58,9 @@ exports.eip7702AuthorizationStep = {
58
58
  s,
59
59
  v,
60
60
  yParity,
61
- address,
62
- chainId: chainIdDecimal.toString(),
63
- nonce: nonce.toString(),
61
+ address: delegatorImplAddress,
62
+ chainId,
63
+ nonce: (0, utils_1.add0x)(nonce.toString(16)),
64
64
  });
65
65
  return 'completed';
66
66
  },
@@ -1 +1 @@
1
- {"version":3,"file":"eip-7702-authorization.cjs","sourceRoot":"","sources":["../../src/steps/eip-7702-authorization.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Provider } from '@metamask/network-controller';\nimport { add0x, isStrictHexString } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport type { Step, StepContext } from './step';\n\nconst EIP_7702_DELEGATION_PREFIX = '0xef0100';\n// '0x' (2) + 'ef0100' (6) + 20-byte address (40) = 48 characters.\nconst EIP_7702_DELEGATED_CODE_LENGTH = 48;\n\n// 65-byte signature: 32-byte r + 32-byte s + 1-byte v.\n// '0x' (2) + 32 bytes (64) + 32 bytes (64) + 1 byte (2) = 132 characters.\nconst SIGNATURE_HEX_LENGTH = 132;\n// '0x' + 32-byte r = 66 characters.\nconst R_END_INDEX = 66;\n// r (66 chars) + 32-byte s (64 chars) = 130 characters.\nconst S_END_INDEX = 130;\nconst V_END_INDEX = SIGNATURE_HEX_LENGTH;\n// v = 27 means yParity = 0; v = 28 means yParity = 1.\nconst V_BASE = 27;\n\n/**\n * Submits the EIP-7702 delegation-slot authorization to CHOMP so the Money\n * Account can be upgraded to a smart account pointed at the configured\n * delegator impl.\n *\n * The step:\n *\n * 1. Reads the account's on-chain code. If it is already delegated to the\n * configured `delegatorImplAddress`, reports `'already-done'`. If it is\n * delegated to a different address, throws rather than silently\n * overwriting an existing delegation.\n * 2. Fetches the account's current on-chain transaction count — CHOMP\n * validates the nonce matches when it applies the authorization.\n * 3. Signs the EIP-7702 authorization `{ chainId, delegatorImpl, nonce }`\n * with the Money Account's key via the keyring.\n * 4. Splits the 65-byte signature into `r`, `s`, `v`, `yParity` and submits\n * it to `POST /v1/account-upgrade`.\n */\nexport const eip7702AuthorizationStep: Step = {\n name: 'eip-7702-authorization',\n async run({ messenger, address, chainId, delegatorImplAddress }) {\n const provider = getProvider(messenger, chainId);\n\n const existingDelegation = await fetchDelegationAddress(provider, address);\n if (existingDelegation !== undefined) {\n if (existingDelegation === delegatorImplAddress.toLowerCase()) {\n return 'already-done';\n }\n throw new Error(\n `Account ${address} is already upgraded to another smart account: ${existingDelegation}.`,\n );\n }\n\n const chainIdDecimal = parseInt(chainId, 16);\n const nonce = await fetchNonce(provider, address);\n\n const signature = await messenger.call(\n 'KeyringController:signEip7702Authorization',\n {\n chainId: chainIdDecimal,\n contractAddress: delegatorImplAddress,\n nonce,\n from: address,\n },\n );\n\n const { r, s, v, yParity } = splitEip7702Signature(signature);\n\n await messenger.call('ChompApiService:createUpgrade', {\n r,\n s,\n v,\n yParity,\n address,\n chainId: chainIdDecimal.toString(),\n nonce: nonce.toString(),\n });\n\n return 'completed';\n },\n};\n\n/**\n * Splits a 65-byte ECDSA signature produced by\n * `KeyringController:signEip7702Authorization` into its `r`, `s`, `v`\n * components and derives `yParity` (`0` for `v = 27`, `1` for `v = 28`).\n *\n * @param signature - A 0x-prefixed 132-character hex string. Accepted in any\n * case; normalized to lowercase before validation.\n * @returns The signature components.\n */\nfunction splitEip7702Signature(signature: unknown): {\n r: Hex;\n s: Hex;\n v: number;\n yParity: 0 | 1;\n} {\n const normalized =\n typeof signature === 'string' ? signature.toLowerCase() : signature;\n\n if (\n !isStrictHexString(normalized) ||\n normalized.length !== SIGNATURE_HEX_LENGTH\n ) {\n throw new Error(\n `Expected a 0x-prefixed 65-byte signature from signEip7702Authorization, got ${JSON.stringify(signature)}`,\n );\n }\n\n // eslint-disable-next-line id-length\n const v = parseInt(normalized.slice(S_END_INDEX, V_END_INDEX), 16);\n if (v !== 27 && v !== 28) {\n throw new Error(\n `Expected v to be 27 or 28 in signEip7702Authorization signature, got ${v}`,\n );\n }\n\n return {\n r: normalized.slice(0, R_END_INDEX) as Hex,\n s: add0x(normalized.slice(R_END_INDEX, S_END_INDEX)),\n v,\n yParity: v === V_BASE ? 0 : 1,\n };\n}\n\n/**\n * Reads the account's on-chain code and, if the account is currently\n * delegated via EIP-7702, returns the implementation address the delegation\n * points at. Returns `undefined` if the account has no code (a plain EOA).\n * Throws if the code is present but not a valid EIP-7702 delegation, since\n * that means the address is a regular contract and is not eligible for\n * upgrade.\n *\n * @param provider - JSON-RPC provider for the target chain.\n * @param address - The Money Account address.\n * @returns The current delegation address, or `undefined` if none.\n */\nasync function fetchDelegationAddress(\n provider: Provider,\n address: Hex,\n): Promise<Hex | undefined> {\n const code = await provider.request({\n method: 'eth_getCode',\n params: [address, 'latest'],\n });\n\n if (typeof code !== 'string' || !code.startsWith('0x')) {\n throw new Error(\n `Expected 0x-prefixed hex string from eth_getCode, got ${JSON.stringify(code)}`,\n );\n }\n\n const normalized = code.toLowerCase();\n\n if (normalized === '0x') {\n return undefined;\n }\n\n if (\n normalized.length === EIP_7702_DELEGATED_CODE_LENGTH &&\n normalized.startsWith(EIP_7702_DELEGATION_PREFIX)\n ) {\n return add0x(normalized.slice(EIP_7702_DELEGATION_PREFIX.length));\n }\n\n throw new Error(\n `Account ${address} has unexpected on-chain code; expected either no code or an EIP-7702 delegation.`,\n );\n}\n\n/**\n * Fetches the current on-chain transaction count for the given address by\n * issuing an `eth_getTransactionCount` RPC request.\n *\n * @param provider - JSON-RPC provider for the target chain.\n * @param address - The Money Account address.\n * @returns The current nonce as a decimal number.\n */\nasync function fetchNonce(provider: Provider, address: Hex): Promise<number> {\n const nonceHex = await provider.request({\n method: 'eth_getTransactionCount',\n params: [address, 'latest'],\n });\n\n if (!isStrictHexString(nonceHex)) {\n throw new Error(\n `Expected hex string from eth_getTransactionCount, got ${JSON.stringify(nonceHex)}`,\n );\n }\n\n return parseInt(nonceHex, 16);\n}\n\n/**\n * Resolves the JSON-RPC provider for the given chain via NetworkController.\n *\n * @param messenger - The upgrade controller messenger.\n * @param chainId - The chain to query.\n * @returns The provider for that chain.\n */\nfunction getProvider(\n messenger: StepContext['messenger'],\n chainId: Hex,\n): Provider {\n const networkClientId = messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n chainId,\n );\n return messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n ).provider;\n}\n"]}
1
+ {"version":3,"file":"eip-7702-authorization.cjs","sourceRoot":"","sources":["../../src/steps/eip-7702-authorization.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Provider } from '@metamask/network-controller';\nimport { add0x, isStrictHexString } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport type { Step, StepContext } from './step';\n\nconst EIP_7702_DELEGATION_PREFIX = '0xef0100';\n// '0x' (2) + 'ef0100' (6) + 20-byte address (40) = 48 characters.\nconst EIP_7702_DELEGATED_CODE_LENGTH = 48;\n\n// 65-byte signature: 32-byte r + 32-byte s + 1-byte v.\n// '0x' (2) + 32 bytes (64) + 32 bytes (64) + 1 byte (2) = 132 characters.\nconst SIGNATURE_HEX_LENGTH = 132;\n// '0x' + 32-byte r = 66 characters.\nconst R_END_INDEX = 66;\n// r (66 chars) + 32-byte s (64 chars) = 130 characters.\nconst S_END_INDEX = 130;\nconst V_END_INDEX = SIGNATURE_HEX_LENGTH;\n// v = 27 means yParity = 0; v = 28 means yParity = 1.\nconst V_BASE = 27;\n\n/**\n * Submits the EIP-7702 delegation-slot authorization to CHOMP so the Money\n * Account can be upgraded to a smart account pointed at the configured\n * delegator impl.\n *\n * The step:\n *\n * 1. Reads the account's on-chain code. If it is already delegated to the\n * configured `delegatorImplAddress`, reports `'already-done'`. If it is\n * delegated to a different address, throws rather than silently\n * overwriting an existing delegation.\n * 2. Fetches the account's current on-chain transaction count — CHOMP\n * validates the nonce matches when it applies the authorization.\n * 3. Signs the EIP-7702 authorization `{ chainId, delegatorImpl, nonce }`\n * with the Money Account's key via the keyring.\n * 4. Splits the 65-byte signature into `r`, `s`, `v`, `yParity` and submits\n * it to `POST /v1/account-upgrade`.\n */\nexport const eip7702AuthorizationStep: Step = {\n name: 'eip-7702-authorization',\n async run({ messenger, address, chainId, delegatorImplAddress }) {\n const provider = getProvider(messenger, chainId);\n\n const existingDelegation = await fetchDelegationAddress(provider, address);\n if (existingDelegation !== undefined) {\n if (existingDelegation === delegatorImplAddress.toLowerCase()) {\n return 'already-done';\n }\n throw new Error(\n `Account ${address} is already upgraded to another smart account: ${existingDelegation}.`,\n );\n }\n\n const chainIdDecimal = parseInt(chainId, 16);\n const nonce = await fetchNonce(provider, address);\n\n const signature = await messenger.call(\n 'KeyringController:signEip7702Authorization',\n {\n chainId: chainIdDecimal,\n contractAddress: delegatorImplAddress,\n nonce,\n from: address,\n },\n );\n\n const { r, s, v, yParity } = splitEip7702Signature(signature);\n\n await messenger.call('ChompApiService:createUpgrade', {\n r,\n s,\n v,\n yParity,\n address: delegatorImplAddress,\n chainId,\n nonce: add0x(nonce.toString(16)),\n });\n\n return 'completed';\n },\n};\n\n/**\n * Splits a 65-byte ECDSA signature produced by\n * `KeyringController:signEip7702Authorization` into its `r`, `s`, `v`\n * components and derives `yParity` (`0` for `v = 27`, `1` for `v = 28`).\n *\n * @param signature - A 0x-prefixed 132-character hex string. Accepted in any\n * case; normalized to lowercase before validation.\n * @returns The signature components.\n */\nfunction splitEip7702Signature(signature: unknown): {\n r: Hex;\n s: Hex;\n v: number;\n yParity: 0 | 1;\n} {\n const normalized =\n typeof signature === 'string' ? signature.toLowerCase() : signature;\n\n if (\n !isStrictHexString(normalized) ||\n normalized.length !== SIGNATURE_HEX_LENGTH\n ) {\n throw new Error(\n `Expected a 0x-prefixed 65-byte signature from signEip7702Authorization, got ${JSON.stringify(signature)}`,\n );\n }\n\n // eslint-disable-next-line id-length\n const v = parseInt(normalized.slice(S_END_INDEX, V_END_INDEX), 16);\n if (v !== 27 && v !== 28) {\n throw new Error(\n `Expected v to be 27 or 28 in signEip7702Authorization signature, got ${v}`,\n );\n }\n\n return {\n r: normalized.slice(0, R_END_INDEX) as Hex,\n s: add0x(normalized.slice(R_END_INDEX, S_END_INDEX)),\n v,\n yParity: v === V_BASE ? 0 : 1,\n };\n}\n\n/**\n * Reads the account's on-chain code and, if the account is currently\n * delegated via EIP-7702, returns the implementation address the delegation\n * points at. Returns `undefined` if the account has no code (a plain EOA).\n * Throws if the code is present but not a valid EIP-7702 delegation, since\n * that means the address is a regular contract and is not eligible for\n * upgrade.\n *\n * @param provider - JSON-RPC provider for the target chain.\n * @param address - The Money Account address.\n * @returns The current delegation address, or `undefined` if none.\n */\nasync function fetchDelegationAddress(\n provider: Provider,\n address: Hex,\n): Promise<Hex | undefined> {\n const code = await provider.request({\n method: 'eth_getCode',\n params: [address, 'latest'],\n });\n\n if (typeof code !== 'string' || !code.startsWith('0x')) {\n throw new Error(\n `Expected 0x-prefixed hex string from eth_getCode, got ${JSON.stringify(code)}`,\n );\n }\n\n const normalized = code.toLowerCase();\n\n if (normalized === '0x') {\n return undefined;\n }\n\n if (\n normalized.length === EIP_7702_DELEGATED_CODE_LENGTH &&\n normalized.startsWith(EIP_7702_DELEGATION_PREFIX)\n ) {\n return add0x(normalized.slice(EIP_7702_DELEGATION_PREFIX.length));\n }\n\n throw new Error(\n `Account ${address} has unexpected on-chain code; expected either no code or an EIP-7702 delegation.`,\n );\n}\n\n/**\n * Fetches the current on-chain transaction count for the given address by\n * issuing an `eth_getTransactionCount` RPC request.\n *\n * @param provider - JSON-RPC provider for the target chain.\n * @param address - The Money Account address.\n * @returns The current nonce as a decimal number.\n */\nasync function fetchNonce(provider: Provider, address: Hex): Promise<number> {\n const nonceHex = await provider.request({\n method: 'eth_getTransactionCount',\n params: [address, 'latest'],\n });\n\n if (!isStrictHexString(nonceHex)) {\n throw new Error(\n `Expected hex string from eth_getTransactionCount, got ${JSON.stringify(nonceHex)}`,\n );\n }\n\n return parseInt(nonceHex, 16);\n}\n\n/**\n * Resolves the JSON-RPC provider for the given chain via NetworkController.\n *\n * @param messenger - The upgrade controller messenger.\n * @param chainId - The chain to query.\n * @returns The provider for that chain.\n */\nfunction getProvider(\n messenger: StepContext['messenger'],\n chainId: Hex,\n): Provider {\n const networkClientId = messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n chainId,\n );\n return messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n ).provider;\n}\n"]}
@@ -55,9 +55,9 @@ export const eip7702AuthorizationStep = {
55
55
  s,
56
56
  v,
57
57
  yParity,
58
- address,
59
- chainId: chainIdDecimal.toString(),
60
- nonce: nonce.toString(),
58
+ address: delegatorImplAddress,
59
+ chainId,
60
+ nonce: add0x(nonce.toString(16)),
61
61
  });
62
62
  return 'completed';
63
63
  },
@@ -1 +1 @@
1
- {"version":3,"file":"eip-7702-authorization.mjs","sourceRoot":"","sources":["../../src/steps/eip-7702-authorization.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Provider } from '@metamask/network-controller';\nimport { add0x, isStrictHexString } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport type { Step, StepContext } from './step';\n\nconst EIP_7702_DELEGATION_PREFIX = '0xef0100';\n// '0x' (2) + 'ef0100' (6) + 20-byte address (40) = 48 characters.\nconst EIP_7702_DELEGATED_CODE_LENGTH = 48;\n\n// 65-byte signature: 32-byte r + 32-byte s + 1-byte v.\n// '0x' (2) + 32 bytes (64) + 32 bytes (64) + 1 byte (2) = 132 characters.\nconst SIGNATURE_HEX_LENGTH = 132;\n// '0x' + 32-byte r = 66 characters.\nconst R_END_INDEX = 66;\n// r (66 chars) + 32-byte s (64 chars) = 130 characters.\nconst S_END_INDEX = 130;\nconst V_END_INDEX = SIGNATURE_HEX_LENGTH;\n// v = 27 means yParity = 0; v = 28 means yParity = 1.\nconst V_BASE = 27;\n\n/**\n * Submits the EIP-7702 delegation-slot authorization to CHOMP so the Money\n * Account can be upgraded to a smart account pointed at the configured\n * delegator impl.\n *\n * The step:\n *\n * 1. Reads the account's on-chain code. If it is already delegated to the\n * configured `delegatorImplAddress`, reports `'already-done'`. If it is\n * delegated to a different address, throws rather than silently\n * overwriting an existing delegation.\n * 2. Fetches the account's current on-chain transaction count — CHOMP\n * validates the nonce matches when it applies the authorization.\n * 3. Signs the EIP-7702 authorization `{ chainId, delegatorImpl, nonce }`\n * with the Money Account's key via the keyring.\n * 4. Splits the 65-byte signature into `r`, `s`, `v`, `yParity` and submits\n * it to `POST /v1/account-upgrade`.\n */\nexport const eip7702AuthorizationStep: Step = {\n name: 'eip-7702-authorization',\n async run({ messenger, address, chainId, delegatorImplAddress }) {\n const provider = getProvider(messenger, chainId);\n\n const existingDelegation = await fetchDelegationAddress(provider, address);\n if (existingDelegation !== undefined) {\n if (existingDelegation === delegatorImplAddress.toLowerCase()) {\n return 'already-done';\n }\n throw new Error(\n `Account ${address} is already upgraded to another smart account: ${existingDelegation}.`,\n );\n }\n\n const chainIdDecimal = parseInt(chainId, 16);\n const nonce = await fetchNonce(provider, address);\n\n const signature = await messenger.call(\n 'KeyringController:signEip7702Authorization',\n {\n chainId: chainIdDecimal,\n contractAddress: delegatorImplAddress,\n nonce,\n from: address,\n },\n );\n\n const { r, s, v, yParity } = splitEip7702Signature(signature);\n\n await messenger.call('ChompApiService:createUpgrade', {\n r,\n s,\n v,\n yParity,\n address,\n chainId: chainIdDecimal.toString(),\n nonce: nonce.toString(),\n });\n\n return 'completed';\n },\n};\n\n/**\n * Splits a 65-byte ECDSA signature produced by\n * `KeyringController:signEip7702Authorization` into its `r`, `s`, `v`\n * components and derives `yParity` (`0` for `v = 27`, `1` for `v = 28`).\n *\n * @param signature - A 0x-prefixed 132-character hex string. Accepted in any\n * case; normalized to lowercase before validation.\n * @returns The signature components.\n */\nfunction splitEip7702Signature(signature: unknown): {\n r: Hex;\n s: Hex;\n v: number;\n yParity: 0 | 1;\n} {\n const normalized =\n typeof signature === 'string' ? signature.toLowerCase() : signature;\n\n if (\n !isStrictHexString(normalized) ||\n normalized.length !== SIGNATURE_HEX_LENGTH\n ) {\n throw new Error(\n `Expected a 0x-prefixed 65-byte signature from signEip7702Authorization, got ${JSON.stringify(signature)}`,\n );\n }\n\n // eslint-disable-next-line id-length\n const v = parseInt(normalized.slice(S_END_INDEX, V_END_INDEX), 16);\n if (v !== 27 && v !== 28) {\n throw new Error(\n `Expected v to be 27 or 28 in signEip7702Authorization signature, got ${v}`,\n );\n }\n\n return {\n r: normalized.slice(0, R_END_INDEX) as Hex,\n s: add0x(normalized.slice(R_END_INDEX, S_END_INDEX)),\n v,\n yParity: v === V_BASE ? 0 : 1,\n };\n}\n\n/**\n * Reads the account's on-chain code and, if the account is currently\n * delegated via EIP-7702, returns the implementation address the delegation\n * points at. Returns `undefined` if the account has no code (a plain EOA).\n * Throws if the code is present but not a valid EIP-7702 delegation, since\n * that means the address is a regular contract and is not eligible for\n * upgrade.\n *\n * @param provider - JSON-RPC provider for the target chain.\n * @param address - The Money Account address.\n * @returns The current delegation address, or `undefined` if none.\n */\nasync function fetchDelegationAddress(\n provider: Provider,\n address: Hex,\n): Promise<Hex | undefined> {\n const code = await provider.request({\n method: 'eth_getCode',\n params: [address, 'latest'],\n });\n\n if (typeof code !== 'string' || !code.startsWith('0x')) {\n throw new Error(\n `Expected 0x-prefixed hex string from eth_getCode, got ${JSON.stringify(code)}`,\n );\n }\n\n const normalized = code.toLowerCase();\n\n if (normalized === '0x') {\n return undefined;\n }\n\n if (\n normalized.length === EIP_7702_DELEGATED_CODE_LENGTH &&\n normalized.startsWith(EIP_7702_DELEGATION_PREFIX)\n ) {\n return add0x(normalized.slice(EIP_7702_DELEGATION_PREFIX.length));\n }\n\n throw new Error(\n `Account ${address} has unexpected on-chain code; expected either no code or an EIP-7702 delegation.`,\n );\n}\n\n/**\n * Fetches the current on-chain transaction count for the given address by\n * issuing an `eth_getTransactionCount` RPC request.\n *\n * @param provider - JSON-RPC provider for the target chain.\n * @param address - The Money Account address.\n * @returns The current nonce as a decimal number.\n */\nasync function fetchNonce(provider: Provider, address: Hex): Promise<number> {\n const nonceHex = await provider.request({\n method: 'eth_getTransactionCount',\n params: [address, 'latest'],\n });\n\n if (!isStrictHexString(nonceHex)) {\n throw new Error(\n `Expected hex string from eth_getTransactionCount, got ${JSON.stringify(nonceHex)}`,\n );\n }\n\n return parseInt(nonceHex, 16);\n}\n\n/**\n * Resolves the JSON-RPC provider for the given chain via NetworkController.\n *\n * @param messenger - The upgrade controller messenger.\n * @param chainId - The chain to query.\n * @returns The provider for that chain.\n */\nfunction getProvider(\n messenger: StepContext['messenger'],\n chainId: Hex,\n): Provider {\n const networkClientId = messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n chainId,\n );\n return messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n ).provider;\n}\n"]}
1
+ {"version":3,"file":"eip-7702-authorization.mjs","sourceRoot":"","sources":["../../src/steps/eip-7702-authorization.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Provider } from '@metamask/network-controller';\nimport { add0x, isStrictHexString } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport type { Step, StepContext } from './step';\n\nconst EIP_7702_DELEGATION_PREFIX = '0xef0100';\n// '0x' (2) + 'ef0100' (6) + 20-byte address (40) = 48 characters.\nconst EIP_7702_DELEGATED_CODE_LENGTH = 48;\n\n// 65-byte signature: 32-byte r + 32-byte s + 1-byte v.\n// '0x' (2) + 32 bytes (64) + 32 bytes (64) + 1 byte (2) = 132 characters.\nconst SIGNATURE_HEX_LENGTH = 132;\n// '0x' + 32-byte r = 66 characters.\nconst R_END_INDEX = 66;\n// r (66 chars) + 32-byte s (64 chars) = 130 characters.\nconst S_END_INDEX = 130;\nconst V_END_INDEX = SIGNATURE_HEX_LENGTH;\n// v = 27 means yParity = 0; v = 28 means yParity = 1.\nconst V_BASE = 27;\n\n/**\n * Submits the EIP-7702 delegation-slot authorization to CHOMP so the Money\n * Account can be upgraded to a smart account pointed at the configured\n * delegator impl.\n *\n * The step:\n *\n * 1. Reads the account's on-chain code. If it is already delegated to the\n * configured `delegatorImplAddress`, reports `'already-done'`. If it is\n * delegated to a different address, throws rather than silently\n * overwriting an existing delegation.\n * 2. Fetches the account's current on-chain transaction count — CHOMP\n * validates the nonce matches when it applies the authorization.\n * 3. Signs the EIP-7702 authorization `{ chainId, delegatorImpl, nonce }`\n * with the Money Account's key via the keyring.\n * 4. Splits the 65-byte signature into `r`, `s`, `v`, `yParity` and submits\n * it to `POST /v1/account-upgrade`.\n */\nexport const eip7702AuthorizationStep: Step = {\n name: 'eip-7702-authorization',\n async run({ messenger, address, chainId, delegatorImplAddress }) {\n const provider = getProvider(messenger, chainId);\n\n const existingDelegation = await fetchDelegationAddress(provider, address);\n if (existingDelegation !== undefined) {\n if (existingDelegation === delegatorImplAddress.toLowerCase()) {\n return 'already-done';\n }\n throw new Error(\n `Account ${address} is already upgraded to another smart account: ${existingDelegation}.`,\n );\n }\n\n const chainIdDecimal = parseInt(chainId, 16);\n const nonce = await fetchNonce(provider, address);\n\n const signature = await messenger.call(\n 'KeyringController:signEip7702Authorization',\n {\n chainId: chainIdDecimal,\n contractAddress: delegatorImplAddress,\n nonce,\n from: address,\n },\n );\n\n const { r, s, v, yParity } = splitEip7702Signature(signature);\n\n await messenger.call('ChompApiService:createUpgrade', {\n r,\n s,\n v,\n yParity,\n address: delegatorImplAddress,\n chainId,\n nonce: add0x(nonce.toString(16)),\n });\n\n return 'completed';\n },\n};\n\n/**\n * Splits a 65-byte ECDSA signature produced by\n * `KeyringController:signEip7702Authorization` into its `r`, `s`, `v`\n * components and derives `yParity` (`0` for `v = 27`, `1` for `v = 28`).\n *\n * @param signature - A 0x-prefixed 132-character hex string. Accepted in any\n * case; normalized to lowercase before validation.\n * @returns The signature components.\n */\nfunction splitEip7702Signature(signature: unknown): {\n r: Hex;\n s: Hex;\n v: number;\n yParity: 0 | 1;\n} {\n const normalized =\n typeof signature === 'string' ? signature.toLowerCase() : signature;\n\n if (\n !isStrictHexString(normalized) ||\n normalized.length !== SIGNATURE_HEX_LENGTH\n ) {\n throw new Error(\n `Expected a 0x-prefixed 65-byte signature from signEip7702Authorization, got ${JSON.stringify(signature)}`,\n );\n }\n\n // eslint-disable-next-line id-length\n const v = parseInt(normalized.slice(S_END_INDEX, V_END_INDEX), 16);\n if (v !== 27 && v !== 28) {\n throw new Error(\n `Expected v to be 27 or 28 in signEip7702Authorization signature, got ${v}`,\n );\n }\n\n return {\n r: normalized.slice(0, R_END_INDEX) as Hex,\n s: add0x(normalized.slice(R_END_INDEX, S_END_INDEX)),\n v,\n yParity: v === V_BASE ? 0 : 1,\n };\n}\n\n/**\n * Reads the account's on-chain code and, if the account is currently\n * delegated via EIP-7702, returns the implementation address the delegation\n * points at. Returns `undefined` if the account has no code (a plain EOA).\n * Throws if the code is present but not a valid EIP-7702 delegation, since\n * that means the address is a regular contract and is not eligible for\n * upgrade.\n *\n * @param provider - JSON-RPC provider for the target chain.\n * @param address - The Money Account address.\n * @returns The current delegation address, or `undefined` if none.\n */\nasync function fetchDelegationAddress(\n provider: Provider,\n address: Hex,\n): Promise<Hex | undefined> {\n const code = await provider.request({\n method: 'eth_getCode',\n params: [address, 'latest'],\n });\n\n if (typeof code !== 'string' || !code.startsWith('0x')) {\n throw new Error(\n `Expected 0x-prefixed hex string from eth_getCode, got ${JSON.stringify(code)}`,\n );\n }\n\n const normalized = code.toLowerCase();\n\n if (normalized === '0x') {\n return undefined;\n }\n\n if (\n normalized.length === EIP_7702_DELEGATED_CODE_LENGTH &&\n normalized.startsWith(EIP_7702_DELEGATION_PREFIX)\n ) {\n return add0x(normalized.slice(EIP_7702_DELEGATION_PREFIX.length));\n }\n\n throw new Error(\n `Account ${address} has unexpected on-chain code; expected either no code or an EIP-7702 delegation.`,\n );\n}\n\n/**\n * Fetches the current on-chain transaction count for the given address by\n * issuing an `eth_getTransactionCount` RPC request.\n *\n * @param provider - JSON-RPC provider for the target chain.\n * @param address - The Money Account address.\n * @returns The current nonce as a decimal number.\n */\nasync function fetchNonce(provider: Provider, address: Hex): Promise<number> {\n const nonceHex = await provider.request({\n method: 'eth_getTransactionCount',\n params: [address, 'latest'],\n });\n\n if (!isStrictHexString(nonceHex)) {\n throw new Error(\n `Expected hex string from eth_getTransactionCount, got ${JSON.stringify(nonceHex)}`,\n );\n }\n\n return parseInt(nonceHex, 16);\n}\n\n/**\n * Resolves the JSON-RPC provider for the given chain via NetworkController.\n *\n * @param messenger - The upgrade controller messenger.\n * @param chainId - The chain to query.\n * @returns The provider for that chain.\n */\nfunction getProvider(\n messenger: StepContext['messenger'],\n chainId: Hex,\n): Provider {\n const networkClientId = messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n chainId,\n );\n return messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n ).provider;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask/money-account-upgrade-controller",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "MetaMask Money account upgrade controller",
5
5
  "keywords": [
6
6
  "Ethereum",
@@ -54,10 +54,10 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@metamask/base-controller": "^9.1.0",
57
- "@metamask/chomp-api-service": "^3.0.0",
58
- "@metamask/keyring-controller": "^25.3.0",
57
+ "@metamask/chomp-api-service": "^3.0.1",
58
+ "@metamask/keyring-controller": "^25.5.0",
59
59
  "@metamask/messenger": "^1.2.0",
60
- "@metamask/network-controller": "^30.1.0",
60
+ "@metamask/network-controller": "^31.0.0",
61
61
  "@metamask/utils": "^11.9.0"
62
62
  },
63
63
  "devDependencies": {