@metamask-previews/network-enablement-controller 3.1.0-preview-ac95634 → 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 +8 -0
- package/dist/ChainService.cjs +128 -0
- package/dist/ChainService.cjs.map +1 -0
- package/dist/ChainService.d.cts +15 -0
- package/dist/ChainService.d.cts.map +1 -0
- package/dist/ChainService.d.mts +15 -0
- package/dist/ChainService.d.mts.map +1 -0
- package/dist/ChainService.mjs +120 -0
- package/dist/ChainService.mjs.map +1 -0
- package/dist/NetworkEnablementController.cjs +158 -15
- package/dist/NetworkEnablementController.cjs.map +1 -1
- package/dist/NetworkEnablementController.d.cts +15 -1
- package/dist/NetworkEnablementController.d.cts.map +1 -1
- package/dist/NetworkEnablementController.d.mts +15 -1
- package/dist/NetworkEnablementController.d.mts.map +1 -1
- package/dist/NetworkEnablementController.mjs +158 -15
- package/dist/NetworkEnablementController.mjs.map +1 -1
- package/dist/index.cjs +3 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ 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
|
+
|
|
10
18
|
### Fixed
|
|
11
19
|
|
|
12
20
|
- include additional popular networks now enabled by default ([#7014](https://github.com/MetaMask/core/pull/7014))
|
|
@@ -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
|
|
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';
|
|
@@ -47,6 +54,33 @@ const getDefaultNetworkEnablementControllerState = () => ({
|
|
|
47
54
|
[keyring_api_1.TrxScope.Shasta]: false,
|
|
48
55
|
},
|
|
49
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
|
+
},
|
|
50
84
|
});
|
|
51
85
|
// Metadata for the controller state
|
|
52
86
|
const metadata = {
|
|
@@ -56,6 +90,12 @@ const metadata = {
|
|
|
56
90
|
includeInDebugSnapshot: true,
|
|
57
91
|
usedInUi: true,
|
|
58
92
|
},
|
|
93
|
+
slip44: {
|
|
94
|
+
includeInStateLogs: true,
|
|
95
|
+
persist: true,
|
|
96
|
+
includeInDebugSnapshot: true,
|
|
97
|
+
usedInUi: true,
|
|
98
|
+
},
|
|
59
99
|
};
|
|
60
100
|
/**
|
|
61
101
|
* Controller responsible for managing network enablement state across different blockchain networks.
|
|
@@ -85,8 +125,12 @@ class NetworkEnablementController extends base_controller_1.BaseController {
|
|
|
85
125
|
},
|
|
86
126
|
});
|
|
87
127
|
_NetworkEnablementController_instances.add(this);
|
|
128
|
+
_NetworkEnablementController_initialized.set(this, false);
|
|
88
129
|
messenger.subscribe('NetworkController:networkAdded', ({ chainId }) => {
|
|
89
|
-
|
|
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
|
+
});
|
|
90
134
|
});
|
|
91
135
|
messenger.subscribe('NetworkController:networkRemoved', ({ chainId }) => {
|
|
92
136
|
__classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_removeNetworkEntry).call(this, chainId);
|
|
@@ -149,8 +193,9 @@ class NetworkEnablementController extends base_controller_1.BaseController {
|
|
|
149
193
|
throw new Error(`Chain ID ${chainId} belongs to namespace ${derivedNamespace}, but namespace ${namespace} was specified`);
|
|
150
194
|
}
|
|
151
195
|
this.update((s) => {
|
|
152
|
-
// Ensure the namespace
|
|
196
|
+
// Ensure the namespace buckets exist
|
|
153
197
|
__classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
|
|
198
|
+
__classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
|
|
154
199
|
// Disable all networks in the specified namespace first
|
|
155
200
|
if (s.enabledNetworkMap[namespace]) {
|
|
156
201
|
Object.keys(s.enabledNetworkMap[namespace]).forEach((key) => {
|
|
@@ -187,8 +232,9 @@ class NetworkEnablementController extends base_controller_1.BaseController {
|
|
|
187
232
|
const { namespace, storageKey } = (0, utils_2.deriveKeys)(chainId);
|
|
188
233
|
// Check if network exists in NetworkController configurations
|
|
189
234
|
if (networkControllerState.networkConfigurationsByChainId[chainId]) {
|
|
190
|
-
// Ensure namespace
|
|
235
|
+
// Ensure namespace buckets exist
|
|
191
236
|
__classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
|
|
237
|
+
__classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
|
|
192
238
|
// Enable the network
|
|
193
239
|
s.enabledNetworkMap[namespace][storageKey] = true;
|
|
194
240
|
}
|
|
@@ -196,16 +242,18 @@ class NetworkEnablementController extends base_controller_1.BaseController {
|
|
|
196
242
|
// Enable Solana mainnet if it exists in MultichainNetworkController configurations
|
|
197
243
|
const solanaKeys = (0, utils_2.deriveKeys)(keyring_api_1.SolScope.Mainnet);
|
|
198
244
|
if (multichainState.multichainNetworkConfigurationsByChainId[keyring_api_1.SolScope.Mainnet]) {
|
|
199
|
-
// Ensure namespace
|
|
245
|
+
// Ensure namespace buckets exist
|
|
200
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);
|
|
201
248
|
// Enable Solana mainnet
|
|
202
249
|
s.enabledNetworkMap[solanaKeys.namespace][solanaKeys.storageKey] = true;
|
|
203
250
|
}
|
|
204
251
|
// Enable Bitcoin mainnet if it exists in MultichainNetworkController configurations
|
|
205
252
|
const bitcoinKeys = (0, utils_2.deriveKeys)(keyring_api_1.BtcScope.Mainnet);
|
|
206
253
|
if (multichainState.multichainNetworkConfigurationsByChainId[keyring_api_1.BtcScope.Mainnet]) {
|
|
207
|
-
// Ensure namespace
|
|
254
|
+
// Ensure namespace buckets exist
|
|
208
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);
|
|
209
257
|
// Enable Bitcoin mainnet
|
|
210
258
|
s.enabledNetworkMap[bitcoinKeys.namespace][bitcoinKeys.storageKey] =
|
|
211
259
|
true;
|
|
@@ -213,8 +261,9 @@ class NetworkEnablementController extends base_controller_1.BaseController {
|
|
|
213
261
|
// Enable Tron mainnet if it exists in MultichainNetworkController configurations
|
|
214
262
|
const tronKeys = (0, utils_2.deriveKeys)(keyring_api_1.TrxScope.Mainnet);
|
|
215
263
|
if (multichainState.multichainNetworkConfigurationsByChainId[keyring_api_1.TrxScope.Mainnet]) {
|
|
216
|
-
// Ensure namespace
|
|
264
|
+
// Ensure namespace buckets exist
|
|
217
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);
|
|
218
267
|
// Enable Tron mainnet
|
|
219
268
|
s.enabledNetworkMap[tronKeys.namespace][tronKeys.storageKey] = true;
|
|
220
269
|
}
|
|
@@ -227,20 +276,29 @@ class NetworkEnablementController extends base_controller_1.BaseController {
|
|
|
227
276
|
* and MultichainNetworkController and syncs the enabled network map accordingly.
|
|
228
277
|
* It ensures proper namespace buckets exist for all configured networks and only
|
|
229
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.
|
|
230
284
|
*
|
|
231
285
|
* This method should be called after the NetworkController and MultichainNetworkController
|
|
232
286
|
* have been initialized and their configurations are available.
|
|
233
287
|
*/
|
|
234
|
-
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
|
|
235
296
|
this.update((s) => {
|
|
236
|
-
// Get network configurations from NetworkController (EVM networks)
|
|
237
|
-
const networkControllerState = this.messenger.call('NetworkController:getState');
|
|
238
|
-
// Get network configurations from MultichainNetworkController (all networks)
|
|
239
|
-
const multichainState = this.messenger.call('MultichainNetworkController:getState');
|
|
240
297
|
// Initialize namespace buckets for EVM networks from NetworkController
|
|
241
298
|
Object.keys(networkControllerState.networkConfigurationsByChainId).forEach((chainId) => {
|
|
242
299
|
const { namespace, storageKey } = (0, utils_2.deriveKeys)(chainId);
|
|
243
300
|
__classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
|
|
301
|
+
__classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
|
|
244
302
|
// Only add network if it doesn't already exist in state (preserves user settings)
|
|
245
303
|
if (s.enabledNetworkMap[namespace][storageKey] === undefined) {
|
|
246
304
|
s.enabledNetworkMap[namespace][storageKey] = false;
|
|
@@ -250,12 +308,63 @@ class NetworkEnablementController extends base_controller_1.BaseController {
|
|
|
250
308
|
Object.keys(multichainState.multichainNetworkConfigurationsByChainId).forEach((chainId) => {
|
|
251
309
|
const { namespace, storageKey } = (0, utils_2.deriveKeys)(chainId);
|
|
252
310
|
__classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
|
|
311
|
+
__classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
|
|
253
312
|
// Only add network if it doesn't already exist in state (preserves user settings)
|
|
254
313
|
if (s.enabledNetworkMap[namespace][storageKey] === undefined) {
|
|
255
314
|
s.enabledNetworkMap[namespace][storageKey] = false;
|
|
256
315
|
}
|
|
257
316
|
});
|
|
258
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();
|
|
259
368
|
}
|
|
260
369
|
/**
|
|
261
370
|
* Disables a network for the user.
|
|
@@ -293,10 +402,14 @@ class NetworkEnablementController extends base_controller_1.BaseController {
|
|
|
293
402
|
}
|
|
294
403
|
}
|
|
295
404
|
exports.NetworkEnablementController = NetworkEnablementController;
|
|
296
|
-
_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) {
|
|
297
406
|
if (!state.enabledNetworkMap[ns]) {
|
|
298
407
|
state.enabledNetworkMap[ns] = {};
|
|
299
408
|
}
|
|
409
|
+
}, _NetworkEnablementController_ensureSlip44NamespaceBucket = function _NetworkEnablementController_ensureSlip44NamespaceBucket(state, ns) {
|
|
410
|
+
if (!state.slip44[ns]) {
|
|
411
|
+
state.slip44[ns] = {};
|
|
412
|
+
}
|
|
300
413
|
}, _NetworkEnablementController_isInPopularNetworksMode = function _NetworkEnablementController_isInPopularNetworksMode() {
|
|
301
414
|
// Get current network configurations to check which popular networks exist
|
|
302
415
|
const networkControllerState = this.messenger.call('NetworkController:getState');
|
|
@@ -325,11 +438,41 @@ _NetworkEnablementController_instances = new WeakSet(), _NetworkEnablementContro
|
|
|
325
438
|
delete s.enabledNetworkMap[namespace][storageKey];
|
|
326
439
|
}
|
|
327
440
|
});
|
|
328
|
-
}, _NetworkEnablementController_onAddNetwork =
|
|
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) {
|
|
329
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
|
+
}
|
|
330
468
|
this.update((s) => {
|
|
331
|
-
// Ensure the namespace
|
|
469
|
+
// Ensure the namespace buckets exist
|
|
332
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
|
+
}
|
|
333
476
|
// Check if popular networks mode is active (>2 popular networks enabled)
|
|
334
477
|
const inPopularNetworksMode = __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_isInPopularNetworksMode).call(this);
|
|
335
478
|
// Check if the network being added is a popular network
|