diff --git a/packages/keyring-sdk/CHANGELOG.md b/packages/keyring-sdk/CHANGELOG.md index 82995ff6..d82090d2 100644 --- a/packages/keyring-sdk/CHANGELOG.md +++ b/packages/keyring-sdk/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `encodeMnemonic` ([#495](https://github.com/MetaMask/accounts/pull/495)) + ### Changed - Bump `@metamask/utils` from `^11.1.0` to `^11.10.0` ([#489](https://github.com/MetaMask/accounts/pull/489)) diff --git a/packages/keyring-sdk/package.json b/packages/keyring-sdk/package.json index 8a3329e8..f324e157 100644 --- a/packages/keyring-sdk/package.json +++ b/packages/keyring-sdk/package.json @@ -49,6 +49,7 @@ "@metamask/eth-sig-util": "^8.2.0", "@metamask/keyring-api": "^22.0.0", "@metamask/keyring-utils": "^3.2.0", + "@metamask/scure-bip39": "^2.1.1", "@metamask/superstruct": "^3.1.0", "@metamask/utils": "^11.10.0", "async-mutex": "^0.5.0", diff --git a/packages/keyring-sdk/src/index.ts b/packages/keyring-sdk/src/index.ts index ba41eb93..f942b0d5 100644 --- a/packages/keyring-sdk/src/index.ts +++ b/packages/keyring-sdk/src/index.ts @@ -1,3 +1,4 @@ export * from './keyring-wrapper'; export * from './keyring-account-registry'; +export * from './mnemonic'; export * from './eth'; diff --git a/packages/keyring-sdk/src/mnemonic.test.ts b/packages/keyring-sdk/src/mnemonic.test.ts new file mode 100644 index 00000000..17b39747 --- /dev/null +++ b/packages/keyring-sdk/src/mnemonic.test.ts @@ -0,0 +1,34 @@ +import { encodeMnemonic } from './mnemonic'; + +const toIndicesBytes = (indices: number[]): Uint8Array => + new Uint8Array(new Uint16Array(indices).buffer); + +describe('encodeMnemonic', () => { + it('returns an empty array for empty input', () => { + expect(encodeMnemonic(toIndicesBytes([]))).toStrictEqual([]); + }); + + it('encodes a single word (index 0 → "abandon")', () => { + const expected = Array.from(new TextEncoder().encode('abandon')); + expect(encodeMnemonic(toIndicesBytes([0]))).toStrictEqual(expected); + }); + + it('encodes two words separated by a space', () => { + // index 0 → "abandon", index 1 → "ability" + const expected = Array.from(new TextEncoder().encode('abandon ability')); + expect(encodeMnemonic(toIndicesBytes([0, 1]))).toStrictEqual(expected); + }); + + it('encodes a standard 12-word mnemonic (all "abandon")', () => { + const indices = new Array(12).fill(0); + const expected = Array.from( + new TextEncoder().encode(new Array(12).fill('abandon').join(' ')), + ); + expect(encodeMnemonic(toIndicesBytes(indices))).toStrictEqual(expected); + }); + + it('returns number[] (not a Uint8Array)', () => { + const result = encodeMnemonic(toIndicesBytes([0])); + expect(Array.isArray(result)).toBe(true); + }); +}); diff --git a/packages/keyring-sdk/src/mnemonic.ts b/packages/keyring-sdk/src/mnemonic.ts new file mode 100644 index 00000000..524e5d2c --- /dev/null +++ b/packages/keyring-sdk/src/mnemonic.ts @@ -0,0 +1,18 @@ +import { wordlist } from '@metamask/scure-bip39/dist/wordlists/english'; + +/** + * Encodes a mnemonic as an array of bytes (UTF-8). + * + * @param mnemonicIndicesBytes - An array of bytes (16-bit unsigned integers) representing the indices of the words in the mnemonic. + * @returns An array of bytes (UTF-8) representing the mnemonic. + */ +export function encodeMnemonic(mnemonicIndicesBytes: Uint8Array): number[] { + const mnemonicIndices = Array.from( + // Create a new `Uint8Array` to ensure we have a proper view on the buffer + // without having to worry about `byteOffset` and `byteLength` of + // the inner buffer. + new Uint16Array(new Uint8Array(mnemonicIndicesBytes).buffer), + ); + const mnemonic = mnemonicIndices.map((i) => wordlist[i]).join(' '); + return Array.from(new TextEncoder().encode(mnemonic)); +} diff --git a/yarn.lock b/yarn.lock index a1ba37be..13fdd6d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2126,6 +2126,7 @@ __metadata: "@metamask/eth-sig-util": "npm:^8.2.0" "@metamask/keyring-api": "npm:^22.0.0" "@metamask/keyring-utils": "npm:^3.2.0" + "@metamask/scure-bip39": "npm:^2.1.1" "@metamask/superstruct": "npm:^3.1.0" "@metamask/utils": "npm:^11.10.0" "@ts-bridge/cli": "npm:^0.6.3"