blocktrail-sdk
Version:
BlockTrail's Developer Friendly API binding for NodeJS
223 lines (192 loc) • 6.52 kB
JavaScript
var blocktrail = require('../blocktrail');
var request = require('superagent');
var _ = require('lodash');
var q = require('q');
var InsightEndpointMainnet = 'https://insight.bitpay.com/api';
var InsightEndpointTestnet = 'https://test-insight.bitpay.com/api';
/**
*
* @param options
* @constructor
*/
var InsightBitcoinService = function(options) {
this.defaultSettings = {
host: InsightEndpointMainnet,
testnet: false,
retryLimit: 5,
retryDelay: 20
};
// Backwards compatibility: change default host to bitpay
// if host not set but testnet requested.
if (typeof options.host === 'undefined' && options.testnet) {
this.defaultSettings.host = InsightEndpointTestnet;
}
this.settings = _.merge({}, this.defaultSettings, options);
this.DEFAULT_ENDPOINT_MAINNET = InsightEndpointMainnet;
this.DEFAULT_ENDPOINT_TESTNET = InsightEndpointTestnet;
};
/**
* gets unspent outputs for a batch of addresses, returning an array of outputs with hash, index, value,
* and script pub hex mapped to each corresponding address
*
* @param {array} addresses array of addresses
* @returns {q.Promise} promise resolves with array of unspent outputs mapped to addresses as
* { address: [{"hash": hash, "index": index, "value": value, "script_hex": scriptHex}]}
*/
InsightBitcoinService.prototype.getBatchUnspentOutputs = function(addresses) {
var self = this;
var deferred = q.defer();
//get unspent outputs for the chunk of addresses - required data: hash, index, value, and script hex,
var data = {"addrs": addresses.join(',')};
self.postEndpoint('addrs/utxo', data).then(function(results) {
var batchResults = {}; //utxos mapped to addresses
//reduce the returned data into the values we're interested in, and map to the relevant addresses
results.forEach(function(utxo) {
var address = utxo['address'];
if (typeof batchResults[address] === "undefined") {
batchResults[address] = [];
}
batchResults[address].push({
'hash': utxo['txid'],
'index': utxo['vout'],
'value': blocktrail.toSatoshi(utxo['amount']),
'script_hex': utxo['scriptPubKey'],
'confirmations': utxo['confirmations']
});
});
deferred.resolve(batchResults);
}, function(err) {
deferred.reject(err);
});
return deferred.promise;
};
/**
* gets transactions for a batch of addresses
*
* @param {array} addresses array of addresses
* @returns {q.Promise}
*/
InsightBitcoinService.prototype.batchAddressHasTransactions = function(addresses) {
var self = this;
var data = {"addrs": addresses.join(',')};
return self.postEndpoint('addrs/txs', data)
.then(function(results) {
return results.items.length > 0;
})
;
};
/**
* get estimated fee/kb
*
* @returns {q.Promise}
*/
InsightBitcoinService.prototype.estimateFee = function() {
var self = this;
var nBlocks = "4";
return self.getEndpoint('utils/estimatefee?nbBlocks=' + nBlocks)
.then(function(results) {
if (results[nBlocks] === -1) {
return 100000;
}
// sanity check if reported fee is under 1 satoshi/byte
if (results[nBlocks] < 1000 / 1e8) {
return 1000;
}
return parseInt(results[nBlocks] * 1e8, 10);
})
;
};
/**
* Submit a raw transaction hex to the tx/send endpoint
* @param hex
* @returns {*}
*/
InsightBitcoinService.prototype.sendTx = function(hex) {
return this.postEndpoint('tx/send', {rawtx: hex});
};
/**
* Makes a URL from the endpoint and issues a GET request.
* @param endpoint
*/
InsightBitcoinService.prototype.getEndpoint = function(endpoint) {
return this.getRequest(this.settings.host + '/' + endpoint);
};
/**
* Makes URL from endpoint and issues a POST request.
*
* @param endpoint
* @param data
* @returns {promise|Function|*}
*/
InsightBitcoinService.prototype.postEndpoint = function(endpoint, data) {
return this.postRequest(this.settings.host + '/' + endpoint, data);
};
/**
* Makes a GET request to url
* @param url
* @returns {promise|Function|*}
*/
InsightBitcoinService.prototype.getRequest = function(url) {
var deferred = q.defer();
request
.get(url)
.end(function(error, res) {
if (error) {
deferred.reject(error);
return;
}
if (res.ok) {
if (res.headers['content-type'].indexOf('application/json') >= 0) {
try {
var body = JSON.parse(res.text);
return deferred.resolve(body);
} catch (e) {
return deferred.reject(error);
}
} else {
return deferred.resolve(res.body);
}
} else {
return deferred.reject(res.text);
}
});
return deferred.promise;
};
/**
* Makes a POST request given the url and data
*
* @param url
* @param data
* @returns {promise|Function|*}
*/
InsightBitcoinService.prototype.postRequest = function(url, data) {
var deferred = q.defer();
request
.post(url)
.send(data)
.set('Content-Type', 'application/json')
.end(function(error, res) {
if (error) {
deferred.reject(error);
return;
}
if (res.ok) {
// Insight server provides valid JSON but doesn't set Content-Type on some responses
// so we are going to try and parse JSON first, then check the Content-Type and resolve accordingly
try {
var body = JSON.parse(res.text);
return deferred.resolve(body);
} catch (e) {
if (res.headers['content-type'].indexOf('application/json') >= 0) {
return deferred.reject(error);
} else {
return deferred.resolve(res.body);
}
}
} else {
return deferred.reject(res.text);
}
});
return deferred.promise;
};
module.exports = InsightBitcoinService;