diff --git a/.github/docs/CLN_command_coverage.md b/.github/docs/CLN_command_coverage.md deleted file mode 100644 index 87869f45..00000000 --- a/.github/docs/CLN_command_coverage.md +++ /dev/null @@ -1,71 +0,0 @@ -### Core Lightning Commands Covered on RTL - -=== bitcoin === -- [x] feerates -- [x] newaddr -- [ ] txdiscard -- [ ] txprepare -- [ ] txsend -- [x] withdraw - -=== channels === -- [x] close -- [ ] fundchannel_cancel -- [ ] fundchannel_complete -- [ ] fundchannel_start -- [x] getroute -- [x] listchannels -- [x] listforwards -- [x] setchannelfee - -=== network === -- [x] connect -- [x] disconnect -- [x] listnodes -- [x] listpeers -- [ ] ping - -=== payment === -- [ ] createonion -- [x] decodepay -- [x] delexpiredinvoice -- [ ] delinvoice -- [x] invoice -- [x] listinvoices -- [x] listsendpays -- [ ] listtransactions -- [ ] sendonion -- [ ] sendpay -- [ ] waitanyinvoice -- [ ] waitinvoice -- [ ] waitsendpay - -=== plugin === -- [ ] autocleaninvoice -- [ ] estimatefees -- [x] fundchannel -- [ ] getchaininfo -- [ ] getrawblockbyheight -- [ ] getutxout -- [x] listpays -- [x] pay -- [ ] paystatus -- [ ] plugin -- [ ] sendrawtransaction - -=== utility === -- [ ] check -- [x] checkmessage -- [x] getinfo -- [ ] getlog -- [ ] getsharedsecret -- [ ] help -- [ ] listconfigs -- [x] listfunds -- [x] signmessage -- [ ] stop -- [ ] waitblockheight - -=== developer === -- [ ] dev-listaddrs -- [ ] dev-rescan-outputs \ No newline at end of file diff --git a/.github/docs/LND_API_coverage.md b/.github/docs/LND_API_coverage.md deleted file mode 100644 index 69648f04..00000000 --- a/.github/docs/LND_API_coverage.md +++ /dev/null @@ -1,57 +0,0 @@ -[Intro](../README.md) -- [Application Features](Application_features.md) -- [Road Map](Roadmap.md) -- **LND API Coverage** -- [Application Configurations](Application_configurations.md) - -- [x] GenSeed -- [x] InitWallet -- [x] UnlockWallet -- [ ] ChangePassword -- [x] WalletBalance -- [x] ChannelBalance -- [x] GetTransactions -- [ ] EstimateFee -- [x] SendCoins -- [ ] ListUnspent -- [ ] SubscribeTransactions -- [ ] SendMany -- [x] NewAddress -- [x] SignMessage -- [x] VerifyMessage -- [x] ConnectPeer -- [x] DisconnectPeer -- [x] ListPeers -- [x] GetInfo -- [x] PendingChannels -- [x] ListChannels -- [ ] SubscribeChannelEvents -- [x] ClosedChannels -- [ ] OpenChannelSync -- [x] OpenChannel -- [x] CloseChannel -- [ ] AbandonChannel -- [x] SendPayment -- [ ] SendPaymentSync -- [ ] SendToRoute -- [ ] SendToRouteSync -- [x] AddInvoice -- [x] ListInvoices -- [ ] LookupInvoice -- [ ] SubscribeInvoices -- [x] DecodePayReq -- [x] ListPayments -- [ ] DeleteAllPayments -- [ ] DescribeGraph -- [x] GetChanInfo -- [x] GetNodeInfo -- [x] QueryRoutes -- [x] GetNetworkInfo -- [ ] StopDaemon -- [ ] SubscribeChannelGraph -- [ ] DebugLevel -- [x] FeeReport -- [x] UpdateChannelPolicy -- [x] ForwardingHistory -- [x] ExportChannelBackup -- [x] ExportAllChannelBackups -- [x] VerifyChanBackup -- [x] RestoreChannelBackups -- [ ] SubscribeChannelBackups -- [ ] Messages diff --git a/backend/controllers/cln/balance.js b/backend/controllers/cln/balance.js deleted file mode 100644 index 00761166..00000000 --- a/backend/controllers/cln/balance.js +++ /dev/null @@ -1,35 +0,0 @@ -import request from 'request-promise'; -import { Logger } from '../../utils/logger.js'; -import { Common } from '../../utils/common.js'; -let options = null; -const logger = Logger; -const common = Common; -export const getBalance = (req, res, next) => { - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Getting Balance..' }); - options = common.getOptions(req); - if (options.error) { - return res.status(options.statusCode).json({ message: options.message, error: options.error }); - } - options.url = req.session.selectedNode.ln_server_url + '/v1/listfunds'; - request.post(options).then((body) => { - let confBalance = 0; - let unconfBalance = 0; - let totalBalance = 0; - const versionCompatible = common.isVersionCompatible(req.session.selectedNode.ln_version, '23.02'); - body.outputs.forEach((output) => { - if (output.status === 'confirmed') { - confBalance = confBalance + (versionCompatible ? (output.amount_msat / 1000) : output.value); - } - else if (output.status === 'unconfirmed') { - unconfBalance = unconfBalance + (versionCompatible ? (output.amount_msat / 1000) : output.value); - } - }); - totalBalance = confBalance + unconfBalance; - const walBalance = { totalBalance: totalBalance || 0, confBalance: confBalance || 0, unconfBalance: unconfBalance || 0 }; - logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Balance', msg: 'Balance Received', data: walBalance }); - res.status(200).json(walBalance); - }).catch((errRes) => { - const err = common.handleError(errRes, 'Balance', 'Get Balance Error', req.session.selectedNode); - return res.status(err.statusCode).json({ message: err.message, error: err.error }); - }); -}; diff --git a/backend/controllers/cln/channels.js b/backend/controllers/cln/channels.js index 68c7d3e4..7ac2fe28 100644 --- a/backend/controllers/cln/channels.js +++ b/backend/controllers/cln/channels.js @@ -15,11 +15,29 @@ export const listPeerChannels = (req, res, next) => { request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Peer Channels List Received', data: body.channels }); return Promise.all(body.channels?.map((channel) => { - const local = (channel.to_us_msat && typeof channel.to_us_msat === 'string' && channel.to_us_msat.includes('msat')) ? +channel.to_us_msat.replace('msat', '') : channel.to_us_msat ? channel.to_us_msat : 0; - const total = (channel.total_msat && typeof channel.total_msat === 'string' && channel.total_msat.includes('msat')) ? +channel.total_msat.replace('msat', '') : channel.total_msat ? channel.total_msat : 0; - const remote = total - local; - channel.to_them_msat = remote; - channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); + channel.to_us_msat = common.removeMSat(channel.to_us_msat); + channel.total_msat = common.removeMSat(channel.total_msat); + channel.last_tx_fee_msat = common.removeMSat(channel.last_tx_fee_msat); + channel.funding.local_funds_msat = common.removeMSat(channel.funding.local_funds_msat); + channel.funding.remote_funds_msat = common.removeMSat(channel.funding.remote_funds_msat); + channel.funding.pushed_msat = common.removeMSat(channel.funding.pushed_msat); + channel.min_to_us_msat = common.removeMSat(channel.min_to_us_msat); + channel.max_to_us_msat = common.removeMSat(channel.max); + channel.fee_base_msat = common.removeMSat(channel.fee_base_msat); + channel.dust_limit_msat = common.removeMSat(channel.dust_limit_msat); + channel.max_total_htlc_in_msat = common.removeMSat(channel.max_total_htlc_in_msat); + channel.their_reserve_msat = common.removeMSat(channel.their_reserve_msat); + channel.our_reserve_msat = common.removeMSat(channel.our_reserve_msat); + channel.spendable_msat = common.removeMSat(channel.spendable_msat); + channel.receivable_msat = common.removeMSat(channel.receivable_msat); + channel.minimum_htlc_in_msat = common.removeMSat(channel.minimum_htlc_in_msat); + channel.minimum_htlc_out_msat = common.removeMSat(channel.minimum_htlc_out_msat); + channel.maximum_htlc_out_msat = common.removeMSat(channel.maximum_htlc_out_msat); + channel.in_offered_msat = common.removeMSat(channel.in_offered_msat); + channel.in_fulfilled_msat = common.removeMSat(channel.in_fulfilled_msat); + channel.out_offered_msat = common.removeMSat(channel.out_offered_msat); + channel.out_fulfilled_msat = common.removeMSat(channel.out_fulfilled_msat); + channel.balancedness = (channel.total_msat === 0) ? 1 : (1 - Math.abs((channel.to_us_msat - (channel.total_msat - channel.to_us_msat)) / channel.total_msat)).toFixed(3); return getAlias(req.session.selectedNode, channel, 'peer_id'); })).then((values) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Peer Channels List With Aliases Received', data: body.channels }); @@ -109,8 +127,8 @@ export const funderUpdatePolicy = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Funder Update Body', data: options.body }); request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Funder Policy Received', data: body }); - body.channel_fee_max_base_msat = (body.channel_fee_max_base_msat && typeof body.channel_fee_max_base_msat === 'string' && body.channel_fee_max_base_msat.includes('msat')) ? +body.channel_fee_max_base_msat?.replace('msat', '') : body.channel_fee_max_base_msat ? body.channel_fee_max_base_msat : 0; - body.lease_fee_base_msat = (body.lease_fee_base_msat && typeof body.lease_fee_base_msat === 'string' && body.lease_fee_base_msat.includes('msat')) ? +body.lease_fee_base_msat?.replace('msat', '') : body.channel_fee_max_base_msat ? body.channel_fee_max_base_msat : 0; + body.channel_fee_max_base_msat = common.removeMSat(body.channel_fee_max_base_msat); + body.lease_fee_base_msat = common.removeMSat(body.lease_fee_base_msat); res.status(200).json(body); }).catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Funder Policy Error', req.session.selectedNode); diff --git a/backend/controllers/cln/getInfo.js b/backend/controllers/cln/getInfo.js index 9ceaa069..34131818 100644 --- a/backend/controllers/cln/getInfo.js +++ b/backend/controllers/cln/getInfo.js @@ -48,7 +48,7 @@ export const getInfo = (req, res, next) => { body.uris.push(body.id + '@' + addr.address + ':' + addr.port); }); } - body.fees_collected_msat = (body.fees_collected_msat && typeof body.fees_collected_msat === 'string' && body.fees_collected_msat.includes('msat')) ? body.fees_collected_msat?.replace('msat', '') : body.fees_collected_msat ? body.fees_collected_msat : 0; + body.fees_collected_msat = common.removeMSat(body.fees_collected_msat); req.session.selectedNode.ln_version = body.version || ''; req.session.selectedNode.api_version = body.api_version || ''; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' }); diff --git a/backend/controllers/cln/invoices.js b/backend/controllers/cln/invoices.js index d7fdf280..1b4bb758 100644 --- a/backend/controllers/cln/invoices.js +++ b/backend/controllers/cln/invoices.js @@ -31,6 +31,10 @@ export const listInvoices = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url }); request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body }); + body.invoices.forEach((invoice) => { + invoice.amount_msat = common.removeMSat(invoice.amount_msat); + invoice.amount_received_msat = common.removeMSat(invoice.amount_received_msat); + }); res.status(200).json(body); }).catch((errRes) => { const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode); diff --git a/backend/controllers/cln/network.js b/backend/controllers/cln/network.js index 39e9cfd7..981b1051 100644 --- a/backend/controllers/cln/network.js +++ b/backend/controllers/cln/network.js @@ -66,10 +66,8 @@ export const listNodes = (req, res, next) => { if (req.query.liquidity_ads && typeof req.query.liquidity_ads === 'string' && req.query.liquidity_ads.toLowerCase() === 'yes') { response = body.nodes.filter((node) => { if (node.option_will_fund) { - node.option_will_fund.lease_fee_base_msat = (node.option_will_fund.lease_fee_base_msat && typeof node.option_will_fund.lease_fee_base_msat === 'string' && - node.option_will_fund.lease_fee_base_msat.includes('msat')) ? node.option_will_fund.lease_fee_base_msat?.replace('msat', '') : node.option_will_fund.lease_fee_base_msat ? node.option_will_fund.lease_fee_base_msat : 0; - node.option_will_fund.channel_fee_max_base_msat = (node.option_will_fund.channel_fee_max_base_msat && typeof node.option_will_fund.channel_fee_max_base_msat === 'string' && - node.option_will_fund.channel_fee_max_base_msat.includes('msat')) ? node.option_will_fund.channel_fee_max_base_msat?.replace('msat', '') : node.option_will_fund.channel_fee_max_base_msat ? node.option_will_fund.channel_fee_max_base_msat : 0; + node.option_will_fund.lease_fee_base_msat = common.removeMSat(node.option_will_fund.lease_fee_base_msat); + node.option_will_fund.channel_fee_max_base_msat = common.removeMSat(node.option_will_fund.channel_fee_max_base_msat); } return node; }); diff --git a/backend/controllers/cln/onchain.js b/backend/controllers/cln/onchain.js index b0c4be57..ee58a0aa 100644 --- a/backend/controllers/cln/onchain.js +++ b/backend/controllers/cln/onchain.js @@ -49,6 +49,8 @@ export const getUTXOs = (req, res, next) => { // Local Remote Balance Calculation let lrBalance = { localBalance: 0, remoteBalance: 0, inactiveBalance: 0, pendingBalance: 0 }; body.channels.forEach((channel) => { + channel.our_amount_msat = common.removeMSat(channel.our_amount_msat); + channel.amount_msat = common.removeMSat(channel.amount_msat); if ((channel.state === 'CHANNELD_NORMAL') && channel.connected === true) { lrBalance.localBalance = lrBalance.localBalance + channel.our_amount_msat; lrBalance.remoteBalance = lrBalance.remoteBalance + (channel.amount_msat - channel.our_amount_msat); @@ -70,6 +72,7 @@ export const getUTXOs = (req, res, next) => { // Onchain Balance Calculation let onchainBalance = { totalBalance: 0, confBalance: 0, unconfBalance: 0 }; body.outputs.forEach((output) => { + output.amount_msat = common.removeMSat(output.amount_msat); if (output.status === 'confirmed') { onchainBalance.confBalance = onchainBalance.confBalance + output.amount_msat; } @@ -78,8 +81,8 @@ export const getUTXOs = (req, res, next) => { } }); onchainBalance = { - totalBalance: onchainBalance.totalBalance / 1000, - confBalance: onchainBalance.confBalance / 1000, + totalBalance: onchainBalance.confBalance / 1000, + confBalance: (onchainBalance.confBalance - onchainBalance.unconfBalance) / 1000, unconfBalance: onchainBalance.unconfBalance / 1000 }; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Onchain', msg: 'Onchain Balance Received', data: onchainBalance }); diff --git a/backend/controllers/cln/payments.js b/backend/controllers/cln/payments.js index 03d69b1d..688f9acb 100644 --- a/backend/controllers/cln/payments.js +++ b/backend/controllers/cln/payments.js @@ -7,6 +7,18 @@ let options = null; const logger = Logger; const common = Common; const databaseService = Database; +export const getMemo = (selNode, payment) => { + options.url = selNode.ln_server_url + '/v1/decode'; + options.body = { string: payment.bolt11 }; + return request.post(options).then((res) => { + logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Decode Received', data: res }); + payment.memo = res.description || ''; + return payment; + }).catch((err) => { + payment.memo = ''; + return payment; + }); +}; function paymentReducer(accumulator, currentPayment) { const currPayHash = currentPayment.payment_hash; if (!currentPayment.partid) { @@ -22,6 +34,8 @@ function paymentReducer(accumulator, currentPayment) { } function summaryReducer(accumulator, mpp) { if (mpp.status === 'complete') { + mpp.amount_msat = common.removeMSat(mpp.amount_msat); + mpp.amount_sent_msat = common.removeMSat(mpp.amount_sent_msat); accumulator.amount_msat = accumulator.amount_msat + mpp.amount_msat; accumulator.amount_sent_msat = accumulator.amount_sent_msat + mpp.amount_sent_msat; accumulator.status = mpp.status; @@ -44,6 +58,8 @@ function groupBy(payments) { temp.is_group = false; temp.is_expanded = false; temp.total_parts = 1; + temp.amount_msat = common.removeMSat(temp.amount_msat); + temp.amount_sent_msat = common.removeMSat(temp.amount_sent_msat); delete temp.partid; } else { @@ -78,7 +94,11 @@ export const listPayments = (req, res, next) => { }; request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments }); - res.status(200).json(body.payments && body.payments.length && body.payments.length > 0 ? groupBy(body.payments) : []); + body.payments = body.payments && body.payments.length && body.payments.length > 0 ? groupBy(body.payments) : []; + return Promise.all(body.payments?.map((payment) => getMemo(req.session.selectedNode, payment))).then((values) => { + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payments List with Memo Received', data: body.payments }); + res.status(200).json(body.payments); + }); }).catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); diff --git a/backend/controllers/cln/peers.js b/backend/controllers/cln/peers.js index 70a01a33..3e120d5e 100644 --- a/backend/controllers/cln/peers.js +++ b/backend/controllers/cln/peers.js @@ -15,7 +15,33 @@ export const getPeers = (req, res, next) => { request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body }); const peers = !body.peers ? [] : body.peers; - return Promise.all(peers?.map((peer) => getAlias(req.session.selectedNode, peer, 'id'))).then((values) => { + return Promise.all(peers?.map((peer) => { + peer.channels.forEach((channel) => { + channel.to_us_msat = common.removeMSat(channel.to_us_msat); + channel.total_msat = common.removeMSat(channel.total_msat); + channel.last_tx_fee_msat = common.removeMSat(channel.last_tx_fee_msat); + channel.funding.local_funds_msat = common.removeMSat(channel.funding.local_funds_msat); + channel.funding.remote_funds_msat = common.removeMSat(channel.funding.remote_funds_msat); + channel.funding.pushed_msat = common.removeMSat(channel.funding.pushed_msat); + channel.min_to_us_msat = common.removeMSat(channel.min_to_us_msat); + channel.max_to_us_msat = common.removeMSat(channel.max); + channel.fee_base_msat = common.removeMSat(channel.fee_base_msat); + channel.dust_limit_msat = common.removeMSat(channel.dust_limit_msat); + channel.max_total_htlc_in_msat = common.removeMSat(channel.max_total_htlc_in_msat); + channel.their_reserve_msat = common.removeMSat(channel.their_reserve_msat); + channel.our_reserve_msat = common.removeMSat(channel.our_reserve_msat); + channel.spendable_msat = common.removeMSat(channel.spendable_msat); + channel.receivable_msat = common.removeMSat(channel.receivable_msat); + channel.minimum_htlc_in_msat = common.removeMSat(channel.minimum_htlc_in_msat); + channel.minimum_htlc_out_msat = common.removeMSat(channel.minimum_htlc_out_msat); + channel.maximum_htlc_out_msat = common.removeMSat(channel.maximum_htlc_out_msat); + channel.in_offered_msat = common.removeMSat(channel.in_offered_msat); + channel.in_fulfilled_msat = common.removeMSat(channel.in_fulfilled_msat); + channel.out_offered_msat = common.removeMSat(channel.out_offered_msat); + channel.out_fulfilled_msat = common.removeMSat(channel.out_fulfilled_msat); + }); + return getAlias(req.session.selectedNode, peer, 'id'); + })).then((values) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body.peers }); res.status(200).json(body.peers || []); }); diff --git a/backend/utils/common.js b/backend/utils/common.js index 7681176a..1237aa45 100644 --- a/backend/utils/common.js +++ b/backend/utils/common.js @@ -71,6 +71,7 @@ export class CommonService { if (req.session.selectedNode && req.session.selectedNode.options) { req.session.selectedNode.options.method = (req.session.selectedNode.ln_implementation && req.session.selectedNode.ln_implementation.toUpperCase() === 'LND') ? 'GET' : 'POST'; delete req.session.selectedNode.options.form; + delete req.session.selectedNode.options.body; req.session.selectedNode.options.qs = {}; return req.session.selectedNode.options; } @@ -569,6 +570,7 @@ export class CommonService { const dataStr = foundDataLine ? foundDataLine.substring((foundDataLine.indexOf(search_string)) + search_string.length) : '{}'; return JSON.parse(dataStr); }; + this.removeMSat = (value) => ((value && typeof value === 'string' && value.includes('msat')) ? +value.replace('msat', '') : value ? value : 0); } } export const Common = new CommonService(); diff --git a/server/controllers/cln/channels.ts b/server/controllers/cln/channels.ts index 094c3f32..e87851bb 100644 --- a/server/controllers/cln/channels.ts +++ b/server/controllers/cln/channels.ts @@ -15,11 +15,29 @@ export const listPeerChannels = (req, res, next) => { request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Peer Channels List Received', data: body.channels }); return Promise.all(body.channels?.map((channel) => { - const local = (channel.to_us_msat && typeof channel.to_us_msat === 'string' && channel.to_us_msat.includes('msat')) ? +channel.to_us_msat.replace('msat', '') : channel.to_us_msat ? channel.to_us_msat : 0; - const total = (channel.total_msat && typeof channel.total_msat === 'string' && channel.total_msat.includes('msat')) ? +channel.total_msat.replace('msat', '') : channel.total_msat ? channel.total_msat : 0; - const remote = total - local; - channel.to_them_msat = remote; - channel.balancedness = (total === 0) ? 1 : (1 - Math.abs((local - remote) / total)).toFixed(3); + channel.to_us_msat = common.removeMSat(channel.to_us_msat); + channel.total_msat = common.removeMSat(channel.total_msat); + channel.last_tx_fee_msat = common.removeMSat(channel.last_tx_fee_msat); + channel.funding.local_funds_msat = common.removeMSat(channel.funding.local_funds_msat); + channel.funding.remote_funds_msat = common.removeMSat(channel.funding.remote_funds_msat); + channel.funding.pushed_msat = common.removeMSat(channel.funding.pushed_msat); + channel.min_to_us_msat = common.removeMSat(channel.min_to_us_msat); + channel.max_to_us_msat = common.removeMSat(channel.max); + channel.fee_base_msat = common.removeMSat(channel.fee_base_msat); + channel.dust_limit_msat = common.removeMSat(channel.dust_limit_msat); + channel.max_total_htlc_in_msat = common.removeMSat(channel.max_total_htlc_in_msat); + channel.their_reserve_msat = common.removeMSat(channel.their_reserve_msat); + channel.our_reserve_msat = common.removeMSat(channel.our_reserve_msat); + channel.spendable_msat = common.removeMSat(channel.spendable_msat); + channel.receivable_msat = common.removeMSat(channel.receivable_msat); + channel.minimum_htlc_in_msat = common.removeMSat(channel.minimum_htlc_in_msat); + channel.minimum_htlc_out_msat = common.removeMSat(channel.minimum_htlc_out_msat); + channel.maximum_htlc_out_msat = common.removeMSat(channel.maximum_htlc_out_msat); + channel.in_offered_msat = common.removeMSat(channel.in_offered_msat); + channel.in_fulfilled_msat = common.removeMSat(channel.in_fulfilled_msat); + channel.out_offered_msat = common.removeMSat(channel.out_offered_msat); + channel.out_fulfilled_msat = common.removeMSat(channel.out_fulfilled_msat); + channel.balancedness = (channel.total_msat === 0) ? 1 : (1 - Math.abs((channel.to_us_msat - (channel.total_msat - channel.to_us_msat)) / channel.total_msat)).toFixed(3); return getAlias(req.session.selectedNode, channel, 'peer_id'); })).then((values) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Peer Channels List With Aliases Received', data: body.channels }); @@ -104,8 +122,8 @@ export const funderUpdatePolicy = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Funder Update Body', data: options.body }); request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Funder Policy Received', data: body }); - body.channel_fee_max_base_msat = (body.channel_fee_max_base_msat && typeof body.channel_fee_max_base_msat === 'string' && body.channel_fee_max_base_msat.includes('msat')) ? +body.channel_fee_max_base_msat?.replace('msat', '') : body.channel_fee_max_base_msat ? body.channel_fee_max_base_msat : 0; - body.lease_fee_base_msat = (body.lease_fee_base_msat && typeof body.lease_fee_base_msat === 'string' && body.lease_fee_base_msat.includes('msat')) ? +body.lease_fee_base_msat?.replace('msat', '') : body.channel_fee_max_base_msat ? body.channel_fee_max_base_msat : 0; + body.channel_fee_max_base_msat = common.removeMSat(body.channel_fee_max_base_msat); + body.lease_fee_base_msat = common.removeMSat(body.lease_fee_base_msat); res.status(200).json(body); }).catch((errRes) => { const err = common.handleError(errRes, 'Channels', 'Funder Policy Error', req.session.selectedNode); diff --git a/server/controllers/cln/getInfo.ts b/server/controllers/cln/getInfo.ts index 9028273c..ebe7ff99 100644 --- a/server/controllers/cln/getInfo.ts +++ b/server/controllers/cln/getInfo.ts @@ -45,7 +45,7 @@ export const getInfo = (req, res, next) => { body.uris.push(body.id + '@' + addr.address + ':' + addr.port); }); } - body.fees_collected_msat = (body.fees_collected_msat && typeof body.fees_collected_msat === 'string' && body.fees_collected_msat.includes('msat')) ? body.fees_collected_msat?.replace('msat', '') : body.fees_collected_msat ? body.fees_collected_msat : 0; + body.fees_collected_msat = common.removeMSat(body.fees_collected_msat); req.session.selectedNode.ln_version = body.version || ''; req.session.selectedNode.api_version = body.api_version || ''; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'GetInfo', msg: 'Connecting to the Core Lightning\'s Websocket Server.' }); diff --git a/server/controllers/cln/invoices.ts b/server/controllers/cln/invoices.ts index 3cac3ee8..7fe79e66 100644 --- a/server/controllers/cln/invoices.ts +++ b/server/controllers/cln/invoices.ts @@ -29,6 +29,10 @@ export const listInvoices = (req, res, next) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List URL', data: options.url }); request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Invoice', msg: 'Invoices List Received', data: body }); + body.invoices.forEach((invoice) => { + invoice.amount_msat = common.removeMSat(invoice.amount_msat); + invoice.amount_received_msat = common.removeMSat(invoice.amount_received_msat); + }); res.status(200).json(body); }).catch((errRes) => { const err = common.handleError(errRes, 'Invoice', 'List Invoices Error', req.session.selectedNode); diff --git a/server/controllers/cln/network.ts b/server/controllers/cln/network.ts index c23023c4..c574d4f3 100644 --- a/server/controllers/cln/network.ts +++ b/server/controllers/cln/network.ts @@ -64,10 +64,8 @@ export const listNodes = (req, res, next) => { if (req.query.liquidity_ads && typeof req.query.liquidity_ads === 'string' && req.query.liquidity_ads.toLowerCase() === 'yes') { response = body.nodes.filter((node) => { if (node.option_will_fund) { - node.option_will_fund.lease_fee_base_msat = (node.option_will_fund.lease_fee_base_msat && typeof node.option_will_fund.lease_fee_base_msat === 'string' && - node.option_will_fund.lease_fee_base_msat.includes('msat')) ? node.option_will_fund.lease_fee_base_msat?.replace('msat', '') : node.option_will_fund.lease_fee_base_msat ? node.option_will_fund.lease_fee_base_msat : 0; - node.option_will_fund.channel_fee_max_base_msat = (node.option_will_fund.channel_fee_max_base_msat && typeof node.option_will_fund.channel_fee_max_base_msat === 'string' && - node.option_will_fund.channel_fee_max_base_msat.includes('msat')) ? node.option_will_fund.channel_fee_max_base_msat?.replace('msat', '') : node.option_will_fund.channel_fee_max_base_msat ? node.option_will_fund.channel_fee_max_base_msat : 0; + node.option_will_fund.lease_fee_base_msat = common.removeMSat(node.option_will_fund.lease_fee_base_msat); + node.option_will_fund.channel_fee_max_base_msat = common.removeMSat(node.option_will_fund.channel_fee_max_base_msat); } return node; }); diff --git a/server/controllers/cln/onchain.ts b/server/controllers/cln/onchain.ts index fa06aca3..bb2520c6 100644 --- a/server/controllers/cln/onchain.ts +++ b/server/controllers/cln/onchain.ts @@ -46,6 +46,8 @@ export const getUTXOs = (req, res, next) => { // Local Remote Balance Calculation let lrBalance = { localBalance: 0, remoteBalance: 0, inactiveBalance: 0, pendingBalance: 0 }; body.channels.forEach((channel) => { + channel.our_amount_msat = common.removeMSat(channel.our_amount_msat); + channel.amount_msat = common.removeMSat(channel.amount_msat); if ((channel.state === 'CHANNELD_NORMAL') && channel.connected === true) { lrBalance.localBalance = lrBalance.localBalance + channel.our_amount_msat; lrBalance.remoteBalance = lrBalance.remoteBalance + (channel.amount_msat - channel.our_amount_msat); @@ -65,6 +67,7 @@ export const getUTXOs = (req, res, next) => { // Onchain Balance Calculation let onchainBalance = { totalBalance: 0, confBalance: 0, unconfBalance: 0 }; body.outputs.forEach((output) => { + output.amount_msat = common.removeMSat(output.amount_msat); if (output.status === 'confirmed') { onchainBalance.confBalance = onchainBalance.confBalance + output.amount_msat; } else if (output.status === 'unconfirmed') { @@ -72,8 +75,8 @@ export const getUTXOs = (req, res, next) => { } }); onchainBalance = { - totalBalance: onchainBalance.totalBalance / 1000, - confBalance: onchainBalance.confBalance / 1000, + totalBalance: onchainBalance.confBalance / 1000, + confBalance: (onchainBalance.confBalance - onchainBalance.unconfBalance) / 1000, unconfBalance: onchainBalance.unconfBalance / 1000 }; logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Onchain', msg: 'Onchain Balance Received', data: onchainBalance }); diff --git a/server/controllers/cln/payments.ts b/server/controllers/cln/payments.ts index d4b27add..8ce8e243 100644 --- a/server/controllers/cln/payments.ts +++ b/server/controllers/cln/payments.ts @@ -3,12 +3,26 @@ import { Logger, LoggerService } from '../../utils/logger.js'; import { Common, CommonService } from '../../utils/common.js'; import { Database, DatabaseService } from '../../utils/database.js'; import { CollectionFieldsEnum, CollectionsEnum, Offer } from '../../models/database.model.js'; +import { CommonSelectedNode } from '../../models/config.model.js'; let options = null; const logger: LoggerService = Logger; const common: CommonService = Common; const databaseService: DatabaseService = Database; +export const getMemo = (selNode: CommonSelectedNode, payment: any) => { + options.url = selNode.ln_server_url + '/v1/decode'; + options.body = { string: payment.bolt11 }; + return request.post(options).then((res) => { + logger.log({ selectedNode: selNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment Decode Received', data: res }); + payment.memo = res.description || ''; + return payment; + }).catch((err) => { + payment.memo = ''; + return payment; + }); +}; + function paymentReducer(accumulator, currentPayment) { const currPayHash = currentPayment.payment_hash; if (!currentPayment.partid) { currentPayment.partid = 0; } @@ -22,6 +36,8 @@ function paymentReducer(accumulator, currentPayment) { function summaryReducer(accumulator, mpp) { if (mpp.status === 'complete') { + mpp.amount_msat = common.removeMSat(mpp.amount_msat); + mpp.amount_sent_msat = common.removeMSat(mpp.amount_sent_msat); accumulator.amount_msat = accumulator.amount_msat + mpp.amount_msat; accumulator.amount_sent_msat = accumulator.amount_sent_msat + mpp.amount_sent_msat; accumulator.status = mpp.status; @@ -41,6 +57,8 @@ function groupBy(payments) { temp.is_group = false; temp.is_expanded = false; temp.total_parts = 1; + temp.amount_msat = common.removeMSat(temp.amount_msat); + temp.amount_sent_msat = common.removeMSat(temp.amount_sent_msat); delete temp.partid; } else { const paySummary = curr?.reduce(summaryReducer, { amount_msat: 0, amount_sent_msat: 0, status: (curr[0] && curr[0].status) ? curr[0].status : 'failed' }); @@ -69,7 +87,11 @@ export const listPayments = (req, res, next) => { }; request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Payment List Received', data: body.payments }); - res.status(200).json(body.payments && body.payments.length && body.payments.length > 0 ? groupBy(body.payments) : []); + body.payments = body.payments && body.payments.length && body.payments.length > 0 ? groupBy(body.payments) : []; + return Promise.all(body.payments?.map((payment) => getMemo(req.session.selectedNode, payment))).then((values) => { + logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payments List with Memo Received', data: body.payments }); + res.status(200).json(body.payments); + }); }).catch((errRes) => { const err = common.handleError(errRes, 'Payments', 'List Payments Error', req.session.selectedNode); return res.status(err.statusCode).json({ message: err.message, error: err.error }); diff --git a/server/controllers/cln/peers.ts b/server/controllers/cln/peers.ts index 24e19a02..73980dfe 100644 --- a/server/controllers/cln/peers.ts +++ b/server/controllers/cln/peers.ts @@ -15,7 +15,33 @@ export const getPeers = (req, res, next) => { request.post(options).then((body) => { logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Peers', msg: 'Peers List Received', data: body }); const peers = !body.peers ? [] : body.peers; - return Promise.all(peers?.map((peer) => getAlias(req.session.selectedNode, peer, 'id'))).then((values) => { + return Promise.all(peers?.map((peer) => { + peer.channels.forEach((channel) => { + channel.to_us_msat = common.removeMSat(channel.to_us_msat); + channel.total_msat = common.removeMSat(channel.total_msat); + channel.last_tx_fee_msat = common.removeMSat(channel.last_tx_fee_msat); + channel.funding.local_funds_msat = common.removeMSat(channel.funding.local_funds_msat); + channel.funding.remote_funds_msat = common.removeMSat(channel.funding.remote_funds_msat); + channel.funding.pushed_msat = common.removeMSat(channel.funding.pushed_msat); + channel.min_to_us_msat = common.removeMSat(channel.min_to_us_msat); + channel.max_to_us_msat = common.removeMSat(channel.max); + channel.fee_base_msat = common.removeMSat(channel.fee_base_msat); + channel.dust_limit_msat = common.removeMSat(channel.dust_limit_msat); + channel.max_total_htlc_in_msat = common.removeMSat(channel.max_total_htlc_in_msat); + channel.their_reserve_msat = common.removeMSat(channel.their_reserve_msat); + channel.our_reserve_msat = common.removeMSat(channel.our_reserve_msat); + channel.spendable_msat = common.removeMSat(channel.spendable_msat); + channel.receivable_msat = common.removeMSat(channel.receivable_msat); + channel.minimum_htlc_in_msat = common.removeMSat(channel.minimum_htlc_in_msat); + channel.minimum_htlc_out_msat = common.removeMSat(channel.minimum_htlc_out_msat); + channel.maximum_htlc_out_msat = common.removeMSat(channel.maximum_htlc_out_msat); + channel.in_offered_msat = common.removeMSat(channel.in_offered_msat); + channel.in_fulfilled_msat = common.removeMSat(channel.in_fulfilled_msat); + channel.out_offered_msat = common.removeMSat(channel.out_offered_msat); + channel.out_fulfilled_msat = common.removeMSat(channel.out_fulfilled_msat); + }); + return getAlias(req.session.selectedNode, peer, 'id'); + })).then((values) => { logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Peers', msg: 'Sorted Peers List Received', data: body.peers }); res.status(200).json(body.peers || []); }); diff --git a/server/utils/common.ts b/server/utils/common.ts index 5a32f3fb..6a3f7e18 100644 --- a/server/utils/common.ts +++ b/server/utils/common.ts @@ -76,6 +76,7 @@ export class CommonService { if (req.session.selectedNode && req.session.selectedNode.options) { req.session.selectedNode.options.method = (req.session.selectedNode.ln_implementation && req.session.selectedNode.ln_implementation.toUpperCase() === 'LND') ? 'GET' : 'POST'; delete req.session.selectedNode.options.form; + delete req.session.selectedNode.options.body; req.session.selectedNode.options.qs = {}; return req.session.selectedNode.options; } @@ -536,6 +537,8 @@ export class CommonService { return JSON.parse(dataStr); }; + public removeMSat = (value) => ((value && typeof value === 'string' && value.includes('msat')) ? +value.replace('msat', '') : value ? value : 0); + } export const Common = new CommonService(); diff --git a/src/app/cln/transactions/payments/lightning-payments.component.ts b/src/app/cln/transactions/payments/lightning-payments.component.ts index 207a3646..9e1a2a49 100644 --- a/src/app/cln/transactions/payments/lightning-payments.component.ts +++ b/src/app/cln/transactions/payments/lightning-payments.component.ts @@ -18,7 +18,6 @@ import { CommonService } from '../../../shared/services/common.service'; import { CLNLightningSendPaymentsComponent } from '../send-payment-modal/send-payment.component'; import { SelNodeChild } from '../../../shared/models/RTLconfig'; -import { CLNEffects } from '../../store/cln.effects'; import { RTLEffects } from '../../../store/rtl.effects'; import { RTLState } from '../../../store/rtl.state'; import { openAlert, openConfirmation } from '../../../store/rtl.actions';