bitbox 0.0.2

  • Readme
  • Changelog
  • Example
  • Installing
  • new46

Bitbox lite #

Lite version of Bitcoin.com's Bitbox JS library for building Bitcoin Cash apps in Flutter. Integrated to Bitcoin.com's REST API. Works with mainnet and testnet.

Getting Started #

1) Depend on it #

If you just want to get this from Dart's public package directory:

dependencies:
  bitbox: ^0.0.1

If you checked this out from Github, add a local dependency into the pubspec.yaml of your testing or development projet:

dependencies:
  bitbox_plugin:
    path: <path to the directory>/

2) Import it #

// There's a good chance your own project will use similar names as some of the 
// classes in this library. A simple way to create some order is to import the 
// library with Bitbox prefix:
import 'package:bitbox/bitbox.dart' as Bitbox;

2) Use it #

// set this to true to use testnet
final testnet = true;

// After running the code for the first time, depositing an amount to the address 
// displayed in the console, and waiting for confirmation, paste the generated 
// mnemonic here, so the code continues below with address withdrawal
String mnemonic = "";

if (mnemonic == "") {
  // generate 12-word (128bit) mnemonic
  mnemonic = Bitbox.Mnemonic.generate();

  print(mnemonic);
}

// generate a seed from mnemonic
final seed = Bitbox.Mnemonic.toSeed(mnemonic);

// create an instance of Bitbox.HDNode for mainnet
final masterNode = Bitbox.HDNode.fromSeed(seed, testnet);

// This format is compatible with Bitcoin.com wallet.
// Other wallets use Change to m/44'/145'/0'/0
final accountDerivationPath = "m/44'/0'/0'/0";

// create an account node using the provided derivation path
final accountNode = masterNode.derivePath(accountDerivationPath);

// get account's extended private key
final accountXPriv = accountNode.toXPriv();

// create a Bitbox.HDNode instance of the first child in this account
final childNode = accountNode.derive(0);

// get an address of the child
final address = childNode.toCashAddress();

// if you are using testnet, set the appropriate rest api url before making any API
// calls (like getting address or transaction details or broadcasting a transaction
if (testnet) {
  Bitbox.Bitbox.setRestUrl(restUrl: Bitbox.Bitbox.trestUrl);
}

// get address details
final addressDetails = await Bitbox.Address.details(address);

print(addressDetails);

// If there is a confirmed balance, attempt to withdraw it
if (addressDetails["balance"] > 0) {
  final builder = Bitbox.Bitbox.transactionBuilder(testnet: testnet);

  // retrieve address' utxos from the rest api
  final utxos = await Bitbox.Address.utxo(address) as List<Bitbox.Utxo>;

  // placeholder for input signatures
  final signatures = <Map>[];

  // placeholder for total input balance
  int totalBalance = 0;

  // iterate through the list of address utxos and use them as inputs for the 
  // withdrawal transaction
  utxos.forEach((Bitbox.Utxo utxo) {
    // add the utxo as an input for the transaction
    builder.addInput(utxo.txid, utxo.vout);

    // add a signature to the list to be used later
    signatures.add({
      "vin": signatures.length,
      "key_pair": childNode.keyPair,
      "original_amount": utxo.satoshis
    });

    totalBalance += utxo.satoshis;
  });

  // set an address to send the remaining balance to
  final outputAddress = "";

  // if there is an unspent balance, create a spending transaction
  if (totalBalance > 0 && outputAddress != "") {
    // calculate the fee based on number of inputs and one expected output
    final fee = Bitbox.BitcoinCash.getByteCount(signatures.length, 1);

    // calculate how much balance will be left over to spend after the fee
    final sendAmount = totalBalance - fee;

    // add the output based on the address provided in the testing data
    builder.addOutput(outputAddress, sendAmount);

    // sign all inputs
    signatures.forEach((signature) {
      builder.sign(signature["vin"], signature["key_pair"], 
	    signature["original_amount"]);
    });

    // build the transaction
    final tx = builder.build();

    // broadcast the transaction
    final txid = await Bitbox.RawTransactions.sendRawTransaction(tx.toHex());

    // Yatta!
    print("Transaction broadcasted: $txid");
  } else if (totalBalance > 0) {
    print("Enter an output address to test withdrawal transaction");
  }
}

For further documentation, refer to apidoc of this repository

Testing #

There are some unit tests in test/bitbox_test.dart. They use data generated from the original Bitbox for JS and compare them with the output of this library. The following is tested for both testnet and mainnet:

  • Generating the master node from mnemonic and comparing both its XPub and XPriv
  • Generating an account node and comparing XPub and XPriv
  • Generating 10 test childs and comparing their private keys and addresses
  • Conversion of cashAddr format to legacy and vice versa
  • Fetching address details for the rest API
  • Fetching utxos for addresses with balance
  • Building a transaction spending the deposited balance and comparing its hash with Bitbox JS output
  • Optionally broadcasting the transaction and

To run the test:

  1. Copy create_test_data.js to a separate directory and download the original Bitbox JS into the directory
  2. Generate the testing data by runing create_test_data.js with your local nodeJS engine
  3. Update bitbox_test.dart with the path to the generated test_data.json file
  4. Run bitbox_test.dart Optionally between step 1) and 2), send some balance to either testnet or mainnet addresses (or both), wait for confirmations and run create_test_data.js again to update the data and generate testing transactions

Acknowledgments #

This is a port of the original JS-based Bitbox library by Gabriel Cardona and Bitcoin.com, so first of all huge thanks to Gabriel and the whole Bitcoin.com team for doing so much for the BCH ecosystem.

Also I either re-used a lot of code originally wrote for Bitcoin or called some libraries (bip39 and bip32) by anicdh, so Thanks big time to him. Without that it would take me many more weeks!

[0.0.1] - TODO: Add release date.

  • TODO: Describe initial release.

example/main.dart

import 'package:bitbox/bitbox.dart' as Bitbox;

void main() async {
  // set this to false to use mainnet
  final testnet = false;

  // After running the code for the first time, depositing an amount to the address displayed in the console,
  // and waiting for confirmation, paste the generated mnemonic here,
  // so the code continues below with address withdrawal
  String mnemonic = "";

  if (mnemonic == "") {
    // generate 12-word (128bit) mnemonic
    mnemonic = Bitbox.Mnemonic.generate();

    print(mnemonic);
  }

  // generate a seed from mnemonic
  final seed = Bitbox.Mnemonic.toSeed(mnemonic);

  // create an instance of Bitbox.HDNode for mainnet
  final masterNode = Bitbox.HDNode.fromSeed(seed, testnet);

  // This format is compatible with Bitcoin.com wallet.
  // Other wallets use Change to m/44'/145'/0'/0
  final accountDerivationPath = "m/44'/0'/0'/0";

  // create an account node using the provided derivation path
  final accountNode = masterNode.derivePath(accountDerivationPath);

  // get account's extended private key
  final accountXPriv = accountNode.toXPriv();

  // create a Bitbox.HDNode instance of the first child in this account
  final childNode = accountNode.derive(0);

  // get an address of the child
  final address = childNode.toCashAddress();

  // if you are using testnet, set the appropriate rest api url before making
  // any API calls (like getting address or transaction details or broadcasting a transaction
  if (testnet) {
    Bitbox.Bitbox.setRestUrl(restUrl: Bitbox.Bitbox.trestUrl);
  }

  // get address details
  final addressDetails = await Bitbox.Address.details(address);

  print(addressDetails);

  // If there is a confirmed balance, attempt to withdraw it to the address defined below
  if (addressDetails["balance"] > 0) {
    final builder = Bitbox.Bitbox.transactionBuilder(testnet: testnet);

    // retrieve address' utxos from the rest api
    final utxos = await Bitbox.Address.utxo(address) as List<Bitbox.Utxo>;

    // placeholder for input signatures
    final signatures = <Map>[];

    // placeholder for total input balance
    int totalBalance = 0;

    // iterate through the list of address utxos and use them as inputs for the withdrawal transaction
    utxos.forEach((Bitbox.Utxo utxo) {
      // add the utxo as an input for the transaction
      builder.addInput(utxo.txid, utxo.vout);

      // add a signature to the list to be used later
      signatures.add({
        "vin": signatures.length,
        "key_pair": childNode.keyPair,
        "original_amount": utxo.satoshis
      });

      totalBalance += utxo.satoshis;
    });

    // set an address to send the remaining balance to
    final outputAddress = "";

    // if there is an unspent balance, create a spending transaction
    if (totalBalance > 0 && outputAddress != "") {
      // calculate the fee based on number of inputs and one expected output
      final fee = Bitbox.BitcoinCash.getByteCount(signatures.length, 1);

      // calculate how much balance will be left over to spend after the fee
      final sendAmount = totalBalance - fee;

      // add the output based on the address provided in the testing data
      builder.addOutput(outputAddress, sendAmount);

      // sign all inputs
      signatures.forEach((signature) {
        builder.sign(signature["vin"], signature["key_pair"], signature["original_amount"]);
      });

      // build the transaction
      final tx = builder.build();

      // broadcast the transaction
      final txid = await Bitbox.RawTransactions.sendRawTransaction(tx.toHex());

      // Yatta!
      print("Transaction broadcasted: $txid");
    } else if (totalBalance > 0) {
      print("Enter an output address to test withdrawal transaction");
    }
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  bitbox: ^0.0.2

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:bitbox/bitbox.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
0
Health:
Code health derived from static analysis. [more]
94
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
90
Overall:
Weighted score of the above. [more]
46
Learn more about scoring.

We analyzed this package on Oct 9, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.5.1
  • pana: 0.12.21
  • Flutter: 1.9.1+hotfix.4

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Health suggestions

Fix lib/src/transactionbuilder.dart. (-1.99 points)

Analysis of lib/src/transactionbuilder.dart reported 4 hints:

line 382 col 15: The value of the local variable 'pkh1' isn't used.

line 383 col 15: The value of the local variable 'pkh2' isn't used.

line 401 col 13: The declaration '_pubkeyToOutputScript' isn't referenced.

line 407 col 13: The declaration '_toInputScript' isn't referenced.

Fix lib/src/ecpair.dart. (-1 points)

Analysis of lib/src/ecpair.dart reported 2 hints:

line 3 col 8: Don't import implementation files from another package.

line 4 col 8: Don't import implementation files from another package.

Fix lib/src/hdnode.dart. (-1 points)

Analysis of lib/src/hdnode.dart reported 2 hints:

line 175 col 11: Name non-constant identifiers using lowerCamelCase.

line 176 col 11: Name non-constant identifiers using lowerCamelCase.

Fix additional 8 files with analysis or formatting issues. (-2.50 points)

Additional issues in the following files:

  • lib/src/transaction.dart (2 hints)
  • lib/src/crypto/ecurve.dart (1 hint)
  • lib/src/mnemonic.dart (1 hint)
  • lib/src/utils/p2pkh.dart (1 hint)
  • lib/src/address.dart (Run flutter format to format lib/src/address.dart.)
  • lib/src/utils/rest_api.dart (Run flutter format to format lib/src/utils/rest_api.dart.)
  • lib/src/utils/script.dart (Run flutter format to format lib/src/utils/script.dart.)
  • lib/src/varuint.dart (Run flutter format to format lib/src/varuint.dart.)

Maintenance suggestions

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
bip32 ^1.0.5 1.0.5
bip39 ^1.0.3 1.0.3
bs58check ^1.0.1 1.0.1
fixnum ^0.10.9 0.10.9
flutter 0.0.0
hex ^0.1.2 0.1.2
http ^0.12.0+1 0.12.0+2
meta ^1.1.6 1.1.7
pointycastle ^1.0.1 1.0.1
Transitive dependencies
async 2.4.0
charcode 1.1.2
collection 1.14.11 1.14.12
convert 2.1.1
crypto 2.1.3
http_parser 3.1.3
path 1.6.4
pedantic 1.8.0+1
sky_engine 0.0.99
source_span 1.5.5
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test