WIP
This commit is contained in:
parent
668ba7d08c
commit
c8ba96515f
1 changed files with 103 additions and 85 deletions
154
lib/plugins.js
154
lib/plugins.js
|
|
@ -18,9 +18,9 @@ var DEPOSIT_TIMEOUT = 130 * 1000;
|
|||
|
||||
var db = null;
|
||||
|
||||
var tickerPlugin = null;
|
||||
var tickerPlugins = {};
|
||||
var traderPlugin = null;
|
||||
var walletPlugin = null;
|
||||
var walletPlugins = {};
|
||||
var idVerifierPlugin = null;
|
||||
var infoPlugin = null;
|
||||
|
||||
|
|
@ -29,14 +29,9 @@ var currentlyUsedPlugins = {};
|
|||
var cachedConfig = null;
|
||||
var deviceCurrency = 'USD';
|
||||
|
||||
var lastBalances = null;
|
||||
var lastBalances = {};
|
||||
var lastRates = {};
|
||||
|
||||
var balanceInterval = null;
|
||||
var rateInterval = null;
|
||||
var tradeInterval = null;
|
||||
var reapTxInterval = null;
|
||||
|
||||
var tradesQueue = [];
|
||||
|
||||
// that's basically a constructor
|
||||
|
|
@ -104,10 +99,15 @@ function loadPlugin(name, config) {
|
|||
return plugin;
|
||||
}
|
||||
|
||||
function loadOrConfigPlugin(pluginHandle, pluginType, currency,
|
||||
function loadOrConfigPlugin(pluginHandle, pluginType, cryptoCoin, currency,
|
||||
onChangeCallback) {
|
||||
var currentName = cachedConfig.exchanges.plugins.current[pluginType];
|
||||
var pluginChanged = currentlyUsedPlugins[pluginType] !== currentName;
|
||||
|
||||
if (!cryptoCoin) cryptoCoin = 'any'
|
||||
var currentName = cryptoCoin === 'any' || cryptoCoin === 'BTC'
|
||||
? cachedConfig.exchanges.plugins.current[pluginType]
|
||||
: cachedConfig.exchanges.plugins.current[cryptoCoin][pluginType]
|
||||
|
||||
var pluginChanged = currentlyUsedPlugins[cryptoCoin][pluginType] !== currentName;
|
||||
|
||||
if (!currentName) pluginHandle = null;
|
||||
else { // some plugins may be disabled
|
||||
|
|
@ -119,8 +119,9 @@ function loadOrConfigPlugin(pluginHandle, pluginType, currency,
|
|||
if (pluginHandle && !pluginChanged) pluginHandle.config(pluginConfig);
|
||||
else {
|
||||
pluginHandle = loadPlugin(currentName, pluginConfig);
|
||||
currentlyUsedPlugins[pluginType] = currentName
|
||||
logger.debug('plugin(%s) loaded: %s', pluginType, pluginHandle.NAME ||
|
||||
currentlyUsedPlugins[cryptoCoin] ||= {}
|
||||
currentlyUsedPlugins[cryptoCoin][pluginType] = currentName
|
||||
logger.debug('[%s] plugin(%s) loaded: %s', cryptoCoin, pluginType, pluginHandle.NAME ||
|
||||
currentName);
|
||||
}
|
||||
}
|
||||
|
|
@ -138,34 +139,40 @@ exports.configure = function configure(config) {
|
|||
|
||||
cachedConfig = config;
|
||||
deviceCurrency = config.exchanges.settings.currency;
|
||||
cryptoCoins = config.exchanges.settings.coins || ['BTC'];
|
||||
|
||||
cryptoCoins.forEach(function (cryptoCoin) {
|
||||
// TICKER [required] configure (or load)
|
||||
loadOrConfigPlugin(
|
||||
tickerPlugin,
|
||||
tickerPlugins[cryptoCoin],
|
||||
'ticker',
|
||||
cryptoCoin,
|
||||
deviceCurrency, // device currency
|
||||
function onTickerChange(newTicker) {
|
||||
tickerPlugin = newTicker;
|
||||
pollRate();
|
||||
tickerPlugins[cryptoCoin] = newTicker;
|
||||
pollRate(cryptoCoin);
|
||||
}
|
||||
);
|
||||
|
||||
// WALLET [required] configure (or load)
|
||||
loadOrConfigPlugin(
|
||||
walletPlugin,
|
||||
walletPlugins[cryptoCoin],
|
||||
'transfer',
|
||||
cryptoCoin,
|
||||
null,
|
||||
function onWalletChange(newWallet) {
|
||||
walletPlugin = newWallet;
|
||||
pollBalance();
|
||||
walletPlugins[cryptoCoin] = newWallet;
|
||||
pollBalance(cryptoCoin);
|
||||
}
|
||||
);
|
||||
})
|
||||
|
||||
// TRADER [optional] configure (or load)
|
||||
traderPlugin = loadOrConfigPlugin(
|
||||
traderPlugin,
|
||||
'trade',
|
||||
null,
|
||||
null,
|
||||
function onTraderChange(newTrader) {
|
||||
traderPlugin = newTrader;
|
||||
if (newTrader === null) stopTrader();
|
||||
|
|
@ -222,26 +229,35 @@ exports.pollQueries = function pollQueries(session, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
function _sendBitcoins(toAddress, satoshis, cb) {
|
||||
function _sendCoins(toAddress, cryptoUnits, cryptoCoin, cb) {
|
||||
var walletPlugin = walletPlugins[cryptoCoin]
|
||||
var transactionFee = cachedConfig.exchanges.settings.transactionFee;
|
||||
walletPlugin.sendBitcoins(toAddress, satoshis, transactionFee, cb);
|
||||
if (cryptoCoin === 'BTC')
|
||||
walletPlugin.sendBitcoins(toAddress, cryptoUnits, transactionFee, cb);
|
||||
else
|
||||
walletPlugin.sendCoins(toAddress, cryptoUnits, cryptoCoin, transactionFee, cb);
|
||||
}
|
||||
|
||||
function executeTx(session, tx, authority, cb) {
|
||||
db.addOutgoingTx(session, tx, function(err, toSend) {
|
||||
if (err) return cb(err);
|
||||
var satoshisToSend = toSend.satoshis;
|
||||
if (satoshisToSend === 0)
|
||||
var cryptoUnitsToSend = toSend.cryptoUnits;
|
||||
if (cryptoUnitsToSend === 0)
|
||||
return cb(null, {statusCode: 204, txId: tx.txId, txHash: null});
|
||||
|
||||
_sendBitcoins(tx.toAddress, satoshisToSend, function(_err, txHash) {
|
||||
_sendCoins(tx.toAddress, cryptoUnitsToSend, function(_err, txHash) {
|
||||
var fee = null; // Need to fill this out in plugins
|
||||
if (_err) toSend = {satoshis: 0, fiat: 0};
|
||||
if (_err) toSend = {cryptoUnits: new BigNumber(0), fiat: 0};
|
||||
db.sentCoins(session, tx, authority, toSend, fee, _err, txHash);
|
||||
|
||||
if (_err) return cb(_err);
|
||||
|
||||
pollBalance();
|
||||
var cryptoCoin = tx.coin
|
||||
? tx.coin.unitCode
|
||||
: 'BTC'
|
||||
|
||||
pollBalance('BTC');
|
||||
|
||||
cb(null, {
|
||||
statusCode: 201, // Created
|
||||
txHash: txHash,
|
||||
|
|
@ -326,6 +342,13 @@ exports.cashOut = function cashOut(session, tx, cb) {
|
|||
label: 'TX ' + Date.now(),
|
||||
account: 'deposit'
|
||||
};
|
||||
|
||||
var cryptoCoin = tx.coin
|
||||
? tx.coin.unitCode
|
||||
: 'BTC'
|
||||
|
||||
var walletPlugin = walletPlugins[cryptoCoin]
|
||||
|
||||
walletPlugin.newAddress(tmpInfo, function(err, address) {
|
||||
if (err) return cb(err);
|
||||
|
||||
|
|
@ -341,11 +364,12 @@ exports.dispenseAck = function dispenseAck(session, rec) {
|
|||
db.addDispense(session, rec.tx, rec.cartridges);
|
||||
};
|
||||
|
||||
exports.fiatBalance = function fiatBalance() {
|
||||
var rawRate = exports.getDeviceRate().rates.ask;
|
||||
exports.fiatBalance = function fiatBalance(cryptoCoin) {
|
||||
var rawRate = exports.getDeviceRate(cryptoCoin).rates.ask;
|
||||
var commission = cachedConfig.exchanges.settings.commission;
|
||||
var lastBalance = lastBalances[cryptoCoin]
|
||||
|
||||
if (!rawRate || !lastBalances) return null;
|
||||
if (!rawRate || !lastBalance) return null;
|
||||
|
||||
// The rate is actually our commission times real rate.
|
||||
var rate = commission * rawRate;
|
||||
|
|
@ -356,16 +380,9 @@ exports.fiatBalance = function fiatBalance() {
|
|||
|
||||
// `balance.transferBalance` is the balance of our transfer account (the one
|
||||
// we use to send Bitcoins to clients) in satoshis.
|
||||
var transferBalance = lastBalances.transferBalance.BTC;
|
||||
var transferBalance = lastBalances.transferBalance;
|
||||
|
||||
// Since `transferBalance` is in satoshis, we need to turn it into
|
||||
// bitcoins and then fiat to learn how much fiat currency we can exchange.
|
||||
//
|
||||
// Unit validity proof: [ $ ] = [ (B * 10^8) / 10^8 * $/B ]
|
||||
// [ $ ] = [ B * $/B ]
|
||||
// [ $ ] = [ $ ]
|
||||
var fiatTransferBalance = ((transferBalance / SATOSHI_FACTOR) * rate) /
|
||||
lowBalanceMargin;
|
||||
var fiatTransferBalance = (transferBalance * rate) / lowBalanceMargin;
|
||||
|
||||
return fiatTransferBalance;
|
||||
};
|
||||
|
|
@ -376,14 +393,12 @@ exports.fiatBalance = function fiatBalance() {
|
|||
exports.startPolling = function startPolling() {
|
||||
executeTrades();
|
||||
|
||||
if (!balanceInterval)
|
||||
balanceInterval = setInterval(pollBalance, POLLING_RATE);
|
||||
cryptoCoins.forEach(function (coin) {
|
||||
setInterval(async.apply(pollBalance, coin), POLLING_RATE);
|
||||
setInterval(async.apply(pollRate, coin), POLLING_RATE);
|
||||
});
|
||||
|
||||
if (!rateInterval)
|
||||
rateInterval = setInterval(pollRate, POLLING_RATE);
|
||||
|
||||
if (!reapTxInterval)
|
||||
reapTxInterval = setInterval(reapTxs, REAP_RATE);
|
||||
setInterval(reapTxs, REAP_RATE);
|
||||
|
||||
startTrader();
|
||||
};
|
||||
|
|
@ -400,6 +415,7 @@ function startTrader() {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
function stopTrader() {
|
||||
if (tradeInterval) {
|
||||
clearInterval(tradeInterval);
|
||||
|
|
@ -408,35 +424,39 @@ function stopTrader() {
|
|||
}
|
||||
}
|
||||
|
||||
function pollBalance(cb) {
|
||||
logger.debug('collecting balance');
|
||||
function pollBalance(cryptoCoin, cb) {
|
||||
logger.debug('[%s] collecting balance', cryptoCoin);
|
||||
|
||||
var walletPlugin = walletPlugins[cryptoCoin]
|
||||
var jobs = {
|
||||
transferBalance: walletPlugin.balance
|
||||
};
|
||||
|
||||
// only add if trader is enabled
|
||||
// if (traderPlugin) {
|
||||
// // NOTE: we would need to handle when traderCurrency!=deviceCurrency
|
||||
// jobs.tradeBalance = traderPlugin.balance;
|
||||
// }
|
||||
|
||||
async.parallel(jobs, function(err, balance) {
|
||||
walletPlugin.balance(function(err, balance) {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
return cb && cb(err);
|
||||
}
|
||||
|
||||
logger.debug('Balance update:', balance);
|
||||
logger.debug('[%s] Balance update:', cryptoCoin, balance);
|
||||
balance.timestamp = Date.now();
|
||||
lastBalances = balance;
|
||||
lastBalances[cryptoCoin] = balance;
|
||||
|
||||
return cb && cb(null, lastBalances);
|
||||
});
|
||||
}
|
||||
|
||||
function pollRate(cb) {
|
||||
logger.debug('polling for rates (%s)', tickerPlugin.NAME);
|
||||
function pollRates (cb) {
|
||||
var polls = cryptoCoins.map(function (cryptoCoin) {
|
||||
async.apply(pollRate, cryptoCoin)
|
||||
});
|
||||
|
||||
async.parallel(polls, cb);
|
||||
}
|
||||
|
||||
function pollRate(cryptoCoin, cb) {
|
||||
logger.debug('[%s] polling for rates (%s)', cryptoCoin, tickerPlugin.NAME);
|
||||
var tickerPlugin = tickerPlugins[cryptoCoin];
|
||||
|
||||
tickerPlugin.ticker(deviceCurrency, function(err, resRates) {
|
||||
if (err) {
|
||||
|
|
@ -446,7 +466,7 @@ function pollRate(cb) {
|
|||
|
||||
logger.debug('got rates: %j', resRates);
|
||||
resRates.timestamp = new Date();
|
||||
lastRates = resRates;
|
||||
lastRates[cryptoCoin] = resRates;
|
||||
|
||||
return cb && cb(null, lastRates);
|
||||
});
|
||||
|
|
@ -455,16 +475,14 @@ function pollRate(cb) {
|
|||
/*
|
||||
* Getters | Helpers
|
||||
*/
|
||||
function getLastRate(currency) {
|
||||
if (!lastRates) return null;
|
||||
|
||||
var tmpCurrency = currency || deviceCurrency;
|
||||
if (!lastRates[tmpCurrency]) return null;
|
||||
exports.getDeviceRate = function getDeviceRate(cryptoCoin) {
|
||||
if (!lastRates[cryptoCoin]) return null;
|
||||
|
||||
return lastRates[tmpCurrency];
|
||||
}
|
||||
exports.getDeviceRate = function getDeviceRate() {
|
||||
return getLastRate(deviceCurrency);
|
||||
var lastRate = lastRates[cryptoCoin]
|
||||
if (!lastRate) return null;
|
||||
|
||||
return lastRate[deviceCurrency];
|
||||
};
|
||||
|
||||
exports.getBalance = function getBalance() {
|
||||
|
|
@ -479,7 +497,7 @@ exports.getBalance = function getBalance() {
|
|||
function purchase(trade, cb) {
|
||||
traderPlugin.purchase(trade.satoshis, null, function(err) {
|
||||
if (err) return cb(err);
|
||||
pollBalance();
|
||||
pollBalance('BTC');
|
||||
if (typeof cb === 'function') cb();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue