fix: added missing code from various LN PRs
This commit is contained in:
parent
3fe3fed203
commit
a3eb44bda2
9 changed files with 241 additions and 509 deletions
|
|
@ -26,7 +26,8 @@ const SECRET_FIELDS = [
|
|||
'twilio.authToken',
|
||||
'telnyx.apiKey',
|
||||
'vonage.apiSecret',
|
||||
'galoy.walletId'
|
||||
'galoy.walletId',
|
||||
'galoy.apiSecret'
|
||||
]
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -845,6 +845,10 @@ function plugins (settings, deviceId) {
|
|||
return walletScoring.isWalletScoringEnabled(settings, tx.cryptoCode)
|
||||
}
|
||||
|
||||
function probeLN (cryptoCode, address) {
|
||||
return wallet.probeLN(settings, cryptoCode, address)
|
||||
}
|
||||
|
||||
return {
|
||||
getRates,
|
||||
recordPing,
|
||||
|
|
@ -877,7 +881,8 @@ function plugins (settings, deviceId) {
|
|||
isValidWalletScore,
|
||||
getTransactionHash,
|
||||
getInputAddresses,
|
||||
isWalletScoringEnabled
|
||||
isWalletScoringEnabled,
|
||||
probeLN
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,20 @@
|
|||
const _ = require('lodash/fp')
|
||||
const invoice = require('@node-lightning/invoice')
|
||||
const axios = require('axios')
|
||||
const { utils: coinUtils } = require('@lamassu/coins')
|
||||
|
||||
const NAME = 'LN'
|
||||
const SUPPORTED_COINS = ['LN', 'BTC']
|
||||
const TX_PENDING = 'PENDING'
|
||||
const TX_SUCCESS = 'SUCCESS'
|
||||
|
||||
const URI = 'https://api.staging.galoy.io/graphql'
|
||||
|
||||
const BN = require('../../../bn')
|
||||
|
||||
function request (graphqlQuery, token) {
|
||||
function request (graphqlQuery, token, endpoint) {
|
||||
const headers = {
|
||||
'content-type': 'application/json',
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
return axios({
|
||||
method: 'post',
|
||||
url: URI,
|
||||
url: endpoint,
|
||||
headers: headers,
|
||||
data: graphqlQuery
|
||||
})
|
||||
|
|
@ -27,6 +22,9 @@ function request (graphqlQuery, token) {
|
|||
if (r.error) throw r.error
|
||||
return r.data
|
||||
})
|
||||
.catch(err => {
|
||||
throw new Error(err)
|
||||
})
|
||||
}
|
||||
|
||||
function checkCryptoCode (cryptoCode) {
|
||||
|
|
@ -37,7 +35,41 @@ function checkCryptoCode (cryptoCode) {
|
|||
return Promise.resolve()
|
||||
}
|
||||
|
||||
function getGaloyAccount (token) {
|
||||
function getTransactionsByAddress (token, endpoint, address) {
|
||||
const accountInfo = {
|
||||
'operationName': 'me',
|
||||
'query': `query me {
|
||||
me {
|
||||
defaultAccount {
|
||||
defaultWalletId
|
||||
wallets {
|
||||
id
|
||||
walletCurrency
|
||||
transactionsByAddress (address: "${address}")
|
||||
edges {
|
||||
node {
|
||||
direction
|
||||
settlementAmount
|
||||
status
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
'variables': {}
|
||||
}
|
||||
return request(accountInfo, token, endpoint)
|
||||
.then(r => {
|
||||
return r.data.me.defaultAccount
|
||||
})
|
||||
.catch(err => {
|
||||
throw new Error(err)
|
||||
})
|
||||
}
|
||||
|
||||
function getGaloyAccount (token, endpoint) {
|
||||
const accountInfo = {
|
||||
'operationName': 'me',
|
||||
'query': `query me {
|
||||
|
|
@ -48,59 +80,27 @@ function getGaloyAccount (token) {
|
|||
id
|
||||
walletCurrency
|
||||
balance
|
||||
transactions {
|
||||
edges {
|
||||
node {
|
||||
direction
|
||||
id
|
||||
settlementAmount
|
||||
settlementFee
|
||||
status
|
||||
initiationVia {
|
||||
... on InitiationViaIntraLedger {
|
||||
counterPartyUsername
|
||||
counterPartyWalletId
|
||||
}
|
||||
... on InitiationViaLn {
|
||||
paymentHash
|
||||
}
|
||||
... on InitiationViaOnChain {
|
||||
address
|
||||
}
|
||||
}
|
||||
settlementVia {
|
||||
... on SettlementViaIntraLedger {
|
||||
counterPartyUsername
|
||||
counterPartyWalletId
|
||||
}
|
||||
... on SettlementViaLn {
|
||||
preImage
|
||||
}
|
||||
... on SettlementViaOnChain {
|
||||
transactionHash
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pendingIncomingBalance
|
||||
}
|
||||
}
|
||||
id
|
||||
}
|
||||
}`,
|
||||
'variables': {}
|
||||
}
|
||||
return request(accountInfo, token)
|
||||
return request(accountInfo, token, endpoint)
|
||||
.then(r => {
|
||||
return r.data.me.defaultAccount
|
||||
})
|
||||
.catch(err => {
|
||||
throw new Error(err)
|
||||
})
|
||||
}
|
||||
|
||||
function isLightning (address) {
|
||||
return address.substr(0, 2) === 'ln'
|
||||
}
|
||||
|
||||
function sendFundsOnChain (walletId, address, cryptoAtoms, token) {
|
||||
function sendFundsOnChain (walletId, address, cryptoAtoms, token, endpoint) {
|
||||
const sendOnChain = {
|
||||
'operationName': 'onChainPaymentSend',
|
||||
'query': `mutation onChainPaymentSend($input: OnChainPaymentSendInput!) {
|
||||
|
|
@ -114,17 +114,17 @@ function sendFundsOnChain (walletId, address, cryptoAtoms, token) {
|
|||
}`,
|
||||
'variables': { 'input': { 'address': `${address}`, 'amount': `${cryptoAtoms}`, 'walletId': `${walletId}` } }
|
||||
}
|
||||
return request(sendOnChain, token)
|
||||
return request(sendOnChain, token, endpoint)
|
||||
.then(result => {
|
||||
return result.data.onChainPaymentSend
|
||||
})
|
||||
}
|
||||
|
||||
function sendFundsLN (walletId, invoice, token) {
|
||||
const sendLN = {
|
||||
'operationName': 'lnInvoicePaymentSend',
|
||||
'query': `mutation lnInvoicePaymentSend($input: LnInvoicePaymentInput!) {
|
||||
lnInvoicePaymentSend(input: $input) {
|
||||
function sendFundsLN (walletId, invoice, cryptoAtoms, token, endpoint) {
|
||||
const sendLnNoAmount = {
|
||||
'operationName': 'lnNoAmountInvoicePaymentSend',
|
||||
'query': `mutation lnNoAmountInvoicePaymentSend($input: LnNoAmountInvoicePaymentInput!) {
|
||||
lnNoAmountInvoicePaymentSend(input: $input) {
|
||||
errors {
|
||||
message
|
||||
path
|
||||
|
|
@ -132,29 +132,41 @@ function sendFundsLN (walletId, invoice, token) {
|
|||
status
|
||||
}
|
||||
}`,
|
||||
'variables': { 'input': { 'paymentRequest': `${invoice}`, 'walletId': `${walletId}` } }
|
||||
'variables': { 'input': { 'paymentRequest': `${invoice}`, 'walletId': `${walletId}`, 'amount': `${cryptoAtoms}` } }
|
||||
}
|
||||
return request(sendLN, token)
|
||||
.then(result => {
|
||||
return result.data.lnInvoicePaymentSend
|
||||
})
|
||||
return request(sendLnNoAmount, token, endpoint).then(result => result.data.lnNoAmountInvoicePaymentSend)
|
||||
}
|
||||
|
||||
function sendProbeRequest (walletId, invoice, cryptoAtoms, token, endpoint) {
|
||||
const sendProbeNoAmount = {
|
||||
'operationName': 'lnNoAmountInvoiceFeeProbe',
|
||||
'query': `mutation lnNoAmountInvoiceFeeProbe($input: LnNoAmountInvoiceFeeProbeInput!) {
|
||||
lnNoAmountInvoiceFeeProbe(input: $input) {
|
||||
amount
|
||||
errors {
|
||||
message
|
||||
path
|
||||
}
|
||||
}
|
||||
}`,
|
||||
'variables': { 'input': { 'paymentRequest': `${invoice}`, 'walletId': `${walletId}`, 'amount': `${cryptoAtoms}` } }
|
||||
}
|
||||
return request(sendProbeNoAmount, token, endpoint).then(result => result.data.lnNoAmountInvoiceFeeProbe)
|
||||
}
|
||||
|
||||
function sendCoins (account, tx, settings, operatorId) {
|
||||
const { toAddress, cryptoAtoms, cryptoCode } = tx
|
||||
const externalCryptoCode = coinUtils.getEquivalentCode(cryptoCode)
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => getGaloyAccount(account.apiKey))
|
||||
.then(() => getGaloyAccount(account.apiSecret, account.endpoint))
|
||||
.then(galoyAccount => {
|
||||
const wallet = _.head(
|
||||
_.filter(wallet => wallet.walletCurrency === externalCryptoCode &&
|
||||
wallet.id === galoyAccount.defaultWalletId &&
|
||||
wallet.id === account.walletId)(galoyAccount.wallets)
|
||||
)
|
||||
const wallet = _.find(wallet => wallet.walletCurrency === externalCryptoCode &&
|
||||
wallet.id === galoyAccount.defaultWalletId &&
|
||||
wallet.id === account.walletId)(galoyAccount.wallets)
|
||||
if (isLightning(toAddress)) {
|
||||
return sendFundsLN(wallet.id, toAddress, account.apiKey)
|
||||
return sendFundsLN(wallet.id, toAddress, cryptoAtoms, account.apiSecret, account.endpoint)
|
||||
}
|
||||
return sendFundsOnChain(wallet.id, toAddress, cryptoAtoms, account.apiKey)
|
||||
return sendFundsOnChain(wallet.id, toAddress, cryptoAtoms, account.apiSecret, account.endpoint)
|
||||
})
|
||||
.then(result => {
|
||||
switch (result.status) {
|
||||
|
|
@ -172,7 +184,17 @@ function sendCoins (account, tx, settings, operatorId) {
|
|||
})
|
||||
}
|
||||
|
||||
function newOnChainAddress (walletId, token) {
|
||||
function probeLN (account, cryptoCode, invoice) {
|
||||
const probeHardLimits = [100, 500, 1000]
|
||||
const promises = probeHardLimits.map(limit => {
|
||||
return sendProbeRequest(account.walletId, invoice, limit, account.apiSecret, account.endpoint)
|
||||
.then(r => _.isEmpty(r.errors))
|
||||
})
|
||||
return Promise.all(promises)
|
||||
.then(results => _.zipObject(probeHardLimits, results))
|
||||
}
|
||||
|
||||
function newOnChainAddress (walletId, token, endpoint) {
|
||||
const createOnChainAddress = {
|
||||
'operationName': 'onChainAddressCreate',
|
||||
'query': `mutation onChainAddressCreate($input: OnChainAddressCreateInput!) {
|
||||
|
|
@ -186,13 +208,13 @@ function newOnChainAddress (walletId, token) {
|
|||
}`,
|
||||
'variables': { 'input': { 'walletId': `${walletId}` } }
|
||||
}
|
||||
return request(createOnChainAddress, token)
|
||||
return request(createOnChainAddress, token, endpoint)
|
||||
.then(result => {
|
||||
return result.data.onChainAddressCreate.address
|
||||
})
|
||||
}
|
||||
|
||||
function newInvoice (walletId, cryptoAtoms, token) {
|
||||
function newInvoice (walletId, cryptoAtoms, token, endpoint) {
|
||||
const createInvoice = {
|
||||
'operationName': 'lnInvoiceCreate',
|
||||
'query': `mutation lnInvoiceCreate($input: LnInvoiceCreateInput!) {
|
||||
|
|
@ -208,7 +230,7 @@ function newInvoice (walletId, cryptoAtoms, token) {
|
|||
}`,
|
||||
'variables': { 'input': { 'walletId': `${walletId}`, 'amount': `${cryptoAtoms}` } }
|
||||
}
|
||||
return request(createInvoice, token)
|
||||
return request(createInvoice, token, endpoint)
|
||||
.then(result => {
|
||||
return result.data.lnInvoiceCreate.invoice.paymentRequest
|
||||
})
|
||||
|
|
@ -217,15 +239,13 @@ function newInvoice (walletId, cryptoAtoms, token) {
|
|||
function balance (account, cryptoCode, settings, operatorId) {
|
||||
const externalCryptoCode = coinUtils.getEquivalentCode(cryptoCode)
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => getGaloyAccount(account.apiKey))
|
||||
.then(() => getGaloyAccount(account.apiSecret, account.endpoint))
|
||||
.then(galoyAccount => {
|
||||
// account has a list of wallets, should we consider the balance of each one?
|
||||
// for now we'll get the first BTC wallet that matches the defaultWalletId
|
||||
const wallet = _.head(
|
||||
_.filter(
|
||||
wallet => wallet.walletCurrency === externalCryptoCode &&
|
||||
wallet.id === account.walletId)(galoyAccount.wallets)
|
||||
)
|
||||
const wallet = _.find(wallet => wallet.walletCurrency === externalCryptoCode &&
|
||||
wallet.id === galoyAccount.defaultWalletId &&
|
||||
wallet.id === account.walletId)(galoyAccount.wallets)
|
||||
return new BN(wallet.balance || 0)
|
||||
})
|
||||
}
|
||||
|
|
@ -234,16 +254,14 @@ function newAddress (account, info, tx, settings, operatorId) {
|
|||
const { cryptoAtoms, cryptoCode } = tx
|
||||
const externalCryptoCode = coinUtils.getEquivalentCode(cryptoCode)
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => getGaloyAccount(account.apiKey))
|
||||
.then(() => getGaloyAccount(account.apiSecret, account.endpoint))
|
||||
.then(galoyAccount => {
|
||||
const wallet = _.head(
|
||||
_.filter(wallet => wallet.walletCurrency === externalCryptoCode &&
|
||||
wallet.id === galoyAccount.defaultWalletId &&
|
||||
wallet.id === account.walletId)(galoyAccount.wallets)
|
||||
)
|
||||
const wallet = _.find(wallet => wallet.walletCurrency === externalCryptoCode &&
|
||||
wallet.id === galoyAccount.defaultWalletId &&
|
||||
wallet.id === account.walletId)(galoyAccount.wallets)
|
||||
const promises = [
|
||||
newOnChainAddress(wallet.id, account.apiKey),
|
||||
newInvoice(wallet.id, cryptoAtoms, account.apiKey)
|
||||
newOnChainAddress(wallet.id, account.apiSecret, account.endpoint),
|
||||
newInvoice(wallet.id, cryptoAtoms, account.apiSecret, account.endpoint)
|
||||
]
|
||||
return Promise.all(promises)
|
||||
})
|
||||
|
|
@ -254,31 +272,32 @@ function newAddress (account, info, tx, settings, operatorId) {
|
|||
|
||||
function getStatus (account, tx, requested, settings, operatorId) {
|
||||
const { toAddress, cryptoAtoms, cryptoCode } = tx
|
||||
const mapStatus = tx => {
|
||||
if (!tx) return 'notSeen'
|
||||
if (tx.node.status === TX_PENDING) return 'authorized'
|
||||
if (tx.node.status === TX_SUCCESS) return 'confirmed'
|
||||
return 'notSeen'
|
||||
}
|
||||
const getBalance = _.reduce((acc, value) => {
|
||||
acc[value.node.status] = acc[value.node.status].plus(new BN(value.node.settlementAmount))
|
||||
return acc
|
||||
}, { SUCCESS: new BN(0), PENDING: new BN(0), FAILURE: new BN(0) })
|
||||
|
||||
const externalCryptoCode = coinUtils.getEquivalentCode(cryptoCode)
|
||||
const address = coinUtils.parseUrl(toAddress)
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => getGaloyAccount(account.apiKey))
|
||||
.then(galoyAccount => {
|
||||
const wallet = _.head(
|
||||
_.filter(wallet => wallet.walletCurrency === externalCryptoCode &&
|
||||
wallet.id === galoyAccount.defaultWalletId &&
|
||||
wallet.id === account.walletId)(galoyAccount.wallets)
|
||||
)
|
||||
const transactions = wallet.transactions.edges
|
||||
.then(() => {
|
||||
const address = coinUtils.parseUrl(cryptoCode, account.environment, toAddress)
|
||||
// Consider all LN transactions successful
|
||||
if (isLightning(address)) {
|
||||
const paymentHash = invoice.decode(address).paymentHash.toString('hex')
|
||||
const transaction = _.head(_.filter(tx => tx.node.initiationVia.paymentHash === paymentHash && tx.node.direction === 'RECEIVE')(transactions))
|
||||
return { receivedCryptoAtoms: cryptoAtoms, status: mapStatus(transaction) }
|
||||
return { receivedCryptoAtoms: cryptoAtoms, status: 'confirmed' }
|
||||
}
|
||||
// On-chain tx
|
||||
const transaction = _.head(_.filter(tx => tx.node.initiationVia.address === address)(transactions))
|
||||
return { receivedCryptoAtoms: cryptoAtoms, status: mapStatus(transaction) }
|
||||
// On-chain and intra-ledger transactions
|
||||
return getTransactionsByAddress(account.apiSecret, account.endpoint, address)
|
||||
.then(accountInfo => {
|
||||
const transactions =
|
||||
_.find(wallet => wallet.walletCurrency === externalCryptoCode &&
|
||||
wallet.id === accountInfo.defaultWalletId &&
|
||||
wallet.id === account.walletId)(accountInfo.wallets).transactions.edges
|
||||
const { SUCCESS: confirmed, PENDING: pending } = getBalance(transactions)
|
||||
if (confirmed.gte(requested)) return { receivedCryptoAtoms: confirmed, status: 'confirmed' }
|
||||
if (pending.gte(requested)) return { receivedCryptoAtoms: pending, status: 'authorized' }
|
||||
if (pending.gt(0)) return { receivedCryptoAtoms: pending, status: 'insufficientFunds' }
|
||||
return { receivedCryptoAtoms: pending, status: 'notSeen' }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -286,23 +305,17 @@ function newFunding (account, cryptoCode, settings, operatorId) {
|
|||
const externalCryptoCode = coinUtils.getEquivalentCode(cryptoCode)
|
||||
// Regular BTC address
|
||||
return checkCryptoCode(cryptoCode)
|
||||
.then(() => getGaloyAccount(account.apiKey))
|
||||
.then(() => getGaloyAccount(account.apiSecret, account.endpoint))
|
||||
.then(galoyAccount => {
|
||||
const wallet = _.head(
|
||||
_.filter(wallet => wallet.walletCurrency === externalCryptoCode &&
|
||||
wallet.id === galoyAccount.defaultWalletId &&
|
||||
wallet.id === account.walletId)(galoyAccount.wallets)
|
||||
)
|
||||
const pendingBalance = _.sumBy(tx => {
|
||||
if (tx.node.status === TX_PENDING) return tx.node.settlementAmount
|
||||
return 0
|
||||
})(wallet.transactions.edges)
|
||||
return newOnChainAddress(wallet.id, account.apiKey)
|
||||
.then(onChainAddress => [onChainAddress, wallet.balance, pendingBalance])
|
||||
const wallet = _.find(wallet => wallet.walletCurrency === externalCryptoCode &&
|
||||
wallet.id === galoyAccount.defaultWalletId &&
|
||||
wallet.id === account.walletId)(galoyAccount.wallets)
|
||||
return newOnChainAddress(wallet.id, account.apiSecret, account.endpoint)
|
||||
.then(onChainAddress => [onChainAddress, wallet.balance, wallet.pendingIncomingBalance])
|
||||
})
|
||||
.then(([onChainAddress, balance, pendingBalance]) => {
|
||||
.then(([onChainAddress, balance, pendingIncomingBalance]) => {
|
||||
return {
|
||||
fundingPendingBalance: new BN(pendingBalance),
|
||||
fundingPendingBalance: new BN(pendingIncomingBalance),
|
||||
fundingConfirmedBalance: new BN(balance),
|
||||
fundingAddress: onChainAddress
|
||||
}
|
||||
|
|
@ -327,5 +340,7 @@ module.exports = {
|
|||
getStatus,
|
||||
newFunding,
|
||||
cryptoNetwork,
|
||||
checkBlockchainStatus
|
||||
checkBlockchainStatus,
|
||||
sendProbeRequest,
|
||||
probeLN
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ const { router: txRoutes } = require('./routes/txRoutes')
|
|||
const verifyUserRoutes = require('./routes/verifyUserRoutes')
|
||||
const verifyTxRoutes = require('./routes/verifyTxRoutes')
|
||||
const verifyPromoCodeRoutes = require('./routes/verifyPromoCodeRoutes')
|
||||
const probeRoutes = require('./routes/probeLnRoutes')
|
||||
|
||||
const graphQLServer = require('./graphql/server')
|
||||
|
||||
|
|
@ -83,6 +84,8 @@ app.use('/tx', txRoutes)
|
|||
|
||||
app.use('/logs', logsRoutes)
|
||||
|
||||
app.use('/probe', probeRoutes)
|
||||
|
||||
graphQLServer.applyMiddleware({ app })
|
||||
|
||||
app.use(errorHandler)
|
||||
|
|
|
|||
20
lib/routes/probeLnRoutes.js
Normal file
20
lib/routes/probeLnRoutes.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const plugins = require('../plugins')
|
||||
const settingsLoader = require('../new-settings-loader')
|
||||
|
||||
function probe (req, res, next) {
|
||||
// TODO: why req.settings is undefined?
|
||||
settingsLoader.loadLatest()
|
||||
.then(settings => {
|
||||
const pi = plugins(settings, req.deviceId)
|
||||
return pi.probeLN('LN', req.body.address)
|
||||
.then(r => res.status(200).send({ hardLimits: r }))
|
||||
.catch(next)
|
||||
})
|
||||
}
|
||||
|
||||
router.get('/', probe)
|
||||
|
||||
module.exports = router
|
||||
|
|
@ -62,6 +62,13 @@ function _balance (settings, cryptoCode) {
|
|||
})
|
||||
}
|
||||
|
||||
function probeLN (settings, cryptoCode, address) {
|
||||
return fetchWallet(settings, cryptoCode).then(r => {
|
||||
if (!r.wallet.probeLN) return null
|
||||
return r.wallet.probeLN(r.account, cryptoCode, address)
|
||||
})
|
||||
}
|
||||
|
||||
function sendCoins (settings, tx) {
|
||||
return fetchWallet(settings, tx.cryptoCode)
|
||||
.then(r => {
|
||||
|
|
@ -299,5 +306,6 @@ module.exports = {
|
|||
newFunding,
|
||||
cryptoNetwork,
|
||||
supportsBatching,
|
||||
checkBlockchainStatus
|
||||
checkBlockchainStatus,
|
||||
probeLN
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue