balanceofsatoshis 22.1.7 → 23.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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # Versions
2
2
 
3
+ ## 23.0.0
4
+
5
+ ### Breaking Changes
6
+
7
+ - Remove support for extended range of formulas, supported formulas are here:
8
+ https://github.com/alexbosworth/formulas#custom-functions
9
+
3
10
  ## 22.1.7
4
11
 
5
12
  - Add support for LND 0.21.1
package/README.md CHANGED
@@ -564,8 +564,11 @@ alias bosd="docker run -d --rm -v $HOME/.bos:/home/node/.bos alexbosworth/balanc
564
564
  Some commands take formula arguments. Formulas are expressions that allow you
565
565
  to perform functions and reference variables.
566
566
 
567
- There is a dynamic playground here where you can play with expressions:
568
- https://formulajs.info/functions/
567
+ Formulas are modeled after common spreadsheet formulas, but there is not a 1:1
568
+ mapping of supported functions from all spreadsheets.
569
+
570
+ See supported formula construction here:
571
+ https://github.com/alexbosworth/formulas#custom-functions
569
572
 
570
573
  ### `--avoid`
571
574
 
package/display/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  const chartAliasForPeer = require('./chart_alias_for_peer');
2
2
  const describeConfidence = require('./describe_confidence');
3
- const describeParseError = require('./describe_parse_error');
4
3
  const describeRoute = require('./describe_route');
5
4
  const describeRoutingFailure = require('./describe_routing_failure');
6
5
  const formatFeeRate = require('./format_fee_rate');
@@ -12,7 +11,6 @@ const sumsForSegment = require('./sums_for_segment');
12
11
  module.exports = {
13
12
  chartAliasForPeer,
14
13
  describeConfidence,
15
- describeParseError,
16
14
  describeRoute,
17
15
  describeRoutingFailure,
18
16
  formatFeeRate,
@@ -1,10 +1,7 @@
1
- const {Parser} = require('hot-formula-parser');
2
-
3
- const describeParseError = require('./describe_parse_error');
1
+ const {evaluateFormula} = require('@alexbosworth/formulas');
4
2
 
5
3
  const {assign} = Object;
6
4
  const defaultVariables = {btc: 1e8, k: 1e3, m: 1e6, mm: 1e6};
7
- const {keys} = Object;
8
5
 
9
6
  /** Determine if variables are consistent with filters
10
7
 
@@ -32,25 +29,14 @@ module.exports = ({filters, variables}) => {
32
29
 
33
30
  const vars = {};
34
31
 
35
- [defaultVariables, variables].forEach(n => assign(vars, n));
32
+ [defaultVariables, variables].forEach(variable => assign(vars, variable));
36
33
 
37
34
  const filtered = filters.map(formula => {
38
- const parser = new Parser();
39
-
40
- keys(vars).forEach(key => {
41
- parser.setVariable(key.toLowerCase(), vars[key]);
42
- parser.setVariable(key.toUpperCase(), vars[key]);
43
-
44
- return;
45
- });
46
-
47
- const parsed = parser.parse(formula);
48
-
49
- if (!!parsed.error) {
50
- return {formula, error: describeParseError({error: parsed.error})};
35
+ try {
36
+ return !evaluateFormula({constants: vars, formula}).result;
37
+ } catch (err) {
38
+ return {formula, error: err.message};
51
39
  }
52
-
53
- return parsed.result === false;
54
40
  });
55
41
 
56
42
  const [errored] = filtered.filter(n => !!n.error);
package/package.json CHANGED
@@ -13,6 +13,7 @@
13
13
  "@alexbosworth/blockchain": "3.2.0",
14
14
  "@alexbosworth/caporal": "2.0.0",
15
15
  "@alexbosworth/fiat": "2.0.0",
16
+ "@alexbosworth/formulas": "1.4.0",
16
17
  "@alexbosworth/html2unicode": "1.1.5",
17
18
  "@alexbosworth/node-fetch": "2.6.2",
18
19
  "abort-controller": "3.0.0",
@@ -29,19 +30,18 @@
29
30
  "colorette": "2.0.20",
30
31
  "crypto-js": "4.2.0",
31
32
  "csv-parse": "7.0.1",
32
- "goldengate": "16.0.3",
33
- "grammy": "1.44.0",
34
- "hot-formula-parser": "4.0.0",
33
+ "goldengate": "16.0.4",
34
+ "grammy": "1.45.1",
35
35
  "import-lazy": "4.0.0",
36
36
  "ini": "7.0.0",
37
37
  "inquirer": "14.0.2",
38
- "ln-accounting": "10.0.3",
39
- "ln-service": "59.2.0",
38
+ "ln-accounting": "10.0.4",
39
+ "ln-service": "59.2.2",
40
40
  "ln-sync": "8.0.3",
41
- "ln-telegram": "8.0.3",
41
+ "ln-telegram": "8.0.4",
42
42
  "minimist": "1.2.8",
43
43
  "moment": "2.30.1",
44
- "paid-services": "8.0.3",
44
+ "paid-services": "8.0.4",
45
45
  "probing": "7.0.0",
46
46
  "qrcode-terminal": "0.12.0",
47
47
  "sanitize-filename": "1.6.4",
@@ -78,7 +78,7 @@
78
78
  "integration-tests": "node test/integration",
79
79
  "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",
80
80
  "postpublish": "docker buildx build --platform linux/amd64,linux/arm64 -t alexbosworth/balanceofsatoshis -t alexbosworth/balanceofsatoshis:$npm_package_version --push .",
81
- "test": "npx nyc@17.1.0 node --experimental-test-coverage --test test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/telegram/*.js test/wallets/*.js"
81
+ "test": "node --experimental-test-coverage --test test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/telegram/*.js test/wallets/*.js"
82
82
  },
83
- "version": "22.1.7"
83
+ "version": "23.0.0"
84
84
  }
@@ -12,6 +12,7 @@ const tok = n => Number(BigInt(n) / BigInt(1e3));
12
12
 
13
13
  {
14
14
  channels: [{
15
+ capacity: <Channel Token Capacity Number>
15
16
  id: <Standard Format Channel Id String>
16
17
  local_balance: <Channel Local Balance Tokens Number>
17
18
  partner_public_key: <Peer Public Key Hex String>
@@ -97,20 +98,25 @@ module.exports = ({channels, filters, policies, tags, query}) => {
97
98
  const feeRates = peerPolicies.filter(n => n.fee_rate !== undefined);
98
99
  const pendingPayments = withPeer.map(n => n.pending_payments.length);
99
100
 
100
- const matching = isMatchingFilters({
101
- filters: filters || [],
102
- variables: {
103
- capacity: sumOf(withPeer.map(n => n.capacity)),
104
- heights: withPeer.map(n => {
105
- return decodeChanId({channel: n.id}).block_height;
106
- }),
107
- inbound_base_fee: max(...feeRates.map(n => tok(n.base_fee_mtokens))),
108
- inbound_fee_rate: max(...feeRates.map(n => n.fee_rate)),
109
- inbound_liquidity: sumOf(withPeer.map(n => n.remote_balance)),
110
- outbound_liquidity: sumOf(withPeer.map(n => n.local_balance)),
111
- pending_payments: sumOf(pendingPayments),
112
- },
113
- });
101
+ const maxBaseFee = max(...feeRates.map(n => tok(n.base_fee_mtokens)));
102
+ const maxFeeRate = max(...feeRates.map(n => n.fee_rate));
103
+
104
+ const variables = {
105
+ capacity: sumOf(withPeer.map(n => n.capacity)),
106
+ heights: withPeer.map(n => {
107
+ return decodeChanId({channel: n.id}).block_height;
108
+ }),
109
+ inbound_liquidity: sumOf(withPeer.map(n => n.remote_balance)),
110
+ outbound_liquidity: sumOf(withPeer.map(n => n.local_balance)),
111
+ pending_payments: sumOf(pendingPayments),
112
+ };
113
+
114
+ if (!!feeRates.length) {
115
+ variables.inbound_base_fee = maxBaseFee;
116
+ variables.inbound_fee_rate = maxFeeRate;
117
+ }
118
+
119
+ const matching = isMatchingFilters({variables, filters: filters || []});
114
120
 
115
121
  if (!!matching.failure) {
116
122
  return matching;
@@ -1,4 +1,4 @@
1
- const {Parser} = require('hot-formula-parser');
1
+ const {evaluateFormula} = require('@alexbosworth/formulas');
2
2
 
3
3
  const {isArray} = Array;
4
4
  const {keys} = Object;
@@ -72,7 +72,7 @@ module.exports = args => {
72
72
  throw new Error('ExpectedTorStatusToCheckOpenRequestRules');
73
73
  }
74
74
 
75
- const variables = {
75
+ const constants = {
76
76
  btc: 1e8,
77
77
  capacities: args.capacities,
78
78
  capacity: args.capacity,
@@ -84,46 +84,14 @@ module.exports = args => {
84
84
  local_balance: args.local_balance,
85
85
  m: 1e6,
86
86
  mm: 1e6,
87
- obsolete: args.is_obsolete,
87
+ obsolete: !!args.is_obsolete,
88
88
  private: args.is_private,
89
89
  public_key: args.public_key,
90
90
  tor: args.is_tor,
91
91
  };
92
92
 
93
- const parser = new Parser();
94
-
95
- // Add the variables to the parser
96
- keys(variables).forEach(key => {
97
- parser.setVariable(key.toLowerCase(), variables[key]);
98
- parser.setVariable(key.toUpperCase(), variables[key]);
99
-
100
- return;
101
- });
102
-
103
93
  const violation = args.rules.find(rule => {
104
- const parsed = parser.parse(rule);
105
-
106
- switch (parsed.error) {
107
- case '#DIV/0!':
108
- throw new Error('CannotDivideByZeroInOpenRequestRule');
109
-
110
- case '#ERROR!':
111
- throw new Error('FailedToParseSpecifiedOpenRequestRule');
112
-
113
- case '#N/A':
114
- case '#NAME?':
115
- throw new Error('UnrecognizedVariableOrFunctionInRequestRule');
116
-
117
- case '#NUM':
118
- throw new Error('InvalidNumberFoundInRequestRule');
119
-
120
- case '#VALUE!':
121
- throw new Error('UnexpectedValueTypeInRequestRule');
122
-
123
- default:
124
- // Rules must evaluate as truthy
125
- return !parsed.result;
126
- }
94
+ return !evaluateFormula({constants, formula: rule}).result;
127
95
  });
128
96
 
129
97
  return {rule: violation};
@@ -1,18 +1,15 @@
1
1
  const asyncAuto = require('async/auto');
2
2
  const asyncMap = require('async/map');
3
+ const {evaluateFormula} = require('@alexbosworth/formulas');
3
4
  const {findKey} = require('ln-sync');
4
5
  const {getChannel} = require('ln-service');
5
6
  const {getHeight} = require('ln-service');
6
7
  const {getNode} = require('ln-service');
7
- const {Parser} = require('hot-formula-parser');
8
8
  const {returnResult} = require('asyncjs-util');
9
9
 
10
- const {describeParseError} = require('./../display');
11
-
12
10
  const amountVariables = {btc: 1e8, k: 1e3, m: 1e6, mm: 1e6};
13
11
  const asFormula = n => ({formula: n.slice(0, n.length-67), key: n.slice(-66)});
14
12
  const asOutFilter = n => ({out_filter: n.slice(67), key: n.slice(0, 66)});
15
- const {assign} = Object;
16
13
  const channelFromEdge = edge => edge.slice(0, -2);
17
14
  const decodePair = n => n.split('/');
18
15
  const flatten = arr => [].concat(...arr);
@@ -25,7 +22,6 @@ const isFormula = n => /(.*)\/0[2-3][0-9A-F]{64}$/gim.test(n);
25
22
  const isOutFilter = n => /^0[2-3][0-9A-F]{64}\/(.*)/gim.test(n);
26
23
  const isPair = n => !!n && /^0[2-3][0-9A-F]{64}\/0[2-3][0-9A-F]{64}$/i.test(n);
27
24
  const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/gim.test(n);
28
- const {keys} = Object;
29
25
  const pairAsIgnore = (a, b) => ({from_public_key: a, to_public_key: b});
30
26
  const uniq = arr => Array.from(new Set(arr));
31
27
 
@@ -255,35 +251,25 @@ module.exports = (args, cbk) => {
255
251
  return;
256
252
  }
257
253
 
258
- const parser = new Parser();
259
- const variables = {};
260
-
261
- assign(variables, amountVariables);
262
-
263
- assign(variables, {
264
- capacity,
265
- height,
266
- age: getHeight.current_block_height - height,
267
- base_fee: Number(outPolicy.base_fee_mtokens) || Number(),
268
- fee_rate: outPolicy.fee_rate || Number(),
269
- opposite_fee_rate: peerPolicy.fee_rate || Number(),
270
- });
271
-
272
- keys(variables).forEach(key => {
273
- parser.setVariable(key.toLowerCase(), variables[key]);
274
- parser.setVariable(key.toUpperCase(), variables[key]);
275
-
276
- return;
277
- });
278
-
279
- const parsed = parser.parse(formula);
280
-
281
- if (!!parsed.error) {
282
- return {error: describeParseError({error: parsed.error})};
283
- }
284
-
285
- if (parsed.result === false) {
286
- return;
254
+ try {
255
+ const parsed = evaluateFormula({
256
+ formula,
257
+ constants: {
258
+ ...amountVariables,
259
+ capacity,
260
+ height,
261
+ age: getHeight.current_block_height - height,
262
+ base_fee: Number(outPolicy.base_fee_mtokens) || Number(),
263
+ fee_rate: outPolicy.fee_rate || Number(),
264
+ opposite_fee_rate: peerPolicy.fee_rate || Number(),
265
+ },
266
+ });
267
+
268
+ if (!parsed.result) {
269
+ return;
270
+ }
271
+ } catch (err) {
272
+ return {error: err.message};
287
273
  }
288
274
 
289
275
  return {
@@ -333,35 +319,25 @@ module.exports = (args, cbk) => {
333
319
  return;
334
320
  }
335
321
 
336
- const parser = new Parser();
337
- const variables = {};
338
-
339
- assign(variables, amountVariables);
340
-
341
- assign(variables, {
342
- capacity,
343
- height,
344
- age: getHeight.current_block_height - height,
345
- base_fee: Number(inPolicy.base_fee_mtokens) || Number(),
346
- fee_rate: inPolicy.fee_rate || Number(),
347
- opposite_fee_rate: outPolicy.fee_rate || Number(),
348
- });
349
-
350
- keys(variables).forEach(key => {
351
- parser.setVariable(key.toLowerCase(), variables[key]);
352
- parser.setVariable(key.toUpperCase(), variables[key]);
353
-
354
- return;
355
- });
356
-
357
- const parsed = parser.parse(formula);
358
-
359
- if (!!parsed.error) {
360
- return {error: describeParseError({error: parsed.error})};
361
- }
362
-
363
- if (parsed.result === false) {
364
- return;
322
+ try {
323
+ const parsed = evaluateFormula({
324
+ formula,
325
+ constants: {
326
+ ...amountVariables,
327
+ capacity,
328
+ height,
329
+ age: getHeight.current_block_height - height,
330
+ base_fee: Number(inPolicy.base_fee_mtokens) || Number(),
331
+ fee_rate: inPolicy.fee_rate || Number(),
332
+ opposite_fee_rate: outPolicy.fee_rate || Number(),
333
+ },
334
+ });
335
+
336
+ if (!parsed.result) {
337
+ return;
338
+ }
339
+ } catch (err) {
340
+ return {error: err.message};
365
341
  }
366
342
 
367
343
  return {
@@ -1,4 +1,4 @@
1
- const {Parser} = require('hot-formula-parser');
1
+ const {evaluateFormula} = require('@alexbosworth/formulas');
2
2
 
3
3
  const bipsAsPpm = bips => bips * 1e2;
4
4
  const {ceil} = Math;
@@ -12,7 +12,7 @@ const percentAsPpm = percent => percent * 1e4;
12
12
  inbound_liquidity: <Inbound Tokens Number>
13
13
  outbound_liquidity: <Outbound Tokens Number>
14
14
  node_rates: [{
15
- key: <Node Key String>
15
+ key: <Label Prefixed Node Key String>
16
16
  rate: <Node PPM Rate Number>
17
17
  }]
18
18
  }
@@ -28,31 +28,30 @@ module.exports = args => {
28
28
  return {};
29
29
  }
30
30
 
31
- const parser = new Parser();
32
-
33
- parser.setFunction('BIPS', params => bipsAsPpm(params.slice().pop()));
34
- parser.setFunction('PERCENT', params => percentAsPpm(params.slice().pop()));
35
- parser.setVariable('INBOUND', args.inbound_liquidity);
36
- parser.setVariable('INBOUND_FEE_RATE', args.inbound_fee_rate);
37
- parser.setVariable('OUTBOUND', args.outbound_liquidity);
38
-
39
- args.node_rates.forEach(({key, rate}) => parser.setVariable(key, rate));
40
-
41
- const parsedRate = parser.parse(args.fee_rate.toUpperCase());
42
-
43
- switch (parsedRate.error) {
44
- case null:
45
- break;
46
-
47
- case '#DIV/0!':
48
- return {failure: 'FeeRateCalculationCannotDivideByZeroFormula'};
49
-
50
- case '#ERROR!':
51
- return {failure: 'FailedToParseFeeRateFormula'};
52
-
53
- default:
54
- return {failure: 'UnrecognizedVariableOrFunctionInFeeRateFormula'};
31
+ const rates = args.node_rates.reduce((sum, n) => {
32
+ sum[n.key] = n.rate;
33
+
34
+ return sum;
35
+ },
36
+ {});
37
+
38
+ try {
39
+ const {result} = evaluateFormula({
40
+ constants: {
41
+ ...rates,
42
+ inbound: args.inbound_liquidity,
43
+ inbound_fee_rate: args.inbound_fee_rate,
44
+ outbound: args.outbound_liquidity,
45
+ },
46
+ formula: args.fee_rate,
47
+ functions: {
48
+ bips: bipsAsPpm,
49
+ percent: percentAsPpm,
50
+ },
51
+ });
52
+
53
+ return {rate: ceil(result)};
54
+ } catch (err) {
55
+ return {failure: err.message};
55
56
  }
56
-
57
- return {rate: ceil(parsedRate.result)};
58
57
  };
@@ -15,7 +15,7 @@ const tests = [
15
15
  description: 'An invalid filter returns a failure',
16
16
  expected: {
17
17
  failure: {
18
- error: 'UnrecognizedVariableOrFunctionInFormula',
18
+ error: 'ExpectedAllKnownConstantsInFormulaToEvaluate',
19
19
  formula: 'foo',
20
20
  },
21
21
  },
@@ -7,6 +7,7 @@ const {findTagMatch} = require('./../../peers');
7
7
  const makeArgs = overrides => {
8
8
  const args = {
9
9
  channels: [{
10
+ capacity: 3,
10
11
  id: '1x1x1',
11
12
  local_balance: 1,
12
13
  partner_public_key: Buffer.alloc(33, 3).toString('hex'),
@@ -36,13 +37,17 @@ const tests = [
36
37
  args: makeArgs({filters: ['invalid formula']}),
37
38
  description: 'A failed formula is provided',
38
39
  expected: {
39
- failure: {error: 'FailedToParseFormula', formula: 'invalid formula'},
40
+ failure: {
41
+ error: 'UnexpectedTrailingTokenForFormulaParsing',
42
+ formula: 'invalid formula',
43
+ },
40
44
  },
41
45
  },
42
46
  {
43
47
  args: makeArgs({
44
48
  channels: [
45
49
  {
50
+ capacity: 2e8,
46
51
  id: '1x1x1',
47
52
  local_balance: 1e3,
48
53
  partner_public_key: Buffer.alloc(33, 3).toString('hex'),
@@ -50,6 +55,7 @@ const tests = [
50
55
  remote_balance: 1,
51
56
  },
52
57
  {
58
+ capacity: 2e8,
53
59
  id: '2x2x2',
54
60
  local_balance: 1e8,
55
61
  partner_public_key: Buffer.alloc(33, 2).toString('hex'),
@@ -42,17 +42,17 @@ const tests = [
42
42
  {
43
43
  args: makeArgs({fee_rate: '1/0'}),
44
44
  description: 'Cannot divide by zero',
45
- expected: {failure: 'FeeRateCalculationCannotDivideByZeroFormula'},
45
+ expected: {failure: 'ExpectedNonZeroDivisorForFormulaEvaluation'},
46
46
  },
47
47
  {
48
48
  args: makeArgs({fee_rate: '/'}),
49
49
  description: 'Formula must be valid',
50
- expected: {failure: 'FailedToParseFeeRateFormula'},
50
+ expected: {failure: 'UnexpectedPrimaryTokenForFormulaParsing'},
51
51
  },
52
52
  {
53
53
  args: makeArgs({fee_rate: 'fee_rate'}),
54
54
  description: 'Formula must be valid',
55
- expected: {failure: 'UnrecognizedVariableOrFunctionInFeeRateFormula'},
55
+ expected: {failure: 'ExpectedAllKnownConstantsInFormulaToEvaluate'},
56
56
  },
57
57
  ];
58
58
 
@@ -1,16 +0,0 @@
1
- const {parseErrors} = require('./constants');
2
-
3
- const defaultError = '#ERROR!';
4
-
5
- /** Describe a hot formula parser error
6
-
7
- {
8
- error: <Error Type String>
9
- }
10
-
11
- @returns
12
- <Display String>
13
- */
14
- module.exports = ({error}) => {
15
- return parseErrors[error] || parseErrors[defaultError];
16
- };