Untitled

 avatar
unknown
plain_text
a month ago
3.8 kB
0
Indexable
declare enum AddressType {
  p2pkh = "p2pkh",
  p2sh = "p2sh",
  p2wpkh = "p2wpkh",
  p2wsh = "p2wsh",
  p2tr = "p2tr"
}
const networkModes = ['mainnet', 'testnet'] as const;
type NetworkModes = (typeof networkModes)[number];
const getCoinType = (network: NetworkModes) => (network === 'mainnet' ? 0 : 1);
const walletTypes = [
  { type: 'Native Segwit', path: `m/84'/`, addressType: AddressType.p2wpkh },
  { type: 'Nested Segwit', path: `m/49'/`, addressType: AddressType.p2sh },
  { type: 'Legacy', path: `m/44'/`, addressType: AddressType.p2pkh },
  { type: 'Taproot', path: `m/86'/`, addressType: AddressType.p2tr },
];


function getPublicKeyFromXpubAtIndex(xpub: string, index: number, network: string): Buffer {
  const btcNetwork = network === 'mainnet' ? networks.bitcoin : networks.testnet;
  const { publicKey } = bip32.fromBase58(xpub, btcNetwork).derivePath(`0/${index}`);

  return Buffer.from(publicKey);
}

function publicKeyToAddress(publicKey: string, type: AddressType, network: Network) {
  if (!publicKey) return '';
  const pubkey = Buffer.from(publicKey, 'hex');
  if (type === AddressType.p2pkh) {
    const { address } = bitcoin.payments.p2pkh({
      pubkey,
      network,
    });

    return address || '';
  } else if (type === AddressType.p2wpkh) {
    const { address } = bitcoin.payments.p2wpkh({
      pubkey,
      network,
    });

    return address || '';
  } else if (type === AddressType.p2tr) {
    const { address } = bitcoin.payments.p2tr({
      internalPubkey: pubkey.length === 32 ? pubkey : pubkey.slice(1, 33),
      network,
    });

    return address || '';
  } else if (type === AddressType.p2sh) {
    const { address } = bitcoin.payments.p2sh({
      pubkey: pubkey,
      network,
      redeem: bitcoin.payments.p2wpkh({ pubkey: pubkey, network }),
    });

    return address || '';
  } else {
    return '';
  }
}

async function importAccointFromLedger({
                                         extendedPublicKey,
                                         network,
                                         addressIndex,
                                         addressType,
                                       }: {
  extendedPublicKey: string;
  network: NetworkModes;
  addressIndex: number;
  addressType: AddressType;
}): Promise<{ address: string; publicKey: string }> {
  const publicKey = getPublicKeyFromXpubAtIndex(extendedPublicKey, addressIndex, network).toString(
    'hex'
  );
  const net = network === 'mainnet' ? networks.bitcoin : networks.testnet;

  const address = publicKeyToAddress(publicKey, addressType, net);

  return { address, publicKey: publicKey };
}

export function pullBitcoinKeysFromLedgerDevice(
  app: BitcoinApp,
  derivationPath: string,
  addressType: AddressType
) {
  return async ({ onRequestKey, network }) => {
    const amountOfKeysToExtractFromDevice = 10;
    const btcNetwork = getCoinType(network);
    const keys: IAddressSelection[] = [];
    for (let accountIndex = 0; accountIndex < amountOfKeysToExtractFromDevice; accountIndex++) {
      const xpubDerivationPath = `${derivationPath}${btcNetwork}'/${accountIndex}'`;
      const extendedPublicKey = await app.getExtendedPubkey(xpubDerivationPath);
      onRequestKey?.(accountIndex);
      const { publicKey, address } = await importAccointFromLedger({
        extendedPublicKey,
        network,
        addressIndex: 0,
        addressType,
      });

      const fullDerivationPath = `${xpubDerivationPath}/0/0`;
      keys.push({
        publicKey,
        address,
        xpub: extendedPublicKey,
        derivationPath: fullDerivationPath,
      });
    }

    return { status: 'success', keys };
  };
}
Leave a Comment