@hackerhouse/xpub-scan 1.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.
Files changed (83) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/CONTRIBUTING.md +130 -0
  3. package/LICENSE +23 -0
  4. package/README.md +215 -0
  5. package/__tests__/checkAddresses/checkBitcoinAddressesNegative.test.ts +75 -0
  6. package/__tests__/checkAddresses/checkBitcoinAddressesPositive.test.ts +87 -0
  7. package/__tests__/checkAddresses/checkDogeAddressesPositive.test.ts +43 -0
  8. package/__tests__/checkAddresses/checkLitecoinAddressesNegative.test.ts +75 -0
  9. package/__tests__/checkAddresses/checkLitecoinAddressesPositive.test.ts +87 -0
  10. package/__tests__/checkModels/address.test.ts +183 -0
  11. package/__tests__/checkModels/fakeRawTransactions.json +182 -0
  12. package/__tests__/deriveAddresses/deriveBitcoinAddresses.test.ts +207 -0
  13. package/__tests__/deriveAddresses/deriveBitcoinCashAddresses.test copy.ts +79 -0
  14. package/__tests__/deriveAddresses/deriveDogecoinAddresses.test.ts +43 -0
  15. package/__tests__/deriveAddresses/deriveEthereumAddresses.test.ts +26 -0
  16. package/__tests__/deriveAddresses/deriveLitecoinAddresses.test.ts +110 -0
  17. package/__tests__/helpers.test.ts +274 -0
  18. package/__tests__/test-utils.ts +3 -0
  19. package/babel.config.js +6 -0
  20. package/jest.config.ts +5 -0
  21. package/ledgerhq-xpub-scan-1.0.4.tgz +0 -0
  22. package/lib/actions/checkAddress.d.ts +29 -0
  23. package/lib/actions/checkAddress.js +122 -0
  24. package/lib/actions/checkBalance.d.ts +20 -0
  25. package/lib/actions/checkBalance.js +300 -0
  26. package/lib/actions/deriveAddresses.d.ts +17 -0
  27. package/lib/actions/deriveAddresses.js +239 -0
  28. package/lib/actions/processTransactions.d.ts +29 -0
  29. package/lib/actions/processTransactions.js +289 -0
  30. package/lib/actions/saveAnalysis.d.ts +2 -0
  31. package/lib/actions/saveAnalysis.js +800 -0
  32. package/lib/actions/scanner.d.ts +15 -0
  33. package/lib/actions/scanner.js +152 -0
  34. package/lib/api/customProvider.d.ts +19 -0
  35. package/lib/api/customProvider.js +434 -0
  36. package/lib/api/defaultProvider.d.ts +23 -0
  37. package/lib/api/defaultProvider.js +275 -0
  38. package/lib/comparison/compareOperations.d.ts +13 -0
  39. package/lib/comparison/compareOperations.js +500 -0
  40. package/lib/comparison/diffs.d.ts +18 -0
  41. package/lib/comparison/diffs.js +70 -0
  42. package/lib/configuration/currencies.d.ts +55 -0
  43. package/lib/configuration/currencies.js +72 -0
  44. package/lib/configuration/settings.d.ts +51 -0
  45. package/lib/configuration/settings.js +113 -0
  46. package/lib/display.d.ts +12 -0
  47. package/lib/display.js +251 -0
  48. package/lib/helpers.d.ts +27 -0
  49. package/lib/helpers.js +255 -0
  50. package/lib/input/args.d.ts +6 -0
  51. package/lib/input/args.js +129 -0
  52. package/lib/input/check.d.ts +6 -0
  53. package/lib/input/check.js +217 -0
  54. package/lib/input/importOperations.d.ts +11 -0
  55. package/lib/input/importOperations.js +406 -0
  56. package/lib/models/address.d.ts +40 -0
  57. package/lib/models/address.js +101 -0
  58. package/lib/models/comparison.d.ts +8 -0
  59. package/lib/models/comparison.js +6 -0
  60. package/lib/models/currency.d.ts +11 -0
  61. package/lib/models/currency.js +6 -0
  62. package/lib/models/operation.d.ts +33 -0
  63. package/lib/models/operation.js +80 -0
  64. package/lib/models/ownAddresses.d.ts +11 -0
  65. package/lib/models/ownAddresses.js +31 -0
  66. package/lib/models/scanLimits.d.ts +7 -0
  67. package/lib/models/scanLimits.js +6 -0
  68. package/lib/models/stats.d.ts +7 -0
  69. package/lib/models/stats.js +6 -0
  70. package/lib/models/transaction.d.ts +10 -0
  71. package/lib/models/transaction.js +13 -0
  72. package/lib/scan.d.ts +2 -0
  73. package/lib/scan.js +31 -0
  74. package/lib/templates/logos.base64.d.ts +2 -0
  75. package/lib/templates/logos.base64.js +9 -0
  76. package/lib/templates/report.html.d.ts +1 -0
  77. package/lib/templates/report.html.js +393 -0
  78. package/lib/tsconfig.tsbuildinfo +1 -0
  79. package/lib/types.d.ts +55 -0
  80. package/lib/types.js +2 -0
  81. package/npm-shrinkwrap.json +12323 -0
  82. package/package.json +81 -0
  83. package/sonar-project.properties +15 -0
package/lib/helpers.js ADDED
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.toAccountUnit = exports.toBaseUnit = exports.toUnprefixedCashAddress = exports.setNetwork = exports.retry = exports.getNetworkLabel = exports.getJSON = exports.init = void 0;
39
+ const axios_1 = __importDefault(require("axios"));
40
+ const chalk_1 = __importDefault(require("chalk"));
41
+ const bchaddrjs_1 = __importDefault(require("bchaddrjs"));
42
+ const settings_1 = require("./configuration/settings");
43
+ const currencies_1 = require("./configuration/currencies");
44
+ const bip32_1 = __importDefault(require("bip32"));
45
+ const ecc = __importStar(require("tiny-secp256k1"));
46
+ const bip32 = (0, bip32_1.default)(ecc);
47
+ function getJSON(url, APIKey, { retries, retryDelayMS } = {}) {
48
+ return __awaiter(this, void 0, void 0, function* () {
49
+ const job = () => __awaiter(this, void 0, void 0, function* () {
50
+ const headers = Object.assign({}, (APIKey ? { "X-API-Key": APIKey } : {}));
51
+ const res = yield axios_1.default.get(url, { headers });
52
+ if (res.status !== 200) {
53
+ console.log(chalk_1.default.red("GET request error"));
54
+ throw new Error("GET REQUEST ERROR: "
55
+ .concat(url)
56
+ .concat(", Status Code: ")
57
+ .concat(String(res.status)));
58
+ }
59
+ return res.data;
60
+ });
61
+ return retry(job, { retries, retryDelayMS });
62
+ });
63
+ }
64
+ exports.getJSON = getJSON;
65
+ function retry(job, { retries = 5, retryDelayMS = 0 } = {}) {
66
+ return __awaiter(this, void 0, void 0, function* () {
67
+ let err = null;
68
+ for (let i = 0; i < retries; i++) {
69
+ try {
70
+ return yield job();
71
+ }
72
+ catch (e) {
73
+ err = e;
74
+ // wait before retrying if it's not the last try
75
+ if (retryDelayMS && i < retries - 1) {
76
+ yield new Promise((r) => setTimeout(r, retryDelayMS));
77
+ }
78
+ }
79
+ }
80
+ if (err)
81
+ throw err;
82
+ throw new Error(`No result after ${retries} retries`);
83
+ });
84
+ }
85
+ exports.retry = retry;
86
+ function setNetwork(xpub, currency, testnet) {
87
+ settings_1.configuration.testnet = testnet || false;
88
+ if (typeof currency === "undefined" ||
89
+ currency === "BTC" ||
90
+ currency === "LTC" ||
91
+ currency === "DOGE") {
92
+ const prefix = xpub.substring(0, 4).toLocaleLowerCase();
93
+ if (prefix === "xpub") {
94
+ // Bitcoin mainnet
95
+ settings_1.configuration.currency = currencies_1.currencies.btc;
96
+ settings_1.configuration.currency.network = currencies_1.currencies.btc.network_mainnet;
97
+ }
98
+ else if (prefix === "tpub") {
99
+ // Bitcoin testnet
100
+ settings_1.configuration.currency = currencies_1.currencies.btc;
101
+ settings_1.configuration.currency.network = currencies_1.currencies.btc.network_testnet;
102
+ settings_1.configuration.testnet = true;
103
+ }
104
+ else if (prefix === "ltub") {
105
+ // Litecoin
106
+ settings_1.configuration.currency = currencies_1.currencies.ltc;
107
+ // TODO: LTC testnet
108
+ settings_1.configuration.currency.network = currencies_1.currencies.ltc.network_mainnet;
109
+ }
110
+ else if (prefix === "dgub") {
111
+ // Dogecoin
112
+ settings_1.configuration.currency = currencies_1.currencies.doge;
113
+ settings_1.configuration.currency.network = currencies_1.currencies.doge.network_mainnet;
114
+ }
115
+ else {
116
+ throw new Error("INVALID XPUB: " + xpub + " has not a valid prefix");
117
+ }
118
+ }
119
+ else {
120
+ // Bitcoin Cash
121
+ if (currency === "BCH") {
122
+ settings_1.configuration.currency = currencies_1.currencies.bch;
123
+ // TODO: BCH testnet
124
+ settings_1.configuration.currency.network = currencies_1.currencies.bch.network_mainnet;
125
+ return;
126
+ }
127
+ // Ethereum
128
+ else if (currency === "ETH") {
129
+ settings_1.configuration.currency = currencies_1.currencies.eth;
130
+ return;
131
+ }
132
+ throw new Error("INVALID CURRENCY: '" + currency + "' is not supported");
133
+ }
134
+ }
135
+ exports.setNetwork = setNetwork;
136
+ /**
137
+ * Configure the external provider URL (i.e., default v. Crypto APIs provider)
138
+ * @param {string} currency?
139
+ * Symbol of the currency (e.g. 'BCH')
140
+ * @returns void
141
+ */
142
+ const setExternalProviderURL = () => {
143
+ // custom provider (i.e., API key is set)
144
+ if (process.env.XPUB_SCAN_CUSTOM_API_KEY_V2) {
145
+ settings_1.configuration.externalProviderURL = settings_1.CRYPTOAPIS_URL.replace("{network}", getNetworkLabel());
146
+ settings_1.configuration.providerType = "Crypto APIs";
147
+ return;
148
+ }
149
+ // default provider
150
+ const currency = settings_1.configuration.currency;
151
+ if (currency.symbol === currencies_1.currencies.btc.symbol ||
152
+ currency.symbol === currencies_1.currencies.ltc.symbol ||
153
+ currency.symbol === currencies_1.currencies.doge.symbol) {
154
+ settings_1.configuration.externalProviderURL = settings_1.DEFAULT_API_URLS.general;
155
+ return;
156
+ }
157
+ if (currency.symbol === currencies_1.currencies.bch.symbol) {
158
+ settings_1.configuration.externalProviderURL = settings_1.DEFAULT_API_URLS.bch;
159
+ return;
160
+ }
161
+ if (currency.symbol === currencies_1.currencies.eth.symbol) {
162
+ settings_1.configuration.externalProviderURL = settings_1.DEFAULT_API_URLS.eth;
163
+ }
164
+ };
165
+ function checkXpub(xpub) {
166
+ try {
167
+ bip32.fromBase58(xpub, settings_1.configuration.currency.network);
168
+ }
169
+ catch (e) {
170
+ throw new Error("INVALID XPUB: " + xpub + " is not a valid xpub -- " + e);
171
+ }
172
+ }
173
+ function init(xpub, silent, quiet, currency, testnet, derivationMode) {
174
+ if (typeof silent !== "undefined") {
175
+ settings_1.configuration.silent = silent;
176
+ }
177
+ if (typeof quiet !== "undefined") {
178
+ settings_1.configuration.quiet = quiet;
179
+ }
180
+ setNetwork(xpub, currency, testnet);
181
+ setExternalProviderURL();
182
+ if (settings_1.configuration.currency.utxo_based) {
183
+ checkXpub(xpub);
184
+ }
185
+ settings_1.configuration.specificDerivationMode = derivationMode;
186
+ if (settings_1.configuration.silent) {
187
+ return;
188
+ }
189
+ console.log(chalk_1.default.grey("(Data fetched from the "
190
+ .concat(chalk_1.default.bold(settings_1.configuration.providerType))
191
+ .concat(" provider)")));
192
+ }
193
+ exports.init = init;
194
+ // remove prefixes (`bitcoincash:`) from Bitcoin Cash addresses
195
+ function toUnprefixedCashAddress(address) {
196
+ if (settings_1.configuration.currency.symbol !== currencies_1.currencies.bch.symbol) {
197
+ return undefined;
198
+ }
199
+ if (!bchaddrjs_1.default.isCashAddress(address)) {
200
+ address = bchaddrjs_1.default.toCashAddress(address);
201
+ }
202
+ return address.replace("bitcoincash:", "");
203
+ }
204
+ exports.toUnprefixedCashAddress = toUnprefixedCashAddress;
205
+ /**
206
+ * Convert from unit of account to base unit (e.g. bitcoins to satoshis)
207
+ * @param amount the amount (in unit of account) to convert
208
+ * @returns the converted amount, in base unit
209
+ */
210
+ function toBaseUnit(amount) {
211
+ if (amount.isZero()) {
212
+ return amount.toFixed(0);
213
+ }
214
+ const convertedAmount = amount.times(settings_1.configuration.currency.precision);
215
+ return convertedAmount.toFixed(0);
216
+ }
217
+ exports.toBaseUnit = toBaseUnit;
218
+ /**
219
+ * Convert from base unit to unit of account (e.g. satoshis to bitcoins)
220
+ * @param amount the amount (in base unit) to convert
221
+ * @param decimalPlaces (optional) decimal precision
222
+ * @returns the converted amount, in unit of account
223
+ */
224
+ function toAccountUnit(amount, decimalPlaces) {
225
+ if (amount.isZero()) {
226
+ return amount.toFixed();
227
+ }
228
+ let convertedValue;
229
+ if (settings_1.configuration.currency.symbol === currencies_1.currencies.eth.symbol) {
230
+ return (amount.toNumber() / settings_1.configuration.currency.precision).toFixed(settings_1.ETH_FIXED_PRECISION);
231
+ }
232
+ else {
233
+ convertedValue = amount.dividedBy(settings_1.configuration.currency.precision);
234
+ if (typeof decimalPlaces !== "undefined" && decimalPlaces) {
235
+ return convertedValue.toFixed(decimalPlaces);
236
+ }
237
+ }
238
+ return convertedValue.toFixed();
239
+ }
240
+ exports.toAccountUnit = toAccountUnit;
241
+ function getNetworkLabel() {
242
+ if (settings_1.configuration.testnet) {
243
+ if (settings_1.configuration.currency.symbol === currencies_1.currencies.eth.symbol &&
244
+ typeof settings_1.configuration.APIKey !== "undefined") {
245
+ return "ropsten";
246
+ }
247
+ else {
248
+ return "testnet";
249
+ }
250
+ }
251
+ else {
252
+ return "mainnet";
253
+ }
254
+ }
255
+ exports.getNetworkLabel = getNetworkLabel;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Returns the valid args entered by the user
3
+ * @returns any
4
+ * The validated args
5
+ */
6
+ export declare const getArgs: () => any;
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getArgs = void 0;
7
+ const yargs_1 = __importDefault(require("yargs"));
8
+ const check_1 = require("./check");
9
+ /**
10
+ * Returns the valid args entered by the user
11
+ * @returns any
12
+ * The validated args
13
+ */
14
+ const getArgs = () => {
15
+ const args = yargs_1.default
16
+ // primary options
17
+ .option("currency", {
18
+ description: "currency",
19
+ demand: false,
20
+ type: "string",
21
+ })
22
+ .option("testnet", {
23
+ description: "testnet",
24
+ demand: false,
25
+ type: "boolean",
26
+ default: false,
27
+ })
28
+ .option("account", {
29
+ alias: "a",
30
+ description: "Account number",
31
+ demand: false,
32
+ type: "number",
33
+ })
34
+ .option("index", {
35
+ alias: "i",
36
+ description: "Index number",
37
+ demand: false,
38
+ type: "number",
39
+ })
40
+ .option("from-index", {
41
+ description: "ScanLimits: FROM index X",
42
+ demand: false,
43
+ type: "number",
44
+ })
45
+ .option("to-index", {
46
+ description: "ScanLimits: TO index Y",
47
+ demand: false,
48
+ type: "number",
49
+ })
50
+ .option("pre-derivation-size", {
51
+ description: "ScanLimits: number of pre-derived addresses per account",
52
+ demand: false,
53
+ type: "number",
54
+ })
55
+ .option("address", {
56
+ description: "Address",
57
+ demand: false,
58
+ type: "string",
59
+ })
60
+ .option("derivation-mode", {
61
+ description: "Select specific derivation mode (legacy, SegWit, etc.)",
62
+ demand: false,
63
+ type: "string",
64
+ })
65
+ // stdout options
66
+ .option("silent", {
67
+ description: "Do not display anything (except for the filepath of the saved reports)",
68
+ demand: false,
69
+ type: "boolean",
70
+ default: false,
71
+ })
72
+ .option("quiet", {
73
+ description: "Do not display analysis progress",
74
+ demand: false,
75
+ type: "boolean",
76
+ default: false,
77
+ })
78
+ .option("diff", {
79
+ description: "Show diffs",
80
+ demand: false,
81
+ type: "boolean",
82
+ })
83
+ .option("addresses", {
84
+ description: "Import addresses (file) for comparison",
85
+ demand: false,
86
+ type: "string",
87
+ })
88
+ .option("balance", {
89
+ description: "Import balance for comparison (has to be in base unit) for comparison",
90
+ demand: false,
91
+ type: "string",
92
+ })
93
+ .option("balance-only", {
94
+ description: "Do not fetch operations, only balance of the account (utxo-list for btc)",
95
+ demand: false,
96
+ type: "boolean",
97
+ default: false,
98
+ })
99
+ .option("utxos", {
100
+ description: "Import UTXOs (file) for comparison",
101
+ demand: false,
102
+ type: "string",
103
+ })
104
+ .option("operations", {
105
+ description: "Import operations history (file) for comparison",
106
+ demand: false,
107
+ type: "string",
108
+ })
109
+ // save
110
+ .option("save", {
111
+ description: "Save analysis",
112
+ demand: false,
113
+ type: "string",
114
+ })
115
+ .option("custom-provider", {
116
+ description: "Require the use of the custom provider",
117
+ demand: false,
118
+ type: "boolean",
119
+ default: false,
120
+ })
121
+ .option("block-height-limit", {
122
+ description: "Block height limit",
123
+ demand: false,
124
+ type: "number",
125
+ }).argv;
126
+ (0, check_1.checkArgs)(args, process.argv);
127
+ return args;
128
+ };
129
+ exports.getArgs = getArgs;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Ensure that args are valid
3
+ * @param {any} args
4
+ * @returns void
5
+ */
6
+ export declare const checkArgs: (args: any, argv: Array<string>) => void;
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkArgs = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const currencies_1 = require("../configuration/currencies");
10
+ const settings_1 = require("../configuration/settings");
11
+ /**
12
+ * Ensure that args are valid
13
+ * @param {any} args
14
+ * @returns void
15
+ */
16
+ const checkArgs = (args, argv) => {
17
+ // (important) Command line mode: enable output
18
+ settings_1.configuration.commandLineMode = true;
19
+ args.itemToScan = args._[0];
20
+ if (Number(args.itemToScan)) {
21
+ args.itemToScan = argv[2];
22
+ }
23
+ const itemToScan = args.itemToScan;
24
+ const testnet = args.testnet;
25
+ const address = args.address;
26
+ const balance = args.balance;
27
+ const save = args.save;
28
+ const currency = args.currency;
29
+ const derivationMode = args.derivationMode;
30
+ const account = args.account;
31
+ const index = args.index;
32
+ const fromIndex = args.fromIndex;
33
+ const toIndex = args.toIndex;
34
+ const preDerivationSize = args.preDerivationSize;
35
+ const blockHeightLimit = args.blockHeightLimit;
36
+ // xpub: set, non-empty
37
+ if (typeof itemToScan === "undefined" || itemToScan === "") {
38
+ throw new Error("Xpub or address is required");
39
+ }
40
+ // address: non-empty
41
+ if (typeof address !== "undefined" && address === "") {
42
+ throw new Error("Address should not be empty");
43
+ }
44
+ // imported balance: integer (i.e., base unit)
45
+ if (typeof balance !== "undefined") {
46
+ if (balance % 1 !== 0) {
47
+ throw new Error("Balance is not an integer: " + balance);
48
+ }
49
+ }
50
+ if (args.balanceOnly && args.operations) {
51
+ throw new Error("You cannot pass an operation file in --balance-only mode");
52
+ }
53
+ // currency: exists
54
+ if (typeof currency !== "undefined") {
55
+ args.currency = args.currency.toUpperCase();
56
+ const currencyProperties = Object.entries(currencies_1.currencies).filter((c) => c[1].symbol.toUpperCase() === args.currency.toUpperCase());
57
+ if (currencyProperties.length === 0) {
58
+ throw new Error("Currency '" + currency + "' has not been implemented yet");
59
+ }
60
+ }
61
+ // derivation mode: compatible with (implicitly) selected currency
62
+ if (typeof derivationMode !== "undefined") {
63
+ let availableDerivationModes = [];
64
+ if (typeof currency !== "undefined") {
65
+ // if currency is defined, explicitly use its derivation modes
66
+ const configuredCurrency = Object.entries(currencies_1.currencies)
67
+ .filter((c) => c[1].symbol.toUpperCase() === args.currency.toUpperCase())
68
+ .map((c) => {
69
+ return c[1];
70
+ })[0];
71
+ // implementation note: this complex way of performing this verification
72
+ // is due to the fact that derivationModes is optional...
73
+ for (const c of Object.entries(configuredCurrency)) {
74
+ if (c[0] === "derivationModes") {
75
+ availableDerivationModes = c[1];
76
+ }
77
+ }
78
+ }
79
+ else {
80
+ // if currency is not defined, implicitly use BTC's derivation modes
81
+ availableDerivationModes = currencies_1.currencies.btc.derivationModes;
82
+ }
83
+ if (availableDerivationModes.filter((d) => d.toLocaleLowerCase().startsWith(derivationMode.toLocaleLowerCase())).length === 0) {
84
+ throw new Error("Selected derivation mode " +
85
+ derivationMode +
86
+ " is not compatible with selected currency");
87
+ }
88
+ }
89
+ // warnings: not implemented yet
90
+ if (typeof args.addresses !== "undefined") {
91
+ console.log(chalk_1.default.bgYellowBright.black(" Warning: `--addresses` option has not been implemented yet. Skipped. "));
92
+ }
93
+ if (typeof args.utxos !== "undefined") {
94
+ console.log(chalk_1.default.bgYellowBright.black(" Warning: `--utxos` option has not been implemented yet. Skipped. "));
95
+ }
96
+ // imported files: non-empty, exist
97
+ const importedFiles = [args.addresses, args.utxos, args.operations];
98
+ for (const importedFile of importedFiles) {
99
+ if (typeof importedFile !== "undefined") {
100
+ if (importedFile === "" || !fs_1.default.existsSync(importedFile)) {
101
+ throw new Error("Imported file " + importedFile + " does not exist");
102
+ }
103
+ }
104
+ }
105
+ if (blockHeightLimit) {
106
+ if (!args.operations) {
107
+ throw new Error("The block height limit can only be used in comparison mode");
108
+ }
109
+ if (blockHeightLimit < 0) {
110
+ throw new Error("The block height limit cannot be negative");
111
+ }
112
+ settings_1.configuration.blockHeightUpperLimit = blockHeightLimit;
113
+ }
114
+ // save dirpath: exists, is a directory, writable
115
+ if (typeof save !== "undefined" && save.toLocaleLowerCase() !== "stdout") {
116
+ try {
117
+ if (!fs_1.default.statSync(save).isDirectory()) {
118
+ throw new Error("Save path " + save + " is not a directory");
119
+ }
120
+ }
121
+ catch (_a) {
122
+ throw new Error("Save path " + save + " does not exist");
123
+ }
124
+ fs_1.default.access(save, fs_1.default.constants.W_OK, function (err) {
125
+ if (err) {
126
+ throw new Error("Save directory " + save + " is not writable");
127
+ }
128
+ });
129
+ }
130
+ // account/index/scanLimits options
131
+ if (typeof account !== "undefined") {
132
+ // -a {positive number}
133
+ if (account < 0) {
134
+ throw new Error("Account number is required to be positive (including zero)");
135
+ }
136
+ // -a X -i Y or -a X --from-index Y [--to-index Z]
137
+ if (typeof index === "undefined" && typeof fromIndex === "undefined") {
138
+ throw new Error("Index or scanLimits is required when account number option (`-a`) is enabled");
139
+ }
140
+ // -a X -i {positive number}
141
+ if (typeof index !== "undefined" && index < 0) {
142
+ throw new Error("Index number is required to be positive (including zero)");
143
+ }
144
+ if (typeof fromIndex !== "undefined") {
145
+ // -a X --from-index {postive number} [--to-index {postive number}]
146
+ if (fromIndex < 0) {
147
+ throw new Error("`--from-index` option is required to be positive (including zero)");
148
+ }
149
+ if (typeof toIndex !== "undefined") {
150
+ if (toIndex < 0) {
151
+ throw new Error("`--to-index` option is required to be positive");
152
+ }
153
+ // -a X --from-index Y --to-index Z | Y <= Z
154
+ if (fromIndex > toIndex) {
155
+ throw new Error("`--from-index` has to be less or equal to `--to-index`");
156
+ }
157
+ }
158
+ }
159
+ }
160
+ else {
161
+ // -a X -i Y
162
+ if (typeof index !== "undefined") {
163
+ throw new Error("Account number is required when index number option (`-i`) is enabled");
164
+ }
165
+ // -a X --from-index Y --to-index Z
166
+ if (typeof fromIndex !== "undefined") {
167
+ throw new Error("Account number is required when scanLimits index option (`--from-index`) is enabled");
168
+ }
169
+ }
170
+ if (typeof preDerivationSize !== "undefined") {
171
+ if (preDerivationSize < 0) {
172
+ throw new Error("`--pre-derivation-size` option is required to be positive");
173
+ }
174
+ }
175
+ else if (typeof account !== "undefined") {
176
+ args.preDerivationSize = 2000; // magic number
177
+ }
178
+ // if needed, create scanLimits
179
+ if (typeof account !== "undefined") {
180
+ if (typeof index !== "undefined") {
181
+ args.scanLimits = {
182
+ account,
183
+ indexFrom: index,
184
+ indexTo: index,
185
+ preDerivationSize: args.preDerivationSize,
186
+ };
187
+ }
188
+ else if (typeof fromIndex !== "undefined") {
189
+ args.scanLimits = {
190
+ account,
191
+ indexFrom: fromIndex,
192
+ indexTo: toIndex,
193
+ preDerivationSize: args.preDerivationSize,
194
+ };
195
+ }
196
+ }
197
+ // testnet
198
+ if (typeof testnet !== "undefined" && testnet) {
199
+ // temporary guard clause:
200
+ // only Bitcoin and Ethereum testnet are supported at the moment
201
+ if (
202
+ // case 1. non-tpub
203
+ (typeof args.xpub !== "undefined" &&
204
+ args.xpub.substring(0, 4).toLocaleLowerCase() !== "tpub") ||
205
+ // case 2. non-ETH
206
+ (typeof currency !== "undefined" && currency.toUpperCase() !== "ETH") ||
207
+ // case 3. ETH via default provider
208
+ (currency.toUpperCase() === "ETH" &&
209
+ typeof process.env.XPUB_SCAN_CUSTOM_API_KEY_V2 === "undefined")) {
210
+ throw new Error("The analysis of this currency cannot be performed on testnet");
211
+ }
212
+ }
213
+ if (args.customProvider && !process.env.XPUB_SCAN_CUSTOM_API_KEY_V2) {
214
+ throw new Error("Custom provider v2 API key (XPUB_SCAN_CUSTOM_API_KEY_V2) is missing");
215
+ }
216
+ };
217
+ exports.checkArgs = checkArgs;
@@ -0,0 +1,11 @@
1
+ import { Operation } from "../models/operation";
2
+ /**
3
+ * Dispatcher: detect the type of the imported file
4
+ * based on its contents
5
+ * @param {string} path
6
+ * Path of file to import
7
+ * @returns Operation
8
+ * Imported transactions
9
+ */
10
+ declare const importOperations: (path: string) => Array<Operation>;
11
+ export { importOperations };