balanceofsatoshis 25.6.2 → 26.0.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 +38 -0
- package/README.md +29 -3
- 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 +17 -0
- package/balances/get_accounting_report.js +106 -61
- package/balances/range_for_date.js +10 -18
- package/bos +75 -76
- package/chain/fund_transaction.js +173 -41
- package/commands/constants.json +1 -0
- package/package.json +16 -11
- package/routing/get_chain_fees_chart.js +0 -6
- package/swaps/cancel_unsigned_swap_in.js +94 -0
- package/swaps/constants.json +23 -10
- package/swaps/decode_swap_in_recovery.js +122 -0
- package/swaps/encode_swap_in_recovery.js +115 -0
- package/swaps/get_paid_service.js +41 -72
- package/swaps/get_swap_cost.js +3 -0
- package/swaps/subscribe_to_swap_in.js +147 -0
- package/swaps/swap_api_key.js +4 -2
- package/swaps/swap_in.js +1132 -493
- package/swaps/swap_out.js +366 -765
- package/swaps/wait_for_swap_in_payment.js +254 -0
- package/swaps/get_raw_recoveries.js +0 -154
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 26.0.0
|
|
4
|
+
|
|
5
|
+
- `swap-api-key`: Add `--max-fee` to limit what is paid for an API key
|
|
6
|
+
- `swap-in`: Use the shared address swap in protocol of the swap service,
|
|
7
|
+
swapping in a deposit of the exact amount once it has 3 confirmations
|
|
8
|
+
- `swap-in`: Add `--max-fee-rate` to limit the swap fee in parts per million
|
|
9
|
+
- `swap-in`, `swap-out`: Add `--max-api-key-fee` to limit what is paid for a
|
|
10
|
+
swap service API key
|
|
11
|
+
- `swap-out`: Use the Taproot MuSig2 swap out protocol of the swap service,
|
|
12
|
+
like `swap` does
|
|
13
|
+
- `swap-out`: Add `--out` to select the peer or tag to increase inbound
|
|
14
|
+
liquidity from, with `--out-filter` formulas like `rebalance`
|
|
15
|
+
|
|
16
|
+
### Breaking Changes
|
|
17
|
+
|
|
18
|
+
- `increase-inbound-liquidity`: Rename command to `swap-out`
|
|
19
|
+
- `swap-in`: `--recovery` takes a shared address secret, not old recovery codes
|
|
20
|
+
- `swap-in`: `--refund-address` with `--recovery` refunds expired deposits
|
|
21
|
+
and unpaid swaps that timed out
|
|
22
|
+
- `swap-in`: Remove `--test-refund`
|
|
23
|
+
- `swap-in`: Use 25.7.0 to refund swaps in started with earlier versions
|
|
24
|
+
- `swap-out`: `--recovery` takes the restart code of the new swap protocol,
|
|
25
|
+
which `swap` also accepts, not the previous recovery code
|
|
26
|
+
- `swap-out`: Remove `--max-hours`, `--show-raw-recovery`, `--spend-address`
|
|
27
|
+
and `--spend-amount`
|
|
28
|
+
- `swap-out`: Rename `--with` to `--out`
|
|
29
|
+
|
|
30
|
+
## 25.7.0
|
|
31
|
+
|
|
32
|
+
- `accounting`: Add `lightning-fees` category to report the routing fees paid
|
|
33
|
+
on payments
|
|
34
|
+
- `accounting`: Keep the payments of recent blocks in `chain_blocks.json`, so
|
|
35
|
+
that later runs only fetch the blocks that are new
|
|
36
|
+
- `accounting`: Pass `--node` more than once to get one report of several
|
|
37
|
+
nodes, with their records together in order of date
|
|
38
|
+
- `fund`: Add `--fee-rate-precise` to set a fee rate per 1000 vbytes
|
|
39
|
+
- `fund`: `MAX` amounts send as much as possible without overpaying the fee
|
|
40
|
+
|
|
3
41
|
## 25.6.2
|
|
4
42
|
|
|
5
43
|
- `telegram`: Fix invoices settled before the bot started being reported as new
|
package/README.md
CHANGED
|
@@ -123,9 +123,6 @@ bos inbound-liquidity
|
|
|
123
123
|
# Enforce rules on inbound channels
|
|
124
124
|
bos inbound-channel-rules
|
|
125
125
|
|
|
126
|
-
# Increase inbound liquidity to the node
|
|
127
|
-
bos increase-inbound-liquidity
|
|
128
|
-
|
|
129
126
|
# Increase the outbound liquidity of the node
|
|
130
127
|
bos increase-outbound-liquidity
|
|
131
128
|
|
|
@@ -171,6 +168,12 @@ bos server
|
|
|
171
168
|
# Stream a payment to a lnurl or lightning address in a series of payments
|
|
172
169
|
bos stream "amount" --lnurl "lnurl/lightning_address"
|
|
173
170
|
|
|
171
|
+
# Trade on-chain funds for off-chain funds to increase outbound liquidity
|
|
172
|
+
bos swap-in
|
|
173
|
+
|
|
174
|
+
# Trade off-chain funds for on-chain funds to increase inbound liquidity
|
|
175
|
+
bos swap-out
|
|
176
|
+
|
|
174
177
|
# Connect up to a Telegram bot
|
|
175
178
|
bos telegram
|
|
176
179
|
|
|
@@ -305,6 +308,9 @@ You can also add a JSON file to your .bos directory: config.json, add
|
|
|
305
308
|
|
|
306
309
|
`bos` keeps its files in `~/.bos`, or in `BOS_DATA_PATH` when that is set:
|
|
307
310
|
|
|
311
|
+
- `chain_blocks.json`: the payments in the last few days of blocks that
|
|
312
|
+
`bos accounting` derives prices from, kept so that later runs only fetch
|
|
313
|
+
the blocks that are new. The file can be deleted
|
|
308
314
|
- `chain_prices.json`: USD prices of BTC that `bos accounting` derived from
|
|
309
315
|
chain data, kept so that a day is only derived once. Prices are derived
|
|
310
316
|
this way when no `--rate-provider` is given, and the prices of the days
|
|
@@ -614,6 +620,7 @@ Formula amounts are supported in the following commands:
|
|
|
614
620
|
- `probe`
|
|
615
621
|
- `rebalance`
|
|
616
622
|
- `send`
|
|
623
|
+
- `swap-out`
|
|
617
624
|
|
|
618
625
|
When passing an amount you can pass a formula expression, and the following variables are
|
|
619
626
|
defined:
|
|
@@ -673,6 +680,25 @@ Example:
|
|
|
673
680
|
bos send <key> --amount "1*usd"
|
|
674
681
|
```
|
|
675
682
|
|
|
683
|
+
#### `swap-out`
|
|
684
|
+
|
|
685
|
+
Swap out defines variables for `--out-filter`:
|
|
686
|
+
|
|
687
|
+
- `capacity`: The total capacity with the peer
|
|
688
|
+
- `heights`: The set of heights of the channels with the peer
|
|
689
|
+
- `inbound_base_fee`: The base fee the peer is charging
|
|
690
|
+
- `inbound_fee_rate`: The fee rate the peer is charging
|
|
691
|
+
- `inbound_liquidity`: The inbound liquidity with the peer
|
|
692
|
+
- `outbound_liquidity`: The outbound liquidity with the peer
|
|
693
|
+
- `pending_payments`: The number of pending payments
|
|
694
|
+
|
|
695
|
+
Example:
|
|
696
|
+
|
|
697
|
+
```shell
|
|
698
|
+
// Swap out 1,000,000 with a peer of the tag that has under 2,000,000 inbound
|
|
699
|
+
bos swap-out --amount "1*m" --out "tag" --out-filter "inbound_liquidity < 2*m"
|
|
700
|
+
```
|
|
701
|
+
|
|
676
702
|
#### `transfer`
|
|
677
703
|
|
|
678
704
|
Transfer variables:
|
|
@@ -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,6 +1,14 @@
|
|
|
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",
|
|
11
|
+
"dayFormat": "YYYY-MM-DD",
|
|
4
12
|
"defaultCurrency": "BTC",
|
|
5
13
|
"defaultFiat": "USD",
|
|
6
14
|
"failedRequestErrors": ["UnexpectedErrorGettingCoinbaseCandles"],
|
|
@@ -38,5 +46,14 @@
|
|
|
38
46
|
"FailedToReplacePricesFile",
|
|
39
47
|
"FailedToWritePricesFile",
|
|
40
48
|
"UnsupportedPricesFileVersion"
|
|
49
|
+
],
|
|
50
|
+
"summaryHeadings": [
|
|
51
|
+
"Total",
|
|
52
|
+
"Asset",
|
|
53
|
+
"Report Date",
|
|
54
|
+
"Total Fiat",
|
|
55
|
+
"Category",
|
|
56
|
+
"Start Date",
|
|
57
|
+
"End Date"
|
|
41
58
|
]
|
|
42
59
|
}
|
|
@@ -1,12 +1,20 @@
|
|
|
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');
|
|
9
|
+
const moment = require('moment');
|
|
5
10
|
const {returnResult} = require('asyncjs-util');
|
|
6
11
|
|
|
7
12
|
const categories = require('./accounting_categories');
|
|
13
|
+
const {chainBlocksFile} = require('./constants');
|
|
8
14
|
const {chainPricesFile} = require('./constants');
|
|
15
|
+
const chainPricesLogger = require('./chain_prices_logger');
|
|
9
16
|
const {coinbaseProvider} = require('./constants');
|
|
17
|
+
const {dayFormat} = require('./constants');
|
|
10
18
|
const {defaultCurrency} = require('./constants');
|
|
11
19
|
const {defaultFiat} = require('./constants');
|
|
12
20
|
const {fsMethods} = require('./constants');
|
|
@@ -14,18 +22,20 @@ const getRecordRate = require('./get_record_rate');
|
|
|
14
22
|
const guardChainRate = require('./guard_chain_rate');
|
|
15
23
|
const {homePath} = require('./../storage');
|
|
16
24
|
const {mainnet} = require('./constants');
|
|
17
|
-
const {monthNumbers} = require('./constants');
|
|
18
|
-
const {monthOffset} = require('./constants');
|
|
19
|
-
const {notFoundIndex} = require('./constants');
|
|
20
25
|
const rangeForDate = require('./range_for_date');
|
|
26
|
+
const {sortBy} = require('./../arrays');
|
|
27
|
+
const {summaryHeadings} = require('./constants');
|
|
21
28
|
const tableRowsFromCsv = require('./table_rows_from_csv');
|
|
22
29
|
|
|
23
30
|
const assetType = 'BTC';
|
|
24
|
-
const
|
|
31
|
+
const dayOf = n => moment.utc(n).format(dayFormat);
|
|
25
32
|
const empty = '';
|
|
33
|
+
const flatten = arr => [].concat(...arr);
|
|
34
|
+
const {isArray} = Array;
|
|
35
|
+
const {keys} = Object;
|
|
36
|
+
const lastDayBefore = n => moment.utc(n).subtract(1, 'ms').format(dayFormat);
|
|
26
37
|
const round = n => parseFloat(n).toFixed(2);
|
|
27
38
|
const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
|
|
28
|
-
const summaryHeadings = ['Total', 'Asset', 'Report Date', 'Total Fiat'];
|
|
29
39
|
|
|
30
40
|
/** Get an accounting report
|
|
31
41
|
|
|
@@ -42,10 +52,9 @@ const summaryHeadings = ['Total', 'Asset', 'Report Date', 'Total Fiat'];
|
|
|
42
52
|
}
|
|
43
53
|
[is_csv]: <Return CSV Output Bool>
|
|
44
54
|
[is_fiat_disabled]: <Omit Fiat Conversion Bool>
|
|
45
|
-
|
|
55
|
+
lnds: [<Authenticated LND API Object>]
|
|
46
56
|
[logger]: <Winston Logger Object>
|
|
47
57
|
[month]: <Month for Report String>
|
|
48
|
-
[node]: <Node Name String>
|
|
49
58
|
[rate_provider]: <Rate Provider String>
|
|
50
59
|
request: <Request Function>
|
|
51
60
|
[year]: <Year for Report String>
|
|
@@ -65,7 +74,9 @@ module.exports = (args, cbk) => {
|
|
|
65
74
|
const {fs} = args;
|
|
66
75
|
|
|
67
76
|
if (!args.category || !categories[args.category]) {
|
|
68
|
-
return cbk([400, 'ExpectedKnownAccountingRecordsCategory'
|
|
77
|
+
return cbk([400, 'ExpectedKnownAccountingRecordsCategory', {
|
|
78
|
+
categories: keys(categories),
|
|
79
|
+
}]);
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
if (!!args.date && !args.month) {
|
|
@@ -76,8 +87,8 @@ module.exports = (args, cbk) => {
|
|
|
76
87
|
return cbk([400, 'ExpectedFileSystemMethodsToGetAccountingReport']);
|
|
77
88
|
}
|
|
78
89
|
|
|
79
|
-
if (!args.
|
|
80
|
-
return cbk([400, '
|
|
90
|
+
if (!isArray(args.lnds) || !args.lnds.length) {
|
|
91
|
+
return cbk([400, 'ExpectedAuthenticatedLndsToGetAccountingReport']);
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
if (!args.request) {
|
|
@@ -88,34 +99,37 @@ module.exports = (args, cbk) => {
|
|
|
88
99
|
},
|
|
89
100
|
|
|
90
101
|
// 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]);
|
|
102
|
+
dateRange: ['validate', asyncify(() => {
|
|
103
|
+
try {
|
|
104
|
+
return rangeForDate({
|
|
105
|
+
date: args.date,
|
|
106
|
+
month: args.month,
|
|
107
|
+
year: args.year,
|
|
108
|
+
});
|
|
109
|
+
} catch (err) {
|
|
110
|
+
throw [400, err.message];
|
|
109
111
|
}
|
|
112
|
+
})],
|
|
110
113
|
|
|
111
|
-
|
|
114
|
+
// Get the network names of the nodes
|
|
115
|
+
getNetworks: ['validate', ({}, cbk) => {
|
|
116
|
+
return asyncMap(args.lnds, (lnd, cbk) => getNetwork({lnd}, cbk), cbk);
|
|
112
117
|
}],
|
|
113
118
|
|
|
114
|
-
//
|
|
115
|
-
|
|
119
|
+
// The network that the nodes are on
|
|
120
|
+
network: ['getNetworks', ({getNetworks}, cbk) => {
|
|
121
|
+
const [{network}] = getNetworks;
|
|
122
|
+
|
|
123
|
+
// Exit early when the nodes are not all on the same network
|
|
124
|
+
if (getNetworks.some(n => n.network !== network)) {
|
|
125
|
+
return cbk([400, 'ExpectedSameNetworkNodesForAccountingReport']);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return cbk(null, network);
|
|
129
|
+
}],
|
|
116
130
|
|
|
117
131
|
// Make the method that gets chain prices, used without a rate provider
|
|
118
|
-
chain: ['
|
|
132
|
+
chain: ['network', ({network}, cbk) => {
|
|
119
133
|
// Exit early when fiat is disabled or a rate provider is specified
|
|
120
134
|
if (!!args.is_fiat_disabled || !!args.rate_provider) {
|
|
121
135
|
return cbk();
|
|
@@ -123,17 +137,23 @@ module.exports = (args, cbk) => {
|
|
|
123
137
|
|
|
124
138
|
// Exit early when there are no chain prices for the network or no
|
|
125
139
|
// file system to keep them in
|
|
126
|
-
if (!args.fs ||
|
|
140
|
+
if (!args.fs || network !== mainnet) {
|
|
127
141
|
return cbk();
|
|
128
142
|
}
|
|
129
143
|
|
|
130
144
|
// Progress is only logged when it cannot end up in CSV output
|
|
131
|
-
const logger =
|
|
145
|
+
const {logger} = chainPricesLogger({
|
|
146
|
+
logger: !args.is_csv ? args.logger : undefined,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Chain prices are derived through the first node
|
|
150
|
+
const [lnd] = args.lnds;
|
|
132
151
|
|
|
133
152
|
const {getChainRate} = chainRates({
|
|
153
|
+
lnd,
|
|
134
154
|
logger,
|
|
155
|
+
blocks_path: homePath({file: chainBlocksFile}).path,
|
|
135
156
|
fs: args.fs,
|
|
136
|
-
lnd: args.lnd,
|
|
137
157
|
path: homePath({file: chainPricesFile}).path,
|
|
138
158
|
});
|
|
139
159
|
|
|
@@ -165,51 +185,63 @@ module.exports = (args, cbk) => {
|
|
|
165
185
|
});
|
|
166
186
|
}],
|
|
167
187
|
|
|
168
|
-
// Get accounting
|
|
188
|
+
// Get the accounting records of each node, one node after another
|
|
169
189
|
getAccounting: [
|
|
170
190
|
'dateRange',
|
|
171
|
-
'
|
|
191
|
+
'network',
|
|
172
192
|
'rate',
|
|
173
|
-
({dateRange,
|
|
193
|
+
({dateRange, network, rate}, cbk) =>
|
|
174
194
|
{
|
|
175
|
-
return
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
195
|
+
return asyncMapSeries(args.lnds, (lnd, cbk) => {
|
|
196
|
+
return getAccountingReport({
|
|
197
|
+
lnd,
|
|
198
|
+
network,
|
|
199
|
+
after: dateRange.after,
|
|
200
|
+
before: dateRange.before,
|
|
201
|
+
category: categories[args.category],
|
|
202
|
+
currency: args.currency || defaultCurrency,
|
|
203
|
+
fiat: !args.is_fiat_disabled ? (args.fiat || defaultFiat) : null,
|
|
204
|
+
rate: !!rate ? rate.rate : undefined,
|
|
205
|
+
rate_provider: !rate ? args.rate_provider : undefined,
|
|
206
|
+
request: args.request,
|
|
207
|
+
},
|
|
208
|
+
cbk);
|
|
186
209
|
},
|
|
187
210
|
cbk);
|
|
188
211
|
}],
|
|
189
212
|
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
const
|
|
213
|
+
// The records of all of the nodes in the category, in order of date
|
|
214
|
+
records: ['getAccounting', ({getAccounting}, cbk) => {
|
|
215
|
+
const category = categories[args.category];
|
|
216
|
+
|
|
217
|
+
const records = flatten(getAccounting.map(report => report[category]));
|
|
218
|
+
|
|
219
|
+
const {sorted} = sortBy({array: records, attribute: 'created_at'});
|
|
220
|
+
|
|
221
|
+
return cbk(null, sorted);
|
|
222
|
+
}],
|
|
223
|
+
|
|
224
|
+
// Convert the records into a CSV, or into rows for table display output
|
|
225
|
+
accounting: ['records', ({records}, cbk) => {
|
|
226
|
+
const {csv} = harmonize({records});
|
|
193
227
|
|
|
194
228
|
// Exit early when a CSV dump is requested
|
|
195
229
|
if (!!args.is_csv) {
|
|
196
|
-
return cbk(null,
|
|
230
|
+
return cbk(null, csv);
|
|
197
231
|
}
|
|
198
232
|
|
|
199
|
-
return tableRowsFromCsv({csv
|
|
233
|
+
return tableRowsFromCsv({csv}, cbk);
|
|
200
234
|
}],
|
|
201
235
|
|
|
202
236
|
// Calculate total amounts
|
|
203
|
-
total: ['
|
|
237
|
+
total: ['records', ({records}, cbk) => {
|
|
204
238
|
// Exit early when a CSV dump is requested
|
|
205
239
|
if (!!args.is_csv) {
|
|
206
240
|
return cbk();
|
|
207
241
|
}
|
|
208
242
|
|
|
209
|
-
const rows = getAccounting[categories[args.category]];
|
|
210
|
-
|
|
211
243
|
// Token values are represented as amounts
|
|
212
|
-
const tokens = sumOf(
|
|
244
|
+
const tokens = sumOf(records.map(n => n.amount));
|
|
213
245
|
|
|
214
246
|
// Exit early when there is no fiat data
|
|
215
247
|
if (!!args.is_fiat_disabled) {
|
|
@@ -217,13 +249,18 @@ module.exports = (args, cbk) => {
|
|
|
217
249
|
}
|
|
218
250
|
|
|
219
251
|
// Fiat values are represented as fiat amounts
|
|
220
|
-
const fiat = round(sumOf(
|
|
252
|
+
const fiat = round(sumOf(records.map(n => n.fiat_amount)));
|
|
221
253
|
|
|
222
254
|
return cbk(null, {fiat, tokens});
|
|
223
255
|
}],
|
|
224
256
|
|
|
225
257
|
// Clean rows for display if necessary
|
|
226
|
-
report: [
|
|
258
|
+
report: [
|
|
259
|
+
'accounting',
|
|
260
|
+
'dateRange',
|
|
261
|
+
'total',
|
|
262
|
+
({accounting, dateRange, total}, cbk) =>
|
|
263
|
+
{
|
|
227
264
|
// Exit early when there is no cleaning necessary
|
|
228
265
|
if (!!args.is_csv) {
|
|
229
266
|
return cbk(null, accounting);
|
|
@@ -249,7 +286,15 @@ module.exports = (args, cbk) => {
|
|
|
249
286
|
|
|
250
287
|
const summary = [
|
|
251
288
|
summaryHeadings,
|
|
252
|
-
[
|
|
289
|
+
[
|
|
290
|
+
total.tokens,
|
|
291
|
+
assetType,
|
|
292
|
+
new Date().toISOString(),
|
|
293
|
+
total.fiat,
|
|
294
|
+
args.category,
|
|
295
|
+
!dateRange.after ? empty : dayOf(dateRange.after),
|
|
296
|
+
!dateRange.before ? empty : lastDayBefore(dateRange.before),
|
|
297
|
+
],
|
|
253
298
|
];
|
|
254
299
|
|
|
255
300
|
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
|
};
|