Skip to content

Commit 20deccb

Browse files
add extendedPairFromSeedWords and accountFromExtendedKey functions
1 parent f9142ce commit 20deccb

5 files changed

+68
-3
lines changed

src/constants.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export const PUBLIC_KEY_PREFIX = 'npub'
22
export const SECRET_KEY_PREFIX = 'nsec'
3-
export const DERIVATION_PATH = `m/44'/1237'/0'/0/0`
3+
export const DERIVATION_PATH = `m/44'/1237'`

src/index.ts

+35-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function privateKeyFromSeedWords(
1717
}: { mnemonic: string, passphrase?: string }
1818
): { privateKey: string } {
1919
const root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))
20-
const { privateKey } = root.derive(DERIVATION_PATH)
20+
const { privateKey } = root.derive(`${DERIVATION_PATH}/0'/0/0`)
2121
if (!privateKey) {
2222
throw new Error('could not derive private key')
2323
}
@@ -55,6 +55,40 @@ export function getBech32PublicKey({ publicKey }: { publicKey: string }): { bech
5555
}
5656
}
5757

58+
export function extendedPairFromSeedWords(mnemonic: string, passphrase?: string, extendedAccountIndex = 0): {
59+
privateExtendedKey: string,
60+
publicExtendedKey: string
61+
} {
62+
let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase))
63+
let seed = root.derive(`${DERIVATION_PATH}/${extendedAccountIndex}'`)
64+
let privateExtendedKey = seed.privateExtendedKey
65+
let publicExtendedKey = seed.publicExtendedKey
66+
if (!privateExtendedKey) throw new Error('could not derive private extended key')
67+
return { privateExtendedKey, publicExtendedKey }
68+
}
69+
70+
export function accountFromExtendedKey(base58key: string, accountIndex = 0): {
71+
privateKey?: { hex: string, bech32: string },
72+
publicKey: { hex: string, bech32: string }
73+
} {
74+
let extendedKey = HDKey.fromExtendedKey(base58key)
75+
let version = base58key.slice(0, 4)
76+
let child = extendedKey.deriveChild(0).deriveChild(accountIndex)
77+
let publicKeyHex = bytesToHex(child.publicKey!.slice(1))
78+
if (!publicKeyHex) throw new Error('could not derive public key')
79+
let publicKeyBech32 = hexToBech32(publicKeyHex, 'npub')
80+
if (version === 'xprv') {
81+
let privateKeyHex = bytesToHex(child.privateKey!)
82+
if (!privateKeyHex) throw new Error('could not derive private key')
83+
let privateKeyBech32 = hexToBech32(privateKeyHex, 'nsec')
84+
return {
85+
privateKey: { hex: privateKeyHex, bech32: privateKeyBech32 },
86+
publicKey: { hex: publicKeyHex, bech32: publicKeyBech32 }
87+
}
88+
}
89+
return { publicKey: { hex: publicKeyHex, bech32: publicKeyBech32 } }
90+
}
91+
5892
export function generateSeedWords(): { mnemonic: string } {
5993
return {
6094
mnemonic: generateMnemonic(wordlist)

tests/accountFromExtendedKey.test.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { accountFromExtendedKey } from '../src/index'
3+
4+
describe('accountFromExtendedKey', () => {
5+
it('should get account from extended private key', () => {
6+
const { privateKey, publicKey } = accountFromExtendedKey('xprv9z78fizET65qsCaRr1MSutTSGk1fcKfSt1sBqmuWShtkjRJJ4WCKcSnha6EmgNzFSsyom3MWtydHyPtJtSLZQUtictVQtM2vkPcguh6TQCH')
7+
8+
expect(privateKey?.hex).toBe('5f29af3b9676180290e77a4efad265c4c2ff28a5302461f73597fda26bb25731')
9+
expect(privateKey?.bech32).toBe('nsec1tu567wukwcvq9y880f8045n9cnp07299xqjxrae4jl76y6aj2ucs2mkupq')
10+
expect(publicKey.hex).toBe('e8bcf3823669444d0b49ad45d65088635d9fd8500a75b5f20b59abefa56a144f')
11+
expect(publicKey.bech32).toBe('npub1az708q3kd9zy6z6f44zav5ygvdwelkzspf6mtusttx47lft2z38sghk0w7')
12+
})
13+
14+
it('should get account from extended public key', () => {
15+
const { privateKey, publicKey } = accountFromExtendedKey('xpub6D6V5EX8HTe95getx2tTH2QApmrA1nPJFEnneAK813RjcDdSc3WaAF7BRNpTF7o7zXjVm3DD3VMX66jhQ7wLaZ9sS6NzyfiwfzqDZbxvpDN')
16+
17+
expect(publicKey.hex).toBe('e8bcf3823669444d0b49ad45d65088635d9fd8500a75b5f20b59abefa56a144f')
18+
expect(publicKey.bech32).toBe('npub1az708q3kd9zy6z6f44zav5ygvdwelkzspf6mtusttx47lft2z38sghk0w7')
19+
})
20+
})
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { extendedPairFromSeedWords } from '../src/index'
3+
4+
describe('extendedPairFromSeedWords', () => {
5+
it('should get extended keys pair from mnemonic', () => {
6+
const { privateExtendedKey, publicExtendedKey } = extendedPairFromSeedWords('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about')
7+
8+
expect(privateExtendedKey).toBe('xprv9z78fizET65qsCaRr1MSutTSGk1fcKfSt1sBqmuWShtkjRJJ4WCKcSnha6EmgNzFSsyom3MWtydHyPtJtSLZQUtictVQtM2vkPcguh6TQCH')
9+
expect(publicExtendedKey).toBe('xpub6D6V5EX8HTe95getx2tTH2QApmrA1nPJFEnneAK813RjcDdSc3WaAF7BRNpTF7o7zXjVm3DD3VMX66jhQ7wLaZ9sS6NzyfiwfzqDZbxvpDN')
10+
})
11+
})

tests/privateKeyFromSeedWords.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { DERIVATION_PATH } from '../src/constants'
44

55
describe('privateKeyFromSeedWords', () => {
66
it('should use the NIP-06 derivation path', () => {
7-
expect(DERIVATION_PATH).toBe(`m/44'/1237'/0'/0/0`)
7+
expect(`${DERIVATION_PATH}/0'/0/0`).toBe(`m/44'/1237'/0'/0/0`)
88
})
99

1010
it('should get a 64 bytes hex private key from mnemonic', () => {

0 commit comments

Comments
 (0)