@metamask-previews/seedless-onboarding-controller 0.0.0-preview-ddbcf048

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 (60) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/LICENSE +20 -0
  3. package/README.md +15 -0
  4. package/dist/SeedPhraseMetadata.cjs +146 -0
  5. package/dist/SeedPhraseMetadata.cjs.map +1 -0
  6. package/dist/SeedPhraseMetadata.d.cts +82 -0
  7. package/dist/SeedPhraseMetadata.d.cts.map +1 -0
  8. package/dist/SeedPhraseMetadata.d.mts +82 -0
  9. package/dist/SeedPhraseMetadata.d.mts.map +1 -0
  10. package/dist/SeedPhraseMetadata.mjs +142 -0
  11. package/dist/SeedPhraseMetadata.mjs.map +1 -0
  12. package/dist/SeedlessOnboardingController.cjs +578 -0
  13. package/dist/SeedlessOnboardingController.cjs.map +1 -0
  14. package/dist/SeedlessOnboardingController.d.cts +87 -0
  15. package/dist/SeedlessOnboardingController.d.cts.map +1 -0
  16. package/dist/SeedlessOnboardingController.d.mts +87 -0
  17. package/dist/SeedlessOnboardingController.d.mts.map +1 -0
  18. package/dist/SeedlessOnboardingController.mjs +573 -0
  19. package/dist/SeedlessOnboardingController.mjs.map +1 -0
  20. package/dist/constants.cjs +29 -0
  21. package/dist/constants.cjs.map +1 -0
  22. package/dist/constants.d.cts +24 -0
  23. package/dist/constants.d.cts.map +1 -0
  24. package/dist/constants.d.mts +24 -0
  25. package/dist/constants.d.mts.map +1 -0
  26. package/dist/constants.mjs +26 -0
  27. package/dist/constants.mjs.map +1 -0
  28. package/dist/errors.cjs +76 -0
  29. package/dist/errors.cjs.map +1 -0
  30. package/dist/errors.d.cts +26 -0
  31. package/dist/errors.d.cts.map +1 -0
  32. package/dist/errors.d.mts +26 -0
  33. package/dist/errors.d.mts.map +1 -0
  34. package/dist/errors.mjs +72 -0
  35. package/dist/errors.mjs.map +1 -0
  36. package/dist/index.cjs +24 -0
  37. package/dist/index.cjs.map +1 -0
  38. package/dist/index.d.cts +5 -0
  39. package/dist/index.d.cts.map +1 -0
  40. package/dist/index.d.mts +5 -0
  41. package/dist/index.d.mts.map +1 -0
  42. package/dist/index.mjs +4 -0
  43. package/dist/index.mjs.map +1 -0
  44. package/dist/logger.cjs +8 -0
  45. package/dist/logger.cjs.map +1 -0
  46. package/dist/logger.d.cts +5 -0
  47. package/dist/logger.d.cts.map +1 -0
  48. package/dist/logger.d.mts +5 -0
  49. package/dist/logger.d.mts.map +1 -0
  50. package/dist/logger.mjs +5 -0
  51. package/dist/logger.mjs.map +1 -0
  52. package/dist/types.cjs +3 -0
  53. package/dist/types.cjs.map +1 -0
  54. package/dist/types.d.cts +120 -0
  55. package/dist/types.d.cts.map +1 -0
  56. package/dist/types.d.mts +120 -0
  57. package/dist/types.d.mts.map +1 -0
  58. package/dist/types.mjs +2 -0
  59. package/dist/types.mjs.map +1 -0
  60. package/package.json +80 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - Initial implementation of the seedless onboarding controller. ([#5671](https://github.com/MetaMask/core/pull/5671))
13
+ - Authenticate OAuth user using the seedless onboarding flow and determine if the user is already registered or not
14
+ - Create a new Toprf key and backup seed phrase
15
+ - Add a new seed phrase backup to the metadata store
16
+ - Add array of new seed phrase backups to the metadata store in batch (useful in multi-srp flow)
17
+ - Fetch seed phrase metadata from the metadata store
18
+ - Update the password of the seedless onboarding flow
19
+
20
+ [Unreleased]: https://github.com/MetaMask/core/
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 MetaMask
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # `@metamask/seedless-onboarding-controller`
2
+
3
+ Backup and rehydrate SRP(s) using social login and password
4
+
5
+ ## Installation
6
+
7
+ `yarn add @metamask/seedless-onboarding-controller`
8
+
9
+ or
10
+
11
+ `npm install @metamask/seedless-onboarding-controller`
12
+
13
+ ## Contributing
14
+
15
+ This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme).
@@ -0,0 +1,146 @@
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 _SeedPhraseMetadata_seedPhrase, _SeedPhraseMetadata_timestamp;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.SeedPhraseMetadata = void 0;
16
+ const utils_1 = require("@metamask/utils");
17
+ const constants_1 = require("./constants.cjs");
18
+ /**
19
+ * SeedPhraseMetadata is a class that adds metadata to the seed phrase.
20
+ *
21
+ * It contains the seed phrase and the timestamp when it was created.
22
+ * It is used to store the seed phrase in the metadata store.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const seedPhraseMetadata = new SeedPhraseMetadata(seedPhrase);
27
+ * ```
28
+ */
29
+ class SeedPhraseMetadata {
30
+ /**
31
+ * Create a new SeedPhraseMetadata instance.
32
+ *
33
+ * @param seedPhrase - The seed phrase to add metadata to.
34
+ * @param timestamp - The timestamp when the seed phrase was created.
35
+ */
36
+ constructor(seedPhrase, timestamp = Date.now()) {
37
+ _SeedPhraseMetadata_seedPhrase.set(this, void 0);
38
+ _SeedPhraseMetadata_timestamp.set(this, void 0);
39
+ __classPrivateFieldSet(this, _SeedPhraseMetadata_seedPhrase, seedPhrase, "f");
40
+ __classPrivateFieldSet(this, _SeedPhraseMetadata_timestamp, timestamp, "f");
41
+ }
42
+ /**
43
+ * Create an Array of SeedPhraseMetadata instances from an array of seed phrases.
44
+ *
45
+ * To respect the order of the seed phrases, we add the index to the timestamp
46
+ * so that the first seed phrase backup will have the oldest timestamp
47
+ * and the last seed phrase backup will have the newest timestamp.
48
+ *
49
+ * @param seedPhrases - The seed phrases to add metadata to.
50
+ * @returns The SeedPhraseMetadata instances.
51
+ */
52
+ static fromBatchSeedPhrases(seedPhrases) {
53
+ const timestamp = Date.now();
54
+ return seedPhrases.map((seedPhrase, index) => {
55
+ // To respect the order of the seed phrases, we add the index to the timestamp
56
+ // so that the first seed phrase backup will have the oldest timestamp
57
+ // and the last seed phrase backup will have the newest timestamp
58
+ const backupCreatedAt = timestamp + index * 5;
59
+ return new SeedPhraseMetadata(seedPhrase, backupCreatedAt);
60
+ });
61
+ }
62
+ /**
63
+ * Assert that the provided value is a valid seed phrase metadata.
64
+ *
65
+ * @param value - The value to check.
66
+ * @throws If the value is not a valid seed phrase metadata.
67
+ */
68
+ static assertIsBase64SeedphraseMetadata(value) {
69
+ if (typeof value !== 'object' ||
70
+ !value ||
71
+ !('seedPhrase' in value) ||
72
+ typeof value.seedPhrase !== 'string' ||
73
+ !('timestamp' in value) ||
74
+ typeof value.timestamp !== 'number') {
75
+ throw new Error(constants_1.SeedlessOnboardingControllerError.InvalidSeedPhraseMetadata);
76
+ }
77
+ }
78
+ /**
79
+ * Parse the seed phrase metadata from the metadata store and return the array of raw seed phrases.
80
+ *
81
+ * This method also sorts the seed phrases by timestamp in descending order, i.e. the newest seed phrase will be the first element in the array.
82
+ *
83
+ * @param seedPhraseMetadataArr - The array of SeedPhrase Metadata from the metadata store.
84
+ * @returns The array of raw seed phrases.
85
+ */
86
+ static parseSeedPhraseFromMetadataStore(seedPhraseMetadataArr) {
87
+ const parsedSeedPhraseMetadata = seedPhraseMetadataArr.map((metadata) => SeedPhraseMetadata.fromRawMetadata(metadata));
88
+ const seedPhrases = SeedPhraseMetadata.sort(parsedSeedPhraseMetadata);
89
+ return seedPhrases.map((seedPhraseMetadata) => seedPhraseMetadata.seedPhrase);
90
+ }
91
+ /**
92
+ * Parse and create the SeedPhraseMetadata instance from the raw metadata.
93
+ *
94
+ * @param rawMetadata - The raw metadata.
95
+ * @returns The parsed seed phrase metadata.
96
+ */
97
+ static fromRawMetadata(rawMetadata) {
98
+ const serializedMetadata = (0, utils_1.bytesToString)(rawMetadata);
99
+ const parsedMetadata = JSON.parse(serializedMetadata);
100
+ SeedPhraseMetadata.assertIsBase64SeedphraseMetadata(parsedMetadata);
101
+ const seedPhraseBytes = (0, utils_1.base64ToBytes)(parsedMetadata.seedPhrase);
102
+ return new SeedPhraseMetadata(seedPhraseBytes, parsedMetadata.timestamp);
103
+ }
104
+ /**
105
+ * Sort the seed phrases by timestamp.
106
+ *
107
+ * @param seedPhrases - The seed phrases to sort.
108
+ * @param order - The order to sort the seed phrases. Default is `desc`.
109
+ *
110
+ * @returns The sorted seed phrases.
111
+ */
112
+ static sort(seedPhrases, order = 'desc') {
113
+ return seedPhrases.sort((a, b) => {
114
+ if (order === 'asc') {
115
+ return a.timestamp - b.timestamp;
116
+ }
117
+ return b.timestamp - a.timestamp;
118
+ });
119
+ }
120
+ get seedPhrase() {
121
+ return __classPrivateFieldGet(this, _SeedPhraseMetadata_seedPhrase, "f");
122
+ }
123
+ get timestamp() {
124
+ return __classPrivateFieldGet(this, _SeedPhraseMetadata_timestamp, "f");
125
+ }
126
+ /**
127
+ * Serialize the seed phrase metadata and convert it to a Uint8Array.
128
+ *
129
+ * @returns The serialized SeedPhraseMetadata value in bytes.
130
+ */
131
+ toBytes() {
132
+ // encode the raw SeedPhrase to base64 encoded string
133
+ // to create more compacted metadata
134
+ const b64SeedPhrase = (0, utils_1.bytesToBase64)(__classPrivateFieldGet(this, _SeedPhraseMetadata_seedPhrase, "f"));
135
+ // serialize the metadata to a JSON string
136
+ const serializedMetadata = JSON.stringify({
137
+ seedPhrase: b64SeedPhrase,
138
+ timestamp: __classPrivateFieldGet(this, _SeedPhraseMetadata_timestamp, "f"),
139
+ });
140
+ // convert the serialized metadata to bytes(Uint8Array)
141
+ return (0, utils_1.stringToBytes)(serializedMetadata);
142
+ }
143
+ }
144
+ exports.SeedPhraseMetadata = SeedPhraseMetadata;
145
+ _SeedPhraseMetadata_seedPhrase = new WeakMap(), _SeedPhraseMetadata_timestamp = new WeakMap();
146
+ //# sourceMappingURL=SeedPhraseMetadata.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeedPhraseMetadata.cjs","sourceRoot":"","sources":["../src/SeedPhraseMetadata.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n base64ToBytes,\n bytesToBase64,\n stringToBytes,\n bytesToString,\n} from '@metamask/utils';\n\nimport { SeedlessOnboardingControllerError } from './constants';\n\ntype ISeedPhraseMetadata = {\n seedPhrase: Uint8Array;\n timestamp: number;\n toBytes: () => Uint8Array;\n};\n\n// SeedPhraseMetadata type without the seedPhrase and toBytes methods\n// in which the seedPhrase is base64 encoded for more compacted metadata\ntype IBase64SeedPhraseMetadata = Omit<\n ISeedPhraseMetadata,\n 'seedPhrase' | 'toBytes'\n> & {\n seedPhrase: string; // base64 encoded string\n};\n\n/**\n * SeedPhraseMetadata is a class that adds metadata to the seed phrase.\n *\n * It contains the seed phrase and the timestamp when it was created.\n * It is used to store the seed phrase in the metadata store.\n *\n * @example\n * ```ts\n * const seedPhraseMetadata = new SeedPhraseMetadata(seedPhrase);\n * ```\n */\nexport class SeedPhraseMetadata implements ISeedPhraseMetadata {\n readonly #seedPhrase: Uint8Array;\n\n readonly #timestamp: number;\n\n /**\n * Create a new SeedPhraseMetadata instance.\n *\n * @param seedPhrase - The seed phrase to add metadata to.\n * @param timestamp - The timestamp when the seed phrase was created.\n */\n constructor(seedPhrase: Uint8Array, timestamp: number = Date.now()) {\n this.#seedPhrase = seedPhrase;\n this.#timestamp = timestamp;\n }\n\n /**\n * Create an Array of SeedPhraseMetadata instances from an array of seed phrases.\n *\n * To respect the order of the seed phrases, we add the index to the timestamp\n * so that the first seed phrase backup will have the oldest timestamp\n * and the last seed phrase backup will have the newest timestamp.\n *\n * @param seedPhrases - The seed phrases to add metadata to.\n * @returns The SeedPhraseMetadata instances.\n */\n static fromBatchSeedPhrases(seedPhrases: Uint8Array[]): SeedPhraseMetadata[] {\n const timestamp = Date.now();\n return seedPhrases.map((seedPhrase, index) => {\n // To respect the order of the seed phrases, we add the index to the timestamp\n // so that the first seed phrase backup will have the oldest timestamp\n // and the last seed phrase backup will have the newest timestamp\n const backupCreatedAt = timestamp + index * 5;\n return new SeedPhraseMetadata(seedPhrase, backupCreatedAt);\n });\n }\n\n /**\n * Assert that the provided value is a valid seed phrase metadata.\n *\n * @param value - The value to check.\n * @throws If the value is not a valid seed phrase metadata.\n */\n static assertIsBase64SeedphraseMetadata(\n value: unknown,\n ): asserts value is IBase64SeedPhraseMetadata {\n if (\n typeof value !== 'object' ||\n !value ||\n !('seedPhrase' in value) ||\n typeof value.seedPhrase !== 'string' ||\n !('timestamp' in value) ||\n typeof value.timestamp !== 'number'\n ) {\n throw new Error(\n SeedlessOnboardingControllerError.InvalidSeedPhraseMetadata,\n );\n }\n }\n\n /**\n * Parse the seed phrase metadata from the metadata store and return the array of raw seed phrases.\n *\n * This method also sorts the seed phrases by timestamp in descending order, i.e. the newest seed phrase will be the first element in the array.\n *\n * @param seedPhraseMetadataArr - The array of SeedPhrase Metadata from the metadata store.\n * @returns The array of raw seed phrases.\n */\n static parseSeedPhraseFromMetadataStore(\n seedPhraseMetadataArr: Uint8Array[],\n ): Uint8Array[] {\n const parsedSeedPhraseMetadata = seedPhraseMetadataArr.map((metadata) =>\n SeedPhraseMetadata.fromRawMetadata(metadata),\n );\n\n const seedPhrases = SeedPhraseMetadata.sort(parsedSeedPhraseMetadata);\n\n return seedPhrases.map(\n (seedPhraseMetadata) => seedPhraseMetadata.seedPhrase,\n );\n }\n\n /**\n * Parse and create the SeedPhraseMetadata instance from the raw metadata.\n *\n * @param rawMetadata - The raw metadata.\n * @returns The parsed seed phrase metadata.\n */\n static fromRawMetadata(rawMetadata: Uint8Array): SeedPhraseMetadata {\n const serializedMetadata = bytesToString(rawMetadata);\n const parsedMetadata = JSON.parse(serializedMetadata);\n\n SeedPhraseMetadata.assertIsBase64SeedphraseMetadata(parsedMetadata);\n\n const seedPhraseBytes = base64ToBytes(parsedMetadata.seedPhrase);\n return new SeedPhraseMetadata(seedPhraseBytes, parsedMetadata.timestamp);\n }\n\n /**\n * Sort the seed phrases by timestamp.\n *\n * @param seedPhrases - The seed phrases to sort.\n * @param order - The order to sort the seed phrases. Default is `desc`.\n *\n * @returns The sorted seed phrases.\n */\n static sort(\n seedPhrases: SeedPhraseMetadata[],\n order: 'asc' | 'desc' = 'desc',\n ): SeedPhraseMetadata[] {\n return seedPhrases.sort((a, b) => {\n if (order === 'asc') {\n return a.timestamp - b.timestamp;\n }\n return b.timestamp - a.timestamp;\n });\n }\n\n get seedPhrase() {\n return this.#seedPhrase;\n }\n\n get timestamp() {\n return this.#timestamp;\n }\n\n /**\n * Serialize the seed phrase metadata and convert it to a Uint8Array.\n *\n * @returns The serialized SeedPhraseMetadata value in bytes.\n */\n toBytes(): Uint8Array {\n // encode the raw SeedPhrase to base64 encoded string\n // to create more compacted metadata\n const b64SeedPhrase = bytesToBase64(this.#seedPhrase);\n\n // serialize the metadata to a JSON string\n const serializedMetadata = JSON.stringify({\n seedPhrase: b64SeedPhrase,\n timestamp: this.#timestamp,\n });\n\n // convert the serialized metadata to bytes(Uint8Array)\n return stringToBytes(serializedMetadata);\n }\n}\n"]}
@@ -0,0 +1,82 @@
1
+ type ISeedPhraseMetadata = {
2
+ seedPhrase: Uint8Array;
3
+ timestamp: number;
4
+ toBytes: () => Uint8Array;
5
+ };
6
+ type IBase64SeedPhraseMetadata = Omit<ISeedPhraseMetadata, 'seedPhrase' | 'toBytes'> & {
7
+ seedPhrase: string;
8
+ };
9
+ /**
10
+ * SeedPhraseMetadata is a class that adds metadata to the seed phrase.
11
+ *
12
+ * It contains the seed phrase and the timestamp when it was created.
13
+ * It is used to store the seed phrase in the metadata store.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const seedPhraseMetadata = new SeedPhraseMetadata(seedPhrase);
18
+ * ```
19
+ */
20
+ export declare class SeedPhraseMetadata implements ISeedPhraseMetadata {
21
+ #private;
22
+ /**
23
+ * Create a new SeedPhraseMetadata instance.
24
+ *
25
+ * @param seedPhrase - The seed phrase to add metadata to.
26
+ * @param timestamp - The timestamp when the seed phrase was created.
27
+ */
28
+ constructor(seedPhrase: Uint8Array, timestamp?: number);
29
+ /**
30
+ * Create an Array of SeedPhraseMetadata instances from an array of seed phrases.
31
+ *
32
+ * To respect the order of the seed phrases, we add the index to the timestamp
33
+ * so that the first seed phrase backup will have the oldest timestamp
34
+ * and the last seed phrase backup will have the newest timestamp.
35
+ *
36
+ * @param seedPhrases - The seed phrases to add metadata to.
37
+ * @returns The SeedPhraseMetadata instances.
38
+ */
39
+ static fromBatchSeedPhrases(seedPhrases: Uint8Array[]): SeedPhraseMetadata[];
40
+ /**
41
+ * Assert that the provided value is a valid seed phrase metadata.
42
+ *
43
+ * @param value - The value to check.
44
+ * @throws If the value is not a valid seed phrase metadata.
45
+ */
46
+ static assertIsBase64SeedphraseMetadata(value: unknown): asserts value is IBase64SeedPhraseMetadata;
47
+ /**
48
+ * Parse the seed phrase metadata from the metadata store and return the array of raw seed phrases.
49
+ *
50
+ * This method also sorts the seed phrases by timestamp in descending order, i.e. the newest seed phrase will be the first element in the array.
51
+ *
52
+ * @param seedPhraseMetadataArr - The array of SeedPhrase Metadata from the metadata store.
53
+ * @returns The array of raw seed phrases.
54
+ */
55
+ static parseSeedPhraseFromMetadataStore(seedPhraseMetadataArr: Uint8Array[]): Uint8Array[];
56
+ /**
57
+ * Parse and create the SeedPhraseMetadata instance from the raw metadata.
58
+ *
59
+ * @param rawMetadata - The raw metadata.
60
+ * @returns The parsed seed phrase metadata.
61
+ */
62
+ static fromRawMetadata(rawMetadata: Uint8Array): SeedPhraseMetadata;
63
+ /**
64
+ * Sort the seed phrases by timestamp.
65
+ *
66
+ * @param seedPhrases - The seed phrases to sort.
67
+ * @param order - The order to sort the seed phrases. Default is `desc`.
68
+ *
69
+ * @returns The sorted seed phrases.
70
+ */
71
+ static sort(seedPhrases: SeedPhraseMetadata[], order?: 'asc' | 'desc'): SeedPhraseMetadata[];
72
+ get seedPhrase(): Uint8Array;
73
+ get timestamp(): number;
74
+ /**
75
+ * Serialize the seed phrase metadata and convert it to a Uint8Array.
76
+ *
77
+ * @returns The serialized SeedPhraseMetadata value in bytes.
78
+ */
79
+ toBytes(): Uint8Array;
80
+ }
81
+ export {};
82
+ //# sourceMappingURL=SeedPhraseMetadata.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeedPhraseMetadata.d.cts","sourceRoot":"","sources":["../src/SeedPhraseMetadata.ts"],"names":[],"mappings":"AASA,KAAK,mBAAmB,GAAG;IACzB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,UAAU,CAAC;CAC3B,CAAC;AAIF,KAAK,yBAAyB,GAAG,IAAI,CACnC,mBAAmB,EACnB,YAAY,GAAG,SAAS,CACzB,GAAG;IACF,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,qBAAa,kBAAmB,YAAW,mBAAmB;;IAK5D;;;;;OAKG;gBACS,UAAU,EAAE,UAAU,EAAE,SAAS,GAAE,MAAmB;IAKlE;;;;;;;;;OASG;IACH,MAAM,CAAC,oBAAoB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,kBAAkB,EAAE;IAW5E;;;;;OAKG;IACH,MAAM,CAAC,gCAAgC,CACrC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,KAAK,IAAI,yBAAyB;IAe7C;;;;;;;OAOG;IACH,MAAM,CAAC,gCAAgC,CACrC,qBAAqB,EAAE,UAAU,EAAE,GAClC,UAAU,EAAE;IAYf;;;;;OAKG;IACH,MAAM,CAAC,eAAe,CAAC,WAAW,EAAE,UAAU,GAAG,kBAAkB;IAUnE;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CACT,WAAW,EAAE,kBAAkB,EAAE,EACjC,KAAK,GAAE,KAAK,GAAG,MAAe,GAC7B,kBAAkB,EAAE;IASvB,IAAI,UAAU,eAEb;IAED,IAAI,SAAS,WAEZ;IAED;;;;OAIG;IACH,OAAO,IAAI,UAAU;CActB"}
@@ -0,0 +1,82 @@
1
+ type ISeedPhraseMetadata = {
2
+ seedPhrase: Uint8Array;
3
+ timestamp: number;
4
+ toBytes: () => Uint8Array;
5
+ };
6
+ type IBase64SeedPhraseMetadata = Omit<ISeedPhraseMetadata, 'seedPhrase' | 'toBytes'> & {
7
+ seedPhrase: string;
8
+ };
9
+ /**
10
+ * SeedPhraseMetadata is a class that adds metadata to the seed phrase.
11
+ *
12
+ * It contains the seed phrase and the timestamp when it was created.
13
+ * It is used to store the seed phrase in the metadata store.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const seedPhraseMetadata = new SeedPhraseMetadata(seedPhrase);
18
+ * ```
19
+ */
20
+ export declare class SeedPhraseMetadata implements ISeedPhraseMetadata {
21
+ #private;
22
+ /**
23
+ * Create a new SeedPhraseMetadata instance.
24
+ *
25
+ * @param seedPhrase - The seed phrase to add metadata to.
26
+ * @param timestamp - The timestamp when the seed phrase was created.
27
+ */
28
+ constructor(seedPhrase: Uint8Array, timestamp?: number);
29
+ /**
30
+ * Create an Array of SeedPhraseMetadata instances from an array of seed phrases.
31
+ *
32
+ * To respect the order of the seed phrases, we add the index to the timestamp
33
+ * so that the first seed phrase backup will have the oldest timestamp
34
+ * and the last seed phrase backup will have the newest timestamp.
35
+ *
36
+ * @param seedPhrases - The seed phrases to add metadata to.
37
+ * @returns The SeedPhraseMetadata instances.
38
+ */
39
+ static fromBatchSeedPhrases(seedPhrases: Uint8Array[]): SeedPhraseMetadata[];
40
+ /**
41
+ * Assert that the provided value is a valid seed phrase metadata.
42
+ *
43
+ * @param value - The value to check.
44
+ * @throws If the value is not a valid seed phrase metadata.
45
+ */
46
+ static assertIsBase64SeedphraseMetadata(value: unknown): asserts value is IBase64SeedPhraseMetadata;
47
+ /**
48
+ * Parse the seed phrase metadata from the metadata store and return the array of raw seed phrases.
49
+ *
50
+ * This method also sorts the seed phrases by timestamp in descending order, i.e. the newest seed phrase will be the first element in the array.
51
+ *
52
+ * @param seedPhraseMetadataArr - The array of SeedPhrase Metadata from the metadata store.
53
+ * @returns The array of raw seed phrases.
54
+ */
55
+ static parseSeedPhraseFromMetadataStore(seedPhraseMetadataArr: Uint8Array[]): Uint8Array[];
56
+ /**
57
+ * Parse and create the SeedPhraseMetadata instance from the raw metadata.
58
+ *
59
+ * @param rawMetadata - The raw metadata.
60
+ * @returns The parsed seed phrase metadata.
61
+ */
62
+ static fromRawMetadata(rawMetadata: Uint8Array): SeedPhraseMetadata;
63
+ /**
64
+ * Sort the seed phrases by timestamp.
65
+ *
66
+ * @param seedPhrases - The seed phrases to sort.
67
+ * @param order - The order to sort the seed phrases. Default is `desc`.
68
+ *
69
+ * @returns The sorted seed phrases.
70
+ */
71
+ static sort(seedPhrases: SeedPhraseMetadata[], order?: 'asc' | 'desc'): SeedPhraseMetadata[];
72
+ get seedPhrase(): Uint8Array;
73
+ get timestamp(): number;
74
+ /**
75
+ * Serialize the seed phrase metadata and convert it to a Uint8Array.
76
+ *
77
+ * @returns The serialized SeedPhraseMetadata value in bytes.
78
+ */
79
+ toBytes(): Uint8Array;
80
+ }
81
+ export {};
82
+ //# sourceMappingURL=SeedPhraseMetadata.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeedPhraseMetadata.d.mts","sourceRoot":"","sources":["../src/SeedPhraseMetadata.ts"],"names":[],"mappings":"AASA,KAAK,mBAAmB,GAAG;IACzB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,UAAU,CAAC;CAC3B,CAAC;AAIF,KAAK,yBAAyB,GAAG,IAAI,CACnC,mBAAmB,EACnB,YAAY,GAAG,SAAS,CACzB,GAAG;IACF,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,qBAAa,kBAAmB,YAAW,mBAAmB;;IAK5D;;;;;OAKG;gBACS,UAAU,EAAE,UAAU,EAAE,SAAS,GAAE,MAAmB;IAKlE;;;;;;;;;OASG;IACH,MAAM,CAAC,oBAAoB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,kBAAkB,EAAE;IAW5E;;;;;OAKG;IACH,MAAM,CAAC,gCAAgC,CACrC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,KAAK,IAAI,yBAAyB;IAe7C;;;;;;;OAOG;IACH,MAAM,CAAC,gCAAgC,CACrC,qBAAqB,EAAE,UAAU,EAAE,GAClC,UAAU,EAAE;IAYf;;;;;OAKG;IACH,MAAM,CAAC,eAAe,CAAC,WAAW,EAAE,UAAU,GAAG,kBAAkB;IAUnE;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CACT,WAAW,EAAE,kBAAkB,EAAE,EACjC,KAAK,GAAE,KAAK,GAAG,MAAe,GAC7B,kBAAkB,EAAE;IASvB,IAAI,UAAU,eAEb;IAED,IAAI,SAAS,WAEZ;IAED;;;;OAIG;IACH,OAAO,IAAI,UAAU;CActB"}
@@ -0,0 +1,142 @@
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 _SeedPhraseMetadata_seedPhrase, _SeedPhraseMetadata_timestamp;
13
+ import { base64ToBytes, bytesToBase64, stringToBytes, bytesToString } from "@metamask/utils";
14
+ import { SeedlessOnboardingControllerError } from "./constants.mjs";
15
+ /**
16
+ * SeedPhraseMetadata is a class that adds metadata to the seed phrase.
17
+ *
18
+ * It contains the seed phrase and the timestamp when it was created.
19
+ * It is used to store the seed phrase in the metadata store.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const seedPhraseMetadata = new SeedPhraseMetadata(seedPhrase);
24
+ * ```
25
+ */
26
+ export class SeedPhraseMetadata {
27
+ /**
28
+ * Create a new SeedPhraseMetadata instance.
29
+ *
30
+ * @param seedPhrase - The seed phrase to add metadata to.
31
+ * @param timestamp - The timestamp when the seed phrase was created.
32
+ */
33
+ constructor(seedPhrase, timestamp = Date.now()) {
34
+ _SeedPhraseMetadata_seedPhrase.set(this, void 0);
35
+ _SeedPhraseMetadata_timestamp.set(this, void 0);
36
+ __classPrivateFieldSet(this, _SeedPhraseMetadata_seedPhrase, seedPhrase, "f");
37
+ __classPrivateFieldSet(this, _SeedPhraseMetadata_timestamp, timestamp, "f");
38
+ }
39
+ /**
40
+ * Create an Array of SeedPhraseMetadata instances from an array of seed phrases.
41
+ *
42
+ * To respect the order of the seed phrases, we add the index to the timestamp
43
+ * so that the first seed phrase backup will have the oldest timestamp
44
+ * and the last seed phrase backup will have the newest timestamp.
45
+ *
46
+ * @param seedPhrases - The seed phrases to add metadata to.
47
+ * @returns The SeedPhraseMetadata instances.
48
+ */
49
+ static fromBatchSeedPhrases(seedPhrases) {
50
+ const timestamp = Date.now();
51
+ return seedPhrases.map((seedPhrase, index) => {
52
+ // To respect the order of the seed phrases, we add the index to the timestamp
53
+ // so that the first seed phrase backup will have the oldest timestamp
54
+ // and the last seed phrase backup will have the newest timestamp
55
+ const backupCreatedAt = timestamp + index * 5;
56
+ return new SeedPhraseMetadata(seedPhrase, backupCreatedAt);
57
+ });
58
+ }
59
+ /**
60
+ * Assert that the provided value is a valid seed phrase metadata.
61
+ *
62
+ * @param value - The value to check.
63
+ * @throws If the value is not a valid seed phrase metadata.
64
+ */
65
+ static assertIsBase64SeedphraseMetadata(value) {
66
+ if (typeof value !== 'object' ||
67
+ !value ||
68
+ !('seedPhrase' in value) ||
69
+ typeof value.seedPhrase !== 'string' ||
70
+ !('timestamp' in value) ||
71
+ typeof value.timestamp !== 'number') {
72
+ throw new Error(SeedlessOnboardingControllerError.InvalidSeedPhraseMetadata);
73
+ }
74
+ }
75
+ /**
76
+ * Parse the seed phrase metadata from the metadata store and return the array of raw seed phrases.
77
+ *
78
+ * This method also sorts the seed phrases by timestamp in descending order, i.e. the newest seed phrase will be the first element in the array.
79
+ *
80
+ * @param seedPhraseMetadataArr - The array of SeedPhrase Metadata from the metadata store.
81
+ * @returns The array of raw seed phrases.
82
+ */
83
+ static parseSeedPhraseFromMetadataStore(seedPhraseMetadataArr) {
84
+ const parsedSeedPhraseMetadata = seedPhraseMetadataArr.map((metadata) => SeedPhraseMetadata.fromRawMetadata(metadata));
85
+ const seedPhrases = SeedPhraseMetadata.sort(parsedSeedPhraseMetadata);
86
+ return seedPhrases.map((seedPhraseMetadata) => seedPhraseMetadata.seedPhrase);
87
+ }
88
+ /**
89
+ * Parse and create the SeedPhraseMetadata instance from the raw metadata.
90
+ *
91
+ * @param rawMetadata - The raw metadata.
92
+ * @returns The parsed seed phrase metadata.
93
+ */
94
+ static fromRawMetadata(rawMetadata) {
95
+ const serializedMetadata = bytesToString(rawMetadata);
96
+ const parsedMetadata = JSON.parse(serializedMetadata);
97
+ SeedPhraseMetadata.assertIsBase64SeedphraseMetadata(parsedMetadata);
98
+ const seedPhraseBytes = base64ToBytes(parsedMetadata.seedPhrase);
99
+ return new SeedPhraseMetadata(seedPhraseBytes, parsedMetadata.timestamp);
100
+ }
101
+ /**
102
+ * Sort the seed phrases by timestamp.
103
+ *
104
+ * @param seedPhrases - The seed phrases to sort.
105
+ * @param order - The order to sort the seed phrases. Default is `desc`.
106
+ *
107
+ * @returns The sorted seed phrases.
108
+ */
109
+ static sort(seedPhrases, order = 'desc') {
110
+ return seedPhrases.sort((a, b) => {
111
+ if (order === 'asc') {
112
+ return a.timestamp - b.timestamp;
113
+ }
114
+ return b.timestamp - a.timestamp;
115
+ });
116
+ }
117
+ get seedPhrase() {
118
+ return __classPrivateFieldGet(this, _SeedPhraseMetadata_seedPhrase, "f");
119
+ }
120
+ get timestamp() {
121
+ return __classPrivateFieldGet(this, _SeedPhraseMetadata_timestamp, "f");
122
+ }
123
+ /**
124
+ * Serialize the seed phrase metadata and convert it to a Uint8Array.
125
+ *
126
+ * @returns The serialized SeedPhraseMetadata value in bytes.
127
+ */
128
+ toBytes() {
129
+ // encode the raw SeedPhrase to base64 encoded string
130
+ // to create more compacted metadata
131
+ const b64SeedPhrase = bytesToBase64(__classPrivateFieldGet(this, _SeedPhraseMetadata_seedPhrase, "f"));
132
+ // serialize the metadata to a JSON string
133
+ const serializedMetadata = JSON.stringify({
134
+ seedPhrase: b64SeedPhrase,
135
+ timestamp: __classPrivateFieldGet(this, _SeedPhraseMetadata_timestamp, "f"),
136
+ });
137
+ // convert the serialized metadata to bytes(Uint8Array)
138
+ return stringToBytes(serializedMetadata);
139
+ }
140
+ }
141
+ _SeedPhraseMetadata_seedPhrase = new WeakMap(), _SeedPhraseMetadata_timestamp = new WeakMap();
142
+ //# sourceMappingURL=SeedPhraseMetadata.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeedPhraseMetadata.mjs","sourceRoot":"","sources":["../src/SeedPhraseMetadata.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n base64ToBytes,\n bytesToBase64,\n stringToBytes,\n bytesToString,\n} from '@metamask/utils';\n\nimport { SeedlessOnboardingControllerError } from './constants';\n\ntype ISeedPhraseMetadata = {\n seedPhrase: Uint8Array;\n timestamp: number;\n toBytes: () => Uint8Array;\n};\n\n// SeedPhraseMetadata type without the seedPhrase and toBytes methods\n// in which the seedPhrase is base64 encoded for more compacted metadata\ntype IBase64SeedPhraseMetadata = Omit<\n ISeedPhraseMetadata,\n 'seedPhrase' | 'toBytes'\n> & {\n seedPhrase: string; // base64 encoded string\n};\n\n/**\n * SeedPhraseMetadata is a class that adds metadata to the seed phrase.\n *\n * It contains the seed phrase and the timestamp when it was created.\n * It is used to store the seed phrase in the metadata store.\n *\n * @example\n * ```ts\n * const seedPhraseMetadata = new SeedPhraseMetadata(seedPhrase);\n * ```\n */\nexport class SeedPhraseMetadata implements ISeedPhraseMetadata {\n readonly #seedPhrase: Uint8Array;\n\n readonly #timestamp: number;\n\n /**\n * Create a new SeedPhraseMetadata instance.\n *\n * @param seedPhrase - The seed phrase to add metadata to.\n * @param timestamp - The timestamp when the seed phrase was created.\n */\n constructor(seedPhrase: Uint8Array, timestamp: number = Date.now()) {\n this.#seedPhrase = seedPhrase;\n this.#timestamp = timestamp;\n }\n\n /**\n * Create an Array of SeedPhraseMetadata instances from an array of seed phrases.\n *\n * To respect the order of the seed phrases, we add the index to the timestamp\n * so that the first seed phrase backup will have the oldest timestamp\n * and the last seed phrase backup will have the newest timestamp.\n *\n * @param seedPhrases - The seed phrases to add metadata to.\n * @returns The SeedPhraseMetadata instances.\n */\n static fromBatchSeedPhrases(seedPhrases: Uint8Array[]): SeedPhraseMetadata[] {\n const timestamp = Date.now();\n return seedPhrases.map((seedPhrase, index) => {\n // To respect the order of the seed phrases, we add the index to the timestamp\n // so that the first seed phrase backup will have the oldest timestamp\n // and the last seed phrase backup will have the newest timestamp\n const backupCreatedAt = timestamp + index * 5;\n return new SeedPhraseMetadata(seedPhrase, backupCreatedAt);\n });\n }\n\n /**\n * Assert that the provided value is a valid seed phrase metadata.\n *\n * @param value - The value to check.\n * @throws If the value is not a valid seed phrase metadata.\n */\n static assertIsBase64SeedphraseMetadata(\n value: unknown,\n ): asserts value is IBase64SeedPhraseMetadata {\n if (\n typeof value !== 'object' ||\n !value ||\n !('seedPhrase' in value) ||\n typeof value.seedPhrase !== 'string' ||\n !('timestamp' in value) ||\n typeof value.timestamp !== 'number'\n ) {\n throw new Error(\n SeedlessOnboardingControllerError.InvalidSeedPhraseMetadata,\n );\n }\n }\n\n /**\n * Parse the seed phrase metadata from the metadata store and return the array of raw seed phrases.\n *\n * This method also sorts the seed phrases by timestamp in descending order, i.e. the newest seed phrase will be the first element in the array.\n *\n * @param seedPhraseMetadataArr - The array of SeedPhrase Metadata from the metadata store.\n * @returns The array of raw seed phrases.\n */\n static parseSeedPhraseFromMetadataStore(\n seedPhraseMetadataArr: Uint8Array[],\n ): Uint8Array[] {\n const parsedSeedPhraseMetadata = seedPhraseMetadataArr.map((metadata) =>\n SeedPhraseMetadata.fromRawMetadata(metadata),\n );\n\n const seedPhrases = SeedPhraseMetadata.sort(parsedSeedPhraseMetadata);\n\n return seedPhrases.map(\n (seedPhraseMetadata) => seedPhraseMetadata.seedPhrase,\n );\n }\n\n /**\n * Parse and create the SeedPhraseMetadata instance from the raw metadata.\n *\n * @param rawMetadata - The raw metadata.\n * @returns The parsed seed phrase metadata.\n */\n static fromRawMetadata(rawMetadata: Uint8Array): SeedPhraseMetadata {\n const serializedMetadata = bytesToString(rawMetadata);\n const parsedMetadata = JSON.parse(serializedMetadata);\n\n SeedPhraseMetadata.assertIsBase64SeedphraseMetadata(parsedMetadata);\n\n const seedPhraseBytes = base64ToBytes(parsedMetadata.seedPhrase);\n return new SeedPhraseMetadata(seedPhraseBytes, parsedMetadata.timestamp);\n }\n\n /**\n * Sort the seed phrases by timestamp.\n *\n * @param seedPhrases - The seed phrases to sort.\n * @param order - The order to sort the seed phrases. Default is `desc`.\n *\n * @returns The sorted seed phrases.\n */\n static sort(\n seedPhrases: SeedPhraseMetadata[],\n order: 'asc' | 'desc' = 'desc',\n ): SeedPhraseMetadata[] {\n return seedPhrases.sort((a, b) => {\n if (order === 'asc') {\n return a.timestamp - b.timestamp;\n }\n return b.timestamp - a.timestamp;\n });\n }\n\n get seedPhrase() {\n return this.#seedPhrase;\n }\n\n get timestamp() {\n return this.#timestamp;\n }\n\n /**\n * Serialize the seed phrase metadata and convert it to a Uint8Array.\n *\n * @returns The serialized SeedPhraseMetadata value in bytes.\n */\n toBytes(): Uint8Array {\n // encode the raw SeedPhrase to base64 encoded string\n // to create more compacted metadata\n const b64SeedPhrase = bytesToBase64(this.#seedPhrase);\n\n // serialize the metadata to a JSON string\n const serializedMetadata = JSON.stringify({\n seedPhrase: b64SeedPhrase,\n timestamp: this.#timestamp,\n });\n\n // convert the serialized metadata to bytes(Uint8Array)\n return stringToBytes(serializedMetadata);\n }\n}\n"]}