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
- Legacy transactions (P2PKH, P2SH(P2PK), P2SH(P2PKH) )
- 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