paid-services 3.5.1 → 3.6.0

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
+ ## Version 3.6.0
4
+
5
+ - `manageTrades`: Add support for persistent open trades
6
+ - `manageTrades`: Add expiration dates to open trades
7
+ - `manageTrades`: Support longer-lived open trade scenarios
8
+ - `manageTrades`: Show final encoded trade when requesting open trade
9
+ - `manageTrades`: Check for RPC signer support before trading
10
+
3
11
  ## Version 3.5.1
4
12
 
5
13
  - `changeChannelCapacity`: Allow changing private/public status of channel
@@ -266,7 +266,6 @@ module.exports = (args, cbk) => {
266
266
  give_tokens: pendingChannel.remote_balance,
267
267
  is_private: args.is_private,
268
268
  partner_public_key: pendingChannel.partner_public_key,
269
- is_private: args.is_private,
270
269
  }],
271
270
  is_avoiding_broadcast: true,
272
271
  lnd: args.lnd,
@@ -47,7 +47,6 @@ const rebroadcastTimeMs = 1000 * 5;
47
47
  const recordsAsObject = a => a.reduce((sum, n) => sum[n.type] = n.value, {});
48
48
  const requestCapacityChangeIntervalMs = 2000;
49
49
  const requestCapacityChangeTimeoutMs = 1000 * 60 * 60 * 6;
50
- const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
51
50
  const testMessage = '00';
52
51
  const tokensAsBigUnit = tokens => (tokens / 1e8).toFixed(8);
53
52
  const uniq = arr => Array.from(new Set(arr));
package/package.json CHANGED
@@ -7,20 +7,20 @@
7
7
  "url": "https://github.com/alexbosworth/paid-services/issues"
8
8
  },
9
9
  "dependencies": {
10
- "async": "3.2.2",
11
- "asyncjs-util": "1.2.7",
10
+ "async": "3.2.3",
11
+ "asyncjs-util": "1.2.8",
12
12
  "bolt01": "1.2.3",
13
13
  "bolt07": "1.8.0",
14
- "invoices": "2.0.2",
15
- "ln-service": "53.2.0",
14
+ "invoices": "2.0.3",
15
+ "ln-service": "53.4.0",
16
16
  "ln-sync": "3.6.1"
17
17
  },
18
18
  "description": "Lightning Paid Services library",
19
19
  "devDependencies": {
20
20
  "@alexbosworth/tap": "15.0.10",
21
- "ln-docker-daemons": "2.1.0",
21
+ "ln-docker-daemons": "2.2.0",
22
22
  "mock-lnd": "1.4.1",
23
- "secp256k1": "4.0.2"
23
+ "secp256k1": "4.0.3"
24
24
  },
25
25
  "engines": {
26
26
  "node": ">=12.20"
@@ -40,5 +40,5 @@
40
40
  "integration-tests": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 120 test/integration/*.js",
41
41
  "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/actions/*.js test/capacity/*.js test/client/*.js test/config/*.js test/p2p/*.js test/records/*.js test/respond/*.js test/server/*.js test/server/*.js test/services/*.js test/trades/*.js"
42
42
  },
43
- "version": "3.5.1"
43
+ "version": "3.6.0"
44
44
  }
@@ -18,7 +18,6 @@ const defaultProbeTimeoutMs = 1000 * 60 * 5;
18
18
  const expiresAt = () => new Date(Date.now() + 1000 * 60 * 30).toISOString();
19
19
  const interval = 1000 * 60 * 10;
20
20
  const rateDivisor = BigInt(1e6);
21
- const sumOf = arr => arr.reduce((sum, n) => BigInt(n) + n, BigInt(0));
22
21
  const text = 'Relay payment request created';
23
22
  const times = 12;
24
23
 
@@ -32,7 +32,6 @@ const maturity = 100;
32
32
  const proposeCapacityChange = propose;
33
33
  const signCapacityReplacement = method;
34
34
  const size = 2;
35
- const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
36
35
  const times = 2000;
37
36
  const {toOutputScript} = address;
38
37
  const weightAsVBytes = n => Math.ceil(n / 4);
@@ -27,7 +27,6 @@ const log = () => {};
27
27
  const maturity = 100;
28
28
  const size = 2;
29
29
  const slow = 5000;
30
- const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
31
30
  const times = 2000;
32
31
  const {toOutputScript} = address;
33
32
  const weightAsVBytes = n => Math.ceil(n / 4);
@@ -27,7 +27,6 @@ const logger = {error: () => {}, info: () => {}};
27
27
  const maturity = 100;
28
28
  const size = 2;
29
29
  const slow = 3000;
30
- const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
31
30
  const times = 2000;
32
31
  const {toOutputScript} = address;
33
32
  const weightAsVBytes = n => Math.ceil(n / 4);
@@ -26,7 +26,6 @@ const interval = 10;
26
26
  const logger = {error: () => {}, info: () => {}};
27
27
  const maturity = 100;
28
28
  const size = 2;
29
- const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
30
29
  const times = 2000;
31
30
  const {toOutputScript} = address;
32
31
  const weightAsVBytes = n => Math.ceil(n / 4);
@@ -1,6 +1,7 @@
1
1
  const asyncAuto = require('async/auto');
2
2
  const asyncEach = require('async/each');
3
3
  const {cancelHodlInvoice} = require('ln-service');
4
+ const {getInvoice} = require('ln-service');
4
5
  const {returnResult} = require('asyncjs-util');
5
6
  const {settleHodlInvoice} = require('ln-service');
6
7
 
@@ -10,13 +11,14 @@ const {isArray} = Array;
10
11
 
11
12
  {
12
13
  cancel: [<Alternative Invoice Id Hex String>]
14
+ id: <Trade Id Hex String>
13
15
  lnd: <Authenticated LND API Object>
14
16
  secret: <Invoice to Settle Preimage Hex String>
15
17
  }
16
18
 
17
19
  @returns via cbk or Promise
18
20
  */
19
- module.exports = ({cancel, lnd, secret}, cbk) => {
21
+ module.exports = ({cancel, id, lnd, secret}, cbk) => {
20
22
  return new Promise((resolve, reject) => {
21
23
  return asyncAuto({
22
24
  // Check arguments
@@ -25,6 +27,10 @@ module.exports = ({cancel, lnd, secret}, cbk) => {
25
27
  return cbk([400, 'ExpectedArrayOfIdsToCancelToAcceptTrade']);
26
28
  }
27
29
 
30
+ if (!id) {
31
+ return cbk([400, 'ExpectedAnchorTradeIdToAcceptTrade']);
32
+ }
33
+
28
34
  if (!lnd) {
29
35
  return cbk([400, 'ExpectedAuthenticatedLndToAcceptTrade']);
30
36
  }
@@ -36,9 +42,12 @@ module.exports = ({cancel, lnd, secret}, cbk) => {
36
42
  return cbk();
37
43
  },
38
44
 
45
+ // Fetch the anchor invoice to make sure it's still open
46
+ getAnchor: ['validate', ({}, cbk) => getInvoice({id, lnd}, cbk)],
47
+
39
48
  // Cancel alternative invoices so that only one resolves as settled
40
- cancel: ['validate', ({}, cbk) => {
41
- return asyncEach(cancel, (id, cbk) => {
49
+ cancel: ['getAnchor', ({}, cbk) => {
50
+ return asyncEach([].concat(cancel).concat(id), (id, cbk) => {
42
51
  return cancelHodlInvoice({id, lnd}, cbk);
43
52
  },
44
53
  cbk);
@@ -1,21 +1,23 @@
1
1
  const asyncAuto = require('async/auto');
2
+ const {createInvoice} = require('ln-service');
2
3
  const {getChannels} = require('ln-service');
3
4
  const {getNetwork} = require('ln-sync');
4
- const {getNodeAlias} = require('ln-sync');
5
5
  const {getWalletInfo} = require('ln-service');
6
6
  const {returnResult} = require('asyncjs-util');
7
7
 
8
- const encodeOpenTrade = require('./encode_open_trade');
8
+ const encodeAnchoredTrade = require('./encode_anchored_trade');
9
9
  const finalizeTradeSecret = require('./finalize_trade_secret');
10
- const serviceTradeRequests = require('./service_trade_requests');
10
+ const serviceOpenTrade = require('./service_open_trade');
11
11
 
12
12
  const asNumber = n => parseFloat(n, 10);
13
+ const daysAsMs = days => Number(days) * 1000 * 60 * 60 * 24;
14
+ const defaultExpirationDays = 14;
13
15
  const {floor} = Math;
16
+ const futureDate = ms => new Date(Date.now() + ms).toISOString();
14
17
  const isNumber = n => !isNaN(n);
15
18
  const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
16
19
  const maxDescriptionLength = 100;
17
20
  const maxSecretLength = 100;
18
- const nodeName = (alias, id) => `${alias} ${id}`;
19
21
  const uriAsSocket = n => n.substring(67);
20
22
  const utf8AsHex = utf8 => Buffer.from(utf8).toString('hex');
21
23
 
@@ -164,20 +166,76 @@ module.exports = ({ask, lnd, logger}, cbk) => {
164
166
  cbk);
165
167
  }],
166
168
 
169
+ // Ask for the expiration of the trade
170
+ askForExpiration: ['askForPrice', ({}, cbk) => {
171
+ return ask({
172
+ default: defaultExpirationDays,
173
+ name: 'days',
174
+ message: 'Days to offer this for?',
175
+ validate: input => {
176
+ if (!isNumber(input) || !Number(input)) {
177
+ return false;
178
+ }
179
+
180
+ return true;
181
+ },
182
+ },
183
+ cbk);
184
+ }],
185
+
186
+ // Create an anchor invoice for the trade
187
+ createAnchor: [
188
+ 'askForDescription',
189
+ 'askForExpiration',
190
+ 'askForNodeId',
191
+ 'askForPrice',
192
+ 'askForSecret',
193
+ ({
194
+ askForDescription,
195
+ askForExpiration,
196
+ askForNodeId,
197
+ askForPrice,
198
+ askForSecret,
199
+ },
200
+ cbk) =>
201
+ {
202
+ // Exit early if this is a closed trade
203
+ if (!!askForNodeId.id) {
204
+ return cbk();
205
+ }
206
+
207
+ const {encoded} = encodeAnchoredTrade({
208
+ description: askForDescription.description,
209
+ secret: askForSecret.secret,
210
+ });
211
+
212
+ return createInvoice({
213
+ lnd,
214
+ description: encoded,
215
+ expires_at: futureDate(daysAsMs(askForExpiration.days)),
216
+ tokens: asNumber(askForPrice.tokens),
217
+ },
218
+ cbk);
219
+ }],
220
+
167
221
  // Wait for a peer to connect and ask for the trade details
168
222
  serviceTradeRequests: [
169
223
  'askForDescription',
224
+ 'askForExpiration',
170
225
  'askForNodeId',
171
226
  'askForPrice',
172
227
  'askForSecret',
228
+ 'createAnchor',
173
229
  'getChannels',
174
230
  'getIdentity',
175
231
  'getNetwork',
176
232
  ({
177
233
  askForDescription,
234
+ askForExpiration,
178
235
  askForNodeId,
179
236
  askForPrice,
180
237
  askForSecret,
238
+ createAnchor,
181
239
  getChannels,
182
240
  getIdentity,
183
241
  getNetwork,
@@ -189,64 +247,37 @@ module.exports = ({ask, lnd, logger}, cbk) => {
189
247
  return cbk();
190
248
  }
191
249
 
192
- // Encode the open trade details to give out
193
- const openTrade = encodeOpenTrade({
194
- network: getNetwork.network,
195
- nodes: [{
196
- channels: getChannels.channels,
197
- id: getIdentity.public_key,
198
- sockets: (getIdentity.uris || []).map(uriAsSocket),
199
- }],
200
- });
201
-
202
- const settled = [];
203
-
204
- const sub = serviceTradeRequests({
250
+ return serviceOpenTrade({
205
251
  lnd,
252
+ logger,
253
+ channels: getChannels.channels,
206
254
  description: askForDescription.description,
255
+ expires_at: futureDate(daysAsMs(askForExpiration.days)),
256
+ id: createAnchor.id,
257
+ network: getNetwork.network,
258
+ public_key: getIdentity.public_key,
207
259
  secret: askForSecret.secret,
208
260
  tokens: asNumber(askForPrice.tokens),
209
- });
210
-
211
- sub.on('details', async ({to}) => {
212
- const {alias, id} = await getNodeAlias({lnd, id: to});
213
-
214
- return logger.info({return_trade_info_to: nodeName(alias, id)});
215
- });
216
-
217
- sub.once('end', async () => {
218
- const [to] = settled;
219
-
220
- if (!!to) {
221
- const {alias, id} = await getNodeAlias({lnd, id: to});
222
-
223
- return logger.info({finished_trade_with: nodeName(alias, id)});
224
- }
225
-
226
- return cbk();
227
- });
228
-
229
- sub.on('failure', failure => logger.error({failure}));
230
-
231
- sub.on('settled', ({to}) => settled.push(to));
232
-
233
- sub.on('trade', async ({to}) => {
234
- const {alias, id} = await getNodeAlias({lnd, id: to});
235
-
236
- return logger.info({make_trade_invoice_for: nodeName(alias, id)});
237
- });
238
-
239
- // Show the trade details blob
240
- return logger.info({waiting_for_trade_request_to: openTrade.trade});
261
+ uris: (getIdentity.uris || []),
262
+ },
263
+ cbk);
241
264
  }],
242
265
 
243
266
  // Finalize the trade with an encrypted secret and an invoice
244
267
  finalize: [
245
268
  'askForDescription',
269
+ 'askForExpiration',
246
270
  'askForNodeId',
247
271
  'askForPrice',
248
272
  'askForSecret',
249
- ({askForDescription, askForNodeId, askForPrice, askForSecret}, cbk) =>
273
+ ({
274
+ askForDescription,
275
+ askForExpiration,
276
+ askForNodeId,
277
+ askForPrice,
278
+ askForSecret,
279
+ },
280
+ cbk) =>
250
281
  {
251
282
  // Exit early when there is no node to finalize the trade to
252
283
  if (!askForNodeId.id) {
@@ -256,6 +287,7 @@ module.exports = ({ask, lnd, logger}, cbk) => {
256
287
  return finalizeTradeSecret({
257
288
  lnd,
258
289
  description: askForDescription.description,
290
+ expires_at: futureDate(daysAsMs(askForExpiration.days)),
259
291
  secret: askForSecret.secret,
260
292
  to: askForNodeId.id,
261
293
  tokens: asNumber(askForPrice.tokens),
@@ -0,0 +1,56 @@
1
+ const {decodeTlvStream} = require('bolt01');
2
+
3
+ const anchorPrefix = 'anchor-trade-secret:';
4
+ const base64AsHex = base64 => Buffer.from(base64, 'base64').toString('hex');
5
+ const findDescription = records => records.find(n => n.type === '1');
6
+ const findSecret = records => records.find(n => n.type === '0');
7
+ const hexAsUtf = n => !n ? '' : Buffer.from(n.value, 'hex').toString('utf8');
8
+
9
+ /** Decode an anchored trade data blob
10
+
11
+ Data blobs look like anchored-trade-secret:<base64-encoded-data>
12
+
13
+ Inside the base64 is encoded TLV data:
14
+ 0: <Trade Secret>
15
+ [1]: <Trade Description>
16
+
17
+ {
18
+ encoded: <Encoded Trade Data String>
19
+ }
20
+
21
+ @returns
22
+ {
23
+ [trade]: {
24
+ description: <Description of Trade String>
25
+ secret: <Trade Secret String>
26
+ }
27
+ }
28
+ */
29
+ module.exports = ({encoded}) => {
30
+ if (!encoded.startsWith(anchorPrefix)) {
31
+ return {};
32
+ }
33
+
34
+ const data = encoded.slice(anchorPrefix.length);
35
+
36
+ try {
37
+ decodeTlvStream({encoded: base64AsHex(data)});
38
+ } catch (err) {
39
+ return {};
40
+ }
41
+
42
+ const {records} = decodeTlvStream({encoded: base64AsHex(data)});
43
+
44
+ const secret = findSecret(records);
45
+
46
+ if (!secret.value) {
47
+ return {};
48
+ }
49
+
50
+ return {
51
+ trade: {
52
+ description: hexAsUtf(findDescription(records)),
53
+ secret: hexAsUtf(secret),
54
+ },
55
+ };
56
+ };
@@ -0,0 +1,35 @@
1
+ const {encodeTlvStream} = require('bolt01');
2
+
3
+ const anchorPrefix = 'anchor-trade-secret:';
4
+ const descriptionRecord = value => !!value ? ({value, type: '1'}) : undefined;
5
+ const secretRecord = value => ({value, type: '0'});
6
+ const utf8AsHex = utf8 => Buffer.from(utf8, 'utf8').toString('hex');
7
+ const withPrefix = (pre, v) => pre + Buffer.from(v, 'hex').toString('base64');
8
+
9
+ /** Encode a trade into an anchor blob
10
+
11
+ This will look like anchored-trade-secret:<base64-encoded-data>
12
+
13
+ The data encodes a TLV:
14
+ 0: <Trade Secret>
15
+ [1]: <Trade Description>
16
+
17
+ {
18
+ [description]: <Open Trade Description String>
19
+ secret: <Open Trade Secret String>
20
+ }
21
+
22
+ @returns
23
+ {
24
+ encoded: <Encoded Anchor Trade Data Blob String>
25
+ }
26
+ */
27
+ module.exports = ({description, secret}) => {
28
+ const records = [secretRecord(secret), descriptionRecord(description)]
29
+ .filter(n => !!n)
30
+ .map(({type, value}) => ({type, value: utf8AsHex(value)}));
31
+
32
+ const {encoded} = encodeTlvStream({records});
33
+
34
+ return {encoded: withPrefix(anchorPrefix, encoded)};
35
+ };
@@ -17,6 +17,7 @@ const utf8AsHex = utf8 => Buffer.from(utf8, 'utf8').toString('hex');
17
17
 
18
18
  {
19
19
  description: <Trade Description String>
20
+ expires_at: <Trade Expires At ISO 8601 Date String>
20
21
  is_hold: <Create Hold Invoice Bool>
21
22
  lnd: <Authenticated LND API Object>
22
23
  secret: <Clear Secret String>
@@ -40,6 +41,10 @@ module.exports = (args, cbk) => {
40
41
  return cbk([400, 'ExpectedDescriptionToFinalizeTradeSecret']);
41
42
  }
42
43
 
44
+ if (!args.expires_at) {
45
+ return cbk([400, 'ExpectedExpiresAtDateToFinalizeTradeSecret']);
46
+ }
47
+
43
48
  if (!args.lnd) {
44
49
  return cbk([400, 'ExpectedAuthenticatedLndToFinalizeTradeSecret']);
45
50
  }
@@ -75,6 +80,7 @@ module.exports = (args, cbk) => {
75
80
  if (!!args.is_hold) {
76
81
  return createHodlInvoice({
77
82
  description: args.description,
83
+ expires_at: args.expires_at,
78
84
  id: bufferAsHex(sha256(hexAsBuffer(encrypt.payment_secret))),
79
85
  lnd: args.lnd,
80
86
  secret: encrypt.payment_secret,
@@ -85,6 +91,7 @@ module.exports = (args, cbk) => {
85
91
 
86
92
  return createInvoice({
87
93
  description: args.description,
94
+ expires_at: args.expires_at,
88
95
  lnd: args.lnd,
89
96
  secret: encrypt.payment_secret,
90
97
  tokens: args.tokens,
@@ -24,6 +24,8 @@ const makeRequestId = () => randomBytes(32).toString('hex');
24
24
  const requestTradeType = '805005';
25
25
  const requestTradesTimeoutMs = 1000 * 30;
26
26
  const tradesRequestIdType = '1';
27
+ const uniq = arr => Array.from(new Set(arr));
28
+ const uniqBy = (a,b) => a.filter((e,i) => a.findIndex(n => n[b] == e[b]) == i);
27
29
  const waitForTradesMs = 1000 * 5;
28
30
 
29
31
  /** Find a trade to purchase
@@ -49,6 +51,7 @@ const waitForTradesMs = 1000 * 5;
49
51
  auth: <Encrypted Payload Auth Hex String>
50
52
  payload: <Preimage Encrypted Payload Hex String>
51
53
  request: <BOLT 11 Payment Request String>
54
+ trade: <Encoded Trade String>
52
55
  }
53
56
  */
54
57
  module.exports = ({ask, id, identity, lnd, logger, nodes}, cbk) => {
@@ -89,7 +92,23 @@ module.exports = ({ask, id, identity, lnd, logger, nodes}, cbk) => {
89
92
 
90
93
  // Exit early when the node id and sockets is specified directly
91
94
  if (!!connect.node) {
92
- return cbk(null, connect.node);
95
+ return getNode({
96
+ lnd,
97
+ is_omitting_channels: true,
98
+ public_key: connect.node.id,
99
+ },
100
+ (err, res) => {
101
+ if (!!err) {
102
+ return cbk(null, connect.node);
103
+ }
104
+
105
+ const sockets = connect.node.sockets.map(n => n.socket);
106
+
107
+ return cbk(null, {
108
+ id: connect.node.id,
109
+ sockets: uniq([].concat(sockets).concat(connect.node.sockets)),
110
+ });
111
+ });
93
112
  }
94
113
 
95
114
  // Exit early when there is no landmark channel
@@ -156,6 +175,8 @@ module.exports = ({ask, id, identity, lnd, logger, nodes}, cbk) => {
156
175
 
157
176
  // Try and connect to a referenced node
158
177
  return asyncDetect(getNodes.filter(n => !!n), (node, cbk) => {
178
+ logger.info({connecting_to: node.id});
179
+
159
180
  // Attempt referenced sockets to establish p2p connection
160
181
  return asyncDetectSeries(node.sockets, (socket, cbk) => {
161
182
  return addPeer({lnd, socket, public_key: node.id}, err => {
@@ -238,6 +259,8 @@ module.exports = ({ask, id, identity, lnd, logger, nodes}, cbk) => {
238
259
  return res.success({});
239
260
  });
240
261
 
262
+ logger.info({requesting_trade_details: true});
263
+
241
264
  // Once connected, ask for the trade details
242
265
  return makePeerRequest({
243
266
  lnd,
@@ -270,7 +293,9 @@ module.exports = ({ask, id, identity, lnd, logger, nodes}, cbk) => {
270
293
  return cbk([404, 'NoTradesFound']);
271
294
  }
272
295
 
273
- const [trade, other] = requestTrades.trades;
296
+ const uniqueTrades = uniqBy(requestTrades.trades, 'description');
297
+
298
+ const [trade, other] = uniqueTrades;
274
299
 
275
300
  // Exit early when there is only a single trade
276
301
  if (!other) {
@@ -279,7 +304,7 @@ module.exports = ({ask, id, identity, lnd, logger, nodes}, cbk) => {
279
304
 
280
305
  // Present the possible choices
281
306
  return ask({
282
- choices: requestTrades.trades.map(trade => ({
307
+ choices: uniqueTrades.map(trade => ({
283
308
  name: trade.description,
284
309
  value: trade.id,
285
310
  })),
@@ -16,6 +16,7 @@ const findTrade = require('./find_trade');
16
16
  const hexAsUtf8 = hex => Buffer.from(hex, 'hex').toString();
17
17
  const {isArray} = Array;
18
18
  const isHexPreimage = n => !!n && /^[0-9A-F]{64}$/i.test(n);
19
+ const tokensAsBigUnit = tokens => (tokens / 1e8).toFixed(8);
19
20
 
20
21
  /** Manage an individual trade
21
22
 
@@ -141,7 +142,7 @@ module.exports = ({ask, lnd, logger}, cbk) => {
141
142
  logger.info({
142
143
  id: requestDetails.id,
143
144
  offering: requestDetails.description,
144
- price: requestDetails.tokens,
145
+ price: tokensAsBigUnit(requestDetails.tokens),
145
146
  is_canceled: invoice.is_canceled || undefined,
146
147
  is_purchased: invoice.is_confirmed || undefined,
147
148
  });
@@ -169,7 +170,7 @@ module.exports = ({ask, lnd, logger}, cbk) => {
169
170
  logger.info({
170
171
  id: requestDetails.id,
171
172
  offering: requestDetails.description,
172
- price: requestDetails.tokens,
173
+ price: tokensAsBigUnit(requestDetails.tokens),
173
174
  });
174
175
 
175
176
  return ask({
@@ -195,7 +196,15 @@ module.exports = ({ask, lnd, logger}, cbk) => {
195
196
  return cbk();
196
197
  }
197
198
 
198
- return cancelHodlInvoice({lnd, id: requestDetails.id}, cbk);
199
+ return cancelHodlInvoice({lnd, id: requestDetails.id}, err => {
200
+ if (!!err) {
201
+ return cbk(err);
202
+ }
203
+
204
+ logger.info({trade_canceled: true});
205
+
206
+ return cbk();
207
+ });
199
208
  }],
200
209
 
201
210
  // Select a purchase option for the trade
@@ -216,10 +225,12 @@ module.exports = ({ask, lnd, logger}, cbk) => {
216
225
  return cbk();
217
226
  }
218
227
 
228
+ logger.info({trade: findTrade.trade});
229
+
219
230
  logger.info({
220
231
  request: findTrade.request,
221
232
  purchase: requestDetails.description,
222
- price: requestDetails.tokens,
233
+ price: tokensAsBigUnit(requestDetails.tokens),
223
234
  to: requestDetails.destination,
224
235
  });
225
236