balanceofsatoshis 25.6.2 → 25.7.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 +11 -0
- package/README.md +3 -0
- package/balances/accounting_categories.json +1 -0
- package/balances/chain_price_range.js +30 -0
- package/balances/chain_prices_logger.js +78 -0
- package/balances/constants.json +7 -0
- package/balances/get_accounting_report.js +88 -57
- package/balances/range_for_date.js +10 -18
- package/bos +7 -4
- package/chain/fund_transaction.js +173 -41
- package/commands/constants.json +1 -0
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 25.7.0
|
|
4
|
+
|
|
5
|
+
- `accounting`: Add `lightning-fees` category to report the routing fees paid
|
|
6
|
+
on payments
|
|
7
|
+
- `accounting`: Keep the payments of recent blocks in `chain_blocks.json`, so
|
|
8
|
+
that later runs only fetch the blocks that are new
|
|
9
|
+
- `accounting`: Pass `--node` more than once to get one report of several
|
|
10
|
+
nodes, with their records together in order of date
|
|
11
|
+
- `fund`: Add `--fee-rate-precise` to set a fee rate per 1000 vbytes
|
|
12
|
+
- `fund`: `MAX` amounts send as much as possible without overpaying the fee
|
|
13
|
+
|
|
3
14
|
## 25.6.2
|
|
4
15
|
|
|
5
16
|
- `telegram`: Fix invoices settled before the bot started being reported as new
|
package/README.md
CHANGED
|
@@ -305,6 +305,9 @@ You can also add a JSON file to your .bos directory: config.json, add
|
|
|
305
305
|
|
|
306
306
|
`bos` keeps its files in `~/.bos`, or in `BOS_DATA_PATH` when that is set:
|
|
307
307
|
|
|
308
|
+
- `chain_blocks.json`: the payments in the last few days of blocks that
|
|
309
|
+
`bos accounting` derives prices from, kept so that later runs only fetch
|
|
310
|
+
the blocks that are new. The file can be deleted
|
|
308
311
|
- `chain_prices.json`: USD prices of BTC that `bos accounting` derived from
|
|
309
312
|
chain data, kept so that a day is only derived once. Prices are derived
|
|
310
313
|
this way when no `--rate-provider` is given, and the prices of the days
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const {isFinite} = Number;
|
|
2
|
+
const {round} = Math;
|
|
3
|
+
const {sqrt} = Math;
|
|
4
|
+
|
|
5
|
+
/** Get the range of prices that the round dollar payments of a day point to
|
|
6
|
+
|
|
7
|
+
{
|
|
8
|
+
[dispersion]: <Peak Width Beyond Smoothing As Price Fraction Number>
|
|
9
|
+
price: <Price String>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@returns
|
|
13
|
+
{
|
|
14
|
+
[range]: <Low Price to High Price String>
|
|
15
|
+
}
|
|
16
|
+
*/
|
|
17
|
+
module.exports = ({dispersion, price}) => {
|
|
18
|
+
// Exit early when there is no width to describe
|
|
19
|
+
if (!isFinite(dispersion)) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// The peak reaches the square root of the dispersion factor either side
|
|
24
|
+
const reach = sqrt(1 + dispersion);
|
|
25
|
+
|
|
26
|
+
const high = round(Number(price) * reach);
|
|
27
|
+
const low = round(Number(price) / reach);
|
|
28
|
+
|
|
29
|
+
return {range: `${low} to ${high}`};
|
|
30
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const chainPriceRange = require('./chain_price_range');
|
|
2
|
+
const {chainPriceFlagNotes} = require('./constants');
|
|
3
|
+
|
|
4
|
+
const {floor} = Math;
|
|
5
|
+
const {keys} = Object;
|
|
6
|
+
const msPerDay = 1000 * 60 * 60 * 24;
|
|
7
|
+
|
|
8
|
+
/** Make a logger that describes deriving prices from chain data
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
[logger]: <Winston Logger Object>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@returns
|
|
15
|
+
{
|
|
16
|
+
[logger]: {
|
|
17
|
+
error: <Error Function>
|
|
18
|
+
info: <Info Function>
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
*/
|
|
22
|
+
module.exports = ({logger}) => {
|
|
23
|
+
// Exit early when there is no logger to describe progress with
|
|
24
|
+
if (!logger) {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
logger: {
|
|
30
|
+
error: err => logger.error(err),
|
|
31
|
+
info: line => {
|
|
32
|
+
// Exit early describing fetching blocks as calculating prices
|
|
33
|
+
if (!!line && !!line.fetching_blocks) {
|
|
34
|
+
const {blocks, date} = line.fetching_blocks;
|
|
35
|
+
|
|
36
|
+
const days = floor((Date.now() - Date.parse(date)) / msPerDay);
|
|
37
|
+
|
|
38
|
+
const isToday = days === 0;
|
|
39
|
+
|
|
40
|
+
return logger.info({
|
|
41
|
+
calculating_prices_from_blocks: {
|
|
42
|
+
blocks,
|
|
43
|
+
day: date,
|
|
44
|
+
days_out_of_date: !isNaN(days) && !isToday ? days : undefined,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Exit early when the line is not a price derived from chain data
|
|
50
|
+
if (!line || !line.chain_price || !line.chain_price.price) {
|
|
51
|
+
return logger.info(line);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const price = line.chain_price;
|
|
55
|
+
|
|
56
|
+
const flags = keys(chainPriceFlagNotes).filter(flag => !!price[flag]);
|
|
57
|
+
|
|
58
|
+
const note = flags.map(flag => chainPriceFlagNotes[flag]).join(', ');
|
|
59
|
+
|
|
60
|
+
// Only a price from a full day of blocks has the measures behind it
|
|
61
|
+
const isFullDay = price.outputs !== undefined;
|
|
62
|
+
|
|
63
|
+
// Keys are in reading order, the price first and then its evidence
|
|
64
|
+
return logger.info({
|
|
65
|
+
chain_price: {
|
|
66
|
+
date: price.date,
|
|
67
|
+
price: price.price,
|
|
68
|
+
price_range: isFullDay ? chainPriceRange(price).range : undefined,
|
|
69
|
+
blocks: isFullDay ? price.blocks : undefined,
|
|
70
|
+
payment_outputs: isFullDay ? price.outputs : undefined,
|
|
71
|
+
round_dollar_payments: isFullDay ? price.payments : undefined,
|
|
72
|
+
note: note || undefined,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
};
|
package/balances/constants.json
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
+
"chainBlocksFile": "chain_blocks.json",
|
|
3
|
+
"chainPriceFlagNotes": {
|
|
4
|
+
"is_provisional": "provisional, the day is not over yet",
|
|
5
|
+
"is_sample": "rough estimate from a sample of the blocks",
|
|
6
|
+
"is_unsettled": "the estimate was still shifting when refining stopped",
|
|
7
|
+
"is_widened": "found outside the expected price range"
|
|
8
|
+
},
|
|
2
9
|
"chainPricesFile": "chain_prices.json",
|
|
3
10
|
"coinbaseProvider": "coinbase",
|
|
4
11
|
"defaultCurrency": "BTC",
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
const asyncAuto = require('async/auto');
|
|
2
|
+
const asyncify = require('async/asyncify');
|
|
3
|
+
const asyncMap = require('async/map');
|
|
4
|
+
const asyncMapSeries = require('async/mapSeries');
|
|
2
5
|
const {chainRates} = require('@alexbosworth/fiat');
|
|
3
6
|
const {getAccountingReport} = require('ln-accounting');
|
|
4
7
|
const {getNetwork} = require('ln-sync');
|
|
8
|
+
const {harmonize} = require('ln-accounting');
|
|
5
9
|
const {returnResult} = require('asyncjs-util');
|
|
6
10
|
|
|
7
11
|
const categories = require('./accounting_categories');
|
|
12
|
+
const {chainBlocksFile} = require('./constants');
|
|
8
13
|
const {chainPricesFile} = require('./constants');
|
|
14
|
+
const chainPricesLogger = require('./chain_prices_logger');
|
|
9
15
|
const {coinbaseProvider} = require('./constants');
|
|
10
16
|
const {defaultCurrency} = require('./constants');
|
|
11
17
|
const {defaultFiat} = require('./constants');
|
|
@@ -18,11 +24,14 @@ const {monthNumbers} = require('./constants');
|
|
|
18
24
|
const {monthOffset} = require('./constants');
|
|
19
25
|
const {notFoundIndex} = require('./constants');
|
|
20
26
|
const rangeForDate = require('./range_for_date');
|
|
27
|
+
const {sortBy} = require('./../arrays');
|
|
21
28
|
const tableRowsFromCsv = require('./table_rows_from_csv');
|
|
22
29
|
|
|
23
30
|
const assetType = 'BTC';
|
|
24
|
-
const currentDate = new Date().toISOString();
|
|
25
31
|
const empty = '';
|
|
32
|
+
const flatten = arr => [].concat(...arr);
|
|
33
|
+
const {isArray} = Array;
|
|
34
|
+
const {keys} = Object;
|
|
26
35
|
const round = n => parseFloat(n).toFixed(2);
|
|
27
36
|
const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
|
|
28
37
|
const summaryHeadings = ['Total', 'Asset', 'Report Date', 'Total Fiat'];
|
|
@@ -42,10 +51,9 @@ const summaryHeadings = ['Total', 'Asset', 'Report Date', 'Total Fiat'];
|
|
|
42
51
|
}
|
|
43
52
|
[is_csv]: <Return CSV Output Bool>
|
|
44
53
|
[is_fiat_disabled]: <Omit Fiat Conversion Bool>
|
|
45
|
-
|
|
54
|
+
lnds: [<Authenticated LND API Object>]
|
|
46
55
|
[logger]: <Winston Logger Object>
|
|
47
56
|
[month]: <Month for Report String>
|
|
48
|
-
[node]: <Node Name String>
|
|
49
57
|
[rate_provider]: <Rate Provider String>
|
|
50
58
|
request: <Request Function>
|
|
51
59
|
[year]: <Year for Report String>
|
|
@@ -65,7 +73,9 @@ module.exports = (args, cbk) => {
|
|
|
65
73
|
const {fs} = args;
|
|
66
74
|
|
|
67
75
|
if (!args.category || !categories[args.category]) {
|
|
68
|
-
return cbk([400, 'ExpectedKnownAccountingRecordsCategory'
|
|
76
|
+
return cbk([400, 'ExpectedKnownAccountingRecordsCategory', {
|
|
77
|
+
categories: keys(categories),
|
|
78
|
+
}]);
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
if (!!args.date && !args.month) {
|
|
@@ -76,8 +86,8 @@ module.exports = (args, cbk) => {
|
|
|
76
86
|
return cbk([400, 'ExpectedFileSystemMethodsToGetAccountingReport']);
|
|
77
87
|
}
|
|
78
88
|
|
|
79
|
-
if (!args.
|
|
80
|
-
return cbk([400, '
|
|
89
|
+
if (!isArray(args.lnds) || !args.lnds.length) {
|
|
90
|
+
return cbk([400, 'ExpectedAuthenticatedLndsToGetAccountingReport']);
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
if (!args.request) {
|
|
@@ -88,34 +98,37 @@ module.exports = (args, cbk) => {
|
|
|
88
98
|
},
|
|
89
99
|
|
|
90
100
|
// Get date range
|
|
91
|
-
dateRange: ['validate', (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
};
|
|
101
|
-
} catch (err) {
|
|
102
|
-
return {err};
|
|
103
|
-
}
|
|
104
|
-
})();
|
|
105
|
-
|
|
106
|
-
// Exit early when the date range cannot be determined
|
|
107
|
-
if (!!range.err) {
|
|
108
|
-
return cbk([400, range.err.message]);
|
|
101
|
+
dateRange: ['validate', asyncify(() => {
|
|
102
|
+
try {
|
|
103
|
+
return rangeForDate({
|
|
104
|
+
date: args.date,
|
|
105
|
+
month: args.month,
|
|
106
|
+
year: args.year,
|
|
107
|
+
});
|
|
108
|
+
} catch (err) {
|
|
109
|
+
throw [400, err.message];
|
|
109
110
|
}
|
|
111
|
+
})],
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
// Get the network names of the nodes
|
|
114
|
+
getNetworks: ['validate', ({}, cbk) => {
|
|
115
|
+
return asyncMap(args.lnds, (lnd, cbk) => getNetwork({lnd}, cbk), cbk);
|
|
112
116
|
}],
|
|
113
117
|
|
|
114
|
-
//
|
|
115
|
-
|
|
118
|
+
// The network that the nodes are on
|
|
119
|
+
network: ['getNetworks', ({getNetworks}, cbk) => {
|
|
120
|
+
const [{network}] = getNetworks;
|
|
121
|
+
|
|
122
|
+
// Exit early when the nodes are not all on the same network
|
|
123
|
+
if (getNetworks.some(n => n.network !== network)) {
|
|
124
|
+
return cbk([400, 'ExpectedSameNetworkNodesForAccountingReport']);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return cbk(null, network);
|
|
128
|
+
}],
|
|
116
129
|
|
|
117
130
|
// Make the method that gets chain prices, used without a rate provider
|
|
118
|
-
chain: ['
|
|
131
|
+
chain: ['network', ({network}, cbk) => {
|
|
119
132
|
// Exit early when fiat is disabled or a rate provider is specified
|
|
120
133
|
if (!!args.is_fiat_disabled || !!args.rate_provider) {
|
|
121
134
|
return cbk();
|
|
@@ -123,17 +136,23 @@ module.exports = (args, cbk) => {
|
|
|
123
136
|
|
|
124
137
|
// Exit early when there are no chain prices for the network or no
|
|
125
138
|
// file system to keep them in
|
|
126
|
-
if (!args.fs ||
|
|
139
|
+
if (!args.fs || network !== mainnet) {
|
|
127
140
|
return cbk();
|
|
128
141
|
}
|
|
129
142
|
|
|
130
143
|
// Progress is only logged when it cannot end up in CSV output
|
|
131
|
-
const logger =
|
|
144
|
+
const {logger} = chainPricesLogger({
|
|
145
|
+
logger: !args.is_csv ? args.logger : undefined,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Chain prices are derived through the first node
|
|
149
|
+
const [lnd] = args.lnds;
|
|
132
150
|
|
|
133
151
|
const {getChainRate} = chainRates({
|
|
152
|
+
lnd,
|
|
134
153
|
logger,
|
|
154
|
+
blocks_path: homePath({file: chainBlocksFile}).path,
|
|
135
155
|
fs: args.fs,
|
|
136
|
-
lnd: args.lnd,
|
|
137
156
|
path: homePath({file: chainPricesFile}).path,
|
|
138
157
|
});
|
|
139
158
|
|
|
@@ -165,51 +184,63 @@ module.exports = (args, cbk) => {
|
|
|
165
184
|
});
|
|
166
185
|
}],
|
|
167
186
|
|
|
168
|
-
// Get accounting
|
|
187
|
+
// Get the accounting records of each node, one node after another
|
|
169
188
|
getAccounting: [
|
|
170
189
|
'dateRange',
|
|
171
|
-
'
|
|
190
|
+
'network',
|
|
172
191
|
'rate',
|
|
173
|
-
({dateRange,
|
|
192
|
+
({dateRange, network, rate}, cbk) =>
|
|
174
193
|
{
|
|
175
|
-
return
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
194
|
+
return asyncMapSeries(args.lnds, (lnd, cbk) => {
|
|
195
|
+
return getAccountingReport({
|
|
196
|
+
lnd,
|
|
197
|
+
network,
|
|
198
|
+
after: dateRange.after,
|
|
199
|
+
before: dateRange.before,
|
|
200
|
+
category: categories[args.category],
|
|
201
|
+
currency: args.currency || defaultCurrency,
|
|
202
|
+
fiat: !args.is_fiat_disabled ? (args.fiat || defaultFiat) : null,
|
|
203
|
+
rate: !!rate ? rate.rate : undefined,
|
|
204
|
+
rate_provider: !rate ? args.rate_provider : undefined,
|
|
205
|
+
request: args.request,
|
|
206
|
+
},
|
|
207
|
+
cbk);
|
|
186
208
|
},
|
|
187
209
|
cbk);
|
|
188
210
|
}],
|
|
189
211
|
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
const
|
|
212
|
+
// The records of all of the nodes in the category, in order of date
|
|
213
|
+
records: ['getAccounting', ({getAccounting}, cbk) => {
|
|
214
|
+
const category = categories[args.category];
|
|
215
|
+
|
|
216
|
+
const records = flatten(getAccounting.map(report => report[category]));
|
|
217
|
+
|
|
218
|
+
const {sorted} = sortBy({array: records, attribute: 'created_at'});
|
|
219
|
+
|
|
220
|
+
return cbk(null, sorted);
|
|
221
|
+
}],
|
|
222
|
+
|
|
223
|
+
// Convert the records into a CSV, or into rows for table display output
|
|
224
|
+
accounting: ['records', ({records}, cbk) => {
|
|
225
|
+
const {csv} = harmonize({records});
|
|
193
226
|
|
|
194
227
|
// Exit early when a CSV dump is requested
|
|
195
228
|
if (!!args.is_csv) {
|
|
196
|
-
return cbk(null,
|
|
229
|
+
return cbk(null, csv);
|
|
197
230
|
}
|
|
198
231
|
|
|
199
|
-
return tableRowsFromCsv({csv
|
|
232
|
+
return tableRowsFromCsv({csv}, cbk);
|
|
200
233
|
}],
|
|
201
234
|
|
|
202
235
|
// Calculate total amounts
|
|
203
|
-
total: ['
|
|
236
|
+
total: ['records', ({records}, cbk) => {
|
|
204
237
|
// Exit early when a CSV dump is requested
|
|
205
238
|
if (!!args.is_csv) {
|
|
206
239
|
return cbk();
|
|
207
240
|
}
|
|
208
241
|
|
|
209
|
-
const rows = getAccounting[categories[args.category]];
|
|
210
|
-
|
|
211
242
|
// Token values are represented as amounts
|
|
212
|
-
const tokens = sumOf(
|
|
243
|
+
const tokens = sumOf(records.map(n => n.amount));
|
|
213
244
|
|
|
214
245
|
// Exit early when there is no fiat data
|
|
215
246
|
if (!!args.is_fiat_disabled) {
|
|
@@ -217,13 +248,13 @@ module.exports = (args, cbk) => {
|
|
|
217
248
|
}
|
|
218
249
|
|
|
219
250
|
// Fiat values are represented as fiat amounts
|
|
220
|
-
const fiat = round(sumOf(
|
|
251
|
+
const fiat = round(sumOf(records.map(n => n.fiat_amount)));
|
|
221
252
|
|
|
222
253
|
return cbk(null, {fiat, tokens});
|
|
223
254
|
}],
|
|
224
255
|
|
|
225
256
|
// Clean rows for display if necessary
|
|
226
|
-
report: ['accounting', 'total',({accounting, total}, cbk) => {
|
|
257
|
+
report: ['accounting', 'total', ({accounting, total}, cbk) => {
|
|
227
258
|
// Exit early when there is no cleaning necessary
|
|
228
259
|
if (!!args.is_csv) {
|
|
229
260
|
return cbk(null, accounting);
|
|
@@ -249,7 +280,7 @@ module.exports = (args, cbk) => {
|
|
|
249
280
|
|
|
250
281
|
const summary = [
|
|
251
282
|
summaryHeadings,
|
|
252
|
-
[total.tokens, assetType,
|
|
283
|
+
[total.tokens, assetType, new Date().toISOString(), total.fiat],
|
|
253
284
|
];
|
|
254
285
|
|
|
255
286
|
return cbk(null, {rows, rows_summary: summary});
|
|
@@ -4,7 +4,9 @@ const {monthNumbers} = require('./constants');
|
|
|
4
4
|
const {monthOffset} = require('./constants');
|
|
5
5
|
const {notFoundIndex} = require('./constants');
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const isDay = n => /^\d+$/.test(n) && Number(n) > 0;
|
|
8
|
+
|
|
9
|
+
/** Get a before and after range
|
|
8
10
|
|
|
9
11
|
{
|
|
10
12
|
[date]: <Day of Month String>
|
|
@@ -17,8 +19,8 @@ const {notFoundIndex} = require('./constants');
|
|
|
17
19
|
|
|
18
20
|
@returns
|
|
19
21
|
{
|
|
20
|
-
[after]: <After ISO 8601 Date String>
|
|
21
|
-
[before]: <Before ISO 8601 Date String>
|
|
22
|
+
[after]: <Records Created On or After ISO 8601 Date String>
|
|
23
|
+
[before]: <Records Created Before ISO 8601 Date String>
|
|
22
24
|
}
|
|
23
25
|
*/
|
|
24
26
|
module.exports = ({date, month, year}) => {
|
|
@@ -32,12 +34,6 @@ module.exports = ({date, month, year}) => {
|
|
|
32
34
|
after.year(year);
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
try {
|
|
36
|
-
after.toISOString();
|
|
37
|
-
} catch (err) {
|
|
38
|
-
throw new Error('UnrecognizedFormatForAccountingYear');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
37
|
const end = after.clone();
|
|
42
38
|
|
|
43
39
|
if (!!month && monthNumbers.indexOf(month) !== notFoundIndex) {
|
|
@@ -46,8 +42,12 @@ module.exports = ({date, month, year}) => {
|
|
|
46
42
|
[after, end].forEach(n => n.month(month));
|
|
47
43
|
}
|
|
48
44
|
|
|
45
|
+
if (!!date && (!isDay(date) || Number(date) > after.daysInMonth())) {
|
|
46
|
+
throw new Error('ExpectedDayInMonthForAccountingDate');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
49
|
if (!!date) {
|
|
50
|
-
[after, end].forEach(n => n.date(date));
|
|
50
|
+
[after, end].forEach(n => n.date(Number(date)));
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
if (!!date) {
|
|
@@ -58,13 +58,5 @@ module.exports = ({date, month, year}) => {
|
|
|
58
58
|
end.add([after].length, 'years');
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
try {
|
|
62
|
-
after.toISOString();
|
|
63
|
-
} catch (err) {
|
|
64
|
-
throw new Error('UnrecognizedFormatForAccountingMonth');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
after.subtract([after].length, 'millisecond');
|
|
68
|
-
|
|
69
61
|
return {after: after.toISOString(), before: end.toISOString()};
|
|
70
62
|
};
|
package/bos
CHANGED
|
@@ -67,16 +67,17 @@ prog
|
|
|
67
67
|
|
|
68
68
|
// Get accounting information
|
|
69
69
|
.command('accounting', 'Get an accounting rundown')
|
|
70
|
-
.argument('<category>', 'Report category'
|
|
70
|
+
.argument('<category>', 'Report category')
|
|
71
71
|
.help(`Categories: ${keys(accountingCategories).join(', ')}`)
|
|
72
72
|
.help(`Rate providers: ${rateProviders.join(', ')}`)
|
|
73
73
|
.help('By default fiat rates after 2021-10-19 use chain data derived rates')
|
|
74
|
+
.help('Multiple --node arguments are supported to report on nodes together')
|
|
74
75
|
.help('Privacy note: this requests tx related data from third parties')
|
|
75
76
|
.option('--csv', 'Output a CSV')
|
|
76
77
|
.option('--date <day>', 'Show only records for specific date')
|
|
77
78
|
.option('--disable-fiat', 'Avoid looking up fiat conversions for records')
|
|
78
79
|
.option('--month <month>', 'Show only records for specific month', months)
|
|
79
|
-
.option('--node <node_name>', 'Get details from
|
|
80
|
+
.option('--node <node_name>', 'Get details from saved node(s)', REPEATABLE)
|
|
80
81
|
.option('--rate-provider <rate_provider>', 'Rate provider', rateProviders)
|
|
81
82
|
.option('--year <year>', 'Show only records for specified year', yearMatch)
|
|
82
83
|
.action((args, options, logger) => {
|
|
@@ -96,9 +97,8 @@ prog
|
|
|
96
97
|
},
|
|
97
98
|
is_csv: !!options.csv,
|
|
98
99
|
is_fiat_disabled: options.disableFiat,
|
|
99
|
-
|
|
100
|
+
lnds: (await lnd.getLnds({logger, nodes: options.node})).lnds,
|
|
100
101
|
month: options.month,
|
|
101
|
-
node: options.node,
|
|
102
102
|
rate_provider: options.rateProvider,
|
|
103
103
|
request: commands.simpleRequest,
|
|
104
104
|
year: options.year,
|
|
@@ -824,10 +824,12 @@ prog
|
|
|
824
824
|
.help('Specify <address> <amount> <address> <amount> for addresses, amounts')
|
|
825
825
|
.help('Amounts support formulas, use MAX to reference selected UTXOs total')
|
|
826
826
|
.help('--utxo can be specified multiple times to spend multiple UTXOs')
|
|
827
|
+
.help('--fee-rate-precise is per 1000 vbytes, so 1500 is 1.5 per vbyte')
|
|
827
828
|
.argument('<address_amount...>', 'Address and amount to send')
|
|
828
829
|
.option('--broadcast', 'Broadcast the signed transaction')
|
|
829
830
|
.option('--dryrun', 'Avoid locking up UTXOs')
|
|
830
831
|
.option('--fee-rate <fee>', 'Per vbyte fee rate for on-chain tx fee')
|
|
832
|
+
.option('--fee-rate-precise <per_kvbyte>', 'Fee rate per 1000 vbytes', INT)
|
|
831
833
|
.option('--node <node_name>', 'Node to spend coins')
|
|
832
834
|
.option('--select-utxos', 'Specify UTXOs to spend interactively from a list')
|
|
833
835
|
.option('--utxo <outpoint>', 'Spend a specific tx_id:vout', REPEATABLE)
|
|
@@ -839,6 +841,7 @@ prog
|
|
|
839
841
|
addresses: args.addressAmount.filter((n, i) => !(i % 2)),
|
|
840
842
|
amounts: args.addressAmount.filter((n, i) => i % 2),
|
|
841
843
|
ask: await commands.interrogate({}),
|
|
844
|
+
fee_mtokens_per_vbyte: options.feeRatePrecise,
|
|
842
845
|
fee_tokens_per_vbyte: options.feeRate,
|
|
843
846
|
is_broadcast: options.broadcast,
|
|
844
847
|
is_dry_run: !!options.dryrun,
|
|
@@ -1,32 +1,40 @@
|
|
|
1
1
|
const asyncAuto = require('async/auto');
|
|
2
2
|
const asyncEach = require('async/each');
|
|
3
3
|
const asyncify = require('async/asyncify');
|
|
4
|
+
const asyncMap = require('async/map');
|
|
5
|
+
const asyncReflect = require('async/reflect');
|
|
4
6
|
const {broadcastTransaction} = require('ln-sync');
|
|
5
7
|
const {componentsOfTransaction} = require('@alexbosworth/blockchain');
|
|
8
|
+
const {createFundedPsbt} = require('ln-service');
|
|
6
9
|
const {formatTokens} = require('ln-sync');
|
|
7
|
-
const {fundPsbt} = require('ln-service');
|
|
8
10
|
const {getChainFeeRate} = require('ln-service');
|
|
9
|
-
const {
|
|
11
|
+
const {getMaxFundingValue} = require('ln-sync');
|
|
10
12
|
const {getNetwork} = require('ln-sync');
|
|
11
13
|
const {getUtxos} = require('ln-service');
|
|
14
|
+
const {lockUtxo} = require('ln-service');
|
|
15
|
+
const {outputScriptForAddress} = require('@alexbosworth/blockchain');
|
|
12
16
|
const {parseAmount} = require('ln-accounting');
|
|
13
17
|
const {returnResult} = require('asyncjs-util');
|
|
14
18
|
const {signPsbt} = require('ln-service');
|
|
15
19
|
const {sizeOfTransaction} = require('@alexbosworth/blockchain');
|
|
16
20
|
const {unlockUtxo} = require('ln-service');
|
|
17
21
|
|
|
18
|
-
const allowUnconfirmed = 0;
|
|
19
22
|
const asBigUnit = n => (n / 1e8).toFixed(8);
|
|
20
23
|
const asOutpoint = utxo => `${utxo.transaction_id}:${utxo.transaction_vout}`;
|
|
21
24
|
const asInput = n => ({transaction_id: n.id, transaction_vout: n.vout});
|
|
25
|
+
const asMtokens = tokens => Math.round(Number(tokens) * 1e3);
|
|
22
26
|
const asUtxo = n => ({id: n.slice(0, 64), vout: Number(n.slice(65))});
|
|
23
27
|
const dustValue = 293;
|
|
24
28
|
const formattedFeeRate = n => n.toFixed(2);
|
|
25
29
|
const hasMaxAmount = amounts => !!amounts.find(n => !!n && !!/max/gim.test(n));
|
|
26
30
|
const {isArray} = Array;
|
|
27
31
|
const isOutpoint = n => !!n && /^[0-9A-F]{64}:[0-9]{1,6}$/i.test(n);
|
|
32
|
+
const isPreciseFeeRate = n => !n || (Number.isSafeInteger(n) && n > 0);
|
|
28
33
|
const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
|
|
29
34
|
const isValidFeeRate = n => !n || Number.isInteger(Number(n));
|
|
35
|
+
const mtokensPerToken = 1e3;
|
|
36
|
+
const noCoinSelectionMinConfs = 2147483647;
|
|
37
|
+
const placeholderTokens = 546;
|
|
30
38
|
const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
|
|
31
39
|
|
|
32
40
|
/** Fund and sign a transaction
|
|
@@ -35,7 +43,7 @@ const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
|
|
|
35
43
|
addresses: [<Address String>]
|
|
36
44
|
amounts: [<Amount String>]
|
|
37
45
|
ask: <Ask Function>
|
|
38
|
-
|
|
46
|
+
[fee_mtokens_per_vbyte]: <Fee Millitokens Per Virtual Byte Number>
|
|
39
47
|
[fee_tokens_per_vbyte]: <Fee Tokens Per Virtual Byte Number>
|
|
40
48
|
[is_broadcast]: <Broadcast Signed Transaction Bool>
|
|
41
49
|
is_dry_run: <Release Locks on Transaction Bool>
|
|
@@ -87,6 +95,14 @@ module.exports = (args, cbk) => {
|
|
|
87
95
|
return cbk([400, 'ExpectedIntegerFeeRateForFundingTransaction']);
|
|
88
96
|
}
|
|
89
97
|
|
|
98
|
+
if (!isPreciseFeeRate(args.fee_mtokens_per_vbyte)) {
|
|
99
|
+
return cbk([400, 'ExpectedIntegerPreciseFeeRateToFundTransaction']);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!!args.fee_mtokens_per_vbyte && !!args.fee_tokens_per_vbyte) {
|
|
103
|
+
return cbk([400, 'ExpectedEitherFeeRateOrPreciseFeeRateNotBoth']);
|
|
104
|
+
}
|
|
105
|
+
|
|
90
106
|
if (!!args.is_broadcast && !!args.is_dry_run) {
|
|
91
107
|
return cbk([400, 'BroadcastingSignedTxUnsupportedInDryRun']);
|
|
92
108
|
}
|
|
@@ -193,13 +209,38 @@ module.exports = (args, cbk) => {
|
|
|
193
209
|
({inputs}) => cbk(null, inputs));
|
|
194
210
|
}],
|
|
195
211
|
|
|
212
|
+
// Get the fee rate to fund at in mtokens per vbyte
|
|
213
|
+
feeRate: ['getFee', ({getFee}, cbk) => {
|
|
214
|
+
// Exit early when the fee rate is already in mtokens
|
|
215
|
+
if (!!args.fee_mtokens_per_vbyte) {
|
|
216
|
+
return cbk(null, args.fee_mtokens_per_vbyte);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const rate = args.fee_tokens_per_vbyte || getFee.tokens_per_vbyte;
|
|
220
|
+
|
|
221
|
+
return cbk(null, asMtokens(rate));
|
|
222
|
+
}],
|
|
223
|
+
|
|
224
|
+
// Get the output scripts of the addresses to send to
|
|
225
|
+
scripts: ['getNetwork', asyncify(({getNetwork}) => {
|
|
226
|
+
const {network} = getNetwork;
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
return args.addresses.map(address => {
|
|
230
|
+
return outputScriptForAddress({address, network}).script;
|
|
231
|
+
});
|
|
232
|
+
} catch (err) {
|
|
233
|
+
throw [400, err.message];
|
|
234
|
+
}
|
|
235
|
+
})],
|
|
236
|
+
|
|
196
237
|
// Calculate the maximum possible amount to fund for selected inputs
|
|
197
238
|
getMax: [
|
|
198
|
-
'
|
|
239
|
+
'feeRate',
|
|
199
240
|
'getUtxos',
|
|
200
|
-
'
|
|
241
|
+
'scripts',
|
|
201
242
|
'utxos',
|
|
202
|
-
({
|
|
243
|
+
({feeRate, getUtxos, scripts, utxos}, cbk) =>
|
|
203
244
|
{
|
|
204
245
|
// Exit early when the amount is not open ended
|
|
205
246
|
if (!hasMaxAmount(args.amounts)) {
|
|
@@ -211,8 +252,6 @@ module.exports = (args, cbk) => {
|
|
|
211
252
|
return cbk([400, 'MaxAmountOnlySupportedWhenUtxosSpecified']);
|
|
212
253
|
}
|
|
213
254
|
|
|
214
|
-
const feeRate = args.fee_tokens_per_vbyte || getFee.tokens_per_vbyte;
|
|
215
|
-
|
|
216
255
|
// Find the local UTXOs in order to get the input values
|
|
217
256
|
const spend = utxos.map(outpoint => {
|
|
218
257
|
return getUtxos.utxos.find(n => asOutpoint(n) === outpoint);
|
|
@@ -225,32 +264,54 @@ module.exports = (args, cbk) => {
|
|
|
225
264
|
return cbk([400, 'UnknownInputSelected', {known: knownUtxos}]);
|
|
226
265
|
}
|
|
227
266
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
267
|
+
const [script, ...others] = scripts;
|
|
268
|
+
|
|
269
|
+
// Size the max as the first output, with placeholders for the others
|
|
270
|
+
return getMaxFundingValue({
|
|
271
|
+
fee_mtokens_per_vbyte: feeRate,
|
|
231
272
|
inputs: spend.map(utxo => ({
|
|
232
|
-
tokens: utxo.tokens,
|
|
233
273
|
transaction_id: utxo.transaction_id,
|
|
234
274
|
transaction_vout: utxo.transaction_vout,
|
|
235
275
|
})),
|
|
236
276
|
lnd: args.lnd,
|
|
277
|
+
output_script: script,
|
|
278
|
+
outputs: others.map(n => ({script: n, tokens: placeholderTokens})),
|
|
237
279
|
},
|
|
238
280
|
cbk);
|
|
239
281
|
}],
|
|
240
282
|
|
|
283
|
+
// The value that the outputs can add up to when spending the max
|
|
284
|
+
maxTokens: ['getMax', 'scripts', ({getMax, scripts}, cbk) => {
|
|
285
|
+
// Exit early when there is no max amount
|
|
286
|
+
if (getMax.max_tokens === undefined) {
|
|
287
|
+
return cbk();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Outputs after the first were sized with placeholder values
|
|
291
|
+
const placeholders = scripts.slice(1).map(() => placeholderTokens);
|
|
292
|
+
|
|
293
|
+
return cbk(null, getMax.max_tokens + sumOf(placeholders));
|
|
294
|
+
}],
|
|
295
|
+
|
|
241
296
|
// Parse amounts and put together the final set of outputs
|
|
242
|
-
finalOutputs: [
|
|
297
|
+
finalOutputs: [
|
|
298
|
+
'maxTokens',
|
|
299
|
+
'scripts',
|
|
300
|
+
asyncify(({maxTokens, scripts}) =>
|
|
301
|
+
{
|
|
243
302
|
try {
|
|
244
303
|
const outputs = args.addresses.map((address, i) => {
|
|
245
304
|
const amount = args.amounts[i];
|
|
246
305
|
const variables = {};
|
|
247
306
|
|
|
248
307
|
// The max variable is only defined when a maximum is calculated
|
|
249
|
-
if (
|
|
250
|
-
variables.max =
|
|
308
|
+
if (maxTokens !== undefined) {
|
|
309
|
+
variables.max = maxTokens;
|
|
251
310
|
}
|
|
252
311
|
|
|
253
|
-
|
|
312
|
+
const {tokens} = parseAmount({amount, variables});
|
|
313
|
+
|
|
314
|
+
return {address, tokens, script: scripts[i]};
|
|
254
315
|
});
|
|
255
316
|
|
|
256
317
|
return outputs;
|
|
@@ -259,64 +320,135 @@ module.exports = (args, cbk) => {
|
|
|
259
320
|
}
|
|
260
321
|
})],
|
|
261
322
|
|
|
262
|
-
//
|
|
323
|
+
// Make sure that the outputs are not dust
|
|
324
|
+
checkDust: ['finalOutputs', ({finalOutputs}, cbk) => {
|
|
325
|
+
if (!!finalOutputs.filter(n => n.tokens < dustValue).length) {
|
|
326
|
+
return cbk([400, 'ExpectedNonDustAmountValueForFundingAmount']);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return cbk();
|
|
330
|
+
}],
|
|
331
|
+
|
|
332
|
+
// Lock the inputs to spend, LND needs them locked before funding
|
|
333
|
+
lock: ['checkDust', 'utxos', ({utxos}, cbk) => {
|
|
334
|
+
return asyncMap(utxos.map(asUtxo), asyncReflect((utxo, cbk) => {
|
|
335
|
+
return lockUtxo({
|
|
336
|
+
lnd: args.lnd,
|
|
337
|
+
transaction_id: utxo.id,
|
|
338
|
+
transaction_vout: utxo.vout,
|
|
339
|
+
},
|
|
340
|
+
cbk);
|
|
341
|
+
}),
|
|
342
|
+
cbk);
|
|
343
|
+
}],
|
|
344
|
+
|
|
345
|
+
// The inputs that were locked, with the ids of their locks
|
|
346
|
+
locked: ['lock', 'utxos', ({lock, utxos}, cbk) => {
|
|
347
|
+
const inputs = utxos.map(asUtxo).map((utxo, i) => ({
|
|
348
|
+
id: !lock[i].error ? lock[i].value.id : undefined,
|
|
349
|
+
transaction_id: utxo.id,
|
|
350
|
+
transaction_vout: utxo.vout,
|
|
351
|
+
}));
|
|
352
|
+
|
|
353
|
+
return cbk(null, inputs.filter(n => !!n.id));
|
|
354
|
+
}],
|
|
355
|
+
|
|
356
|
+
// Create a funded PSBT, holding on to a failure to unlock inputs first
|
|
263
357
|
fund: [
|
|
358
|
+
'feeRate',
|
|
264
359
|
'finalOutputs',
|
|
265
|
-
'
|
|
266
|
-
'
|
|
267
|
-
|
|
268
|
-
({finalOutputs, getFee, getNetwork, utxos}, cbk) =>
|
|
360
|
+
'lock',
|
|
361
|
+
'locked',
|
|
362
|
+
asyncReflect(({feeRate, finalOutputs, lock, locked}, cbk) =>
|
|
269
363
|
{
|
|
270
|
-
const
|
|
271
|
-
const feeRate = args.fee_tokens_per_vbyte || getFee.tokens_per_vbyte;
|
|
364
|
+
const [failed] = lock.filter(n => !!n.error);
|
|
272
365
|
|
|
273
|
-
|
|
274
|
-
|
|
366
|
+
// Exit early when an input could not be locked
|
|
367
|
+
if (!!failed) {
|
|
368
|
+
return cbk(failed.error);
|
|
275
369
|
}
|
|
276
370
|
|
|
371
|
+
const minConfs = !!locked.length ? noCoinSelectionMinConfs : undefined;
|
|
372
|
+
|
|
277
373
|
args.logger.info({
|
|
278
374
|
send_to: finalOutputs.map(({address, tokens}) => ({
|
|
279
375
|
[address]: formatTokens({tokens}).display,
|
|
280
376
|
})),
|
|
281
|
-
requested_fee_rate: feeRate,
|
|
377
|
+
requested_fee_rate: feeRate / mtokensPerToken,
|
|
282
378
|
});
|
|
283
379
|
|
|
284
|
-
return
|
|
285
|
-
|
|
286
|
-
inputs:
|
|
380
|
+
return createFundedPsbt({
|
|
381
|
+
fee_mtokens_per_vbyte: feeRate,
|
|
382
|
+
inputs: locked.map(input => ({
|
|
383
|
+
transaction_id: input.transaction_id,
|
|
384
|
+
transaction_vout: input.transaction_vout,
|
|
385
|
+
})),
|
|
287
386
|
lnd: args.lnd,
|
|
288
|
-
|
|
289
|
-
|
|
387
|
+
// No coins are added to inputs that are specified to be spent
|
|
388
|
+
min_confirmations: minConfs,
|
|
389
|
+
outputs: finalOutputs.map(({script, tokens}) => ({script, tokens})),
|
|
290
390
|
},
|
|
291
391
|
cbk);
|
|
392
|
+
})],
|
|
393
|
+
|
|
394
|
+
// The funded PSBT, the inputs are unlocked when it could not be funded
|
|
395
|
+
funding: ['fund', 'locked', ({fund, locked}, cbk) => {
|
|
396
|
+
// Exit early when the PSBT was funded
|
|
397
|
+
if (!fund.error) {
|
|
398
|
+
return cbk(null, fund.value);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return asyncEach(locked, (input, cbk) => {
|
|
402
|
+
return unlockUtxo({
|
|
403
|
+
id: input.id,
|
|
404
|
+
lnd: args.lnd,
|
|
405
|
+
transaction_id: input.transaction_id,
|
|
406
|
+
transaction_vout: input.transaction_vout,
|
|
407
|
+
},
|
|
408
|
+
() => {
|
|
409
|
+
// Ignore errors unlocking a UTXO, the lock will expire on its own
|
|
410
|
+
return cbk();
|
|
411
|
+
});
|
|
412
|
+
},
|
|
413
|
+
() => {
|
|
414
|
+
// Return the original error that stopped funding
|
|
415
|
+
return cbk(fund.error);
|
|
416
|
+
});
|
|
292
417
|
}],
|
|
293
418
|
|
|
294
419
|
// Sign the funded PSBT
|
|
295
|
-
sign: ['
|
|
296
|
-
const [change] =
|
|
297
|
-
const total = sumOf(
|
|
420
|
+
sign: ['funding', ({funding}, cbk) => {
|
|
421
|
+
const [change] = funding.outputs.filter(n => !!n.is_change);
|
|
422
|
+
const total = sumOf(funding.outputs.map(n => n.tokens));
|
|
298
423
|
|
|
299
424
|
const tokens = !!change ? change.tokens : undefined;
|
|
300
425
|
|
|
301
426
|
args.logger.info({
|
|
302
427
|
change: !!tokens ? formatTokens({tokens}).display : undefined,
|
|
303
428
|
sum_of_outputs: formatTokens({tokens: total}).display,
|
|
304
|
-
spending_utxos:
|
|
429
|
+
spending_utxos: funding.inputs.map(asOutpoint),
|
|
305
430
|
});
|
|
306
431
|
|
|
307
|
-
return signPsbt({lnd: args.lnd, psbt:
|
|
432
|
+
return signPsbt({lnd: args.lnd, psbt: funding.psbt}, cbk);
|
|
308
433
|
}],
|
|
309
434
|
|
|
310
435
|
// Unlock the locked UTXOs in a dry run scenario
|
|
311
|
-
unlock: ['
|
|
436
|
+
unlock: ['funding', 'locked', 'sign', ({funding, locked}, cbk) => {
|
|
312
437
|
// Exit early and keep UTXOs locked when not a dry run
|
|
313
438
|
if (!args.is_dry_run) {
|
|
314
439
|
return cbk();
|
|
315
440
|
}
|
|
316
441
|
|
|
317
|
-
|
|
442
|
+
// LND locks the inputs that it adds to fund the PSBT
|
|
443
|
+
const added = funding.inputs.filter(n => !!n.lock_id).map(input => ({
|
|
444
|
+
id: input.lock_id,
|
|
445
|
+
transaction_id: input.transaction_id,
|
|
446
|
+
transaction_vout: input.transaction_vout,
|
|
447
|
+
}));
|
|
448
|
+
|
|
449
|
+
return asyncEach(locked.concat(added), (input, cbk) => {
|
|
318
450
|
return unlockUtxo({
|
|
319
|
-
id: input.
|
|
451
|
+
id: input.id,
|
|
320
452
|
lnd: args.lnd,
|
|
321
453
|
transaction_id: input.transaction_id,
|
|
322
454
|
transaction_vout: input.transaction_vout,
|
|
@@ -380,7 +512,7 @@ module.exports = (args, cbk) => {
|
|
|
380
512
|
transaction: funded.signed_transaction,
|
|
381
513
|
},
|
|
382
514
|
cbk);
|
|
383
|
-
}]
|
|
515
|
+
}],
|
|
384
516
|
},
|
|
385
517
|
returnResult({reject, resolve, of: 'broadcast'}, cbk));
|
|
386
518
|
});
|
package/commands/constants.json
CHANGED
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@alexbosworth/blockchain": "4.12.0",
|
|
19
19
|
"@alexbosworth/caporal": "2.0.0",
|
|
20
|
-
"@alexbosworth/fiat": "3.
|
|
20
|
+
"@alexbosworth/fiat": "3.1.0",
|
|
21
21
|
"@alexbosworth/formulas": "1.5.0",
|
|
22
22
|
"@alexbosworth/html2unicode": "1.1.5",
|
|
23
23
|
"asciichart": "1.5.25",
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
"bolt07": "2.0.0",
|
|
30
30
|
"colorette": "2.0.20",
|
|
31
31
|
"csv-parse": "7.0.3",
|
|
32
|
-
"goldengate": "16.0.
|
|
32
|
+
"goldengate": "16.0.10",
|
|
33
33
|
"grammy": "1.46.0",
|
|
34
34
|
"import-lazy": "4.0.0",
|
|
35
35
|
"ini": "7.0.0",
|
|
36
36
|
"inquirer": "14.2.5",
|
|
37
|
-
"ln-accounting": "
|
|
38
|
-
"ln-service": "60.
|
|
39
|
-
"ln-sync": "8.0
|
|
37
|
+
"ln-accounting": "11.0.0",
|
|
38
|
+
"ln-service": "60.3.0",
|
|
39
|
+
"ln-sync": "8.1.0",
|
|
40
40
|
"ln-telegram": "8.2.0",
|
|
41
41
|
"moment": "2.31.0",
|
|
42
42
|
"paid-services": "10.3.3",
|
|
@@ -99,9 +99,9 @@
|
|
|
99
99
|
"build-docker": "docker build -t alexbosworth/balanceofsatoshis . && docker save alexbosworth/balanceofsatoshis > balanceofsatoshis.tar && gzip balanceofsatoshis.tar",
|
|
100
100
|
"integration-tests": "node test/integration",
|
|
101
101
|
"postpack": "PACKAGE_VERSION=$(cat package.json | grep \\\"version\\\" | head -1 | awk -F: '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]') && git tag -s v$PACKAGE_VERSION -m v$PACKAGE_VERSION && git push github --tags",
|
|
102
|
-
"postpublish": "docker buildx build --platform linux/amd64,linux/arm64 -t alexbosworth/balanceofsatoshis -t alexbosworth/balanceofsatoshis:$npm_package_version --push .",
|
|
102
|
+
"postpublish": "docker buildx build --pull --platform linux/amd64,linux/arm64 -t alexbosworth/balanceofsatoshis -t alexbosworth/balanceofsatoshis:$npm_package_version --push .",
|
|
103
103
|
"reinstall-dependencies": "rm -rf node_modules package-lock.json && npm i",
|
|
104
104
|
"test": "node --experimental-test-coverage --test test/arrays/*.js test/balances/*.js test/chain/*.js test/commands/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/lnurl/*.js test/network/*.js test/nodes/*.js test/offchain/*.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"
|
|
105
105
|
},
|
|
106
|
-
"version": "25.
|
|
106
|
+
"version": "25.7.0"
|
|
107
107
|
}
|