feat: start working on monero implementation
feat: monero files and config feat: monero interface feat: monero rpc digest request feat: add monero to wallet wizard splash fix: tarball unzipping fix: monero files fix: monero zipped folder path fix: undefined variable chore: xmr stagenet fix: prune-blockchain flag fix: monero wallet arguments chore: rpc-bind-port on monero wallet chore: monero wallet directory fix: fetch digest request fix: monero authentication fix: monero address creation fix: monero config port fix: wallet creation fix: wallet rpc daemon login fix: get monero funding fix: unauthorized issue with multiple requests on Promise.all fix: generate funding addresses fix: destination address balance for XMR cashout fix: transaction creation feat: transaction recommended mixin and ring size fix: monero wallet error handling fix: error handling fix: monero wallet error feat: guide to add new cryptos chore: small code shortcuts fix: crypto implementation guide chore: add XMR to exchange files
This commit is contained in:
parent
9ec871e163
commit
c0808e9bd7
11 changed files with 445 additions and 14 deletions
54
CRYPTO_README.md
Normal file
54
CRYPTO_README.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
## Adding a new cryptocurrency to a Lamassu ATM
|
||||
|
||||
### Structure
|
||||
|
||||
In order to install new coins onto a Lamassu system, there are three points to pay attention:
|
||||
- **The blockchain daemon:** This is a file which will need to be running on the lamassu-server and communicates with the blockchain network. This generally has an integrated wallet, but it may occur for the daemon and the wallet to be completely seperate processes (e.g. XMR). This manager is currently built into the lamassu-server project, but in the future, it will be moved to either lamassu-coins or a new library;
|
||||
- **The wallet plugin:** This uses the capabilities of the RPC (Remote Procedure Call) API built into the blockchain daemon and wallet to create a linking API to standardize with the Lamassu ecosystem. It is built into the lamassu-server project;
|
||||
- **The coin constants:** This has all the information about an implemented coin, including its code, unit scale, daemon RPC ports and all other information to make lamassu-server, lamassu-admin-server and lamassu-machine know about the supported coins. It is built into the lamassu-coins package;
|
||||
|
||||
I'll be using XMR as example for all the steps in this guide.
|
||||
|
||||
#### Blockchain
|
||||
|
||||
Steps to implement a daemon:
|
||||
- Create a file in `lamassu-server/lib/blockchain/<name_of_coin>.js`;
|
||||
- Go to `lamassu-server/lib/blockchain/common.js` and add a new entry to the `BINARIES` object. Each entry has two mandatory fields (`url` and `dir`), and an optional one (`files`).
|
||||
- To get the `url` needed to download the blockchain daemon, you need to access the releases of the daemon of the coin you're working with. For example, for XMR, the daemon can be found in their GitHub releases (https://github.com/monero-project/monero-gui/releases). Get the URL for the Linux 64-bit distribution and note the extension of the file, which will most likely be `.tar.gz` or `.tar.bz2`. For `.tar.bz2`, the coin you're working with needs to be added to the following snippet of code, responsible for the extraction of the downloaded file (`common.js`):
|
||||
```
|
||||
coinRec.cryptoCode === 'XMR'
|
||||
? es(`tar -xf ${downloadFile}`)
|
||||
: es(`tar -xzf ${downloadFile}`)
|
||||
```
|
||||
- To get the `dir`, simply download the file, extract it, and take note of the folder inside the zipped file and the path towards the actual files you want. In XMR's case, `dir` = `monero-x86_64-linux-gnu-v0.17.2.0`, but for BTC it is `bitcoin-0.20.1/bin`
|
||||
- Inside the directory specified in the `dir` field, there can be multiple files inside. In that case, you want to specity the `files` field. This is a multi-dimensional array, where each entry contains a pair of [<file_in_the_downloaded_folder>, <name_with_with_the_file_is_saved_in_the_server>].
|
||||
```
|
||||
[
|
||||
['monerod', 'monerod'],
|
||||
['monero-wallet-rpc', 'monero-wallet-rpc']
|
||||
]
|
||||
```
|
||||
This means that the `monerod` found inside the distribution folder will be saved as `monerod` on the server. Same for the `monero-wallet-rpc`.
|
||||
- Go to `lamassu-server/lib/blockchain/install.js` and add a new entry on the `PLUGINS` object. This entry must import the file created in step 1.
|
||||
- Go to the file created in step one and import the object (which isn't created right now) containing all the information needed of a coin `const coinRec = utils.getCryptoCurrency('<coin_code>')`.
|
||||
- The coin blockchain plugin contains two functions: `setup` and `buildConfig`.
|
||||
- The build config has all the required flags to operate the downloaded daemon, and each coin has their particular set of flags and options, so that specification won't be covered here.
|
||||
- The setup function has a similar structure in any coin, and the differences between them is generally related to how a daemon is ran.
|
||||
|
||||
#### Wallet plugin
|
||||
|
||||
Steps to implement a wallet plugin:
|
||||
- Create a file in `lamassu-server/lib/plugins/wallet/<name_of_daemon>/<name_of_daemon>.js`
|
||||
- The wallet plugin serves as a middleware between the RPC calls supported by each daemon, and the processes ran inside the lamassu-server ecosystem. This includes address creation, balance lookup, making transactions, etc. As such, this file needs to export the following functions:
|
||||
- `balance`: Responds with the amount of usable balance the operator wallet has;
|
||||
- `sendCoins`: Responsible for creating a transaction and responds with an object containing the fee of the transaction and the transactionID;
|
||||
- `newAddress`: Generates a new address for the operator wallet. Used for machine transactions and funding page;
|
||||
- `getStatus`: Responsible for getting the status of a cash-out transaction (transaction from an operator address to a client address).
|
||||
- `newFunding`: Creates the response to the funding page, with the amount of balance the operator has, the pending balance and a new funding address;
|
||||
- `cryptoNetwork`: Responds with the crypto network the wallet is operating in, based on the port of the RPC calls used.
|
||||
|
||||
#### Coin utils
|
||||
|
||||
Steps to work on lamassu-coins:
|
||||
- Create a new object on `lamassu-coins/config/consts.js` containing all the information relative to a coin. If you're using a wallet built into the daemon, use BTC as template. Otherwise, if the wallet process is separated from the daemon, use XMR as template;
|
||||
- Create a new file on `lamassu-coins/plugins/<coin_code>.js`. This file should handle URL parsing and address validation. Despite most coins in lamassu-coins operating on base58 or bech32 validation, this validation can implemented on some variation of the existing algorithms, as is the case with XMR. When this happens, the implementation of this variation needs to be created from scratch. With the validator created, the machine should be able to recognize a valid address. To test this out, simply edit the `lamassu-machine/device-config.json` file with the new coin address to validate.
|
||||
|
|
@ -48,6 +48,11 @@ const BINARIES = {
|
|||
url: 'https://github.com/bitcoin-cash-node/bitcoin-cash-node/releases/download/v23.1.0/bitcoin-cash-node-23.1.0-x86_64-linux-gnu.tar.gz',
|
||||
dir: 'bitcoin-cash-node-23.1.0/bin',
|
||||
files: [['bitcoind', 'bitcoincashd'], ['bitcoin-cli', 'bitcoincash-cli']]
|
||||
},
|
||||
XMR: {
|
||||
url: 'https://downloads.getmonero.org/cli/monero-linux-x64-v0.17.2.0.tar.bz2',
|
||||
dir: 'monero-x86_64-linux-gnu-v0.17.2.0',
|
||||
files: [['monerod', 'monerod'], ['monero-wallet-rpc', 'monero-wallet-rpc']]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,11 +76,24 @@ function es (cmd) {
|
|||
return res.toString()
|
||||
}
|
||||
|
||||
function writeSupervisorConfig (coinRec, cmd) {
|
||||
function writeSupervisorConfig (coinRec, cmd, walletCmd = '') {
|
||||
if (isInstalledSoftware(coinRec)) return
|
||||
|
||||
const blockchain = coinRec.code
|
||||
|
||||
if (!_.isNil(coinRec.wallet)) {
|
||||
const supervisorConfigWallet = `[program:${blockchain}-wallet]
|
||||
command=nice ${walletCmd}
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stderr_logfile=/var/log/supervisor/${blockchain}-wallet.err.log
|
||||
stdout_logfile=/var/log/supervisor/${blockchain}-wallet.out.log
|
||||
environment=HOME="/root"
|
||||
`
|
||||
|
||||
writeFile(`/etc/supervisor/conf.d/${coinRec.code}-wallet.conf`, supervisorConfigWallet)
|
||||
}
|
||||
|
||||
const supervisorConfig = `[program:${blockchain}]
|
||||
command=nice ${cmd}
|
||||
autostart=true
|
||||
|
|
@ -89,7 +107,10 @@ environment=HOME="/root"
|
|||
}
|
||||
|
||||
function isInstalledSoftware (coinRec) {
|
||||
return fs.existsSync(`/etc/supervisor/conf.d/${coinRec.code}.conf`)
|
||||
return !_.isNil(coinRec.wallet)
|
||||
? fs.existsSync(`/etc/supervisor/conf.d/${coinRec.code}.conf`)
|
||||
&& fs.existsSync(`/etc/supervisor/conf.d/${coinRec.code}-wallet.conf`)
|
||||
: fs.existsSync(`/etc/supervisor/conf.d/${coinRec.code}.conf`)
|
||||
}
|
||||
|
||||
function fetchAndInstall (coinRec) {
|
||||
|
|
@ -104,7 +125,9 @@ function fetchAndInstall (coinRec) {
|
|||
const binDir = requiresUpdate ? binaries.defaultDir : binaries.dir
|
||||
|
||||
es(`wget -q ${url}`)
|
||||
es(`tar -xzf ${downloadFile}`)
|
||||
coinRec.cryptoCode === 'XMR'
|
||||
? es(`tar -xf ${downloadFile}`)
|
||||
: es(`tar -xzf ${downloadFile}`)
|
||||
|
||||
if (_.isEmpty(binaries.files)) {
|
||||
es(`sudo cp ${binDir}/* /usr/local/bin`)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ const PLUGINS = {
|
|||
DASH: require('./dash.js'),
|
||||
ETH: require('./ethereum.js'),
|
||||
LTC: require('./litecoin.js'),
|
||||
ZEC: require('./zcash.js')
|
||||
ZEC: require('./zcash.js'),
|
||||
XMR: require('./monero.js')
|
||||
}
|
||||
|
||||
module.exports = {run}
|
||||
|
|
|
|||
30
lib/blockchain/monero.js
Normal file
30
lib/blockchain/monero.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
const path = require('path')
|
||||
|
||||
const { utils } = require('lamassu-coins')
|
||||
|
||||
const common = require('./common')
|
||||
|
||||
module.exports = {setup}
|
||||
|
||||
const coinRec = utils.getCryptoCurrency('XMR')
|
||||
|
||||
function setup (dataDir) {
|
||||
common.firewall([coinRec.defaultPort])
|
||||
const auth = `lamassuserver:${common.randomPass()}`
|
||||
const config = buildConfig(auth)
|
||||
common.writeFile(path.resolve(dataDir, coinRec.configFile), config)
|
||||
const cmd = `/usr/local/bin/${coinRec.daemon} --data-dir ${dataDir} --config-file ${dataDir}/${coinRec.configFile}`
|
||||
const walletCmd = `/usr/local/bin/${coinRec.wallet} --stagenet --rpc-login ${auth} --daemon-host 127.0.0.1 --daemon-port 38081 --trusted-daemon --daemon-login ${auth} --rpc-bind-port 38083 --wallet-dir ${dataDir}/wallets`
|
||||
common.writeSupervisorConfig(coinRec, cmd, walletCmd)
|
||||
}
|
||||
|
||||
function buildConfig (auth) {
|
||||
return `rpc-login=${auth}
|
||||
stagenet=1
|
||||
restricted-rpc=1
|
||||
db-sync-mode=safe
|
||||
out-peers=20
|
||||
in-peers=20
|
||||
prune-blockchain=1
|
||||
`
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ const _ = require('lodash/fp')
|
|||
|
||||
const { ALL } = require('../../plugins/common/ccxt')
|
||||
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT } = COINS
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT, XMR } = COINS
|
||||
const { bitpay, coinbase, itbit, bitstamp, kraken, binanceus, cex, ftx } = ALL
|
||||
|
||||
const TICKER = 'ticker'
|
||||
|
|
@ -32,6 +32,7 @@ const ALL_ACCOUNTS = [
|
|||
{ 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: 'monerod', display: 'monerod', class: WALLET, cryptos: [XMR] },
|
||||
{ code: 'bitcoincashd', display: 'bitcoincashd', class: WALLET, cryptos: [BCH] },
|
||||
{ code: 'bitgo', display: 'BitGo', class: WALLET, cryptos: [BTC, ZEC, LTC, BCH, DASH] },
|
||||
{ code: 'bitstamp', display: 'Bitstamp', class: EXCHANGE, cryptos: bitstamp.CRYPTO },
|
||||
|
|
@ -47,7 +48,7 @@ const ALL_ACCOUNTS = [
|
|||
{ code: 'mock-id-verify', display: 'Mock ID verifier', class: ID_VERIFIER, dev: true },
|
||||
{ code: 'twilio', display: 'Twilio', class: SMS },
|
||||
{ code: 'mailgun', display: 'Mailgun', class: EMAIL },
|
||||
{ code: 'none', display: 'None', class: ZERO_CONF, cryptos: [BTC, ZEC, LTC, DASH, BCH, ETH] },
|
||||
{ code: 'none', display: 'None', class: ZERO_CONF, cryptos: [BTC, ZEC, LTC, DASH, BCH, ETH, XMR] },
|
||||
{ code: 'blockcypher', display: 'Blockcypher', class: ZERO_CONF, cryptos: [BTC] },
|
||||
{ code: 'mock-zero-conf', display: 'Mock 0-conf', class: ZERO_CONF, cryptos: ALL_CRYPTOS, dev: true }
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const binanceus = require('../exchange/binanceus')
|
|||
const cex = require('../exchange/cex')
|
||||
const ftx = require('../exchange/ftx')
|
||||
const bitpay = require('../ticker/bitpay')
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC } = COINS
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT, XMR } = COINS
|
||||
|
||||
const ALL = {
|
||||
cex: cex,
|
||||
|
|
@ -19,7 +19,7 @@ const ALL = {
|
|||
itbit: itbit,
|
||||
bitpay: bitpay,
|
||||
coinbase: {
|
||||
CRYPTO: [BTC, ETH, LTC, DASH, ZEC, BCH],
|
||||
CRYPTO: [BTC, ETH, LTC, DASH, ZEC, BCH, USDT, XMR],
|
||||
FIAT: 'ALL_CURRENCIES'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ const axios = require('axios')
|
|||
const uuid = require('uuid')
|
||||
const fs = require('fs')
|
||||
const _ = require('lodash/fp')
|
||||
const request = require('request-promise')
|
||||
|
||||
module.exports = {fetch, parseConf}
|
||||
module.exports = {fetch, fetchDigest, parseConf}
|
||||
|
||||
function fetch (account = {}, method, params) {
|
||||
params = _.defaultTo([], params)
|
||||
|
|
@ -41,6 +42,40 @@ function fetch (account = {}, method, params) {
|
|||
})
|
||||
}
|
||||
|
||||
function fetchDigest(account = {}, method, params = []) {
|
||||
return Promise.resolve(true)
|
||||
.then(() => {
|
||||
if (_.isNil(account.port))
|
||||
throw new Error('port attribute required for jsonRpc')
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
const dataString = `{"jsonrpc":"2.0","id":"${uuid.v4()}","method":"${method}","params":${JSON.stringify(params)}}`
|
||||
|
||||
const options = {
|
||||
url: `http://localhost:${account.port}/json_rpc`,
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: dataString,
|
||||
forever: true,
|
||||
auth: {
|
||||
user: account.username,
|
||||
pass: account.password,
|
||||
sendImmediately: false
|
||||
}
|
||||
}
|
||||
|
||||
return request(options)
|
||||
})
|
||||
.then((res) => {
|
||||
const r = JSON.parse(res)
|
||||
if (r.error) throw r.error
|
||||
return r.result
|
||||
})
|
||||
}
|
||||
|
||||
function split (str) {
|
||||
const i = str.indexOf('=')
|
||||
if (i === -1) return []
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ const { ORDER_TYPES } = require('./consts')
|
|||
const { COINS } = require('lamassu-coins')
|
||||
|
||||
const ORDER_TYPE = ORDER_TYPES.MARKET
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT } = COINS
|
||||
const CRYPTO = [BTC, ETH, LTC, DASH, ZEC, BCH, USDT]
|
||||
const { BTC, BCH, DASH, ETH, LTC, ZEC, USDT, XMR } = COINS
|
||||
const CRYPTO = [BTC, ETH, LTC, DASH, ZEC, BCH, USDT, XMR]
|
||||
const FIAT = ['USD', 'EUR']
|
||||
const AMOUNT_PRECISION = 6
|
||||
const REQUIRED_CONFIG_FIELDS = ['apiKey', 'privateKey']
|
||||
|
|
|
|||
196
lib/plugins/wallet/monerod/monerod.js
Normal file
196
lib/plugins/wallet/monerod/monerod.js
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const _ = require('lodash/fp')
|
||||
const jsonRpc = require('../../common/json-rpc')
|
||||
|
||||
const { utils } = require('lamassu-coins')
|
||||
|
||||
const blockchainUtils = require('../../../coin-utils')
|
||||
const BN = require('../../../bn')
|
||||
const E = require('../../../error')
|
||||
const { logger } = require('../../../blockchain/common')
|
||||
|
||||
const cryptoRec = utils.getCryptoCurrency('XMR')
|
||||
const configPath = utils.configPath(cryptoRec, blockchainUtils.blockchainDir())
|
||||
const walletDir = path.resolve(utils.cryptoDir(cryptoRec, blockchainUtils.blockchainDir()), 'wallets')
|
||||
const unitScale = cryptoRec.unitScale
|
||||
const config = jsonRpc.parseConf(configPath)
|
||||
|
||||
const rpcConfig = {
|
||||
username: config['rpc-login'].split(':')[0],
|
||||
password: config['rpc-login'].split(':')[1],
|
||||
port: cryptoRec.walletPort || cryptoRec.defaultPort
|
||||
}
|
||||
|
||||
function fetch (method, params) {
|
||||
return jsonRpc.fetchDigest(rpcConfig, method, params)
|
||||
}
|
||||
|
||||
function handleError (error) {
|
||||
switch(error.code) {
|
||||
case -13:
|
||||
{
|
||||
if (fs.existsSync(path.resolve(walletDir, 'Wallet')) && fs.existsSync(path.resolve(walletDir, 'Wallet.keys')) && fs.existsSync(path.resolve(walletDir, 'Wallet.address.txt'))) {
|
||||
logger.debug('Found wallet! Opening wallet...')
|
||||
return openWallet()
|
||||
}
|
||||
logger.debug('Couldn\'t find wallet! Creating...')
|
||||
return createWallet()
|
||||
}
|
||||
case -21:
|
||||
throw new Error('Wallet already exists!')
|
||||
case -17:
|
||||
throw new E.InsufficientFundsError()
|
||||
case -37:
|
||||
throw new E.InsufficientFundsError()
|
||||
default:
|
||||
throw new Error(
|
||||
_.join(' ', [
|
||||
'json-rpc::got error:',
|
||||
JSON.stringify(_.get('message', error, '')),
|
||||
JSON.stringify(_.get('response.data.error', error, ''))
|
||||
])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function openWallet () {
|
||||
return fetch('open_wallet', { filename: 'Wallet', password: rpcConfig.password })
|
||||
}
|
||||
|
||||
function createWallet () {
|
||||
return fetch('create_wallet', { filename: 'Wallet', password: rpcConfig.password, language: 'English' })
|
||||
.then(() => new Promise(() => setTimeout(() => openWallet(), 3000)))
|
||||
.then(() => fetch('auto_refresh'))
|
||||
}
|
||||
|
||||
function checkCryptoCode (cryptoCode) {
|
||||
if (cryptoCode !== 'XMR') return Promise.reject(new Error('Unsupported crypto: ' + cryptoCode))
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
function refreshWallet () {
|
||||
return fetch('refresh')
|
||||
}
|
||||
|
||||
function accountBalance (cryptoCode) {
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => refreshWallet())
|
||||
.then(() => fetch('get_balance', { account_index: 0, address_indices: [0] }))
|
||||
.then(res => {
|
||||
return BN(res.unlocked_balance).shift(unitScale).round()
|
||||
})
|
||||
.catch(err => handleError(err))
|
||||
}
|
||||
|
||||
function balance (account, cryptoCode) {
|
||||
return accountBalance(cryptoCode)
|
||||
}
|
||||
|
||||
function sendCoins (account, address, cryptoAtoms, cryptoCode) {
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => refreshWallet())
|
||||
.then(() => fetch('transfer_split', {
|
||||
destinations: [{ amount: cryptoAtoms, address }],
|
||||
account_index: 0,
|
||||
subaddr_indices: [],
|
||||
priority: 0,
|
||||
mixin: 6,
|
||||
ring_size: 7,
|
||||
unlock_time: 0,
|
||||
get_tx_hex: false,
|
||||
new_algorithm: false,
|
||||
get_tx_metadata: false
|
||||
}))
|
||||
.then(res => ({
|
||||
fee: BN(res.fee_list[0]).abs(),
|
||||
txid: res.tx_hash_list[0]
|
||||
}))
|
||||
.catch(err => handleError(err))
|
||||
}
|
||||
|
||||
function newAddress (account, info) {
|
||||
return checkCryptoCode(info.cryptoCode)
|
||||
.then(() => fetch('create_address', { account_index: 0 }))
|
||||
.then(res => res.address)
|
||||
.catch(err => handleError(err))
|
||||
}
|
||||
|
||||
function addressBalance (address, confirmations) {
|
||||
return fetch('get_address_index', { address: address })
|
||||
.then(addressRes => fetch('get_balance', { account_index: addressRes.index.major, address_indices: [addressRes.index.minor] }))
|
||||
.then(res => BN(res.unlocked_balance))
|
||||
.catch(err => handleError(err))
|
||||
}
|
||||
|
||||
function confirmedBalance (address, cryptoCode) {
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => refreshWallet())
|
||||
.then(() => addressBalance(address, 1))
|
||||
.catch(err => handleError(err))
|
||||
}
|
||||
|
||||
function pendingBalance (address, cryptoCode) {
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => refreshWallet())
|
||||
.then(() => addressBalance(address, 0))
|
||||
.catch(err => handleError(err))
|
||||
}
|
||||
|
||||
function getStatus (account, toAddress, requested, cryptoCode) {
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => refreshWallet())
|
||||
.then(() => confirmedBalance(toAddress, cryptoCode))
|
||||
.then(confirmed => {
|
||||
if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' }
|
||||
|
||||
return pendingBalance(toAddress, cryptoCode)
|
||||
.then(pending => {
|
||||
if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' }
|
||||
if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' }
|
||||
return { receivedCryptoAtoms: pending, status: 'notSeen' }
|
||||
})
|
||||
})
|
||||
.catch(err => handleError(err))
|
||||
}
|
||||
|
||||
function newFunding (account, cryptoCode) {
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => refreshWallet())
|
||||
.then(() => fetch('get_balance', { account_index: 0, address_indices: [0] }))
|
||||
.then(balanceRes => Promise.all([
|
||||
balanceRes,
|
||||
fetch('create_address', { account_index: 0 })
|
||||
]))
|
||||
.then(([balanceRes, addressRes]) => ({
|
||||
fundingPendingBalance: BN(balanceRes.balance).sub(balanceRes.unlocked_balance),
|
||||
fundingConfirmedBalance: BN(balanceRes.unlocked_balance),
|
||||
fundingAddress: addressRes.address
|
||||
}))
|
||||
.catch(err => handleError(err))
|
||||
}
|
||||
|
||||
function cryptoNetwork (account, cryptoCode) {
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => {
|
||||
switch(parseInt(rpcConfig.port, 10)) {
|
||||
case 18083:
|
||||
return 'main'
|
||||
case 28083:
|
||||
return 'test'
|
||||
case 38083:
|
||||
return 'stage'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
balance,
|
||||
sendCoins,
|
||||
newAddress,
|
||||
getStatus,
|
||||
newFunding,
|
||||
cryptoNetwork
|
||||
}
|
||||
96
package-lock.json
generated
96
package-lock.json
generated
|
|
@ -10931,6 +10931,11 @@
|
|||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
|
|
@ -10939,6 +10944,77 @@
|
|||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"keccak": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz",
|
||||
"integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==",
|
||||
"requires": {
|
||||
"node-addon-api": "^2.0.0",
|
||||
"node-gyp-build": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"keccak256": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.2.tgz",
|
||||
"integrity": "sha512-f2EncSgmHmmQOkgxZ+/f2VaWTNkFL6f39VIrpoX+p8cEXJVyyCs/3h9GNz/ViHgwchxvv7oG5mjT2Tk4ZqInag==",
|
||||
"requires": {
|
||||
"bn.js": "^4.11.8",
|
||||
"keccak": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
||||
"requires": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"merkle-lib": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/merkle-lib/-/merkle-lib-2.0.10.tgz",
|
||||
"integrity": "sha1-grjbrnXieneFOItz+ddyXQ9vMyY="
|
||||
},
|
||||
"minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
||||
},
|
||||
"minimalistic-crypto-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
||||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.14.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
|
||||
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
|
||||
},
|
||||
"node-addon-api": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz",
|
||||
"integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="
|
||||
},
|
||||
"node-gyp-build": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
|
||||
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg=="
|
||||
},
|
||||
"pushdata-bitcoin": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz",
|
||||
"integrity": "sha1-FZMdPNlnreUiBvUjqnMxrvfUOvc=",
|
||||
"requires": {
|
||||
"bitcoin-ops": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
|
|
@ -10949,6 +11025,11 @@
|
|||
"strip-ansi": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
|
|
@ -18081,11 +18162,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"request-promise": {
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz",
|
||||
"integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==",
|
||||
"requires": {
|
||||
"bluebird": "^3.5.0",
|
||||
"request-promise-core": "1.1.4",
|
||||
"stealthy-require": "^1.1.1",
|
||||
"tough-cookie": "^2.3.3"
|
||||
}
|
||||
},
|
||||
"request-promise-core": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
|
||||
"integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "^4.17.19"
|
||||
}
|
||||
|
|
@ -19805,8 +19896,7 @@
|
|||
"stealthy-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
|
||||
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
|
||||
"dev": true
|
||||
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
|
||||
},
|
||||
"stellar-base": {
|
||||
"version": "5.3.0",
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
"pify": "^3.0.0",
|
||||
"pretty-ms": "^2.1.0",
|
||||
"promise-sequential": "^1.1.1",
|
||||
"request-promise": "^4.2.6",
|
||||
"semver": "^7.1.3",
|
||||
"serve-static": "^1.12.4",
|
||||
"socket.io": "^2.0.3",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue