From 45e6e2b82d84cb3fefe9aafd74ced76644a26148 Mon Sep 17 00:00:00 2001 From: Josh Harvey Date: Sat, 24 Jun 2017 01:33:10 +0300 Subject: [PATCH] Support ZEC --- lib/admin/config.js | 19 ++++++++---- lib/coin-utils.js | 15 ++++++++-- lib/plugins/common/kraken.js | 30 ++++++++++++------- lib/plugins/exchange/kraken/kraken.js | 17 +++-------- lib/plugins/ticker/kraken/kraken.js | 12 ++------ lib/plugins/wallet/mock-wallet/mock-wallet.js | 6 +++- package.json | 1 + public/elm.js | 17 +++++++++-- schemas/kraken.json | 22 ++++++++++++++ yarn.lock | 13 +++++++- 10 files changed, 105 insertions(+), 47 deletions(-) create mode 100644 schemas/kraken.json diff --git a/lib/admin/config.js b/lib/admin/config.js index 8a53eda8..2b8dc1e0 100644 --- a/lib/admin/config.js +++ b/lib/admin/config.js @@ -155,27 +155,34 @@ function fetchData () { return machineLoader.getMachineNames() .then(machineList => ({ currencies: massageCurrencies(currencies), - cryptoCurrencies: [{crypto: 'BTC', display: 'Bitcoin'}, {crypto: 'ETH', display: 'Ethereum'}], + cryptoCurrencies: [ + {crypto: 'BTC', display: 'Bitcoin'}, + {crypto: 'ETH', display: 'Ethereum'}, + {crypto: 'ZEC', display: 'Zcash'} + ], languages: languages, countries, accounts: [ {code: 'bitpay', display: 'Bitpay', class: 'ticker', cryptos: ['BTC']}, - {code: 'kraken', display: 'Kraken', class: 'ticker', cryptos: ['BTC', 'ETH']}, + {code: 'kraken', display: 'Kraken', class: 'ticker', cryptos: ['BTC', 'ETH', 'ZEC']}, {code: 'bitstamp', display: 'Bitstamp', class: 'ticker', cryptos: ['BTC']}, {code: 'coinbase', display: 'Coinbase', class: 'ticker', cryptos: ['BTC', 'ETH']}, {code: 'bitcoind', display: 'bitcoind', class: 'wallet', cryptos: ['BTC']}, {code: 'bitgo', display: 'BitGo', class: 'wallet', cryptos: ['BTC']}, {code: 'geth', display: 'geth', class: 'wallet', cryptos: ['ETH']}, - {code: 'mock-wallet', display: 'Mock wallet', class: 'wallet', cryptos: ['BTC', 'ETH']}, - {code: 'no-exchange', display: 'No exchange', class: 'exchange', cryptos: ['BTC', 'ETH']}, - {code: 'mock-exchange', display: 'Mock exchange', class: 'exchange', cryptos: ['BTC', 'ETH']}, + {code: 'zcashd', display: 'zcashd', class: 'wallet', cryptos: ['ZEC']}, + {code: 'bitstamp', display: 'Bitstamp', class: 'exchange', cryptos: ['BTC']}, + {code: 'kraken', display: 'Kraken', class: 'exchange', cryptos: ['BTC', 'ETH', 'ZEC']}, + {code: 'mock-wallet', display: 'Mock wallet', class: 'wallet', cryptos: ['BTC', 'ETH', 'ZEC']}, + {code: 'no-exchange', display: 'No exchange', class: 'exchange', cryptos: ['BTC', 'ETH', 'ZEC']}, + {code: 'mock-exchange', display: 'Mock exchange', class: 'exchange', cryptos: ['BTC', 'ETH', 'ZEC']}, {code: 'mock-sms', display: 'Mock SMS', class: 'sms'}, {code: 'mock-id-verify', display: 'Mock ID verifier', class: 'idVerifier'}, {code: 'twilio', display: 'Twilio', class: 'sms'}, {code: 'mailjet', display: 'Mailjet', class: 'email'}, {code: 'all-zero-conf', display: 'All pass', class: 'zeroConf'}, {code: 'blockcypher', display: 'Blockcypher', class: 'zeroConf', cryptos: ['BTC']}, - {code: 'mock-zero-conf', display: 'Mock 0-conf', class: 'zeroConf', cryptos: ['BTC']} + {code: 'mock-zero-conf', display: 'Mock 0-conf', class: 'zeroConf', cryptos: ['BTC', 'ZEC']} ], machines: machineList.map(machine => ({machine: machine.deviceId, display: machine.name})) })) diff --git a/lib/coin-utils.js b/lib/coin-utils.js index df069925..9102f3b6 100644 --- a/lib/coin-utils.js +++ b/lib/coin-utils.js @@ -1,19 +1,28 @@ const coins = { BTC: {unitScale: 8}, - ETH: {unitScale: 18} + ETH: {unitScale: 18}, + ZEC: {unitScale: 8} } const cryptoDisplays = [ {cryptoCode: 'BTC', display: 'Bitcoin'}, - {cryptoCode: 'ETH', display: 'Ethereum'} + {cryptoCode: 'ETH', display: 'Ethereum'}, + {cryptoCode: 'ZEC', display: 'Zcash'} ] -module.exports = {coins, cryptoDisplays, buildUrl} +module.exports = {coins, cryptoDisplays, buildUrl, unitScale} function buildUrl (cryptoCode, address) { switch (cryptoCode) { case 'BTC': return `bitcoin:${address}` case 'ETH': return `ethereum:${address}` + case 'ZEC': return `zcash:${address}` default: throw new Error(`Unsupported crypto: ${cryptoCode}`) } } + +function unitScale (cryptoCode) { + const scaleRec = coins[cryptoCode] + if (!scaleRec) throw new Error(`Unsupported crypto: ${cryptoCode}`) + return scaleRec.unitScale +} diff --git a/lib/plugins/common/kraken.js b/lib/plugins/common/kraken.js index fbf34fb8..5c644fe3 100644 --- a/lib/plugins/common/kraken.js +++ b/lib/plugins/common/kraken.js @@ -1,17 +1,27 @@ -var BigNumber = require('bignumber.js') +const BigNumber = require('bignumber.js') -var TEN = new BigNumber(10) +const coinUtils = require('../../coin-utils') -var UNIT_SCALES = { - BTC: 8, - ETH: 18 +const TEN = new BigNumber(10) + +const PAIRS = { + BTC: { + USD: 'XXBTZUSD', + EUR: 'XXBTZEUR' + }, + ETH: { + USD: 'XETHZUSD', + EUR: 'XETHZEUR' + }, + ZEC: { + USD: 'XZECZUSD', + EUR: 'XZECZEUR' + } } -function unitScale (cryptoCoin) { - return UNIT_SCALES[cryptoCoin] -} +module.exports = {PAIRS, toUnit} -exports.toUnit = function toUnit (cryptoAtoms, cryptoCoin) { - var scale = TEN.pow(unitScale(cryptoCoin)) +function toUnit (cryptoAtoms, cryptoCoin) { + var scale = TEN.pow(coinUtils.unitScale(cryptoCoin)) return cryptoAtoms.div(scale) } diff --git a/lib/plugins/exchange/kraken/kraken.js b/lib/plugins/exchange/kraken/kraken.js index d939a4bc..8d90ca11 100644 --- a/lib/plugins/exchange/kraken/kraken.js +++ b/lib/plugins/exchange/kraken/kraken.js @@ -1,16 +1,7 @@ const Kraken = require('kraken-api') -const coinmath = require('../common/kraken') +const common = require('../common/kraken') -var PAIRS = { - BTC: { - USD: 'XXBTZUSD', - EUR: 'XXBTZEUR' - }, - ETH: { - USD: 'XETHZUSD', - EUR: 'XETHZEUR' - } -} +var PAIRS = common.PAIRS module.exports = {buy, sell} @@ -23,8 +14,8 @@ function sell (account, cryptoAtoms, fiatCode, cryptoCode) { } function trade (account, type, cryptoAtoms, fiatCode, cryptoCode) { - const kraken = new Kraken(account.key, account.secret) - const amount = coinmath.toUnit(cryptoAtoms, cryptoCode) + const kraken = new Kraken(account.apiKey, account.privateKey) + const amount = common.toUnit(cryptoAtoms, cryptoCode) if (amount.lte('0.01')) { const err = new Error('Order size too small') diff --git a/lib/plugins/ticker/kraken/kraken.js b/lib/plugins/ticker/kraken/kraken.js index 05600059..64ffbffb 100644 --- a/lib/plugins/ticker/kraken/kraken.js +++ b/lib/plugins/ticker/kraken/kraken.js @@ -2,20 +2,12 @@ const axios = require('axios') const _ = require('lodash/fp') const BN = require('../../../bn') +const common = require('../../common/kraken') exports.NAME = 'Kraken' exports.SUPPORTED_MODULES = ['ticker'] -const PAIRS = { - BTC: { - USD: 'XXBTZUSD', - EUR: 'XXBTZEUR' - }, - ETH: { - USD: 'XETHZUSD', - EUR: 'XETHZEUR' - } -} +const PAIRS = common.PAIRS function findCurrency (fxRates, fiatCode) { const rates = _.find(_.matchesProperty('code', fiatCode), fxRates) diff --git a/lib/plugins/wallet/mock-wallet/mock-wallet.js b/lib/plugins/wallet/mock-wallet/mock-wallet.js index 4773fc2a..66591705 100644 --- a/lib/plugins/wallet/mock-wallet/mock-wallet.js +++ b/lib/plugins/wallet/mock-wallet/mock-wallet.js @@ -15,6 +15,7 @@ function balance (account, cryptoCode) { .then(() => { if (cryptoCode === 'BTC') return BN(1e8 * 10) if (cryptoCode === 'ETH') return BN(1e18 * 10) + if (cryptoCode === 'ZEC') return BN(1e8 * 10) throw new Error('Unsupported crypto: ' + cryptoCode) }) } @@ -24,6 +25,7 @@ function pendingBalance (account, cryptoCode) { .then(() => { if (cryptoCode === 'BTC') return BN(1e8 * 10.1) if (cryptoCode === 'ETH') return BN(1e18 * 10.1) + if (cryptoCode === 'ZEC') return BN(1e8 * 10.1) throw new Error('Unsupported crypto: ' + cryptoCode) }) } @@ -33,16 +35,18 @@ function confirmedBalance (account, cryptoCode) { .then(() => { if (cryptoCode === 'BTC') return BN(1e8 * 10) if (cryptoCode === 'ETH') return BN(1e18 * 10) + if (cryptoCode === 'ZEC') return BN(1e8 * 10) throw new Error('Unsupported crypto: ' + cryptoCode) }) } // Note: This makes it easier to test insufficient funds errors -let sendCount = 0 +let sendCount = 100 function isInsufficient (cryptoAtoms, cryptoCode) { if (cryptoCode === 'BTC') return cryptoAtoms.gt(1e5 * 10 * sendCount) if (cryptoCode === 'ETH') return cryptoAtoms.gt(1e18 * 0.25 * sendCount) + if (cryptoCode === 'ZEC') return cryptoAtoms.gt(1e5 * 0.25 * sendCount) throw new Error('Unsupported crypto: ' + cryptoCode) } diff --git a/package.json b/package.json index 005949bc..d886e3ef 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "express-ws": "^3.0.0", "got": "^7.0.0", "helmet": "^3.6.1", + "kraken-api": "^0.1.7", "lodash": "^4.17.2", "mem": "^1.1.0", "migrate": "^0.2.2", diff --git a/public/elm.js b/public/elm.js index ec999d00..bbabb758 100644 --- a/public/elm.js +++ b/public/elm.js @@ -32590,6 +32590,8 @@ var _user$project$Transaction$multiplier = function (code) { return 1.0e8; case 'ETH': return 1.0e18; + case 'ZEC': + return 1.0e8; default: return 1.0; } @@ -33670,8 +33672,8 @@ var _user$project$NavBar$view = F2( ctor: '::', _0: { ctor: '_Tuple3', - _0: 'Twilio', - _1: _user$project$CoreTypes$AccountRoute('twilio'), + _0: 'Kraken', + _1: _user$project$CoreTypes$AccountRoute('kraken'), _2: true }, _1: { @@ -33682,7 +33684,16 @@ var _user$project$NavBar$view = F2( _1: _user$project$CoreTypes$AccountRoute('mailjet'), _2: true }, - _1: {ctor: '[]'} + _1: { + ctor: '::', + _0: { + ctor: '_Tuple3', + _0: 'Twilio', + _1: _user$project$CoreTypes$AccountRoute('twilio'), + _2: true + }, + _1: {ctor: '[]'} + } } } } diff --git a/schemas/kraken.json b/schemas/kraken.json new file mode 100644 index 00000000..da99418c --- /dev/null +++ b/schemas/kraken.json @@ -0,0 +1,22 @@ +{ + "code": "kraken", + "display": "Kraken", + "fields": [ + { + "code": "apiKey", + "display": "API Key", + "fieldType": "string", + "secret": true, + "required": true, + "value": "" + }, + { + "code": "privateKey", + "display": "Private Key", + "fieldType": "password", + "secret": true, + "required": true, + "value": "" + } + ] +} diff --git a/yarn.lock b/yarn.lock index 7b75a44f..6dead1a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3108,6 +3108,13 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.1.5" +kraken-api@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/kraken-api/-/kraken-api-0.1.7.tgz#b752435e6917ba71d9e734a5fed3fa5961622de8" + dependencies: + querystring ">=0.2.0" + request ">=2.27.0" + last-line-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/last-line-stream/-/last-line-stream-1.0.0.tgz#d1b64d69f86ff24af2d04883a2ceee14520a5600" @@ -4122,6 +4129,10 @@ qs@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/qs/-/qs-4.0.0.tgz#c31d9b74ec27df75e543a86c78728ed8d4623607" +querystring@>=0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + ramda@^0.22.1: version "0.22.1" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.22.1.tgz#031da0c3df417c5b33c96234757eb37033f36a0e" @@ -4356,7 +4367,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@2.81.x, request@^2.81.0: +request@2.81.x, request@>=2.27.0, request@^2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: