Generic API Provider

The Generic API Providers typically provide a range of API services. The services offered can differ from one provider to the next, and the REST APIs tend to be provider-specific. A key difference between these offerings and those provided by the Merchant APIs, is that they typically provide a TestNet API, and as such it is a very quick way to bootstrap development.

Some Generic API Providers may specialize their offerings around certain use-cases e.g. Data Uploads. The developer is encouraged to look beyond the immediate list of providers provided below to ensure a good match with their application design.

None of the Generic API Providers listed below provide any WebHooks (callbacks). It is up to the individual developer to decide how they would implement a transaction monitoring solution in these cases.

Architecture

Figure 1: Connecting to a Generic API Provider

Figure 1: Connecting to a Generic API Provider

This is an abbreviated list of Generic API Providers, all of whom provide access the BitcoinSV blockchain. Beyond those specified below there exists a much larger pool of providers who offer services for the Bitcoin Core (BTC) and Bitcoin Cash (BCH) networks.

API Provider URL
WhatsOnChain https://developers.whatsonchain.com/#stats
BlockChair https://blockchair.com/api
MatterCloud https://www.mattercloud.net/ (previously bitindex)

Connect

The API Documentation for the Generic API Providers are provider-specific. Please read the documentation provided by the individual providers.

Interact

For the purpose of our example below, we shall be using the WhatsOnChain API. At the time of writing there are no authentication key requirements, and REST queries are rate-limited to 3/second.

We shall be building out a much more complete example that includes transaction broadcast to the live TestNet, as well as querying for available UTXOs that we can spend from. In order to do all that, we need some TestNet-specific Bitcoin.

TestNet Coins

Testnet Bitcoins are not spendable on the main Bitcoin network. They are typically mined into existence by TestNet miners who currently perform this function altruistically.

In order to get our hands on some TestNet coins we will need 2 things:

  • A TestNet faucet (it’s like a free Bitcoin fountain)
  • A Private Key and accompanying TestNet Bitcoin Address where we can receive the coins.

A quick way to get an Address is to generate one using a random SVPrivateKey instance, and printing both the key and address to the console. You will need to copy-paste the Key into your code so you can deterministically retrieve your coins associated with that Key.

void createKey(){
  var privKey = SVPrivateKey(networkType: NetworkType.TEST);
  var address = privKey.toAddress(networkType: NetworkType.TEST);

  print('Private Key : ${privKey.toWIF()}');
  print('Address : ${address.toString()}');
}

Once you have your Address and Private Key in hand, you can reconstruct them from their serialized form as follows:

var privKey = SVPrivateKey.fromWIF('cTW2ivBd2rwSpSRAxzsb7uYgMzCdosasVFTuXPdWFRQEwaPYGm7d');
var address = Address.fromBase58('n3RYAZLyMHZDb52Qxtz8raz9NBrHCbB6RL');

At the time of writing, you can get some testnet satoshis from the following faucet at BitcoinCloud.

Provider Url
Bitcoin Cloud https://faucet.bitcoincloud.net/

API code example

Once we have some satoshis in our Address, the following bit of code will :

  • Lookup a list of Unspent Outputs using our Address
  • Retrieve a raw transaction associated with the first UTXO
  • Create a spending Transaction from that UTXO
  • Broadcast the spending Transaction using the WhatsOnChain API
  • Print the Transaction ID to the console so we can check the Block Explorer

import 'dart:convert';
import 'dart:io';

import 'package:dartsv/dartsv.dart';
import 'package:http/http.dart' as http;

var privateKey = SVPrivateKey.fromWIF('cTW2ivBd2rwSpSRAxzsb7uYgMzCdosasVFTuXPdWFRQEwaPYGm7d');

Future<Transaction> getTransaction(txHash) async{
  var client = http.Client();
  var response = await client.get('https://api.whatsonchain.com/v1/bsv/test/tx/${txHash}/hex');
  return Transaction.fromHex(response.body.toString());
}

void sendTransaction(Transaction tx) async {

  print('Sending transaction with ID: ${tx.id}');

  var client = http.Client();
  var response = await client.post('https://api.whatsonchain.com/v1/bsv/test/tx/raw',
      headers: {
        HttpHeaders.contentTypeHeader : ContentType.json.toString()
      },
      body: jsonEncode({
        'txhex' : tx.serialize()
      }));

  print ('Response status: ${response.statusCode}');
  print('Confirmation TxID: ${response.body.toString()}');
}

void spendUtxo() async {
  var address = Address.fromBase58('n3RYAZLyMHZDb52Qxtz8raz9NBrHCbB6RL');

  var client = http.Client();

  //Retrieve all our UTXOs for the Address
  var response = await client.get('https://api.whatsonchain.com/v1/bsv/test/address/${address.toString()}/unspent');
  var jsonResponse = jsonDecode(response.body.toString());

  if (jsonResponse is List){
    //spend all the coins from 1st UTXO

    var txId = jsonResponse[0]['tx_hash'];
    var value = jsonResponse[0]['value'];

    var txWithUtxo = await getTransaction(txId);

    //grab the first utxo
    var utxo = txWithUtxo.outputs[jsonResponse[0]['tx_pos']];

    var unlockBuilder = P2PKHUnlockBuilder(privateKey.publicKey);
    var transaction = Transaction()
        .spendFromOutput(utxo, Transaction.NLOCKTIME_MAX_VALUE, scriptBuilder: unlockBuilder)
        .spendTo(address, BigInt.from(value - 10000), scriptBuilder: P2PKHLockBuilder(address))
        .sendChangeTo(address, scriptBuilder: P2PKHLockBuilder(address))
        .withFeePerKb(1000);

    //Sign the Transaction Input
    transaction.signInput(0, privateKey, sighashType: SighashType.SIGHASH_ALL | SighashType.SIGHASH_FORKID);

    //broadcast the new transaction, sending the money back to ourselves
    sendTransaction(transaction);
  }
}

void main(List<String> args){

  spendUtxo();
}