From d254c94ff83c5898c0557db45a4732449cd8029b Mon Sep 17 00:00:00 2001 From: Denis Davidyuk Date: Thu, 16 Jan 2025 17:27:47 +0100 Subject: [PATCH] feat(account): init AccountMnemonicFactory by seed --- src/account/MnemonicFactory.ts | 25 ++++++++++++++++--------- test/unit/mnemonic.ts | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/account/MnemonicFactory.ts b/src/account/MnemonicFactory.ts index e3cecefd74..f132dcb8ee 100644 --- a/src/account/MnemonicFactory.ts +++ b/src/account/MnemonicFactory.ts @@ -4,6 +4,7 @@ import AccountBaseFactory from './BaseFactory.js'; import AccountMemory from './Memory.js'; import { encode, Encoding, Encoded, decode } from '../utils/encoder.js'; import { concatBuffers } from '../utils/other.js'; +import { ArgumentError } from '../utils/errors.js'; export const ED25519_CURVE = Buffer.from('ed25519 seed'); const HARDENED_OFFSET = 0x80000000; @@ -41,14 +42,14 @@ interface Wallet { * A factory class that generates instances of AccountMemory based on provided mnemonic phrase. */ export default class AccountMnemonicFactory extends AccountBaseFactory { - #mnemonicOrWallet: string | Wallet; + #mnemonicOrWalletOrSeed: string | Wallet | Uint8Array; /** - * @param mnemonicOrWallet - BIP39-compatible mnemonic phrase or a wallet derived from mnemonic + * @param mnemonicOrWallet - BIP39-compatible mnemonic phrase or a wallet/seed derived from mnemonic */ - constructor(mnemonicOrWallet: string | Wallet) { + constructor(mnemonicOrWalletOrSeed: string | Wallet | Uint8Array) { super(); - this.#mnemonicOrWallet = mnemonicOrWallet; + this.#mnemonicOrWalletOrSeed = mnemonicOrWalletOrSeed; } #getWallet(sync: true): Wallet; @@ -57,17 +58,23 @@ export default class AccountMnemonicFactory extends AccountBaseFactory { const setWalletBySeed = (seed: Uint8Array): Wallet => { const masterKey = deriveKey(seed, ED25519_CURVE); const walletKey = derivePathFromKey(masterKey, [44, 457]); - this.#mnemonicOrWallet = { + this.#mnemonicOrWalletOrSeed = { secretKey: encode(walletKey.secretKey, Encoding.Bytearray), chainCode: encode(walletKey.chainCode, Encoding.Bytearray), }; - return this.#mnemonicOrWallet; + return this.#mnemonicOrWalletOrSeed; }; - if (typeof this.#mnemonicOrWallet === 'object') return this.#mnemonicOrWallet; + if (ArrayBuffer.isView(this.#mnemonicOrWalletOrSeed)) { + if (this.#mnemonicOrWalletOrSeed.length !== 64) { + throw new ArgumentError('seed length', 64, this.#mnemonicOrWalletOrSeed.length); + } + return setWalletBySeed(this.#mnemonicOrWalletOrSeed); + } + if (typeof this.#mnemonicOrWalletOrSeed === 'object') return this.#mnemonicOrWalletOrSeed; return sync - ? setWalletBySeed(mnemonicToSeedSync(this.#mnemonicOrWallet)) - : mnemonicToSeed(this.#mnemonicOrWallet).then(setWalletBySeed); + ? setWalletBySeed(mnemonicToSeedSync(this.#mnemonicOrWalletOrSeed)) + : mnemonicToSeed(this.#mnemonicOrWalletOrSeed).then(setWalletBySeed); } /** diff --git a/test/unit/mnemonic.ts b/test/unit/mnemonic.ts index d4945373e6..c095a9e410 100644 --- a/test/unit/mnemonic.ts +++ b/test/unit/mnemonic.ts @@ -4,6 +4,13 @@ import { Node, Encoded, AccountMnemonicFactory, MemoryAccount } from '../../src' const mnemonic = 'eye quarter chapter suit cruel scrub verify stuff volume control learn dust'; +const seed = new Uint8Array([ + 26, 43, 123, 108, 82, 100, 153, 240, 181, 30, 143, 186, 96, 84, 133, 187, 20, 179, 152, 54, 114, + 118, 104, 243, 147, 193, 110, 110, 179, 195, 207, 131, 230, 174, 67, 145, 148, 16, 229, 126, 115, + 211, 147, 77, 150, 171, 211, 227, 217, 151, 80, 229, 196, 192, 209, 44, 71, 40, 106, 234, 223, 20, + 163, 59, +]); + const wallet = { secretKey: 'ba_I1lro/ANfEKuBUal0Glo++D5abkcFLIIihTDLcC8l3My1PuP', chainCode: 'ba_XZL45EKIQiLe9v/pkY37Bn3GiqLXZ5v2hIya6llA0QOlYf6i', @@ -15,6 +22,11 @@ describe('Account mnemonic factory', () => { expect(await factory.getWallet()).to.be.eql(wallet); }); + it('derives wallet by seed', async () => { + const factory = new AccountMnemonicFactory(seed); + expect(await factory.getWallet()).to.be.eql(wallet); + }); + it('derives wallet by wallet', async () => { const factory = new AccountMnemonicFactory(wallet); expect(await factory.getWallet()).to.be.eql(wallet); @@ -32,6 +44,13 @@ describe('Account mnemonic factory', () => { expect(account.address).to.be.equal('ak_2HteeujaJzutKeFZiAmYTzcagSoRErSXpBFV179xYgqT4teakv'); }); + it('initializes an account by seed', async () => { + const factory = new AccountMnemonicFactory(seed); + const account = await factory.initialize(42); + expect(account).to.be.instanceOf(MemoryAccount); + expect(account.address).to.be.equal('ak_2HteeujaJzutKeFZiAmYTzcagSoRErSXpBFV179xYgqT4teakv'); + }); + it('initializes an account by wallet', async () => { const factory = new AccountMnemonicFactory(wallet); const account = await factory.initialize(42);