diff --git a/lib/cli.ts b/lib/cli.ts index d5d15b2..d424f62 100644 --- a/lib/cli.ts +++ b/lib/cli.ts @@ -234,9 +234,11 @@ program.command('wallet-decode') .description('Decode secret mnemonic phrase to display derive address and key at provided path') .argument('', 'string') .option('-p, --path ', 'Derivation path to use', `m/44'/0'/0'/0/0`) + .option('--passphrase ', 'Passphrase for the wallet') .action(async (phrase, options) => { let path = options.path; - const result = await Atomicals.walletPhraseDecode(phrase, path); + let passphrase = options.passphrase; + const result = await Atomicals.walletPhraseDecode(phrase, path, passphrase); console.log('Provided mnemonic phrase:'); console.log(`phrase: ${result.data.phrase}`); console.log(`Requested Derivation Path: ${path}`); @@ -251,10 +253,16 @@ program.command('wallet-init') .description('Initializes a new wallet at wallet.json') .option('--phrase ', 'Provide a wallet phrase') .option('--path ', 'Provide a path base', `m/86'/0'/0'`) + .option('--passphrase ', 'Provide a passphrase for the wallet') .option('--n ', 'Provider number of alias') .action(async (options) => { try { - const result = await Atomicals.walletInit(options.phrase, options.path, options.n ? parseInt(options.n, 10) : undefined); + const result = await Atomicals.walletInit( + options.phrase, + options.path, + options.passphrase, + options.n ? parseInt(options.n, 10) : undefined + ); console.log('Wallet created at wallet.json'); console.log(`phrase: ${result.data.phrase}`); console.log(`Primary address (P2TR): ${result.data.primary.address}`); diff --git a/lib/commands/wallet-init-command.ts b/lib/commands/wallet-init-command.ts index 40195d7..447aba3 100644 --- a/lib/commands/wallet-init-command.ts +++ b/lib/commands/wallet-init-command.ts @@ -8,21 +8,35 @@ import * as fs from 'fs'; const walletPath = walletPathResolver(); export class WalletInitCommand implements CommandInterface { - constructor(private phrase: string | undefined, private path: string, private n?: number) { - + constructor( + private phrase: string | undefined, + private path: string, + private passphrase?: string, + private n?: number + ) { } + async run(): Promise { if (await this.walletExists()) { throw "wallet.json exists, please remove it first to initialize another wallet. You may also use 'wallet-create' command to generate a new wallet." } - const { wallet, imported } = await createPrimaryAndFundingImportedKeyPairs(this.phrase, this.path, this.n); + const { + wallet, + imported + } = await createPrimaryAndFundingImportedKeyPairs( + this.phrase, + this.path, + this.passphrase, + this.n + ); const walletDir = `wallets/`; if (!fs.existsSync(walletDir)) { fs.mkdirSync(walletDir); } const created = { phrase: wallet.phrase, + passphrase: wallet.passphrase, primary: { address: wallet.primary.address, path: wallet.primary.path, diff --git a/lib/commands/wallet-phrase-decode-command.ts b/lib/commands/wallet-phrase-decode-command.ts index 298f4f4..0bcfdc2 100644 --- a/lib/commands/wallet-phrase-decode-command.ts +++ b/lib/commands/wallet-phrase-decode-command.ts @@ -3,13 +3,13 @@ import { CommandInterface } from "./command.interface"; import { decodeMnemonicPhrase } from "../utils/decode-mnemonic-phrase"; export class WalletPhraseDecodeCommand implements CommandInterface { - constructor(private phrase, private path) { + constructor(private phrase: string, private path: string, private passphrase?: string) { } async run(): Promise { - const wallet = await decodeMnemonicPhrase(this.phrase, this.path); + const wallet = await decodeMnemonicPhrase(this.phrase, this.path, this.passphrase); return { success: true, data: wallet } } -} \ No newline at end of file +} diff --git a/lib/index.ts b/lib/index.ts index 0af8dae..f5cfce6 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -2,7 +2,7 @@ import { APIInterface, BaseRequestOptions } from "./interfaces/api.interface"; const bitcoin = require('bitcoinjs-lib'); import * as ecc from 'tiny-secp256k1'; bitcoin.initEccLib(ecc); -import * as cbor from 'borc'; + export { ElectrumApiMock } from "./api/electrum-api-mock"; import { ConfigurationInterface } from "./interfaces/configuration.interface"; import { ElectrumApiInterface } from "./api/electrum-api.interface"; @@ -248,9 +248,9 @@ export class Atomicals implements APIInterface { } } - static async walletPhraseDecode(phrase: string, path: string): Promise { + static async walletPhraseDecode(phrase: string, path: string, passphrase?: string): Promise { try { - const command: CommandInterface = new WalletPhraseDecodeCommand(phrase, path); + const command: CommandInterface = new WalletPhraseDecodeCommand(phrase, path, passphrase); return command.run(); } catch (error: any) { return { @@ -261,9 +261,9 @@ export class Atomicals implements APIInterface { } } - static async walletInit(phrase: string | undefined, path: string, n?: number): Promise { + static async walletInit(phrase: string | undefined, path: string, passphrase?: string, n?: number): Promise { try { - const command: CommandInterface = new WalletInitCommand(phrase, path, n); + const command: CommandInterface = new WalletInitCommand(phrase, path, passphrase, n); return command.run(); } catch (error: any) { return { diff --git a/lib/utils/address-keypair-path.ts b/lib/utils/address-keypair-path.ts index ac52d76..eebf999 100644 --- a/lib/utils/address-keypair-path.ts +++ b/lib/utils/address-keypair-path.ts @@ -16,8 +16,12 @@ export interface ExtendTaprootAddressScriptKeyPairInfo { path: string; } -export const getExtendTaprootAddressKeypairPath = async (phrase: string, path: string): Promise => { - const seed = await bip39.mnemonicToSeed(phrase); +export const getExtendTaprootAddressKeypairPath = async ( + phrase: string, + path: string, + passphrase?: string, +): Promise => { + const seed = await bip39.mnemonicToSeed(phrase, passphrase); const rootKey = bip32.fromSeed(seed); const childNode = rootKey.derivePath(path); const childNodeXOnlyPubkey = childNode.publicKey.slice(1, 33); diff --git a/lib/utils/create-key-pair.ts b/lib/utils/create-key-pair.ts index f6e6b01..c99d4a2 100644 --- a/lib/utils/create-key-pair.ts +++ b/lib/utils/create-key-pair.ts @@ -24,12 +24,16 @@ export interface KeyPair { privateKey?: string } -export const createKeyPair = async (phrase: string = '', path = `m/44'/0'/0'/0/0`) : Promise => { +export const createKeyPair = async ( + phrase: string = '', + path = `m/44'/0'/0'/0/0`, + passphrase: string = '' +) : Promise => { if (!phrase || phrase === '') { - const phraseResult = await createMnemonicPhrase(); + const phraseResult = createMnemonicPhrase(); phrase = phraseResult.phrase; } - const seed = await bip39.mnemonicToSeed(phrase); + const seed = await bip39.mnemonicToSeed(phrase, passphrase); const rootKey = bip32.fromSeed(seed); const childNodePrimary = rootKey.derivePath(path); // const p2pkh = bitcoin.payments.p2pkh({ pubkey: childNodePrimary.publicKey }); @@ -74,11 +78,14 @@ export interface WalletRequestDefinition { path?: string | undefined } -export const createPrimaryAndFundingImportedKeyPairs = async (phrase?: string | undefined, path?: string | undefined, n?: number) => { - let phraseResult: any = phrase; - if (!phraseResult) { - phraseResult = await createMnemonicPhrase(); - phraseResult = phraseResult.phrase; +export const createPrimaryAndFundingImportedKeyPairs = async ( + phrase?: string | undefined, + path?: string | undefined, + passphrase?: string | undefined, + n?: number +) => { + if (!phrase) { + phrase = createMnemonicPhrase().phrase; } let pathUsed = `m/44'/0'/0'`; if (path) { @@ -88,23 +95,28 @@ export const createPrimaryAndFundingImportedKeyPairs = async (phrase?: string | if (n) { for (let i = 2; i < n + 2; i++) { - imported[i+''] = await createKeyPair(phraseResult, `${pathUsed}/0/` + i) + imported[i+''] = await createKeyPair(phrase, `${pathUsed}/0/` + i, passphrase) } } return { wallet: { - phrase: phraseResult, - primary: await createKeyPair(phraseResult, `${pathUsed}/0/0`), - funding: await createKeyPair(phraseResult, `${pathUsed}/1/0`) + phrase, + passphrase, + primary: await createKeyPair(phrase, `${pathUsed}/0/0`, passphrase), + funding: await createKeyPair(phrase, `${pathUsed}/1/0`, passphrase) }, imported } } -export const createNKeyPairs = async (phrase, n = 1) => { +export const createNKeyPairs = async ( + phrase: string | undefined, + passphrase: string | undefined, + n = 1 +) => { const keypairs: any = []; for (let i = 0; i < n; i++) { - keypairs.push(await createKeyPair(phrase, `m/44'/0'/0'/0/${i}`)); + keypairs.push(await createKeyPair(phrase, `m/44'/0'/0'/0/${i}`, passphrase)); } return { phrase, diff --git a/lib/utils/decode-mnemonic-phrase.ts b/lib/utils/decode-mnemonic-phrase.ts index 80c8d78..1c96d97 100644 --- a/lib/utils/decode-mnemonic-phrase.ts +++ b/lib/utils/decode-mnemonic-phrase.ts @@ -13,11 +13,11 @@ const toXOnly = (publicKey) => { } const bip39 = require('bip39'); -export const decodeMnemonicPhrase = async (phrase: string, path: string) => { +export const decodeMnemonicPhrase = async (phrase: string, path: string, passphrase?: string) => { if (!bip39.validateMnemonic(phrase)) { throw new Error("Invalid mnemonic phrase provided!"); } - const seed = await bip39.mnemonicToSeed(phrase); + const seed = await bip39.mnemonicToSeed(phrase, passphrase); const rootKey = bip32.fromSeed(seed); const childNode = rootKey.derivePath(path); // const { address } = bitcoin.payments.p2pkh({ pubkey: childNode.publicKey }); @@ -46,4 +46,4 @@ export const decodeMnemonicPhrase = async (phrase: string, path: string) => { privateKey: childNode.privateKey?.toString('hex'), } -} \ No newline at end of file +} diff --git a/lib/utils/validate-wallet-storage.ts b/lib/utils/validate-wallet-storage.ts index 5afe789..c6157dd 100644 --- a/lib/utils/validate-wallet-storage.ts +++ b/lib/utils/validate-wallet-storage.ts @@ -83,7 +83,7 @@ export const validateWalletStorage = async (): Promise => throw new Error(`Funding address not set`); } - const seed = await bip39.mnemonicToSeed(wallet.phrase); + const seed = await bip39.mnemonicToSeed(wallet.phrase, wallet.passphrase); const rootKey = bip32.fromSeed(seed); const derivePathPrimary = wallet.primary.path; //`m/44'/0'/0'/0/0`;