import axios from 'axios';
import crypto from 'crypto';
import qs from 'qs';
/**
An importable Javascript class to make REST requests to the MiningRigRentals API
*/
const v1 = 'v1';
const v2 = 'v2';
class MiningRigRentals {
/**
* instantiate a MRR api using this constructor
* @param {Object} apiSettings
* @param {string} apiSettings.key - mining rig rentals api key
* @param {string} apiSettings.secret - mining rig rentals api secret
*/
constructor(apiSettings) {
this.baseURL = 'https://www.miningrigrentals.com/api/';
if (apiSettings && apiSettings.key && apiSettings.secret) {
this.key = apiSettings.key;
this.secret = apiSettings.secret;
this.prevNonce = Date.now()
}
}
/* ------------ Information API ----------- */
/**
* Test connectivity and return information about you
* @async
* @returns {Promise<Object>}
*/
async whoami() {
let endpoint = '/whoami';
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
};
/**
* Get a list of MRR rig servers
* @async
* @returns {Promise<Object>}
*/
async getServers() {
let endpoint = '/info/servers';
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
};
/**
* Get all algos and statistics for them (suggested price, unit information, current rented hash/etc)
* @param {string} algo - algo to search on
* @param {string} [currency='BTC'] - Currency to use for price info *Ticker. Options: BTC, ETH, LTC, DASH
* @async
* @returns {Promise<Object>}
*/
async getAlgos(algo, currency) {
algo = algo || ''
let endpoint = `/info/algos/${algo}`;
let params;
if (currency) {
params = {
currency: currency
}
}
let api = this.initAPI(endpoint, params);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
};
/**
* Get statistics for an algo (suggested price, unit information, current rented hash/etc)
* @param {string} algo - the name of the algorithm you wish to search by. Ex: 'scrypt'
* @param {string} [currency='BTC'] - Currency to use for price info. Options: BTC, ETH, LTC, DASH
* @async
* @returns {Promise<Object>}
*/
async getAlgo(algo, currency) {
let endpoint = `/info/algos/${algo}`, params;
if (currency) {
params = {
currency: currency
}
}
let api = this.initAPI(endpoint, params);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
};
/* ------------ Account API ----------- */
/**
* Retrieve account information
* @async
* @returns {Promise<Object>}
*/
async getAccount() {
let endpoint = `/account`;
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* Retrieve account balances
* @async
* @returns {Promise<Object>}
*/
async getAccountBalance() {
let endpoint = `/account/balance`;
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
// /**
// * Request a payout/withdrawal **CURRENTLY DISABLED
// * ToDO: DISABLED ENDPOINT
// * @async
// * @returns {Promise<Object>}
// */
// async withdrawFunds() {
// let endpoint = `/account/balance`;
// let api = this.initAPI(endpoint);
// try {
// return (await api.put(endpoint)).data;
// } catch (err) {
// throw this.createError(endpoint, 'PUT', err)
// }
// }
/**
* List/search transaction history
* @param {Object} [options]
* @param {number} [options.start=0] - Start number (for pagination)
* @param {number} [options.limit=100] - Limit number (for pagination)
* @param {string} [options.algo] - Algo to filter -- see /info/algos
* @param {string} [options.type] - Type to filter -- one of [credit,payout,referral,deposit,payment,credit/refund,debit/refund,Rental Fee]
* @param {number} [options.rig] - Filter to specific rig by ID
* @param {number} [options.rental] - Filter to specific rental by ID
* @param {string} [options.txid] - Filter to specific txid
* @async
* @returns {Promise<Object>}
*/
async getTransactions(options) {
let endpoint = `/account/transactions`;
let api = this.initAPI(endpoint, options);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* List all pool profiles, or list by algo
* @param {string} [algo] - Algo to filter -- see /info/algos
* @async
* @returns {Promise<Object>}
*/
async getPoolProfiles(algo) {
let endpoint = `/account/profile`;
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* Create a pool profile
* @param {string} name - Name of the profile
* @param {string} algo - Algo of the profile -> see /info/algos
* @async
* @returns {Promise<Object>}
*/
async createPoolProfile(name, algo) {
let endpoint = `/account/profile`;
let params = {
name,
algo: algo.toLowerCase()
};
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* Get a specific pool profile
* @param {number} id - ID of the pool profile
* @async
* @returns {Promise<Object>}
*/
async getPoolProfile(id) {
let endpoint = `/account/profile/${id}`;
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* Add a pool to the profile
* @param {Object} options
* @param {number} options.profileID - The profile id you want to add the pool to
* @param {number} options.poolid - Pool ID to add -- see /account/pool
* @param {number} options.priority - 0-4
* @param {string} options.algo - Name of algorithm
* @param {string} options.name - Pool name (doesn't change the pool name... just an MRR requirement)
* @async
* @returns {Promise<Object>}
* //return example
* {
* success: true,
* data: { id: '23136', success: true, message: 'Updated' }
* }
*/
async addPoolToProfile(options) {
let endpoint = `/account/profile/${options.profileID}`;
let params = {};
for (let opts in options) {
params[opts] = options[opts]
}
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* Update or replace a pool to a profile... **Poor MRR Documentation
* @param {Object} options
* @param {number} options.profileID - Pool Profile ID
* @param {number} options.poolid - Pool ID
* @param {number} options.priority - 0-4
* @param {string} options.algo - Name of algorithm
* @param {string} options.name - Pool name (doesn't change the pool name... just an MRR requirement)
* @async
* @returns {Promise<Object>}
*/
async updatePoolOnProfile(options) {
try {
return await this.addPoolToProfile(options)
} catch (err) {
throw this.createError(`/account/profile/${options.profileID}`, 'PUT', err)
}
}
/**
* Delete a specific pool profile
* @param {number} id - Pool Profile ID
* @async
* @returns {Promise<Object>}
*/
async deletePoolProfile(id) {
let endpoint = `/account/profile/${id}`;
let api = this.initAPI(endpoint);
try {
return (await api.delete(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'DELETE', err)
}
}
// /**
// * Test a pool to verify connectivity/functionality **Disabled Endpoint
// * ToDo: ** NO DOCUMENTATION || DISABLED ENDPOINT
// * @async
// * @returns {Promise<Object>}
// */
// async testPoolConnection() {
// let endpoint = `/account/pool/test`;
//
// let api = this.initAPI(endpoint);
// try {
// return (await api.put(endpoint)).data;
// } catch (err) {
// throw this.createError(endpoint, 'PUT', err)
// }
// }
/**
* Get saved pools
* @async
* @returns {Promise<Object>}
*/
async getPools() {
let endpoint = `/account/pool`;
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* Get pools by ID
* @param {(number|Array.<number>)} ids - pool ids
* @async
* @returns {Promise<Object>}
*/
async getPoolsByID(ids) {
let queryString = '';
if (Array.isArray(ids)) {
queryString = ids.join(';');
} else {
if (typeof ids === 'string' || typeof ids === 'number') {
queryString = ids
}
}
let endpoint = `/account/pool/${queryString}`;
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* Create a pool
* @param {Object} options
* @param {string} options.type - Pool algo, eg: sha256, scrypt, x11, etc
* @param {string} options.name - Name to identify the pool with
* @param {string} options.host - Pool host, the part after stratum+tcp://
* @param {number} options.port - Pool port, the part after the : in most pool host strings
* @param {string} options.user - Your workname
* @param {string} [options.pass='x'] - Worker password
* @param {string} [options.notes] - Additional notes to help identify the pool for you
* @async
* @returns {Promise<Object>}
*/
async createPool(options) {
let endpoint = `/account/pool`;
let params = {};
for (let opt in options) {
params[opt] = options[opt]
}
if (!params.pass) {
params.pass = 'x'
}
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* Update saved pools
* @param {(number|Array.<number>)} poolIDs - IDs of the pools you wish to update
* @param {Object} [options]
* @param {string} [options.type] - Pool algo, eg: sha256, scrypt, x11, etc
* @param {string} [options.name] - Name to identify the pool with
* @param {string} [options.host] - Pool host, the part after stratum+tcp://
* @param {number} [options.port] - Pool port, the part after the : in most pool host strings
* @param {string} [options.user] - Your workname
* @param {string} [options.pass] - Worker password
* @param {string} [options.notes] - Additional notes to help identify the pool for you
* @async
* @returns {Promise<Object>}
*/
async updatePools(poolIDs, options) {
let queryString = '';
if (Array.isArray(poolIDs)) {
queryString = poolIDs.join(';');
} else {
if (typeof poolIDs === 'string' || typeof poolIDs === 'number') {
queryString = poolIDs
}
}
let endpoint = `/account/pool/${queryString}`;
let params = {};
for (let opt in options) {
params[opt] = options[opt]
}
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* Delete 1 or more pools
* @param {(number|Array.<number>)} poolIDs - Pool IDS to delete
* @async
* @returns {Promise<Object>}
*/
async deletePools(poolIDs) {
let queryString = '';
if (Array.isArray(poolIDs)) {
queryString = poolIDs.join(';');
} else {
if (typeof poolIDs === 'string' || typeof poolIDs === 'number') {
queryString = poolIDs
}
}
let endpoint = `/account/pool/${queryString}`;
let api = this.initAPI(endpoint);
try {
return (await api.delete(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'DELETE', err)
}
}
/* ------------ Rig API ----------- */
/**
* Search for rigs on a specified algo. This is identical to the main rig list pages.
* @param {object} options - input fields/query parameters
* @param {string} options.type - Rig type, eg: sha256, scrypt, x11, etc
* @param {Object} [options.minhours] - Filter the minimum hours of the rig *broken
* @param {number} [options.minhours.min]
* @param {number} [options.minhours.max]
* @param {Object} [options.maxhours] - Filter the maximum hours of the rig *broken
* @param {number} [options.maxhours.min]
* @param {number} [options.maxhours.max]
* @param {Object} [options.rpi] - Filter the RPI score
* @param {number} [options.rpi.min]
* @param {number} [options.rpi.max]
* @param {Object} [options.hash] - Filter the hashrate
* @param {number} [options.hash.min]
* @param {number} [options.hash.max]
* @param {string} [options.hash.type] - The hash type of min/max. defaults to "mh", possible values: [hash,kh,mh,gh,th]
* @param {Object} [options.price] - Filter the price
* @param {number} [options.price.min]
* @param {number} [options.price.max]
* @param {boolean} [options.offline=false] - To show or not to show offline rigs
* @param {boolean} [options.rented=false} - to show or not to show rented rigs
* @param {Object} [options.region] - Filter the region
* @param {string} [options.region.type] - Determines if this filter is an inclusive or exclusive filter.. possible options are [include,exclude]
* @param {boolean} [options.region.<REGION>] - A region to include/exclude
* @param {number} [options.count=100] - Number of results to return, max is 100
* @param {number} [options.offset=0] - What result number to start with, returning COUNT results
* @param {string} [options.order="score"] - Field to order the results by. Default is "score", Possible values: [rpi,hash,price,minhrs,maxhrs,score]
* @param {string} [options.orderdir="asc"] - Order direction
* @async
* @returns {Promise<Object>}
*/
async getRigs(options) {
let endpoint = '/rig';
let params = {};
if (options) {
for (let opt in options) {
params[opt] = options[opt]
}
}
let api = this.initAPI(endpoint, params);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* List my rigs
* @param {Object} [options]
* @param {string} [options.type] - Filter on algo -- see /info/algos
* @param {boolean} [options.hashrate=false] - Calculate and display hashrates
* @async
* @returns {Promise<Object>}
*/
async listMyRigs(options) {
let endpoint = '/rig/mine';
let params = {};
if (options) {
for (let opt in options) {
params[opt] = options[opt]
}
}
let api = this.initAPI(endpoint, params);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* Get 1 or more rigs by ID
* @param {(number|Array<number>)} rigIDs - Rig IDs
* @async
* @returns {Promise<Object>}
*/
async getRigsByID(rigIDs) {
let queryString = '';
if (Array.isArray(rigIDs)) {
queryString = rigIDs.join(';');
} else {
if (typeof rigIDs === 'string' || typeof rigIDs === 'number') {
queryString = rigIDs
}
}
let endpoint = `/rig/${queryString}`;
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* Create a Rig
* @param {Object} [options]
* @param {string} options.name - Name of rig
* @param {string} options.type - Type of rig (scrypt, sha256, x11, etc)
* @param {string} [options.status] - "enabled","disabled"
* @param {string} options.server - Server name -- see /info/servers
* @param {Object} [options.price]
* @param {string} [options.price.btc.price] - Price of the rig per price.type per day (BTC)
* @param {boolean} [options.price.btc.autoprice] - Enable BTC autopricing
* @param {(string|number)} [options.price.btc.minimum] - Minimum price for the autopricer -- 0 to disable
* @param {string} [options.price.btc.modified] - Percent +/- to modify the autopricing (eg: +10 or -5.13 is 10% over or 5.13% under market rates, respectively), 0 to disable
* @param {boolean} [options.price.ltc.enabled=true]
* @param {(string|number)} [options.price.ltc.price] - Price of the rig per price.type per day (LTC)
* @param {boolean} [options.price.ltc.autoprice] - Enable LTC autopricing -- adjusts the LTC rate based on your BTC price and the GDAX market rate
* @param {string} [options.price.eth.enabled=true]
* @param {(string|number)} [options.price.eth.price] - Price of the rig per price.type per day (ETH)
* @param {boolean} [options.price.eth.autoprice] - Enable ETH autopricing -- adjusts the ETH rate based on your BTC price and the GDAX market rate
* @param {string} [options.price.dash.enabled=true]
* @param {string} [options.price.dash.price] - Price of the rig per price.type per day (DASH)
* @param {boolean} [options.price.dash.autoprice] - Enable DASH autopricing -- adjusts the DASH rate based on your BTC price and the GDAX market rate
* @param {string} [options.price.type='mh'] - The hash type of hash.. defaults to "mh" possible values: [hash,kh,mh,gh,th]
* @param {number} [options.minhours] - Minimum number of hours available
* @param {number} [options.maxhours] - Maximum number of hours available
* @param {Object} [options.hash]
* @param {(string|number)} options.hash.hash - Amount of hash to advertise
* @param {string} options.hash.type='mh' - The hash type of hash.. defaults to "mh" possible values: [hash,kh,mh,gh,th]
* @async
* @returns {Promise<Object>}
*/
async createRig(options) {
let endpoint = `/rig`;
let params = {};
if (options) {
for (let opt in options) {
params[opt] = options[opt]
}
}
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* Update 1 or more rigs by ID
* @param {(number|Array<number>)} rigIDs - Rig ID(s)
* @param {Object} [options]
* @param {string} [options.name] - Name of rig
* @param {string} [options.status] - "enabled","disabled"
* @param {string} [options.server] - Server name -- see /info/servers
* @param {Object} [options.price]
* @param {string} [options.price.btc.price] - Price of the rig per price.type per day (BTC)
* @param {boolean} [options.price.btc.autoprice] - Enable BTC autopricing
* @param {(string|number)} [options.price.btc.minimum] - Minimum price for the autopricer -- 0 to disable
* @param {string} [options.price.btc.modified] - Percent +/- to modify the autopricing (eg: +10 or -5.13 is 10% over or 5.13% under market rates, respectively), 0 to disable
* @param {boolean} [options.price.ltc.enabled=true]
* @param {(string|number)} [options.price.ltc.price] - Price of the rig per price.type per day (LTC)
* @param {boolean} [options.price.ltc.autoprice] - Enable LTC autopricing -- adjusts the LTC rate based on your BTC price and the GDAX market rate
* @param {string} [options.price.eth.enabled=true]
* @param {(string|number)} [options.price.eth.price] - Price of the rig per price.type per day (ETH)
* @param {boolean} [options.price.eth.autoprice] - Enable ETH autopricing -- adjusts the ETH rate based on your BTC price and the GDAX market rate
* @param {string} [options.price.dash.enabled=true]
* @param {string} [options.price.dash.price] - Price of the rig per price.type per day (DASH)
* @param {boolean} [options.price.dash.autoprice] - Enable DASH autopricing -- adjusts the DASH rate based on your BTC price and the GDAX market rate
* @param {string} [options.price.type='mh'] - The hash type of hash.. defaults to "mh" possible values: [hash,kh,mh,gh,th]
* @param {number} [options.minhours] - Minimum number of hours available
* @param {number} [options.maxhours] - Maximum number of hours available
* @param {Object} [options.hash]
* @param {(string|number)} [options.hash.hash] - Amounto f hash to advertise
* @param {string} [options.hash.type='mh'] - The hash type of hash.. defaults to "mh" possible values: [hash,kh,mh,gh,th]
* @async
* @returns {Promise<Object>}
*/
async updateRigsByID(rigIDs, options) {
let queryString = '';
if (Array.isArray(rigIDs)) {
queryString = rigIDs.join(';');
} else {
if (typeof rigIDs === 'string' || typeof rigIDs === 'number') {
queryString = rigIDs
}
}
let endpoint = `/rig/${queryString}`;
let params = {};
if (options) {
for (let opt in options) {
params[opt] = options[opt]
}
}
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* Delete 1 or more rigs by ID
* @param {(number|Array<number>)} rigIDs
* @async
* @returns {Promise<Object>}
*/
async deleteRigs(rigIDs) {
let queryString = '';
if (Array.isArray(rigIDs)) {
queryString = rigIDs.join(';');
} else {
if (typeof rigIDs === 'string' || typeof rigIDs === 'number') {
queryString = rigIDs
}
}
let endpoint = `/rig/${queryString}`;
let api = this.initAPI(endpoint);
try {
return (await api.delete(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'DELETE', err)
}
}
/**
* For rig owners: extend a rental to donate time to the renter -- Assuming an active rental is in progress.
* @param {(number|Array<number>)} rigIDs - IDs of the Rigs you wish to extend @ToDo: unclear if rig ID or rental ID needed
* @param {Object} options
* @param {number} options.hours - Hours to extend by
* @param {number} options.minutes - Minutes to extend by
* @async
* @returns {Promise<Object>}
*/
async extendRental(rigIDs, options) {
let queryString = '';
if (Array.isArray(rigIDs)) {
queryString = rigIDs.join(';');
} else {
if (typeof rigIDs === 'string' || typeof rigIDs === 'number') {
queryString = rigIDs
}
}
let endpoint = `/rig/${queryString}/extend`;
let params = {};
if (options) {
for (let opt in options) {
params[opt] = options[opt]
}
}
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* Apply a pool profile to one or more rigs
* @param {(number|Array<number>)} rigIDs - Rig IDs
* @param {number} profileID - Profile ID to apply -- see /account/profile
* @async
* @returns {Promise<Object>}
*/
async applyPoolToRigs(rigIDs, profileID) {
let queryString = '';
if (Array.isArray(rigIDs)) {
queryString = rigIDs.join(';');
} else {
if (typeof rigIDs === 'string' || typeof rigIDs === 'number') {
queryString = rigIDs
}
}
let endpoint = `/rig/${queryString}/profile`;
let params = {
profile: profileID
};
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* List pools assigned to one or more rigs
* @param {(number|Array<number>)} rigIDs - Rig IDs
* @async
* @returns {Promise<Object>}
*/
async getPoolsFromRigs(rigIDs) {
let queryString = '';
if (Array.isArray(rigIDs)) {
queryString = rigIDs.join(';');
} else {
if (typeof rigIDs === 'string' || typeof rigIDs === 'number') {
queryString = rigIDs
}
}
let endpoint = `/rig/${queryString}/pool`;
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/** Replace a pool on one or more rigs
* @param {(number|Array<number>)} rigIDs - Rig IDs
* @param {Object} options
* @param {string} options.host - pool host (the part after stratum+tcp://)
* @param {number} options.port - pool port (ex: 3333)
* @param {string} options.user - workername
* @param {string} options.pass - worker password
* @param {number} [options.priority] - 0-4 -- can be passed in after pool/ instead.eg /rig/17/pool/0
* @async
* @returns {Promise<Object>}
*/
async replacePoolOnRigs(rigIDs, options) {
try {
return await this.addPoolToRigs(rigIDs, options)
} catch (err) {
throw new Error(err)
}
}
/** Add a pool on one or more rigs
* @param {(number|Array<number>)} rigIDs - Rig IDs
* @param {Object} options
* @param {string} options.host - pool host (the part after stratum+tcp://)
* @param {number} options.port - pool port (ex: 3333)
* @param {string} options.user - workername
* @param {string} options.pass - worker password
* @param {number} [options.priority] - 0-4 -- can be passed in after pool/ instead.eg /rig/17/pool/0
* @async
* @returns {Promise<Object>}
*/
async addPoolToRigs(rigIDs, options) {
let queryString = '';
if (Array.isArray(rigIDs)) {
queryString = rigIDs.join(';');
} else {
if (typeof rigIDs === 'string' || typeof rigIDs === 'number') {
queryString = rigIDs
}
}
let params = {};
if (options) {
for (let opt in options) {
params[opt] = options[opt]
}
}
let endpoint = `/rental/${queryString}/pool`;
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* Delete a pool on one or more rigs
* @param {(number|Array<number>)} rigIDs - Rig IDs
* @param {number} priority - 0-4 -- can be passed in after pool/ instead.eg /rig/17/pool/0
* @async
* @returns {Promise<Object>}
*/
async deletePoolOnRigs(rigIDs, priority) {
let queryString = '';
if (Array.isArray(rigIDs)) {
queryString = rigIDs.join(';');
} else {
if (typeof rigIDs === 'string' || typeof rigIDs === 'number') {
queryString = rigIDs
}
}
let params = {
priority
};
let endpoint = `/rental/${queryString}/pool`;
let api = this.initAPI(endpoint, params);
try {
return (await api.delete(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'DELETE', err)
}
}
/* ------------ Rental API ----------- */
/**
* Lists rentals
* @param {Object} [options] - input fields/query parameters
* @param {string} [options.type=renter] - Type is one of [owner,renter] -- owner means rentals on your rigs, renter means rentals you purchased
* @param {string} [options.algo] - Filter by algo, see /info/algos
* @param {boolean} [options.history=false] - true = Show completed rentals, false = Active rentals
* @param {number} [options.rig] - Show rentals related to a specific rig ID
* @param {number} [options.start=0] - Start number (for pagination)
* @param {number} [options.limit=25] - Limit number (for pagination)
* @async
* @returns {Promise<Object>}
*/
async getRentals(options) {
let endpoint = '/rental';
let params = {};
if (options) {
for (let opt in options) {
params[opt] = options[opt]
}
}
let api = this.initAPI(endpoint, params);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* Get information on rentals by rental ID.
* @param {(number|Array<number>)} ids - Rental IDs
* @async
* @returns {Promise<Object>}
*/
async getRentalById(ids) {
let idQueryString = '';
if (Array.isArray(ids)) {
idQueryString = ids.join(';');
} else {
if (typeof ids === 'string' || typeof ids === 'number') {
idQueryString = ids
}
}
let endpoint = `/rental/${idQueryString}`;
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* Create a new rental
* @param {Object} options
* @param {number} options.rig - Rig ID to rent
* @param {number} options.length - Length in hours to rent
* @param {number} options.profile - The profile ID to apply (see /account/profile)
* @param {string} [options.currency='BTC'] - Currency to use -- one of [BTC,LTC,ETH,DASH]
* @param {Object} [options.rate]
* @param {string} [options.rate.type='mh'] - The hash type of rate. defaults to "mh", possible values: [hash,kh,mh,gh,th]
* @param {number} [options.rate.price] - Price per [rate.type] per day to pay -- this is a filter only, it will use the rig's current price as long as it is <= this value
* @async
* @returns {Promise<Object>}
*/
async createRental(options) {
let endpoint = `/rental`;
let params = {};
if (options) {
for (let opt in options) {
params[opt] = options[opt]
}
}
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* Apply a pool profile to one or more rentals
* @param {(number|Array<number>)} rentalIDs - rental IDs
* @param {number} profileID - Profile ID to apply -- see /account/profile
* @async
* @returns {Promise<Object>}
*/
async applyPoolProfileToRentals(rentalIDs, profileID) {
let queryString = '';
if (Array.isArray(rentalIDs)) {
queryString = rentalIDs.join(';');
} else {
if (typeof rentalIDs === 'string' || typeof rentalIDs === 'number') {
queryString = rentalIDs
}
}
let endpoint = `/rental/${queryString}/profile/${profileID}`;
let api = this.initAPI(endpoint);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* List pools assigned to one or more rentals.
* @param {(number|Array<number>)} rentalIDs - Rental IDs
* @async
* @returns {Promise<Object>}
*/
async getPoolsByRentalID(rentalIDs) {
let queryString = '';
if (Array.isArray(rentalIDs)) {
queryString = rentalIDs.join(';');
} else {
if (typeof rentalIDs === 'string' || typeof rentalIDs === 'number') {
queryString = rentalIDs
}
}
let endpoint = `/rental/${queryString}/pool`;
let api = this.initAPI(endpoint);
try {
return (await api.get(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'GET', err)
}
}
/**
* Update a pool on one or more rentals
* @param {(number|Array<number>)} rentalIDs - Rental IDs
* @param {Object} options
* @param {string} options.host - pool host (the part after stratum+tcp://)
* @param {number} options.port - pool port (ex: 3333)
* @param {string} options.user - workername
* @param {string} options.pass - worker password
* @param {number} [options.priority] - 0-4 -- can be passed in after pool/ instead.eg /rig/17/pool/0
* @async
* @returns {Promise<Object>}
*/
async updatePoolOnRentals(rentalIDs, options) {
try {
return await this.addPoolToRentals(rentalIDs, options)
} catch (err) {
throw new Error(err)
}
}
/**
* Add a pool on one or more rentals
* @param {(number|Array<number>)} rentalIDs - Rental IDs
* @param {Object} options
* @param {string} options.host - pool host (the part after stratum+tcp://)
* @param {number} options.port - pool port (ex: 3333)
* @param {string} options.user - workername
* @param {string} options.pass - worker password
* @param {number} [options.priority] - 0-4 -- can be passed in after pool/ instead.eg /rig/17/pool/0
* @async
* @returns {Promise<Object>}
*/
async addPoolToRentals(rentalIDs, options) {
let queryString = '';
if (Array.isArray(rentalIDs)) {
queryString = rentalIDs.join(';');
} else {
if (typeof rentalIDs === 'string' || typeof rentalIDs === 'number') {
queryString = rentalIDs
}
}
let params = {};
if (options) {
for (let opt in options) {
params[opt] = options[opt]
}
}
let endpoint = `/rental/${queryString}/pool`;
let api = this.initAPI(endpoint, params);
try {
return (await api.put(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'PUT', err)
}
}
/**
* Delete a pool on one or more rentals
* @param {(number|Array<number>)} rentalIDs - Rental IDs
* @param {number} priority - 0-4 -- can be passed in after pool/ instead.eg /rig/17/pool/0
* @async
* @return {Promise<Object>}
*/
async deletePoolOnRentals(rentalIDs, priority) {
let queryString = '';
if (Array.isArray(rentalIDs)) {
queryString = rentalIDs.join(';');
} else {
if (typeof rentalIDs === 'string' || typeof rentalIDs === 'number') {
queryString = rentalIDs
}
}
let endpoint = `/rental/${queryString}/pool/${priority}`;
let api = this.initAPI(endpoint);
try {
return (await api.delete(endpoint)).data;
} catch (err) {
throw this.createError(endpoint, 'DELETE', err)
}
}
/* ------------ AXIOS INITIATION ----------- */
/**
* Initialize a new instance of axios with desired endpoint
* @param {string} endpoint - the endpoint you wish to hit WITHOUT THE TRAILING SLASH; ex: /rig/14
* @param {Object} [params] - extra parameters to be passed along to the API
* @param {string} [version='v2'] - specify the mining rig rental api version you want to hit; defaults v2
* @returns {AxiosInstance}
*/
initAPI(endpoint, params, version = v2) {
let nonce = this.generateNonce();
let hmac_digest = this.createHMACSignature(endpoint, nonce, version, params);
if (version === v1) {
params = {...params, nonce}
}
return (
new axios.create({
baseURL: `${this.baseURL}${version}/`,
headers: {
'x-api-key': this.key,
'x-api-sign': hmac_digest,
'x-api-nonce': nonce,
'Access-Control-Allow-Origin': '*',
},
params: params,
paramsSerializer: params => {
return qs.stringify(params, {arrayFormat: 'repeat'})
},
})
)
};
/**
* Create a SHA1 HMAC signature required for every mrr api call (see more at 'https://www.miningrigrentals.com/apidocv2')
* @param {string} endpoint - the endpoint your wish to hit without the trailing slash
* @param {number} nonce - a nonce that increments with each call
* @param {string} [version='v2'] - MRR API version number (which version of the api you want to hit)
* @param {Object} [params] - An object of parameters. Only needed if hitting the v1 API (used for creating the signature)
* @returns {string} hmacSig - the HMAC signature in hex
*/
createHMACSignature(endpoint, nonce, version, params) {
if (version === 'v2') {
const concatString = `${this.key}${nonce}${endpoint}`;
return crypto.createHmac('sha1', this.secret).update(concatString).digest('hex');
} else if (version === 'v1') {
let args = {...params, nonce};
let querystring = qs.stringify(args);
return crypto.createHmac('sha1', this.secret).update(querystring).digest('hex');
}
};
/**
* Generate a nonce needed to build the HMAC signature
* @returns {number} - the current UNIX time + the previous Nonce
*/
generateNonce() {
this.prevNonce += 1;
return this.prevNonce
};
/* ----------------- Utilities ----------------- */
/**
* Utility function to provide users with in depth error messaging for debugging
* @param url - the api endpoint
* @param type - the type of request (GET, POST, PUT, etc)
* @param error - the caught error
* @returns {Error}
*/
createError = (url, type, error) => {
var extraErrorText = "";
if (error && error.response){
if (error.response.status)
extraErrorText += error.response.status + " "
if (error.response.statusText)
extraErrorText += error.response.statusText + " | "
if (error.response.data)
extraErrorText += JSON.stringify(error.response.data)
} else {
extraErrorText = error.toString()
}
return new Error("Unable to " + type + " " + url + ": " + extraErrorText)
}
}
export default MiningRigRentals