@metamask-previews/multichain-account-controller 0.0.0-preview-7b670bb5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +20 -0
  3. package/README.md +15 -0
  4. package/dist/MultichainAccountController.cjs +88 -0
  5. package/dist/MultichainAccountController.cjs.map +1 -0
  6. package/dist/MultichainAccountController.d.cts +66 -0
  7. package/dist/MultichainAccountController.d.cts.map +1 -0
  8. package/dist/MultichainAccountController.d.mts +66 -0
  9. package/dist/MultichainAccountController.d.mts.map +1 -0
  10. package/dist/MultichainAccountController.mjs +84 -0
  11. package/dist/MultichainAccountController.mjs.map +1 -0
  12. package/dist/index.cjs +13 -0
  13. package/dist/index.cjs.map +1 -0
  14. package/dist/index.d.cts +8 -0
  15. package/dist/index.d.cts.map +1 -0
  16. package/dist/index.d.mts +8 -0
  17. package/dist/index.d.mts.map +1 -0
  18. package/dist/index.mjs +10 -0
  19. package/dist/index.mjs.map +1 -0
  20. package/dist/providers/EvmAccountProvider.cjs +99 -0
  21. package/dist/providers/EvmAccountProvider.cjs.map +1 -0
  22. package/dist/providers/EvmAccountProvider.d.cts +64 -0
  23. package/dist/providers/EvmAccountProvider.d.cts.map +1 -0
  24. package/dist/providers/EvmAccountProvider.d.mts +64 -0
  25. package/dist/providers/EvmAccountProvider.d.mts.map +1 -0
  26. package/dist/providers/EvmAccountProvider.mjs +95 -0
  27. package/dist/providers/EvmAccountProvider.mjs.map +1 -0
  28. package/dist/providers/SolAccountProvider.cjs +102 -0
  29. package/dist/providers/SolAccountProvider.cjs.map +1 -0
  30. package/dist/providers/SolAccountProvider.d.cts +65 -0
  31. package/dist/providers/SolAccountProvider.d.cts.map +1 -0
  32. package/dist/providers/SolAccountProvider.d.mts +65 -0
  33. package/dist/providers/SolAccountProvider.d.mts.map +1 -0
  34. package/dist/providers/SolAccountProvider.mjs +98 -0
  35. package/dist/providers/SolAccountProvider.mjs.map +1 -0
  36. package/dist/types.cjs +3 -0
  37. package/dist/types.cjs.map +1 -0
  38. package/dist/types.d.cts +2 -0
  39. package/dist/types.d.cts.map +1 -0
  40. package/dist/types.d.mts +2 -0
  41. package/dist/types.d.mts.map +1 -0
  42. package/dist/types.mjs +2 -0
  43. package/dist/types.mjs.map +1 -0
  44. package/package.json +80 -0
@@ -0,0 +1,64 @@
1
+ import { type EntropySourceId } from "@metamask/keyring-api";
2
+ import type { InternalAccount } from "@metamask/keyring-internal-api";
3
+ import type { AccountProvider } from "@metamask/multichain-account-api";
4
+ import type { MultichainAccountControllerMessenger } from "src/MultichainAccountController";
5
+ export declare class EvmAccountProvider implements AccountProvider {
6
+ #private;
7
+ constructor(messenger: MultichainAccountControllerMessenger);
8
+ createAccounts({ entropySource, groupIndex, }: {
9
+ entropySource: EntropySourceId;
10
+ groupIndex: number;
11
+ }): Promise<{
12
+ type: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account";
13
+ id: string;
14
+ options: Record<string, import("@metamask/utils").Json>;
15
+ metadata: {
16
+ name: string;
17
+ importTime: number;
18
+ keyring: {
19
+ type: string;
20
+ };
21
+ nameLastUpdatedAt?: number | undefined;
22
+ snap?: {
23
+ name: string;
24
+ id: string;
25
+ enabled: boolean;
26
+ } | undefined;
27
+ lastSelected?: number | undefined;
28
+ };
29
+ address: string;
30
+ scopes: `${string}:${string}`[];
31
+ methods: string[];
32
+ }[]>;
33
+ discoverAndCreateAccounts({ entropySource, groupIndex, }: {
34
+ entropySource: EntropySourceId;
35
+ groupIndex: number;
36
+ }): Promise<{
37
+ type: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account";
38
+ id: string;
39
+ options: Record<string, import("@metamask/utils").Json>;
40
+ metadata: {
41
+ name: string;
42
+ importTime: number;
43
+ keyring: {
44
+ type: string;
45
+ };
46
+ nameLastUpdatedAt?: number | undefined;
47
+ snap?: {
48
+ name: string;
49
+ id: string;
50
+ enabled: boolean;
51
+ } | undefined;
52
+ lastSelected?: number | undefined;
53
+ };
54
+ address: string;
55
+ scopes: `${string}:${string}`[];
56
+ methods: string[];
57
+ }[]>;
58
+ getAccounts({ entropySource, groupIndex, }: {
59
+ entropySource: EntropySourceId;
60
+ groupIndex: number;
61
+ }): InternalAccount[];
62
+ getEntropySources(): EntropySourceId[];
63
+ }
64
+ //# sourceMappingURL=EvmAccountProvider.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EvmAccountProvider.d.cts","sourceRoot":"","sources":["../../src/providers/EvmAccountProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,eAAe,EAAE,8BAA8B;AAM7E,OAAO,KAAK,EAEV,eAAe,EAChB,uCAAuC;AACxC,OAAO,KAAK,EAAE,eAAe,EAAE,yCAAyC;AACxE,OAAO,KAAK,EAAE,oCAAoC,EAAE,wCAAwC;AAqB5F,qBAAa,kBAAmB,YAAW,eAAe;;gBAG5C,SAAS,EAAE,oCAAoC;IA8BrD,cAAc,CAAC,EACnB,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB;;;;;;;;;;;;;;;;;;;;;;IA4BK,yBAAyB,CAAC,EAC9B,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB;;;;;;;;;;;;;;;;;;;;;;IAyCD,WAAW,CAAC,EACV,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,eAAe,EAAE;IASrB,iBAAiB,IAAI,eAAe,EAAE;CASvC"}
@@ -0,0 +1,64 @@
1
+ import { type EntropySourceId } from "@metamask/keyring-api";
2
+ import type { InternalAccount } from "@metamask/keyring-internal-api";
3
+ import type { AccountProvider } from "@metamask/multichain-account-api";
4
+ import type { MultichainAccountControllerMessenger } from "src/MultichainAccountController";
5
+ export declare class EvmAccountProvider implements AccountProvider {
6
+ #private;
7
+ constructor(messenger: MultichainAccountControllerMessenger);
8
+ createAccounts({ entropySource, groupIndex, }: {
9
+ entropySource: EntropySourceId;
10
+ groupIndex: number;
11
+ }): Promise<{
12
+ type: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account";
13
+ id: string;
14
+ options: Record<string, import("@metamask/utils").Json>;
15
+ metadata: {
16
+ name: string;
17
+ importTime: number;
18
+ keyring: {
19
+ type: string;
20
+ };
21
+ nameLastUpdatedAt?: number | undefined;
22
+ snap?: {
23
+ name: string;
24
+ id: string;
25
+ enabled: boolean;
26
+ } | undefined;
27
+ lastSelected?: number | undefined;
28
+ };
29
+ address: string;
30
+ scopes: `${string}:${string}`[];
31
+ methods: string[];
32
+ }[]>;
33
+ discoverAndCreateAccounts({ entropySource, groupIndex, }: {
34
+ entropySource: EntropySourceId;
35
+ groupIndex: number;
36
+ }): Promise<{
37
+ type: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account";
38
+ id: string;
39
+ options: Record<string, import("@metamask/utils").Json>;
40
+ metadata: {
41
+ name: string;
42
+ importTime: number;
43
+ keyring: {
44
+ type: string;
45
+ };
46
+ nameLastUpdatedAt?: number | undefined;
47
+ snap?: {
48
+ name: string;
49
+ id: string;
50
+ enabled: boolean;
51
+ } | undefined;
52
+ lastSelected?: number | undefined;
53
+ };
54
+ address: string;
55
+ scopes: `${string}:${string}`[];
56
+ methods: string[];
57
+ }[]>;
58
+ getAccounts({ entropySource, groupIndex, }: {
59
+ entropySource: EntropySourceId;
60
+ groupIndex: number;
61
+ }): InternalAccount[];
62
+ getEntropySources(): EntropySourceId[];
63
+ }
64
+ //# sourceMappingURL=EvmAccountProvider.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EvmAccountProvider.d.mts","sourceRoot":"","sources":["../../src/providers/EvmAccountProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,eAAe,EAAE,8BAA8B;AAM7E,OAAO,KAAK,EAEV,eAAe,EAChB,uCAAuC;AACxC,OAAO,KAAK,EAAE,eAAe,EAAE,yCAAyC;AACxE,OAAO,KAAK,EAAE,oCAAoC,EAAE,wCAAwC;AAqB5F,qBAAa,kBAAmB,YAAW,eAAe;;gBAG5C,SAAS,EAAE,oCAAoC;IA8BrD,cAAc,CAAC,EACnB,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB;;;;;;;;;;;;;;;;;;;;;;IA4BK,yBAAyB,CAAC,EAC9B,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB;;;;;;;;;;;;;;;;;;;;;;IAyCD,WAAW,CAAC,EACV,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,eAAe,EAAE;IASrB,iBAAiB,IAAI,eAAe,EAAE;CASvC"}
@@ -0,0 +1,95 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ 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");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ 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");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _EvmAccountProvider_instances, _EvmAccountProvider_messenger, _EvmAccountProvider_withKeyring, _EvmAccountProvider_getAccounts;
13
+ import { EthAccountType } from "@metamask/keyring-api";
14
+ import { KeyringTypes } from "@metamask/keyring-controller";
15
+ // Max index used by discovery (until we move the proper discovery here).
16
+ const MAX_GROUP_INDEX = 1;
17
+ // eslint-disable-next-line jsdoc/require-jsdoc
18
+ function assertInternalAccountExists(account) {
19
+ if (!account) {
20
+ throw new Error('Internal account does not exist');
21
+ }
22
+ }
23
+ export class EvmAccountProvider {
24
+ constructor(messenger) {
25
+ _EvmAccountProvider_instances.add(this);
26
+ _EvmAccountProvider_messenger.set(this, void 0);
27
+ __classPrivateFieldSet(this, _EvmAccountProvider_messenger, messenger, "f");
28
+ }
29
+ async createAccounts({ entropySource, groupIndex, }) {
30
+ const addresses = await __classPrivateFieldGet(this, _EvmAccountProvider_instances, "m", _EvmAccountProvider_withKeyring).call(this, { id: entropySource }, async ({ keyring }) => {
31
+ const accounts = await keyring.getAccounts();
32
+ if (groupIndex <= accounts.length) {
33
+ // Nothing new to create, we just re-use the existing accounts here,
34
+ return [accounts[groupIndex]];
35
+ }
36
+ // Create new accounts (and returns their addresses).
37
+ return await keyring.addAccounts(groupIndex);
38
+ });
39
+ // Only use the account associated for that index.
40
+ const address = addresses[groupIndex];
41
+ const account = __classPrivateFieldGet(this, _EvmAccountProvider_messenger, "f").call('AccountsController:getAccountByAddress', address);
42
+ // We MUST have the associated internal account.
43
+ assertInternalAccountExists(account);
44
+ return [account];
45
+ }
46
+ async discoverAndCreateAccounts({ entropySource, groupIndex, }) {
47
+ // TODO: Move account discovery here (for EVM).
48
+ if (groupIndex < MAX_GROUP_INDEX) {
49
+ return await this.createAccounts({ entropySource, groupIndex });
50
+ }
51
+ return [];
52
+ }
53
+ getAccounts({ entropySource, groupIndex, }) {
54
+ return __classPrivateFieldGet(this, _EvmAccountProvider_instances, "m", _EvmAccountProvider_getAccounts).call(this).filter((account) => {
55
+ return (account.options.entropySource === entropySource &&
56
+ account.options.index === groupIndex);
57
+ });
58
+ }
59
+ getEntropySources() {
60
+ const entropySources = new Set();
61
+ for (const account of __classPrivateFieldGet(this, _EvmAccountProvider_instances, "m", _EvmAccountProvider_getAccounts).call(this)) {
62
+ entropySources.add(account.options.entropySource);
63
+ }
64
+ return Array.from(entropySources);
65
+ }
66
+ }
67
+ _EvmAccountProvider_messenger = new WeakMap(), _EvmAccountProvider_instances = new WeakSet(), _EvmAccountProvider_withKeyring = async function _EvmAccountProvider_withKeyring(selector, operation) {
68
+ const result = await __classPrivateFieldGet(this, _EvmAccountProvider_messenger, "f").call('KeyringController:withKeyring', selector, ({ keyring, metadata }) => operation({
69
+ keyring: keyring,
70
+ metadata,
71
+ }));
72
+ return result;
73
+ }, _EvmAccountProvider_getAccounts = function _EvmAccountProvider_getAccounts() {
74
+ return __classPrivateFieldGet(this, _EvmAccountProvider_messenger, "f")
75
+ .call('AccountsController:listMultichainAccounts')
76
+ .filter((account) => {
77
+ // We only check for EOA accounts for multichain accounts.
78
+ if (account.type !== EthAccountType.Eoa ||
79
+ account.metadata.keyring.type !== KeyringTypes.hd) {
80
+ return false;
81
+ }
82
+ // TODO: Maybe use superstruct to validate the structure of HD account since they are not strongly-typed for now?
83
+ if (!account.options.entropySource) {
84
+ console.warn("! Found an HD account with no entropy source: account won't be associated to its wallet.");
85
+ return false;
86
+ }
87
+ // TODO: We need to add this index for native accounts too!
88
+ if (account.options.index === undefined) {
89
+ console.warn("! Found an HD account with no index: account won't be associated to its wallet.");
90
+ return false;
91
+ }
92
+ return true;
93
+ }); // Safe, we did check for options fields during filtering.
94
+ };
95
+ //# sourceMappingURL=EvmAccountProvider.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EvmAccountProvider.mjs","sourceRoot":"","sources":["../../src/providers/EvmAccountProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import { EthAccountType, type EntropySourceId } from '@metamask/keyring-api';\nimport {\n KeyringTypes,\n type KeyringMetadata,\n type KeyringSelector,\n} from '@metamask/keyring-controller';\nimport type {\n EthKeyring,\n InternalAccount,\n} from '@metamask/keyring-internal-api';\nimport type { AccountProvider } from '@metamask/multichain-account-api';\nimport type { MultichainAccountControllerMessenger } from 'src/MultichainAccountController';\n\n// Max index used by discovery (until we move the proper discovery here).\nconst MAX_GROUP_INDEX = 1;\n\ntype EoaInternalAccount = InternalAccount & {\n options: {\n index: number;\n entropySource: EntropySourceId;\n };\n};\n\n// eslint-disable-next-line jsdoc/require-jsdoc\nfunction assertInternalAccountExists(\n account: InternalAccount | undefined,\n): asserts account is InternalAccount {\n if (!account) {\n throw new Error('Internal account does not exist');\n }\n}\n\nexport class EvmAccountProvider implements AccountProvider {\n readonly #messenger: MultichainAccountControllerMessenger;\n\n constructor(messenger: MultichainAccountControllerMessenger) {\n this.#messenger = messenger;\n }\n\n async #withKeyring<\n SelectedKeyring extends EthKeyring = EthKeyring,\n CallbackResult = void,\n >(\n selector: KeyringSelector,\n operation: ({\n keyring,\n metadata,\n }: {\n keyring: SelectedKeyring;\n metadata: KeyringMetadata;\n }) => Promise<CallbackResult>,\n ): Promise<CallbackResult> {\n const result = await this.#messenger.call(\n 'KeyringController:withKeyring',\n selector,\n ({ keyring, metadata }) =>\n operation({\n keyring: keyring as SelectedKeyring,\n metadata,\n }),\n );\n\n return result as CallbackResult;\n }\n\n async createAccounts({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }) {\n const addresses = await this.#withKeyring(\n { id: entropySource },\n async ({ keyring }) => {\n const accounts = await keyring.getAccounts();\n if (groupIndex <= accounts.length) {\n // Nothing new to create, we just re-use the existing accounts here,\n return [accounts[groupIndex]];\n }\n\n // Create new accounts (and returns their addresses).\n return await keyring.addAccounts(groupIndex);\n },\n );\n\n // Only use the account associated for that index.\n const address = addresses[groupIndex];\n const account = this.#messenger.call(\n 'AccountsController:getAccountByAddress',\n address,\n );\n\n // We MUST have the associated internal account.\n assertInternalAccountExists(account);\n\n return [account];\n }\n\n async discoverAndCreateAccounts({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }) {\n // TODO: Move account discovery here (for EVM).\n\n if (groupIndex < MAX_GROUP_INDEX) {\n return await this.createAccounts({ entropySource, groupIndex });\n }\n return [];\n }\n\n #getAccounts(): EoaInternalAccount[] {\n return this.#messenger\n .call('AccountsController:listMultichainAccounts')\n .filter((account) => {\n // We only check for EOA accounts for multichain accounts.\n if (\n account.type !== EthAccountType.Eoa ||\n account.metadata.keyring.type !== (KeyringTypes.hd as string)\n ) {\n return false;\n }\n\n // TODO: Maybe use superstruct to validate the structure of HD account since they are not strongly-typed for now?\n if (!account.options.entropySource) {\n console.warn(\n \"! Found an HD account with no entropy source: account won't be associated to its wallet.\",\n );\n return false;\n }\n\n // TODO: We need to add this index for native accounts too!\n if (account.options.index === undefined) {\n console.warn(\n \"! Found an HD account with no index: account won't be associated to its wallet.\",\n );\n return false;\n }\n\n return true;\n }) as EoaInternalAccount[]; // Safe, we did check for options fields during filtering.\n }\n\n getAccounts({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }): InternalAccount[] {\n return this.#getAccounts().filter((account) => {\n return (\n account.options.entropySource === entropySource &&\n account.options.index === groupIndex\n );\n });\n }\n\n getEntropySources(): EntropySourceId[] {\n const entropySources = new Set<EntropySourceId>();\n\n for (const account of this.#getAccounts()) {\n entropySources.add(account.options.entropySource);\n }\n\n return Array.from(entropySources);\n }\n}\n"]}
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ 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");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ 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");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _SolAccountProvider_instances, _SolAccountProvider_messenger, _SolAccountProvider_client, _SolAccountProvider_getKeyringClientFromSnapId, _SolAccountProvider_createAccount, _SolAccountProvider_getAccounts;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.SolAccountProvider = void 0;
16
+ const keyring_api_1 = require("@metamask/keyring-api");
17
+ const keyring_controller_1 = require("@metamask/keyring-controller");
18
+ const keyring_snap_client_1 = require("@metamask/keyring-snap-client");
19
+ const snaps_utils_1 = require("@metamask/snaps-utils");
20
+ // eslint-disable-next-line jsdoc/require-jsdoc
21
+ function assertInternalAccountExists(account) {
22
+ if (!account) {
23
+ throw new Error('Internal account does not exist');
24
+ }
25
+ }
26
+ class SolAccountProvider {
27
+ constructor(messenger) {
28
+ _SolAccountProvider_instances.add(this);
29
+ _SolAccountProvider_messenger.set(this, void 0);
30
+ _SolAccountProvider_client.set(this, void 0);
31
+ __classPrivateFieldSet(this, _SolAccountProvider_messenger, messenger, "f");
32
+ // TODO: Change this once we introduce 1 Snap keyring per Snaps
33
+ __classPrivateFieldSet(this, _SolAccountProvider_client, __classPrivateFieldGet(this, _SolAccountProvider_instances, "m", _SolAccountProvider_getKeyringClientFromSnapId).call(this, 'npm:@metamask/solana-wallet-snap'), "f");
34
+ }
35
+ async createAccounts({ entropySource, groupIndex, }) {
36
+ const account = await __classPrivateFieldGet(this, _SolAccountProvider_instances, "m", _SolAccountProvider_createAccount).call(this, {
37
+ entropySource,
38
+ derivationPath: `m/44'/501'/${groupIndex}'/0'`,
39
+ });
40
+ return [account];
41
+ }
42
+ async discoverAndCreateAccounts({ entropySource, groupIndex, }) {
43
+ const discoveredAccounts = await __classPrivateFieldGet(this, _SolAccountProvider_client, "f").discoverAccounts([keyring_api_1.SolScope.Mainnet, keyring_api_1.SolScope.Testnet], entropySource, groupIndex);
44
+ return await Promise.all(discoveredAccounts.map(async ({ derivationPath }) => await __classPrivateFieldGet(this, _SolAccountProvider_instances, "m", _SolAccountProvider_createAccount).call(this, { entropySource, derivationPath })));
45
+ }
46
+ getAccounts({ entropySource, groupIndex, }) {
47
+ return __classPrivateFieldGet(this, _SolAccountProvider_instances, "m", _SolAccountProvider_getAccounts).call(this).filter((account) => {
48
+ return (account.options.entropySource === entropySource &&
49
+ account.options.index === groupIndex);
50
+ });
51
+ }
52
+ getEntropySources() {
53
+ const entropySources = new Set();
54
+ for (const account of __classPrivateFieldGet(this, _SolAccountProvider_instances, "m", _SolAccountProvider_getAccounts).call(this)) {
55
+ entropySources.add(account.options.entropySource);
56
+ }
57
+ return Array.from(entropySources);
58
+ }
59
+ }
60
+ exports.SolAccountProvider = SolAccountProvider;
61
+ _SolAccountProvider_messenger = new WeakMap(), _SolAccountProvider_client = new WeakMap(), _SolAccountProvider_instances = new WeakSet(), _SolAccountProvider_getKeyringClientFromSnapId = function _SolAccountProvider_getKeyringClientFromSnapId(snapId) {
62
+ return new keyring_snap_client_1.KeyringClient({
63
+ send: async (request) => {
64
+ const response = await __classPrivateFieldGet(this, _SolAccountProvider_messenger, "f").call('SnapController:handleRequest', {
65
+ snapId: snapId,
66
+ origin: 'metamask',
67
+ handler: snaps_utils_1.HandlerType.OnKeyringRequest,
68
+ request,
69
+ });
70
+ return response;
71
+ },
72
+ });
73
+ }, _SolAccountProvider_createAccount = async function _SolAccountProvider_createAccount(opts) {
74
+ const keyringAccount = await __classPrivateFieldGet(this, _SolAccountProvider_client, "f").createAccount(opts);
75
+ // Actually get the associated `InternalAccount`.
76
+ const account = __classPrivateFieldGet(this, _SolAccountProvider_messenger, "f").call('AccountsController:getAccount', keyringAccount.id);
77
+ // We MUST have the associated internal account.
78
+ assertInternalAccountExists(account);
79
+ return account;
80
+ }, _SolAccountProvider_getAccounts = function _SolAccountProvider_getAccounts() {
81
+ return __classPrivateFieldGet(this, _SolAccountProvider_messenger, "f")
82
+ .call('AccountsController:listMultichainAccounts')
83
+ .filter((account) => {
84
+ // We only check for EOA accounts for multichain accounts.
85
+ if (account.type !== keyring_api_1.SolAccountType.DataAccount ||
86
+ account.metadata.keyring.type !== keyring_controller_1.KeyringTypes.snap) {
87
+ return false;
88
+ }
89
+ // TODO: Maybe use superstruct to validate the structure of HD account since they are not strongly-typed for now?
90
+ if (!account.options.entropySource) {
91
+ console.warn("! Found a Solana account with no entropy source: account won't be associated to its wallet.");
92
+ return false;
93
+ }
94
+ // TODO: We need to add this index for native accounts too!
95
+ if (account.options.index === undefined) {
96
+ console.warn("! Found a Solana account with no index: account won't be associated to its wallet.");
97
+ return false;
98
+ }
99
+ return true;
100
+ }); // Safe, we did check for options fields during filtering.
101
+ };
102
+ //# sourceMappingURL=SolAccountProvider.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SolAccountProvider.cjs","sourceRoot":"","sources":["../../src/providers/SolAccountProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n SolAccountType,\n SolScope,\n type EntropySourceId,\n} from '@metamask/keyring-api';\nimport { KeyringTypes } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { KeyringClient } from '@metamask/keyring-snap-client';\nimport type { AccountProvider } from '@metamask/multichain-account-api';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type { Json, JsonRpcRequest } from '@metamask/utils';\nimport type { MultichainAccountControllerMessenger } from 'src/MultichainAccountController';\n\ntype SolInternalAccount = InternalAccount & {\n options: {\n index: number;\n entropySource: EntropySourceId;\n };\n};\n\n// eslint-disable-next-line jsdoc/require-jsdoc\nfunction assertInternalAccountExists(\n account: InternalAccount | undefined,\n): asserts account is InternalAccount {\n if (!account) {\n throw new Error('Internal account does not exist');\n }\n}\n\nexport class SolAccountProvider implements AccountProvider {\n readonly #messenger: MultichainAccountControllerMessenger;\n\n readonly #client: KeyringClient;\n\n constructor(messenger: MultichainAccountControllerMessenger) {\n this.#messenger = messenger;\n\n // TODO: Change this once we introduce 1 Snap keyring per Snaps\n this.#client = this.#getKeyringClientFromSnapId(\n 'npm:@metamask/solana-wallet-snap',\n );\n }\n\n #getKeyringClientFromSnapId(snapId: string): KeyringClient {\n return new KeyringClient({\n send: async (request: JsonRpcRequest) => {\n const response = await this.#messenger.call(\n 'SnapController:handleRequest',\n {\n snapId: snapId as SnapId,\n origin: 'metamask',\n handler: HandlerType.OnKeyringRequest,\n request,\n },\n );\n return response as Promise<Json>;\n },\n });\n }\n\n async #createAccount(opts: {\n entropySource: EntropySourceId;\n derivationPath: `m/${string}`;\n }) {\n const keyringAccount = await this.#client.createAccount(opts);\n\n // Actually get the associated `InternalAccount`.\n const account = this.#messenger.call(\n 'AccountsController:getAccount',\n keyringAccount.id,\n );\n\n // We MUST have the associated internal account.\n assertInternalAccountExists(account);\n\n return account;\n }\n\n async createAccounts({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }) {\n const account = await this.#createAccount({\n entropySource,\n derivationPath: `m/44'/501'/${groupIndex}'/0'`,\n });\n\n return [account];\n }\n\n async discoverAndCreateAccounts({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }) {\n const discoveredAccounts = await this.#client.discoverAccounts(\n [SolScope.Mainnet, SolScope.Testnet],\n entropySource,\n groupIndex,\n );\n\n return await Promise.all(\n discoveredAccounts.map(\n async ({ derivationPath }) =>\n await this.#createAccount({ entropySource, derivationPath }),\n ),\n );\n }\n\n #getAccounts(): SolInternalAccount[] {\n return this.#messenger\n .call('AccountsController:listMultichainAccounts')\n .filter((account) => {\n // We only check for EOA accounts for multichain accounts.\n if (\n account.type !== SolAccountType.DataAccount ||\n account.metadata.keyring.type !== (KeyringTypes.snap as string)\n ) {\n return false;\n }\n\n // TODO: Maybe use superstruct to validate the structure of HD account since they are not strongly-typed for now?\n if (!account.options.entropySource) {\n console.warn(\n \"! Found a Solana account with no entropy source: account won't be associated to its wallet.\",\n );\n return false;\n }\n\n // TODO: We need to add this index for native accounts too!\n if (account.options.index === undefined) {\n console.warn(\n \"! Found a Solana account with no index: account won't be associated to its wallet.\",\n );\n return false;\n }\n\n return true;\n }) as SolInternalAccount[]; // Safe, we did check for options fields during filtering.\n }\n\n getAccounts({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }): InternalAccount[] {\n return this.#getAccounts().filter((account) => {\n return (\n account.options.entropySource === entropySource &&\n account.options.index === groupIndex\n );\n });\n }\n\n getEntropySources(): EntropySourceId[] {\n const entropySources = new Set<EntropySourceId>();\n\n for (const account of this.#getAccounts()) {\n entropySources.add(account.options.entropySource);\n }\n\n return Array.from(entropySources);\n }\n}\n"]}
@@ -0,0 +1,65 @@
1
+ import { type EntropySourceId } from "@metamask/keyring-api";
2
+ import type { InternalAccount } from "@metamask/keyring-internal-api";
3
+ import type { AccountProvider } from "@metamask/multichain-account-api";
4
+ import type { Json } from "@metamask/utils";
5
+ import type { MultichainAccountControllerMessenger } from "src/MultichainAccountController";
6
+ export declare class SolAccountProvider implements AccountProvider {
7
+ #private;
8
+ constructor(messenger: MultichainAccountControllerMessenger);
9
+ createAccounts({ entropySource, groupIndex, }: {
10
+ entropySource: EntropySourceId;
11
+ groupIndex: number;
12
+ }): Promise<{
13
+ type: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account";
14
+ id: string;
15
+ options: Record<string, Json>;
16
+ metadata: {
17
+ name: string;
18
+ importTime: number;
19
+ keyring: {
20
+ type: string;
21
+ };
22
+ nameLastUpdatedAt?: number | undefined;
23
+ snap?: {
24
+ name: string;
25
+ id: string;
26
+ enabled: boolean;
27
+ } | undefined;
28
+ lastSelected?: number | undefined;
29
+ };
30
+ address: string;
31
+ scopes: `${string}:${string}`[];
32
+ methods: string[];
33
+ }[]>;
34
+ discoverAndCreateAccounts({ entropySource, groupIndex, }: {
35
+ entropySource: EntropySourceId;
36
+ groupIndex: number;
37
+ }): Promise<{
38
+ type: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account";
39
+ id: string;
40
+ options: Record<string, Json>;
41
+ metadata: {
42
+ name: string;
43
+ importTime: number;
44
+ keyring: {
45
+ type: string;
46
+ };
47
+ nameLastUpdatedAt?: number | undefined;
48
+ snap?: {
49
+ name: string;
50
+ id: string;
51
+ enabled: boolean;
52
+ } | undefined;
53
+ lastSelected?: number | undefined;
54
+ };
55
+ address: string;
56
+ scopes: `${string}:${string}`[];
57
+ methods: string[];
58
+ }[]>;
59
+ getAccounts({ entropySource, groupIndex, }: {
60
+ entropySource: EntropySourceId;
61
+ groupIndex: number;
62
+ }): InternalAccount[];
63
+ getEntropySources(): EntropySourceId[];
64
+ }
65
+ //# sourceMappingURL=SolAccountProvider.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SolAccountProvider.d.cts","sourceRoot":"","sources":["../../src/providers/SolAccountProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EACrB,8BAA8B;AAE/B,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAEtE,OAAO,KAAK,EAAE,eAAe,EAAE,yCAAyC;AAGxE,OAAO,KAAK,EAAE,IAAI,EAAkB,wBAAwB;AAC5D,OAAO,KAAK,EAAE,oCAAoC,EAAE,wCAAwC;AAkB5F,qBAAa,kBAAmB,YAAW,eAAe;;gBAK5C,SAAS,EAAE,oCAAoC;IA4CrD,cAAc,CAAC,EACnB,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB;;;;;;;;;;;;;;;;;;;;;;IASK,yBAAyB,CAAC,EAC9B,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB;;;;;;;;;;;;;;;;;;;;;;IA+CD,WAAW,CAAC,EACV,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,eAAe,EAAE;IASrB,iBAAiB,IAAI,eAAe,EAAE;CASvC"}
@@ -0,0 +1,65 @@
1
+ import { type EntropySourceId } from "@metamask/keyring-api";
2
+ import type { InternalAccount } from "@metamask/keyring-internal-api";
3
+ import type { AccountProvider } from "@metamask/multichain-account-api";
4
+ import type { Json } from "@metamask/utils";
5
+ import type { MultichainAccountControllerMessenger } from "src/MultichainAccountController";
6
+ export declare class SolAccountProvider implements AccountProvider {
7
+ #private;
8
+ constructor(messenger: MultichainAccountControllerMessenger);
9
+ createAccounts({ entropySource, groupIndex, }: {
10
+ entropySource: EntropySourceId;
11
+ groupIndex: number;
12
+ }): Promise<{
13
+ type: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account";
14
+ id: string;
15
+ options: Record<string, Json>;
16
+ metadata: {
17
+ name: string;
18
+ importTime: number;
19
+ keyring: {
20
+ type: string;
21
+ };
22
+ nameLastUpdatedAt?: number | undefined;
23
+ snap?: {
24
+ name: string;
25
+ id: string;
26
+ enabled: boolean;
27
+ } | undefined;
28
+ lastSelected?: number | undefined;
29
+ };
30
+ address: string;
31
+ scopes: `${string}:${string}`[];
32
+ methods: string[];
33
+ }[]>;
34
+ discoverAndCreateAccounts({ entropySource, groupIndex, }: {
35
+ entropySource: EntropySourceId;
36
+ groupIndex: number;
37
+ }): Promise<{
38
+ type: "eip155:eoa" | "eip155:erc4337" | "bip122:p2pkh" | "bip122:p2sh" | "bip122:p2wpkh" | "bip122:p2tr" | "solana:data-account";
39
+ id: string;
40
+ options: Record<string, Json>;
41
+ metadata: {
42
+ name: string;
43
+ importTime: number;
44
+ keyring: {
45
+ type: string;
46
+ };
47
+ nameLastUpdatedAt?: number | undefined;
48
+ snap?: {
49
+ name: string;
50
+ id: string;
51
+ enabled: boolean;
52
+ } | undefined;
53
+ lastSelected?: number | undefined;
54
+ };
55
+ address: string;
56
+ scopes: `${string}:${string}`[];
57
+ methods: string[];
58
+ }[]>;
59
+ getAccounts({ entropySource, groupIndex, }: {
60
+ entropySource: EntropySourceId;
61
+ groupIndex: number;
62
+ }): InternalAccount[];
63
+ getEntropySources(): EntropySourceId[];
64
+ }
65
+ //# sourceMappingURL=SolAccountProvider.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SolAccountProvider.d.mts","sourceRoot":"","sources":["../../src/providers/SolAccountProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EACrB,8BAA8B;AAE/B,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAEtE,OAAO,KAAK,EAAE,eAAe,EAAE,yCAAyC;AAGxE,OAAO,KAAK,EAAE,IAAI,EAAkB,wBAAwB;AAC5D,OAAO,KAAK,EAAE,oCAAoC,EAAE,wCAAwC;AAkB5F,qBAAa,kBAAmB,YAAW,eAAe;;gBAK5C,SAAS,EAAE,oCAAoC;IA4CrD,cAAc,CAAC,EACnB,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB;;;;;;;;;;;;;;;;;;;;;;IASK,yBAAyB,CAAC,EAC9B,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB;;;;;;;;;;;;;;;;;;;;;;IA+CD,WAAW,CAAC,EACV,aAAa,EACb,UAAU,GACX,EAAE;QACD,aAAa,EAAE,eAAe,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,eAAe,EAAE;IASrB,iBAAiB,IAAI,eAAe,EAAE;CASvC"}
@@ -0,0 +1,98 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ 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");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ 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");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _SolAccountProvider_instances, _SolAccountProvider_messenger, _SolAccountProvider_client, _SolAccountProvider_getKeyringClientFromSnapId, _SolAccountProvider_createAccount, _SolAccountProvider_getAccounts;
13
+ import { SolAccountType, SolScope } from "@metamask/keyring-api";
14
+ import { KeyringTypes } from "@metamask/keyring-controller";
15
+ import { KeyringClient } from "@metamask/keyring-snap-client";
16
+ import { HandlerType } from "@metamask/snaps-utils";
17
+ // eslint-disable-next-line jsdoc/require-jsdoc
18
+ function assertInternalAccountExists(account) {
19
+ if (!account) {
20
+ throw new Error('Internal account does not exist');
21
+ }
22
+ }
23
+ export class SolAccountProvider {
24
+ constructor(messenger) {
25
+ _SolAccountProvider_instances.add(this);
26
+ _SolAccountProvider_messenger.set(this, void 0);
27
+ _SolAccountProvider_client.set(this, void 0);
28
+ __classPrivateFieldSet(this, _SolAccountProvider_messenger, messenger, "f");
29
+ // TODO: Change this once we introduce 1 Snap keyring per Snaps
30
+ __classPrivateFieldSet(this, _SolAccountProvider_client, __classPrivateFieldGet(this, _SolAccountProvider_instances, "m", _SolAccountProvider_getKeyringClientFromSnapId).call(this, 'npm:@metamask/solana-wallet-snap'), "f");
31
+ }
32
+ async createAccounts({ entropySource, groupIndex, }) {
33
+ const account = await __classPrivateFieldGet(this, _SolAccountProvider_instances, "m", _SolAccountProvider_createAccount).call(this, {
34
+ entropySource,
35
+ derivationPath: `m/44'/501'/${groupIndex}'/0'`,
36
+ });
37
+ return [account];
38
+ }
39
+ async discoverAndCreateAccounts({ entropySource, groupIndex, }) {
40
+ const discoveredAccounts = await __classPrivateFieldGet(this, _SolAccountProvider_client, "f").discoverAccounts([SolScope.Mainnet, SolScope.Testnet], entropySource, groupIndex);
41
+ return await Promise.all(discoveredAccounts.map(async ({ derivationPath }) => await __classPrivateFieldGet(this, _SolAccountProvider_instances, "m", _SolAccountProvider_createAccount).call(this, { entropySource, derivationPath })));
42
+ }
43
+ getAccounts({ entropySource, groupIndex, }) {
44
+ return __classPrivateFieldGet(this, _SolAccountProvider_instances, "m", _SolAccountProvider_getAccounts).call(this).filter((account) => {
45
+ return (account.options.entropySource === entropySource &&
46
+ account.options.index === groupIndex);
47
+ });
48
+ }
49
+ getEntropySources() {
50
+ const entropySources = new Set();
51
+ for (const account of __classPrivateFieldGet(this, _SolAccountProvider_instances, "m", _SolAccountProvider_getAccounts).call(this)) {
52
+ entropySources.add(account.options.entropySource);
53
+ }
54
+ return Array.from(entropySources);
55
+ }
56
+ }
57
+ _SolAccountProvider_messenger = new WeakMap(), _SolAccountProvider_client = new WeakMap(), _SolAccountProvider_instances = new WeakSet(), _SolAccountProvider_getKeyringClientFromSnapId = function _SolAccountProvider_getKeyringClientFromSnapId(snapId) {
58
+ return new KeyringClient({
59
+ send: async (request) => {
60
+ const response = await __classPrivateFieldGet(this, _SolAccountProvider_messenger, "f").call('SnapController:handleRequest', {
61
+ snapId: snapId,
62
+ origin: 'metamask',
63
+ handler: HandlerType.OnKeyringRequest,
64
+ request,
65
+ });
66
+ return response;
67
+ },
68
+ });
69
+ }, _SolAccountProvider_createAccount = async function _SolAccountProvider_createAccount(opts) {
70
+ const keyringAccount = await __classPrivateFieldGet(this, _SolAccountProvider_client, "f").createAccount(opts);
71
+ // Actually get the associated `InternalAccount`.
72
+ const account = __classPrivateFieldGet(this, _SolAccountProvider_messenger, "f").call('AccountsController:getAccount', keyringAccount.id);
73
+ // We MUST have the associated internal account.
74
+ assertInternalAccountExists(account);
75
+ return account;
76
+ }, _SolAccountProvider_getAccounts = function _SolAccountProvider_getAccounts() {
77
+ return __classPrivateFieldGet(this, _SolAccountProvider_messenger, "f")
78
+ .call('AccountsController:listMultichainAccounts')
79
+ .filter((account) => {
80
+ // We only check for EOA accounts for multichain accounts.
81
+ if (account.type !== SolAccountType.DataAccount ||
82
+ account.metadata.keyring.type !== KeyringTypes.snap) {
83
+ return false;
84
+ }
85
+ // TODO: Maybe use superstruct to validate the structure of HD account since they are not strongly-typed for now?
86
+ if (!account.options.entropySource) {
87
+ console.warn("! Found a Solana account with no entropy source: account won't be associated to its wallet.");
88
+ return false;
89
+ }
90
+ // TODO: We need to add this index for native accounts too!
91
+ if (account.options.index === undefined) {
92
+ console.warn("! Found a Solana account with no index: account won't be associated to its wallet.");
93
+ return false;
94
+ }
95
+ return true;
96
+ }); // Safe, we did check for options fields during filtering.
97
+ };
98
+ //# sourceMappingURL=SolAccountProvider.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SolAccountProvider.mjs","sourceRoot":"","sources":["../../src/providers/SolAccountProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n SolAccountType,\n SolScope,\n type EntropySourceId,\n} from '@metamask/keyring-api';\nimport { KeyringTypes } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { KeyringClient } from '@metamask/keyring-snap-client';\nimport type { AccountProvider } from '@metamask/multichain-account-api';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type { Json, JsonRpcRequest } from '@metamask/utils';\nimport type { MultichainAccountControllerMessenger } from 'src/MultichainAccountController';\n\ntype SolInternalAccount = InternalAccount & {\n options: {\n index: number;\n entropySource: EntropySourceId;\n };\n};\n\n// eslint-disable-next-line jsdoc/require-jsdoc\nfunction assertInternalAccountExists(\n account: InternalAccount | undefined,\n): asserts account is InternalAccount {\n if (!account) {\n throw new Error('Internal account does not exist');\n }\n}\n\nexport class SolAccountProvider implements AccountProvider {\n readonly #messenger: MultichainAccountControllerMessenger;\n\n readonly #client: KeyringClient;\n\n constructor(messenger: MultichainAccountControllerMessenger) {\n this.#messenger = messenger;\n\n // TODO: Change this once we introduce 1 Snap keyring per Snaps\n this.#client = this.#getKeyringClientFromSnapId(\n 'npm:@metamask/solana-wallet-snap',\n );\n }\n\n #getKeyringClientFromSnapId(snapId: string): KeyringClient {\n return new KeyringClient({\n send: async (request: JsonRpcRequest) => {\n const response = await this.#messenger.call(\n 'SnapController:handleRequest',\n {\n snapId: snapId as SnapId,\n origin: 'metamask',\n handler: HandlerType.OnKeyringRequest,\n request,\n },\n );\n return response as Promise<Json>;\n },\n });\n }\n\n async #createAccount(opts: {\n entropySource: EntropySourceId;\n derivationPath: `m/${string}`;\n }) {\n const keyringAccount = await this.#client.createAccount(opts);\n\n // Actually get the associated `InternalAccount`.\n const account = this.#messenger.call(\n 'AccountsController:getAccount',\n keyringAccount.id,\n );\n\n // We MUST have the associated internal account.\n assertInternalAccountExists(account);\n\n return account;\n }\n\n async createAccounts({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }) {\n const account = await this.#createAccount({\n entropySource,\n derivationPath: `m/44'/501'/${groupIndex}'/0'`,\n });\n\n return [account];\n }\n\n async discoverAndCreateAccounts({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }) {\n const discoveredAccounts = await this.#client.discoverAccounts(\n [SolScope.Mainnet, SolScope.Testnet],\n entropySource,\n groupIndex,\n );\n\n return await Promise.all(\n discoveredAccounts.map(\n async ({ derivationPath }) =>\n await this.#createAccount({ entropySource, derivationPath }),\n ),\n );\n }\n\n #getAccounts(): SolInternalAccount[] {\n return this.#messenger\n .call('AccountsController:listMultichainAccounts')\n .filter((account) => {\n // We only check for EOA accounts for multichain accounts.\n if (\n account.type !== SolAccountType.DataAccount ||\n account.metadata.keyring.type !== (KeyringTypes.snap as string)\n ) {\n return false;\n }\n\n // TODO: Maybe use superstruct to validate the structure of HD account since they are not strongly-typed for now?\n if (!account.options.entropySource) {\n console.warn(\n \"! Found a Solana account with no entropy source: account won't be associated to its wallet.\",\n );\n return false;\n }\n\n // TODO: We need to add this index for native accounts too!\n if (account.options.index === undefined) {\n console.warn(\n \"! Found a Solana account with no index: account won't be associated to its wallet.\",\n );\n return false;\n }\n\n return true;\n }) as SolInternalAccount[]; // Safe, we did check for options fields during filtering.\n }\n\n getAccounts({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }): InternalAccount[] {\n return this.#getAccounts().filter((account) => {\n return (\n account.options.entropySource === entropySource &&\n account.options.index === groupIndex\n );\n });\n }\n\n getEntropySources(): EntropySourceId[] {\n const entropySources = new Set<EntropySourceId>();\n\n for (const account of this.#getAccounts()) {\n entropySources.add(account.options.entropySource);\n }\n\n return Array.from(entropySources);\n }\n}\n"]}
package/dist/types.cjs ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.cjs.map