var request = require('request'); /** * ## Responses * Responses from this API with be wrapped in an envelope to assist the caller * in dealing with the various responses from the service. There are no types * to check against in JavaScript, so this facilitates that. * * { * status: 200, * data: { name: "John", age: 29 } * } * * ## Logging * To enable logging, use the NODE_DEBUG environment variable. * * NODE_DEBUG=apidoc * * @constructor */ function Hilltopapi (host) { var showLogMessages = process.env.NODE_DEBUG ? process.env.NODE_DEBUG.indexOf('apidoc') !== -1 : false; function log (message) { if (showLogMessages) { console.log('[apidoc] ' + message); } } function getQueryString (options) { var params = []; var qs = options.qs || {}; for (var key in qs) { if (qs[key]) { params.push(key + '=' + qs[key]); } } return params.join('&'); } function doRequest (options) { var startMs = Date.now(); options = options || {}; if (!options.json) { options.json = true; } options.qsStringifyOptions = { arrayFormat: 'repeat' }; var queryString = getQueryString(options); var url = options.uri + (queryString ? '?' + queryString : ''); log(options.method + ' ' + url); if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH') { log('|--> With body:\n\t\t' + JSON.stringify(options.json)); } return new Promise(function (resolve, reject) { request(options, function (err, response) { if (err) { reject(err); return; } var duration = Date.now() - startMs; var requestHref = response.request ? response.request.href : ''; log('Completed ' + options.method + ' ' + requestHref + ' ' + response.statusCode + ' ' + duration + 'ms'); resolve(response); }); }); } /** * Simple plain object check. This is actually really involved to be 100% * accurate */ function isObject (obj) { return typeof obj === 'object' && !Array.isArray(obj); } var ActiveStakeAmounts = (function ActiveStakeAmounts() { /** * * * @param {Object} options * @param {number} options.epochNumber */ function get(options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/active_stake_amounts'; var queryString = { epoch_number: options.epochNumber, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {string} poolBech32Id * @param {Object} options * @param {number} options.epochNumber */ function getByPoolBech32Id(poolBech32Id, options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/active_stake_amounts/' + poolBech32Id + ''; var queryString = { epoch_number: options.epochNumber, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {string} poolBech32Id * @param {Object} options * @param {number} options.epochNumber */ function getAddressesByPoolBech32Id(poolBech32Id, options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/active_stake_amounts/' + poolBech32Id + '/addresses'; var queryString = { epoch_number: options.epochNumber, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { get: get, getByPoolBech32Id: getByPoolBech32Id, getAddressesByPoolBech32Id: getAddressesByPoolBech32Id, }; }()); var Blocks = (function Blocks() { /** * * * @param {Object} options * @param {string} options.date */ function getBlocksInDay(options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/blocks/blocks_in_day'; var queryString = { date: options.date, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {string} blockNumber */ function getByBlockNumber(blockNumber) { var url = host + '/blocks/' + blockNumber + ''; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getBlocksInDay: getBlocksInDay, getByBlockNumber: getByBlockNumber, }; }()); var Delegations = (function Delegations() { /** * * * @param {string} stakingAddress */ function getSafePaymentByStakingAddress(stakingAddress) { var url = host + '/delegations/' + stakingAddress + '/safe_payment'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {string} stakingAddress */ function getStakepoolByStakingAddress(stakingAddress) { var url = host + '/delegations/' + stakingAddress + '/stakepool'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getSafePaymentByStakingAddress: getSafePaymentByStakingAddress, getStakepoolByStakingAddress: getStakepoolByStakingAddress, }; }()); var DelegatorsCounts = (function DelegatorsCounts() { /** * * * @param {string} poolBech32Id */ function getByPoolBech32Id(poolBech32Id) { var url = host + '/delegators_counts/' + poolBech32Id + ''; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getByPoolBech32Id: getByPoolBech32Id, }; }()); var Epochs = (function Epochs() { /** * * */ function getCurrent() { var url = host + '/epochs/current'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {string} epochNumber */ function getByEpochNumber(epochNumber) { var url = host + '/epochs/' + epochNumber + ''; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getCurrent: getCurrent, getByEpochNumber: getByEpochNumber, }; }()); var Healthchecks = (function Healthchecks() { /** * * */ function get() { var url = host + '/healthchecks'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { get: get, }; }()); var NativeAssets = (function NativeAssets() { /** * * * @param {Object} options * @param {number} options.blockNumberFrom * @param {number} options.limit */ function getIds(options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/native_assets/ids'; var queryString = { block_number_from: options.blockNumberFrom, limit: options.limit, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {string} policyId */ function getAssetNamesByPolicyId(policyId) { var url = host + '/native_assets/' + policyId + '/asset_names'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {Object} options * @param {number} options.blockNumberFrom * @param {number} options.blockNumberTo */ function getNfts(options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/native_assets/nfts'; var queryString = { block_number_from: options.blockNumberFrom, block_number_to: options.blockNumberTo, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {Object} options * @param {Object} options.data - The request body */ function postNativeAssetsDetails(options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } if (!options.data) { return Promise.reject(new Error('options.data property is required.')); } var url = host + '/native_assets/native_assets_details'; var queryString = { }; var requestOpts = { qs: queryString, json: options.data, uri: url, method: 'POST' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {string} stakeAddress */ function getByStakeAddress(stakeAddress) { var url = host + '/native_assets/' + stakeAddress + ''; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getIds: getIds, getAssetNamesByPolicyId: getAssetNamesByPolicyId, getNfts: getNfts, postNativeAssetsDetails: postNativeAssetsDetails, getByStakeAddress: getByStakeAddress, }; }()); var PoolUpdates = (function PoolUpdates() { /** * * * @param {Object} options * @param {number} options.blockNumberFrom * @param {number} options.limit */ function getRetiring(options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/pool_updates/retiring'; var queryString = { block_number_from: options.blockNumberFrom, limit: options.limit, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {string} poolId * @param {Object} options * @param {number} options.blockNumberFrom * @param {number} options.limit */ function getByPoolId(poolId, options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/pool_updates/' + poolId + ''; var queryString = { block_number_from: options.blockNumberFrom, limit: options.limit, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * * @param {Object} options * @param {number} options.blockNumberFrom * @param {number} options.limit */ function get(options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/pool_updates'; var queryString = { block_number_from: options.blockNumberFrom, limit: options.limit, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getRetiring: getRetiring, getByPoolId: getByPoolId, get: get, }; }()); var Rewards = (function Rewards() { /** * * * @param {string} stakingAddress * @param {Object} options * @param {number} options.epochFrom * @param {number} options.epochTo */ function getByStakingAddress(stakingAddress, options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/rewards/' + stakingAddress + ''; var queryString = { epoch_from: options.epochFrom, epoch_to: options.epochTo, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getByStakingAddress: getByStakingAddress, }; }()); var Sigmas = (function Sigmas() { /** * can be either the pool_id (e.g. pool1wcwe923r...) or the pool_hash * * @param {string} poolId * @param {Object} options * @param {number} options.epochNumber */ function getByPoolId(poolId, options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/sigmas/' + poolId + ''; var queryString = { epoch_number: options.epochNumber, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getByPoolId: getByPoolId, }; }()); var Stakepools = (function Stakepools() { /** * find pool details by vrf_vkey * * @param {string} vrfVkeyHash */ function getVrfVkeyByVrfVkeyHash(vrfVkeyHash) { var url = host + '/stakepools/vrf_vkey/' + vrfVkeyHash + ''; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * Epoch at which stake pool was originally registered * * @param {string} poolId */ function getEstablishedEpochByPoolId(poolId) { var url = host + '/stakepools/' + poolId + '/established_epoch'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * live stake in lovelaces * * @param {string} poolId */ function getLiveStakeByPoolId(poolId) { var url = host + '/stakepools/' + poolId + '/live_stake'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * number minted blocks in epoch * * @param {string} poolId * @param {Object} options * @param {number} options.epoch */ function getBlocksByPoolId(poolId, options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/stakepools/' + poolId + '/blocks'; var queryString = { epoch: options.epoch, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * * */ function get() { var url = host + '/stakepools'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getVrfVkeyByVrfVkeyHash: getVrfVkeyByVrfVkeyHash, getEstablishedEpochByPoolId: getEstablishedEpochByPoolId, getLiveStakeByPoolId: getLiveStakeByPoolId, getBlocksByPoolId: getBlocksByPoolId, get: get, }; }()); var StakepoolMetadata = (function StakepoolMetadata() { /** * * * @param {string} poolIdBech32 */ function getByPoolIdBech32(poolIdBech32) { var url = host + '/stakepool_metadata/' + poolIdBech32 + ''; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); case 404: return Promise.reject({ status: serverResponse.statusCode, data: serverResponse.body }); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getByPoolIdBech32: getByPoolIdBech32, }; }()); var Transactions = (function Transactions() { /** * * * @param {Object} options * @param {Object} options.data - The request body */ function postHistory(options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } if (!options.data) { return Promise.reject(new Error('options.data property is required.')); } var url = host + '/transactions/history'; var queryString = { }; var requestOpts = { qs: queryString, json: options.data, uri: url, method: 'POST' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { postHistory: postHistory, }; }()); var WalletBalances = (function WalletBalances() { /** * * * @param {string} stakingAddress */ function getByStakingAddress(stakingAddress) { var url = host + '/wallet_balances/' + stakingAddress + ''; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { getByStakingAddress: getByStakingAddress, }; }()); this.ActiveStakeAmounts = ActiveStakeAmounts; this.Blocks = Blocks; this.Delegations = Delegations; this.DelegatorsCounts = DelegatorsCounts; this.Epochs = Epochs; this.Healthchecks = Healthchecks; this.NativeAssets = NativeAssets; this.PoolUpdates = PoolUpdates; this.Rewards = Rewards; this.Sigmas = Sigmas; this.Stakepools = Stakepools; this.StakepoolMetadata = StakepoolMetadata; this.Transactions = Transactions; this.WalletBalances = WalletBalances; } module.exports = Hilltopapi;