import { HARDENED_OFFSET, HDKey } from '@scure/bip32'; import { BitcoinAddress } from '@leather.io/models'; import { createCounter } from '@leather.io/utils'; import { makeTaprootAddressIndexDerivationPath } from '../payments/p2tr-address-gen'; import { makeNativeSegwitAddressIndexDerivationPath } from '../payments/p2wpkh-address-gen'; import { getNativeSegwitAddress, getTaprootAddress, inferNetworkFromAddress, inferPaymentTypeFromAddress, whenSupportedPaymentType, } from './bitcoin.utils'; interface LookUpDerivationByAddressArgs { taprootXpub: string; nativeSegwitXpub: string; iterationLimit: number; } export function lookupDerivationByAddress(args: LookUpDerivationByAddressArgs) { const { taprootXpub, nativeSegwitXpub, iterationLimit } = args; const taprootKeychain = HDKey.fromExtendedKey(taprootXpub); const nativeSegwitKeychain = HDKey.fromExtendedKey(nativeSegwitXpub); return (address: BitcoinAddress) => { const network = inferNetworkFromAddress(address); const changeIndex = 0; const paymentType = inferPaymentTypeFromAddress(address); const accountIndex = whenSupportedPaymentType(paymentType)({ p2tr: taprootKeychain.index - HARDENED_OFFSET, p2wpkh: nativeSegwitKeychain.index - HARDENED_OFFSET, }); function getTaprootAddressAtIndex(addressIndex: number) { return getTaprootAddress({ changeIndex, addressIndex, keychain: taprootKeychain, network }); } function getNativeSegwitAddressAtIndex(addressIndex: number) { return getNativeSegwitAddress({ changeIndex, addressIndex, keychain: nativeSegwitKeychain, network, }); } const paymentFn = whenSupportedPaymentType(paymentType)({ p2tr: getTaprootAddressAtIndex, p2wpkh: getNativeSegwitAddressAtIndex, }); const derivationPathFn = whenSupportedPaymentType(paymentType)({ p2tr: makeTaprootAddressIndexDerivationPath, p2wpkh: makeNativeSegwitAddressIndexDerivationPath, }); const count = createCounter(); const t0 = performance.now(); while (count.getValue() <= iterationLimit) { const currentIndex = count.getValue(); const addressToCheck = paymentFn(currentIndex); if (addressToCheck !== address) { count.increment(); continue; } const t1 = performance.now(); return { status: 'success', duration: t1 - t0, path: derivationPathFn({ network, accountIndex, addressIndex: currentIndex, changeIndex, }), } as const; } return { status: 'failure' } as const; }; }