mirror of
https://github.com/Ride-The-Lightning/RTL.git
synced 2024-11-19 01:40:29 +01:00
Add AMP toggle for LND Send Payments (#1466)
This commit is contained in:
parent
417fbb3e84
commit
667aad822e
@ -116,7 +116,6 @@
|
||||
"no-label-var": "error",
|
||||
"no-restricted-globals": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-undefined": "error",
|
||||
"block-spacing": "error",
|
||||
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
|
||||
"comma-style": "error",
|
||||
|
@ -155,47 +155,6 @@ export const postChannel = (req, res, next) => {
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
export const postTransactions = (req, res, next) => {
|
||||
const { paymentReq, paymentAmount, feeLimit, outgoingChannel, allowSelfPayment, lastHopPubkey } = req.body;
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sending Payment..' });
|
||||
options = common.getOptions(req);
|
||||
if (options.error) {
|
||||
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||
}
|
||||
options.url = req.session.selectedNode.settings.lnServerUrl + '/v1/channels/transaction-stream';
|
||||
options.form = { payment_request: paymentReq };
|
||||
if (paymentAmount) {
|
||||
options.form.amt = paymentAmount;
|
||||
}
|
||||
if (feeLimit) {
|
||||
options.form.fee_limit = feeLimit;
|
||||
}
|
||||
if (outgoingChannel) {
|
||||
options.form.outgoing_chan_id = outgoingChannel;
|
||||
}
|
||||
if (allowSelfPayment) {
|
||||
options.form.allow_self_payment = allowSelfPayment;
|
||||
}
|
||||
if (lastHopPubkey) {
|
||||
options.form.last_hop_pubkey = Buffer.from(lastHopPubkey, 'hex').toString('base64');
|
||||
}
|
||||
options.form = JSON.stringify(options.form);
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Send Payment Options', data: options.form });
|
||||
request.post(options).then((body) => {
|
||||
body = body.result ? body.result : body;
|
||||
if (body.payment_error) {
|
||||
const err = common.handleError(body.payment_error, 'Channels', 'Send Payment Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
}
|
||||
else {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Payment Sent', data: body });
|
||||
res.status(201).json(body);
|
||||
}
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Channels', 'Send Payment Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
export const closeChannel = (req, res, next) => {
|
||||
try {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' });
|
||||
|
@ -97,3 +97,33 @@ export const paymentLookup = (req, res, next) => {
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
export const sendPayment = (req, res, next) => {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sending Payment..' });
|
||||
options = common.getOptions(req);
|
||||
if (options.error) {
|
||||
return res.status(options.statusCode).json({ message: options.message, error: options.error });
|
||||
}
|
||||
options.url = req.session.selectedNode.settings.lnServerUrl + '/v2/router/send';
|
||||
if (req.body.last_hop_pubkey) {
|
||||
req.body.last_hop_pubkey = Buffer.from(req.body.last_hop_pubkey, 'hex').toString('base64');
|
||||
}
|
||||
req.body.amp = req.body.amp ?? false;
|
||||
req.body.timeout_seconds = req.body.timeout_seconds || 600;
|
||||
options.form = JSON.stringify(req.body);
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Send Payment Options', data: options.form });
|
||||
request.post(options).then((body) => {
|
||||
const results = body.split('\n').filter(Boolean).map((jsonString) => JSON.parse(jsonString));
|
||||
body = results.length > 0 ? results[results.length - 1] : { result: { status: 'UNKNOWN' } };
|
||||
if (body.result.status === 'FAILED') {
|
||||
const err = common.handleError(common.titleCase(body.result.failure_reason.replace(/_/g, ' ').replace('FAILURE REASON ', '')), 'Payments', 'Send Payment Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
}
|
||||
if (body.result.status === 'SUCCEEDED') {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent', data: body.result });
|
||||
res.status(201).json(body.result);
|
||||
}
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
|
@ -1,13 +1,12 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { getAllChannels, getPendingChannels, getClosedChannels, postChannel, postTransactions, closeChannel, postChanPolicy } from '../../controllers/lnd/channels.js';
|
||||
import { getAllChannels, getPendingChannels, getClosedChannels, postChannel, closeChannel, postChanPolicy } from '../../controllers/lnd/channels.js';
|
||||
const router = Router();
|
||||
router.get('/', isAuthenticated, getAllChannels);
|
||||
router.get('/pending', isAuthenticated, getPendingChannels);
|
||||
router.get('/closed', isAuthenticated, getClosedChannels);
|
||||
router.post('/', isAuthenticated, postChannel);
|
||||
router.post('/transactions', isAuthenticated, postTransactions);
|
||||
router.delete('/:channelPoint', isAuthenticated, closeChannel);
|
||||
router.post('/chanPolicy', isAuthenticated, postChanPolicy);
|
||||
export default router;
|
||||
|
@ -1,11 +1,12 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { decodePayment, decodePayments, getPayments, getAllLightningTransactions, paymentLookup } from '../../controllers/lnd/payments.js';
|
||||
import { decodePayment, decodePayments, getPayments, getAllLightningTransactions, paymentLookup, sendPayment } from '../../controllers/lnd/payments.js';
|
||||
const router = Router();
|
||||
router.get('/', isAuthenticated, getPayments);
|
||||
router.get('/alltransactions', isAuthenticated, getAllLightningTransactions);
|
||||
router.get('/decode/:payRequest', isAuthenticated, decodePayment);
|
||||
router.get('/lookup/:paymentHash', isAuthenticated, paymentLookup);
|
||||
router.post('/', isAuthenticated, decodePayments);
|
||||
router.post('/send', isAuthenticated, sendPayment);
|
||||
export default router;
|
||||
|
1
frontend/190.03f035c34a56c8be.js
Normal file
1
frontend/190.03f035c34a56c8be.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
frontend/main.6e2567250e1fcca3.js
Normal file
1
frontend/main.6e2567250e1fcca3.js
Normal file
File diff suppressed because one or more lines are too long
1
frontend/runtime.8f43557e02af97f0.js
Normal file
1
frontend/runtime.8f43557e02af97f0.js
Normal file
@ -0,0 +1 @@
|
||||
(()=>{"use strict";var e,v={},m={};function r(e){var f=m[e];if(void 0!==f)return f.exports;var t=m[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(f,t,i,d)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,d]=e[n],c=!0,o=0;o<t.length;o++)(!1&d||a>=d)&&Object.keys(r.O).every(b=>r.O[b](t[o]))?t.splice(o--,1):(c=!1,d<a&&(a=d));if(c){e.splice(n--,1);var u=i();void 0!==u&&(f=u)}}return f}d=d||0;for(var n=e.length;n>0&&e[n-1][2]>d;n--)e[n]=e[n-1];e[n]=[t,i,d]},r.d=(e,f)=>{for(var t in f)r.o(f,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:f[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((f,t)=>(r.f[t](e,f),f),[])),r.u=e=>e+"."+{17:"6fa7154eb6e447e2",190:"03f035c34a56c8be",193:"0e1a81316bbc29da",853:"50b06a24091d386f"}[e]+".js",r.miniCssF=e=>{},r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="RTLApp:";r.l=(t,i,d,n)=>{if(e[t])e[t].push(i);else{var a,c;if(void 0!==d)for(var o=document.getElementsByTagName("script"),u=0;u<o.length;u++){var l=o[u];if(l.getAttribute("src")==t||l.getAttribute("data-webpack")==f+d){a=l;break}}a||(c=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",f+d),a.src=r.tu(t)),e[t]=[i];var s=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(y=>y(b)),g)return g(b)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&document.head.appendChild(a)}}})(),r.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:f=>f},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={121:0};r.f.j=(i,d)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)d.push(n[2]);else if(121!=i){var a=new Promise((l,s)=>n=e[i]=[l,s]);d.push(n[2]=a);var c=r.p+r.u(i),o=new Error;r.l(c,l=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var s=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;o.message="Loading chunk "+i+" failed.\n("+s+": "+p+")",o.name="ChunkLoadError",o.type=s,o.request=p,n[1](o)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var f=(i,d)=>{var o,u,[n,a,c]=d,l=0;if(n.some(p=>0!==e[p])){for(o in a)r.o(a,o)&&(r.m[o]=a[o]);if(c)var s=c(r)}for(i&&i(d);l<n.length;l++)r.o(e,u=n[l])&&e[u]&&e[u][0](),e[u]=0;return r.O(s)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();
|
@ -1 +0,0 @@
|
||||
(()=>{"use strict";var e,v={},m={};function r(e){var o=m[e];if(void 0!==o)return o.exports;var t=m[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(o,t,i,f)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,f]=e[n],s=!0,l=0;l<t.length;l++)(!1&f||a>=f)&&Object.keys(r.O).every(b=>r.O[b](t[l]))?t.splice(l--,1):(s=!1,f<a&&(a=f));if(s){e.splice(n--,1);var d=i();void 0!==d&&(o=d)}}return o}f=f||0;for(var n=e.length;n>0&&e[n-1][2]>f;n--)e[n]=e[n-1];e[n]=[t,i,f]},r.d=(e,o)=>{for(var t in o)r.o(o,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((o,t)=>(r.f[t](e,o),o),[])),r.u=e=>e+"."+{17:"6fa7154eb6e447e2",190:"8009411a512731e5",193:"0e1a81316bbc29da",853:"50b06a24091d386f"}[e]+".js",r.miniCssF=e=>{},r.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),(()=>{var e={},o="RTLApp:";r.l=(t,i,f,n)=>{if(e[t])e[t].push(i);else{var a,s;if(void 0!==f)for(var l=document.getElementsByTagName("script"),d=0;d<l.length;d++){var u=l[d];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==o+f){a=u;break}}a||(s=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",o+f),a.src=r.tu(t)),e[t]=[i];var c=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(y=>y(b)),g)return g(b)},p=setTimeout(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&document.head.appendChild(a)}}})(),r.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:o=>o},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={121:0};r.f.j=(i,f)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)f.push(n[2]);else if(121!=i){var a=new Promise((u,c)=>n=e[i]=[u,c]);f.push(n[2]=a);var s=r.p+r.u(i),l=new Error;r.l(s,u=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var c=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;l.message="Loading chunk "+i+" failed.\n("+c+": "+p+")",l.name="ChunkLoadError",l.type=c,l.request=p,n[1](l)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var o=(i,f)=>{var l,d,[n,a,s]=f,u=0;if(n.some(p=>0!==e[p])){for(l in a)r.o(a,l)&&(r.m[l]=a[l]);if(s)var c=s(r)}for(i&&i(f);u<n.length;u++)r.o(e,d=n[u])&&e[d]&&e[d][0](),e[d]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))})()})();
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "rtl",
|
||||
"version": "0.15.2-beta",
|
||||
"version": "0.15.3-beta",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "rtl",
|
||||
"version": "0.15.2-beta",
|
||||
"version": "0.15.3-beta",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ngrx/effects": "^17.2.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "rtl",
|
||||
"version": "0.15.2-beta",
|
||||
"version": "0.15.3-beta",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
@ -155,37 +155,6 @@ export const postChannel = (req, res, next) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const postTransactions = (req, res, next) => {
|
||||
const { paymentReq, paymentAmount, feeLimit, outgoingChannel, allowSelfPayment, lastHopPubkey } = req.body;
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Sending Payment..' });
|
||||
options = common.getOptions(req);
|
||||
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
|
||||
options.url = req.session.selectedNode.settings.lnServerUrl + '/v1/channels/transaction-stream';
|
||||
options.form = { payment_request: paymentReq };
|
||||
if (paymentAmount) {
|
||||
options.form.amt = paymentAmount;
|
||||
}
|
||||
if (feeLimit) { options.form.fee_limit = feeLimit; }
|
||||
if (outgoingChannel) { options.form.outgoing_chan_id = outgoingChannel; }
|
||||
if (allowSelfPayment) { options.form.allow_self_payment = allowSelfPayment; }
|
||||
if (lastHopPubkey) { options.form.last_hop_pubkey = Buffer.from(lastHopPubkey, 'hex').toString('base64'); }
|
||||
options.form = JSON.stringify(options.form);
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Channels', msg: 'Send Payment Options', data: options.form });
|
||||
request.post(options).then((body) => {
|
||||
body = body.result ? body.result : body;
|
||||
if (body.payment_error) {
|
||||
const err = common.handleError(body.payment_error, 'Channels', 'Send Payment Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
} else {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Payment Sent', data: body });
|
||||
res.status(201).json(body);
|
||||
}
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Channels', 'Send Payment Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
|
||||
export const closeChannel = (req, res, next) => {
|
||||
try {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Channels', msg: 'Closing Channel..' });
|
||||
|
@ -96,3 +96,32 @@ export const paymentLookup = (req, res, next) => {
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
|
||||
export const sendPayment = (req, res, next) => {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Sending Payment..' });
|
||||
options = common.getOptions(req);
|
||||
if (options.error) { return res.status(options.statusCode).json({ message: options.message, error: options.error }); }
|
||||
options.url = req.session.selectedNode.settings.lnServerUrl + '/v2/router/send';
|
||||
if (req.body.last_hop_pubkey) {
|
||||
req.body.last_hop_pubkey = Buffer.from(req.body.last_hop_pubkey, 'hex').toString('base64');
|
||||
}
|
||||
req.body.amp = req.body.amp ?? false;
|
||||
req.body.timeout_seconds = req.body.timeout_seconds || 600;
|
||||
options.form = JSON.stringify(req.body);
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Payments', msg: 'Send Payment Options', data: options.form });
|
||||
request.post(options).then((body) => {
|
||||
const results = body.split('\n').filter(Boolean).map((jsonString) => JSON.parse(jsonString));
|
||||
body = results.length > 0 ? results[results.length - 1] : { result: { status: 'UNKNOWN' } };
|
||||
if (body.result.status === 'FAILED') {
|
||||
const err = common.handleError(common.titleCase(body.result.failure_reason.replace(/_/g, ' ').replace('FAILURE REASON ', '')), 'Payments', 'Send Payment Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
}
|
||||
if (body.result.status === 'SUCCEEDED') {
|
||||
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Payments', msg: 'Payment Sent', data: body.result });
|
||||
res.status(201).json(body.result);
|
||||
}
|
||||
}).catch((errRes) => {
|
||||
const err = common.handleError(errRes, 'Payments', 'Send Payment Error', req.session.selectedNode);
|
||||
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
||||
});
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { getAllChannels, getPendingChannels, getClosedChannels, postChannel, postTransactions, closeChannel, postChanPolicy } from '../../controllers/lnd/channels.js';
|
||||
import { getAllChannels, getPendingChannels, getClosedChannels, postChannel, closeChannel, postChanPolicy } from '../../controllers/lnd/channels.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@ -9,7 +9,6 @@ router.get('/', isAuthenticated, getAllChannels);
|
||||
router.get('/pending', isAuthenticated, getPendingChannels);
|
||||
router.get('/closed', isAuthenticated, getClosedChannels);
|
||||
router.post('/', isAuthenticated, postChannel);
|
||||
router.post('/transactions', isAuthenticated, postTransactions);
|
||||
router.delete('/:channelPoint', isAuthenticated, closeChannel);
|
||||
router.post('/chanPolicy', isAuthenticated, postChanPolicy);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { decodePayment, decodePayments, getPayments, getAllLightningTransactions, paymentLookup } from '../../controllers/lnd/payments.js';
|
||||
import { decodePayment, decodePayments, getPayments, getAllLightningTransactions, paymentLookup, sendPayment } from '../../controllers/lnd/payments.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@ -10,5 +10,6 @@ router.get('/alltransactions', isAuthenticated, getAllLightningTransactions);
|
||||
router.get('/decode/:payRequest', isAuthenticated, decodePayment);
|
||||
router.get('/lookup/:paymentHash', isAuthenticated, paymentLookup);
|
||||
router.post('/', isAuthenticated, decodePayments);
|
||||
router.post('/send', isAuthenticated, sendPayment);
|
||||
|
||||
export default router;
|
||||
|
@ -12,8 +12,8 @@ import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import { ChannelRebalanceAlert } from '../../../../shared/models/alertData';
|
||||
import { LoggerService } from '../../../../shared/services/logger.service';
|
||||
import { CommonService } from '../../../../shared/services/common.service';
|
||||
import { Channel, QueryRoutes, ListInvoices } from '../../../../shared/models/lndModels';
|
||||
import { DEFAULT_INVOICE_EXPIRY, FEE_LIMIT_TYPES, LNDActions, PAGE_SIZE, ScreenSizeEnum, UI_MESSAGES } from '../../../../shared/services/consts-enums-functions';
|
||||
import { Channel, QueryRoutes, ListInvoices, SendPayment } from '../../../../shared/models/lndModels';
|
||||
import { DEFAULT_INVOICE_EXPIRY, FEE_LIMIT_TYPES, LNDActions, PAGE_SIZE, ScreenSizeEnum, UI_MESSAGES, getFeeLimitSat } from '../../../../shared/services/consts-enums-functions';
|
||||
|
||||
import { RTLState } from '../../../../store/rtl.state';
|
||||
import { saveNewInvoice, sendPayment } from '../../../store/lnd.actions';
|
||||
@ -239,13 +239,29 @@ export class ChannelRebalanceComponent implements OnInit, OnDestroy {
|
||||
this.flgInvoiceGenerated = true;
|
||||
this.paymentRequest = payReq;
|
||||
if (this.feeFormGroup.controls.selFeeLimitType.value.id === 'percent' && !(+this.feeFormGroup.controls.feeLimit.value % 1 === 0)) {
|
||||
this.store.dispatch(sendPayment({ payload: { uiMessage: UI_MESSAGES.NO_SPINNER, paymentReq: payReq, outgoingChannel: this.selChannel,
|
||||
feeLimitType: 'fixed', feeLimit: Math.ceil((+this.feeFormGroup.controls.feeLimit.value * +this.inputFormGroup.controls.rebalanceAmount.value) / 100),
|
||||
allowSelfPayment: true, lastHopPubkey: this.inputFormGroup.controls.selRebalancePeer.value.remote_pubkey, fromDialog: true } }));
|
||||
const payload: SendPayment = {
|
||||
uiMessage: UI_MESSAGES.NO_SPINNER,
|
||||
payment_request: payReq,
|
||||
amp: false,
|
||||
outgoing_chan_ids: this.selChannel?.chan_id ? [this.selChannel?.chan_id] : undefined,
|
||||
fee_limit_sat: Math.ceil(getFeeLimitSat('fixed', this.feeFormGroup.controls.feeLimit.value, (this.inputFormGroup.controls.rebalanceAmount.value || 0))),
|
||||
allow_self_payment: true,
|
||||
last_hop_pubkey: this.inputFormGroup.controls.selRebalancePeer.value.remote_pubkey,
|
||||
fromDialog: true
|
||||
};
|
||||
this.store.dispatch(sendPayment({ payload }));
|
||||
} else {
|
||||
this.store.dispatch(sendPayment({ payload: { uiMessage: UI_MESSAGES.NO_SPINNER, paymentReq: payReq, outgoingChannel: this.selChannel,
|
||||
feeLimitType: this.feeFormGroup.controls.selFeeLimitType.value.id, feeLimit: this.feeFormGroup.controls.feeLimit.value, allowSelfPayment: true,
|
||||
lastHopPubkey: this.inputFormGroup.controls.selRebalancePeer.value.remote_pubkey, fromDialog: true } }));
|
||||
const payload: SendPayment = {
|
||||
uiMessage: UI_MESSAGES.NO_SPINNER,
|
||||
payment_request: payReq,
|
||||
amp: false,
|
||||
outgoing_chan_ids: this.selChannel?.chan_id ? [this.selChannel?.chan_id] : undefined,
|
||||
fee_limit_sat: getFeeLimitSat(this.feeFormGroup.controls.selFeeLimitType.value.id, this.feeFormGroup.controls.feeLimit.value, (this.inputFormGroup.controls.rebalanceAmount.value || 0)),
|
||||
allow_self_payment: true,
|
||||
last_hop_pubkey: this.inputFormGroup.controls.selRebalancePeer.value.remote_pubkey,
|
||||
fromDialog: true
|
||||
};
|
||||
this.store.dispatch(sendPayment({ payload }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -691,31 +691,16 @@ export class LNDEffects implements OnDestroy {
|
||||
mergeMap((action: { type: string, payload: SendPayment }) => {
|
||||
this.store.dispatch(openSpinner({ payload: action.payload.uiMessage }));
|
||||
this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SendPayment', status: APICallStatusEnum.INITIATED } }));
|
||||
const queryHeaders = {};
|
||||
queryHeaders['paymentReq'] = action.payload.paymentReq;
|
||||
if (action.payload.paymentAmount) {
|
||||
queryHeaders['paymentAmount'] = action.payload.paymentAmount;
|
||||
}
|
||||
if (action.payload.outgoingChannel) {
|
||||
queryHeaders['outgoingChannel'] = action.payload.outgoingChannel.chan_id;
|
||||
}
|
||||
if (action.payload.allowSelfPayment) {
|
||||
queryHeaders['allowSelfPayment'] = action.payload.allowSelfPayment;
|
||||
} // Channel Rebalancing
|
||||
if (action.payload.lastHopPubkey) {
|
||||
queryHeaders['lastHopPubkey'] = action.payload.lastHopPubkey;
|
||||
}
|
||||
if (action.payload.feeLimitType && action.payload.feeLimitType !== FEE_LIMIT_TYPES[0].id) {
|
||||
queryHeaders['feeLimit'] = {};
|
||||
queryHeaders['feeLimit'][action.payload.feeLimitType] = action.payload.feeLimit;
|
||||
}
|
||||
return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + '/transactions', queryHeaders).pipe(
|
||||
const queryParams = JSON.parse(JSON.stringify(action.payload));
|
||||
delete queryParams.uiMessage;
|
||||
delete queryParams.fromDialog;
|
||||
return this.httpClient.post(this.CHILD_API_URL + API_END_POINTS.PAYMENTS_API + '/send', queryParams).pipe(
|
||||
map((sendRes: any) => {
|
||||
this.logger.info(sendRes);
|
||||
this.store.dispatch(closeSpinner({ payload: action.payload.uiMessage }));
|
||||
this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SendPayment', status: APICallStatusEnum.COMPLETED } }));
|
||||
if (sendRes.payment_error) {
|
||||
if (action.payload.allowSelfPayment) {
|
||||
if (action.payload.allow_self_payment) {
|
||||
this.store.dispatch(fetchInvoices({ payload: { num_max_invoices: this.invoicesPageSettings?.recordsPerPage, reversed: true } }));
|
||||
return {
|
||||
type: LNDActions.SEND_PAYMENT_STATUS_LND,
|
||||
@ -734,7 +719,7 @@ export class LNDEffects implements OnDestroy {
|
||||
this.store.dispatch(updateLNDAPICallStatus({ payload: { action: 'SendPayment', status: APICallStatusEnum.COMPLETED } }));
|
||||
this.store.dispatch(fetchChannels());
|
||||
this.store.dispatch(fetchPayments({ payload: { max_payments: this.paymentsPageSettings?.recordsPerPage, reversed: true } }));
|
||||
if (action.payload.allowSelfPayment) {
|
||||
if (action.payload.allow_self_payment) {
|
||||
this.store.dispatch(fetchInvoices({ payload: { num_max_invoices: this.invoicesPageSettings?.recordsPerPage, reversed: true } }));
|
||||
} else {
|
||||
let msg = 'Payment Sent Successfully.';
|
||||
@ -751,7 +736,7 @@ export class LNDEffects implements OnDestroy {
|
||||
}),
|
||||
catchError((err: any) => {
|
||||
this.logger.error('Error: ' + JSON.stringify(err));
|
||||
if (action.payload.allowSelfPayment) {
|
||||
if (action.payload.allow_self_payment) {
|
||||
this.handleErrorWithoutAlert('SendPayment', action.payload.uiMessage, 'Send Payment Failed.', err);
|
||||
this.store.dispatch(fetchInvoices({ payload: { num_max_invoices: this.invoicesPageSettings?.recordsPerPage, reversed: true } }));
|
||||
return of({
|
||||
|
@ -195,7 +195,7 @@ export class LightningPaymentsComponent implements OnInit, AfterViewInit, OnDest
|
||||
subscribe((confirmRes) => {
|
||||
if (confirmRes) {
|
||||
this.paymentDecoded.num_satoshis = confirmRes[0].inputValue;
|
||||
this.store.dispatch(sendPayment({ payload: { uiMessage: UI_MESSAGES.SEND_PAYMENT, paymentReq: this.paymentRequest, paymentAmount: confirmRes[0].inputValue, fromDialog: false } }));
|
||||
this.store.dispatch(sendPayment({ payload: { uiMessage: UI_MESSAGES.SEND_PAYMENT, payment_request: this.paymentRequest, amp: false, amt: confirmRes[0].inputValue, fromDialog: false } }));
|
||||
this.resetData();
|
||||
}
|
||||
});
|
||||
@ -224,7 +224,7 @@ export class LightningPaymentsComponent implements OnInit, AfterViewInit, OnDest
|
||||
pipe(take(1)).
|
||||
subscribe((confirmRes) => {
|
||||
if (confirmRes) {
|
||||
this.store.dispatch(sendPayment({ payload: { uiMessage: UI_MESSAGES.SEND_PAYMENT, paymentReq: this.paymentRequest, fromDialog: false } }));
|
||||
this.store.dispatch(sendPayment({ payload: { uiMessage: UI_MESSAGES.SEND_PAYMENT, payment_request: this.paymentRequest, amp: false, fromDialog: false } }));
|
||||
this.resetData();
|
||||
}
|
||||
});
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div fxFlex="95" fxLayoutAlign="start start">
|
||||
<span class="page-title">Send Payment</span>
|
||||
</div>
|
||||
<button tabindex="8" fxFlex="5" fxLayoutAlign="center center" class="btn-close-x p-0" default mat-button [mat-dialog-close]="false">X</button>
|
||||
<button tabindex="11" fxFlex="5" fxLayoutAlign="center center" class="btn-close-x p-0" default mat-button [mat-dialog-close]="false">X</button>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="padding-gap-x-large">
|
||||
<form #sendPaymentForm="ngForm" fxLayoutAlign="space-between stretch" fxLayout="column">
|
||||
@ -56,6 +56,7 @@
|
||||
</mat-autocomplete>
|
||||
<mat-error *ngIf="selectedChannelCtrl.errors?.notfound">Channel not found in the list.</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-slide-toggle fxFlex="25" tabindex="8" color="primary" name="isAmp" [(ngModel)]="isAmp">AMP Payment</mat-slide-toggle>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
<div *ngIf="paymentError !== ''" fxFlex="100" class="alert alert-danger mt-1">
|
||||
@ -63,8 +64,8 @@
|
||||
<span *ngIf="paymentError !== ''">{{paymentError}}</span>
|
||||
</div>
|
||||
<div class="mt-2" fxLayout="row" fxLayoutAlign="end center">
|
||||
<button class="mr-1" mat-button color="primary" tabindex="2" type="reset" (click)="resetData()">Clear Fields</button>
|
||||
<button mat-button id="sendBtn" color="primary" tabindex="3" (click)="onSendPayment()">Send Payment</button>
|
||||
<button class="mr-1" mat-button color="primary" tabindex="9" type="reset" (click)="resetData()">Clear Fields</button>
|
||||
<button mat-button id="sendBtn" color="primary" tabindex="10" (click)="onSendPayment()">Send Payment</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
|
@ -90,8 +90,8 @@ describe('LightningSendPaymentsComponent', () => {
|
||||
const sendButton = fixture.debugElement.nativeElement.querySelector('#sendBtn');
|
||||
sendButton.click();
|
||||
const expectedSendPaymentPayload: SendPayment = {
|
||||
uiMessage: UI_MESSAGES.SEND_PAYMENT, outgoingChannel: null, feeLimitType: 'none', feeLimit: null, fromDialog: true,
|
||||
paymentReq: 'lntb4u1psvdzaypp555uks3f6774kl3vdy2dfr00j847pyxtrqelsdnczuxnmtqv99srsdpy23jhxarfdenjqmn8wfuzq3txvejkxarnyq' +
|
||||
uiMessage: UI_MESSAGES.SEND_PAYMENT, outgoing_chan_ids: undefined, fee_limit_sat: 1000000, fromDialog: true, amp: false,
|
||||
payment_request: 'lntb4u1psvdzaypp555uks3f6774kl3vdy2dfr00j847pyxtrqelsdnczuxnmtqv99srsdpy23jhxarfdenjqmn8wfuzq3txvejkxarnyq' +
|
||||
'6qcqp2sp5xjzu6pz2sf8x4v8nmr58kjdm6k05etjfq9c96mwkhzl0g9j7sjkqrzjq28vwprzypa40c75myejm8s2aenkeykcnd7flvy9plp2yjq56nvrc8ss5c' +
|
||||
'qqqzgqqqqqqqlgqqqqqqgq9q9qy9qsqpt6u4rwfrck3tmpn54kdxjx3xdch62t5wype2f44mmlar07y749xt9elhfhf6dnlfk2tjwg3qpy8njh6remphfcc0630aq' +
|
||||
'38j0s3hrgpv4eel3'
|
||||
|
@ -9,8 +9,8 @@ import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { Node } from '../../../shared/models/RTLconfig';
|
||||
import { PayRequest, Channel, ChannelsSummary, LightningBalance } from '../../../shared/models/lndModels';
|
||||
import { APICallStatusEnum, CurrencyUnitEnum, CURRENCY_UNIT_FORMATS, FEE_LIMIT_TYPES, LNDActions, UI_MESSAGES } from '../../../shared/services/consts-enums-functions';
|
||||
import { PayRequest, Channel, ChannelsSummary, LightningBalance, SendPayment } from '../../../shared/models/lndModels';
|
||||
import { APICallStatusEnum, CurrencyUnitEnum, CURRENCY_UNIT_FORMATS, FEE_LIMIT_TYPES, LNDActions, UI_MESSAGES, getFeeLimitSat } from '../../../shared/services/consts-enums-functions';
|
||||
import { CommonService } from '../../../shared/services/common.service';
|
||||
import { LoggerService } from '../../../shared/services/logger.service';
|
||||
import { DataService } from '../../../shared/services/data.service';
|
||||
@ -43,6 +43,7 @@ export class LightningSendPaymentsComponent implements OnInit, OnDestroy {
|
||||
public activeChannels: Channel[] = [];
|
||||
public filteredMinAmtActvChannels: Channel[] = [];
|
||||
public selectedChannelCtrl = new UntypedFormControl();
|
||||
public isAmp = false;
|
||||
public feeLimit: number | null = null;
|
||||
public selFeeLimitType = FEE_LIMIT_TYPES[0];
|
||||
public feeLimitTypes = FEE_LIMIT_TYPES;
|
||||
@ -142,10 +143,27 @@ export class LightningSendPaymentsComponent implements OnInit, OnDestroy {
|
||||
if (!this.paymentDecoded.num_satoshis || this.paymentDecoded.num_satoshis === '' || this.paymentDecoded.num_satoshis === '0') {
|
||||
this.zeroAmtInvoice = true;
|
||||
this.paymentDecoded.num_satoshis = this.paymentAmount?.toString() || '';
|
||||
this.store.dispatch(sendPayment({ payload: { uiMessage: UI_MESSAGES.SEND_PAYMENT, paymentReq: this.paymentRequest, paymentAmount: this.paymentAmount || 0, outgoingChannel: this.selectedChannelCtrl.value, feeLimitType: this.selFeeLimitType.id, feeLimit: this.feeLimit, fromDialog: true } }));
|
||||
const payload: SendPayment = {
|
||||
uiMessage: UI_MESSAGES.SEND_PAYMENT,
|
||||
payment_request: this.paymentRequest,
|
||||
amp: this.isAmp,
|
||||
amt: this.paymentAmount || 0,
|
||||
outgoing_chan_ids: this.selectedChannelCtrl.value?.chan_id ? [this.selectedChannelCtrl.value.chan_id] : undefined,
|
||||
fee_limit_sat: getFeeLimitSat(this.selFeeLimitType.id, this.feeLimit, (this.paymentAmount || 0)),
|
||||
fromDialog: true
|
||||
};
|
||||
this.store.dispatch(sendPayment({ payload }));
|
||||
} else {
|
||||
this.zeroAmtInvoice = false;
|
||||
this.store.dispatch(sendPayment({ payload: { uiMessage: UI_MESSAGES.SEND_PAYMENT, paymentReq: this.paymentRequest, outgoingChannel: this.selectedChannelCtrl.value, feeLimitType: this.selFeeLimitType.id, feeLimit: this.feeLimit, fromDialog: true } }));
|
||||
const payload: SendPayment = {
|
||||
uiMessage: UI_MESSAGES.SEND_PAYMENT,
|
||||
payment_request: this.paymentRequest,
|
||||
amp: this.isAmp,
|
||||
outgoing_chan_ids: this.selectedChannelCtrl.value?.chan_id ? [this.selectedChannelCtrl.value.chan_id] : undefined,
|
||||
fee_limit_sat: getFeeLimitSat(this.selFeeLimitType.id, this.feeLimit, (+this.paymentDecoded.num_satoshis || 0)),
|
||||
fromDialog: true
|
||||
};
|
||||
this.store.dispatch(sendPayment({ payload }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,6 +249,7 @@ export class LightningSendPaymentsComponent implements OnInit, OnDestroy {
|
||||
resetData() {
|
||||
this.paymentDecoded = {};
|
||||
this.paymentRequest = '';
|
||||
this.isAmp = false;
|
||||
this.selectedChannelCtrl.setValue(null);
|
||||
this.filteredMinAmtActvChannels = this.activeChannels;
|
||||
if (this.filteredMinAmtActvChannels.length && this.filteredMinAmtActvChannels.length > 0) {
|
||||
|
@ -552,13 +552,13 @@ export interface FetchPayments {
|
||||
export interface SendPayment {
|
||||
uiMessage: string;
|
||||
fromDialog: boolean;
|
||||
paymentReq: string;
|
||||
paymentAmount?: number;
|
||||
outgoingChannel?: Channel | null;
|
||||
feeLimitType?: string;
|
||||
feeLimit?: number | null;
|
||||
allowSelfPayment?: boolean;
|
||||
lastHopPubkey?: string;
|
||||
payment_request: string;
|
||||
amp: boolean;
|
||||
amt?: number;
|
||||
outgoing_chan_ids?: string[] | [];
|
||||
fee_limit_sat?: number | null;
|
||||
allow_self_payment?: boolean;
|
||||
last_hop_pubkey?: string;
|
||||
}
|
||||
|
||||
export interface GetNewAddress {
|
||||
|
@ -16,7 +16,7 @@ export const SECS_IN_YEAR = 31536000;
|
||||
|
||||
export const DEFAULT_INVOICE_EXPIRY = HOUR_SECONDS * 24 * 7;
|
||||
|
||||
export const VERSION = '0.15.2-beta';
|
||||
export const VERSION = '0.15.3-beta';
|
||||
|
||||
export const API_URL = isDevMode() ? 'http://localhost:3000/rtl/api' : './api';
|
||||
|
||||
@ -70,7 +70,7 @@ export const TRANS_TYPES = [
|
||||
export const FEE_LIMIT_TYPES = [
|
||||
{ id: 'none', name: 'No Fee Limit', placeholder: 'No Limit' },
|
||||
{ id: 'fixed', name: 'Fixed Limit (Sats)', placeholder: 'Fixed Limit in Sats' },
|
||||
{ id: 'percent', name: 'Percentage of Payment Amount', placeholder: 'Percentage Limit' }
|
||||
{ id: 'percent', name: 'Percentage of Amount', placeholder: 'Percentage Limit' }
|
||||
];
|
||||
|
||||
export const FEE_RATE_TYPES = [
|
||||
@ -1397,3 +1397,14 @@ export function getSelectedCurrency(currencyID: string) {
|
||||
}
|
||||
return foundCurrency;
|
||||
}
|
||||
|
||||
|
||||
export function getFeeLimitSat(selFeeLimitTypeID: string, feeLimit: number, amount?: number) {
|
||||
if (selFeeLimitTypeID === 'fixed') {
|
||||
return feeLimit;
|
||||
}
|
||||
if (selFeeLimitTypeID === 'percent') {
|
||||
return Math.ceil(((amount || 0) * feeLimit) / 100);
|
||||
}
|
||||
return 1000000;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user