Skip to content

Commit

Permalink
Support passphrase with wallets
Browse files Browse the repository at this point in the history
  • Loading branch information
wizz-wallet-dev committed Oct 14, 2024
1 parent 2506c56 commit bb54bf5
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 33 deletions.
12 changes: 10 additions & 2 deletions lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,11 @@ program.command('wallet-decode')
.description('Decode secret mnemonic phrase to display derive address and key at provided path')
.argument('<phrase>', 'string')
.option('-p, --path <string>', 'Derivation path to use', `m/44'/0'/0'/0/0`)
.option('--passphrase <string>', '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}`);
Expand All @@ -251,10 +253,16 @@ program.command('wallet-init')
.description('Initializes a new wallet at wallet.json')
.option('--phrase <string>', 'Provide a wallet phrase')
.option('--path <string>', 'Provide a path base', `m/86'/0'/0'`)
.option('--passphrase <string>', 'Provide a passphrase for the wallet')
.option('--n <number>', '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}`);
Expand Down
20 changes: 17 additions & 3 deletions lib/commands/wallet-init-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CommandResultInterface> {
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,
Expand Down
6 changes: 3 additions & 3 deletions lib/commands/wallet-phrase-decode-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CommandResultInterface> {
const wallet = await decodeMnemonicPhrase(this.phrase, this.path);
const wallet = await decodeMnemonicPhrase(this.phrase, this.path, this.passphrase);
return {
success: true,
data: wallet
}
}
}
}
10 changes: 5 additions & 5 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -248,9 +248,9 @@ export class Atomicals implements APIInterface {
}
}

static async walletPhraseDecode(phrase: string, path: string): Promise<any> {
static async walletPhraseDecode(phrase: string, path: string, passphrase?: string): Promise<any> {
try {
const command: CommandInterface = new WalletPhraseDecodeCommand(phrase, path);
const command: CommandInterface = new WalletPhraseDecodeCommand(phrase, path, passphrase);
return command.run();
} catch (error: any) {
return {
Expand All @@ -261,9 +261,9 @@ export class Atomicals implements APIInterface {
}
}

static async walletInit(phrase: string | undefined, path: string, n?: number): Promise<any> {
static async walletInit(phrase: string | undefined, path: string, passphrase?: string, n?: number): Promise<any> {
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 {
Expand Down
8 changes: 6 additions & 2 deletions lib/utils/address-keypair-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ export interface ExtendTaprootAddressScriptKeyPairInfo {
path: string;
}

export const getExtendTaprootAddressKeypairPath = async (phrase: string, path: string): Promise<ExtendTaprootAddressScriptKeyPairInfo> => {
const seed = await bip39.mnemonicToSeed(phrase);
export const getExtendTaprootAddressKeypairPath = async (
phrase: string,
path: string,
passphrase?: string,
): Promise<ExtendTaprootAddressScriptKeyPairInfo> => {
const seed = await bip39.mnemonicToSeed(phrase, passphrase);
const rootKey = bip32.fromSeed(seed);
const childNode = rootKey.derivePath(path);
const childNodeXOnlyPubkey = childNode.publicKey.slice(1, 33);
Expand Down
40 changes: 26 additions & 14 deletions lib/utils/create-key-pair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ export interface KeyPair {
privateKey?: string
}

export const createKeyPair = async (phrase: string = '', path = `m/44'/0'/0'/0/0`) : Promise<KeyPair> => {
export const createKeyPair = async (
phrase: string = '',
path = `m/44'/0'/0'/0/0`,
passphrase: string = ''
) : Promise<KeyPair> => {
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 });
Expand Down Expand Up @@ -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) {
Expand All @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions lib/utils/decode-mnemonic-phrase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand Down Expand Up @@ -46,4 +46,4 @@ export const decodeMnemonicPhrase = async (phrase: string, path: string) => {
privateKey: childNode.privateKey?.toString('hex'),
}

}
}
2 changes: 1 addition & 1 deletion lib/utils/validate-wallet-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const validateWalletStorage = async (): Promise<IValidatedWalletInfo> =>
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`;

Expand Down

0 comments on commit bb54bf5

Please sign in to comment.