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