balanceofsatoshis 12.7.1 → 12.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Versions
2
2
 
3
+ ## 12.8.2
4
+
5
+ - `telegram`: Add safeguards to deal with errors on /graph command
6
+
7
+ ## 12.8.0
8
+
9
+ - `send`: Add support for specifying a LNURL or lightning.address to send to
10
+
3
11
  ## 12.7.1
4
12
 
5
13
  - `swap`: Add keysend support to swap for pushing swap requests
package/bos CHANGED
@@ -1129,7 +1129,7 @@ prog
1129
1129
  })
1130
1130
 
1131
1131
  // Open a balanced channel with a peer
1132
- .command('open-balanced-channel', 'Open a dual-funded channel with a node')
1132
+ .command('open-balanced-channel', 'Open channel with equal start balances')
1133
1133
  .help('Remote node must be prepared to receive this type of channel')
1134
1134
  .help('The remote node should also run this same command after you propose')
1135
1135
  .help('Channel details are negotiated with keysend so that is also required')
@@ -1506,7 +1506,7 @@ prog
1506
1506
  .help('Formulas supported in amount, and N*USD or N*EUR')
1507
1507
  .help('Also supported in formulas: LIQUIDITY, INBOUND, OUTBOUND (with peer)')
1508
1508
  .help('OUT_INBOUND, OUT_OUTBOUND (when specifying outbound peer)')
1509
- .argument('<to>', 'Send to node with public key, or zero amount pay request')
1509
+ .argument('<to>', 'Send to public key, zero pay request, lnurl, ln.address')
1510
1510
  .option('--amount <amount>', 'Amount to send to destination', STRING, '1')
1511
1511
  .option('--avoid <avoid>', 'Avoid forwarding via node/chan/tag', REPEATABLE)
1512
1512
  .option('--dryrun', 'Avoid actually sending funds')
@@ -0,0 +1,75 @@
1
+ const asyncAuto = require('async/auto');
2
+ const {returnResult} = require('asyncjs-util');
3
+
4
+ const getPayRequest = require('./get_pay_request');
5
+ const getPayTerms = require('./get_pay_terms');
6
+ const parseUrl = require('./parse_url');
7
+
8
+ const tokensAsMtokens = tokens => (BigInt(tokens) * BigInt(1e3)).toString();
9
+
10
+ /** Get a LNURL request for a given amount
11
+
12
+ {
13
+ lnurl: <LNUrl String>
14
+ request: <Request Function>
15
+ tokens: <Tokens Payment Request String>
16
+ }
17
+
18
+ @returns via cbk or Promise
19
+ {
20
+ destination: <Destination Public Key Hex String>
21
+ request: <BOLT 11 Payment Request String>
22
+ }
23
+ */
24
+ module.exports = ({lnurl, request, tokens}, cbk) => {
25
+ return new Promise((resolve, reject) => {
26
+ return asyncAuto({
27
+ // Check arguments
28
+ validate: cbk => {
29
+ try {
30
+ parseUrl({url: lnurl});
31
+ } catch (err) {
32
+ return cbk([400, err.message]);
33
+ }
34
+
35
+ if (!request) {
36
+ return cbk([400, 'ExpectedRequestFunctionToGetLnurlRequest']);
37
+ }
38
+
39
+ if (!tokens) {
40
+ return cbk([400, 'ExpectedTokensToGetLnurlRequest']);
41
+ }
42
+
43
+ return cbk();
44
+ },
45
+
46
+ // Parse the LNURL into a regular url
47
+ url: ['validate', ({}, cbk) => {
48
+ return cbk(null, parseUrl({url: lnurl}).url);
49
+ }],
50
+
51
+ // Get accepted terms from the encoded url
52
+ getTerms: ['url', ({url}, cbk) => getPayTerms({request, url}, cbk)],
53
+
54
+ // Get payment request
55
+ getRequest: ['getTerms', ({getTerms}, cbk) => {
56
+ if (tokens > getTerms.max) {
57
+ return cbk([400, 'PaymentAmountAboveMaximum', {max: getTerms.max}]);
58
+ }
59
+
60
+ if (tokens < getTerms.min) {
61
+ return cbk([400, 'PaymentAmountBelowMinimum', {min: getTerms.min}]);
62
+ }
63
+
64
+ return getPayRequest({
65
+ request,
66
+ hash: getTerms.hash,
67
+ mtokens: tokensAsMtokens(tokens),
68
+ url: getTerms.url,
69
+ },
70
+ cbk);
71
+ }],
72
+ },
73
+ returnResult({reject, resolve, of: 'getRequest'}, cbk));
74
+ });
75
+ };
@@ -0,0 +1,96 @@
1
+ const asyncAuto = require('async/auto');
2
+ const {parsePaymentRequest} = require('ln-service');
3
+ const {returnResult} = require('asyncjs-util');
4
+
5
+ const errorStatus = 'ERROR';
6
+
7
+ /** Get a payment request for a LNURL
8
+
9
+ {
10
+ hash: <Hash Hex String>
11
+ mtokens: <Millitokens For Payment Request String>
12
+ request: <Request Function>
13
+ url: <URL String>
14
+ }
15
+
16
+ @returns via cbk or Promise
17
+ {
18
+ destination: <Destination Public Key Hex String>
19
+ request: <BOLT 11 Payment Request String>
20
+ }
21
+ */
22
+ module.exports = ({hash, mtokens, request, url}, cbk) => {
23
+ return new Promise((resolve, reject) => {
24
+ return asyncAuto({
25
+ // Check arguments
26
+ validate: cbk => {
27
+ if (!hash) {
28
+ return cbk([400, 'ExpectedDescriptionHashToGetLnurlPayRequest']);
29
+ }
30
+
31
+ if (!mtokens) {
32
+ return cbk([400, 'ExpectedMillitokensToGetLnurlPayRequest']);
33
+ }
34
+
35
+ if (!request) {
36
+ return cbk([400, 'ExpectedRequestFunctionToGetLnurlPayRequest']);
37
+ }
38
+
39
+ if (!url) {
40
+ return cbk([400, 'ExpectedUrlToGetLnurlPayRequest']);
41
+ }
42
+
43
+ return cbk();
44
+ },
45
+
46
+ // Get the payment request
47
+ getRequest: ['validate', ({}, cbk) => {
48
+ const qs = {amount: mtokens};
49
+
50
+ return request({qs, url, json: true}, (err, r, json) => {
51
+ if (!!err) {
52
+ return cbk([503, 'FailedToGetPaymentRequestFromService', {err}]);
53
+ }
54
+
55
+ if (!json) {
56
+ return cbk([503, 'ServiceFailedToReturnPayReqJson']);
57
+ }
58
+
59
+ if (json.status === errorStatus) {
60
+ return cbk([503, 'ServiceReturnedError', {err: json.reason}]);
61
+ }
62
+
63
+ if (!json.pr) {
64
+ return cbk([503, 'ExpectedPaymentRequestFromService']);
65
+ }
66
+
67
+ try {
68
+ parsePaymentRequest({request: json.pr});
69
+ } catch (err) {
70
+ return cbk([503, 'FailedToParseReturnedPaymentRequest', {err}]);
71
+ }
72
+
73
+ const request = parsePaymentRequest({request: json.pr});
74
+
75
+ if (request.description_hash !== hash) {
76
+ return cbk([503, 'ServiceReturnedInvalidPaymentDescriptionHash']);
77
+ }
78
+
79
+ if (request.is_expired) {
80
+ return cbk([503, 'ServiceReturnedExpiredPaymentRequest']);
81
+ }
82
+
83
+ if (request.mtokens !== mtokens) {
84
+ return cbk([503, 'ServiceReturnedIncorrectInvoiceAmount']);
85
+ }
86
+
87
+ return cbk(null, {
88
+ destination: request.destination,
89
+ request: json.pr,
90
+ });
91
+ });
92
+ }],
93
+ },
94
+ returnResult({reject, resolve, of: 'getRequest'}, cbk));
95
+ });
96
+ };
@@ -0,0 +1,139 @@
1
+ const {createHash} = require('crypto');
2
+
3
+ const asyncAuto = require('async/auto');
4
+ const {returnResult} = require('asyncjs-util');
5
+
6
+ const {isArray} = Array;
7
+ const isNumber = n => !isNaN(n);
8
+ const lowestSendableValue = 1000;
9
+ const {max} = Math;
10
+ const minMaxSendable = 1000;
11
+ const minMinSendable = 1;
12
+ const mtokensAsTokens = n => Math.floor(n / 1000);
13
+ const {parse} = JSON;
14
+ const payRequestTag = 'payRequest';
15
+ const sha256 = n => createHash('sha256').update(n).digest().toString('hex');
16
+ const sslProtocol = 'https:';
17
+ const textPlain = 'text/plain';
18
+ const utf8AsBuffer = utf8 => Buffer.from(utf8, 'utf8');
19
+
20
+ /** Get payment terms
21
+
22
+ {
23
+ request: <Request Function>
24
+ url: <URL String>
25
+ }
26
+
27
+ @returns via cbk or Promise
28
+ {
29
+ description: <Payment Description String>
30
+ hash: <Expected Description Hash Hex String>
31
+ max: <Maximum Tokens Number>
32
+ min: <Minimum Tokens Number>
33
+ url: <Callback URL String>
34
+ }
35
+ */
36
+ module.exports = ({request, url}, cbk) => {
37
+ return new Promise((resolve, reject) => {
38
+ return asyncAuto({
39
+ // Check arguments
40
+ validate: cbk => {
41
+ if (!request) {
42
+ return cbk([400, 'ExpectedRequestFunctionToGetPayTerms']);
43
+ }
44
+
45
+ if (!url) {
46
+ return cbk([400, 'ExpectedUrlToGetPayTerms']);
47
+ }
48
+
49
+ return cbk();
50
+ },
51
+
52
+ // Get payment terms
53
+ getTerms: ['validate', ({}, cbk) => {
54
+ return request({url, json: true}, (err, r, json) => {
55
+ if (!!err) {
56
+ return cbk([503, 'FailureGettingLnUrlDataFromUrl', {err}]);
57
+ }
58
+
59
+ if (!json) {
60
+ return cbk([503, 'ExpectedJsonObjectReturnedInLnurlResponse']);
61
+ }
62
+
63
+ if (!json.callback) {
64
+ return cbk([503, 'ExpectedCallbackInLnurlResponseJson']);
65
+ }
66
+
67
+ try {
68
+ new URL(json.callback);
69
+ } catch (err) {
70
+ return cbk([503, 'ExpectedValidCallbackUrlInLnurlResponseJson']);
71
+ }
72
+
73
+ if ((new URL(json.callback)).protocol !== sslProtocol) {
74
+ return cbk([400, 'LnurlsThatSpecifyNonSslUrlsAreUnsupported']);
75
+ }
76
+
77
+ if (!isNumber(json.maxSendable)) {
78
+ return cbk([503, 'ExpectedNumericValueForMaxSendable']);
79
+ }
80
+
81
+ if (!json.maxSendable) {
82
+ return cbk([503, 'ExpectedNonZeroMaxSendableInLnurlResponse']);
83
+ }
84
+
85
+ if (json.maxSendable < minMaxSendable) {
86
+ return cbk([400, 'MaxSendableValueIsLowerThanSupportedValue']);
87
+ }
88
+
89
+ if (!json.metadata) {
90
+ return cbk([503, 'ExpectedLnUrlMetadataInLnurlResponse']);
91
+ }
92
+
93
+ try {
94
+ parse(json.metadata);
95
+ } catch (err) {
96
+ return cbk([503, 'ExpectedValidMetadataInLnurlResponse']);
97
+ }
98
+
99
+ if (!isArray(parse(json.metadata))) {
100
+ return cbk([503, 'ExpectedMetadataArrayInLnurlResponse', json]);
101
+ }
102
+
103
+ const [, description] = parse(json.metadata)
104
+ .filter(isArray)
105
+ .find(([entry, text]) => entry === textPlain && !!text);
106
+
107
+ if (!description) {
108
+ return cbk([503, 'ExpectedTextPlainEntryInLnurlResponse']);
109
+ }
110
+
111
+ if (!isNumber(json.minSendable)) {
112
+ return cbk([503, 'ExpectedNumericValueForMinSendable']);
113
+ }
114
+
115
+ if (json.minSendable < minMinSendable) {
116
+ return cbk([503, 'ExpectedHigherMinSendableValueInLnurlResponse']);
117
+ }
118
+
119
+ if (json.minSendable > json.maxSendable) {
120
+ return cbk([503, 'ExpectedMaxSendableMoreThanMinSendable']);
121
+ }
122
+
123
+ if (json.tag !== payRequestTag) {
124
+ return cbk([503, 'ExpectedPaymentRequestTagInLnurlResponse']);
125
+ }
126
+
127
+ return cbk(null, {
128
+ description,
129
+ hash: sha256(utf8AsBuffer(json.metadata)),
130
+ max: mtokensAsTokens(json.maxSendable),
131
+ min: mtokensAsTokens(max(lowestSendableValue, json.minSendable)),
132
+ url: json.callback,
133
+ });
134
+ });
135
+ }],
136
+ },
137
+ returnResult({reject, resolve, of: 'getTerms'}, cbk));
138
+ });
139
+ };
package/lnurl/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ const getLnurlRequest = require('./get_lnurl_request');
1
2
  const manageLnurl = require('./manage_lnurl');
3
+ const parseUrl = require('./parse_url');
2
4
 
3
- module.exports = {manageLnurl};
5
+ module.exports = {getLnurlRequest, manageLnurl, parseUrl};
package/lnurl/pay.js CHANGED
@@ -1,44 +1,35 @@
1
- const {createHash} = require('crypto');
2
-
3
1
  const asyncAuto = require('async/auto');
4
2
  const {getNodeAlias} = require('ln-sync');
5
3
  const moment = require('moment');
6
4
  const {returnResult} = require('asyncjs-util');
7
5
  const {parsePaymentRequest} = require('ln-service');
8
6
 
7
+ const getPayRequest = require('./get_pay_request');
8
+ const getPayTerms = require('./get_pay_terms');
9
9
  const parseUrl = require('./parse_url');
10
10
  const {pay} = require('./../network');
11
11
 
12
- const errorStatus = 'ERROR';
13
12
  const {isArray} = Array;
14
13
  const isNumber = n => !isNaN(n);
15
- const lowestSendableValue = 1000;
16
- const {max} = Math;
17
- const minMaxSendable = 1000;
18
- const minMinSendable = 1;
19
- const mtokensAsTokens = n => Math.floor(n / 1000);
20
- const {parse} = JSON;
21
- const payRequestTag = 'payRequest';
22
14
  const {round} = Math;
23
- const sha256 = n => createHash('sha256').update(n).digest().toString('hex');
24
- const sslProtocol = 'https:';
25
- const textPlain = 'text/plain';
26
15
  const tokensAsBigUnit = tokens => (tokens / 1e8).toFixed(8);
27
16
  const tokensAsMillitokens = n => n * 1000;
28
- const utf8AsBuffer = utf8 => Buffer.from(utf8, 'utf8');
29
17
 
30
18
  /** Pay to lnurl
31
- {
32
- ask: <Ask Function>
33
- avoid: [<Avoid Forwarding Through String>]
34
- lnd: <Authenticated LND API Object>
35
- lnurl: <Lnurl String>
36
- logger: <Winston Logger Object>
37
- max_fee: <Max Fee Tokens Number>
38
- max_paths: <Maximum Paths Number>
39
- out: [<Out Through Peer With Public Key Hex String>]
40
- request: <Request Function>
41
- }
19
+
20
+ {
21
+ ask: <Ask Function>
22
+ avoid: [<Avoid Forwarding Through String>]
23
+ lnd: <Authenticated LND API Object>
24
+ lnurl: <Lnurl String>
25
+ logger: <Winston Logger Object>
26
+ max_fee: <Max Fee Tokens Number>
27
+ max_paths: <Maximum Paths Number>
28
+ out: [<Out Through Peer With Public Key Hex String>]
29
+ request: <Request Function>
30
+ }
31
+
32
+ @returns via cbk or Promise
42
33
  */
43
34
  module.exports = (args, cbk) => {
44
35
  return new Promise((resolve, reject) => {
@@ -92,89 +83,11 @@ module.exports = (args, cbk) => {
92
83
 
93
84
  // Get accepted terms from the encoded url
94
85
  getTerms: ['validate', ({}, cbk) => {
95
- const {url} = parseUrl({url: args.lnurl});
96
-
97
- return args.request({url, json: true}, (err, r, json) => {
98
- if (!!err) {
99
- return cbk([503, 'FailureGettingLnUrlDataFromUrl', {err}]);
100
- }
101
-
102
- if (!json) {
103
- return cbk([503, 'ExpectedJsonObjectReturnedInLnurlResponse']);
104
- }
105
-
106
- if (!json.callback) {
107
- return cbk([503, 'ExpectedCallbackInLnurlResponseJson']);
108
- }
109
-
110
- try {
111
- new URL(json.callback);
112
- } catch (err) {
113
- return cbk([503, 'ExpectedValidCallbackUrlInLnurlResponseJson']);
114
- }
115
-
116
- if ((new URL(json.callback)).protocol !== sslProtocol) {
117
- return cbk([400, 'LnurlsThatSpecifyNonSslUrlsAreUnsupported']);
118
- }
119
-
120
- if (!isNumber(json.maxSendable)) {
121
- return cbk([503, 'ExpectedNumericValueForMaxSendable']);
122
- }
123
-
124
- if (!json.maxSendable) {
125
- return cbk([503, 'ExpectedNonZeroMaxSendableInLnurlResponse']);
126
- }
127
-
128
- if (json.maxSendable < minMaxSendable) {
129
- return cbk([400, 'MaxSendableValueIsLowerThanSupportedValue']);
130
- }
131
-
132
- if (!json.metadata) {
133
- return cbk([503, 'ExpectedLnUrlMetadataInLnurlResponse']);
134
- }
135
-
136
- try {
137
- parse(json.metadata);
138
- } catch (err) {
139
- return cbk([503, 'ExpectedValidMetadataInLnurlResponse']);
140
- }
141
-
142
- if (!isArray(parse(json.metadata))) {
143
- return cbk([503, 'ExpectedMetadataArrayInLnurlResponse', json]);
144
- }
145
-
146
- const [, description] = parse(json.metadata)
147
- .filter(isArray)
148
- .find(([entry, text]) => entry === textPlain && !!text);
149
-
150
- if (!description) {
151
- return cbk([503, 'ExpectedTextPlainEntryInLnurlResponse']);
152
- }
153
-
154
- if (!isNumber(json.minSendable)) {
155
- return cbk([503, 'ExpectedNumericValueForMinSendable']);
156
- }
157
-
158
- if (json.minSendable < minMinSendable) {
159
- return cbk([503, 'ExpectedHigherMinSendableValueInLnurlResponse']);
160
- }
161
-
162
- if (json.minSendable > json.maxSendable) {
163
- return cbk([503, 'ExpectedMaxSendableMoreThanMinSendable']);
164
- }
165
-
166
- if (json.tag !== payRequestTag) {
167
- return cbk([503, 'ExpectedPaymentRequestTagInLnurlResponse']);
168
- }
169
-
170
- return cbk(null, {
171
- description,
172
- hash: sha256(utf8AsBuffer(json.metadata)),
173
- max: mtokensAsTokens(json.maxSendable),
174
- min: mtokensAsTokens(max(lowestSendableValue, json.minSendable)),
175
- url: json.callback,
176
- });
177
- });
86
+ return getPayTerms({
87
+ request: args.request,
88
+ url: parseUrl({url: args.lnurl}).url,
89
+ },
90
+ cbk);
178
91
  }],
179
92
 
180
93
  // Ask the user for how much they want to send
@@ -217,57 +130,18 @@ module.exports = (args, cbk) => {
217
130
 
218
131
  // Get payment request
219
132
  getRequest: ['askAmount', 'getTerms', ({askAmount, getTerms}, cbk) => {
220
- const qs = {amount: askAmount};
221
- const {url} = getTerms;
222
-
223
- return args.request({url, qs, json: true}, (err, r, json) => {
224
- if (!!err) {
225
- return cbk([503, 'FailedToGetPaymentRequestFromService', {err}]);
226
- }
227
-
228
- if (!json) {
229
- return cbk([503, 'ServiceFailedToReturnPayReqJson']);
230
- }
231
-
232
- if (json.status === errorStatus) {
233
- return cbk([503, 'ServiceReturnedError', {err: json.reason}]);
234
- }
235
-
236
- if (!json.pr) {
237
- return cbk([503, 'ExpectedPaymentRequestFromService']);
238
- }
239
-
240
- try {
241
- parsePaymentRequest({request: json.pr});
242
- } catch (err) {
243
- return cbk([503, 'FailedToParseReturnedPaymentRequest', {err}]);
244
- }
245
-
246
- const request = parsePaymentRequest({request: json.pr});
247
-
248
- if (request.description_hash !== getTerms.hash) {
249
- return cbk([503, 'ServiceReturnedInvalidPaymentDescriptionHash']);
250
- }
251
-
252
- if (request.is_expired) {
253
- return cbk([503, 'ServiceReturnedExpiredPaymentRequest']);
254
- }
255
-
256
- if (request.mtokens !== askAmount.toString()) {
257
- return cbk([503, 'ServiceReturnedIncorrectInvoiceAmount']);
258
- }
259
-
260
- return cbk(null, json.pr);
261
- });
133
+ return getPayRequest({
134
+ hash: getTerms.hash,
135
+ mtokens: askAmount.toString(),
136
+ request: args.request,
137
+ url: getTerms.url,
138
+ },
139
+ cbk);
262
140
  }],
263
141
 
264
142
  // Get the destination node alias
265
143
  getAlias: ['getRequest', ({getRequest}, cbk) => {
266
- return getNodeAlias({
267
- id: parsePaymentRequest({request: getRequest}).destination,
268
- lnd: args.lnd,
269
- },
270
- cbk);
144
+ return getNodeAlias({id: getRequest.destination, lnd: args.lnd}, cbk);
271
145
  }],
272
146
 
273
147
  // Confirm payment
@@ -277,12 +151,12 @@ module.exports = (args, cbk) => {
277
151
  'getTerms',
278
152
  ({getAlias, getRequest, getTerms}, cbk) =>
279
153
  {
280
- const details = parsePaymentRequest({request: getRequest});
154
+ const details = parsePaymentRequest({request: getRequest.request});
281
155
 
282
156
  args.logger.info({
283
157
  amount: details.safe_tokens,
284
158
  description: getTerms.description,
285
- payment_request: getRequest,
159
+ payment_request: getRequest.request,
286
160
  expires: moment(details.expires_at).fromNow(),
287
161
  });
288
162
 
@@ -312,7 +186,7 @@ module.exports = (args, cbk) => {
312
186
  max_fee: args.max_fee,
313
187
  max_paths: args.max_paths,
314
188
  out: args.out,
315
- request: getRequest,
189
+ request: getRequest.request,
316
190
  },
317
191
  cbk);
318
192
  }],
@@ -10,8 +10,10 @@ const {parsePaymentRequest} = require('ln-service');
10
10
  const {returnResult} = require('asyncjs-util');
11
11
 
12
12
  const {getIgnores} = require('./../routing');
13
+ const getLnurlRequest = require('./../lnurl/get_lnurl_request');
13
14
  const {getTags} = require('./../tags');
14
15
  const {parseAmount} = require('./../display');
16
+ const parseUrl = require('./../lnurl/parse_url');
15
17
  const probeDestination = require('./probe_destination');
16
18
 
17
19
  const coins = ['BTC', 'LTC'];
@@ -153,28 +155,39 @@ module.exports = (args, cbk) => {
153
155
  // Payment details
154
156
  payment: ['validate', ({}, cbk) => {
155
157
  try {
156
- const {destination, mtokens} = parsePaymentRequest({
157
- request: args.destination,
158
- });
158
+ const {url} = parseUrl({url: args.destination});
159
+
160
+ return cbk(null, {lnurl: args.destination});
161
+ } catch (err) {
162
+ // Ignore errors, destination isn't a LNURL
163
+ }
164
+
165
+ try {
166
+ const details = parsePaymentRequest({request: args.destination});
167
+
168
+ const {destination, mtokens} = details;
159
169
 
160
170
  if (!!BigInt(mtokens)) {
161
171
  return cbk([400, 'ExpectedZeroAmountPayRequestToSendFunds']);
162
172
  }
163
173
 
164
174
  return cbk(null, {destination, request: args.destination});
165
- } catch (_) {
166
- return findKey({
167
- lnd: args.lnd,
168
- query: args.destination,
169
- },
170
- (err, res) => {
171
- if (!!err) {
172
- return cbk(err);
173
- }
174
-
175
- return cbk(null, {destination: res.public_key, is_push: true});
176
- });
175
+ } catch (err) {
176
+ // Ignore errors, destination isn't BOLT 11
177
177
  }
178
+
179
+ // Find the key to send to
180
+ return findKey({
181
+ lnd: args.lnd,
182
+ query: args.destination,
183
+ },
184
+ (err, res) => {
185
+ if (!!err) {
186
+ return cbk(err);
187
+ }
188
+
189
+ return cbk(null, {destination: res.public_key, is_push: true});
190
+ });
178
191
  }],
179
192
 
180
193
  // Get ignores
@@ -366,22 +379,61 @@ module.exports = (args, cbk) => {
366
379
  }
367
380
  }],
368
381
 
369
- // Push the amount to the destination
370
- push: [
371
- 'getIgnores',
372
- 'getInKey',
373
- 'getOutKey',
382
+ // Get LNURL payment request
383
+ getLnurlRequest: [
374
384
  'parseAmount',
375
385
  'payment',
376
- ({getIgnores, getInKey, getOutKey, parseAmount, payment}, cbk) =>
386
+ ({parseAmount, payment}, cbk) =>
377
387
  {
378
388
  if (parseAmount.tokens < minTokens) {
379
389
  return cbk([400, 'ExpectedNonZeroAmountToPushPayment']);
380
390
  }
381
391
 
392
+ // Exit early when there is no LNURL to send to
393
+ if (!payment.lnurl) {
394
+ return cbk(null, {});
395
+ }
396
+
397
+ return getLnurlRequest({
398
+ lnurl: payment.lnurl,
399
+ request: args.request,
400
+ tokens: parseAmount.tokens,
401
+ },
402
+ cbk);
403
+ }],
404
+
405
+ // Final payment details
406
+ send: [
407
+ 'getLnurlRequest',
408
+ 'parseAmount',
409
+ 'payment',
410
+ ({getLnurlRequest, parseAmount, payment}, cbk) =>
411
+ {
412
+ if (parseAmount.tokens < minTokens) {
413
+ return cbk([400, 'ExpectedNonZeroAmountToPushPayment']);
414
+ }
415
+
416
+ return cbk(null, {
417
+ destination: getLnurlRequest.destination || payment.destination,
418
+ is_push: payment.is_push,
419
+ max_fee: parseAmount.max_fee,
420
+ request: getLnurlRequest.request || payment.request,
421
+ tokens: !getLnurlRequest.request ? parseAmount.tokens : undefined,
422
+ });
423
+ }],
424
+
425
+ // Push the amount to the destination
426
+ push: [
427
+ 'getIgnores',
428
+ 'getInKey',
429
+ 'getOutKey',
430
+ 'send',
431
+ ({getIgnores, getInKey, getOutKey, send}, cbk) =>
432
+ {
382
433
  args.logger.info({
383
- paying: formatTokens({tokens: parseAmount.tokens}).display,
384
- to: payment.destination,
434
+ max_fee: send.max_fee,
435
+ paying: formatTokens({tokens: send.tokens}).display,
436
+ to: send.destination,
385
437
  });
386
438
 
387
439
  if (!!args.is_dry_run) {
@@ -389,25 +441,25 @@ module.exports = (args, cbk) => {
389
441
  }
390
442
 
391
443
  return probeDestination({
392
- destination: payment.destination,
444
+ destination: send.destination,
393
445
  fs: args.fs,
394
446
  ignore: getIgnores.ignore,
395
447
  lnd: args.lnd,
396
448
  logger: args.logger,
397
449
  in_through: getInKey,
398
450
  is_omitting_message_from: args.is_omitting_message_from,
399
- is_push: payment.is_push,
451
+ is_push: send.is_push,
400
452
  is_real_payment: true,
401
- max_fee: parseAmount.max_fee,
453
+ max_fee: send.max_fee,
402
454
  message: args.message,
403
455
  messages: args.quiz_answers.map((answer, i) => ({
404
456
  type: (quizStart + i).toString(),
405
457
  value: utf8AsHex(answer),
406
458
  })),
407
459
  out_through: getOutKey,
408
- request: payment.request,
460
+ request: send.request,
409
461
  timeout_minutes: args.timeout_minutes,
410
- tokens: parseAmount.tokens,
462
+ tokens: send.tokens,
411
463
  },
412
464
  cbk);
413
465
  }],
package/package.json CHANGED
@@ -30,15 +30,15 @@
30
30
  "csv-parse": "5.0.4",
31
31
  "ecpair": "2.0.1",
32
32
  "goldengate": "11.2.1",
33
- "grammy": "1.8.2",
33
+ "grammy": "1.8.3",
34
34
  "hot-formula-parser": "4.0.0",
35
35
  "import-lazy": "4.0.0",
36
36
  "ini": "3.0.0",
37
37
  "inquirer": "8.2.4",
38
38
  "ln-accounting": "5.0.6",
39
- "ln-service": "53.15.0",
39
+ "ln-service": "53.16.0",
40
40
  "ln-sync": "3.12.0",
41
- "ln-telegram": "3.21.4",
41
+ "ln-telegram": "3.21.5",
42
42
  "moment": "2.29.3",
43
43
  "paid-services": "3.16.0",
44
44
  "probing": "2.0.5",
@@ -55,7 +55,7 @@
55
55
  "devDependencies": {
56
56
  "@alexbosworth/tap": "15.0.11",
57
57
  "invoices": "2.0.6",
58
- "ln-docker-daemons": "2.2.9",
58
+ "ln-docker-daemons": "2.2.10",
59
59
  "mock-lnd": "1.4.1",
60
60
  "tiny-secp256k1": "2.2.1"
61
61
  },
@@ -84,5 +84,5 @@
84
84
  "postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .",
85
85
  "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/telegram/*.js test/wallets/*.js"
86
86
  },
87
- "version": "12.7.1"
87
+ "version": "12.8.2"
88
88
  }
@@ -556,9 +556,15 @@ module.exports = (args, cbk) => {
556
556
  // Detect funding transaction
557
557
  detectFunding: [
558
558
  'getNetwork',
559
+ 'isExternal',
559
560
  'openChannels',
560
- ({getNetwork, openChannels}, cbk) =>
561
+ ({getNetwork, isExternal, openChannels}, cbk) =>
561
562
  {
563
+ // Exit early when the funding is coming from the internal wallet
564
+ if (!isExternal) {
565
+ return cbk();
566
+ }
567
+
562
568
  if (!detectNetworks.includes(getNetwork.network)) {
563
569
  return cbk();
564
570
  }