-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
280 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { getBip32, IChildKey, IHDKey } from '../crypto/bip32/hdwallet-token'; | ||
import { getBip39 } from '../crypto/bip39/bip39-token'; | ||
import { WalletOptions } from '../types/wallet'; | ||
import { p2wpkh, Transaction, NETWORK } from '@scure/btc-signer'; | ||
import { hex } from '@scure/base'; | ||
import { HDKey } from '@scure/bip32'; | ||
export type BTCWalletOptions = WalletOptions & { network: typeof NETWORK }; | ||
|
||
export class BtcWallet { | ||
constructor(private hdKey: IHDKey, private options: BTCWalletOptions) {} | ||
|
||
static generateWalletFromPrivKey(privateKey: string, options: BTCWalletOptions) { | ||
const hdKey = HDKey.fromJSON({ xpriv: privateKey }); | ||
return new BtcWallet(hdKey, options); | ||
} | ||
|
||
static generateWalletFromMnemonic(mnemonic: string, options: BTCWalletOptions) { | ||
const bip39 = getBip39(); | ||
const bip32 = getBip32(); | ||
bip39.mnemonicToEntropy(mnemonic); | ||
const seed = bip39.mnemonicToSeedSync(mnemonic); | ||
const hdKey = bip32.fromSeed(seed); | ||
return new BtcWallet(hdKey, options); | ||
} | ||
|
||
getAccountsWithPrivKey(): Array<{ algo: string; address: string; pubkey: Uint8Array; childKey: IChildKey }> { | ||
const accountsWithPubKey = []; | ||
for (let path of this.options.paths) { | ||
const childKey = this.hdKey.derive(path); | ||
|
||
if (!childKey.publicKey) throw new Error(`Could not generate public key for path ${path}`); | ||
const addrData = p2wpkh(childKey.publicKey, this.options.network); | ||
if (!addrData.address) throw new Error(`Could not generate address for path ${path}`); | ||
accountsWithPubKey.push({ | ||
algo: 'secp256k1', | ||
address: addrData.address, | ||
p2ret: addrData, | ||
pubkey: childKey.publicKey, | ||
childKey: childKey, | ||
}); | ||
} | ||
|
||
return accountsWithPubKey; | ||
} | ||
|
||
getAccounts() { | ||
const accounts = this.getAccountsWithPrivKey(); | ||
return accounts.map((account) => { | ||
return { | ||
algo: 'secp256k1', | ||
address: account.address, | ||
pubkey: account.pubkey, | ||
}; | ||
}); | ||
} | ||
|
||
signPsbt(address: string, psbt: string) { | ||
const psbtBytes = hex.decode(psbt); | ||
const tx = Transaction.fromPSBT(psbtBytes); | ||
const accounts = this.getAccountsWithPrivKey(); | ||
const account = accounts.find((account) => account.address === address); | ||
if (!account) throw new Error(`No account found for ${address}`); | ||
if (!account.childKey.privateKey) throw new Error('Private key not found'); | ||
|
||
const signedTx = tx.sign(account.childKey.privateKey); | ||
return signedTx; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
export default function getHDPath(coinType = '118', index = '0', account = '0', chain = '0') { | ||
return `m/44'/${coinType}'/${account}'/${chain}/${index}`; | ||
} | ||
|
||
export function getFullHDPath(purpose = '44', coinType = '0', index = '0', account = '0', chain = '0') { | ||
return `m/${purpose}'/${coinType}'/${account}'/${chain}/${index}`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { ripemd160 } from '@noble/hashes/ripemd160'; | ||
import { sha256 } from '@noble/hashes/sha256'; | ||
import Container from 'typedi'; | ||
import { Bip32 } from '../src/crypto/bip32/hd-wallet'; | ||
import { bip32Token } from '../src/crypto/bip32/hdwallet-token'; | ||
import { Bip39 } from '../src/crypto/bip39/bip39'; | ||
import { setBip39 } from '../src/crypto/bip39/bip39-token'; | ||
import { ripemd160Token, sha256Token } from '../src/crypto/hashes/hashes'; | ||
import { BtcWallet } from '../src/key/btc-wallet'; | ||
import expect from 'expect.js'; | ||
import { mnemonic } from './mockdata'; | ||
import { NETWORK, TEST_NETWORK } from '@scure/btc-signer'; | ||
|
||
beforeEach(() => { | ||
setBip39(Bip39); | ||
Container.set(bip32Token, Bip32); | ||
Container.set(sha256Token, sha256); | ||
Container.set(ripemd160Token, ripemd160); | ||
}); | ||
|
||
describe('generate btc wallet', () => { | ||
it('generates correct btc wallet', () => { | ||
const wallet = BtcWallet.generateWalletFromMnemonic(mnemonic, { | ||
addressPrefix: 'bc1q', | ||
paths: ["m/84'/0'/0'/0/0"], | ||
network: NETWORK, | ||
}); | ||
const accounts = wallet.getAccountsWithPrivKey(); | ||
|
||
const expectedAccount = 'bc1qd5xpfp9zp8q696pu3sz7ej2wrk2wn634dlnhfa'; | ||
if (accounts[0]) { | ||
expect(accounts[0].address).to.be(expectedAccount); | ||
} | ||
}); | ||
it('generates correct signet wallet', () => { | ||
const wallet = BtcWallet.generateWalletFromMnemonic(mnemonic, { | ||
addressPrefix: 'bc1q', | ||
paths: ["m/84'/0'/0'/0/0"], | ||
network: TEST_NETWORK, | ||
}); | ||
const accounts = wallet.getAccountsWithPrivKey(); | ||
|
||
const expectedAccount = 'tb1qd5xpfp9zp8q696pu3sz7ej2wrk2wn6348egyjw'; | ||
if (accounts[0]) { | ||
expect(accounts[0].address).to.be(expectedAccount); | ||
} | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.