diff --git a/lib/plugins.js b/lib/plugins.js index a04e7fd2..db66437b 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -11,18 +11,10 @@ var argv = require('minimist')(process.argv.slice(2)) var tradeInterval = null -var SATOSHI_FACTOR = 1e8 var POLLING_RATE = 60 * 1000 // poll each minute var REAP_RATE = 2 * 1000 var PENDING_TIMEOUT = 70 * 1000 -var BTC_COIN = { - unitCode: 'BTC', - displayCode: 'mBTC', - unitScale: 8, - displayScale: 5 -} - if (argv.timeout) PENDING_TIMEOUT = argv.timeout / 1000 // TODO: might have to update this if user is allowed to extend monitoring time @@ -30,10 +22,10 @@ var DEPOSIT_TIMEOUT = 130 * 1000 var db = null -var cryptoCoins = null +var cryptoCodes = null var tickerPlugins = {} -var traderPlugin = null +var traderPlugins = {} var walletPlugins = {} var idVerifierPlugin = null var infoPlugin = null @@ -46,7 +38,7 @@ var deviceCurrency = 'USD' var lastBalances = {} var lastRates = {} -var tradesQueue = [] +var tradesQueues = {} // that's basically a constructor exports.init = function init (databaseHandle) { @@ -119,9 +111,9 @@ function loadPlugin (name, config) { return plugin } -function loadOrConfigPlugin (pluginHandle, pluginType, cryptoCoin, currency, +function loadOrConfigPlugin (pluginHandle, pluginType, cryptoCode, currency, onChangeCallback) { - var cryptoCode = cryptoCoin ? cryptoCoin.unitCode : 'any' + cryptoCode = cryptoCode || 'BTC' var currentName = cryptoCode === 'any' || cryptoCode === 'BTC' ? cachedConfig.exchanges.plugins.current[pluginType] @@ -157,19 +149,18 @@ exports.configure = function configure (config) { cachedConfig = config deviceCurrency = config.exchanges.settings.currency - cryptoCoins = config.exchanges.settings.coins || [BTC_COIN] + cryptoCodes = config.exchanges.settings.coins || 'BTC' - cryptoCoins.forEach(function (cryptoCoin) { + cryptoCodes.forEach(function (cryptoCode) { // TICKER [required] configure (or load) - var cryptoCode = cryptoCoin.unitCode loadOrConfigPlugin( tickerPlugins[cryptoCode], 'ticker', - cryptoCoin, + cryptoCode, deviceCurrency, // device currency function onTickerChange (newTicker) { tickerPlugins[cryptoCode] = newTicker - pollRate(cryptoCoin) + pollRate(cryptoCode) } ) @@ -177,28 +168,29 @@ exports.configure = function configure (config) { loadOrConfigPlugin( walletPlugins[cryptoCode], 'transfer', - cryptoCoin, + cryptoCode, null, function onWalletChange (newWallet) { walletPlugins[cryptoCode] = newWallet - pollBalance(cryptoCoin) + pollBalance(cryptoCode) + } + ) + + tradesQueues[cryptoCode] = [] + + loadOrConfigPlugin( + traderPlugins[cryptoCode], + 'trade', + cryptoCode, + null, + function onTraderChange (newTrader) { + traderPlugins[cryptoCode] = newTrader + if (newTrader === null) stopTrader(cryptoCode) + else startTrader(cryptoCode) } ) }) - // TRADER [optional] configure (or load) - traderPlugin = loadOrConfigPlugin( - traderPlugin, - 'trade', - null, - null, - function onTraderChange (newTrader) { - traderPlugin = newTrader - if (newTrader === null) stopTrader() - else startTrader() - } - ) - // ID VERIFIER [optional] configure (or load) idVerifierPlugin = loadOrConfigPlugin( idVerifierPlugin, @@ -248,14 +240,13 @@ exports.pollQueries = function pollQueries (session, cb) { }) } -function _sendCoins (toAddress, cryptoAtoms, cryptoCoin, cb) { - var cryptoCode = cryptoCoin.unitCode +function _sendCoins (toAddress, cryptoAtoms, cryptoCode, cb) { var walletPlugin = walletPlugins[cryptoCode] var transactionFee = cachedConfig.exchanges.settings.transactionFee if (cryptoCode === 'BTC') { walletPlugin.sendBitcoins(toAddress, cryptoAtoms, transactionFee, cb) } else { - walletPlugin.sendCoins(toAddress, cryptoAtoms, cryptoCoin, transactionFee, cb) + walletPlugin.sendCoins(toAddress, cryptoAtoms, cryptoCode, transactionFee, cb) } } @@ -323,10 +314,15 @@ function reapTxs () { exports.trade = function trade (session, rawTrade, cb) { // TODO: move this to DB, too // add bill to trader queue (if trader is enabled) + var cryptoCode = rawTrade.cryptoCode || 'BTC' + var traderPlugin = traderPlugins[cryptoCode] + var tradesQueue = tradesQueues[cryptoCode] + if (traderPlugin) { tradesQueue.push({ currency: rawTrade.currency, - satoshis: rawTrade.satoshis + cryptoAtoms: rawTrade.cryptoAtoms, + cryptoCode: rawTrade.cryptoCode }) } @@ -360,9 +356,7 @@ exports.cashOut = function cashOut (session, tx, cb) { account: 'deposit' } - var cryptoCoin = tx.coin || BTC_COIN - var cryptoCode = cryptoCoin.unitCode - + var cryptoCode = tx.cryptoCode || 'BTC' var walletPlugin = walletPlugins[cryptoCode] walletPlugin.newAddress(tmpInfo, function (err, address) { @@ -380,9 +374,8 @@ exports.dispenseAck = function dispenseAck (session, rec) { db.addDispense(session, rec.tx, rec.cartridges) } -exports.fiatBalance = function fiatBalance (cryptoCoin) { - var cryptoCode = cryptoCoin.unitCode - var rawRate = exports.getDeviceRate(cryptoCoin).rates.ask +exports.fiatBalance = function fiatBalance (cryptoCode) { + var rawRate = exports.getDeviceRate(cryptoCode).rates.ask var commission = cachedConfig.exchanges.settings.commission var lastBalance = lastBalances[cryptoCode] @@ -410,9 +403,9 @@ exports.fiatBalance = function fiatBalance (cryptoCoin) { exports.startPolling = function startPolling () { executeTrades() - cryptoCoins.forEach(function (coin) { - setInterval(async.apply(pollBalance, coin), POLLING_RATE) - setInterval(async.apply(pollRate, coin), POLLING_RATE) + cryptoCodes.forEach(function (cryptoCode) { + setInterval(async.apply(pollBalance, cryptoCode), POLLING_RATE) + setInterval(async.apply(pollRate, cryptoCode), POLLING_RATE) }) setInterval(reapTxs, REAP_RATE) @@ -420,29 +413,30 @@ exports.startPolling = function startPolling () { startTrader() } -function startTrader () { +function startTrader (cryptoCode) { // Always start trading, even if we don't have a trade exchange configured, // since configuration can always change in `Trader#configure`. // `Trader#executeTrades` returns early if we don't have a trade exchange // configured at the moment. + var traderPlugin = traderPlugins[cryptoCode] + if (traderPlugin && !tradeInterval) { tradeInterval = setInterval( - executeTrades, + function () { executeTrades(cryptoCode) }, cachedConfig.exchanges.settings.tradeInterval ) } } -function stopTrader () { +function stopTrader (cryptoCode) { if (tradeInterval) { clearInterval(tradeInterval) tradeInterval = null - tradesQueue = [] + tradesQueues[cryptoCode] = [] } } -function pollBalance (cryptoCoin, cb) { - var cryptoCode = cryptoCoin.unitCode +function pollBalance (cryptoCode, cb) { logger.debug('[%s] collecting balance', cryptoCode) var walletPlugin = walletPlugins[cryptoCode] @@ -461,8 +455,7 @@ function pollBalance (cryptoCoin, cb) { }) } -function pollRate (cryptoCoin, cb) { - var cryptoCode = cryptoCoin.unitCode +function pollRate (cryptoCode, cb) { logger.debug('[%s] polling for rates (%s)', cryptoCode, tickerPlugin.NAME) var tickerPlugin = tickerPlugins[cryptoCode] @@ -471,7 +464,7 @@ function pollRate (cryptoCoin, cb) { var tickerF = cryptoCode === 'BTC' ? async.apply(tickerPlugin.ticker, currencies) - : async.apply(tickerPlugin.ticker, currencies, cryptoCoin) + : async.apply(tickerPlugin.ticker, currencies, cryptoCode) tickerF(function (err, resRates) { if (err) { @@ -491,8 +484,7 @@ function pollRate (cryptoCoin, cb) { * Getters | Helpers */ -exports.getDeviceRate = function getDeviceRate (cryptoCoin) { - var cryptoCode = cryptoCoin.unitCode +exports.getDeviceRate = function getDeviceRate (cryptoCode) { if (!lastRates[cryptoCode]) return null var lastRate = lastRates[cryptoCode] @@ -501,8 +493,7 @@ exports.getDeviceRate = function getDeviceRate (cryptoCoin) { return lastRate[deviceCurrency] } -exports.getBalance = function getBalance (cryptoCoin) { - var cryptoCode = cryptoCoin.unitCode +exports.getBalance = function getBalance (cryptoCode) { var lastBalance = lastBalances[cryptoCode] if (!lastBalance) return null @@ -513,44 +504,50 @@ exports.getBalance = function getBalance (cryptoCoin) { * Trader functions */ function purchase (trade, cb) { + var cryptoCode = trade.cryptoCode + var traderPlugin = traderPlugins[cryptoCode] traderPlugin.purchase(trade.satoshis, null, function (err) { if (err) return cb(err) - pollBalance('BTC') + pollBalance(cryptoCode) if (typeof cb === 'function') cb() }) } -function consolidateTrades () { +function consolidateTrades (cryptoCode) { // NOTE: value in satoshis stays the same no matter the currency + var cryptoAtoms = tradesQueues[cryptoCode].reduce(function (prev, current) { + return current.cryptoAtoms.plus(prev) + }, new BigNumber(0)) + var consolidatedTrade = { currency: deviceCurrency, - satoshis: tradesQueue.reduce(function (prev, current) { - return prev + current.satoshis - }, 0) + cryptoAtoms: cryptoAtoms, + cryptoCode: cryptoCode } - tradesQueue = [] + tradesQueues[cryptoCode] = [] logger.debug('consolidated: ', JSON.stringify(consolidatedTrade)) return consolidatedTrade } -function executeTrades () { +function executeTrades (cryptoCode) { + var traderPlugin = traderPlugins[cryptoCode] if (!traderPlugin) return logger.debug('checking for trades') - var trade = consolidateTrades() + var trade = consolidateTrades(cryptoCode) - if (trade.satoshis === 0) { + if (trade.cryptoAtoms.eq(0)) { logger.debug('rejecting 0 trade') return } - logger.debug('making a trade: %d', trade.satoshis / SATOSHI_FACTOR) + logger.debug('making a trade: %d', trade.cryptoAtoms.toString()) purchase(trade, function (err) { if (err) { - tradesQueue.push(trade) + tradesQueues[cryptoCode].push(trade) if (err.name !== 'orderTooSmall') logger.error(err) } }) @@ -567,6 +564,6 @@ exports.verifyTx = function verifyTx (data, cb) { idVerifierPlugin.verifyTransaction(data, cb) } -exports.getCryptoCoins = function getCryptoCoins () { - return cryptoCoins +exports.getcryptoCodes = function getcryptoCodes () { + return cryptoCodes } diff --git a/lib/routes.js b/lib/routes.js index fa4dfd7b..c8121280 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -23,7 +23,7 @@ var pids = {} var reboots = {} function buildRates () { - var cryptoCoins = plugins.getCryptoCoins() + var cryptoCodes = plugins.getcryptoCodes() var config = plugins.getConfig() var settings = config.exchanges.settings @@ -31,9 +31,8 @@ function buildRates () { var cashOutCommission = settings.fiatCommission || cashInCommission var rates = {} - cryptoCoins.forEach(function (coin) { - var cryptoCode = coin.unitCode - var rate = plugins.getDeviceRate(coin).rates + cryptoCodes.forEach(function (cryptoCode) { + var rate = plugins.getDeviceRate(cryptoCode).rates rates[cryptoCode] = { cashIn: rate.ask.times(cashInCommission), cashOut: rate.bid.div(cashOutCommission) @@ -44,12 +43,12 @@ function buildRates () { } function buildBalances () { - var cryptoCoins = plugins.getCryptoCoins() + var cryptoCodes = plugins.getcryptoCodes() var balances = {} - cryptoCoins.forEach(function (coin) { - var balance = plugins.fiatBalance(coin) - balances[coin] = balance + cryptoCodes.forEach(function (cryptoCode) { + var balance = plugins.fiatBalance(cryptoCode) + balances[cryptoCode] = balance }) return balances diff --git a/todo.txt b/todo.txt index 7a9f9bc1..d0493c65 100644 --- a/todo.txt +++ b/todo.txt @@ -1,5 +1,11 @@ -- rethink scale names: is unit satoshi or btc? cryptoAtom, cryptoAtom -- cryptoCoin is record, not code - specify crypto and fiat for trades - make sure ticker is in full coins - getDeviceRate should return bignumber +- test with l-m + +backwards compatibility: + +- new l-m must be backwards compatible with old l-s + +- parse out bignumber when loading in routes +- making a trade -- convert to units