@samouraiwallet/bip47
Advanced tools
Comparing version 0.8.0 to 1.0.0
# Changelog | ||
## v1.0.0 | ||
### Breaking major release | ||
- full browser compatibility (no use of Node.js internals) | ||
- switched to @samouraiwallet/bip32 | ||
- more tests | ||
- more straightforward API | ||
- added methods to create notification payload and extract payment code from payload | ||
- added more comprehensive usage examples | ||
## v0.8.0 | ||
@@ -4,0 +13,0 @@ ### Breaking |
@@ -1,3 +0,4 @@ | ||
export { BIP47Factory } from './payment-code.js'; | ||
export * as utils from './utils.js'; | ||
export { BIP47Factory as default, BIP47Factory } from './payment-code.js'; | ||
export type { TinySecp256k1Interface, Network, AddressType } from './types.js'; | ||
export type { PaymentCodePublic, PaymentCodePrivate } from './payment-code.js'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -1,3 +0,2 @@ | ||
export { BIP47Factory } from './payment-code.js'; | ||
export * as utils from './utils.js'; | ||
export { BIP47Factory as default, BIP47Factory } from './payment-code.js'; | ||
//# sourceMappingURL=index.js.map |
@@ -1,62 +0,43 @@ | ||
import { BIP32Interface } from 'bip32'; | ||
import { Network, TinySecp256k1Interface, AddressType } from './types'; | ||
import { BIP32API, BIP32Interface } from '@samouraiwallet/bip32'; | ||
import type { Network, TinySecp256k1Interface, AddressType } from './types.js'; | ||
export declare class PaymentCodePublic { | ||
protected readonly ecc: TinySecp256k1Interface; | ||
protected readonly bip32: BIP32API; | ||
protected readonly buf: Uint8Array; | ||
protected readonly network: Network; | ||
root: BIP32Interface; | ||
hasPrivKeys: boolean; | ||
segwit: boolean; | ||
constructor(ecc: TinySecp256k1Interface, bip32: BIP32API, buf: Uint8Array, network?: Network); | ||
get features(): Uint8Array; | ||
get pubKey(): Uint8Array; | ||
get chainCode(): Uint8Array; | ||
get paymentCode(): Uint8Array; | ||
clone(): PaymentCodePublic; | ||
toBase58(): string; | ||
derive(index: number): BIP32Interface; | ||
getNotificationPublicKey(): Uint8Array; | ||
getNotificationAddress(): string; | ||
protected derivePublicKeyFromSharedSecret(B: Uint8Array, S: Uint8Array | null): Uint8Array; | ||
derivePaymentPublicKey(paymentCode: PaymentCodePrivate, idx: number): Uint8Array; | ||
protected getAddressFromPubkey(pubKey: Uint8Array, type: AddressType): string; | ||
getPaymentAddress(paymentCode: PaymentCodePrivate, idx: number, type?: AddressType): string; | ||
getBlindedPaymentCode(destinationPaymentCode: PaymentCodePublic, outpoint: Uint8Array, privateKey: Uint8Array): string; | ||
} | ||
export declare class PaymentCodePrivate extends PaymentCodePublic { | ||
constructor(root: BIP32Interface, ecc: TinySecp256k1Interface, bip32: BIP32API, buf: Uint8Array, network?: Network); | ||
toPaymentCodePublic(): PaymentCodePublic; | ||
clone(): PaymentCodePrivate; | ||
deriveHardened(index: number): BIP32Interface; | ||
derivePaymentPublicKey(paymentCode: PaymentCodePublic, idx: number): Uint8Array; | ||
getPaymentAddress(paymentCode: PaymentCodePublic, idx: number, type?: AddressType): string; | ||
derivePaymentPrivateKey(paymentCodePublic: PaymentCodePublic, idx: number): Uint8Array; | ||
getNotificationPrivateKey(): Uint8Array; | ||
getPaymentCodeFromNotificationTransactionData(scriptPubKey: Uint8Array, outpoint: Uint8Array, pubKey: Uint8Array): PaymentCodePublic; | ||
} | ||
export declare const BIP47Factory: (ecc: TinySecp256k1Interface) => { | ||
fromSeed: (bSeed: Uint8Array, id: number | string, network?: Network) => { | ||
version: Uint8Array; | ||
buf: Uint8Array; | ||
network: Network; | ||
root: BIP32Interface; | ||
readonly features: Uint8Array; | ||
readonly pubKey: Uint8Array; | ||
readonly chainCode: Uint8Array; | ||
readonly paymentCode: Uint8Array; | ||
toBase58(): string; | ||
_hasPrivKeys(): boolean; | ||
derive(index: number): BIP32Interface; | ||
deriveHardened(index: number): BIP32Interface; | ||
getNotificationAddress(): string; | ||
getNotificationPrivateKey(): Buffer; | ||
derivePaymentPrivateKey(A: Buffer, idx: number): Buffer; | ||
derivePaymentPublicKey(a: Buffer, idx: number): Buffer; | ||
getPaymentAddress(a: Buffer, idx: number, type?: AddressType): string; | ||
}; | ||
fromBase58: (inString: string, network?: Network) => { | ||
version: Uint8Array; | ||
buf: Uint8Array; | ||
network: Network; | ||
root: BIP32Interface; | ||
readonly features: Uint8Array; | ||
readonly pubKey: Uint8Array; | ||
readonly chainCode: Uint8Array; | ||
readonly paymentCode: Uint8Array; | ||
toBase58(): string; | ||
_hasPrivKeys(): boolean; | ||
derive(index: number): BIP32Interface; | ||
deriveHardened(index: number): BIP32Interface; | ||
getNotificationAddress(): string; | ||
getNotificationPrivateKey(): Buffer; | ||
derivePaymentPrivateKey(A: Buffer, idx: number): Buffer; | ||
derivePaymentPublicKey(a: Buffer, idx: number): Buffer; | ||
getPaymentAddress(a: Buffer, idx: number, type?: AddressType): string; | ||
}; | ||
fromBuffer: (buf: Uint8Array, network?: Network) => { | ||
version: Uint8Array; | ||
buf: Uint8Array; | ||
network: Network; | ||
root: BIP32Interface; | ||
readonly features: Uint8Array; | ||
readonly pubKey: Uint8Array; | ||
readonly chainCode: Uint8Array; | ||
readonly paymentCode: Uint8Array; | ||
toBase58(): string; | ||
_hasPrivKeys(): boolean; | ||
derive(index: number): BIP32Interface; | ||
deriveHardened(index: number): BIP32Interface; | ||
getNotificationAddress(): string; | ||
getNotificationPrivateKey(): Buffer; | ||
derivePaymentPrivateKey(A: Buffer, idx: number): Buffer; | ||
derivePaymentPublicKey(a: Buffer, idx: number): Buffer; | ||
getPaymentAddress(a: Buffer, idx: number, type?: AddressType): string; | ||
}; | ||
fromSeed: (bSeed: Uint8Array, segwit?: boolean, network?: Network) => PaymentCodePrivate; | ||
fromBase58: (inString: string, network?: Network) => PaymentCodePublic; | ||
fromBuffer: (buf: Uint8Array, network?: Network) => PaymentCodePublic; | ||
}; | ||
//# sourceMappingURL=payment-code.d.ts.map |
@@ -1,155 +0,213 @@ | ||
import { BIP32Factory } from 'bip32'; | ||
import bs58check from 'bs58check'; | ||
import { BIP32Factory } from '@samouraiwallet/bip32'; | ||
import { bs58check, hmacSHA512 } from '@samouraiwallet/bip32/crypto'; | ||
import { sha256 } from '@noble/hashes/sha256'; | ||
import * as utils from './utils.js'; | ||
const { encode, decode } = bs58check; | ||
const PC_VERSION = 0x47; | ||
export const BIP47Factory = (ecc) => { | ||
const bip32 = BIP32Factory(ecc); | ||
class PaymentCode { | ||
constructor(buf, network = utils.networks.bitcoin) { | ||
if (buf.length !== 80) | ||
throw new TypeError('Invalid buffer length'); | ||
this.version = buf.slice(0, 1); | ||
if (this.version[0] !== 1) | ||
throw new TypeError('Only payment codes version 1 are supported'); | ||
this.buf = buf; | ||
this.network = network; | ||
this.root = bip32.fromPublicKey(Buffer.from(this.pubKey), Buffer.from(this.chainCode), this.network); | ||
} | ||
get features() { | ||
return this.buf.slice(1, 1); | ||
} | ||
get pubKey() { | ||
return this.buf.slice(2, 2 + 33); | ||
} | ||
get chainCode() { | ||
return this.buf.slice(35, 35 + 32); | ||
} | ||
get paymentCode() { | ||
return this.buf; | ||
} | ||
toBase58() { | ||
const version = Buffer.from([PC_VERSION]); | ||
const buf = Buffer.concat([version, this.buf]); | ||
return encode(buf); | ||
} | ||
_hasPrivKeys() { | ||
return this.root.privateKey != null; | ||
} | ||
derive(index) { | ||
return this.root.derive(index); | ||
} | ||
deriveHardened(index) { | ||
return this.root.deriveHardened(index); | ||
} | ||
getNotificationAddress() { | ||
const child = this.derive(0); | ||
return utils.getP2pkhAddress(child.publicKey, this.network); | ||
} | ||
getNotificationPrivateKey() { | ||
if (!this._hasPrivKeys()) | ||
throw new Error('This payment code does not have private keys'); | ||
const child = this.derive(0); | ||
return child.privateKey; | ||
} | ||
derivePaymentPrivateKey(A, idx) { | ||
if (!ecc.isPoint(A)) | ||
throw new TypeError('Argument is not a valid public key'); | ||
const b_node = this.derive(idx); | ||
if (!b_node.privateKey) | ||
throw new Error('Unable to derive node with private key'); | ||
const b = b_node.privateKey; | ||
const S = ecc.pointMultiply(A, b); | ||
if (!S) | ||
throw new Error('Unable to compute resulting point'); | ||
const Sx = S.slice(1, 33); | ||
const s = utils.sha256(Buffer.from(Sx)); | ||
if (!ecc.isPrivate(s)) | ||
throw new TypeError('Invalid shared secret'); | ||
const paymentPrivateKey = ecc.privateAdd(b, s); | ||
if (!paymentPrivateKey) | ||
throw new TypeError('Unable to compute payment private key'); | ||
return Buffer.from(paymentPrivateKey); | ||
} | ||
derivePaymentPublicKey(a, idx) { | ||
if (!ecc.isPrivate(a) && !ecc.isPoint(a)) | ||
throw new TypeError('Argument is neither a valid private key or public key'); | ||
let B = null; | ||
let S = null; | ||
if (ecc.isPrivate(a)) { | ||
B = this.derive(idx).publicKey; | ||
S = ecc.pointMultiply(B, a); | ||
const SAMOURAI_FEATURE_BYTE = 79; | ||
export class PaymentCodePublic { | ||
constructor(ecc, bip32, buf, network = utils.networks.bitcoin) { | ||
this.ecc = ecc; | ||
this.bip32 = bip32; | ||
this.hasPrivKeys = false; | ||
if (buf.length !== 80) | ||
throw new Error('Invalid buffer length'); | ||
if (buf[0] !== 1) | ||
throw new Error('Only payment codes version 1 are supported'); | ||
this.buf = buf; | ||
this.network = network; | ||
this.segwit = this.buf[SAMOURAI_FEATURE_BYTE] === 1; | ||
this.root = bip32.fromPublicKey(this.pubKey, this.chainCode, this.network); | ||
} | ||
get features() { | ||
return this.buf.subarray(1, 2); | ||
} | ||
get pubKey() { | ||
return this.buf.subarray(2, 2 + 33); | ||
} | ||
get chainCode() { | ||
return this.buf.subarray(35, 35 + 32); | ||
} | ||
get paymentCode() { | ||
return this.buf; | ||
} | ||
clone() { | ||
return new PaymentCodePublic(this.ecc, this.bip32, this.buf.slice(0), this.network); | ||
} | ||
toBase58() { | ||
const version = new Uint8Array([PC_VERSION]); | ||
const buf = new Uint8Array(version.length + this.buf.length); | ||
buf.set(version); | ||
buf.set(this.buf, version.length); | ||
return bs58check.encode(buf); | ||
} | ||
derive(index) { | ||
return this.root.derive(index); | ||
} | ||
getNotificationPublicKey() { | ||
return this.derive(0).publicKey; | ||
} | ||
getNotificationAddress() { | ||
return utils.getP2pkhAddress(this.getNotificationPublicKey(), this.network); | ||
} | ||
derivePublicKeyFromSharedSecret(B, S) { | ||
if (!this.ecc.isPoint(B)) | ||
throw new Error('Invalid derived public key'); | ||
if (!S) | ||
throw new Error('Unable to compute secret point'); | ||
const Sx = S.subarray(1, 33); | ||
const s = sha256(Sx); | ||
if (!this.ecc.isPrivate(s)) | ||
throw new Error('Invalid shared secret'); | ||
const EccPoint = this.ecc.pointFromScalar(s); | ||
if (!EccPoint) | ||
throw new Error('Unable to compute point'); | ||
const paymentPublicKey = this.ecc.pointAdd(B, EccPoint); | ||
if (!paymentPublicKey) | ||
throw new Error('Unable to compute payment public key'); | ||
return paymentPublicKey; | ||
} | ||
derivePaymentPublicKey(paymentCode, idx) { | ||
const a = paymentCode.getNotificationPrivateKey(); | ||
if (!this.ecc.isPrivate(a)) | ||
throw new Error('Received invalid private key'); | ||
const B = this.derive(idx).publicKey; | ||
const S = this.ecc.pointMultiply(B, a); | ||
return this.derivePublicKeyFromSharedSecret(B, S); | ||
} | ||
getAddressFromPubkey(pubKey, type) { | ||
switch (type) { | ||
case 'p2pkh': { | ||
return utils.getP2pkhAddress(pubKey, this.network); | ||
} | ||
else if (ecc.isPoint(a)) { | ||
if (!this._hasPrivKeys()) | ||
throw new Error('Unable to compute the derivation with a public key provided as argument'); | ||
const A = a; | ||
const b_node = this.derive(idx); | ||
if (!b_node.privateKey) | ||
throw new Error('Unable to derive node with private key'); | ||
const b = b_node.privateKey; | ||
B = b_node.publicKey; | ||
S = ecc.pointMultiply(A, b); | ||
case 'p2sh': { | ||
return utils.getP2shAddress(pubKey, this.network); | ||
} | ||
if (!B || !ecc.isPoint(B)) | ||
throw new TypeError('Invalid derived public key'); | ||
if (!S) | ||
throw new Error('Unable to compute resulting point'); | ||
const Sx = S.slice(1, 33); | ||
const s = utils.sha256(Buffer.from(Sx)); | ||
if (!ecc.isPrivate(s)) | ||
throw new TypeError('Invalid shared secret'); | ||
const EccPoint = ecc.pointFromScalar(s); | ||
if (!EccPoint) | ||
throw new Error('Unable to compute point'); | ||
const paymentPublicKey = ecc.pointAdd(B, EccPoint); | ||
if (!paymentPublicKey) | ||
throw new TypeError('Unable to compute payment public key'); | ||
return Buffer.from(paymentPublicKey); | ||
} | ||
getPaymentAddress(a, idx, type = 'p2pkh') { | ||
const pubkey = this.derivePaymentPublicKey(a, idx); | ||
if (!pubkey) | ||
throw new TypeError('Unable to derive public key'); | ||
switch (type) { | ||
case 'p2pkh': { | ||
return utils.getP2pkhAddress(pubkey, this.network); | ||
} | ||
case 'p2sh': { | ||
return utils.getP2shAddress(pubkey, this.network); | ||
} | ||
case 'p2wpkh': { | ||
return utils.getP2wpkhAddress(pubkey, this.network); | ||
} | ||
default: { | ||
throw new Error('Unknown address type. Expected: p2pkh | p2sh | p2wpkh'); | ||
} | ||
case 'p2wpkh': { | ||
return utils.getP2wpkhAddress(pubKey, this.network); | ||
} | ||
default: { | ||
throw new Error(`Unknown address type. Expected: p2pkh | p2sh | p2wpkh, got ${type}`); | ||
} | ||
} | ||
} | ||
const fromSeed = (bSeed, id, network = utils.networks.bitcoin) => { | ||
const root = bip32.fromSeed(Buffer.from(bSeed)); | ||
getPaymentAddress(paymentCode, idx, type = 'p2pkh') { | ||
const pubKey = this.derivePaymentPublicKey(paymentCode, idx); | ||
return this.getAddressFromPubkey(pubKey, type); | ||
} | ||
getBlindedPaymentCode(destinationPaymentCode, outpoint, privateKey) { | ||
const a = privateKey; | ||
const B = destinationPaymentCode.getNotificationPublicKey(); | ||
const S = this.ecc.pointMultiply(B, a); | ||
if (!S) | ||
throw new Error('Unable to compute secret point'); | ||
const x = S.subarray(1, 33); | ||
const o = outpoint; | ||
const s = hmacSHA512(o, x); | ||
const paymentCodeBuffer = this.paymentCode; | ||
const blindedPaymentCode = paymentCodeBuffer.slice(0); | ||
blindedPaymentCode.set(utils.xorUint8Arrays(s.subarray(0, 32), paymentCodeBuffer.subarray(3, 35)), 3); | ||
blindedPaymentCode.set(utils.xorUint8Arrays(s.subarray(32, 64), paymentCodeBuffer.subarray(35, 67)), 35); | ||
return utils.bytesToHex(blindedPaymentCode); | ||
} | ||
} | ||
export class PaymentCodePrivate extends PaymentCodePublic { | ||
constructor(root, ecc, bip32, buf, network = utils.networks.bitcoin) { | ||
super(ecc, bip32, buf, network); | ||
this.root = root; | ||
this.hasPrivKeys = true; | ||
} | ||
toPaymentCodePublic() { | ||
return new PaymentCodePublic(this.ecc, this.bip32, this.buf.slice(0), this.network); | ||
} | ||
clone() { | ||
return new PaymentCodePrivate(this.root, this.ecc, this.bip32, this.buf.slice(0), this.network); | ||
} | ||
deriveHardened(index) { | ||
return this.root.deriveHardened(index); | ||
} | ||
derivePaymentPublicKey(paymentCode, idx) { | ||
const A = paymentCode.getNotificationPublicKey(); | ||
if (!this.ecc.isPoint(A)) | ||
throw new Error('Received invalid public key'); | ||
const b_node = this.derive(idx); | ||
if (!b_node.privateKey) | ||
throw new Error('Unable to derive node with private key'); | ||
const b = b_node.privateKey; | ||
const B = b_node.publicKey; | ||
const S = this.ecc.pointMultiply(A, b); | ||
return this.derivePublicKeyFromSharedSecret(B, S); | ||
} | ||
getPaymentAddress(paymentCode, idx, type = 'p2pkh') { | ||
const pubKey = this.derivePaymentPublicKey(paymentCode, idx); | ||
return this.getAddressFromPubkey(pubKey, type); | ||
} | ||
derivePaymentPrivateKey(paymentCodePublic, idx) { | ||
const A = paymentCodePublic.getNotificationPublicKey(); | ||
if (!this.ecc.isPoint(A)) | ||
throw new Error('Argument is not a valid public key'); | ||
const b_node = this.derive(idx); | ||
if (!b_node.privateKey) | ||
throw new Error('Unable to derive node without private key'); | ||
const b = b_node.privateKey; | ||
const S = this.ecc.pointMultiply(A, b); | ||
if (!S) | ||
throw new Error('Unable to compute resulting point'); | ||
const Sx = S.subarray(1, 33); | ||
const s = sha256(Sx); | ||
if (!this.ecc.isPrivate(s)) | ||
throw new Error('Invalid shared secret'); | ||
const paymentPrivateKey = this.ecc.privateAdd(b, s); | ||
if (!paymentPrivateKey) | ||
throw new Error('Unable to compute payment private key'); | ||
return paymentPrivateKey; | ||
} | ||
getNotificationPrivateKey() { | ||
const child = this.derive(0); | ||
return child.privateKey; | ||
} | ||
getPaymentCodeFromNotificationTransactionData(scriptPubKey, outpoint, pubKey) { | ||
if (!(scriptPubKey.length === 83 && scriptPubKey[0] === 0x6a && scriptPubKey[1] === 0x4c && scriptPubKey[2] === 0x50)) | ||
throw new Error('Invalid OP_RETURN payload'); | ||
const A = pubKey; | ||
const b = this.getNotificationPrivateKey(); | ||
const S = this.ecc.pointMultiply(A, b); | ||
if (!S) | ||
throw new Error('Unable to compute secret point'); | ||
const x = S.subarray(1, 33); | ||
const s = hmacSHA512(outpoint, x); | ||
const blindedPaymentCode = scriptPubKey.subarray(3); | ||
const paymentCodeBuffer = blindedPaymentCode.slice(0); | ||
paymentCodeBuffer.set(utils.xorUint8Arrays(s.subarray(0, 32), blindedPaymentCode.subarray(3, 35)), 3); | ||
paymentCodeBuffer.set(utils.xorUint8Arrays(s.subarray(32, 64), blindedPaymentCode.subarray(35, 67)), 35); | ||
return new PaymentCodePublic(this.ecc, this.bip32, paymentCodeBuffer, this.network); | ||
} | ||
} | ||
export const BIP47Factory = (ecc) => { | ||
const bip32 = BIP32Factory(ecc); | ||
const fromSeed = (bSeed, segwit = false, network = utils.networks.bitcoin) => { | ||
const root = bip32.fromSeed(bSeed); | ||
const coinType = (network.pubKeyHash === utils.networks.bitcoin.pubKeyHash) ? '0' : '1'; | ||
const root_bip47 = root.derivePath(`m/47'/${coinType}'/${id}'`); | ||
const root_bip47 = root.derivePath(`m/47'/${coinType}'/0'`); | ||
const pc = new Uint8Array(80); | ||
pc.set([1, 0]); | ||
if (root_bip47.publicKey.length !== 33) | ||
throw new TypeError('Missing or wrong publicKey'); | ||
throw new Error('Missing or wrong publicKey'); | ||
pc.set(root_bip47.publicKey, 2); | ||
if (root_bip47.chainCode.length !== 32) | ||
throw new TypeError('Missing or wrong chainCode'); | ||
throw new Error('Missing or wrong chainCode'); | ||
pc.set(root_bip47.chainCode, 35); | ||
const pcode = new PaymentCode(pc, network); | ||
pcode.root = root_bip47; | ||
return pcode; | ||
if (segwit) { | ||
pc[SAMOURAI_FEATURE_BYTE] = 1; | ||
} | ||
return new PaymentCodePrivate(root_bip47, ecc, bip32, pc, network); | ||
}; | ||
const fromBase58 = (inString, network) => { | ||
const buf = decode(inString); | ||
const version = buf.slice(0, 1); | ||
if (version[0] !== PC_VERSION) | ||
throw new TypeError('Invalid version'); | ||
return new PaymentCode(buf.slice(1), network); | ||
const buf = bs58check.decode(inString); | ||
const version = buf[0]; | ||
if (version !== PC_VERSION) | ||
throw new Error('Invalid version'); | ||
return new PaymentCodePublic(ecc, bip32, buf.slice(1), network); | ||
}; | ||
const fromBuffer = (buf, network) => { | ||
return new PaymentCode(buf, network); | ||
return new PaymentCodePublic(ecc, bip32, buf, network); | ||
}; | ||
@@ -156,0 +214,0 @@ return { |
@@ -1,11 +0,10 @@ | ||
import { TinySecp256k1Interface as TinySecp256k1InterfaceBIP32 } from 'bip32'; | ||
import type { TinySecp256k1Interface as TinySecp256k1InterfaceBIP32 } from '@samouraiwallet/bip32'; | ||
export type AddressType = 'p2pkh' | 'p2sh' | 'p2wpkh'; | ||
interface Bip32 { | ||
public: number; | ||
private: number; | ||
} | ||
export interface Network { | ||
messagePrefix: string; | ||
bech32: string; | ||
bip32: Bip32; | ||
bip32: { | ||
public: number; | ||
private: number; | ||
}; | ||
pubKeyHash: number; | ||
@@ -16,3 +15,2 @@ scriptHash: number; | ||
export interface TinySecp256k1Interface extends TinySecp256k1InterfaceBIP32 { | ||
privateAdd(d: Uint8Array, tweak: Uint8Array): Uint8Array | null; | ||
pointMultiply(p: Uint8Array, tweak: Uint8Array, compressed?: boolean): Uint8Array | null; | ||
@@ -22,3 +20,2 @@ pointAdd(pA: Uint8Array, pB: Uint8Array, compressed?: boolean): Uint8Array | null; | ||
} | ||
export {}; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -1,11 +0,8 @@ | ||
/// <reference types="node" /> | ||
import { Network } from './types'; | ||
import type { Network } from './types.js'; | ||
export { hexToBytes, bytesToHex } from '@noble/hashes/utils'; | ||
export declare const networks: Record<'bitcoin' | 'regtest' | 'testnet', Network>; | ||
export declare function ripemd160(buffer: Uint8Array): Uint8Array; | ||
export declare function sha256(buffer: Uint8Array): Uint8Array; | ||
export declare function hash160(buffer: Uint8Array): Uint8Array; | ||
export declare function toBase58Check(hash: Uint8Array, version: number): string; | ||
export declare function xorUint8Arrays(a: Uint8Array, b: Uint8Array): Uint8Array; | ||
export declare function getP2pkhAddress(pubkey: Uint8Array, network: Network): string; | ||
export declare function getP2shAddress(pubkey: Buffer, network: Network): string; | ||
export declare function getP2shAddress(pubkey: Uint8Array, network: Network): string; | ||
export declare function getP2wpkhAddress(pubkey: Uint8Array, network: Network): string; | ||
//# sourceMappingURL=utils.d.ts.map |
@@ -1,5 +0,3 @@ | ||
import { sha256 as noble_sha256 } from '@noble/hashes/sha256'; | ||
import { ripemd160 as noble_ripemd160 } from '@noble/hashes/ripemd160'; | ||
import bs58check from 'bs58check'; | ||
import { bech32 } from 'bech32'; | ||
import { bech32, bs58check, hash160 } from '@samouraiwallet/bip32/crypto'; | ||
export { hexToBytes, bytesToHex } from '@noble/hashes/utils'; | ||
export const networks = { | ||
@@ -40,12 +38,13 @@ bitcoin: { | ||
}; | ||
export function ripemd160(buffer) { | ||
return noble_ripemd160(buffer); | ||
export function xorUint8Arrays(a, b) { | ||
if (a.length !== b.length) { | ||
throw new Error('Uint8Arrays are not of the same length'); | ||
} | ||
const result = new Uint8Array(a.length); | ||
for (let i = 0; i < a.length; i++) { | ||
result[i] = a[i] ^ b[i]; | ||
} | ||
return result; | ||
} | ||
export function sha256(buffer) { | ||
return noble_sha256(buffer); | ||
} | ||
export function hash160(buffer) { | ||
return ripemd160(sha256(buffer)); | ||
} | ||
export function toBase58Check(hash, version) { | ||
function toBase58Check(hash, version) { | ||
const payload = new Uint8Array(21); | ||
@@ -52,0 +51,0 @@ payload.set([version], 0); |
{ | ||
"name": "@samouraiwallet/bip47", | ||
"version": "0.8.0", | ||
"version": "1.0.0", | ||
"engines": { | ||
@@ -17,18 +17,13 @@ "node": ">=16.6.0" | ||
"type": "module", | ||
"main": "./dist/index.js", | ||
"exports": { | ||
".": "./dist/index.js" | ||
".": { | ||
"import": "./dist/index.js", | ||
"types": "./dist/index.d.ts" | ||
}, | ||
"./utils": { | ||
"import": "./dist/utils.js", | ||
"types": "./dist/utils.d.ts" | ||
} | ||
}, | ||
"types": "./dist/index.d.ts", | ||
"scripts": { | ||
"test": "vitest run", | ||
"test:watch": "vitest watch", | ||
"typescript": "tsc --noEmit", | ||
"lint": "eslint --ext .ts src/ test/", | ||
"build:clean": "rm -rf dist", | ||
"build:esm": "tsc -p tsconfig.build.json", | ||
"build": "npm run build:clean && npm run build:esm", | ||
"prepack": "npm run lint && npm run typescript && npm run test", | ||
"prepare": "npm run build" | ||
}, | ||
"sideEffects": false, | ||
"repository": { | ||
@@ -45,23 +40,30 @@ "type": "git", | ||
"dependencies": { | ||
"@noble/hashes": "1.3.0", | ||
"bech32": "2.0.0", | ||
"bip32": "4.0.0", | ||
"bs58check": "3.0.1" | ||
"@noble/hashes": "1.4.0", | ||
"@samouraiwallet/bip32": "5.1.0" | ||
}, | ||
"devDependencies": { | ||
"@types/bs58check": "^2.1.0", | ||
"@types/create-hash": "^1.2.2", | ||
"@types/node": "^16.18.30", | ||
"@types/tiny-secp256k1": "^2.0.1", | ||
"@typescript-eslint/eslint-plugin": "^5.59.6", | ||
"@typescript-eslint/parser": "^5.59.6", | ||
"@vitest/coverage-c8": "^0.31.0", | ||
"eslint": "^8.40.0", | ||
"eslint-import-resolver-typescript": "^3.5.5", | ||
"eslint-plugin-import": "^2.27.5", | ||
"eslint-plugin-unicorn": "^47.0.0", | ||
"tiny-secp256k1": "^2.2.1", | ||
"typescript": "^5.0.4", | ||
"vitest": "^0.31.0" | ||
"@types/node": "^18.19.31", | ||
"@typescript-eslint/eslint-plugin": "^7.6.0", | ||
"@typescript-eslint/parser": "^7.6.0", | ||
"@vitest/coverage-v8": "^1.5.0", | ||
"eslint": "^8.57.0", | ||
"eslint-import-resolver-typescript": "^3.6.1", | ||
"eslint-plugin-import": "^2.29.1", | ||
"eslint-plugin-unicorn": "^52.0.0", | ||
"tiny-secp256k1": "^2.2.3", | ||
"typedoc": "^0.25.13", | ||
"typescript": "^5.4.5", | ||
"vitest": "^1.5.0" | ||
}, | ||
"scripts": { | ||
"test": "vitest run", | ||
"test:watch": "vitest watch", | ||
"test:coverage": "vitest run --coverage", | ||
"typescript": "tsc --noEmit", | ||
"lint": "eslint --ext .ts src/ test/", | ||
"docs": "typedoc src/*.ts", | ||
"build:clean": "rm -rf dist", | ||
"build:esm": "tsc -p tsconfig.build.json", | ||
"build": "pnpm run build:clean && pnpm run build:esm" | ||
} | ||
} | ||
} |
326
README.md
@@ -5,58 +5,320 @@ # @samouraiwallet/bip47 | ||
This library uses ES Modules. Node.js v14 or later is required. | ||
This library uses ES Modules. Node.js v16 or later is required. | ||
This library does not use any Node.js built-ins and thus is browser compatible. | ||
Source code was written in Typescript. Type definitions are included in the published bundle. | ||
## Usage | ||
## Contents | ||
- [Installation](#installation) | ||
- [API documentation](#api-documentation) | ||
- Usage | ||
- [ECC library](#ecc-library) | ||
- [Examples](#examples) | ||
- [Interfaces](#interfaces) | ||
You need to provide your own implementation of `secp256k1` elliptic curve. | ||
[tiny-secp256k1](https://github.com/bitcoinjs/tiny-secp256k1) is recommended but you can use any other which has the same API. | ||
## Installation | ||
```bash | ||
npm install @samouraiwallet/bip47 | ||
``` | ||
or | ||
All necessary documentation of usage is provided via test files. | ||
```bash | ||
pnpm add @samouraiwallet/bip47 | ||
``` | ||
or | ||
### Using in browser | ||
```bash | ||
yarn add @samouraiwallet/bip47 | ||
``` | ||
You will need webpack/browserify to bundle this library and it's dependencies to be able to use it in browser | ||
environment. | ||
Since `tiny-secp256k1` is a WASM library, it will require such bundler to be able to load WASM code. | ||
Please consult the docs of a bundler that you use for further information. | ||
## API documentation | ||
Generated API documentation is available in the git repository. To view latest API docs locally, you can run these commands. | ||
For example current webpack will require you to set `{ experiments: { asyncWebAssembly: true }}` in your config. | ||
```bash | ||
# create a temp folder | ||
mkdir bip47-docs | ||
cd bip47-docs | ||
## Examples | ||
# download and extract docs directory | ||
curl -fsSL https://code.samourai.io/dojo/bip47-js/-/archive/master/bip47-js-master.tar.gz\?path\=docs | tar -xzv --strip-components=2 | ||
### Mainnet | ||
# run simple HTTP file server | ||
npx serve . | ||
``` | ||
```js | ||
import * as ecc from 'tiny-secp256k1'; | ||
import {BIP47Factory} from '@samouraiwallet/bip47'; | ||
## Usage | ||
### ECC library | ||
You need to provide an implementation of `secp256k1` elliptic curve. | ||
Supported libraries: | ||
- [tiny-secp256k1](https://github.com/bitcoinjs/tiny-secp256k1) - Rust implementation compiled to Webassembly, work in Node.js and browsers but might require reconfiguring your bundler | ||
- [@bitcoinerlab/secp256k1](https://github.com/bitcoinerlab/secp256k1) - Javascript implementation which works everywhere but has a lower performance | ||
### Examples | ||
- [Create an instance of bip47](#create-an-instance-of-bip47) | ||
- [Create a PaymetCodePrivate instance from wallet master seed](#create-a-paymetcodeprivate-instance-from-wallet-master-seed) | ||
- [Create a PaymetCodePublic instance from payment code string](#create-a-paymetcodepublic-instance-from-payment-code-string) | ||
- [Generate a base58 encoded payment code](#generate-a-base58-encoded-payment-code) | ||
- [Get notification address](#get-notification-address) | ||
- [Get notification address public key](#get-notification-address-public-key) | ||
- [Get notification address private key](#get-notification-address-private-key) | ||
- [Derive addresses from Alice to Bob](#derive-addresses-from-alice-to-bob) | ||
- [Derive payment keys from Alice to Bob](#derive-payment-keys-from-alice-to-bob) | ||
- [Extract payment code from notification transaction](#extract-payment-code-from-notification-transaction) | ||
- [Get blinded payment code for notification transaction](#get-blinded-payment-code-for-notification-transaction) | ||
#### Create an instance of bip47 | ||
```ts | ||
import BIP47Factory from "@samouraiwallet/bip47"; | ||
import * as ecc from "tiny-secp256k1"; | ||
const bip47 = BIP47Factory(ecc); | ||
``` | ||
const aliceB58PCode = 'PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA'; | ||
const bobSeed = '87eaaac5a539ab028df44d9110defbef3797ddb805ca309f61a69ff96dbaa7ab5b24038cf029edec5235d933110f0aea8aeecf939ed14fc20730bba71e4b1110'; | ||
#### Create a PaymetCodePrivate instance from wallet master seed | ||
const alicePcode = bip47.fromBase58(aliceB58PCode); | ||
const bobPcode = bip47.fromSeed(Buffer.from(bobSeed, 'hex'), 0); | ||
**on mainnet** | ||
```ts | ||
import type {PaymentCodePrivate} from "@samouraiwallet/bip47"; | ||
const bobNotifPrivKey = bobPcode.getNotificationPrivateKey(); | ||
let walletSeed: Uint8Array; | ||
const alicePaymentAddr1 = alicePcode.getPaymentAddress(bobNotifPrivKey, 0, 'p2pkh'); // derive P2PKH payment address | ||
const alicePaymentAddr2 = alicePcode.getPaymentAddress(bobNotifPrivKey, 1, 'p2wpkh'); // derive P2WPKH payment address | ||
const alice: PaymentCodePrivate = bip47.fromSeed(walletSeed); | ||
// with segwit support | ||
const alice2: PaymentCodePrivate = bip47.fromSeed(walletSeed, true); | ||
``` | ||
### Testnet | ||
**on testnet** | ||
```ts | ||
import type {PaymentCodePrivate} from "@samouraiwallet/bip47"; | ||
import {networks} from "@samouraiwallet/bip47/utils"; | ||
```js | ||
import * as ecc from 'tiny-secp256k1'; | ||
import {BIP47Factory, utils} from '@samouraiwallet/bip47'; | ||
let walletSeed: Uint8Array; | ||
const networks = utils.networks; | ||
// pass in a desired network object (bitcoin | testnet | regtest) from utils or directly from bitcoinjs-lib | ||
const alice: PaymentCodePrivate = bip47.fromSeed(walletSeed, false, networks['testnet']); | ||
// with segwit support | ||
const alice2: PaymentCodePrivate = bip47.fromSeed(walletSeed, true, networks['testnet']); | ||
``` | ||
const bip47 = BIP47Factory(ecc); | ||
#### Create a PaymetCodePublic instance from payment code string | ||
const b58PCode = 'PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97'; | ||
**on mainnet** | ||
```ts | ||
import type {PaymentCodePublic} from "@samouraiwallet/bip47"; | ||
const alicePcode = bip47.fromBase58(b58PCode, networks.testnet); | ||
const aliceNotifAddr = alicePcode.getNotificationAddress(); | ||
const pcode = "PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97"; | ||
const bob: PaymentCodePublic = bip47.fromBase58(pcode); | ||
``` | ||
**on testnet** | ||
```ts | ||
import type {PaymentCodePublic} from "@samouraiwallet/bip47"; | ||
import {networks} from "@samouraiwallet/bip47/utils"; | ||
const pcode = "PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97"; | ||
// pass in a desired network object (bitcoin | testnet | regtest) from utils or directly from bitcoinjs-lib | ||
const bob: PaymentCodePublic = bip47.fromBase58(pcode, networks['testnet']); | ||
``` | ||
#### Generate a base58 encoded payment code | ||
```ts | ||
const alicePcode: string = alice.toBase58(); // PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA | ||
``` | ||
#### Get notification address | ||
```ts | ||
const aliceNotificationAddress: string = alice.getNotificationAddress(); // 1JDdmqFLhpzcUwPeinhJbUPw4Co3aWLyzW | ||
``` | ||
#### Get notification address public key | ||
```ts | ||
const aliceNotifPubKey: Uint8Array = alice.getNotificationPublicKey(); // 0353883a146a23f988e0f381a9507cbdb3e3130cd81b3ce26daf2af088724ce683 | ||
``` | ||
#### Get notification address private key | ||
```ts | ||
const aliceNotifPrivKey: Uint8Array = alice.getNotificationPrivateKey(); // 8d6a8ecd8ee5e0042ad0cb56e3a971c760b5145c3917a8e7beaf0ed92d7a520c | ||
``` | ||
#### Derive addresses from Alice to Bob | ||
**Alice's side** | ||
```ts | ||
// Bob's P2PKH address at index 0 | ||
const bobAddress: string = bob.getPaymentAddress(alice, 0, 'p2pkh'); // 141fi7TY3h936vRUKh1qfUZr8rSBuYbVBK | ||
// check if Bob's payment code supports receiving to segwit addresses | ||
if (bob.segwit) { | ||
// Bob's P2WPKH address at index 1 | ||
const bobSegwitAddress = bob.getPaymentAddress(alice, 1, 'p2wpkh'); // bc1qzn8a8drxv6ln7rztjsw660gzf3hnrfwupzmsfh | ||
} | ||
``` | ||
**Bob's side** | ||
```ts | ||
import type {PaymentCodePrivate, PaymentCodePublic} from "@samouraiwallet/bip47"; | ||
let bobSeed: Uint8Array; | ||
let alicePcode: string; // base58 encoded payment code | ||
const bob: PaymentCodePrivate = bip47.fromSeed(bobSeed); | ||
const alice: PaymentCodePublic = bip47.fromBase58(alicePcode); | ||
const bobAddress: string = bob.getPaymentAddress(alice, 0, 'p2pkh'); // 141fi7TY3h936vRUKh1qfUZr8rSBuYbVBK | ||
``` | ||
#### Derive payment keys from Alice to Bob | ||
**Alice's side** | ||
```ts | ||
// Bob's payment pubkey at index 0 | ||
const bobPubKey: Uint8Array = bob.derivePaymentPublicKey(alice, 0); // 0344b4795e48df097bd87e6cf87a70e4f0c30b2d847b6e34cddde64af10296952d | ||
``` | ||
**Bob's side** | ||
```ts | ||
import type {PaymentCodePrivate, PaymentCodePublic} from "@samouraiwallet/bip47"; | ||
let bobSeed: Uint8Array; | ||
let alicePcode: string; // base58 encoded payment code | ||
const bob: PaymentCodePrivate = bip47.fromSeed(bobSeed); | ||
const alice: PaymentCodePublic = bip47.fromBase58(alicePcode); | ||
// Bob's payment keys at index 0 | ||
const bobPubKey: Uint8Array = bob.derivePaymentPublicKey(alice, 0); | ||
const bobPrivKey: Uint8Array = bob.derivePaymentPrivateKey(alice, 0); | ||
``` | ||
#### Extract payment code from notification transaction | ||
```ts | ||
import type {PaymentCodePrivate, PaymentCodePublic} from "@samouraiwallet/bip47"; | ||
let bobSeed: Uint8Array; | ||
const bob: PaymentCodePrivate = bip47.fromSeed(bob.seed); | ||
let scriptPubKey: Uint8Array; // scriptPubKey of notification transaction OP_RETURN output | ||
let outpoint: Uint8Array; // outpoint of first input of notification transaction | ||
let pubKey: Uint8Array; // public key of first input of notification transaction | ||
const alice: PaymentCodePublic = bob.getPaymentCodeFromNotificationTransactionData(scriptPubKey, outpoint, pubKey); | ||
const alicePcode: string = alice.toBase58(); // PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA | ||
``` | ||
In order to extract payment code from a notification transaction, the scriptPubKey, outpoint and pubKey must be provided. | ||
You can use `bitcoinjs-lib` to extract these values from a transaction. | ||
```ts | ||
import * as bitcoin from 'bitcoinjs-lib'; | ||
let notificationTxHex: string; | ||
const tx: bitcoin.Transaction = bitcoin.Transaction.fromHex(notificationTxHex); | ||
const opReturnOutput = tx.outs.find((o) => | ||
o.script[0] === 0x6a && o.script[1] === 0x4c && o.script[2] === 0x50 | ||
); | ||
if (!opReturnOutput) throw new error("Transaction doesn't contain OP_RETURN output"); | ||
const scriptPubKey: Uint8Array = opReturnOutput.script; | ||
const input = tx.ins[0]; | ||
const outpoint: Uint8Array = new Uint8Array(input.hash.length + 4); | ||
outpoint.set(input.hash); | ||
outpoint.set(new Uint32Array([input.index]), input.hash.length) | ||
let pubKey: Uint8Array; | ||
if (input.witness.length) { | ||
pubKey = input.witness[1]; | ||
} else if (bitcoin.script.toASM(input.script).split(' ').length === 2) { | ||
pubKey = Buffer.from(bitcoin.script.toASM(input.script).split(' ')[1], 'hex',); | ||
} else throw new Error('Unknown Transaction type'); | ||
``` | ||
#### Get blinded payment code for notification transaction | ||
```ts | ||
let outpoint: Uint8Array; // outpoint of the first input of the notification transaction | ||
let privKey: Uint8Array; // private key of a first input of the notification transaction | ||
const blindedAlicePcode: string = alicePcode.getBlindedPaymentCode(bob, outpoint, privKey); | ||
``` | ||
### Interfaces | ||
```ts | ||
export declare const BIP47Factory: (ecc: TinySecp256k1Interface) => { | ||
fromSeed: (bSeed: Uint8Array, segwit?: boolean, network?: Network) => PaymentCodePrivate; | ||
fromBase58: (inString: string, network?: Network) => PaymentCodePublic; | ||
fromBuffer: (buf: Uint8Array, network?: Network) => PaymentCodePublic; | ||
}; | ||
export declare class PaymentCodePublic { | ||
protected readonly ecc: TinySecp256k1Interface; | ||
protected readonly bip32: BIP32API; | ||
protected readonly buf: Uint8Array; | ||
protected readonly network: Network; | ||
root: BIP32Interface; | ||
hasPrivKeys: boolean; | ||
segwit: boolean; | ||
constructor(ecc: TinySecp256k1Interface, bip32: BIP32API, buf: Uint8Array, network?: Network); | ||
get features(): Uint8Array; | ||
get pubKey(): Uint8Array; | ||
get chainCode(): Uint8Array; | ||
get paymentCode(): Uint8Array; | ||
clone(): PaymentCodePublic; | ||
toBase58(): string; | ||
derive(index: number): BIP32Interface; | ||
getNotificationPublicKey(): Uint8Array; | ||
getNotificationAddress(): string; | ||
protected derivePublicKeyFromSharedSecret(B: Uint8Array, S: Uint8Array | null): Uint8Array; | ||
derivePaymentPublicKey(paymentCode: PaymentCodePrivate, idx: number): Uint8Array; | ||
protected getAddressFromPubkey(pubKey: Uint8Array, type: AddressType): string; | ||
getPaymentAddress(paymentCode: PaymentCodePrivate, idx: number, type?: AddressType): string; | ||
getBlindedPaymentCode(destinationPaymentCode: PaymentCodePublic, outpoint: Uint8Array, privateKey: Uint8Array): string; | ||
} | ||
export declare class PaymentCodePrivate extends PaymentCodePublic { | ||
constructor(root: BIP32Interface, ecc: TinySecp256k1Interface, bip32: BIP32API, buf: Uint8Array, network?: Network); | ||
toPaymentCodePublic(): PaymentCodePublic; | ||
clone(): PaymentCodePrivate; | ||
deriveHardened(index: number): BIP32Interface; | ||
derivePaymentPublicKey(paymentCode: PaymentCodePublic, idx: number): Uint8Array; | ||
getPaymentAddress(paymentCode: PaymentCodePublic, idx: number, type?: AddressType): string; | ||
derivePaymentPrivateKey(paymentCodePublic: PaymentCodePublic, idx: number): Uint8Array; | ||
getNotificationPrivateKey(): Uint8Array; | ||
getPaymentCodeFromNotificationTransactionData(scriptPubKey: Uint8Array, outpoint: Uint8Array, pubKey: Uint8Array): PaymentCodePublic; | ||
} | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
36861
2
12
361
1
324
1
+ Added@samouraiwallet/bip32@5.1.0
+ Added@noble/hashes@1.4.0(transitive)
+ Added@samouraiwallet/bip32@5.1.0(transitive)
+ Added@scure/base@1.1.6(transitive)
+ Added@sindresorhus/is@5.6.0(transitive)
+ Addedcallsites@4.2.0(transitive)
+ Addeddot-prop@7.2.0(transitive)
+ Addedlodash.isequal@4.5.0(transitive)
+ Addedow@1.1.1(transitive)
+ Addedtype-fest@2.19.0(transitive)
+ Addedvali-date@1.0.0(transitive)
+ Addedwif@4.0.0(transitive)
- Removedbech32@2.0.0
- Removedbip32@4.0.0
- Removedbs58check@3.0.1
- Removed@noble/hashes@1.3.0(transitive)
- Removed@scure/base@1.2.1(transitive)
- Removedbase-x@3.0.10(transitive)
- Removedbech32@2.0.0(transitive)
- Removedbip32@4.0.0(transitive)
- Removedbs58@4.0.1(transitive)
- Removedbs58check@2.1.2(transitive)
- Removedcipher-base@1.0.5(transitive)
- Removedcreate-hash@1.2.0(transitive)
- Removedhash-base@3.1.0(transitive)
- Removedinherits@2.0.4(transitive)
- Removedmd5.js@1.3.5(transitive)
- Removedreadable-stream@3.6.2(transitive)
- Removedripemd160@2.0.2(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsha.js@2.4.11(transitive)
- Removedstring_decoder@1.3.0(transitive)
- Removedtypeforce@1.18.0(transitive)
- Removedutil-deprecate@1.0.2(transitive)
- Removedwif@2.0.6(transitive)
Updated@noble/hashes@1.4.0