@metamask-previews/network-enablement-controller 3.1.0-preview-cc7e30d0 → 3.1.0-preview-5460dce

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,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - Add `ChainService` module for SLIP-44 coin type resolution ([#7034](https://github.com/MetaMask/core/pull/7034))
13
+ - Add `getSlip44ByChainId()` function to resolve SLIP-44 coin types from EVM chain IDs
14
+ - Add `getNativeCaip19()` function to generate CAIP-19 asset identifiers for native currencies
15
+ - Add support for fetching chain metadata from chainid.network with caching
16
+ - Add `@metamask/slip44` dependency for symbol-to-coin-type mapping
17
+
18
+ ### Fixed
19
+
20
+ - include additional popular networks now enabled by default ([#7014](https://github.com/MetaMask/core/pull/7014))
21
+
10
22
  ## [3.1.0]
11
23
 
12
24
  ### Added
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getNativeCaip19 = exports.getSlip44ByChainId = void 0;
7
+ const controller_utils_1 = require("@metamask/controller-utils");
8
+ // @ts-expect-error - JSON imports need resolveJsonModule
9
+ const slip44_json_1 = __importDefault(require("@metamask/slip44/slip44.json"));
10
+ const utils_1 = require("@metamask/utils");
11
+ /** ---- Constants / Cache ---- */
12
+ const CHAINS_JSON_URL = 'https://chainid.network/chains.json';
13
+ const CACHE_DURATION_MS = 30 * 60 * 1000; // 30 min
14
+ let chainsCache = null;
15
+ let chainsFetchedAt = 0;
16
+ let inflight = null;
17
+ /** Build a quick symbol -> coinType map from slip44 dataset */
18
+ const SLIP44_BY_SYMBOL = (() => {
19
+ const map = {};
20
+ slip44_json_1.default.forEach((e) => {
21
+ const sym = e.symbol?.toUpperCase();
22
+ if (sym && typeof e.index === 'number' && !(sym in map)) {
23
+ map[sym] = e.index;
24
+ }
25
+ });
26
+ return map;
27
+ })();
28
+ /** Fallbacks for common native symbols */
29
+ const COMMON_SYMBOL_DEFAULTS = {
30
+ SOL: 501,
31
+ BTC: 0,
32
+ ETH: 60,
33
+ POL: 966,
34
+ MATIC: 966,
35
+ BNB: 714,
36
+ AVAX: 9000,
37
+ SEI: 19000118,
38
+ MON: 268435779,
39
+ FTM: 1007,
40
+ XDAI: 700,
41
+ };
42
+ /**
43
+ * Fetch with cache + in-flight dedupe.
44
+ *
45
+ * @returns The chains JSON data.
46
+ */
47
+ async function fetchChains() {
48
+ const now = Date.now();
49
+ if (chainsCache && now - chainsFetchedAt < CACHE_DURATION_MS) {
50
+ return chainsCache;
51
+ }
52
+ if (inflight) {
53
+ return inflight;
54
+ }
55
+ inflight = (async () => {
56
+ const data = (await (0, controller_utils_1.handleFetch)(CHAINS_JSON_URL));
57
+ if (!Array.isArray(data)) {
58
+ throw new Error('chains.json: unexpected shape');
59
+ }
60
+ // Minimal validation; keep what we need
61
+ const parsed = data
62
+ .filter((x) => x && typeof x === 'object')
63
+ .map((x) => {
64
+ const nativeCurrency = x.nativeCurrency;
65
+ return {
66
+ chainId: Number(x.chainId),
67
+ name: x.name,
68
+ nativeCurrency,
69
+ };
70
+ })
71
+ .filter((x) => Number.isFinite(x.chainId));
72
+ chainsCache = parsed;
73
+ chainsFetchedAt = now;
74
+ inflight = null; // clear in-flight marker
75
+ return parsed;
76
+ })();
77
+ try {
78
+ return await inflight;
79
+ }
80
+ catch (e) {
81
+ inflight = null;
82
+ throw e;
83
+ }
84
+ }
85
+ /**
86
+ * Resolve SLIP-44 coinType for a given chainId (EVM only).
87
+ *
88
+ * @param chainId - The chain ID to resolve.
89
+ * @returns The SLIP-44 coin type as a string, or null if not found.
90
+ */
91
+ async function getSlip44ByChainId(chainId) {
92
+ try {
93
+ // 1) Lookup by native symbol from chains.json
94
+ const chains = await fetchChains();
95
+ const chain = chains.find((c) => c.chainId === chainId);
96
+ const symbol = chain?.nativeCurrency?.symbol?.toUpperCase();
97
+ if (symbol) {
98
+ // Exact symbol match from slip44 dataset
99
+ const coinType = SLIP44_BY_SYMBOL[symbol] ?? COMMON_SYMBOL_DEFAULTS[symbol];
100
+ if (coinType !== undefined) {
101
+ return String(coinType);
102
+ }
103
+ }
104
+ // 2) Heuristic for ETH-based L2s: default to ETH coin type
105
+ // Many L2s (Arbitrum, Optimism, Base, Linea, Scroll, zkSync, etc.) use ETH as native
106
+ return '60';
107
+ }
108
+ catch (err) {
109
+ // Prefer null for ergonomics (callers can fallback silently)
110
+ console.error(`getSlip44ByChainId(${chainId}) failed:`, err);
111
+ return null;
112
+ }
113
+ }
114
+ exports.getSlip44ByChainId = getSlip44ByChainId;
115
+ /**
116
+ * Convenience: native CAIP-19 for an EVM chain.
117
+ *
118
+ * @param chainId - The chain ID to generate CAIP-19 for.
119
+ * @returns The CAIP-19 asset type string, or null if not found.
120
+ */
121
+ async function getNativeCaip19(chainId) {
122
+ const coinType = await getSlip44ByChainId(chainId);
123
+ return coinType
124
+ ? (0, utils_1.toCaipAssetType)('eip155', String(chainId), 'slip44', coinType)
125
+ : null;
126
+ }
127
+ exports.getNativeCaip19 = getNativeCaip19;
128
+ //# sourceMappingURL=ChainService.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainService.cjs","sourceRoot":"","sources":["../src/ChainService.ts"],"names":[],"mappings":"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","sourcesContent":["import { handleFetch } from '@metamask/controller-utils';\n// @ts-expect-error - JSON imports need resolveJsonModule\nimport slip44 from '@metamask/slip44/slip44.json';\nimport { toCaipAssetType } from '@metamask/utils';\n\n/** ---- Types ---- */\ntype ChainsJsonItem = {\n chainId: number;\n name: string;\n nativeCurrency?: { symbol?: string; name?: string; decimals?: number };\n};\n\ntype Slip44Entry = {\n index: number; // the coin type (e.g., 60)\n symbol?: string; // e.g., 'ETH', 'BNB'\n name?: string; // e.g., 'Ethereum'\n};\n\n/** ---- Constants / Cache ---- */\nconst CHAINS_JSON_URL = 'https://chainid.network/chains.json';\nconst CACHE_DURATION_MS = 30 * 60 * 1000; // 30 min\n\nlet chainsCache: ChainsJsonItem[] | null = null;\nlet chainsFetchedAt = 0;\nlet inflight: Promise<ChainsJsonItem[]> | null = null;\n\n/** Build a quick symbol -> coinType map from slip44 dataset */\nconst SLIP44_BY_SYMBOL: Record<string, number> = (() => {\n const map: Record<string, number> = {};\n (slip44 as Slip44Entry[]).forEach((e) => {\n const sym = e.symbol?.toUpperCase();\n if (sym && typeof e.index === 'number' && !(sym in map)) {\n map[sym] = e.index;\n }\n });\n return map;\n})();\n\n/** Fallbacks for common native symbols */\nconst COMMON_SYMBOL_DEFAULTS: Record<string, number> = {\n SOL: 501,\n BTC: 0,\n ETH: 60,\n POL: 966,\n MATIC: 966,\n BNB: 714,\n AVAX: 9000,\n SEI: 19000118,\n MON: 268435779,\n FTM: 1007,\n XDAI: 700,\n};\n\n/**\n * Fetch with cache + in-flight dedupe.\n *\n * @returns The chains JSON data.\n */\nasync function fetchChains(): Promise<ChainsJsonItem[]> {\n const now = Date.now();\n if (chainsCache && now - chainsFetchedAt < CACHE_DURATION_MS) {\n return chainsCache;\n }\n if (inflight) {\n return inflight;\n }\n\n inflight = (async () => {\n const data = (await handleFetch(CHAINS_JSON_URL)) as unknown;\n if (!Array.isArray(data)) {\n throw new Error('chains.json: unexpected shape');\n }\n // Minimal validation; keep what we need\n const parsed = data\n .filter((x) => x && typeof x === 'object')\n .map((x: Record<string, unknown>): ChainsJsonItem => {\n const nativeCurrency = x.nativeCurrency as\n | { symbol?: string; name?: string; decimals?: number }\n | undefined;\n return {\n chainId: Number(x.chainId),\n name: x.name as string,\n nativeCurrency,\n };\n })\n .filter((x) => Number.isFinite(x.chainId));\n\n chainsCache = parsed;\n chainsFetchedAt = now;\n inflight = null; // clear in-flight marker\n return parsed;\n })();\n\n try {\n return await inflight;\n } catch (e) {\n inflight = null;\n throw e;\n }\n}\n\n/**\n * Resolve SLIP-44 coinType for a given chainId (EVM only).\n *\n * @param chainId - The chain ID to resolve.\n * @returns The SLIP-44 coin type as a string, or null if not found.\n */\nexport async function getSlip44ByChainId(\n chainId: number,\n): Promise<string | null> {\n try {\n // 1) Lookup by native symbol from chains.json\n const chains = await fetchChains();\n const chain = chains.find((c) => c.chainId === chainId);\n const symbol = chain?.nativeCurrency?.symbol?.toUpperCase();\n\n if (symbol) {\n // Exact symbol match from slip44 dataset\n const coinType =\n SLIP44_BY_SYMBOL[symbol] ?? COMMON_SYMBOL_DEFAULTS[symbol];\n if (coinType !== undefined) {\n return String(coinType);\n }\n }\n\n // 2) Heuristic for ETH-based L2s: default to ETH coin type\n // Many L2s (Arbitrum, Optimism, Base, Linea, Scroll, zkSync, etc.) use ETH as native\n return '60';\n } catch (err) {\n // Prefer null for ergonomics (callers can fallback silently)\n console.error(`getSlip44ByChainId(${chainId}) failed:`, err);\n return null;\n }\n}\n\n/**\n * Convenience: native CAIP-19 for an EVM chain.\n *\n * @param chainId - The chain ID to generate CAIP-19 for.\n * @returns The CAIP-19 asset type string, or null if not found.\n */\nexport async function getNativeCaip19(chainId: number): Promise<string | null> {\n const coinType = await getSlip44ByChainId(chainId);\n return coinType\n ? toCaipAssetType('eip155', String(chainId), 'slip44', coinType)\n : null;\n}\n"]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Resolve SLIP-44 coinType for a given chainId (EVM only).
3
+ *
4
+ * @param chainId - The chain ID to resolve.
5
+ * @returns The SLIP-44 coin type as a string, or null if not found.
6
+ */
7
+ export declare function getSlip44ByChainId(chainId: number): Promise<string | null>;
8
+ /**
9
+ * Convenience: native CAIP-19 for an EVM chain.
10
+ *
11
+ * @param chainId - The chain ID to generate CAIP-19 for.
12
+ * @returns The CAIP-19 asset type string, or null if not found.
13
+ */
14
+ export declare function getNativeCaip19(chainId: number): Promise<string | null>;
15
+ //# sourceMappingURL=ChainService.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainService.d.cts","sourceRoot":"","sources":["../src/ChainService.ts"],"names":[],"mappings":"AAqGA;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwBxB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK7E"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Resolve SLIP-44 coinType for a given chainId (EVM only).
3
+ *
4
+ * @param chainId - The chain ID to resolve.
5
+ * @returns The SLIP-44 coin type as a string, or null if not found.
6
+ */
7
+ export declare function getSlip44ByChainId(chainId: number): Promise<string | null>;
8
+ /**
9
+ * Convenience: native CAIP-19 for an EVM chain.
10
+ *
11
+ * @param chainId - The chain ID to generate CAIP-19 for.
12
+ * @returns The CAIP-19 asset type string, or null if not found.
13
+ */
14
+ export declare function getNativeCaip19(chainId: number): Promise<string | null>;
15
+ //# sourceMappingURL=ChainService.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainService.d.mts","sourceRoot":"","sources":["../src/ChainService.ts"],"names":[],"mappings":"AAqGA;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwBxB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK7E"}
@@ -0,0 +1,120 @@
1
+ import { handleFetch } from "@metamask/controller-utils";
2
+ // @ts-expect-error - JSON imports need resolveJsonModule
3
+ import slip44 from "@metamask/slip44/slip44.json" assert { type: "json" };
4
+ import { toCaipAssetType } from "@metamask/utils";
5
+ /** ---- Constants / Cache ---- */
6
+ const CHAINS_JSON_URL = 'https://chainid.network/chains.json';
7
+ const CACHE_DURATION_MS = 30 * 60 * 1000; // 30 min
8
+ let chainsCache = null;
9
+ let chainsFetchedAt = 0;
10
+ let inflight = null;
11
+ /** Build a quick symbol -> coinType map from slip44 dataset */
12
+ const SLIP44_BY_SYMBOL = (() => {
13
+ const map = {};
14
+ slip44.forEach((e) => {
15
+ const sym = e.symbol?.toUpperCase();
16
+ if (sym && typeof e.index === 'number' && !(sym in map)) {
17
+ map[sym] = e.index;
18
+ }
19
+ });
20
+ return map;
21
+ })();
22
+ /** Fallbacks for common native symbols */
23
+ const COMMON_SYMBOL_DEFAULTS = {
24
+ SOL: 501,
25
+ BTC: 0,
26
+ ETH: 60,
27
+ POL: 966,
28
+ MATIC: 966,
29
+ BNB: 714,
30
+ AVAX: 9000,
31
+ SEI: 19000118,
32
+ MON: 268435779,
33
+ FTM: 1007,
34
+ XDAI: 700,
35
+ };
36
+ /**
37
+ * Fetch with cache + in-flight dedupe.
38
+ *
39
+ * @returns The chains JSON data.
40
+ */
41
+ async function fetchChains() {
42
+ const now = Date.now();
43
+ if (chainsCache && now - chainsFetchedAt < CACHE_DURATION_MS) {
44
+ return chainsCache;
45
+ }
46
+ if (inflight) {
47
+ return inflight;
48
+ }
49
+ inflight = (async () => {
50
+ const data = (await handleFetch(CHAINS_JSON_URL));
51
+ if (!Array.isArray(data)) {
52
+ throw new Error('chains.json: unexpected shape');
53
+ }
54
+ // Minimal validation; keep what we need
55
+ const parsed = data
56
+ .filter((x) => x && typeof x === 'object')
57
+ .map((x) => {
58
+ const nativeCurrency = x.nativeCurrency;
59
+ return {
60
+ chainId: Number(x.chainId),
61
+ name: x.name,
62
+ nativeCurrency,
63
+ };
64
+ })
65
+ .filter((x) => Number.isFinite(x.chainId));
66
+ chainsCache = parsed;
67
+ chainsFetchedAt = now;
68
+ inflight = null; // clear in-flight marker
69
+ return parsed;
70
+ })();
71
+ try {
72
+ return await inflight;
73
+ }
74
+ catch (e) {
75
+ inflight = null;
76
+ throw e;
77
+ }
78
+ }
79
+ /**
80
+ * Resolve SLIP-44 coinType for a given chainId (EVM only).
81
+ *
82
+ * @param chainId - The chain ID to resolve.
83
+ * @returns The SLIP-44 coin type as a string, or null if not found.
84
+ */
85
+ export async function getSlip44ByChainId(chainId) {
86
+ try {
87
+ // 1) Lookup by native symbol from chains.json
88
+ const chains = await fetchChains();
89
+ const chain = chains.find((c) => c.chainId === chainId);
90
+ const symbol = chain?.nativeCurrency?.symbol?.toUpperCase();
91
+ if (symbol) {
92
+ // Exact symbol match from slip44 dataset
93
+ const coinType = SLIP44_BY_SYMBOL[symbol] ?? COMMON_SYMBOL_DEFAULTS[symbol];
94
+ if (coinType !== undefined) {
95
+ return String(coinType);
96
+ }
97
+ }
98
+ // 2) Heuristic for ETH-based L2s: default to ETH coin type
99
+ // Many L2s (Arbitrum, Optimism, Base, Linea, Scroll, zkSync, etc.) use ETH as native
100
+ return '60';
101
+ }
102
+ catch (err) {
103
+ // Prefer null for ergonomics (callers can fallback silently)
104
+ console.error(`getSlip44ByChainId(${chainId}) failed:`, err);
105
+ return null;
106
+ }
107
+ }
108
+ /**
109
+ * Convenience: native CAIP-19 for an EVM chain.
110
+ *
111
+ * @param chainId - The chain ID to generate CAIP-19 for.
112
+ * @returns The CAIP-19 asset type string, or null if not found.
113
+ */
114
+ export async function getNativeCaip19(chainId) {
115
+ const coinType = await getSlip44ByChainId(chainId);
116
+ return coinType
117
+ ? toCaipAssetType('eip155', String(chainId), 'slip44', coinType)
118
+ : null;
119
+ }
120
+ //# sourceMappingURL=ChainService.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainService.mjs","sourceRoot":"","sources":["../src/ChainService.ts"],"names":[],"mappings":"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","sourcesContent":["import { handleFetch } from '@metamask/controller-utils';\n// @ts-expect-error - JSON imports need resolveJsonModule\nimport slip44 from '@metamask/slip44/slip44.json';\nimport { toCaipAssetType } from '@metamask/utils';\n\n/** ---- Types ---- */\ntype ChainsJsonItem = {\n chainId: number;\n name: string;\n nativeCurrency?: { symbol?: string; name?: string; decimals?: number };\n};\n\ntype Slip44Entry = {\n index: number; // the coin type (e.g., 60)\n symbol?: string; // e.g., 'ETH', 'BNB'\n name?: string; // e.g., 'Ethereum'\n};\n\n/** ---- Constants / Cache ---- */\nconst CHAINS_JSON_URL = 'https://chainid.network/chains.json';\nconst CACHE_DURATION_MS = 30 * 60 * 1000; // 30 min\n\nlet chainsCache: ChainsJsonItem[] | null = null;\nlet chainsFetchedAt = 0;\nlet inflight: Promise<ChainsJsonItem[]> | null = null;\n\n/** Build a quick symbol -> coinType map from slip44 dataset */\nconst SLIP44_BY_SYMBOL: Record<string, number> = (() => {\n const map: Record<string, number> = {};\n (slip44 as Slip44Entry[]).forEach((e) => {\n const sym = e.symbol?.toUpperCase();\n if (sym && typeof e.index === 'number' && !(sym in map)) {\n map[sym] = e.index;\n }\n });\n return map;\n})();\n\n/** Fallbacks for common native symbols */\nconst COMMON_SYMBOL_DEFAULTS: Record<string, number> = {\n SOL: 501,\n BTC: 0,\n ETH: 60,\n POL: 966,\n MATIC: 966,\n BNB: 714,\n AVAX: 9000,\n SEI: 19000118,\n MON: 268435779,\n FTM: 1007,\n XDAI: 700,\n};\n\n/**\n * Fetch with cache + in-flight dedupe.\n *\n * @returns The chains JSON data.\n */\nasync function fetchChains(): Promise<ChainsJsonItem[]> {\n const now = Date.now();\n if (chainsCache && now - chainsFetchedAt < CACHE_DURATION_MS) {\n return chainsCache;\n }\n if (inflight) {\n return inflight;\n }\n\n inflight = (async () => {\n const data = (await handleFetch(CHAINS_JSON_URL)) as unknown;\n if (!Array.isArray(data)) {\n throw new Error('chains.json: unexpected shape');\n }\n // Minimal validation; keep what we need\n const parsed = data\n .filter((x) => x && typeof x === 'object')\n .map((x: Record<string, unknown>): ChainsJsonItem => {\n const nativeCurrency = x.nativeCurrency as\n | { symbol?: string; name?: string; decimals?: number }\n | undefined;\n return {\n chainId: Number(x.chainId),\n name: x.name as string,\n nativeCurrency,\n };\n })\n .filter((x) => Number.isFinite(x.chainId));\n\n chainsCache = parsed;\n chainsFetchedAt = now;\n inflight = null; // clear in-flight marker\n return parsed;\n })();\n\n try {\n return await inflight;\n } catch (e) {\n inflight = null;\n throw e;\n }\n}\n\n/**\n * Resolve SLIP-44 coinType for a given chainId (EVM only).\n *\n * @param chainId - The chain ID to resolve.\n * @returns The SLIP-44 coin type as a string, or null if not found.\n */\nexport async function getSlip44ByChainId(\n chainId: number,\n): Promise<string | null> {\n try {\n // 1) Lookup by native symbol from chains.json\n const chains = await fetchChains();\n const chain = chains.find((c) => c.chainId === chainId);\n const symbol = chain?.nativeCurrency?.symbol?.toUpperCase();\n\n if (symbol) {\n // Exact symbol match from slip44 dataset\n const coinType =\n SLIP44_BY_SYMBOL[symbol] ?? COMMON_SYMBOL_DEFAULTS[symbol];\n if (coinType !== undefined) {\n return String(coinType);\n }\n }\n\n // 2) Heuristic for ETH-based L2s: default to ETH coin type\n // Many L2s (Arbitrum, Optimism, Base, Linea, Scroll, zkSync, etc.) use ETH as native\n return '60';\n } catch (err) {\n // Prefer null for ergonomics (callers can fallback silently)\n console.error(`getSlip44ByChainId(${chainId}) failed:`, err);\n return null;\n }\n}\n\n/**\n * Convenience: native CAIP-19 for an EVM chain.\n *\n * @param chainId - The chain ID to generate CAIP-19 for.\n * @returns The CAIP-19 asset type string, or null if not found.\n */\nexport async function getNativeCaip19(chainId: number): Promise<string | null> {\n const coinType = await getSlip44ByChainId(chainId);\n return coinType\n ? toCaipAssetType('eip155', String(chainId), 'slip44', coinType)\n : null;\n}\n"]}
@@ -4,13 +4,20 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
4
4
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
5
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
6
  };
7
- var _NetworkEnablementController_instances, _NetworkEnablementController_ensureNamespaceBucket, _NetworkEnablementController_isInPopularNetworksMode, _NetworkEnablementController_removeNetworkEntry, _NetworkEnablementController_onAddNetwork;
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var _NetworkEnablementController_instances, _NetworkEnablementController_initialized, _NetworkEnablementController_ensureNamespaceBucket, _NetworkEnablementController_ensureSlip44NamespaceBucket, _NetworkEnablementController_isInPopularNetworksMode, _NetworkEnablementController_removeNetworkEntry, _NetworkEnablementController_onAddNetwork;
8
14
  Object.defineProperty(exports, "__esModule", { value: true });
9
15
  exports.NetworkEnablementController = void 0;
10
16
  const base_controller_1 = require("@metamask/base-controller");
11
17
  const controller_utils_1 = require("@metamask/controller-utils");
12
18
  const keyring_api_1 = require("@metamask/keyring-api");
13
19
  const utils_1 = require("@metamask/utils");
20
+ const ChainService_1 = require("./ChainService.cjs");
14
21
  const constants_1 = require("./constants.cjs");
15
22
  const utils_2 = require("./utils.cjs");
16
23
  const controllerName = 'NetworkEnablementController';
@@ -25,6 +32,11 @@ const getDefaultNetworkEnablementControllerState = () => ({
25
32
  [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.Mainnet]]: true,
26
33
  [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.LineaMainnet]]: true,
27
34
  [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.BaseMainnet]]: true,
35
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.ArbitrumOne]]: true,
36
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.BscMainnet]]: true,
37
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.OptimismMainnet]]: true,
38
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.PolygonMainnet]]: true,
39
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.SeiMainnet]]: true,
28
40
  },
29
41
  [utils_1.KnownCaipNamespace.Solana]: {
30
42
  [keyring_api_1.SolScope.Mainnet]: true,
@@ -42,6 +54,33 @@ const getDefaultNetworkEnablementControllerState = () => ({
42
54
  [keyring_api_1.TrxScope.Shasta]: false,
43
55
  },
44
56
  },
57
+ slip44: {
58
+ [utils_1.KnownCaipNamespace.Eip155]: {
59
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.Mainnet]]: '60',
60
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.LineaMainnet]]: '69',
61
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.BaseMainnet]]: '8453',
62
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.ArbitrumOne]]: '42161',
63
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.BscMainnet]]: '56',
64
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.OptimismMainnet]]: '10',
65
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.PolygonMainnet]]: '966',
66
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.SeiMainnet]]: '19000118', // SEI
67
+ },
68
+ [utils_1.KnownCaipNamespace.Solana]: {
69
+ [keyring_api_1.SolScope.Mainnet]: '501',
70
+ [keyring_api_1.SolScope.Testnet]: '501',
71
+ [keyring_api_1.SolScope.Devnet]: '501', // SOL
72
+ },
73
+ [utils_1.KnownCaipNamespace.Bip122]: {
74
+ [keyring_api_1.BtcScope.Mainnet]: '0',
75
+ [keyring_api_1.BtcScope.Testnet]: '0',
76
+ [keyring_api_1.BtcScope.Signet]: '0', // BTC
77
+ },
78
+ [utils_1.KnownCaipNamespace.Tron]: {
79
+ [keyring_api_1.TrxScope.Mainnet]: '195',
80
+ [keyring_api_1.TrxScope.Nile]: '195',
81
+ [keyring_api_1.TrxScope.Shasta]: '195', // TRX
82
+ },
83
+ },
45
84
  });
46
85
  // Metadata for the controller state
47
86
  const metadata = {
@@ -51,6 +90,12 @@ const metadata = {
51
90
  includeInDebugSnapshot: true,
52
91
  usedInUi: true,
53
92
  },
93
+ slip44: {
94
+ includeInStateLogs: true,
95
+ persist: true,
96
+ includeInDebugSnapshot: true,
97
+ usedInUi: true,
98
+ },
54
99
  };
55
100
  /**
56
101
  * Controller responsible for managing network enablement state across different blockchain networks.
@@ -80,8 +125,12 @@ class NetworkEnablementController extends base_controller_1.BaseController {
80
125
  },
81
126
  });
82
127
  _NetworkEnablementController_instances.add(this);
128
+ _NetworkEnablementController_initialized.set(this, false);
83
129
  messenger.subscribe('NetworkController:networkAdded', ({ chainId }) => {
84
- __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_onAddNetwork).call(this, chainId);
130
+ // Handle async network addition
131
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_onAddNetwork).call(this, chainId).catch((error) => {
132
+ console.error('Error adding network:', error);
133
+ });
85
134
  });
86
135
  messenger.subscribe('NetworkController:networkRemoved', ({ chainId }) => {
87
136
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_removeNetworkEntry).call(this, chainId);
@@ -144,8 +193,9 @@ class NetworkEnablementController extends base_controller_1.BaseController {
144
193
  throw new Error(`Chain ID ${chainId} belongs to namespace ${derivedNamespace}, but namespace ${namespace} was specified`);
145
194
  }
146
195
  this.update((s) => {
147
- // Ensure the namespace bucket exists
196
+ // Ensure the namespace buckets exist
148
197
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
198
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
149
199
  // Disable all networks in the specified namespace first
150
200
  if (s.enabledNetworkMap[namespace]) {
151
201
  Object.keys(s.enabledNetworkMap[namespace]).forEach((key) => {
@@ -182,8 +232,9 @@ class NetworkEnablementController extends base_controller_1.BaseController {
182
232
  const { namespace, storageKey } = (0, utils_2.deriveKeys)(chainId);
183
233
  // Check if network exists in NetworkController configurations
184
234
  if (networkControllerState.networkConfigurationsByChainId[chainId]) {
185
- // Ensure namespace bucket exists
235
+ // Ensure namespace buckets exist
186
236
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
237
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
187
238
  // Enable the network
188
239
  s.enabledNetworkMap[namespace][storageKey] = true;
189
240
  }
@@ -191,16 +242,18 @@ class NetworkEnablementController extends base_controller_1.BaseController {
191
242
  // Enable Solana mainnet if it exists in MultichainNetworkController configurations
192
243
  const solanaKeys = (0, utils_2.deriveKeys)(keyring_api_1.SolScope.Mainnet);
193
244
  if (multichainState.multichainNetworkConfigurationsByChainId[keyring_api_1.SolScope.Mainnet]) {
194
- // Ensure namespace bucket exists
245
+ // Ensure namespace buckets exist
195
246
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, solanaKeys.namespace);
247
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, solanaKeys.namespace);
196
248
  // Enable Solana mainnet
197
249
  s.enabledNetworkMap[solanaKeys.namespace][solanaKeys.storageKey] = true;
198
250
  }
199
251
  // Enable Bitcoin mainnet if it exists in MultichainNetworkController configurations
200
252
  const bitcoinKeys = (0, utils_2.deriveKeys)(keyring_api_1.BtcScope.Mainnet);
201
253
  if (multichainState.multichainNetworkConfigurationsByChainId[keyring_api_1.BtcScope.Mainnet]) {
202
- // Ensure namespace bucket exists
254
+ // Ensure namespace buckets exist
203
255
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, bitcoinKeys.namespace);
256
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, bitcoinKeys.namespace);
204
257
  // Enable Bitcoin mainnet
205
258
  s.enabledNetworkMap[bitcoinKeys.namespace][bitcoinKeys.storageKey] =
206
259
  true;
@@ -208,8 +261,9 @@ class NetworkEnablementController extends base_controller_1.BaseController {
208
261
  // Enable Tron mainnet if it exists in MultichainNetworkController configurations
209
262
  const tronKeys = (0, utils_2.deriveKeys)(keyring_api_1.TrxScope.Mainnet);
210
263
  if (multichainState.multichainNetworkConfigurationsByChainId[keyring_api_1.TrxScope.Mainnet]) {
211
- // Ensure namespace bucket exists
264
+ // Ensure namespace buckets exist
212
265
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, tronKeys.namespace);
266
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, tronKeys.namespace);
213
267
  // Enable Tron mainnet
214
268
  s.enabledNetworkMap[tronKeys.namespace][tronKeys.storageKey] = true;
215
269
  }
@@ -222,20 +276,29 @@ class NetworkEnablementController extends base_controller_1.BaseController {
222
276
  * and MultichainNetworkController and syncs the enabled network map accordingly.
223
277
  * It ensures proper namespace buckets exist for all configured networks and only
224
278
  * adds missing networks with a default value of false, preserving existing user settings.
279
+ * Additionally, it fetches slip44 values for EIP-155 networks that don't have them.
280
+ *
281
+ * This method only runs once per controller instance to avoid unnecessary API calls.
282
+ * Subsequent calls will return immediately without performing any operations.
283
+ * Use `reinit()` if you need to force re-initialization.
225
284
  *
226
285
  * This method should be called after the NetworkController and MultichainNetworkController
227
286
  * have been initialized and their configurations are available.
228
287
  */
229
- init() {
288
+ async init() {
289
+ if (__classPrivateFieldGet(this, _NetworkEnablementController_initialized, "f")) {
290
+ return;
291
+ }
292
+ // Get network configurations
293
+ const networkControllerState = this.messenger.call('NetworkController:getState');
294
+ const multichainState = this.messenger.call('MultichainNetworkController:getState');
295
+ // First, initialize the state synchronously
230
296
  this.update((s) => {
231
- // Get network configurations from NetworkController (EVM networks)
232
- const networkControllerState = this.messenger.call('NetworkController:getState');
233
- // Get network configurations from MultichainNetworkController (all networks)
234
- const multichainState = this.messenger.call('MultichainNetworkController:getState');
235
297
  // Initialize namespace buckets for EVM networks from NetworkController
236
298
  Object.keys(networkControllerState.networkConfigurationsByChainId).forEach((chainId) => {
237
299
  const { namespace, storageKey } = (0, utils_2.deriveKeys)(chainId);
238
300
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
301
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
239
302
  // Only add network if it doesn't already exist in state (preserves user settings)
240
303
  if (s.enabledNetworkMap[namespace][storageKey] === undefined) {
241
304
  s.enabledNetworkMap[namespace][storageKey] = false;
@@ -245,12 +308,63 @@ class NetworkEnablementController extends base_controller_1.BaseController {
245
308
  Object.keys(multichainState.multichainNetworkConfigurationsByChainId).forEach((chainId) => {
246
309
  const { namespace, storageKey } = (0, utils_2.deriveKeys)(chainId);
247
310
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
311
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
248
312
  // Only add network if it doesn't already exist in state (preserves user settings)
249
313
  if (s.enabledNetworkMap[namespace][storageKey] === undefined) {
250
314
  s.enabledNetworkMap[namespace][storageKey] = false;
251
315
  }
252
316
  });
253
317
  });
318
+ // Collect EIP-155 networks that need slip44 values
319
+ const networksToFetch = [];
320
+ Object.keys(networkControllerState.networkConfigurationsByChainId).forEach((chainId) => {
321
+ const { namespace, storageKey, reference } = (0, utils_2.deriveKeys)(chainId);
322
+ if (namespace === 'eip155') {
323
+ // Check if slip44 value already exists in state
324
+ const existingSlip44 = this.state.slip44[namespace]?.[storageKey];
325
+ if (!existingSlip44) {
326
+ const numericChainId = parseInt(reference, 16);
327
+ networksToFetch.push({ chainId, storageKey, numericChainId });
328
+ }
329
+ }
330
+ });
331
+ // Fetch slip44 values for networks that don't have them
332
+ if (networksToFetch.length > 0) {
333
+ const slip44Promises = networksToFetch.map(async ({ chainId, storageKey, numericChainId }) => {
334
+ try {
335
+ const slip44Value = await (0, ChainService_1.getSlip44ByChainId)(numericChainId);
336
+ return { chainId, storageKey, slip44Value };
337
+ }
338
+ catch (error) {
339
+ console.error(`Failed to fetch slip44 for chainId ${chainId}:`, error);
340
+ return { chainId, storageKey, slip44Value: null };
341
+ }
342
+ });
343
+ const results = await Promise.all(slip44Promises);
344
+ // Update state with fetched slip44 values
345
+ this.update((s) => {
346
+ results.forEach(({ storageKey, slip44Value }) => {
347
+ if (slip44Value !== null) {
348
+ // Ensure namespace exists (should already exist from above)
349
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, 'eip155');
350
+ // @ts-expect-error - TypeScript doesn't recognize the dynamic namespace access
351
+ s.slip44.eip155[storageKey] = slip44Value;
352
+ }
353
+ });
354
+ });
355
+ }
356
+ __classPrivateFieldSet(this, _NetworkEnablementController_initialized, true, "f");
357
+ }
358
+ /**
359
+ * Re-initializes the controller's state.
360
+ *
361
+ * This method forces a fresh initialization even if the controller has already been initialized.
362
+ * It will re-fetch slip44 values for all EIP-155 networks and re-sync the network state.
363
+ * Use this when you need to force a full re-initialization.
364
+ */
365
+ async reinit() {
366
+ __classPrivateFieldSet(this, _NetworkEnablementController_initialized, false, "f");
367
+ await this.init();
254
368
  }
255
369
  /**
256
370
  * Disables a network for the user.
@@ -288,10 +402,14 @@ class NetworkEnablementController extends base_controller_1.BaseController {
288
402
  }
289
403
  }
290
404
  exports.NetworkEnablementController = NetworkEnablementController;
291
- _NetworkEnablementController_instances = new WeakSet(), _NetworkEnablementController_ensureNamespaceBucket = function _NetworkEnablementController_ensureNamespaceBucket(state, ns) {
405
+ _NetworkEnablementController_initialized = new WeakMap(), _NetworkEnablementController_instances = new WeakSet(), _NetworkEnablementController_ensureNamespaceBucket = function _NetworkEnablementController_ensureNamespaceBucket(state, ns) {
292
406
  if (!state.enabledNetworkMap[ns]) {
293
407
  state.enabledNetworkMap[ns] = {};
294
408
  }
409
+ }, _NetworkEnablementController_ensureSlip44NamespaceBucket = function _NetworkEnablementController_ensureSlip44NamespaceBucket(state, ns) {
410
+ if (!state.slip44[ns]) {
411
+ state.slip44[ns] = {};
412
+ }
295
413
  }, _NetworkEnablementController_isInPopularNetworksMode = function _NetworkEnablementController_isInPopularNetworksMode() {
296
414
  // Get current network configurations to check which popular networks exist
297
415
  const networkControllerState = this.messenger.call('NetworkController:getState');
@@ -320,11 +438,41 @@ _NetworkEnablementController_instances = new WeakSet(), _NetworkEnablementContro
320
438
  delete s.enabledNetworkMap[namespace][storageKey];
321
439
  }
322
440
  });
323
- }, _NetworkEnablementController_onAddNetwork = function _NetworkEnablementController_onAddNetwork(chainId) {
441
+ }, _NetworkEnablementController_onAddNetwork =
442
+ /**
443
+ * Handles the addition of a new network to the controller.
444
+ *
445
+ * @param chainId - The chain ID to add (Hex or CAIP-2 format)
446
+ *
447
+ * @description
448
+ * - If in popular networks mode (>2 popular networks enabled) AND adding a popular network:
449
+ * - Keep current selection (add but don't enable the new network)
450
+ * - Otherwise:
451
+ * - Switch to the newly added network (disable all others, enable this one)
452
+ * - Fetches and stores slip44 value for EIP-155 networks
453
+ */
454
+ async function _NetworkEnablementController_onAddNetwork(chainId) {
324
455
  const { namespace, storageKey, reference } = (0, utils_2.deriveKeys)(chainId);
456
+ // Fetch slip44 for EIP-155 networks
457
+ let slip44Value = null;
458
+ if (namespace === 'eip155') {
459
+ try {
460
+ // Convert hex chainId to decimal for the API call
461
+ const numericChainId = parseInt(reference);
462
+ slip44Value = await (0, ChainService_1.getSlip44ByChainId)(numericChainId);
463
+ }
464
+ catch (error) {
465
+ console.error(`Failed to fetch slip44 for chainId ${chainId}:`, error);
466
+ }
467
+ }
325
468
  this.update((s) => {
326
- // Ensure the namespace bucket exists
469
+ // Ensure the namespace buckets exist
327
470
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
471
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
472
+ // Add slip44 value if fetched successfully
473
+ if (slip44Value !== null) {
474
+ s.slip44[namespace][storageKey] = slip44Value;
475
+ }
328
476
  // Check if popular networks mode is active (>2 popular networks enabled)
329
477
  const inPopularNetworksMode = __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_isInPopularNetworksMode).call(this);
330
478
  // Check if the network being added is a popular network