Publish Nerbus source snapshot and build documentation
This commit is contained in:
commit
76cccceca7
198 files changed
+338574
No files matched your search
@@ -0,0 +1,116 @@
|
||||
const request = require('postman-request');
|
||||
const base_url = 'https://api.coingecko.com/api/v3/';
|
||||
const Decimal = require('decimal.js');
|
||||
|
||||
function get_coin_list(api_key, cb) {
|
||||
request({ uri: base_url + 'coins/list?include_platform=false' + (api_key == null || api_key == '' ? '' : '&x_cg_demo_api_key=' + api_key), json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, []);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb('No data returned', []);
|
||||
else if (body['status'] != null && body['status']['error_message'] != null && body['status']['error_message'] != '')
|
||||
return cb(body['status']['error_message'], []);
|
||||
else
|
||||
return cb(null, body);
|
||||
});
|
||||
}
|
||||
|
||||
function get_simple_price(id, currency, market_array, api_key, cb) {
|
||||
request({ uri: base_url + `simple/price?ids=${id.toLowerCase()}&vs_currencies=usd${currency == null || currency == '' ? '' : `,${currency}`}` + (api_key == null || api_key == '' ? '' : '&x_cg_demo_api_key=' + api_key), json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, new Decimal('0'), new Decimal('0'));
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb('No data returned', new Decimal('0'), new Decimal('0'));
|
||||
else if (body['status'] != null && body['status']['error_message'] != null && body['status']['error_message'] != '')
|
||||
return cb(body['status']['error_message'], new Decimal('0'), new Decimal('0'));
|
||||
else {
|
||||
try {
|
||||
if (market_array != null) {
|
||||
// multiple currencies need to be combined before return
|
||||
let last_price = new Decimal('0');
|
||||
let last_usd_price = new Decimal('0');
|
||||
let counter = 0;
|
||||
let api_market = null;
|
||||
|
||||
// check if the currency variable is set
|
||||
if (currency != null && currency != '') {
|
||||
// find the market currency in the market_array
|
||||
const base_index = market_array.findIndex(p => p.currency.toLowerCase() == currency.toLowerCase());
|
||||
|
||||
// check if the currency is found in the market_array
|
||||
if (base_index > -1) {
|
||||
// find the api market data row
|
||||
api_market = body[market_array[base_index].coingecko_id.toLowerCase()];
|
||||
}
|
||||
}
|
||||
|
||||
// loop through all api object keys
|
||||
Object.keys(body).forEach(function(key, index, map) {
|
||||
const market_index = market_array.findIndex(p => p.coingecko_id.toLowerCase() == key.toLowerCase());
|
||||
|
||||
// check if the currency is found in the market_array
|
||||
if (market_index > -1) {
|
||||
// determine if the api already returned the market price
|
||||
if (currency != null && currency != '' && body[key][currency.toLowerCase()] != null) {
|
||||
// calculate the market price
|
||||
last_price = last_price.add(new Decimal(market_array[market_index].last_price.toString()).mul(new Decimal((currency == null || currency == '' ? new Decimal('0') : new Decimal(body[key][currency.toLowerCase()].toString())))));
|
||||
} else {
|
||||
// check if the market currency exists in the api data
|
||||
if (api_market != null) {
|
||||
// calculate the currency ratio
|
||||
const ratio = new Decimal(api_market['usd'].toString()).div(body[key]['usd'].toString());
|
||||
|
||||
// calculate the market price
|
||||
last_price = last_price.add(new Decimal(market_array[market_index].last_price.toString()).div(ratio));
|
||||
}
|
||||
}
|
||||
|
||||
// calculate the usd price
|
||||
last_usd_price = last_usd_price.add(new Decimal(market_array[market_index].last_price.toString()).mul(body[key]['usd'].toString()));
|
||||
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
|
||||
// check if the counter is greater than 0
|
||||
if (counter > 0) {
|
||||
// average the market and usd prices
|
||||
last_price = last_price.div(counter.toString());
|
||||
last_usd_price = last_usd_price.div(counter.toString());
|
||||
}
|
||||
|
||||
return cb(null, last_price, last_usd_price);
|
||||
} else {
|
||||
// single currency
|
||||
return cb(null, (currency == null || currency == '' ? new Decimal('0') : new Decimal(body[id.toLowerCase()][currency.toLowerCase()].toString())), new Decimal(body[id.toLowerCase()]['usd'].toString()));
|
||||
}
|
||||
} catch(err) {
|
||||
return cb('Received unexpected API data response', new Decimal('0'), new Decimal('0'));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
get_coin_data: function (api_key, cb) {
|
||||
get_coin_list(api_key, function (err, coin_list) {
|
||||
return cb(err, coin_list);
|
||||
});
|
||||
},
|
||||
get_market_prices: function (id, currency, api_key, cb) {
|
||||
get_simple_price(id, currency, null, api_key, function (err, last_price, last_usd) {
|
||||
if (last_price == null)
|
||||
console.log(`Error: "${currency}" is not a valid coingecko api currency`);
|
||||
|
||||
return cb(err, last_price, last_usd);
|
||||
});
|
||||
},
|
||||
get_avg_market_prices: function (id, currency, market_array, api_key, cb) {
|
||||
get_simple_price(id, currency, market_array, api_key, function (err, last_price, last_usd) {
|
||||
if (last_price == null)
|
||||
console.log(`Error: "${currency}" is not a valid coingecko api currency`);
|
||||
|
||||
return cb(err, last_price, last_usd);
|
||||
});
|
||||
}
|
||||
};
|
||||
+1063
File diff suppressed because it is too large.
Load diff
+2334
File diff suppressed because it is too large.
Load diff
+1451
File diff suppressed because it is too large.
Load diff
+171
@@ -0,0 +1,171 @@
|
||||
var http = require('http'),
|
||||
https = require('https');
|
||||
|
||||
var Client = function(opts) {
|
||||
this.opts = opts || {};
|
||||
this.http = this.opts.ssl ? https : http;
|
||||
};
|
||||
|
||||
Client.prototype.call = function(method, params, callback, errback, path) {
|
||||
var time = Date.now();
|
||||
var requestJSON;
|
||||
|
||||
if (Array.isArray(method)) {
|
||||
// multiple rpc batch call
|
||||
requestJSON = [];
|
||||
|
||||
method.forEach(function(batchCall, i) {
|
||||
requestJSON.push({
|
||||
id: time + '-' + i,
|
||||
method: batchCall.method,
|
||||
params: batchCall.params
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// single rpc call
|
||||
requestJSON = {
|
||||
id: time,
|
||||
method: method,
|
||||
params: params
|
||||
};
|
||||
}
|
||||
|
||||
// first we encode the request into JSON
|
||||
var requestJSON = JSON.stringify(requestJSON);
|
||||
|
||||
// prepare request options
|
||||
var requestOptions = {
|
||||
host: this.opts.host,
|
||||
port: this.opts.port,
|
||||
method: 'POST',
|
||||
path: path || '/',
|
||||
headers: {
|
||||
'Host': this.opts.host,
|
||||
'Content-Length': requestJSON.length
|
||||
},
|
||||
agent: false,
|
||||
rejectUnauthorized: this.opts.ssl && this.opts.sslStrict !== false
|
||||
};
|
||||
|
||||
if (this.opts.ssl && this.opts.sslCa) {
|
||||
requestOptions.ca = this.opts.sslCa;
|
||||
}
|
||||
|
||||
// use HTTP auth if user and password set
|
||||
if (this.opts.username && this.opts.password)
|
||||
requestOptions.auth = this.opts.username + ':' + this.opts.password;
|
||||
|
||||
// now make a request to the server
|
||||
var cbCalled = false
|
||||
var request = this.http.request(requestOptions);
|
||||
|
||||
// start request timeout timer
|
||||
var reqTimeout = setTimeout(function() {
|
||||
if (cbCalled)
|
||||
return;
|
||||
|
||||
cbCalled = true;
|
||||
request.abort();
|
||||
|
||||
var err = new Error('ETIMEDOUT');
|
||||
|
||||
err.code = 'ETIMEDOUT';
|
||||
errback(err);
|
||||
}, this.opts.timeout || 30000);
|
||||
|
||||
// set additional timeout on socket in case of remote freeze after sending headers
|
||||
request.setTimeout(this.opts.timeout || 30000, function() {
|
||||
if (cbCalled)
|
||||
return;
|
||||
|
||||
cbCalled = true;
|
||||
request.abort();
|
||||
|
||||
var err = new Error('ESOCKETTIMEDOUT');
|
||||
|
||||
err.code = 'ESOCKETTIMEDOUT';
|
||||
errback(err);
|
||||
});
|
||||
|
||||
request.on('error', function(err) {
|
||||
if (cbCalled)
|
||||
return;
|
||||
|
||||
cbCalled = true;
|
||||
clearTimeout(reqTimeout);
|
||||
errback(err);
|
||||
});
|
||||
|
||||
request.on('response', function(response) {
|
||||
clearTimeout(reqTimeout);
|
||||
|
||||
// we need to buffer the response chunks in a nonblocking way
|
||||
var buffer = '';
|
||||
|
||||
response.on('data', function(chunk) {
|
||||
buffer = buffer + chunk;
|
||||
});
|
||||
|
||||
// when all the responses are finished, we decode the JSON and
|
||||
// depending on whether it's got a result or an error, we call
|
||||
// emitSuccess or emitError on the promise.
|
||||
response.on('end', function() {
|
||||
var err;
|
||||
|
||||
if (cbCalled)
|
||||
return;
|
||||
|
||||
cbCalled = true;
|
||||
|
||||
try {
|
||||
var decoded = JSON.parse(buffer);
|
||||
} catch (e) {
|
||||
if (response.statusCode !== 200) {
|
||||
err = new Error('Invalid params, response status code: ' + response.statusCode);
|
||||
err.code = -32602;
|
||||
errback(err);
|
||||
} else {
|
||||
err = new Error('Problem parsing JSON response from server');
|
||||
err.code = -32603;
|
||||
errback(err);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(decoded))
|
||||
decoded = [decoded];
|
||||
|
||||
// iterate over each response, normally there will be just one
|
||||
// unless a batch rpc call response is being processed
|
||||
decoded.forEach(function(decodedResponse, i) {
|
||||
if (decodedResponse.hasOwnProperty('error') && decodedResponse.error != null) {
|
||||
if (errback) {
|
||||
err = new Error(decodedResponse.error.message || '');
|
||||
|
||||
if (decodedResponse.error.code)
|
||||
err.code = decodedResponse.error.code;
|
||||
|
||||
errback(err);
|
||||
}
|
||||
} else if (decodedResponse.hasOwnProperty('result')) {
|
||||
if (callback)
|
||||
callback(decodedResponse.result, response.headers);
|
||||
} else {
|
||||
if (errback) {
|
||||
err = new Error(decodedResponse.error.message || '');
|
||||
|
||||
if (decodedResponse.error.code)
|
||||
err.code = decodedResponse.error.code;
|
||||
|
||||
errback(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
request.end(requestJSON);
|
||||
};
|
||||
|
||||
module.exports.Client = Client;
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
/**
|
||||
* The Locale Module reads the locale settings and provides
|
||||
* this information to the other modules
|
||||
*/
|
||||
|
||||
exports.localization = {
|
||||
// global
|
||||
"difficulty": "Difficulty",
|
||||
"network": "Network",
|
||||
"masternodecount": "Masternodes",
|
||||
"height": "Height",
|
||||
"timestamp": "Timestamp",
|
||||
"size": "Size",
|
||||
"transactions": "Transactions",
|
||||
"total_sent": "Total Sent",
|
||||
"total_received": "Total Received",
|
||||
"confirmations": "Confirmations",
|
||||
"total": "Total",
|
||||
"total_top_100": "Top 1-100 Total",
|
||||
"bits": "Bits",
|
||||
"nonce": "Nonce",
|
||||
"new_coins": "New Coins",
|
||||
"proof_of_stake": "PoS",
|
||||
"hidden_address": "Hidden Address",
|
||||
"hidden_sender": "Hidden Sender",
|
||||
"hidden_recipient": "Hidden Recipient",
|
||||
"unknown_address": "Unknown Address",
|
||||
"unknown_sender": "Unknown Sender",
|
||||
"unknown_recipient": "Unknown Recipient",
|
||||
"last_updated": "Last Updated",
|
||||
"initial_index_alert": "Blockchain data is currently being synchronized. You may browse the site during this time, but keep in mind that data may not yet be fully accurate and some functionality may not work until synchronization is complete.",
|
||||
"please_wait": "Please wait",
|
||||
"creating_initial_entry": "Creating initial {1} entry",
|
||||
"entry_created_successfully": "Initial {1} entry for {2} created successfully",
|
||||
"method_disabled": "This method is disabled",
|
||||
"compiling_css": "Compiling CSS",
|
||||
"installing_module": "Installing {1} module",
|
||||
"restoring_backup": "Restoring backup",
|
||||
"extracting_backup_files": "Extracting backup files",
|
||||
"explorer_shutting_down": "Explorer shutting down",
|
||||
"initializing_database": "Initializing database",
|
||||
"downloading_newest_explorer_code": "Downloading newest explorer code",
|
||||
"check_outdated_packages": "Checking for outdated packages",
|
||||
"updating_explorer_packages": "Updating out-of-date explorer packages",
|
||||
"checking_webserver_running": "Checking if webserver is running",
|
||||
"reloading_explorer": "Reloading the explorer",
|
||||
"email_sent_successfully": "Email sent successfully",
|
||||
"check_console": "Check your console",
|
||||
"process_aborted": "Process aborted",
|
||||
"nothing_was_deleted": "Nothing was deleted",
|
||||
"nothing_was_restored": "Nothing was restored",
|
||||
|
||||
// prompts
|
||||
"are_you_sure": "Are you sure you want to do this? [y/n]",
|
||||
"short_yes": "y",
|
||||
"long_yes": "yes",
|
||||
|
||||
// menu items
|
||||
"menu_explorer": "Explorer",
|
||||
"menu_api": "API",
|
||||
"menu_markets": "Markets",
|
||||
"menu_richlist": "Rich List",
|
||||
"menu_reward": "Reward",
|
||||
"menu_movement": "Movement",
|
||||
"menu_node": "Nodes",
|
||||
"menu_network": "Network",
|
||||
"menu_claim_address": "Claim Address",
|
||||
"menu_orphans": "Orphaned Blocks",
|
||||
|
||||
// explorer view
|
||||
"ex_title": "{1} Block Explorer",
|
||||
"ex_description": "A listing of all verified {1} transactions",
|
||||
"ex_search_title": "Search",
|
||||
"ex_search_button": "Search",
|
||||
"ex_search_message": "Search by block height, block hash, tx hash or address",
|
||||
"ex_error": "Error",
|
||||
"ex_warning": "Warning",
|
||||
"ex_search_error": "Search found no results.",
|
||||
"ex_latest_transactions": "Latest Transactions",
|
||||
"ex_summary": "Block Summary",
|
||||
"ex_supply": "Coin Supply",
|
||||
"ex_block": "Block",
|
||||
|
||||
// transaction view
|
||||
"tx_title": "{1} Transaction Details",
|
||||
"tx_description": "Viewing tx data from {1} block # {2}",
|
||||
"tx_block_hash": "Block Hash",
|
||||
"tx_recipients": "Recipients",
|
||||
"tx_contributors": "Contributor(s)",
|
||||
"tx_hash": "Tx Hash",
|
||||
"tx_address": "Address",
|
||||
"tx_nonstandard": "NONSTANDARD TX",
|
||||
"view_raw_tx_data": "View Raw Transaction Data",
|
||||
"view_block": "View Block",
|
||||
|
||||
// block view
|
||||
"block_title": "{1} Block Details",
|
||||
"block_description": "Viewing block data from {1} block # {2}",
|
||||
"block_previous": "Previous Block",
|
||||
"block_next": "Next Block",
|
||||
"block_genesis": "GENESIS",
|
||||
"view_raw_block_data": "View Raw Block Data",
|
||||
"view_tx": "View Transaction",
|
||||
|
||||
// error view
|
||||
"error_title": "{1} Block Explorer Error",
|
||||
"error_description": "The page you are looking for cannot be found",
|
||||
"error_description_alt": "An error occurred which prevented the page from loading correctly",
|
||||
"error_not_found": "Not Found",
|
||||
|
||||
// address view
|
||||
"a_title": "{1} Wallet Address Details",
|
||||
"a_description": "Viewing balance and transaction data from {1} address {2}",
|
||||
"a_menu_showing": "Showing",
|
||||
"a_menu_txs": "transactions",
|
||||
"a_menu_all": "All",
|
||||
"a_qr": "QR Code",
|
||||
|
||||
// masternode view
|
||||
"mn_title": "{1} Masternodes",
|
||||
"mn_description": "A listing of all masternodes known to be active on the {1} network",
|
||||
"mn_masternode_list": "Masternode List",
|
||||
|
||||
// movement view
|
||||
"move_title": "{1} Coin Movements",
|
||||
"move_description": "A listing of larger movements where {1} or more {2} coins were sent in a single transaction",
|
||||
|
||||
// richlist view
|
||||
"rl_title": "Top {1} Coin Holders",
|
||||
"rl_description": "A listing of the richest {1} wallet addresses and breakdown of the current coin distribution",
|
||||
"rl_received_coins": "Top 100 - Received Coins",
|
||||
"rl_current_balance": "Top 100 - Current Balance",
|
||||
"rl_received": "Received",
|
||||
"rl_balance": "Balance",
|
||||
"rl_wealth": "Wealth Distribution",
|
||||
"rl_top25": "Top 1-25",
|
||||
"rl_top50": "Top 26-50",
|
||||
"rl_top75": "Top 51-75",
|
||||
"rl_top100": "Top 76-100",
|
||||
"rl_hundredplus": "101+",
|
||||
|
||||
// network view
|
||||
"net_title": "{1} Network Peers",
|
||||
"net_description": "A listing of {1} network peers that have connected to the explorer node in the last 24 hours",
|
||||
"net_addnodes": "Add Nodes",
|
||||
"net_connections": "Connections",
|
||||
"net_address": "Address",
|
||||
"net_protocol": "Protocol",
|
||||
"net_subversion": "Sub-version",
|
||||
"net_country": "Country",
|
||||
|
||||
// api view
|
||||
"api_title": "{1} Public API",
|
||||
"api_description": "A listing of public API endpoints for retrieving {1} coin data from the network without the need for a local wallet",
|
||||
"api_documentation": "API Documentation",
|
||||
"api_calls": "API Calls",
|
||||
"api_getnetworkhashps": "Returns the current network hashrate. (hash/s)",
|
||||
"api_getdifficulty": "Returns the current difficulty.",
|
||||
"api_getconnectioncount": "Returns the number of connections the block explorer has to other nodes.",
|
||||
"api_getmasternodelist": "Returns the complete list of masternodes on the network.",
|
||||
"api_getmasternodecount": "Returns the total number of masternodes on the network.",
|
||||
"api_getvotelist": "Returns the current vote list.",
|
||||
"api_getblockcount": "Returns the number of blocks currently in the block chain.",
|
||||
"api_getblockhash": "Returns the hash of the block at [index]; index 0 is the genesis block.",
|
||||
"api_getblock": "Returns information about the block with the given hash.",
|
||||
"api_getrawtransaction": "Returns raw transaction representation for given transaction id. decrypt can be set to 0(false) or 1(true).",
|
||||
"api_getmaxmoney": 'Returns the maximum possible money supply.',
|
||||
"api_getmaxvote": 'Returns the maximum allowed vote for the current phase of voting.',
|
||||
"api_getvote": 'Returns the current block reward vote setting.',
|
||||
"api_getphase": 'Returns the current voting phase (\'Mint\', \'Limit\' or \'Sustain\').',
|
||||
"api_getreward": 'Returns the current block reward, which has been decided democratically in the previous round of block reward voting.',
|
||||
"api_getsupply": 'Returns the current money supply.',
|
||||
"api_getnextrewardestimate": 'Returns an estimate for the next block reward based on the current state of decentralized voting.',
|
||||
"api_getnextrewardwhenstr": 'Returns a string describing how long until the votes are tallied and the next block reward is computed.',
|
||||
|
||||
// markets view
|
||||
"mkt_title": "{1} Market Details",
|
||||
"mkt_description": "Viewing {1} market data for the {2} exchange",
|
||||
"mkt_hours": "24 hours",
|
||||
"mkt_view_chart": "View 24 hour summary",
|
||||
"mkt_view_summary": "View 24 hour chart",
|
||||
"mkt_no_chart": "Chart data is not available via markets API",
|
||||
"mkt_high": "High",
|
||||
"mkt_low": "Low",
|
||||
"mkt_volume": "Volume",
|
||||
"mkt_top_bid": "Top Bid",
|
||||
"mkt_top_ask": "Top Ask",
|
||||
"mkt_last": "Last Price",
|
||||
"mkt_yesterday": "Yesterday",
|
||||
"mkt_change": "Change",
|
||||
"mkt_sell_orders": "Sell Orders",
|
||||
"mkt_buy_orders": "Buy Orders",
|
||||
"mkt_price": "Price",
|
||||
"mkt_amount": "Amount",
|
||||
"mkt_total": "Total",
|
||||
"mkt_trade_history": "Trade History",
|
||||
"mkt_type": "Type",
|
||||
"mkt_time_stamp": "Time Stamp",
|
||||
"mkt_select": "Market Select",
|
||||
"mkt_unexpected_api_data": "Received unexpected API data response",
|
||||
|
||||
// claim address view
|
||||
"claim_title": "{1} Wallet Address Claim",
|
||||
"claim_description": "Verify ownership of your {1} wallet address and set a custom display name in the explorer",
|
||||
|
||||
// orphans view
|
||||
"orphan_title": "{1} Orphaned Blocks",
|
||||
"orphan_description": "A listing of valid blocks that have been orphaned and do not belong to the main blockchain",
|
||||
"orphan_block_list": "Orphaned Block List",
|
||||
"orphan_block_hash": "Orphaned Block Hash",
|
||||
"orphan_actual_block": "Actual Block",
|
||||
"orphan_prev_block": "Previous Block",
|
||||
"orphan_next_block": "Next Block",
|
||||
"view_orphan": "View Orphaned Block",
|
||||
|
||||
// heavycoin rewards view
|
||||
"heavy_title": "{1} Reward/Voting Details",
|
||||
"heavy_description": "Viewing {1} voting data and coin reward change details",
|
||||
"heavy_reward_voting_info": "Reward/voting information",
|
||||
"heavy_vote": "Vote",
|
||||
"heavy_cap": "Coin Cap",
|
||||
"heavy_phase": "Phase",
|
||||
"heavy_maxvote": "Max Vote",
|
||||
"heavy_reward": "Reward",
|
||||
"heavy_current": "Current Reward",
|
||||
"heavy_estnext": "Est. Next",
|
||||
"heavy_changein": "Reward change in approximately",
|
||||
"heavy_key": "Key",
|
||||
"heavy_lastxvotes": "Last 20 votes",
|
||||
|
||||
// sync
|
||||
"script_launched": "Script launched with pid",
|
||||
"stopping_sync_process": "Stopping sync process",
|
||||
"finding_earliest_orphan": "Finding the earliest orphaned blockindex",
|
||||
"calculating_market_price": "Calculating market price",
|
||||
"syncing_blocks": "Syncing blocks",
|
||||
"syncing_peers": "Syncing peers",
|
||||
"syncing_masternodes": "Syncing masternodes",
|
||||
"syncing_markets": "Syncing markets",
|
||||
"checking_blocks": "Checking blocks",
|
||||
"calculating_tx_count": "Calculating tx count",
|
||||
"finding_last_blockindex": "Finding last blockindex",
|
||||
"path_cannot_be_found": "'{1}' cannot be found",
|
||||
|
||||
// settings
|
||||
"deprecated_setting": "Deprecated setting '{1}' has been mapped to the new location '{2}'. Be sure to make this change in settings.json as the old setting will be removed in a future release",
|
||||
"unknown_setting": "Unknown setting '{1}' has been ignored",
|
||||
"missing_setting": "Setting '{1}' is missing. Loading default value",
|
||||
"continuing_using_defaults": "Continuing using defaults",
|
||||
"missing_settings_file": "The {1} file is missing",
|
||||
"error_processing_settings": "There was an error processing the {1} file",
|
||||
|
||||
// locale
|
||||
"locale_lowercase_req": "Locale strings must start with a lowercase character",
|
||||
"locale_unknown_string": "Unknown Locale String",
|
||||
"locale_not_exists": "This setting doesn't exist or was removed"
|
||||
};
|
||||
|
||||
exports.reloadLocale = function reloadLocale(locale) {
|
||||
const fs = require('fs');
|
||||
const jsonminify = require('jsonminify');
|
||||
const localeFilename = './' + locale;
|
||||
let localeStr;
|
||||
|
||||
try {
|
||||
// read the settings sync
|
||||
localeStr = fs.readFileSync(localeFilename).toString();
|
||||
} catch(e) {
|
||||
console.warn(`${exports.localization.missing_settings_file.replace('{1}', locale.split('/')[1])}. ${exports.localization.continuing_using_defaults}`);
|
||||
}
|
||||
|
||||
var lsettings;
|
||||
|
||||
// try to parse the settings
|
||||
try {
|
||||
if (localeStr) {
|
||||
localeStr = jsonminify(localeStr).replace(",]","]").replace(",}","}");
|
||||
lsettings = JSON.parse(localeStr);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(`${exports.localization.error_processing_settings.replace('{1}', locale.split('/')[1])}: ${e.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// loop through the settings
|
||||
for (var i in lsettings) {
|
||||
// test if the setting start with a low character
|
||||
if (i.charAt(0).search("[a-z]") !== 0)
|
||||
console.warn(`${lsettings.locale_lowercase_req}: '${i}'`);
|
||||
|
||||
if (exports.localization[i] !== undefined) {
|
||||
// we know this setting, so we overwrite it
|
||||
exports.localization[i] = lsettings[i];
|
||||
} else {
|
||||
// this setting is unknown. output a warning and discard it
|
||||
console.warn(`${lsettings.locale_unknown_string}: '${i}'. ${lsettings.locale_not_exists}`);
|
||||
}
|
||||
}
|
||||
|
||||
return exports.localization;
|
||||
};
|
||||
@@ -0,0 +1,194 @@
|
||||
const request = require('postman-request');
|
||||
const base_url = 'https://dexomy.com/api/v2/';
|
||||
const market_url_template = 'https://dexomy.com/exchange/dashboard?coin_pair={coin}_{base}';
|
||||
|
||||
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
|
||||
const rateLimitLib = require('../ratelimit');
|
||||
const rateLimit = new rateLimitLib.RateLimit(1, 2000, false);
|
||||
|
||||
function get_summary(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + 'get-pair-list/';
|
||||
|
||||
// NOTE: no need to pause here because this is the first api call
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null, null);
|
||||
else if (!body.success)
|
||||
return cb((body.message != null ? body.message : api_error_msg), null, null);
|
||||
else {
|
||||
try {
|
||||
// find the correct coin pair record
|
||||
const pairObject = body.data.find(item => item.coin_pair_name === `${coin}/${exchange}`);
|
||||
|
||||
if (pairObject) {
|
||||
const coindata = {
|
||||
parent_coin_id: pairObject.parent_coin_id,
|
||||
child_coin_id: pairObject.child_coin_id
|
||||
};
|
||||
|
||||
const summary = {
|
||||
'high': parseFloat(pairObject.high) || 0,
|
||||
'low': parseFloat(pairObject.low) || 0,
|
||||
'volume': parseFloat(pairObject.volume) || 0,
|
||||
'last': parseFloat(pairObject.last_price) || 0,
|
||||
'change': parseFloat(pairObject.price_change) || 0
|
||||
};
|
||||
|
||||
return cb(null, summary, coindata);
|
||||
} else
|
||||
return cb(api_error_msg, null, null);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function get_trades(parent_coin_id, child_coin_id, api_error_msg, cb) {
|
||||
const req_url = `${base_url}get-exchange-market-trades?base_coin_id=${parent_coin_id}&trade_coin_id=${child_coin_id}`;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (!body.success)
|
||||
return cb((body.message != null ? body.message : api_error_msg), null);
|
||||
else {
|
||||
try {
|
||||
let trades = [];
|
||||
|
||||
for (let t = 0; t < body.data.transactions.data.length; t++) {
|
||||
// convert the string timestamp to iso time and then to a unix timestamp
|
||||
const isoTime = body.data.transactions.data[t].time.replace(' ', 'T') + 'Z';
|
||||
const unixTimestamp = Math.floor(new Date(isoTime).getTime() / 1000);
|
||||
|
||||
trades.push({
|
||||
ordertype: body.data.transactions.data[t].price_order_type.toString().toUpperCase(),
|
||||
price: parseFloat(body.data.transactions.data[t].price) || 0,
|
||||
quantity: parseFloat(body.data.transactions.data[t].amount) || 0,
|
||||
timestamp: unixTimestamp
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, trades);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_orders(parent_coin_id, child_coin_id, api_error_msg, cb) {
|
||||
const req_url = `${base_url}get-exchange-all-orders?base_coin_id=${parent_coin_id}&trade_coin_id=${child_coin_id}`;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null, null);
|
||||
else if (!body.success)
|
||||
return cb((body.message != null ? body.message : api_error_msg), null, null);
|
||||
else {
|
||||
try {
|
||||
let buys = [];
|
||||
let sells = [];
|
||||
|
||||
for (let b = 0; b < body.data.buy_orders.data.length; b++) {
|
||||
buys.push({
|
||||
price: parseFloat(body.data.buy_orders.data[b].price) || 0,
|
||||
quantity: parseFloat(body.data.buy_orders.data[b].amount) || 0
|
||||
});
|
||||
}
|
||||
|
||||
for (let s = 0; s < body.data.sell_orders.data.length; s++) {
|
||||
sells.push({
|
||||
price: parseFloat(body.data.sell_orders.data[s].price) || 0,
|
||||
quantity: parseFloat(body.data.sell_orders.data[s].amount) || 0
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, buys, sells);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_chartdata(parent_coin_id, child_coin_id, api_error_msg, cb) {
|
||||
const end = Date.now();
|
||||
const start = end - 86400000;
|
||||
const req_url = `${base_url}get-exchange-chart-data?base_coin_id=${parent_coin_id}&trade_coin_id=${child_coin_id}&interval=15`;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function(error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object' || typeof body == 'string' || body instanceof String)
|
||||
return cb(api_error_msg, null);
|
||||
else if (!body.success)
|
||||
return cb((body.message != null ? body.message : api_error_msg), null);
|
||||
else {
|
||||
try {
|
||||
let chartdata = [];
|
||||
|
||||
for (let c = 0; c < body.data.length; c++) {
|
||||
// only take values more recent than the last 24 hours
|
||||
if ((body.data[c].time * 1000) > start)
|
||||
chartdata.push([
|
||||
parseInt(body.data[c].time * 1000),
|
||||
parseFloat(body.data[c].open) || 0,
|
||||
parseFloat(body.data[c].high) || 0,
|
||||
parseFloat(body.data[c].low) || 0,
|
||||
parseFloat(body.data[c].close) || 0
|
||||
]);
|
||||
}
|
||||
|
||||
return cb(null, chartdata);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
market_name: 'Dexomy',
|
||||
market_logo: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACbVBMVEUAAAAoTTU2RzdBISEnAQQjIBkpGxolDg9bMi6sTD4nDhlTGyAVTy4QDwzl5uBbNjEUSCllcnIgJR0AFRcAEhYLNyK5KSA8KCgHPCU/TkkuJCUMHhy8q6eOlpUvNDMSMx1IVE1qXVJCWk8zRTGZeGSKk2/GnIFx0ZRQgl2Nonppl3geWy9WV1aOeXusSz4iOyFcOzMxRzI6KCWFiYmIMjKhMDCHeGuMZlYgAQM3OThXISe5pIC3wbVQZU5aOzNlZFdigH9sbGegMixKBAjEYlBBDQ1ZaE0dNiynSzhXGB5EFBaMkZJhX2EcERVkMDGhb2sdExgoQCJKJh9sGxtgPz+OEhM0Py2jamYKExNiNyo2AAdABwJjKiVZWVeOTj8bXzm4Tjk3STd3Oi0mKi0KOiosJScrMSQ3KiN2JyMANiJZHB9NKhobFBWOEBRqCxRVDxMKDxN9BhMBCgpKAAUuAAKrxY6DsXpkc3aAqnOTdHN/unHcgXBla255mWtjZmFLa19SjFokhFmPlFTCWVLNcU5HTEhHRUaNYENveUKTZEIjSUKfaEGQZkC4Uz01YjtkdzokZzo9YzpXOzrpSzk7VDg3NDjJPze0SjZMPjVvODJvSDEVPjFVNTGUNjBNMjCGNy+2Ly8pPi3XMS1RKi1eSiykSytAPisfMCvZJykkQCh+NCg+OCU4RSRKMSQ6MyMeICJYMCGsHiE5FyEAKB9AJh9SJB9dQh0AOx2gLB0vLB0PJR0zHR0hHRx8GhuJEBtuDxsCFhhsHReiDxefGxYgDhROABQALhMCIBMoFxIrGRBTBg0tBg13AAumAAZhAADJFxIcAAAAXXRSTlMA/hMI1UAcD/z6+vf29PLy8vHx8fHw8O/v7e3t7Onp5uXf3tzVzMrJx8XFxcG/v7+9vby7u7m4trW0tKupqKelnpyclZCPjoyBfHp5dnNubGVXV1FORUA+PTAvJgb/bIgPAAABGklEQVQY0w3HQ2IDABAAwI1r27Zt23Zss7Zt27Zt803N3AaS1bxdbR1tHJz8fXyjsgEAU1ez1kTYO+vt3DkR89MASgpMqllfjEf+WMsgQ5AIgMLZrf5JN4nsq4Wlka4gHED+RT/l2znTkzj99IzQzIH05pWq7XdkYVFcI/OXW58CWfH2HTfGSfrosLLxn9NFFwDwu1xXQqtPsmY5PAHTAlAxlQ9UqoIiiUNmf9wtG0KCcvn+aGu4R0Bg6dvL/YQV6HlF0NvMsEhscIXkVkwOhegty+s5o+Jct9rzqY0jRB5A7JBIRBdSGgZ6hl9p1igApClvhjDf3k06lB0jMgAgBP+5yz0QSmUSWl+q/JF4VS0dbV0NAxVzd4z8/2atXThWWOQQAAAAAElFTkSuQmCC',
|
||||
market_url_template: market_url_template,
|
||||
market_url_case: 'u',
|
||||
get_data: function(settings, cb) {
|
||||
get_summary(settings.coin, settings.exchange, settings.api_error_msg, function(summary_error, stats, coindata) {
|
||||
if (summary_error == null) {
|
||||
get_orders(coindata.parent_coin_id, coindata.child_coin_id, settings.api_error_msg, function(order_error, buys, sells) {
|
||||
if (order_error == null) {
|
||||
get_trades(coindata.parent_coin_id, coindata.child_coin_id, settings.api_error_msg, function(trade_error, trades) {
|
||||
if (trade_error == null) {
|
||||
get_chartdata(coindata.parent_coin_id, coindata.child_coin_id, settings.api_error_msg, function (chart_error, chartdata) {
|
||||
if (chart_error == null)
|
||||
return cb(null, {buys: buys, sells: sells, trades: trades, stats: stats, chartdata: chartdata});
|
||||
else
|
||||
return cb(chart_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(trade_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(order_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(summary_error, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,183 @@
|
||||
const request = require('postman-request');
|
||||
const base_url = 'https://api.dex-trade.com/v1/public/';
|
||||
const base_chart_url = 'https://socket.dex-trade.com/graph/';
|
||||
const market_url_template = 'https://dex-trade.com/spot/trading/{coin}{base}';
|
||||
|
||||
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
|
||||
const rateLimitLib = require('../ratelimit');
|
||||
const rateLimit = new rateLimitLib.RateLimit(1, 2000, false);
|
||||
|
||||
function get_summary(coin, exchange, api_error_msg, bid, ask, cb) {
|
||||
const req_url = base_url + 'ticker?pair=' + coin + exchange;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.status == null || body.status == false)
|
||||
return cb((body.error != null ? body.error : api_error_msg), null);
|
||||
else {
|
||||
try {
|
||||
const summary = {
|
||||
'high': parseFloat(body.data.high) || 0,
|
||||
'low': parseFloat(body.data.low) || 0,
|
||||
'volume': parseFloat(body.data.volume_24H) || 0,
|
||||
'bid': parseFloat(bid) || 0,
|
||||
'ask': parseFloat(ask) || 0,
|
||||
'last': parseFloat(body.data.last) || 0
|
||||
};
|
||||
|
||||
return cb(null, summary);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_trades(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + 'trades?pair=' + coin + exchange;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.status == null || body.status == false)
|
||||
return cb((body.error != null ? body.error : api_error_msg), null);
|
||||
else {
|
||||
try {
|
||||
let trades = [];
|
||||
|
||||
for (let t = 0; t < body.data.length; t++) {
|
||||
trades.push({
|
||||
ordertype: body.data[t].type,
|
||||
price: parseFloat(body.data[t].rate) || 0,
|
||||
quantity: parseFloat(body.data[t].volume) || 0,
|
||||
timestamp: parseInt(body.data[t].timestamp)
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, trades);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_orders(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + 'book?pair=' + coin + exchange;
|
||||
|
||||
// NOTE: no need to pause here because this is the first api call
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null, null);
|
||||
else if (body.status == null || body.status == false)
|
||||
return cb((body.error != null ? body.error : api_error_msg), null, null);
|
||||
else {
|
||||
try {
|
||||
let buys = [];
|
||||
let sells = [];
|
||||
|
||||
for (let b = 0; b < body.data.buy.length; b++) {
|
||||
buys.push({
|
||||
price: parseFloat(body.data.buy[b].rate) || 0,
|
||||
quantity: parseFloat(body.data.buy[b].volume) || 0
|
||||
});
|
||||
}
|
||||
|
||||
for (let s = 0; s < body.data.sell.length; s++) {
|
||||
sells.push({
|
||||
price: parseFloat(body.data.sell[s].rate) || 0,
|
||||
quantity: parseFloat(body.data.sell[s].volume) || 0
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, buys, sells);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function get_chartdata(coin, exchange, api_error_msg, cb) {
|
||||
const end = Date.now();
|
||||
const start = end - 86400000;
|
||||
const req_url = base_chart_url + 'hist?t=' + coin + exchange + '&r=15&limit=100';
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object' || typeof body == 'string' || body instanceof String)
|
||||
return cb(api_error_msg, null);
|
||||
else {
|
||||
try {
|
||||
let chartdata = [];
|
||||
|
||||
for (let c = 0; c < body.length; c++) {
|
||||
// only take values more recent than the last 24 hours
|
||||
if ((body[c].time * 1000) > start)
|
||||
chartdata.push([
|
||||
parseInt(body[c].time * 1000),
|
||||
parseFloat(body[c].open) || 0,
|
||||
parseFloat(body[c].high) || 0,
|
||||
parseFloat(body[c].low) || 0,
|
||||
parseFloat(body[c].close) || 0
|
||||
]);
|
||||
}
|
||||
|
||||
return cb(null, chartdata);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
market_name: 'Dex-Trade',
|
||||
market_logo: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAGTGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDIgNzkuMTY0NDYwLCAyMDIwLzA1LzEyLTE2OjA0OjE3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMiAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIyLTA2LTI2VDIxOjEwOjEwLTA2OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIyLTA2LTI2VDIxOjEwOjEwLTA2OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0wNi0yNlQyMToxMDoxMC0wNjowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozNDk5ZmMyYy02YjE0LWNhNDItOWNkYS05ODI4YTQ5MTBiNWMiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpjMzIwOGQ5Yy05M2MyLTNkNDEtYTI0OC0yMjhkMjJkNjhmMmEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxYmVhYmUxYS1hY2FlLTQ1NGEtYThjMy1mMjlkMWNjOTBhZjkiIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDoxYmVhYmUxYS1hY2FlLTQ1NGEtYThjMy1mMjlkMWNjOTBhZjkiIHN0RXZ0OndoZW49IjIwMjItMDYtMjZUMjE6MTA6MTAtMDY6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4yIChXaW5kb3dzKSIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MzQ5OWZjMmMtNmIxNC1jYTQyLTljZGEtOTgyOGE0OTEwYjVjIiBzdEV2dDp3aGVuPSIyMDIyLTA2LTI2VDIxOjEwOjEwLTA2OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMiAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDxwaG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDxyZGY6QmFnPiA8cmRmOmxpPjMyOTEwNDQzQzdFMjhCQTc2QTlCNTRFQkNFMjczMDkwPC9yZGY6bGk+IDwvcmRmOkJhZz4gPC9waG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+3wqdeQAAA4xJREFUOI1NU2tMG1QUPrzpBlRs0U4efTAcK6UMQcu2zsdiZkZndKMjCyoUSCbskel8xB9zw2abmEDiwEQIREVd3TRubqL82SYT1gkUSmlpxyhtN1oeIzCpFST8+Ly96uJNvuSec/J955x7zqVx9x2a8EzSQL+Vhqw2amn7XLVHX96oVGlsaVJlUJKaFVRv0jpe2l3WZKw7XXjlSi/dHBimEecdGhrxEN0e91Hwj2Vyu31kqK41iiWZIEpgIFz9pRsXLlzm9zDWJKxD2StVjdYRJ03PLZFleILI5wvQ/MIilejLu4jiERUvRnyiBIaqWvx3tmp34HsmdLzuFBd6QVfS4/PPkcs9RTQzt0BHjr5nJIrGWmEqFI/n4zVDDTwe7wOBu34/Fu7fx++Li6DIRCYSAUN1zae+ST/RxUtdiqSH0yEUy1hQiJOnGljZlzixu/sGNm/RYnb2Hvr6+3BvbhbllbVMIAoxAhG+MX1bSIaqg00UIeTOQ0fehWPUha/PmtDS2obo+BResiq3COabfQgEAvjhcifzrQVFJGJvacV5Ktr6vJsoEhtVGsywTMM2G/YfeIMTk1PkWJ/9JH/UvCe0aGg4A7P5N+zeW8F8Aqjzt/hJ/KhiRbJOynsMBoP4sfNnaJ8tRlyCBPKsPEgz1UhX5CKW2U3NLTh3/jscOPwWkkUZSJfn/MUSJa7WHHqH92yxWNB9vYdNIgVpshxODiNNpgJFPQS7YxST/gB27ipBZnYBHnlswwrlFWi9QqYmX78JoVAInT918fIj40ScLM/KR6pMyX11H3zIE7V/9iW3c9Sbp6miuqYlbJTuq+TBcBums+dQrNNDkCTh/QtFMjyl2YZr164/GG1axgZoirZfpK86TMrY2CQ4nE78/9jtdpQb9kO3aw+am1vhdI3B4/PC6/Xh1q3bKC2rxPETp7fR0vIyvfn2+x/LMnOhe1GPg4ePwniyHq2t7VxkaelPhhACU/+MUCiS8vJfNbxuWlldZavsnyLvZICU6qLecCAiJhmFRdv5Kh87ZkRbeweustK9vrtsmQY4OTunwOp2eykwPUvUZxmhwMw8ucYnqLTM8EnsGjFfKqK4fz+VgCGGE6MFKXhZv69jcMgWOb8QIuuwk2jI5iSbY4wGbaP0a6+Z6usbnynWlXzBlmRMqlAHMxS5wY25GvfTz+00nTB+tKPHfIP6+gfJ5ZogO+P9DaehK5oI+uoTAAAAAElFTkSuQmCC',
|
||||
market_url_template: market_url_template,
|
||||
market_url_case: 'u',
|
||||
get_data: function(settings, cb) {
|
||||
// ensure coin info is uppercase
|
||||
settings.coin = settings.coin.toUpperCase();
|
||||
settings.exchange = settings.exchange.toUpperCase();
|
||||
|
||||
get_orders(settings.coin, settings.exchange, settings.api_error_msg, function(order_error, buys, sells) {
|
||||
if (order_error == null) {
|
||||
get_trades(settings.coin, settings.exchange, settings.api_error_msg, function(trade_error, trades) {
|
||||
if (trade_error == null) {
|
||||
get_summary(settings.coin, settings.exchange, settings.api_error_msg, (buys == null || buys.length == 0 ? null : buys[0].price), (sells == null || sells.length == 0 ? null : sells[0].price), function(summary_error, stats) {
|
||||
if (summary_error == null) {
|
||||
get_chartdata(settings.coin, settings.exchange, settings.api_error_msg, function (chart_error, chartdata) {
|
||||
if (chart_error == null)
|
||||
return cb(null, {buys: buys, sells: sells, trades: trades, stats: stats, chartdata: chartdata});
|
||||
else
|
||||
return cb(chart_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(summary_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(trade_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(order_error, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,166 @@
|
||||
const request = require('postman-request');
|
||||
const base_url = 'https://api.freiexchange.com/public/';
|
||||
const market_url_template = '{url_prefix}/market/{coin}/{base}';
|
||||
|
||||
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
|
||||
const rateLimitLib = require('../ratelimit');
|
||||
const rateLimit = new rateLimitLib.RateLimit(1, 2000, false);
|
||||
|
||||
function get_summary(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + 'ticker/' + coin;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object') {
|
||||
const summary = {
|
||||
'high': 0,
|
||||
'low': 0,
|
||||
'volume': 0,
|
||||
'volume_btc': 0,
|
||||
'bid': 0,
|
||||
'ask': 0,
|
||||
'last': 0,
|
||||
'change': 0
|
||||
};
|
||||
|
||||
return cb(null, summary);
|
||||
} else if (body.success != null && body.success == false)
|
||||
return cb((body.result != null && body.result.message != null ? body.result.message : api_error_msg), null);
|
||||
else {
|
||||
try {
|
||||
const prefix = body[coin + '_' + exchange];
|
||||
const summary = {
|
||||
'high': parseFloat(prefix[0].high) || 0,
|
||||
'low': parseFloat(prefix[0].low) || 0,
|
||||
'volume': parseFloat(prefix[0].volume24h) || 0,
|
||||
'volume_btc': parseFloat(prefix[0][`volume24h_${exchange.toLowerCase()}`]) || 0,
|
||||
'bid': parseFloat(prefix[0].highestBuy) || 0,
|
||||
'ask': parseFloat(prefix[0].lowestSell) || 0,
|
||||
'last': parseFloat(prefix[0].last) || 0,
|
||||
'change': parseFloat(prefix[0].percent_change_24h) || 0
|
||||
};
|
||||
|
||||
return cb(null, summary);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_trades(coin, exchange, api_error_msg, cb) {
|
||||
// NOTE: Commenting out the trade data since the trades api seems to have been discontinued.
|
||||
// It will be reimplemented again later if the trades api ever returns.
|
||||
|
||||
// const req_url = base_url + 'trades/' + coin + (exchange == 'LTC' ? '/LTC' : '');
|
||||
|
||||
// // pause for 2 seconds before continuing
|
||||
// rateLimit.schedule(function() {
|
||||
// request({uri: req_url, json: true}, function (error, response, body) {
|
||||
// if (error)
|
||||
// return cb(error, null);
|
||||
// else if (body == null || body == '' || typeof body !== 'object')
|
||||
// return cb(api_error_msg, null);
|
||||
// else if (body.success != null && body.success == false)
|
||||
// return cb((body.result != null && body.result.message != null ? body.result.message : api_error_msg), null);
|
||||
// else {
|
||||
// try {
|
||||
// let trades = [];
|
||||
|
||||
// for (let t = 0; t < body.length; t++) {
|
||||
// trades.push({
|
||||
// ordertype: body[t].type,
|
||||
// price: parseFloat(body[t].price) || 0,
|
||||
// quantity: parseFloat(body[t].total_coin) || 0,
|
||||
// timestamp: parseInt(new Date(body[t].time.toString().replace(' ', 'T') + 'Z').getTime() / 1000)
|
||||
// });
|
||||
// }
|
||||
|
||||
// return cb(null, trades);
|
||||
// } catch(err) {
|
||||
// return cb(api_error_msg, null);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
return cb(null, []);
|
||||
}
|
||||
|
||||
function get_orders(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + 'orderbook/' + coin + (exchange == 'LTC' ? '/LTC' : '');
|
||||
|
||||
// NOTE: no need to pause here because this is the first api call
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null, null);
|
||||
else if (body.success != null && body.success == false)
|
||||
return cb((body.result != null && body.result.message != null ? body.result.message : api_error_msg), null, null);
|
||||
else {
|
||||
try {
|
||||
let buys = [];
|
||||
let sells = [];
|
||||
|
||||
for (let b = 0; b < body.BUY.length; b++) {
|
||||
buys.push({
|
||||
price: parseFloat(body.BUY[b].price) || 0,
|
||||
quantity: parseFloat(body.BUY[b].amount) || 0
|
||||
});
|
||||
}
|
||||
|
||||
for (let s = 0; s < body.SELL.length; s++) {
|
||||
sells.push({
|
||||
price: parseFloat(body.SELL[s].price) || 0,
|
||||
quantity: parseFloat(body.SELL[s].amount) || 0
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, buys, sells);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
market_name: 'FreiExchange',
|
||||
market_name_alt: 'FreiXLite',
|
||||
market_logo: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF8WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDIgNzkuMTY0NDYwLCAyMDIwLzA1LzEyLTE2OjA0OjE3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMiAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIyLTA2LTI2VDE2OjU2OjA2LTA2OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIyLTA2LTI2VDE2OjU2OjA2LTA2OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0wNi0yNlQxNjo1NjowNi0wNjowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpjM2IyNDVkYy1kZDI2LWYwNGEtYmIwYS03OWJjNDhmZTQxMzMiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDozZWIzNDAxZC00N2U4LTFmNGUtOGYwMi1kZmMzODg0MzFlZWYiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3NzY1YTNlYi1mMjRlLWVlNGQtOGM0My0xYTY3NjgzYTNkMTAiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjc3NjVhM2ViLWYyNGUtZWU0ZC04YzQzLTFhNjc2ODNhM2QxMCIgc3RFdnQ6d2hlbj0iMjAyMi0wNi0yNlQxNjo1NjowNi0wNjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIxLjIgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDpjM2IyNDVkYy1kZDI2LWYwNGEtYmIwYS03OWJjNDhmZTQxMzMiIHN0RXZ0OndoZW49IjIwMjItMDYtMjZUMTY6NTY6MDYtMDY6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4yIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz48i0LMAAAC2UlEQVQ4jV1T70tTURh+7z0/7tbcKjT7XdC3QIi0yBD6F/rQl8A+SIUlaISETSVGn6QocFaYWhARTpG0MLAvFQZFEbg+ZVAYVnO7cwuderubc2/vOS2VLjzsfe/O87zPOee5ABwABMAOwcBrGFBjikA7k7UhJm/3c+vhVc7vdjDZcIKJvfsYB9Og9XwDcbYTylXzV8AigSCTzZ+FjOelhSg9iELo3xXqvwmRvcNFWBqGALZegCkY0MPlMHKJK4zjLMEmJLhAW0icJbhUI+E159EtvKQcxE4AuQ30c5NbvYqcAsAZQnwdbMPUIromoJQ4Jj3vwfSBxhGTHUOT4YLXh/PhTsw+G0X3+RhmX77Apf5+jHu8GCNiJhzG+UgEberRNLDJMK/o6UNcDKOaFghg3nXw/8eurCRU6ToZDGJGuSD0ADzSAtNC2r+UPX8AsVBAJxrFaerVVHd8HJ2JKLqZDC6MjGCS3iXBcBp8/ub2+vrGo9XVFZDilhMvCuRTKczGZ3D+chDnzp/DdLBVT87ZNsZMU08m312N9+6fKhrMQkpYTqIosDz5adX6MsGJx3S9MPpUO1ICIYBbTX19tcVlDkzJtS0UXBezX7/gD58P0y1BXFHkkWHM5XJoVxzQt5Rk/Hejr+RSy+kzDYeqqvbDIJeP9SH6N2pJ9+MEft+0GfNUZwYG9HkU1Kg3b/En1YuUD7W+GyCiD/GwyWqQ9qeucXEwgnO073RdHbp0jTYJKetzoRBt4wnGGMMU5UJl4QLjrRQEJWHCNS56VWzT/8JDuUgUg5OkGCeK75MUKEUe5d53AF4KMAUJDEkaArqFHEJlj+I6qyKs4kwEhST1WfUfkV8J9qEUfGUAW4lcphyQAJfgFRYEubg4KWRsWU+yivCg6qeEWOri1g2hvx6LeOpDKl0T2EMCfsbgIOMlbcxzso2Jzg4mHrQzfuc6k2ePc7FrNzll+uQ8qwJ/AJ4ryGtYbOHVAAAAAElFTkSuQmCC',
|
||||
market_logo_alt: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF8WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDIgNzkuMTY0NDYwLCAyMDIwLzA1LzEyLTE2OjA0OjE3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMiAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIyLTA2LTI2VDE5OjE0OjMzLTA2OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIyLTA2LTI2VDE5OjE0OjMzLTA2OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMi0wNi0yNlQxOToxNDozMy0wNjowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozYjE4MmI1ZC0zZjU3LWVhNGMtYWFiYS01NmQ0YjVkZWVmNTUiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpiNWRkYTk5MS02MGNlLTc5NGQtOWFlNy04MDcwY2I3NWVjYzkiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDozMjM4NjRhMi0xNjk4LWU0NGUtODA5Yy1iYjc0ZjliM2UyMmEiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjMyMzg2NGEyLTE2OTgtZTQ0ZS04MDljLWJiNzRmOWIzZTIyYSIgc3RFdnQ6d2hlbj0iMjAyMi0wNi0yNlQxOToxNDozMy0wNjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIxLjIgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDozYjE4MmI1ZC0zZjU3LWVhNGMtYWFiYS01NmQ0YjVkZWVmNTUiIHN0RXZ0OndoZW49IjIwMjItMDYtMjZUMTk6MTQ6MzMtMDY6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4yIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4U//lqAAACLUlEQVQ4y42TQUiTYRzGf4GXITsowi4WjIRFXnYLPNRRXSB6M8kCD2J2cAgeJJFEIwRDqA42QmjDTiW2lGkGkRRBGkSktYrUNDZnmOg+9ZvfvqfDNjEx8oWH9/I+//eB/+8hDISBD0AcWIH8GFzagDtJGNyAm+tQOQ98BwzgFTACRMiaR4AZ4DecT8FHgQ5qE8Z/wul14PX+AY+BR8Ay9B1mPKgtqJ4EhoExgHFgHq79z7hZVKSk0ymBZuDsMPAMYA6O5x7Zbrfk9WbkdivtcEgDA9pubdWVzk6F6uslUApib+DYHEACAgJZeXmyUinlTjqRkBmJSJLiNTW63NWl5xUVe4liUHm/pARMWBLI9nhkS9rt79euzyfLspSWJL9fUVBDMKjPpaUSaA2in9rbb/dGImcQmALJ55MkWZK2q6tlR6Oyk0kJ9MLr1dVAIPf7zpfu7oe5pAh2BFJzcyZBU5PMlhZJkm0YEuhBebkuDg0p0NioREGBtVpWNpE0jIW4NIUJywIpGFRa0o7LJUkyp6eVlmT39mrU49GttjZd7+nRUnGxBOlFh6Mr3NEBcbgnkGZnZS0syKqtlR0KyXI6pbo62VVVh671K1yYIoPwCYHkckmFhToKTCbE3gHvASaBH9B+FGNOb+HcHkhPslwn4MZRzAZUjWfxf0qW5zDwDdiCSvMfZTJgYgVO/QJe7u/CGDAKLAIbmftkAvwW3N2FQRP6VqFhEfKXM439a8AfCVrpWXEdq70AAAAASUVORK5CYII=',
|
||||
market_url_template: market_url_template,
|
||||
market_url_case: 'u',
|
||||
market_url: function(settings) {
|
||||
return 'https://' + (settings.exchange.toUpperCase() == 'LTC' ? 'freixlite' : 'freiexchange') + '.com';
|
||||
},
|
||||
isAlt: function(settings) {
|
||||
return settings.exchange.toUpperCase() == 'LTC';
|
||||
},
|
||||
get_data: function(settings, cb) {
|
||||
// ensure coin info is uppercase
|
||||
settings.coin = settings.coin.toUpperCase();
|
||||
settings.exchange = settings.exchange.toUpperCase();
|
||||
|
||||
get_orders(settings.coin, settings.exchange, settings.api_error_msg, function(order_error, buys, sells) {
|
||||
if (order_error == null) {
|
||||
get_trades(settings.coin, settings.exchange, settings.api_error_msg, function(trade_error, trades) {
|
||||
if (trade_error == null) {
|
||||
get_summary(settings.coin, settings.exchange, settings.api_error_msg, function(summary_error, stats) {
|
||||
if (summary_error == null)
|
||||
return cb(null, {buys: buys, sells: sells, trades: trades, stats: stats, chartdata: null});
|
||||
else
|
||||
return cb(summary_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(trade_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(order_error, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
const request = require('postman-request');
|
||||
const base_url = 'https://trade.nestex.one/api/';
|
||||
const market_url_template = 'https://trade.nestex.one/spot/{coin}';
|
||||
|
||||
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
|
||||
const rateLimitLib = require('../ratelimit');
|
||||
const rateLimit = new rateLimitLib.RateLimit(1, 2000, false);
|
||||
|
||||
function get_summary(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + 'cg/tickers/' + coin + '_' + exchange;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.error != null)
|
||||
return cb(body.error, null);
|
||||
else {
|
||||
try {
|
||||
const summary = {
|
||||
'high': parseFloat(body.high) || 0,
|
||||
'low': parseFloat(body.low) || 0,
|
||||
'volume': parseFloat(body.base_volume) || 0,
|
||||
'volume_btc': parseFloat(body.target_volume) || 0,
|
||||
'bid': parseFloat(body.bid) || 0,
|
||||
'ask': parseFloat(body.ask) || 0,
|
||||
'last': parseFloat(body.last_price) || 0
|
||||
};
|
||||
|
||||
return cb(null, summary);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_trades(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + 'cg/tradebook/' + coin + '_' + exchange;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.error != null)
|
||||
return cb(body.error, null);
|
||||
else {
|
||||
try {
|
||||
let trades = [];
|
||||
|
||||
for (let t = 0; t < body.data.length; t++) {
|
||||
trades.push({
|
||||
ordertype: body.data[t].side.toString().toUpperCase(),
|
||||
price: parseFloat(body.data[t].price) || 0,
|
||||
quantity: parseFloat(body.data[t].quantity) || 0,
|
||||
timestamp: parseInt(body.data[t].timestamp) / 1000
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, trades);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_orders(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + 'cg/orderbook/' + coin + '_' + exchange + '?depth=100';
|
||||
|
||||
// NOTE: no need to pause here because this is the first api call
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null, null);
|
||||
else if (body.error != null)
|
||||
return cb(body.error, null, null);
|
||||
else {
|
||||
try {
|
||||
let buys = [];
|
||||
let sells = [];
|
||||
const bids = Object.entries(body.bids);
|
||||
const asks = Object.entries(body.asks);
|
||||
|
||||
for (let b = 0; b < bids.length; b++) {
|
||||
buys.push({
|
||||
price: parseFloat(bids[b][0]) || 0,
|
||||
quantity: parseFloat(bids[b][1]) || 0
|
||||
});
|
||||
}
|
||||
|
||||
for (let a = 0; a < asks.length; a++) {
|
||||
sells.push({
|
||||
price: parseFloat(asks[a][0]) || 0,
|
||||
quantity: parseFloat(asks[a][1]) || 0
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, buys, sells);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
market_name: 'NestEx',
|
||||
market_logo: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAe1BMVEUAAAD/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQX/yQVH+eRqAAAAKXRSTlMArGRHvXthTriLf3FsaFVBMhIGpZqRd1o5JBfRysiyj4RdKSDv4NiehahrJacAAACTSURBVBjTdc1XDsIwEEXRce92nN4rbf8rRApRAAnO37vzMfBHq9T2FfQs0efGFmxu3jvFIYtuSM+Q3HRUYXHHuQHHACXrhb+CQJDMul1MRQCXAdIoCHS8tXGCGosSCoSQIVg+TIUzeicAnubSs2ZjfsyR238UtRC0r0itCOw4kpSxkWYWDqHX3bUcPJwaPhV8hZ+exboH2W57FV0AAAAASUVORK5CYII=',
|
||||
market_url_template: market_url_template,
|
||||
market_url_case: 'u',
|
||||
get_data: function(settings, cb) {
|
||||
get_orders(settings.coin, settings.exchange, settings.api_error_msg, function(order_error, buys, sells) {
|
||||
if (order_error == null) {
|
||||
get_trades(settings.coin, settings.exchange, settings.api_error_msg, function(trade_error, trades) {
|
||||
if (trade_error == null) {
|
||||
get_summary(settings.coin, settings.exchange, settings.api_error_msg, function(summary_error, stats) {
|
||||
if (summary_error == null)
|
||||
return cb(null, {buys: buys, sells: sells, trades: trades, stats: stats, chartdata: null});
|
||||
else
|
||||
return cb(summary_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(trade_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(order_error, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,181 @@
|
||||
const request = require('postman-request');
|
||||
const base_url = 'https://api.nonkyc.io/api/v2';
|
||||
const market_url_template = 'https://nonkyc.io/market/{coin}_{base}';
|
||||
|
||||
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
|
||||
const rateLimitLib = require('../ratelimit');
|
||||
const rateLimit = new rateLimitLib.RateLimit(1, 2000, false);
|
||||
|
||||
function get_summary(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + '/market/getbysymbol/' + coin + '_' + exchange;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.error != null)
|
||||
return cb((body.error.message != null ? body.error.message : api_error_msg), null);
|
||||
else {
|
||||
try {
|
||||
const summary = {
|
||||
'high': parseFloat(body.highPriceNumber) || 0,
|
||||
'low': parseFloat(body.lowPriceNumber) || 0,
|
||||
'volume': parseFloat(body.volumeNumber) || 0,
|
||||
'volume_btc': parseFloat(body.volumeUsdNumber) || 0,
|
||||
'bid': parseFloat(body.bestBidNumber) || 0,
|
||||
'ask': parseFloat(body.bestAskNumber) || 0,
|
||||
'last': parseFloat(body.lastPriceNumber) || 0,
|
||||
'prev': parseFloat(body.yesterdayPriceNumber) || 0,
|
||||
'change': parseFloat(body.changePercentNumber) || 0
|
||||
};
|
||||
|
||||
return cb(null, summary);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_trades(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + '/historical_trades?ticker_id=' + coin + '_' + exchange + '&limit=300';
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.error != null)
|
||||
return cb((body.error.message != null ? body.error.message : api_error_msg), null);
|
||||
else {
|
||||
try {
|
||||
let trades = [];
|
||||
|
||||
for (let t = 0; t < body.length; t++) {
|
||||
trades.push({
|
||||
ordertype: body[t].type,
|
||||
price: parseFloat(body[t].price) || 0,
|
||||
quantity: parseFloat(body[t].base_volume) || 0,
|
||||
timestamp: parseInt(new Date(body[t].trade_timestamp).getTime() / 1000)
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, trades);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_orders(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + '/market/getorderbookbysymbol/' + coin + '_' + exchange;
|
||||
|
||||
// NOTE: no need to pause here because this is the first api call
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null, null);
|
||||
else if (body.error != null)
|
||||
return cb((body.error.message != null ? body.error.message : api_error_msg), null, null);
|
||||
else {
|
||||
try {
|
||||
let buys = [];
|
||||
let sells = [];
|
||||
|
||||
for (let b = 0; b < body.bids.length; b++) {
|
||||
buys.push({
|
||||
price: parseFloat(body.bids[b].numberprice) || 0,
|
||||
quantity: parseFloat(body.bids[b].quantity) || 0
|
||||
});
|
||||
}
|
||||
|
||||
for (let s = 0; s < body.asks.length; s++) {
|
||||
sells.push({
|
||||
price: parseFloat(body.asks[s].numberprice) || 0,
|
||||
quantity: parseFloat(body.asks[s].quantity) || 0
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, buys, sells);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function get_chartdata(coin, exchange, api_error_msg, cb) {
|
||||
const end = Date.now() / 1000;
|
||||
const start = end - 86400;
|
||||
const req_url = base_url + '/market/candles/?symbol=' + coin + '_' + exchange + '&from=' + parseInt(start).toString() + '&to=' + parseInt(end).toString() + '&resolution=15' ;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object' || typeof body == 'string' || body instanceof String)
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.error != null)
|
||||
return cb((body.error.message != null ? body.error.message : api_error_msg), null);
|
||||
else {
|
||||
try {
|
||||
let chartdata = [];
|
||||
|
||||
for (let c = 0; c < body.bars.length; c++) {
|
||||
chartdata.push([
|
||||
parseInt(body.bars[c].time),
|
||||
parseFloat(body.bars[c].open) || 0,
|
||||
parseFloat(body.bars[c].high) || 0,
|
||||
parseFloat(body.bars[c].low) || 0,
|
||||
parseFloat(body.bars[c].close) || 0
|
||||
]);
|
||||
}
|
||||
|
||||
return cb(null, chartdata);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
market_name: 'Nonkyc',
|
||||
market_logo: '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
Line truncated
|
||||
market_url_template: market_url_template,
|
||||
market_url_case: 'u',
|
||||
get_data: function(settings, cb) {
|
||||
get_orders(settings.coin, settings.exchange, settings.api_error_msg, function(order_error, buys, sells) {
|
||||
if (order_error == null) {
|
||||
get_trades(settings.coin, settings.exchange, settings.api_error_msg, function(trade_error, trades) {
|
||||
if (trade_error == null) {
|
||||
get_summary(settings.coin, settings.exchange, settings.api_error_msg, function(summary_error, stats) {
|
||||
if (summary_error == null) {
|
||||
get_chartdata(settings.coin, settings.exchange, settings.api_error_msg, function (chart_error, chartdata) {
|
||||
if (chart_error == null)
|
||||
return cb(null, {buys: buys, sells: sells, trades: trades, stats: stats, chartdata: chartdata});
|
||||
else
|
||||
return cb(chart_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(summary_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(trade_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(order_error, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,182 @@
|
||||
const request = require('postman-request');
|
||||
const base_url = 'https://api.poloniex.com/markets/';
|
||||
const market_url_template = 'https://poloniex.com/trade/{coin}_{base}?type=spot';
|
||||
|
||||
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
|
||||
const rateLimitLib = require('../ratelimit');
|
||||
const rateLimit = new rateLimitLib.RateLimit(1, 2000, false);
|
||||
|
||||
function get_summary(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + coin + '_' + exchange + '/ticker24h';
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.message != null)
|
||||
return cb(body.message, null);
|
||||
else {
|
||||
try {
|
||||
const summary = {
|
||||
'high': parseFloat(body.high) || 0,
|
||||
'low': parseFloat(body.low) || 0,
|
||||
'volume': parseFloat(body.quantity) || 0,
|
||||
'volume_btc': parseFloat(body.amount) || 0,
|
||||
'bid': parseFloat(body.bid) || 0,
|
||||
'ask': parseFloat(body.ask) || 0,
|
||||
'change': parseFloat(body.dailyChange || 0) * 100
|
||||
};
|
||||
|
||||
return cb(null, summary);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_trades(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + coin + '_' + exchange + '/trades';
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.message != null)
|
||||
return cb(body.message, null);
|
||||
else {
|
||||
try {
|
||||
let trades = [];
|
||||
|
||||
for (let t = 0; t < body.length; t++) {
|
||||
trades.push({
|
||||
ordertype: body[t].takerSide,
|
||||
price: parseFloat(body[t].price) || 0,
|
||||
quantity: parseFloat(body[t].quantity) || 0,
|
||||
timestamp: parseInt(new Date(body[t].ts).getTime() / 1000)
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, trades);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_orders(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + coin + '_' + exchange + '/orderBook?limit=50';
|
||||
|
||||
// NOTE: no need to pause here because this is the first api call
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null, null);
|
||||
else if (body.message != null)
|
||||
return cb(body.message, null, null);
|
||||
else {
|
||||
try {
|
||||
let buys = [];
|
||||
let sells = [];
|
||||
|
||||
for (let b = 0; b < body.bids.length; b += 2) {
|
||||
buys.push({
|
||||
price: parseFloat(body.bids[b]) || 0,
|
||||
quantity: parseFloat(body.bids[b + 1]) || 0
|
||||
});
|
||||
}
|
||||
|
||||
for (let s = 0; s < body.asks.length; s += 2) {
|
||||
sells.push({
|
||||
price: parseFloat(body.asks[s]) || 0,
|
||||
quantity: parseFloat(body.asks[s + 1]) || 0
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, buys, sells);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function get_chartdata(coin, exchange, api_error_msg, cb) {
|
||||
const end = Date.now();
|
||||
const start = end - 86400000;
|
||||
const req_url = base_url + coin + '_' + exchange + '/candles?interval=MINUTE_15&limit=96&startTime=' + start.toString() + '&endTime=' + end.toString();
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object' || typeof body == 'string' || body instanceof String)
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.message != null)
|
||||
return cb(body.message, null);
|
||||
else {
|
||||
try {
|
||||
let chartdata = [];
|
||||
|
||||
for (let c = 0; c < body.length; c++)
|
||||
chartdata.push([
|
||||
parseInt(body[c][9]),
|
||||
parseFloat(body[c][2]) || 0,
|
||||
parseFloat(body[c][1]) || 0,
|
||||
parseFloat(body[c][0]) || 0,
|
||||
parseFloat(body[c][3]) || 0
|
||||
]);
|
||||
|
||||
return cb(null, chartdata);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
market_name: 'Poloniex',
|
||||
market_logo: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF8WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDIgNzkuMTY0NDYwLCAyMDIwLzA1LzEyLTE2OjA0OjE3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMiAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTEyLTI1VDIwOjU1OjMzLTA3OjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTEyLTI1VDIwOjU1OjMzLTA3OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMC0xMi0yNVQyMDo1NTozMy0wNzowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMzU0YTQ5My02M2VlLTQ3NDUtOTAyMC1jZjQ5ZTZlODRlNDQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDowZTZlOTkxZi05YTM4LTgxNDAtYTlmMS0yMGU3MThjOTU5YjMiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplNzcwYmQ5Zi1kMzM2LTliNGQtYmVhMC0wOTIwNjZkNDIwZDIiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmU3NzBiZDlmLWQzMzYtOWI0ZC1iZWEwLTA5MjA2NmQ0MjBkMiIgc3RFdnQ6d2hlbj0iMjAyMC0xMi0yNVQyMDo1NTozMy0wNzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIDIxLjIgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDphMzU0YTQ5My02M2VlLTQ3NDUtOTAyMC1jZjQ5ZTZlODRlNDQiIHN0RXZ0OndoZW49IjIwMjAtMTItMjVUMjA6NTU6MzMtMDc6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4yIChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4yAdy4AAACnUlEQVQ4y11TS09TQRid9raFloIY45/wHUNijEsXBjeKC40r3Wrig7b0AS229AVIotISATdiSohuVJDYEkXjxsSFCQlEDSASdvIwo4BiheOZK1x6bTLp3PnOOd/3nZlPOJKpOVs8IUWDXwqPT4p6rxTBkHSn0tKWSEqNq5J7EQgy5iGGcV+DVBw7ucIajRX33exAW6GA8PM8EoURnM/1QzQ2wZVMoZJLhBpxNpdDYmQE4Xwe7fw/QI6NXLGLGXY0hTH2eQalvzM9PbAEgrAFQ6jr7jbFxme+oCocgeIKF8uzU2B/uhW/VlYM0PzCAjR/AO5IM1alNM5//1zFoXQbLKzQmUxJobEXVSr7w6X+flOm7KvX2Hndg8WlJePs8sAAsV5UkWNV3pWxDJoFZ0tcD7wZHzeJnLvTiaPxhL4fm5qElYlcxNJAKK5wbApUsAUX+zrIwOL3H4bAV5Zf4fHB+/ARau9264a6iVUcx5YAFwSzVDMgvD5cuN9nquLdh48QV67CwgS2VHqLbBawlAhc7HtgEng7MQGNAlokAjsF7PH/BOz8cLe2wklnD7e149vyskH+ND+Psmv1SAwOoa73HkQgpLegbQnoJipDoi3QmH1scsqU/TiFT9y6re8nZ2dRRkx5LLZtInuXbnWNvC7/00ETOfD4CXaTgLU148w/NKRfeTVbsahrZDnSzgdzKtMFrK8bwPdT0xStRzkf2cRMySvd2MDpbBds5FSoGXFFY3IPK0BJ33/4ImtS9ISuq1WTTqO4srotQuxechRXWCPNxWOZLDpGRxEaHkacg3Kyp5dzEEI5y3TSMAvnoZZnsXwBgWfDaH/xEkc6M9CabxQFQXMi1vJvTL2b49zYJFV52uY466XyTB9nhVFYZlfcv+GHou2DaH9cAAAAAElFTkSuQmCC',
|
||||
market_url_template: market_url_template,
|
||||
market_url_case: 'u',
|
||||
get_data: function(settings, cb) {
|
||||
// ensure coin info is uppercase
|
||||
settings.coin = settings.coin.toUpperCase();
|
||||
settings.exchange = settings.exchange.toUpperCase();
|
||||
|
||||
get_orders(settings.coin, settings.exchange, settings.api_error_msg, function(order_error, buys, sells) {
|
||||
if (order_error == null) {
|
||||
get_trades(settings.coin, settings.exchange, settings.api_error_msg, function(trade_error, trades) {
|
||||
if (trade_error == null) {
|
||||
get_summary(settings.coin, settings.exchange, settings.api_error_msg, function(summary_error, stats) {
|
||||
if (summary_error == null) {
|
||||
get_chartdata(settings.coin, settings.exchange, settings.api_error_msg, function (chart_error, chartdata) {
|
||||
if (chart_error == null)
|
||||
return cb(null, {buys: buys, sells: sells, trades: trades, stats: stats, chartdata: chartdata});
|
||||
else
|
||||
return cb(chart_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(summary_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(trade_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(order_error, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,144 @@
|
||||
const request = require('postman-request');
|
||||
const base_url = 'https://qutrade.io/api/v1/';
|
||||
const market_url_template = 'https://qutrade.io/en/?market={coin}_{base}';
|
||||
|
||||
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
|
||||
const rateLimitLib = require('../ratelimit');
|
||||
const rateLimit = new rateLimitLib.RateLimit(1, 2000, false);
|
||||
|
||||
function get_summary(coin, exchange, api_error_msg, cb) {
|
||||
const pair_key = coin.toLowerCase() + '_' + exchange.toLowerCase();
|
||||
const req_url = base_url + 'market_data/?pair=' + pair_key;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object' || body.result == null || body.list == null || body.list[pair_key] == null)
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.result !== 'success')
|
||||
return cb(body.info || api_error_msg, null);
|
||||
else {
|
||||
try {
|
||||
const pair_data = body.list[pair_key];
|
||||
|
||||
const summary = {
|
||||
'high': parseFloat(pair_data.high) || 0,
|
||||
'low': parseFloat(pair_data.low) || 0,
|
||||
'volume': parseFloat(pair_data.asset_1_volume) || 0,
|
||||
'volume_btc': parseFloat(pair_data.asset_2_volume) || 0,
|
||||
'bid': parseFloat(pair_data.bid) || 0,
|
||||
'ask': parseFloat(pair_data.ask) || 0,
|
||||
'last': parseFloat(pair_data.price) || 0,
|
||||
'change': parseFloat(pair_data.trend) || 0
|
||||
};
|
||||
|
||||
return cb(null, summary);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_trades(coin, exchange, api_error_msg, cb) {
|
||||
const pair_key = coin.toLowerCase() + '_' + exchange.toLowerCase();
|
||||
const req_url = base_url + 'market_trades/?pair=' + pair_key + '&limit=100';
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object' || body.result == null || body.list == null || body.list.length == 0)
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.result !== 'success')
|
||||
return cb(body.info || api_error_msg, null);
|
||||
else {
|
||||
try {
|
||||
let trades = [];
|
||||
|
||||
for (let t = 0; t < body.list.length; t++) {
|
||||
trades.push({
|
||||
ordertype: body.list[t].side.toString().toUpperCase(),
|
||||
price: parseFloat(body.list[t].price) || 0,
|
||||
quantity: parseFloat(body.list[t].amount) || 0,
|
||||
timestamp: parseInt(body.list[t].timestamp)
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, trades);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_orders(coin, exchange, api_error_msg, cb) {
|
||||
const pair_key = coin.toLowerCase() + '_' + exchange.toLowerCase();
|
||||
const req_url = base_url + 'market_depth/?pair=' + pair_key + '&limit=100';
|
||||
|
||||
// NOTE: no need to pause here because this is the first api call
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object' || body.result == null || body.list == null || body.list[pair_key] == null)
|
||||
return cb(api_error_msg, null, null);
|
||||
else if (body.result !== 'success')
|
||||
return cb(body.info || api_error_msg, null, null);
|
||||
else {
|
||||
try {
|
||||
let buys = [];
|
||||
let sells = [];
|
||||
const pair_data = body.list[pair_key];
|
||||
|
||||
for (let b = 0; b < pair_data.bids.length; b++) {
|
||||
buys.push({
|
||||
price: parseFloat(pair_data.bids[b][0]) || 0,
|
||||
quantity: parseFloat(pair_data.bids[b][1]) || 0
|
||||
});
|
||||
}
|
||||
|
||||
for (let s = 0; s < pair_data.asks.length; s++) {
|
||||
sells.push({
|
||||
price: parseFloat(pair_data.asks[s][0]) || 0,
|
||||
quantity: parseFloat(pair_data.asks[s][1]) || 0
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, buys, sells);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
market_name: 'Qutrade',
|
||||
market_logo: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAhFBMVEUAAAAuSVwvR14vSFwwSF0wSF0wSF0wSF4xR14wR10wSF0vSF0uQ14xR1swSV0vSF0wSF0vSF0vRV4wR10vSF0vSF0xSVsvSV0wSlwxSF0wR1wuSFwwSF0vSV0wSl0vSF4uSFcyRmQuSVg6R2UsRGAxSFwwR1wySlswSF0xSlsyR18uR1sSei0+AAAAKHRSTlMAUg3n3KqdSC+jl0xCHgX89/LPvrOSiYR9d2VbWDwrJhfr2o1rUBQITdwwpgAAAJxJREFUGNNtj0kOgzAUQ02ANMxzmSmdHXr/+zWgZte3sfT99CVj59LkZNG8YDnT9X2X7HHQLW+151p9QhNOwdiadw7ASEaweEzhUCbAaQaWEREnBBTAvOUCLgGGqGh63lyv3ByAtTGQ6ivWbf8tKDHoGA9lLNMnISMIXcLikE+g1509nJhNJgLW4lgE1aa/KfSk9NjCkvgkM1/hH1/r4gv6hEKdhQAAAABJRU5ErkJggg==',
|
||||
market_url_template: market_url_template,
|
||||
market_url_case: 'l',
|
||||
get_data: function(settings, cb) {
|
||||
get_orders(settings.coin, settings.exchange, settings.api_error_msg, function(order_error, buys, sells) {
|
||||
if (order_error == null) {
|
||||
get_trades(settings.coin, settings.exchange, settings.api_error_msg, function(trade_error, trades) {
|
||||
if (trade_error == null) {
|
||||
get_summary(settings.coin, settings.exchange, settings.api_error_msg, function(summary_error, stats) {
|
||||
if (summary_error == null)
|
||||
return cb(null, {buys: buys, sells: sells, trades: trades, stats: stats, chartdata: null});
|
||||
else
|
||||
return cb(summary_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(trade_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(order_error, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,143 @@
|
||||
const request = require('postman-request');
|
||||
const base_url = 'https://yobit.net/api/3';
|
||||
const market_url_template = 'https://yobit.net/en/trade/{coin}/{base}';
|
||||
|
||||
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
|
||||
const rateLimitLib = require('../ratelimit');
|
||||
const rateLimit = new rateLimitLib.RateLimit(1, 2000, false);
|
||||
|
||||
function get_summary(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + '/ticker/' + coin + '_' + exchange;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.success == 0)
|
||||
return cb((body.error != null ? body.error : api_error_msg), null);
|
||||
else {
|
||||
try {
|
||||
const prefix = body[coin + '_' + exchange];
|
||||
const summary = {
|
||||
'high': parseFloat(prefix.high) || 0,
|
||||
'low': parseFloat(prefix.low) || 0,
|
||||
'volume': parseFloat(prefix.vol) || 0,
|
||||
'bid': parseFloat(prefix.buy) || 0,
|
||||
'ask': parseFloat(prefix.sell) || 0,
|
||||
'last': parseFloat(prefix.last) || 0
|
||||
};
|
||||
|
||||
return cb(null, summary);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_trades(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + '/trades/' + coin + '_' + exchange;
|
||||
|
||||
// pause for 2 seconds before continuing
|
||||
rateLimit.schedule(function() {
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null);
|
||||
else if (body.success == 0)
|
||||
return cb((body.error != null ? body.error : api_error_msg), null);
|
||||
else {
|
||||
try {
|
||||
const prefix = body[coin + '_' + exchange];
|
||||
let trades = [];
|
||||
|
||||
for (let t = 0; t < prefix.length; t++) {
|
||||
trades.push({
|
||||
ordertype: (prefix[t].type.toLowerCase() == 'bid' ? 'BUY' : 'SELL'),
|
||||
price: parseFloat(prefix[t].price) || 0,
|
||||
quantity: parseFloat(prefix[t].amount) || 0,
|
||||
timestamp: parseInt(prefix[t].timestamp)
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, trades);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function get_orders(coin, exchange, api_error_msg, cb) {
|
||||
const req_url = base_url + '/depth/' + coin + '_' + exchange;
|
||||
|
||||
// NOTE: no need to pause here because this is the first api call
|
||||
request({uri: req_url, json: true}, function (error, response, body) {
|
||||
if (error)
|
||||
return cb(error, null, null);
|
||||
else if (body == null || body == '' || typeof body !== 'object')
|
||||
return cb(api_error_msg, null, null);
|
||||
else if (body.success == 0)
|
||||
return cb((body.error != null ? body.error : api_error_msg), null, null);
|
||||
else {
|
||||
try {
|
||||
const prefix = body[coin + '_' + exchange];
|
||||
let buys = [];
|
||||
let sells = [];
|
||||
|
||||
for (let b = 0; b < prefix.bids.length; b++) {
|
||||
buys.push({
|
||||
price: parseFloat(prefix.bids[b][0]) || 0,
|
||||
quantity: parseFloat(prefix.bids[b][1]) || 0
|
||||
});
|
||||
}
|
||||
|
||||
for (let s = 0; s < prefix.asks.length; s++) {
|
||||
sells.push({
|
||||
price: parseFloat(prefix.asks[s][0]) || 0,
|
||||
quantity: parseFloat(prefix.asks[s][1]) || 0
|
||||
});
|
||||
}
|
||||
|
||||
return cb(null, buys, sells);
|
||||
} catch(err) {
|
||||
return cb(api_error_msg, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
market_name: 'Yobit',
|
||||
market_logo: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAEsUExURQCZ2ACV1gCY2ACO1ACR1QCT1gCP1AKa2AGZ2AOa2QCX1wOa2AGa2ACS1QCU1gCV1wSb2QCY1wGV16Pc8tLu+Q6c2QCa2ASa2Ruk3DKt4AKZ2Aud2nrL61y/5gGS1QOb2WLB6Cyr3wOW1wSZ2GrE6XrK6zKu4EC04lG65eH1+1C75bjk9Syq3s7t+HjK64DN7ACQ1eT2/AOY2Lnk9Fq+55PV70y45Kne8hCd2sXq98vs+IXN7IrQ7YvR7gOR1ZnX8ASa2FG35Aec2WDB5wmd2tXw+QCM0zuu4Aab2QOO1AKS1ZXW70a24wGX153Z8ard8giY2Aub2QWb2bvm9cTp9mbC6GXC6MPo9h6j3Cap3iKo3iGo3s3s+Cus34nR7d3z+n3M7ACQ1Nry+tjy+pKA2eoAAADASURBVBjTY2AgDLjZgQQnkObkFuAAMsX4+HkYGHiZ2Fl5WVmYgAL8MuqsnIJxUSxmwuZ6yoI8DKwahqLMCcGeoa6O7sLaSlIM8sx+3hHxogFJkcyMbFZavAwcRoEhyT7MsTEcnE52tjZsDAwCBmHhib7RIkwOLmym+kABTjkTf68gIUtnNzYLTTU+oACLgjGLh5COiL21rior0FEcXBKSXBw8jNKyKoogPhBwMQIJdiYWFkYIn4EDTHNwirMzEAMA6McVLdcDhkkAAAAASUVORK5CYII=',
|
||||
market_url_template: market_url_template,
|
||||
market_url_case: 'u',
|
||||
get_data: function(settings, cb) {
|
||||
// ensure coin info is lowercase
|
||||
settings.coin = settings.coin.toLowerCase();
|
||||
settings.exchange = settings.exchange.toLowerCase();
|
||||
|
||||
get_orders(settings.coin, settings.exchange, settings.api_error_msg, function(order_error, buys, sells) {
|
||||
if (order_error == null) {
|
||||
get_trades(settings.coin, settings.exchange, settings.api_error_msg, function(trade_error, trades) {
|
||||
if (trade_error == null) {
|
||||
get_summary(settings.coin, settings.exchange, settings.api_error_msg, function(summary_error, stats) {
|
||||
if (summary_error == null)
|
||||
return cb(null, {buys: buys, sells: sells, trades: trades, stats: stats, chartdata: null});
|
||||
else
|
||||
return cb(summary_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(trade_error, null);
|
||||
});
|
||||
} else
|
||||
return cb(order_error, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
const http = require('http');
|
||||
const settings = require('./settings');
|
||||
const expected = settings.block_page.genesis_block;
|
||||
let state = {status:'offline',chain:'NRB',height:null};
|
||||
function rpc(method, params) {
|
||||
return new Promise((resolve,reject)=>{
|
||||
const data=JSON.stringify({id:'nerbus-health',method,params});
|
||||
const r=http.request({host:settings.wallet.host,port:settings.wallet.port,method:'POST',
|
||||
auth:settings.wallet.username+':'+settings.wallet.password,
|
||||
headers:{'Content-Type':'application/json','Content-Length':Buffer.byteLength(data)}}, res=>{
|
||||
let raw='';res.on('data',d=>raw+=d);res.on('end',()=>{
|
||||
try { const v=JSON.parse(raw);if(v.error) reject(Error('RPC error'));else resolve(v.result); }
|
||||
catch(e){reject(e);}
|
||||
});
|
||||
});
|
||||
r.setTimeout(3000,()=>r.destroy(Error('RPC timeout')));
|
||||
r.on('error',reject);r.end(data);
|
||||
});
|
||||
}
|
||||
async function probe() {
|
||||
try {
|
||||
if(await rpc('getblockhash',[0])!==expected) {
|
||||
state={status:'wrong-network',chain:'NRB',height:null};return;
|
||||
}
|
||||
state={status:'online',chain:'NRB',height:await rpc('getblockcount',[])};
|
||||
} catch (_) { state={status:'offline',chain:'NRB',height:null}; }
|
||||
}
|
||||
module.exports=function(app) {
|
||||
probe();setInterval(probe,10000).unref();
|
||||
app.get('/healthz',(req,res)=>res.json({explorer:'Nerbus',...state}));
|
||||
app.use((req,res,next)=>{res.locals.nerbusState=state;
|
||||
if(state.status!=='online' && req.path==='/ext/getsummary')
|
||||
return res.json({status:state.status,difficulty:'-',difficultyHybrid:'',supply:'-',hashrate:'-',
|
||||
connections:'-',blockcount:'-',lastPrice:'0',lastUSDPrice:'0',marketcap:'0.00',usdMarketcap:'0.00'});
|
||||
if(state.status!=='online' && req.path.startsWith('/api/'))
|
||||
return res.status(503).json({error:'Nerbus node is offline; network launch pending'});
|
||||
next();
|
||||
});
|
||||
};
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
var rpc = require('./jsonrpc');
|
||||
|
||||
function Client(opts) {
|
||||
this.rpc = new rpc.Client(opts);
|
||||
};
|
||||
|
||||
Client.prototype.cmd = function() {
|
||||
var args = [].slice.call(arguments);
|
||||
var cmd = args.shift();
|
||||
|
||||
callRpc(cmd, args, this.rpc);
|
||||
};
|
||||
|
||||
function callRpc (cmd, args, rpc) {
|
||||
var fn = args[args.length - 1];
|
||||
|
||||
// if the last argument is a callback, pop it from the args list
|
||||
if (typeof fn === 'function')
|
||||
args.pop();
|
||||
else
|
||||
fn = function () {};
|
||||
|
||||
rpc.call(cmd, args, function () {
|
||||
var args = [].slice.call(arguments);
|
||||
|
||||
args.unshift(null);
|
||||
fn.apply(this, args);
|
||||
}, function(err) {
|
||||
fn(err);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.Client = Client;
|
||||
+351
@@ -0,0 +1,351 @@
|
||||
var onode = require('./node'),
|
||||
express = require('express'),
|
||||
settings = require('./settings');
|
||||
|
||||
module.exports = function() {
|
||||
function express_app() {
|
||||
var app = express();
|
||||
|
||||
app.get('/{*splat}', hasAccess, function(req, res) {
|
||||
var method = req.path.substring(1, req.path.length);
|
||||
|
||||
if ('undefined' != typeof requires_passphrase[method]) {
|
||||
if (wallet_passphrase)
|
||||
client.walletPassphrase(wallet_passphrase, 10);
|
||||
else
|
||||
res.send('A wallet passphrase is needed and has not been set.');
|
||||
}
|
||||
|
||||
const query_parameters = req.query;
|
||||
let params = [];
|
||||
|
||||
Object.keys(query_parameters).forEach(function(parameter) {
|
||||
let param = query_parameters[parameter];
|
||||
|
||||
if (!isNaN(param))
|
||||
param = parseFloat(param);
|
||||
|
||||
params.push(param);
|
||||
});
|
||||
|
||||
var command = [];
|
||||
|
||||
switch (method) {
|
||||
case 'getnetworkhashps':
|
||||
case 'getmininginfo':
|
||||
case 'getdifficulty':
|
||||
case 'getconnectioncount':
|
||||
case 'getblockcount':
|
||||
case 'getblockhash':
|
||||
case 'getblock':
|
||||
case 'getrawtransaction':
|
||||
case 'getsupply':
|
||||
case 'getinfo':
|
||||
case 'getblockchaininfo':
|
||||
case 'getpeerinfo':
|
||||
case 'gettxoutsetinfo':
|
||||
case 'getmaxmoney':
|
||||
case 'getmaxvote':
|
||||
case 'getvote':
|
||||
case 'getphase':
|
||||
case 'getreward':
|
||||
case 'getnextrewardestimate':
|
||||
case 'getnextrewardwhenstr':
|
||||
case 'getvotelist':
|
||||
case 'getmasternodecount':
|
||||
case 'getmasternodelist':
|
||||
case 'verifymessage':
|
||||
case 'sendmany':
|
||||
command = specialApiCase(method, query_parameters);
|
||||
break;
|
||||
default:
|
||||
command = [{
|
||||
method: method,
|
||||
params: params
|
||||
}];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
client.cmd(command, function(err, response) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
res.send(`${settings.localization.ex_error}: ${settings.localization.check_console}`);
|
||||
} else {
|
||||
if (typeof response === 'object')
|
||||
res.json(response);
|
||||
else {
|
||||
res.setHeader('content-type', 'text/plain');
|
||||
res.end(response.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function hasAccess(req, res, next) {
|
||||
var method = req.path.substring(1, req.path.length);
|
||||
var method_enabled = false;
|
||||
|
||||
// check if this is a "normal" api method
|
||||
if (settings.api_page.public_apis.rpc[method] != null) {
|
||||
// check the enabled property of this normal method
|
||||
method_enabled = settings.api_page.public_apis.rpc[method].enabled;
|
||||
} else {
|
||||
// look for this method in the list of blockchain specific features
|
||||
Object.keys(settings.blockchain_specific).forEach(function(key, index, map) {
|
||||
// check if this feature is enabled and has a definition for this api cmd
|
||||
if (settings.blockchain_specific[key].enabled == true && settings.blockchain_specific[key]['public_apis'] != null && settings.blockchain_specific[key]['public_apis'][method] != null) {
|
||||
// check the enabled properly of this blockchain specific method
|
||||
method_enabled = settings.blockchain_specific[key]['public_apis'][method].enabled;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// only show disabled msg for outside calls. internal calls should always go through
|
||||
if ((!settings.api_page.enabled || method_enabled == null || !method_enabled) && req.headers.host.indexOf('127.0.0.1') == -1)
|
||||
res.end(settings.localization.method_disabled);
|
||||
else if (method == 'getnetworkhashps' && !settings.shared_pages.show_hashrate) {
|
||||
// getnetworkhashps requires show_hashrate to be enabled or else hashrate cannot be returned
|
||||
res.end('-');
|
||||
} else {
|
||||
if (accesslist.type == 'all')
|
||||
return next();
|
||||
|
||||
if ('undefined' == typeof accesslist[method]) {
|
||||
if (accesslist.type == 'only')
|
||||
res.end('This method is restricted');
|
||||
else
|
||||
return next();
|
||||
} else {
|
||||
if (accesslist[method] == true)
|
||||
return next();
|
||||
else
|
||||
res.end('This method is restricted');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function prepareRpcCommand(cmd, addParams) {
|
||||
var method_name = '';
|
||||
var params = addParams || [];
|
||||
|
||||
// Check for null/blank string
|
||||
if (cmd != null && cmd.trim() != '') {
|
||||
// Split cmd by spaces
|
||||
var split = cmd.split(' ');
|
||||
|
||||
for (i=0; i<split.length; i++) {
|
||||
if (i==0)
|
||||
method_name = split[i];
|
||||
else
|
||||
params.push(split[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return { method: method_name, parameters: params };
|
||||
}
|
||||
|
||||
function specialApiCase(method_name, query_parameters) {
|
||||
var params = [];
|
||||
|
||||
switch (method_name) {
|
||||
case 'getnetworkhashps':
|
||||
case 'getmininginfo':
|
||||
case 'getdifficulty':
|
||||
case 'getconnectioncount':
|
||||
case 'getblockcount':
|
||||
case 'getinfo':
|
||||
case 'getblockchaininfo':
|
||||
case 'getpeerinfo':
|
||||
case 'gettxoutsetinfo':
|
||||
case 'getvotelist':
|
||||
case 'getmasternodecount':
|
||||
case 'getmasternodelist':
|
||||
var cmd = prepareRpcCommand(settings.api_cmds[method_name]);
|
||||
|
||||
method_name = cmd.method;
|
||||
params = cmd.parameters;
|
||||
|
||||
break;
|
||||
case 'getmaxmoney':
|
||||
case 'getmaxvote':
|
||||
case 'getvote':
|
||||
case 'getphase':
|
||||
case 'getreward':
|
||||
case 'getnextrewardestimate':
|
||||
case 'getnextrewardwhenstr':
|
||||
case 'getsupply':
|
||||
var cmd = prepareRpcCommand(settings.blockchain_specific.heavycoin.api_cmds[method_name]);
|
||||
|
||||
method_name = cmd.method;
|
||||
params = cmd.parameters;
|
||||
|
||||
break;
|
||||
case 'getblockhash':
|
||||
Object.keys(query_parameters).forEach(function(parameter) {
|
||||
if (parameter == 'height' || parameter == 'index')
|
||||
params.push(parseInt(query_parameters[parameter]));
|
||||
});
|
||||
|
||||
var cmd = prepareRpcCommand(settings.api_cmds.getblockhash, params);
|
||||
|
||||
method_name = cmd.method;
|
||||
params = cmd.parameters;
|
||||
|
||||
break;
|
||||
case 'getblock':
|
||||
Object.keys(query_parameters).forEach(function(parameter) {
|
||||
if (parameter == 'hash')
|
||||
params.push(query_parameters[parameter]);
|
||||
});
|
||||
|
||||
var cmd = prepareRpcCommand(settings.api_cmds.getblock, params);
|
||||
|
||||
method_name = cmd.method;
|
||||
params = cmd.parameters;
|
||||
|
||||
break;
|
||||
case 'getrawtransaction':
|
||||
Object.keys(query_parameters).forEach(function(parameter) {
|
||||
if (parameter == 'txid')
|
||||
params.push(query_parameters[parameter]);
|
||||
else if (parameter == 'decrypt')
|
||||
params.push(parseInt(query_parameters[parameter]));
|
||||
});
|
||||
|
||||
var cmd = prepareRpcCommand(settings.api_cmds.getrawtransaction, params);
|
||||
|
||||
method_name = cmd.method;
|
||||
params = cmd.parameters;
|
||||
|
||||
break;
|
||||
case 'verifymessage':
|
||||
Object.keys(query_parameters).forEach(function(parameter) {
|
||||
if (parameter == 'address' || parameter == 'message')
|
||||
params.push(query_parameters[parameter]);
|
||||
|
||||
if (parameter == 'signature') {
|
||||
let param = decodeURIComponent(query_parameters[parameter]);
|
||||
|
||||
while (param.indexOf(' ') > -1)
|
||||
param = param.replace(' ', '+');
|
||||
|
||||
params.push(param);
|
||||
}
|
||||
});
|
||||
|
||||
var cmd = prepareRpcCommand(settings.api_cmds.verifymessage, params);
|
||||
|
||||
method_name = cmd.method;
|
||||
params = cmd.parameters;
|
||||
|
||||
break;
|
||||
case 'sendmany':
|
||||
var after_account = false;
|
||||
var before_min_conf = true;
|
||||
var address_info = {};
|
||||
|
||||
Object.keys(query_parameters).forEach(function(parameter) {
|
||||
if (parameter == 'minconf') {
|
||||
before_min_conf = false;
|
||||
params.push(address_info);
|
||||
}
|
||||
|
||||
let param = query_parameters[parameter];
|
||||
|
||||
if (!isNaN(param))
|
||||
param = parseFloat(param);
|
||||
|
||||
if (after_account && before_min_conf)
|
||||
address_info[parameter] = param;
|
||||
else
|
||||
params.push(param);
|
||||
|
||||
if (parameter == 'account')
|
||||
after_account = true;
|
||||
});
|
||||
|
||||
if (before_min_conf)
|
||||
params.push(address_info);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return [{
|
||||
method: method_name,
|
||||
params: params
|
||||
}];
|
||||
}
|
||||
|
||||
return app;
|
||||
};
|
||||
|
||||
var accesslist = {};
|
||||
var client = {};
|
||||
var wallet_passphrase = null;
|
||||
var requires_passphrase = {
|
||||
'dumpprivkey': true,
|
||||
'importprivkey': true,
|
||||
'keypoolrefill': true,
|
||||
'sendfrom': true,
|
||||
'sendmany': true,
|
||||
'sendtoaddress': true,
|
||||
'signmessage': true,
|
||||
'signrawtransaction': true
|
||||
};
|
||||
|
||||
accesslist.type = 'all';
|
||||
|
||||
function setAccess(type, access_list) {
|
||||
// reset
|
||||
accesslist = {};
|
||||
accesslist.type = type;
|
||||
|
||||
if (type == "only") {
|
||||
for (i = 0; i < access_list.length; i++)
|
||||
accesslist[access_list[i]] = true;
|
||||
}
|
||||
|
||||
if (type == "restrict") {
|
||||
for (i = 0; i < access_list.length; i++)
|
||||
accesslist[access_list[i]] = false;
|
||||
}
|
||||
|
||||
// default is for security reasons. Prevents accidental theft of coins/attack
|
||||
if (type == 'default-safe') {
|
||||
var restrict_list = ['dumpprivkey', 'walletpassphrasechange', 'stop'];
|
||||
|
||||
accesslist.type = 'restrict';
|
||||
|
||||
for (i = 0; i < restrict_list.length; i++)
|
||||
accesslist[restrict_list[i]] = false;
|
||||
}
|
||||
|
||||
if (type == 'read-only') {
|
||||
var restrict_list = ['addmultisigaddress', 'addnode', 'backupwallet', 'createmultisig', 'createrawtransaction', 'encryptwallet', 'importprivkey', 'keypoolrefill', 'lockunspent', 'move', 'sendfrom', 'sendmany', 'sendrawtransaction', 'sendtoaddress', 'setaccount', 'setgenerate', 'settxfee', 'signmessage', 'signrawtransaction', 'stop', 'submitblock', 'walletlock', 'walletpassphrasechange'];
|
||||
|
||||
accesslist.type = 'restrict';
|
||||
|
||||
for (i = 0; i < restrict_list.length; i++)
|
||||
accesslist[restrict_list[i]] = false;
|
||||
}
|
||||
};
|
||||
|
||||
function setWalletDetails(details) {
|
||||
if ('undefined' == typeof details.rpc)
|
||||
client = new onode.Client(details);
|
||||
else
|
||||
client = details;
|
||||
};
|
||||
|
||||
function setWalletPassphrase(passphrase) {
|
||||
wallet_passphrase = passphrase;
|
||||
};
|
||||
|
||||
return {
|
||||
app: express_app(),
|
||||
setAccess: setAccess,
|
||||
setWalletDetails: setWalletDetails,
|
||||
setWalletPassphrase: setWalletPassphrase
|
||||
}
|
||||
}();
|
||||
@@ -0,0 +1,52 @@
|
||||
// Initialize the rate limiting class from Matteo Agosti via https://www.matteoagosti.com/blog/2013/01/22/rate-limiting-function-calls-in-javascript/
|
||||
var RateLimit = (function() {
|
||||
var RateLimit = function(maxOps, interval, allowBursts) {
|
||||
this._maxRate = allowBursts ? maxOps : maxOps / interval;
|
||||
this._interval = interval;
|
||||
this._allowBursts = allowBursts;
|
||||
|
||||
this._numOps = 0;
|
||||
this._start = new Date().getTime();
|
||||
this._queue = [];
|
||||
};
|
||||
|
||||
RateLimit.prototype.schedule = function(fn) {
|
||||
var that = this,
|
||||
rate = 0,
|
||||
now = new Date().getTime(),
|
||||
elapsed = now - this._start;
|
||||
|
||||
if (elapsed > this._interval) {
|
||||
this._numOps = 0;
|
||||
this._start = now;
|
||||
}
|
||||
|
||||
rate = this._numOps / (this._allowBursts ? 1 : elapsed);
|
||||
|
||||
if (rate < this._maxRate) {
|
||||
if (this._queue.length === 0) {
|
||||
this._numOps++;
|
||||
fn();
|
||||
} else {
|
||||
if (fn)
|
||||
this._queue.push(fn);
|
||||
|
||||
this._numOps++;
|
||||
this._queue.shift()();
|
||||
}
|
||||
} else {
|
||||
if (fn)
|
||||
this._queue.push(fn);
|
||||
|
||||
setTimeout(function() {
|
||||
that.schedule();
|
||||
}, 1 / this._maxRate);
|
||||
}
|
||||
};
|
||||
|
||||
return RateLimit;
|
||||
})();
|
||||
|
||||
module.exports = {
|
||||
RateLimit: RateLimit
|
||||
};
|
||||
+2363
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user