Bitcoin Dart Package

This is a fork of mrtnetwork/bitcoin_base that supports Dart 2.

A comprehensive Bitcoin library for Dart that provides functionality to create, sign, and send Bitcoin transactions. This library supports a wide range of Bitcoin transaction types and features, making it suitable for various use cases.

This package was inspired by the python-bitcoin-utils package and turned into Dart

Features

  • Create and sign Bitcoin transactions
  • Addresses
    • Legacy Keys and Addresses (P2PK, P2PKH, P2SH)
    • Segwit Addresses (P2WPKH, P2SH-P2WPKH, P2WSH and P2SH-P2WSH, Taproot (segwit v1))
  • Support for different transaction types:
    • Legacy transactions (P2PKH, P2SH(P2PK), P2SH(P2PKH) )
      • Transaction with P2PKH input and outputs
      • Create a P2PKH Transaction with different SIGHASHes
      • Create a P2SH Address
      • Create (spent) a P2SH Transaction
  • Segwit Transactions
    • Transaction to pay to a P2WPKH, P2WSH, P2SH(segwit)
    • Spend from a P2SH(P2WPKH) nested segwit address
    • Spend from a P2SH(P2WSH) nested segwit address
  • Timelock Transactions
    • Create a P2SH address with a relative timelock
    • Spend from a timelocked address
  • Taproot (segwit v1) Transactions
    • Spend from a taproot address
    • Spend a multi input that contains both taproot and legacy UTXOs
    • Send to taproot address that contains a single script path spend
    • Spend taproot from key path (has single alternative script path spend)
    • Spend taproot from script path (has single alternative script path spend)
    • Send to taproot address that contains two scripts path spends
    • Send to taproot address that contains three scripts path spends
  • Sign
    • sign message
    • sign transactions
    • Schnorr sign (segwit transactions)
    • support different sighash
    • get public key of signature

Example

A large number of examples and tests have been prepared you can see them in the test folder

Finalizing the transaction with 15 different input types (spend: p2sh, p2wsh, p2wpkh, p2tr, p2sh, p2shInP2wsh, p2shInP2wpkh, p2shInP2pk, p2shInP2pkh, P2wsh(4-4 multi-sig), P2sh(4-7 multi-sig), and etc...) and 15 different output types within a single transaction. mempol

  • Keys and addresses
      final mn = BIP39.generateMnemonic();

      /// accsess to private and public keys
      final masterWallet = HdWallet.fromMnemonic(mn);
      HdWallet.fromXPrivateKey(masterWallet.toXpriveKey());

      /// sign legacy and segwit transaction
      masterWallet.privateKey.signInput(txDigest);

      /// sign taproot transaction
      masterWallet.privateKey.signTapRoot(txDigest);

      /// sign message
      masterWallet.privateKey.signMessage(txDigest);

      /// tprv8ZgxMBicQKsPdEtasyf3Qc1vAycp7pVSf6oAcnN4XAeYuntXsUargabb3Rcdo78YKzAxARfVLah4nfkUfYDrWodRWA9YEstwSrV5ZNvApvt
      masterWallet.toXpriveKey(network: NetworkInfo.TESTNET);

      /// accsess to publicKey
      /// tpubD6NzVbkrYhZ4WhvNmdKdp1g2k18kH9gMEQPwuJQMwSSwkH9JVsQSs5DTDZKeJTiTvLinuTwdL4zf6CJAWE79VwhxHn9tDcq33Xj7BgLKZEH
      final xPublic = masterWallet.toXpublicKey(network: NetworkInfo.TESTNET);
      final publicMasterWallet = HdWallet.fromXpublicKey(xPublic);

      /// derive new path from master wallet
      HdWallet.drivePath(masterWallet, "m/44'/0'/0'/0/0/0");

      /// derive new path from public wallet
      final publicWallet = HdWallet.drivePath(publicMasterWallet, "m/0/1");

      final publicKey = publicWallet.publicKey;

      /// return public key
      publicKey.toHex(compressed: true);

      /// p2pkh address for testnet network
      /// mxukNgWdBF1ibtpCpnNnPR5Zz2FPjvyuCf
      publicKey.toAddress().toAddress(NetworkInfo.TESTNET);

      /// p2sh(p2pk) address for testnet network
      /// 2NE2r3EK7fFYZREaNFVLyEw2UcEUEGjVgF2
      publicKey.toP2pkInP2sh().toAddress(NetworkInfo.TESTNET);

      /// p2sh(p2pkh) address for testnet network
      /// 2MyaJKV4g1R5pWA4LC16pqVqDFtGrn134nP
      publicKey.toP2pkhInP2sh().toAddress(NetworkInfo.TESTNET);

      /// p2sh(p2wpkh) address for testnet network
      /// 2MygAmhWqypY13t8khDM3BtqS9TgHFiSPSi
      publicKey.toP2wpkhInP2sh().toAddress(NetworkInfo.TESTNET);

      /// p2sh(p2wsh) address for testnet network 1-1 multisig segwit script
      /// 2N4hx9SvmENd8DCmbfAVgXUe3ddtadwxW4z
      publicKey.toP2wshInP2sh().toAddress(NetworkInfo.TESTNET);

      /// p2wpkh address
      /// tb1qhmyuz38dy22qlspdnwl6khsycvjpeallzwwcp7
      publicKey.toSegwitAddress().toAddress(NetworkInfo.TESTNET);

      /// p2wsh address 1-1 multisig segwit script
      /// tb1qax8ahkqhm2cvappkdqupjp7w07ervya3rllpechnez6j7hzu7hqq963clk
      publicKey.toP2wshAddress().toAddress(NetworkInfo.TESTNET);

      /// p2tr address
      /// tb1p6hwljzyudccfd3d9ckrh5wqmx786kenmu0caud0ru6e3k2yc5rdq76sw7y
      publicKey.toTaprootAddress().toAddress(NetworkInfo.TESTNET);
  
  • spend P2PK/P2PKH
  final txin = utxo.map((e) => TxInput(txId: e.txId, txIndex: e.vout)).toList(); // p2pk UTXO
  final List<TxOutput> txOut = [
    TxOutput(
        amount: value,
        scriptPubKey: Script(script: receiver.toScriptPubKey()))
  ];
  if (hasChanged) {
    txOut.add(TxOutput(
        amount: changedValue,
        scriptPubKey: Script(script: senderAddress.toScriptPubKey())));
  }
  final tx = BtcTransaction(inputs: txin, outputs: txOut);
  for (int i = 0; i < txin.length; i++) {
    final sc = senderPub.toRedeemScript();
    final txDigit =
        tx.getTransactionDigit(txInIndex: i, script: sc, sighash: sighash);
    final signedTx = prive.signInput(txDigit, sighash);
    txin[i].scriptSig = Script(script: [signedTx]);
  }
  tx.serialize(); // ready for broadcast
  
  • spend P2PKH/P2WKH
  final txin = utxo.map((e) => TxInput(txId: e.txId, txIndex: e.vout)).toList(); // P2PKH UTXO
  final List<TxOutput> txOut = [
    TxOutput(
        amount: value,
        scriptPubKey: Script(script: receiver.toScriptPubKey()))
  ];
  if (hasChanged) {
    final senderAddress = senderPub.toAddress();
    txOut.add(TxOutput(
        amount: changedValue,
        scriptPubKey: Script(script: senderAddress.toScriptPubKey()))); // changed address
  }
  final tx = BtcTransaction(inputs: txin, outputs: txOut, hasSegwit: false);
  for (int b = 0; b < txin.length; b++) {
    final txDigit = tx.getTransactionDigit(
        txInIndex: b,
        script: Script(script: senderPub.toAddress().toScriptPubKey()),
        sighash: sighash);
    final signedTx = sign(txDigit, sigHash: sighash);
    txin[b].scriptSig = Script(script: [signedTx, senderPub.toHex()]);
  }
  tx.serialize(); // ready for broadcast
  
  • spend P2WKH/P2SH
  final txin = utxo.map((e) => TxInput(txId: e.txId, txIndex: e.vout)).toList(); // p2wkh utxo
  final List<TxWitnessInput> w = [];
  final List<TxOutput> txOut = [
    TxOutput(
        amount: value,
        scriptPubKey: receiver.toRedeemScript().toP2shScriptPubKey())
  ];
  if (hasChanged) {
    txOut.add(TxOutput(
        amount: changedValue,
        scriptPubKey:
            Script(script: senderPub.toSegwitAddress().toScriptPubKey()))); // changed address
  }
  final tx = BtcTransaction(inputs: txin, outputs: txOut, hasSegwit: true);
  for (int i = 0; i < txin.length; i++) {
    // get segwit transaction digest
    final txDigit = tx.getTransactionSegwitDigit(
        txInIndex: i,
        script: Script(script: senderPub.toAddress().toScriptPubKey()),
        sighash: sighash,
        amount: utxo[i].value);
    final signedTx = sign(txDigit,sighas);
    w.add(TxWitnessInput(stack: [signedTx, senderPub.toHex()]));
  }
  tx.witnesses.addAll(w);
  tx.serialize(); // ready for broadcast
  

Contributing

Contributions are welcome! Please follow these guidelines:

  • Fork the repository and create a new branch.
  • Make your changes and ensure tests pass.
  • Submit a pull request with a detailed description of your changes.

Feature requests and bugs

Please file feature requests and bugs in the issue tracker.

Support

If you find this repository useful, show us some love, and give us a star! Small Bitcoin donations to the following address are also welcome:

bc1q2y5rcf6uwmg57zsmqy86xsc0pt0rw7qujn2a2h

18Q7w7aQYSAQonWTr2Uj5Z7VT7dNTZFwqk