generalize bitcoind json-rpc
This commit is contained in:
parent
f70211d774
commit
2c525c1e5c
11 changed files with 468 additions and 121 deletions
|
|
@ -150,6 +150,7 @@ const mapLanguage = lang => {
|
||||||
|
|
||||||
const supportedLanguages = languageRec.supported
|
const supportedLanguages = languageRec.supported
|
||||||
const languages = supportedLanguages.map(mapLanguage).filter(r => r)
|
const languages = supportedLanguages.map(mapLanguage).filter(r => r)
|
||||||
|
const ALL_CRYPTOS = ['BTC', 'ETH', 'LTC', 'DASH', 'ZEC']
|
||||||
|
|
||||||
function fetchData () {
|
function fetchData () {
|
||||||
return machineLoader.getMachineNames()
|
return machineLoader.getMachineNames()
|
||||||
|
|
@ -158,31 +159,35 @@ function fetchData () {
|
||||||
cryptoCurrencies: [
|
cryptoCurrencies: [
|
||||||
{crypto: 'BTC', display: 'Bitcoin'},
|
{crypto: 'BTC', display: 'Bitcoin'},
|
||||||
{crypto: 'ETH', display: 'Ethereum'},
|
{crypto: 'ETH', display: 'Ethereum'},
|
||||||
|
{crypto: 'LTC', display: 'Litecoin'},
|
||||||
|
{crypto: 'DASH', display: 'Dash'},
|
||||||
{crypto: 'ZEC', display: 'Zcash'}
|
{crypto: 'ZEC', display: 'Zcash'}
|
||||||
],
|
],
|
||||||
languages: languages,
|
languages: languages,
|
||||||
countries,
|
countries,
|
||||||
accounts: [
|
accounts: [
|
||||||
{code: 'bitpay', display: 'Bitpay', class: 'ticker', cryptos: ['BTC']},
|
{code: 'bitpay', display: 'Bitpay', class: 'ticker', cryptos: ['BTC']},
|
||||||
{code: 'kraken', display: 'Kraken', class: 'ticker', cryptos: ['BTC', 'ETH', 'ZEC']},
|
{code: 'kraken', display: 'Kraken', class: 'ticker', cryptos: ['BTC', 'ETH', 'LTC', 'DASH', 'ZEC']},
|
||||||
{code: 'bitstamp', display: 'Bitstamp', class: 'ticker', cryptos: ['BTC']},
|
{code: 'bitstamp', display: 'Bitstamp', class: 'ticker', cryptos: ['BTC', 'LTC']},
|
||||||
{code: 'coinbase', display: 'Coinbase', class: 'ticker', cryptos: ['BTC', 'ETH']},
|
{code: 'coinbase', display: 'Coinbase', class: 'ticker', cryptos: ['BTC', 'ETH', 'LTC']},
|
||||||
{code: 'bitcoind', display: 'bitcoind', class: 'wallet', cryptos: ['BTC']},
|
{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: 'geth', display: 'geth', class: 'wallet', cryptos: ['ETH']},
|
||||||
{code: 'zcashd', display: 'zcashd', class: 'wallet', cryptos: ['ZEC']},
|
{code: 'zcashd', display: 'zcashd', class: 'wallet', cryptos: ['ZEC']},
|
||||||
|
{code: 'litecoind', display: 'litecoind', class: 'wallet', cryptos: ['LTC']},
|
||||||
|
{code: 'dashd', display: 'dashd', class: 'wallet', cryptos: ['DASH']},
|
||||||
|
{code: 'bitgo', display: 'BitGo', class: 'wallet', cryptos: ['BTC']},
|
||||||
{code: 'bitstamp', display: 'Bitstamp', class: 'exchange', cryptos: ['BTC']},
|
{code: 'bitstamp', display: 'Bitstamp', class: 'exchange', cryptos: ['BTC']},
|
||||||
{code: 'kraken', display: 'Kraken', class: 'exchange', cryptos: ['BTC', 'ETH', 'ZEC']},
|
{code: 'kraken', display: 'Kraken', class: 'exchange', cryptos: ['BTC', 'ETH', 'LTC', 'DASH', 'ZEC']},
|
||||||
{code: 'mock-wallet', display: 'Mock wallet', class: 'wallet', cryptos: ['BTC', 'ETH', 'ZEC']},
|
{code: 'mock-wallet', display: 'Mock wallet', class: 'wallet', cryptos: ALL_CRYPTOS},
|
||||||
{code: 'no-exchange', display: 'No exchange', class: 'exchange', cryptos: ['BTC', 'ETH', 'ZEC']},
|
{code: 'no-exchange', display: 'No exchange', class: 'exchange', cryptos: ALL_CRYPTOS},
|
||||||
{code: 'mock-exchange', display: 'Mock exchange', class: 'exchange', cryptos: ['BTC', 'ETH', 'ZEC']},
|
{code: 'mock-exchange', display: 'Mock exchange', class: 'exchange', cryptos: ALL_CRYPTOS},
|
||||||
{code: 'mock-sms', display: 'Mock SMS', class: 'sms'},
|
{code: 'mock-sms', display: 'Mock SMS', class: 'sms'},
|
||||||
{code: 'mock-id-verify', display: 'Mock ID verifier', class: 'idVerifier'},
|
{code: 'mock-id-verify', display: 'Mock ID verifier', class: 'idVerifier'},
|
||||||
{code: 'twilio', display: 'Twilio', class: 'sms'},
|
{code: 'twilio', display: 'Twilio', class: 'sms'},
|
||||||
{code: 'mailjet', display: 'Mailjet', class: 'email'},
|
{code: 'mailjet', display: 'Mailjet', class: 'email'},
|
||||||
{code: 'all-zero-conf', display: 'All pass', class: 'zeroConf'},
|
{code: 'all-zero-conf', display: 'All pass', class: 'zeroConf'},
|
||||||
{code: 'blockcypher', display: 'Blockcypher', class: 'zeroConf', cryptos: ['BTC']},
|
{code: 'blockcypher', display: 'Blockcypher', class: 'zeroConf', cryptos: ['BTC']},
|
||||||
{code: 'mock-zero-conf', display: 'Mock 0-conf', class: 'zeroConf', cryptos: ['BTC', 'ZEC']}
|
{code: 'mock-zero-conf', display: 'Mock 0-conf', class: 'zeroConf', cryptos: ['BTC', 'ZEC', 'LTC', 'DASH']}
|
||||||
],
|
],
|
||||||
machines: machineList.map(machine => ({machine: machine.deviceId, display: machine.name}))
|
machines: machineList.map(machine => ({machine: machine.deviceId, display: machine.name}))
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,40 @@
|
||||||
|
const _ = require('lodash/fp')
|
||||||
|
|
||||||
const coins = {
|
const coins = {
|
||||||
BTC: {unitScale: 8},
|
BTC: {unitScale: 8},
|
||||||
ETH: {unitScale: 18},
|
ETH: {unitScale: 18},
|
||||||
ZEC: {unitScale: 8}
|
ZEC: {unitScale: 8},
|
||||||
|
LTC: {unitScale: 8},
|
||||||
|
DASH: {unitScale: 8}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cryptoDisplays = [
|
const cryptoDisplays = [
|
||||||
{cryptoCode: 'BTC', display: 'Bitcoin'},
|
{cryptoCode: 'BTC', display: 'Bitcoin'},
|
||||||
{cryptoCode: 'ETH', display: 'Ethereum'},
|
{cryptoCode: 'ETH', display: 'Ethereum'},
|
||||||
{cryptoCode: 'ZEC', display: 'Zcash'}
|
{cryptoCode: 'ZEC', display: 'Zcash'},
|
||||||
|
{cryptoCode: 'LTC', display: 'Litecoin'},
|
||||||
|
{cryptoCode: 'DASH', display: 'Dash'}
|
||||||
]
|
]
|
||||||
|
|
||||||
module.exports = {coins, cryptoDisplays, buildUrl, unitScale}
|
module.exports = {coins, cryptoDisplays, buildUrl, unitScale, display}
|
||||||
|
|
||||||
function buildUrl (cryptoCode, address) {
|
function buildUrl (cryptoCode, address) {
|
||||||
switch (cryptoCode) {
|
switch (cryptoCode) {
|
||||||
case 'BTC': return `bitcoin:${address}`
|
case 'BTC': return `bitcoin:${address}`
|
||||||
case 'ETH': return `ethereum:${address}`
|
case 'ETH': return `ethereum:${address}`
|
||||||
case 'ZEC': return `zcash:${address}`
|
case 'ZEC': return `zcash:${address}`
|
||||||
|
case 'LTC': return `litecoin:${address}`
|
||||||
|
case 'DASH': return `dash:${address}`
|
||||||
default: throw new Error(`Unsupported crypto: ${cryptoCode}`)
|
default: throw new Error(`Unsupported crypto: ${cryptoCode}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function display (cryptoCode) {
|
||||||
|
const rec = _.find(['cryptoCode', cryptoCode], cryptoDisplays)
|
||||||
|
if (!rec) throw new Error(`Unsupported crypto: ${cryptoCode}`)
|
||||||
|
return rec.display
|
||||||
|
}
|
||||||
|
|
||||||
function unitScale (cryptoCode) {
|
function unitScale (cryptoCode) {
|
||||||
const scaleRec = coins[cryptoCode]
|
const scaleRec = coins[cryptoCode]
|
||||||
if (!scaleRec) throw new Error(`Unsupported crypto: ${cryptoCode}`)
|
if (!scaleRec) throw new Error(`Unsupported crypto: ${cryptoCode}`)
|
||||||
|
|
|
||||||
|
|
@ -176,12 +176,12 @@ function plugins (settings, deviceId) {
|
||||||
const minimumTx = BN(config.minimumTx)
|
const minimumTx = BN(config.minimumTx)
|
||||||
const cashInFee = BN(config.cashInFee)
|
const cashInFee = BN(config.cashInFee)
|
||||||
|
|
||||||
const coinSettings = {
|
return {
|
||||||
|
cryptoCode: coin,
|
||||||
|
display: coinUtils.display(coin),
|
||||||
minimumTx: BN.max(minimumTx, cashInFee),
|
minimumTx: BN.max(minimumTx, cashInFee),
|
||||||
cashInFee: cashInFee
|
cashInFee: cashInFee
|
||||||
}
|
}
|
||||||
|
|
||||||
return [coin, coinSettings]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pollQueries (serialNumber, deviceTime, deviceRec) {
|
function pollQueries (serialNumber, deviceTime, deviceRec) {
|
||||||
|
|
@ -211,7 +211,7 @@ function plugins (settings, deviceId) {
|
||||||
cassettes,
|
cassettes,
|
||||||
rates: buildRates(tickers),
|
rates: buildRates(tickers),
|
||||||
balances: buildBalances(balances),
|
balances: buildBalances(balances),
|
||||||
coinSettings: _.fromPairs(_.map(mapCoinSettings, cryptoCodes)),
|
coins: _.map(mapCoinSettings, cryptoCodes),
|
||||||
configVersion
|
configVersion
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
46
lib/plugins/common/json-rpc.js
Normal file
46
lib/plugins/common/json-rpc.js
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
// JSON-RPC for bitcoind-like interfaces
|
||||||
|
const axios = require('axios')
|
||||||
|
const uuid = require('uuid')
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
module.exports = {fetch, parseConf}
|
||||||
|
|
||||||
|
function fetch (account, method, params) {
|
||||||
|
const data = {
|
||||||
|
method,
|
||||||
|
params,
|
||||||
|
id: uuid.v4()
|
||||||
|
}
|
||||||
|
|
||||||
|
return axios({
|
||||||
|
method: 'post',
|
||||||
|
auth: {username: account.username, password: account.password},
|
||||||
|
url: `http://localhost:${account.port}`,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
.then(r => {
|
||||||
|
if (r.error) throw r.error
|
||||||
|
return r.data.result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err.response.data.error)
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseConf (confPath) {
|
||||||
|
const conf = fs.readFileSync(confPath)
|
||||||
|
const lines = conf.toString().split('\n', 2)
|
||||||
|
|
||||||
|
const res = {}
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const keyVal = lines[i].split('=')
|
||||||
|
|
||||||
|
// skip when value is empty
|
||||||
|
if (!keyVal[1]) continue
|
||||||
|
|
||||||
|
res[keyVal[0]] = keyVal[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,14 @@ const PAIRS = {
|
||||||
ZEC: {
|
ZEC: {
|
||||||
USD: 'XZECZUSD',
|
USD: 'XZECZUSD',
|
||||||
EUR: 'XZECZEUR'
|
EUR: 'XZECZEUR'
|
||||||
|
},
|
||||||
|
LTC: {
|
||||||
|
USD: 'XLTCZUSD',
|
||||||
|
EUR: 'XLTCZEUR'
|
||||||
|
},
|
||||||
|
DASH: {
|
||||||
|
USD: 'DASHUSD',
|
||||||
|
EUR: 'DASHEUR'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,24 @@
|
||||||
const path = require('path')
|
const jsonRpc = require('../../common/json-rpc')
|
||||||
const os = require('os')
|
|
||||||
const RpcClient = require('bitcoind-rpc')
|
|
||||||
const fs = require('fs')
|
|
||||||
const pify = require('pify')
|
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
|
const options = require('../../../options')
|
||||||
const BN = require('../../../bn')
|
const BN = require('../../../bn')
|
||||||
const E = require('../../../error')
|
const E = require('../../../error')
|
||||||
|
|
||||||
const NAME = 'Bitcoind'
|
const DEFAULT_PORT = 8332
|
||||||
|
|
||||||
const SATOSHI_SHIFT = 8
|
const SATOSHI_SHIFT = 8
|
||||||
|
|
||||||
const configPath = path.resolve(os.homedir(), '.bitcoin', 'bitcoin.conf')
|
const configPath = path.resolve(options.blockchainDir, 'bitcoin.conf')
|
||||||
const pluginConfig = {
|
const config = jsonRpc.parseConf(configPath)
|
||||||
account: '',
|
|
||||||
bitcoindConfigurationPath: configPath
|
const rpcConfig = {
|
||||||
|
username: config.rpcuser,
|
||||||
|
password: config.rpcpassword,
|
||||||
|
port: config.rpcport || DEFAULT_PORT
|
||||||
}
|
}
|
||||||
|
|
||||||
function initRpc () {
|
function fetch (method, params) {
|
||||||
const bitcoindConf = parseConf(pluginConfig.bitcoindConfigurationPath)
|
return jsonRpc.fetch(rpcConfig, method, params)
|
||||||
|
|
||||||
const rpcConfig = {
|
|
||||||
protocol: 'http',
|
|
||||||
user: bitcoindConf.rpcuser,
|
|
||||||
pass: bitcoindConf.rpcpassword
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RpcClient(rpcConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* initialize RpcClient
|
|
||||||
*/
|
|
||||||
function parseConf (confPath) {
|
|
||||||
const conf = fs.readFileSync(confPath)
|
|
||||||
const lines = conf.toString().split('\n')
|
|
||||||
|
|
||||||
const res = {}
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
|
||||||
const keyVal = lines[i].split('=')
|
|
||||||
|
|
||||||
// skip when value is empty
|
|
||||||
if (!keyVal[1]) continue
|
|
||||||
|
|
||||||
res[keyVal[0]] = keyVal[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkCryptoCode (cryptoCode) {
|
function checkCryptoCode (cryptoCode) {
|
||||||
|
|
@ -54,20 +26,10 @@ function checkCryptoCode (cryptoCode) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
function accountBalance (account, cryptoCode, confirmations) {
|
function accountBalance (acount, cryptoCode, confirmations) {
|
||||||
return checkCryptoCode(cryptoCode)
|
return checkCryptoCode(cryptoCode)
|
||||||
.then(() => {
|
.then(() => fetch('getbalance', ['', confirmations]))
|
||||||
const rpc = initRpc()
|
.then(r => BN(r).shift(SATOSHI_SHIFT).round())
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
rpc.getBalance(pluginConfig.account, confirmations, (err, result) => {
|
|
||||||
if (err) return reject(err)
|
|
||||||
if (result.error) reject(new Error(err))
|
|
||||||
|
|
||||||
return resolve(BN(result.result).shift(SATOSHI_SHIFT).round())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want a balance that includes all spends (0 conf) but only deposits that
|
// We want a balance that includes all spends (0 conf) but only deposits that
|
||||||
|
|
@ -77,42 +39,25 @@ function balance (account, cryptoCode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendCoins (account, address, cryptoAtoms, cryptoCode) {
|
function sendCoins (account, address, cryptoAtoms, cryptoCode) {
|
||||||
const rpc = initRpc()
|
|
||||||
const confirmations = 1
|
const confirmations = 1
|
||||||
const bitcoins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
|
const bitcoins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
|
||||||
|
|
||||||
return checkCryptoCode(cryptoCode)
|
return checkCryptoCode(cryptoCode)
|
||||||
.then(() => {
|
.then(() => fetch('sendfrom', [address, bitcoins, confirmations]))
|
||||||
return new Promise((resolve, reject) => {
|
.catch(err => {
|
||||||
rpc.sendFrom(pluginConfig.account, address, bitcoins, confirmations, (err, result) => {
|
if (err.code === -6) throw new E.InsufficientFundsError()
|
||||||
if (err) {
|
throw err
|
||||||
if (err.code === -6) return reject(new E.InsufficientFundsError())
|
|
||||||
return reject(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(result.result)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function newAddress (account, info) {
|
function newAddress (account, info) {
|
||||||
return checkCryptoCode(info.cryptoCode)
|
return checkCryptoCode(info.cryptoCode)
|
||||||
.then(() => {
|
.then(() => fetch('getnewaddress'))
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const rpc = initRpc()
|
|
||||||
rpc.getNewAddress((err, result) => {
|
|
||||||
if (err) return reject(err)
|
|
||||||
resolve(result.result)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addressBalance (address, confs) {
|
function addressBalance (address, confs) {
|
||||||
const rpc = initRpc()
|
return fetch('getreceivedbyaddress', [address, confs])
|
||||||
return pify(rpc.getReceivedByAddress.bind(rpc))(address, confs)
|
.then(r => BN(r).shift(SATOSHI_SHIFT).round())
|
||||||
.then(r => BN(r.result).shift(SATOSHI_SHIFT).round())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmedBalance (address, cryptoCode) {
|
function confirmedBalance (address, cryptoCode) {
|
||||||
|
|
@ -159,10 +104,7 @@ function newFunding (account, cryptoCode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
NAME,
|
|
||||||
balance,
|
balance,
|
||||||
pendingBalance,
|
|
||||||
confirmedBalance,
|
|
||||||
sendCoins,
|
sendCoins,
|
||||||
newAddress,
|
newAddress,
|
||||||
getStatus,
|
getStatus,
|
||||||
|
|
|
||||||
114
lib/plugins/wallet/dashd/dashd.js
Normal file
114
lib/plugins/wallet/dashd/dashd.js
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
const jsonRpc = require('../../common/json-rpc')
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
|
const options = require('../../../options')
|
||||||
|
|
||||||
|
const BN = require('../../../bn')
|
||||||
|
const E = require('../../../error')
|
||||||
|
|
||||||
|
const DEFAULT_PORT = 9998
|
||||||
|
const SATOSHI_SHIFT = 8
|
||||||
|
|
||||||
|
const configPath = path.resolve(options.blockchainDir, 'dash.conf')
|
||||||
|
|
||||||
|
const config = jsonRpc.parseConf(configPath)
|
||||||
|
|
||||||
|
const rpcConfig = {
|
||||||
|
username: config.rpcuser,
|
||||||
|
password: config.rpcpassword,
|
||||||
|
port: config.rpcport || DEFAULT_PORT
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('DEBUG100: %j', rpcConfig)
|
||||||
|
function fetch (method, params) {
|
||||||
|
return jsonRpc.fetch(rpcConfig, method, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCryptoCode (cryptoCode) {
|
||||||
|
if (cryptoCode !== 'DASH') return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode))
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
function accountBalance (acount, cryptoCode, confirmations) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => fetch('getbalance', ['', confirmations]))
|
||||||
|
.then(r => BN(r).shift(SATOSHI_SHIFT).round())
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want a balance that includes all spends (0 conf) but only deposits that
|
||||||
|
// have at least 1 confirmation. getbalance does this for us automatically.
|
||||||
|
function balance (account, cryptoCode) {
|
||||||
|
return accountBalance(account, cryptoCode, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendCoins (account, address, cryptoAtoms, cryptoCode) {
|
||||||
|
const coins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
|
||||||
|
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => fetch('sendtoaddress', [address, coins]))
|
||||||
|
.catch(err => {
|
||||||
|
if (err.code === -6) throw new E.InsufficientFundsError()
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function newAddress (account, info) {
|
||||||
|
return checkCryptoCode(info.cryptoCode)
|
||||||
|
.then(() => fetch('getnewaddress'))
|
||||||
|
}
|
||||||
|
|
||||||
|
function addressBalance (address, confs) {
|
||||||
|
return fetch('getreceivedbyaddress', [address, confs])
|
||||||
|
.then(r => BN(r).shift(SATOSHI_SHIFT).round())
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmedBalance (address, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => addressBalance(address, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
function pendingBalance (address, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => addressBalance(address, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatus (account, toAddress, requested, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => confirmedBalance(toAddress, cryptoCode))
|
||||||
|
.then(confirmed => {
|
||||||
|
if (confirmed.gte(requested)) return {status: 'confirmed'}
|
||||||
|
|
||||||
|
return pendingBalance(toAddress, cryptoCode)
|
||||||
|
.then(pending => {
|
||||||
|
if (pending.gte(requested)) return {status: 'authorized'}
|
||||||
|
if (pending.gt(0)) return {status: 'insufficientFunds'}
|
||||||
|
return {status: 'notSeen'}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function newFunding (account, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => {
|
||||||
|
const promises = [
|
||||||
|
accountBalance(account, cryptoCode, 0),
|
||||||
|
accountBalance(account, cryptoCode, 1),
|
||||||
|
newAddress(account, {cryptoCode})
|
||||||
|
]
|
||||||
|
|
||||||
|
return Promise.all(promises)
|
||||||
|
})
|
||||||
|
.then(([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({
|
||||||
|
fundingPendingBalance,
|
||||||
|
fundingConfirmedBalance,
|
||||||
|
fundingAddress
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
balance,
|
||||||
|
sendCoins,
|
||||||
|
newAddress,
|
||||||
|
getStatus,
|
||||||
|
newFunding
|
||||||
|
}
|
||||||
113
lib/plugins/wallet/litecoind/litecoind.js
Normal file
113
lib/plugins/wallet/litecoind/litecoind.js
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
const jsonRpc = require('../../common/json-rpc')
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
|
const options = require('../../../options')
|
||||||
|
|
||||||
|
const BN = require('../../../bn')
|
||||||
|
const E = require('../../../error')
|
||||||
|
|
||||||
|
const DEFAULT_PORT = 9332
|
||||||
|
const SATOSHI_SHIFT = 8
|
||||||
|
|
||||||
|
const configPath = path.resolve(options.blockchainDir, 'litecoin.conf')
|
||||||
|
const config = jsonRpc.parseConf(configPath)
|
||||||
|
|
||||||
|
const rpcConfig = {
|
||||||
|
username: config.rpcuser,
|
||||||
|
password: config.rpcpassword,
|
||||||
|
port: config.rpcport || DEFAULT_PORT
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetch (method, params) {
|
||||||
|
return jsonRpc.fetch(rpcConfig, method, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCryptoCode (cryptoCode) {
|
||||||
|
if (cryptoCode !== 'LTC') return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode))
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
function accountBalance (acount, cryptoCode, confirmations) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => fetch('getbalance', ['', confirmations]))
|
||||||
|
.then(r => BN(r).shift(SATOSHI_SHIFT).round())
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want a balance that includes all spends (0 conf) but only deposits that
|
||||||
|
// have at least 1 confirmation. getbalance does this for us automatically.
|
||||||
|
function balance (account, cryptoCode) {
|
||||||
|
return accountBalance(account, cryptoCode, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendCoins (account, address, cryptoAtoms, cryptoCode) {
|
||||||
|
const confirmations = 1
|
||||||
|
const bitcoins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
|
||||||
|
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => fetch('sendfrom', [address, bitcoins, confirmations]))
|
||||||
|
.catch(err => {
|
||||||
|
if (err.code === -6) throw new E.InsufficientFundsError()
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function newAddress (account, info) {
|
||||||
|
return checkCryptoCode(info.cryptoCode)
|
||||||
|
.then(() => fetch('getnewaddress'))
|
||||||
|
}
|
||||||
|
|
||||||
|
function addressBalance (address, confs) {
|
||||||
|
return fetch('getreceivedbyaddress', [address, confs])
|
||||||
|
.then(r => BN(r).shift(SATOSHI_SHIFT).round())
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmedBalance (address, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => addressBalance(address, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
function pendingBalance (address, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => addressBalance(address, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatus (account, toAddress, requested, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => confirmedBalance(toAddress, cryptoCode))
|
||||||
|
.then(confirmed => {
|
||||||
|
if (confirmed.gte(requested)) return {status: 'confirmed'}
|
||||||
|
|
||||||
|
return pendingBalance(toAddress, cryptoCode)
|
||||||
|
.then(pending => {
|
||||||
|
if (pending.gte(requested)) return {status: 'authorized'}
|
||||||
|
if (pending.gt(0)) return {status: 'insufficientFunds'}
|
||||||
|
return {status: 'notSeen'}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function newFunding (account, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => {
|
||||||
|
const promises = [
|
||||||
|
accountBalance(account, cryptoCode, 0),
|
||||||
|
accountBalance(account, cryptoCode, 1),
|
||||||
|
newAddress(account, {cryptoCode})
|
||||||
|
]
|
||||||
|
|
||||||
|
return Promise.all(promises)
|
||||||
|
})
|
||||||
|
.then(([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({
|
||||||
|
fundingPendingBalance,
|
||||||
|
fundingConfirmedBalance,
|
||||||
|
fundingAddress
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
balance,
|
||||||
|
sendCoins,
|
||||||
|
newAddress,
|
||||||
|
getStatus,
|
||||||
|
newFunding
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
const BN = require('../../../bn')
|
const BN = require('../../../bn')
|
||||||
const E = require('../../../error')
|
const E = require('../../../error')
|
||||||
|
const coinUtils = require('../../../coin-utils')
|
||||||
|
|
||||||
const NAME = 'FakeWallet'
|
const NAME = 'FakeWallet'
|
||||||
|
|
||||||
|
|
@ -10,44 +11,31 @@ const CONFIRM_TIME = 180 * SECONDS
|
||||||
|
|
||||||
let t0
|
let t0
|
||||||
|
|
||||||
|
function _balance (cryptoCode) {
|
||||||
|
const unitScale = coinUtils.unitScale(cryptoCode)
|
||||||
|
return BN(10).pow(unitScale).mul(10)
|
||||||
|
}
|
||||||
|
|
||||||
function balance (account, cryptoCode) {
|
function balance (account, cryptoCode) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(() => {
|
.then(() => _balance(cryptoCode))
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pendingBalance (account, cryptoCode) {
|
function pendingBalance (account, cryptoCode) {
|
||||||
return Promise.resolve()
|
return balance(account, cryptoCode)
|
||||||
.then(() => {
|
.then(b => b.mul(1.1))
|
||||||
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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmedBalance (account, cryptoCode) {
|
function confirmedBalance (account, cryptoCode) {
|
||||||
return Promise.resolve()
|
return 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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: This makes it easier to test insufficient funds errors
|
// Note: This makes it easier to test insufficient funds errors
|
||||||
let sendCount = 100
|
let sendCount = 100
|
||||||
|
|
||||||
function isInsufficient (cryptoAtoms, cryptoCode) {
|
function isInsufficient (cryptoAtoms, cryptoCode) {
|
||||||
if (cryptoCode === 'BTC') return cryptoAtoms.gt(1e5 * 10 * sendCount)
|
const b = _balance(cryptoCode)
|
||||||
if (cryptoCode === 'ETH') return cryptoAtoms.gt(1e18 * 0.25 * sendCount)
|
return cryptoAtoms.gt(b.div(1000).mul(sendCount))
|
||||||
if (cryptoCode === 'ZEC') return cryptoAtoms.gt(1e5 * 0.25 * sendCount)
|
|
||||||
throw new Error('Unsupported crypto: ' + cryptoCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendCoins (account, toAddress, cryptoAtoms, cryptoCode) {
|
function sendCoins (account, toAddress, cryptoAtoms, cryptoCode) {
|
||||||
|
|
|
||||||
113
lib/plugins/wallet/zcashd/zcashd.js
Normal file
113
lib/plugins/wallet/zcashd/zcashd.js
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
const jsonRpc = require('../../common/json-rpc')
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
|
const options = require('../../../options')
|
||||||
|
|
||||||
|
const BN = require('../../../bn')
|
||||||
|
const E = require('../../../error')
|
||||||
|
|
||||||
|
const DEFAULT_PORT = 8232
|
||||||
|
const SATOSHI_SHIFT = 8
|
||||||
|
|
||||||
|
const configPath = path.resolve(options.blockchainDir, 'zcash.conf')
|
||||||
|
const config = jsonRpc.parseConf(configPath)
|
||||||
|
|
||||||
|
const rpcConfig = {
|
||||||
|
username: config.rpcuser,
|
||||||
|
password: config.rpcpassword,
|
||||||
|
port: config.rpcport || DEFAULT_PORT
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetch (method, params) {
|
||||||
|
return jsonRpc.fetch(rpcConfig, method, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCryptoCode (cryptoCode) {
|
||||||
|
if (cryptoCode !== 'ZEC') return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode))
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
function accountBalance (acount, cryptoCode, confirmations) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => fetch('getbalance', ['', confirmations]))
|
||||||
|
.then(r => BN(r).shift(SATOSHI_SHIFT).round())
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want a balance that includes all spends (0 conf) but only deposits that
|
||||||
|
// have at least 1 confirmation. getbalance does this for us automatically.
|
||||||
|
function balance (account, cryptoCode) {
|
||||||
|
return accountBalance(account, cryptoCode, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendCoins (account, address, cryptoAtoms, cryptoCode) {
|
||||||
|
const confirmations = 1
|
||||||
|
const bitcoins = cryptoAtoms.shift(-SATOSHI_SHIFT).toFixed(8)
|
||||||
|
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => fetch('sendfrom', [address, bitcoins, confirmations]))
|
||||||
|
.catch(err => {
|
||||||
|
if (err.code === -6) throw new E.InsufficientFundsError()
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function newAddress (account, info) {
|
||||||
|
return checkCryptoCode(info.cryptoCode)
|
||||||
|
.then(() => fetch('getnewaddress'))
|
||||||
|
}
|
||||||
|
|
||||||
|
function addressBalance (address, confs) {
|
||||||
|
return fetch('getreceivedbyaddress', [address, confs])
|
||||||
|
.then(r => BN(r).shift(SATOSHI_SHIFT).round())
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmedBalance (address, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => addressBalance(address, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
function pendingBalance (address, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => addressBalance(address, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatus (account, toAddress, requested, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => confirmedBalance(toAddress, cryptoCode))
|
||||||
|
.then(confirmed => {
|
||||||
|
if (confirmed.gte(requested)) return {status: 'confirmed'}
|
||||||
|
|
||||||
|
return pendingBalance(toAddress, cryptoCode)
|
||||||
|
.then(pending => {
|
||||||
|
if (pending.gte(requested)) return {status: 'authorized'}
|
||||||
|
if (pending.gt(0)) return {status: 'insufficientFunds'}
|
||||||
|
return {status: 'notSeen'}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function newFunding (account, cryptoCode) {
|
||||||
|
return checkCryptoCode(cryptoCode)
|
||||||
|
.then(() => {
|
||||||
|
const promises = [
|
||||||
|
accountBalance(account, cryptoCode, 0),
|
||||||
|
accountBalance(account, cryptoCode, 1),
|
||||||
|
newAddress(account, {cryptoCode})
|
||||||
|
]
|
||||||
|
|
||||||
|
return Promise.all(promises)
|
||||||
|
})
|
||||||
|
.then(([fundingPendingBalance, fundingConfirmedBalance, fundingAddress]) => ({
|
||||||
|
fundingPendingBalance,
|
||||||
|
fundingConfirmedBalance,
|
||||||
|
fundingAddress
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
balance,
|
||||||
|
sendCoins,
|
||||||
|
newAddress,
|
||||||
|
getStatus,
|
||||||
|
newFunding
|
||||||
|
}
|
||||||
|
|
@ -32592,6 +32592,10 @@ var _user$project$Transaction$multiplier = function (code) {
|
||||||
return 1.0e18;
|
return 1.0e18;
|
||||||
case 'ZEC':
|
case 'ZEC':
|
||||||
return 1.0e8;
|
return 1.0e8;
|
||||||
|
case 'DASH':
|
||||||
|
return 1.0e8;
|
||||||
|
case 'LTC':
|
||||||
|
return 1.0e8;
|
||||||
default:
|
default:
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue