lightning 4.10.1 → 4.10.5

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/grpc/protos/lightning.proto +20 -0
  3. package/grpc/protos/signer.proto +3 -0
  4. package/lnd_messages/close_channel_request.js +35 -0
  5. package/lnd_messages/index.js +5 -0
  6. package/lnd_messages/open_channel_request.js +51 -0
  7. package/lnd_messages/pay_via_route_request.js +61 -0
  8. package/lnd_methods/index.js +2 -0
  9. package/lnd_methods/macaroon/accept_rpc_request.js +44 -0
  10. package/lnd_methods/macaroon/handle_rpc_request_update.js +148 -0
  11. package/lnd_methods/macaroon/methods.json +9 -5
  12. package/lnd_methods/macaroon/reject_rpc_request.js +52 -0
  13. package/lnd_methods/macaroon/subscribe_to_rpc_requests.js +150 -49
  14. package/lnd_methods/macaroon/uris_for_method.js +8 -5
  15. package/lnd_methods/offchain/get_failed_payments.js +190 -0
  16. package/lnd_methods/offchain/get_payments.js +1 -3
  17. package/lnd_methods/offchain/index.js +2 -0
  18. package/lnd_methods/offchain/probe_for_route.js +5 -0
  19. package/lnd_responses/rpc_invoice_as_invoice.js +4 -2
  20. package/lnd_responses/rpc_payment_as_payment.js +71 -21
  21. package/package.json +3 -3
  22. package/test/lnd_messages/test_close_channel_request.js +72 -0
  23. package/test/lnd_messages/test_open_channel_request.js +87 -0
  24. package/test/lnd_messages/test_pay_via_route_request.js +79 -0
  25. package/test/lnd_methods/macaroon/test_accept_rpc_request.js +37 -0
  26. package/test/lnd_methods/macaroon/test_handle_rpc_request_update.js +237 -0
  27. package/test/lnd_methods/macaroon/test_reject_rpc_request.js +41 -0
  28. package/test/lnd_methods/macaroon/test_subscribe_to_rpc_requests.js +242 -0
  29. package/test/lnd_methods/macaroon/test_uris_for_method.js +10 -0
  30. package/test/lnd_methods/offchain/test_get_failed_payments.js +160 -0
  31. package/test/lnd_methods/offchain/test_get_payments.js +2 -2
  32. package/test/lnd_methods/onchain/test_close_channel.js +0 -1
  33. package/test/lnd_responses/test_rpc_invoice_as_invoice.js +34 -0
  34. package/test/lnd_responses/test_rpc_payment_as_payment.js +43 -8
  35. package/test/protos/protos.json +4 -4
@@ -6,25 +6,29 @@ const {returnResult} = require('asyncjs-util');
6
6
 
7
7
  const {emitSubscriptionError} = require('./../../grpc');
8
8
  const {handleRemoveListener} = require('./../../grpc');
9
+ const handleRpcRequestUpdate = require('./handle_rpc_request_update');
9
10
  const {isLnd} = require('./../../lnd_requests');
10
- const {rpcRequestUpdateAsEvent} = require('./../../lnd_responses');
11
11
 
12
- const cancelError = 'Cancelled on client';
13
- const events = ['request', 'response'];
14
12
  const makeId = () => randomBytes(32).toString('hex');
15
13
  const method = 'RegisterRPCMiddleware';
16
- const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
14
+ const minIdLength = 5;
17
15
  const type = 'default';
18
16
 
19
17
  /** Subscribe to RPC requests and their responses
20
18
 
19
+ `accept` and `reject` methods can be used with cbk or Promise syntax
20
+
21
21
  Requires `macaroon:write` permission
22
22
 
23
- LND must be running with rpc middleware enabled: `rpcmiddleware.enable=1`
23
+ LND must be running with "RPC middleware" enabled: `rpcmiddleware.enable=1`
24
24
 
25
25
  This method is not supported in LND 0.13.3 and below
26
26
 
27
27
  {
28
+ [id]: <RPC Middleware Interception Name String>
29
+ [is_intercepting_close_channel_requests]: <Intercept Channel Closes Bool>
30
+ [is_intercepting_open_channel_requests]: <Intercept Channel Opens Bool>
31
+ [is_intercepting_pay_via_routes_requests]: <Intercept Pay Via Route Bool>
28
32
  lnd: <Authenticated LND API Object>
29
33
  }
30
34
 
@@ -33,6 +37,77 @@ const type = 'default';
33
37
  subscription: <RPC Request Subscription EventEmitter Object>
34
38
  }
35
39
 
40
+ // A channel close request was intercepted: by default it will be rejected
41
+ @event 'close_channel_request'
42
+ {
43
+ accept: ({}, [cbk]) => {}
44
+ id: <Request Id Number>
45
+ macaroon: <Base64 Encoded Macaroon String>
46
+ reject: ({message: <Rejection String>}, [cbk]) => {}
47
+ request: {
48
+ [address]: <Request Sending Local Channel Funds To Address String>
49
+ [is_force_close]: <Is Force Close Bool>
50
+ [target_confirmations]: <Confirmation Target Number>
51
+ [tokens_per_vbyte]: <Tokens Per Virtual Byte Number>
52
+ transaction_id: <Transaction Id Hex String>
53
+ transaction_vout: <Transaction Output Index Number>
54
+ }
55
+ uri: <RPC URI String>
56
+ }
57
+
58
+ // A channel open request was intercepted: by default it will be rejected
59
+ @event 'open_channel_request'
60
+ {
61
+ accept: ({}, [cbk]) => {}
62
+ id: <Request Id Number>
63
+ macaroon: <Base64 Encoded Macaroon String>
64
+ reject: ({message: <Rejection String>}, [cbk]) => {}
65
+ request: {
66
+ [chain_fee_tokens_per_vbyte]: <Chain Fee Tokens Per VByte Number>
67
+ [cooperative_close_address]: <Prefer Cooperative Close To Address String>
68
+ [give_tokens]: <Tokens to Gift To Partner Number>
69
+ [is_private]: <Channel is Private Bool>
70
+ local_tokens: <Local Tokens Number>
71
+ [min_confirmations]: <Spend UTXOs With Minimum Confirmations Number>
72
+ [min_htlc_mtokens]: <Minimum HTLC Millitokens String>
73
+ partner_public_key: <Public Key Hex String>
74
+ [partner_csv_delay]: <Peer Output CSV Delay Number>
75
+ }
76
+ uri: <RPC URI String>
77
+ }
78
+
79
+ // A pay to route request was intercepted: by default it will be rejected
80
+ @event 'pay_via_route_request'
81
+ {
82
+ accept: ({}, [cbk]) => {}
83
+ id: <Request Id Number>
84
+ macaroon: <Base64 Encoded Macaroon String>
85
+ reject: ({message: <Rejection String>}, [cbk]) => {}
86
+ request: {
87
+ id: <Payment Hash Hex String>
88
+ route: {
89
+ fee: <Route Fee Tokens Number>
90
+ fee_mtokens: <Route Fee Millitokens String>
91
+ hops: [{
92
+ channel: <Standard Format Channel Id String>
93
+ channel_capacity: <Channel Capacity Tokens Number>
94
+ fee: <Fee Tokens Number>
95
+ fee_mtokens: <Fee Millitokens String>
96
+ forward: <Forward Tokens Number>
97
+ forward_mtokens: <Forward Millitokens String>
98
+ public_key: <Forward Edge Public Key Hex String>
99
+ [timeout]: <Timeout Block Height Number>
100
+ }]
101
+ mtokens: <Total Fee-Inclusive Millitokens String>
102
+ [payment]: <Payment Identifier Hex String>
103
+ [timeout]: <Timeout Block Height Number>
104
+ tokens: <Total Fee-Inclusive Tokens Number>
105
+ [total_mtokens]: <Total Payment Millitokens String>
106
+ }
107
+ }
108
+ uri: <RPC URI String>
109
+ }
110
+
36
111
  @event 'request'
37
112
  {
38
113
  id: <Request Id Number>
@@ -47,65 +122,91 @@ const type = 'default';
47
122
  [uri]: <RPC URI String>
48
123
  }
49
124
  */
50
- module.exports = ({lnd}, cbk) => {
125
+ module.exports = (args, cbk) => {
51
126
  return new Promise((resolve, reject) => {
52
127
  return asyncAuto({
53
128
  // Check arguments
54
129
  validate: cbk => {
55
- if (!isLnd({lnd, method, type})) {
130
+ if (!!args.id && args.id.length < minIdLength) {
131
+ return cbk([400, 'ExpectedLongerIdLengthToSpecifyMiddlewareName']);
132
+ }
133
+
134
+ if (!isLnd({method, type, lnd: args.lnd})) {
56
135
  return cbk([400, 'ExpectedLndToSubscribeToRpcRequests']);
57
136
  }
58
137
 
59
138
  return cbk();
60
139
  },
61
140
 
62
- // Intercept RPC requests
63
- intercept: ['validate', ({}, cbk) => {
141
+ // Initialize subscription
142
+ init: ['validate', ({}, cbk) => {
64
143
  const emitter = new EventEmitter();
65
-
66
- try {
67
- const subscription = lnd[type][method]({});
68
-
69
- subscription.write({
70
- register: {middleware_name: makeId(), read_only_mode: true},
71
- },
72
- err => {
73
- if (!!err) {
74
- return cbk([503, 'UnexpectedErrInterceptingRpcRequests', {err}]);
144
+ const isInterceptCloses = args.is_intercepting_close_channel_requests;
145
+ const isInterceptOpens = args.is_intercepting_open_channel_requests;
146
+ const isInterceptRoute = args.is_intercepting_pay_via_routes_requests;
147
+ const subscription = args.lnd[type][method]({});
148
+
149
+ const events = [
150
+ 'close_channel_request',
151
+ 'open_channel_request',
152
+ 'pay_via_route_request',
153
+ 'request',
154
+ 'response',
155
+ ];
156
+
157
+ const errored = emitSubscriptionError({emitter, subscription});
158
+
159
+ // Terminate subscription when all listeners are removed
160
+ handleRemoveListener({subscription, emitter, events});
161
+
162
+ subscription.on('end', () => emitter.emit('end'));
163
+ subscription.on('error', err => errored(err));
164
+ subscription.on('status', n => emitter.emit('status', n));
165
+
166
+ subscription.on('data', update => {
167
+ try {
168
+ const {accept, data, event} = handleRpcRequestUpdate({
169
+ subscription,
170
+ update,
171
+ is_intercepting_close_channel_requests: isInterceptCloses,
172
+ is_intercepting_open_channel_requests: isInterceptOpens,
173
+ is_intercepting_pay_via_routes_requests: isInterceptRoute,
174
+ lnd: args.lnd,
175
+ });
176
+
177
+ // Notify listeners of the RPC update
178
+ emitter.emit(event, data);
179
+
180
+ // Exit early when accepting/rejecting is left to the caller
181
+ if (!accept) {
182
+ return;
75
183
  }
76
184
 
77
- return cbk(null, {subscription: emitter});
78
- });
79
-
80
- handleRemoveListener({subscription, emitter, events});
81
-
82
- const errored = emitSubscriptionError({emitter, subscription});
185
+ // Accept traffic by default
186
+ return accept({}, err => !!err ? errored(err) : undefined);
187
+ } catch (err) {
188
+ return errored([503, err.message]);
189
+ }
190
+ });
83
191
 
84
- subscription.on('data', update => {
85
- try {
86
- const details = rpcRequestUpdateAsEvent(update);
87
-
88
- emitter.emit(details.event, {
89
- id: details.id,
90
- macaroon: details.macaroon,
91
- uri: details.uri,
92
- });
93
-
94
- return subscription.write({
95
- feedback: {replace_response: false},
96
- request_id: details.id,
97
- });
98
- } catch (err) {
99
- return errored([503, err.message]);
100
- }
101
- });
192
+ return cbk(null, {emitter, subscription});
193
+ }],
102
194
 
103
- subscription.on('end', () => emitter.emit('end'));
104
- subscription.on('error', err => errored(err));
105
- subscription.on('status', n => emitter.emit('status', n));
106
- } catch (err) {
107
- return cbk([503, 'UnexpectedErrorSubscribingToRpcRequests', {err}]);
108
- }
195
+ // Start the interception of RPC requests
196
+ intercept: ['init', ({init}, cbk) => {
197
+ return init.subscription.write({
198
+ register: {
199
+ middleware_name: args.id || makeId(),
200
+ read_only_mode: true,
201
+ },
202
+ },
203
+ err => {
204
+ if (!!err) {
205
+ return cbk([503, 'UnexpectedErrInterceptingRpcRequests', {err}]);
206
+ }
207
+
208
+ return cbk(null, {subscription: init.emitter});
209
+ });
109
210
  }],
110
211
  },
111
212
  returnResult({reject, resolve, of: 'intercept'}, cbk));
@@ -1,10 +1,11 @@
1
1
  const methods = require('./methods');
2
2
 
3
+ const {packageTypes} = require('./../../grpc');
3
4
  const {serviceTypes} = require('./../../grpc');
4
5
 
5
6
  const flatten = arr => arr.flat(Infinity);
6
7
  const type = n => [methods[n]].concat((methods[n].depends_on || []).map(type));
7
- const uriForMethod = (service, method) => `/lnrpc.${service}/${method}`;
8
+ const uriForMethod = (rpc, service, method) => `/${rpc}.${service}/${method}`;
8
9
 
9
10
  /** Given a method, derive URI macaroon permission strings required
10
11
 
@@ -31,19 +32,21 @@ module.exports = ({method}) => {
31
32
 
32
33
  const requiredMethods = flatten(type(method)).filter(({type}) => type);
33
34
 
34
- const uris = requiredMethods.map(({method, methods, type}) => {
35
+ const uris = flatten(requiredMethods.map(({method, methods, type}) => {
35
36
  const service = serviceTypes[type];
36
37
 
37
38
  if (!service) {
38
39
  throw new Error('ExpectedKnownMethodServiceToDeriveMacaroonUris');
39
40
  }
40
41
 
42
+ const rpc = packageTypes[service];
43
+
41
44
  return (methods || [method]).map(methodName => {
42
- return uriForMethod(service, methodName);
45
+ return uriForMethod(rpc, service, methodName);
43
46
  });
44
- });
47
+ }));
45
48
 
46
49
  uris.sort();
47
50
 
48
- return {uris: flatten(uris)};
51
+ return {uris};
49
52
  };
@@ -0,0 +1,190 @@
1
+ const asyncAuto = require('async/auto');
2
+ const {returnResult} = require('asyncjs-util');
3
+
4
+ const {isLnd} = require('./../../lnd_requests');
5
+ const {rpcPaymentAsPayment} = require('./../../lnd_responses');
6
+ const {sortBy} = require('./../../arrays');
7
+
8
+ const defaultLimit = 250;
9
+ const {isArray} = Array;
10
+ const isFailed = payment => !!payment && payment.status === 'FAILED';
11
+ const lastPageFirstIndexOffset = 1;
12
+ const method = 'listPayments';
13
+ const {parse} = JSON;
14
+ const {stringify} = JSON;
15
+ const type = 'default';
16
+
17
+ /** Get failed payments made through channels.
18
+
19
+ Requires `offchain:read` permission
20
+
21
+ {
22
+ [limit]: <Page Result Limit Number>
23
+ lnd: <Authenticated LND API Object>
24
+ [token]: <Opaque Paging Token String>
25
+ }
26
+
27
+ @returns via cbk or Promise
28
+ {
29
+ payments: [{
30
+ attempts: [{
31
+ [failure]: {
32
+ code: <Error Type Code Number>
33
+ [details]: {
34
+ [channel]: <Standard Format Channel Id String>
35
+ [height]: <Error Associated Block Height Number>
36
+ [index]: <Failed Hop Index Number>
37
+ [mtokens]: <Error Millitokens String>
38
+ [policy]: {
39
+ base_fee_mtokens: <Base Fee Millitokens String>
40
+ cltv_delta: <Locktime Delta Number>
41
+ fee_rate: <Fees Charged in Millitokens Per Million Number>
42
+ [is_disabled]: <Channel is Disabled Bool>
43
+ max_htlc_mtokens: <Maximum HLTC Millitokens Value String>
44
+ min_htlc_mtokens: <Minimum HTLC Millitokens Value String>
45
+ updated_at: <Updated At ISO 8601 Date String>
46
+ }
47
+ [timeout_height]: <Error CLTV Timeout Height Number>
48
+ [update]: {
49
+ chain: <Chain Id Hex String>
50
+ channel_flags: <Channel Flags Number>
51
+ extra_opaque_data: <Extra Opaque Data Hex String>
52
+ message_flags: <Message Flags Number>
53
+ signature: <Channel Update Signature Hex String>
54
+ }
55
+ }
56
+ message: <Error Message String>
57
+ }
58
+ [index]: <Payment Add Index Number>
59
+ [confirmed_at]: <Payment Confirmed At ISO 8601 Date String>
60
+ is_confirmed: <Payment Attempt Succeeded Bool>
61
+ is_failed: <Payment Attempt Failed Bool>
62
+ is_pending: <Payment Attempt is Waiting For Resolution Bool>
63
+ route: {
64
+ fee: <Route Fee Tokens Number>
65
+ fee_mtokens: <Route Fee Millitokens String>
66
+ hops: [{
67
+ channel: <Standard Format Channel Id String>
68
+ channel_capacity: <Channel Capacity Tokens Number>
69
+ fee: <Fee Number>
70
+ fee_mtokens: <Fee Millitokens String>
71
+ forward: <Forward Tokens Number>
72
+ forward_mtokens: <Forward Millitokens String>
73
+ [public_key]: <Forward Edge Public Key Hex String>
74
+ [timeout]: <Timeout Block Height Number>
75
+ }]
76
+ mtokens: <Total Fee-Inclusive Millitokens String>
77
+ [payment]: <Payment Identifier Hex String>
78
+ timeout: <Timeout Block Height Number>
79
+ tokens: <Total Fee-Inclusive Tokens Number>
80
+ [total_mtokens]: <Total Millitokens String>
81
+ }
82
+ }]
83
+ created_at: <Payment at ISO-8601 Date String>
84
+ [destination]: <Destination Node Public Key Hex String>
85
+ id: <Payment Preimage Hash String>
86
+ [index]: <Payment Add Index Number>
87
+ is_confirmed: <Payment is Confirmed Bool>
88
+ is_outgoing: <Transaction Is Outgoing Bool>
89
+ mtokens: <Millitokens Attempted to Pay to Destination String>
90
+ [request]: <BOLT 11 Payment Request String>
91
+ safe_tokens: <Payment Tokens Attempted to Pay Rounded Up Number>
92
+ tokens: <Rounded Down Tokens Attempted to Pay to Destination Number>
93
+ }]
94
+ [next]: <Next Opaque Paging Token String>
95
+ }
96
+ */
97
+ module.exports = ({limit, lnd, token}, cbk) => {
98
+ return new Promise((resolve, reject) => {
99
+ return asyncAuto({
100
+ // Check arguments
101
+ validate: cbk => {
102
+ if (!!limit && !!token) {
103
+ return cbk([400, 'ExpectedNoLimitWhenPagingPayFailuresWithToken']);
104
+ }
105
+
106
+ if (!isLnd({lnd, method, type})) {
107
+ return cbk([400, 'ExpectedLndForGetFailedPaymentsRequest']);
108
+ }
109
+
110
+ return cbk();
111
+ },
112
+
113
+ // Get all payments
114
+ listPayments: ['validate', ({}, cbk) => {
115
+ let offset;
116
+ let resultsLimit = limit || defaultLimit;
117
+
118
+ if (!!token) {
119
+ try {
120
+ const pagingToken = parse(token);
121
+
122
+ offset = pagingToken.offset;
123
+ resultsLimit = pagingToken.limit;
124
+ } catch (err) {
125
+ return cbk([400, 'ExpectedValidPagingTokenForGetFailed', {err}]);
126
+ }
127
+ }
128
+
129
+ return lnd[type][method]({
130
+ include_incomplete: true,
131
+ index_offset: offset || Number(),
132
+ max_payments: resultsLimit,
133
+ reversed: true,
134
+ },
135
+ (err, res) => {
136
+ if (!!err) {
137
+ return cbk([503, 'UnexpectedGetFailedPaymentsError', {err}]);
138
+ }
139
+
140
+ if (!res || !isArray(res.payments)) {
141
+ return cbk([503, 'ExpectedFailedPaymentsInListPaymentsResponse']);
142
+ }
143
+
144
+ if (typeof res.last_index_offset !== 'string') {
145
+ return cbk([503, 'ExpectedLastIndexOffsetWhenRequestingFailed']);
146
+ }
147
+
148
+ const lastOffset = Number(res.last_index_offset);
149
+ const offset = Number(res.first_index_offset);
150
+
151
+ const token = stringify({offset, limit: resultsLimit});
152
+
153
+ return cbk(null, {
154
+ payments: res.payments.filter(isFailed),
155
+ token: offset === lastPageFirstIndexOffset ? undefined : token,
156
+ });
157
+ });
158
+ }],
159
+
160
+ // Check and map payments
161
+ foundPayments: ['listPayments', ({listPayments}, cbk) => {
162
+ try {
163
+ const payments = listPayments.payments.map(rpcPaymentAsPayment);
164
+
165
+ return cbk(null, payments);
166
+ } catch (err) {
167
+ return cbk([503, err.message]);
168
+ }
169
+ }],
170
+
171
+ // Final found failed payments
172
+ payments: [
173
+ 'foundPayments',
174
+ 'listPayments',
175
+ ({foundPayments, listPayments}, cbk) =>
176
+ {
177
+ const payments = sortBy({
178
+ array: foundPayments,
179
+ attribute: 'created_at',
180
+ });
181
+
182
+ return cbk(null, {
183
+ next: listPayments.token || undefined,
184
+ payments: payments.sorted.reverse(),
185
+ });
186
+ }],
187
+ },
188
+ returnResult({reject, resolve, of: 'payments'}, cbk));
189
+ });
190
+ };
@@ -131,8 +131,6 @@ module.exports = ({limit, lnd, token}, cbk) => {
131
131
  }
132
132
  }
133
133
 
134
- const start = Date.now();
135
-
136
134
  return lnd[type][method]({
137
135
  include_incomplete: false,
138
136
  index_offset: offset || Number(),
@@ -187,8 +185,8 @@ module.exports = ({limit, lnd, token}, cbk) => {
187
185
  });
188
186
 
189
187
  return cbk(null, {
190
- payments: payments.sorted.reverse(),
191
188
  next: !!foundPayments.length ? listPayments.token : undefined,
189
+ payments: payments.sorted.reverse(),
192
190
  });
193
191
  }],
194
192
  },
@@ -14,6 +14,7 @@ const getChannelBalance = require('./get_channel_balance');
14
14
  const getChannels = require('./get_channels');
15
15
  const getClosedChannels = require('./get_closed_channels');
16
16
  const getConnectedWatchtowers = require('./get_connected_watchtowers');
17
+ const getFailedPayments = require('./get_failed_payments');
17
18
  const getFeeRates = require('./get_fee_rates');
18
19
  const getForwardingConfidence = require('./get_forwarding_confidence');
19
20
  const getForwardingReputations = require('./get_forwarding_reputations');
@@ -65,6 +66,7 @@ module.exports = {
65
66
  getChannels,
66
67
  getClosedChannels,
67
68
  getConnectedWatchtowers,
69
+ getFailedPayments,
68
70
  getFeeRates,
69
71
  getForwardingConfidence,
70
72
  getForwardingReputations,
@@ -10,6 +10,11 @@ const isHex = n => !(n.length % 2) && /^[0-9A-F]*$/i.test(n);
10
10
 
11
11
  /** Probe to find a successful route
12
12
 
13
+ When probing to a payment request, make sure to specify the fields encoded in
14
+ the payment request such as `cltv_delta`.
15
+
16
+ If `total_mtokens` are specified, a `payment` nonce is required.
17
+
13
18
  Requires `offchain:write` permission
14
19
 
15
20
  {
@@ -3,6 +3,7 @@ const {featureFlagDetails} = require('bolt09');
3
3
  const htlcAsPayment = require('./htlc_as_payment');
4
4
 
5
5
  const dateFrom = epoch => new Date(1e3 * epoch).toISOString();
6
+ const emptyHash = Buffer.alloc(32).toString('hex');
6
7
  const {isArray} = Array;
7
8
  const {keys} = Object;
8
9
  const msPerSec = 1e3;
@@ -178,12 +179,13 @@ module.exports = args => {
178
179
  const createdAtEpochTime = Number(args.creation_date);
179
180
  const descHash = args.description_hash;
180
181
  const expiresInMs = Number(args.expiry) * msPerSec;
181
- const isAmpPush = !args.request && !!args.is_amp;
182
+ const isAmpPush = !args.payment_request && !!args.is_amp;
182
183
  const mtok = (BigInt(args.value) * mtokensPerToken).toString();
183
184
  const payment = args.payment_addr.toString('hex');
184
185
  const settleDate = args.settle_date;
185
186
 
186
187
  const createdAtMs = createdAtEpochTime * msPerSec;
188
+ const hasPaymentId = !!payment && payment !== emptyHash;
187
189
 
188
190
  return {
189
191
  chain_address: args.fallback_addr || undefined,
@@ -208,7 +210,7 @@ module.exports = args => {
208
210
  is_private: args.private,
209
211
  is_push: isAmpPush || args.is_keysend || undefined,
210
212
  mtokens: args.value_msat === '0' ? mtok : args.value_msat,
211
- payment: payment || undefined,
213
+ payment: hasPaymentId ? payment : undefined,
212
214
  payments: args.htlcs.map(htlcAsPayment),
213
215
  received: Number(args.amt_paid_sat),
214
216
  received_mtokens: args.amt_paid_msat,