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
196 lines
6 KiB
JavaScript
196 lines
6 KiB
JavaScript
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
|
|
}
|