@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
@@ -0,0 +1,122 @@
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.run = exports._private = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const deriveAddresses_1 = require("./deriveAddresses");
9
+ const settings_1 = require("../configuration/settings");
10
+ const helpers_1 = require("../helpers");
11
+ function showError(message, derived, provided) {
12
+ let errorMessage = chalk_1.default.red("[Comparison error] ".concat(message));
13
+ if (typeof derived !== "undefined") {
14
+ const comparison = "\nProvided address:\t"
15
+ .concat(String(provided))
16
+ .concat("\nFirst derived address: ")
17
+ .concat(String(derived));
18
+ errorMessage = errorMessage.concat(chalk_1.default.redBright(comparison));
19
+ }
20
+ console.log(errorMessage);
21
+ }
22
+ // TODO?: export in a dedicated module (display.ts)?
23
+ function showComparisonResult(xpub, address, result) {
24
+ console.log("\nXpub:", chalk_1.default.whiteBright(xpub));
25
+ console.log("Provided address:", chalk_1.default.whiteBright(address));
26
+ if (Object.keys(result).length === 0) {
27
+ // no match
28
+ console.log(chalk_1.default.redBright("The address does not seem to have been derived from this xpub!"));
29
+ }
30
+ else {
31
+ const derivationPath = "m/"
32
+ .concat(String(result.account))
33
+ .concat("/")
34
+ .concat(String(result.index));
35
+ if (typeof result.partial === "undefined") {
36
+ // full match
37
+ console.log(chalk_1.default.greenBright("The address has been derived from this xpub using derivation path ".concat(chalk_1.default.bold(derivationPath))));
38
+ }
39
+ else {
40
+ // partial match
41
+ console.log("Derived address: ", chalk_1.default.whiteBright(result.partial));
42
+ console.log(chalk_1.default.blueBright("There is a partial match between the provided address and the one derived using derivation path ".concat(chalk_1.default.bold(derivationPath))));
43
+ }
44
+ }
45
+ }
46
+ // partial match, using '?' as wildcards
47
+ function partialMatch(derived, provided) {
48
+ for (let i = 0; i < derived.length; ++i) {
49
+ if (provided[i] === "?") {
50
+ continue;
51
+ }
52
+ if (provided[i] !== derived[i]) {
53
+ return false;
54
+ }
55
+ }
56
+ return true;
57
+ }
58
+ /**
59
+ * identify whether an address provided by the user belongs or not to the xpub
60
+ * @param xpub the xpub from which the address may have been derived
61
+ * @param providedAddress the address provided by the user
62
+ * @param range the range of the search (i.e., accounts and indices ranges)
63
+ * @param searchType indication of the type of search (quick/deep)
64
+ * @returns a match or an empty object (i.e., non-match)
65
+ */
66
+ function search(xpub, providedAddress, range, searchType) {
67
+ const derivationMode = (0, deriveAddresses_1.getDerivationMode)(providedAddress);
68
+ const partialSearch = providedAddress.includes("?");
69
+ (0, helpers_1.setNetwork)(xpub);
70
+ for (let account = range.account.min; account < range.account.max; ++account) {
71
+ for (let index = range.index.min; index < range.index.max; ++index) {
72
+ const derivedAddress = (0, deriveAddresses_1.deriveAddress)(derivationMode, xpub, account, index);
73
+ // m/{account}/{index}
74
+ const derivationPath = "m/"
75
+ .concat(account.toFixed())
76
+ .concat("/")
77
+ .concat(index.toFixed());
78
+ // quick|deep search {derivation path} {derived address}
79
+ const status = searchType
80
+ .padEnd(18, " ")
81
+ .concat(derivationPath.padEnd(14, " "))
82
+ .concat(derivedAddress);
83
+ const derived = derivedAddress.toUpperCase();
84
+ const provided = providedAddress.toUpperCase();
85
+ // perfect match (case insensitive)
86
+ if (derived === provided) {
87
+ console.log(chalk_1.default.green(status));
88
+ return {
89
+ account,
90
+ index,
91
+ };
92
+ }
93
+ // partial match (if enabled)
94
+ if (partialSearch && partialMatch(derived, provided)) {
95
+ console.log(chalk_1.default.blueBright(status));
96
+ return {
97
+ partial: derivedAddress,
98
+ account,
99
+ index,
100
+ };
101
+ }
102
+ console.log(status);
103
+ }
104
+ }
105
+ return {};
106
+ }
107
+ function run(xpub, providedAddress) {
108
+ if (typeof settings_1.DERIVATION_SCOPE === "undefined") {
109
+ showError("DERIVATION_SCOPE setting is not defined");
110
+ }
111
+ const quickSearchRange = settings_1.DERIVATION_SCOPE.quick_search;
112
+ let result = search(xpub, providedAddress, quickSearchRange, "quick search");
113
+ if (Object.keys(result).length === 0) {
114
+ const deepSearchRange = settings_1.DERIVATION_SCOPE.deep_search;
115
+ result = search(xpub, providedAddress, deepSearchRange, "deep search");
116
+ }
117
+ showComparisonResult(xpub, providedAddress, result);
118
+ }
119
+ exports.run = run;
120
+ exports._private = {
121
+ search,
122
+ };
@@ -0,0 +1,20 @@
1
+ import { Address } from "../models/address";
2
+ import { ScanLimits } from "../models/scanLimits";
3
+ import { DerivationMode } from "../configuration/currencies";
4
+ import BigNumber from "bignumber.js";
5
+ /**
6
+ * Run the analysis
7
+ * @param itemToScan ITEM TO SCAN can be an xpub or an address
8
+ * (that is why it is named `itemToScan` instead of xpub)
9
+ * @param balanceOnly option to fetch the balance only—not the transactions
10
+ * @param scanLimits option to limit the scan to a certain account and indices range
11
+ * @returns a list of active addresses and a summary (total balance per derivation mode)
12
+ */
13
+ declare function run(itemToScan: string, balanceOnly: boolean, scanLimits?: ScanLimits): Promise<{
14
+ addresses: Address[];
15
+ summary: {
16
+ derivationMode: DerivationMode;
17
+ balance: BigNumber;
18
+ }[];
19
+ }>;
20
+ export { run };
@@ -0,0 +1,300 @@
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.run = void 0;
39
+ const chalk_1 = __importDefault(require("chalk"));
40
+ const display = __importStar(require("../display"));
41
+ const address_1 = require("../models/address");
42
+ const ownAddresses_1 = require("../models/ownAddresses");
43
+ const settings_1 = require("../configuration/settings");
44
+ const currencies_1 = require("../configuration/currencies");
45
+ const processTransactions_1 = require("./processTransactions");
46
+ const deriveAddresses_1 = require("../actions/deriveAddresses");
47
+ const bignumber_js_1 = __importDefault(require("bignumber.js"));
48
+ /**
49
+ * derive and scan all active addresses _for a given derivation mode_
50
+ * note: an ACTIVE ADDRESS is an address with > 0 transactions
51
+ * @param derivationMode a derivation mode (enum)
52
+ * @param xpub the xpub to scan
53
+ * @param balanceOnly option to fetch the balance only—not the transactions
54
+ * @param scanLimits option to limit the scan to a certain account and indices range
55
+ * @returns an object containing the total balance for the derivation mode as well as
56
+ * a list of active addresses associated with it
57
+ */
58
+ function deriveAndScanAddressesByDerivationMode(derivationMode, xpub, balanceOnly, scanLimits) {
59
+ return __awaiter(this, void 0, void 0, function* () {
60
+ display.logStatus("Scanning ".concat(chalk_1.default.bold(derivationMode)).concat(" addresses..."));
61
+ const ownAddresses = new ownAddresses_1.OwnAddresses();
62
+ let totalBalance = new bignumber_js_1.default(0);
63
+ let txCounter = 0;
64
+ const addresses = [];
65
+ let accountSpan = undefined;
66
+ let indexFromSpan = undefined;
67
+ let indexToSpan = undefined;
68
+ let preDerivationSize = undefined;
69
+ if (scanLimits) {
70
+ accountSpan = scanLimits.account;
71
+ indexFromSpan = scanLimits.indexFrom;
72
+ indexToSpan = scanLimits.indexTo;
73
+ preDerivationSize = scanLimits.preDerivationSize;
74
+ // crucial step in limited scan mode: precompute the addresses belonging
75
+ // to the same xpub in order to perform transaction analysis further
76
+ // down the flow. `preDerivationSize` define the number of addresses to pre-derive.
77
+ for (let a = 0; a < 2; a++) {
78
+ for (let i = 0; i < preDerivationSize; i++) {
79
+ ownAddresses.addAddress(new address_1.Address(xpub, derivationMode, a, i));
80
+ }
81
+ }
82
+ }
83
+ // loop over derivation path accounts: `m/{account}/{index}`
84
+ // note: we limit ourselves to accounts 0 and 1
85
+ // but the scope could be extended further if needed
86
+ for (let account = 0; account < 2; ++account) {
87
+ // if limited scan mode is enabled and the current account is outside the scope, skip it
88
+ if (typeof accountSpan != "undefined" && account !== accountSpan) {
89
+ continue;
90
+ }
91
+ // account 0 == external addresses
92
+ // account 1 == internal (aka change) addresses
93
+ const typeAccount = account === 1 ? "internal" : "external";
94
+ display.logStatus("- scanning " + chalk_1.default.italic(typeAccount) + " addresses -");
95
+ txCounter = 0;
96
+ for (let index = 0 /* scan all active indices */;; ++index) {
97
+ // if limited scan mode is enabled and the current index is _below_ the range, skip it
98
+ // ______(current index)_______[ LIMITED SCAN RANGE ]____________________________
99
+ if (typeof indexFromSpan !== "undefined" && index < indexFromSpan) {
100
+ continue;
101
+ }
102
+ // if limited scan mode is enabled and the current index is _beyond_ the range, stop the scan
103
+ // ____________________________[ LIMITED SCAN RANGE ]______(current index)_______
104
+ if (typeof indexToSpan !== "undefined" && index > indexToSpan) {
105
+ break;
106
+ }
107
+ // get address derived according to:
108
+ // - its xpub (by definition),
109
+ // - the current derivation mode (legacy, SegWit, etc.)
110
+ // - the derivation path characteristics: `m/{account:0|1}/{index:0|∞}`
111
+ const address = new address_1.Address(xpub, derivationMode, account, index);
112
+ display.updateAddressDetails(address);
113
+ const status = txCounter === 0 ? "analyzing" : "probing address gap";
114
+ if (!settings_1.configuration.silent && !settings_1.configuration.quiet) {
115
+ process.stdout.write(chalk_1.default.yellow(status + "..."));
116
+ }
117
+ // fetch (from external provider) the basic data regarding the address
118
+ // (balance, transactions count, etc.)
119
+ yield (0, processTransactions_1.getStats)(address, balanceOnly);
120
+ const addressStats = address.getStats();
121
+ // here, evaluate if the address needs further analysis
122
+ if (addressStats.txsCount.isZero()) {
123
+ // no transaction associated with the address:
124
+ // perform address gap probing
125
+ // GAP PROBE: check whether an address is active in a certain range
126
+ //
127
+ // for instance:
128
+ // ┌┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐
129
+ // ┊ m/0/0 — active (10 transactions) ┊
130
+ // ┊ m/0/1 — active (2 transactions) ┊
131
+ // ┊ ┐ ┊
132
+ // ┊ m/0/2 — inactive (0 transaction) │ ┊
133
+ // ┊ m/0/3 — inactive (0 transaction) │ GAP ┊
134
+ // ┊ m/0/4 — inactive (0 transaction) │ ┊
135
+ // ┊ ┘ ┊
136
+ // ┊ m/0/5 — active (4 transactions) ┊
137
+ // └┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘
138
+ //
139
+ // in this example, the gap probing allows to detect that
140
+ // `m/0/5` is an active address
141
+ //
142
+ // note: the scope of the gap probing is 20 addresses by
143
+ // default (check `DEFAULT_GAP_LIMIT`) but can be configured
144
+ // using the `GAP_LIMIT` environment variable
145
+ txCounter++;
146
+ display.transientLine( /* delete address as it is not an active one */);
147
+ if (account === 1 || txCounter >= Number(settings_1.configuration.gap_limit)) {
148
+ // all active addresses have been scanned and the gap limit reached:
149
+ // stop the scan for this specific derivation mode
150
+ display.transientLine( /* delete last probing info */);
151
+ display.logStatus("- " + chalk_1.default.italic(typeAccount) + " addresses scanned -");
152
+ break;
153
+ }
154
+ continue;
155
+ }
156
+ else {
157
+ txCounter = 0;
158
+ }
159
+ // convert address balance into satoshis (or equivalent unit)
160
+ // in order to avoid issue with floats addition
161
+ totalBalance = totalBalance.plus(address.getBalance());
162
+ display.updateAddressDetails(address);
163
+ // important step: add the active address to the
164
+ // list of own addresses in order to perform
165
+ // transaction analysis further down the flow
166
+ ownAddresses.addAddress(address);
167
+ addresses.push(address);
168
+ }
169
+ }
170
+ // process transactions
171
+ display.transientLine(chalk_1.default.yellowBright("Processing transactions..."));
172
+ if (!balanceOnly) {
173
+ addresses.forEach((address) => {
174
+ (0, processTransactions_1.getTransactions)(address, ownAddresses);
175
+ });
176
+ }
177
+ display.transientLine( /* delete address */);
178
+ display.logStatus(derivationMode.concat(" addresses scanned\n"));
179
+ return {
180
+ balance: totalBalance,
181
+ addresses,
182
+ };
183
+ });
184
+ }
185
+ /**
186
+ * Scan an address (account-based mode)
187
+ * @param itemToScan xpub from which an address will be derived, or directly an address
188
+ * @param balanceOnly option to fetch the balance only—not the transactions
189
+ * @returns an array containing this address and a summary that includes the total balance
190
+ * (TODO: simplify in order to normalize with xpub analysis)
191
+ */
192
+ function addressAnalysis(itemToScan, balanceOnly) {
193
+ return __awaiter(this, void 0, void 0, function* () {
194
+ if (!settings_1.configuration.silent) {
195
+ console.log(chalk_1.default.bold("\nScanned address\n"));
196
+ }
197
+ let address;
198
+ if (itemToScan.substring(0, 4).toLocaleLowerCase() === "xpub") {
199
+ // the item to scan appears to be an xpub: derive just the first address
200
+ const derivationMode = settings_1.configuration.currency.derivationModes[0] || currencies_1.DerivationMode.UNKNOWN;
201
+ address = new address_1.Address((0, deriveAddresses_1.deriveAddress)(derivationMode, itemToScan));
202
+ }
203
+ else {
204
+ // the item to scan is directly an address: construct it
205
+ address = new address_1.Address(itemToScan);
206
+ }
207
+ if (typeof address === "undefined") {
208
+ throw new Error(`Address cannot be instantiated from "${itemToScan}"`);
209
+ }
210
+ display.updateAddressDetails(address);
211
+ // fetch (from external provider) the basic data regarding the address
212
+ // (balance, transactions count, etc.)
213
+ yield (0, processTransactions_1.getStats)(address, balanceOnly);
214
+ if (!balanceOnly) {
215
+ // also (if applicable), fetch (from the external provider) the raw transactions
216
+ // associated with the address
217
+ (0, processTransactions_1.getTransactions)(address);
218
+ }
219
+ display.updateAddressDetails(address);
220
+ const summary = [
221
+ {
222
+ derivationMode: currencies_1.DerivationMode.ETHEREUM,
223
+ balance: new bignumber_js_1.default(address.getBalance()),
224
+ },
225
+ ];
226
+ return {
227
+ addresses: [address],
228
+ summary,
229
+ };
230
+ });
231
+ }
232
+ /**
233
+ * Run the analysis
234
+ * @param itemToScan ITEM TO SCAN can be an xpub or an address
235
+ * (that is why it is named `itemToScan` instead of xpub)
236
+ * @param balanceOnly option to fetch the balance only—not the transactions
237
+ * @param scanLimits option to limit the scan to a certain account and indices range
238
+ * @returns a list of active addresses and a summary (total balance per derivation mode)
239
+ */
240
+ function run(itemToScan, balanceOnly, scanLimits) {
241
+ return __awaiter(this, void 0, void 0, function* () {
242
+ if (settings_1.configuration.currency.utxo_based) {
243
+ // if the currency is UTXOs based (e.g., Bitcoin):
244
+ // the item to scan is an xpub
245
+ return xpubAnalysis(itemToScan, balanceOnly, scanLimits);
246
+ }
247
+ else {
248
+ // otherwise, the currency is account-based (e.g., Ethereum):
249
+ // the item to scan is an address
250
+ return addressAnalysis(itemToScan, balanceOnly);
251
+ }
252
+ });
253
+ }
254
+ exports.run = run;
255
+ /**
256
+ * scan an xpub (UTXO-based mode)
257
+ * @param xpub the xpub to scan
258
+ * @param balanceOnly option to fetch the balance only—not the transactions
259
+ * @param scanLimits option to limit the scan to a certain account and indices range
260
+ * @returns a list of active addresses and a summary (total balance per derivation mode)
261
+ */
262
+ function xpubAnalysis(xpub, balanceOnly, scanLimits) {
263
+ return __awaiter(this, void 0, void 0, function* () {
264
+ let activeAddresses = [];
265
+ const summary = [];
266
+ // get all derivation modes associated with the currency type
267
+ // (e.g., for Bitcoin: legacy, SegWit, native SegWit, and taproot)
268
+ let derivationModes = settings_1.configuration.currency.derivationModes;
269
+ if (settings_1.configuration.specificDerivationMode) {
270
+ // if a specific derivation mode is set, limit the scan to this mode
271
+ derivationModes = derivationModes.filter((derivation) => derivation
272
+ .toString()
273
+ .toLocaleLowerCase()
274
+ .startsWith(settings_1.configuration.specificDerivationMode.toLocaleLowerCase()));
275
+ }
276
+ if (!settings_1.configuration.silent) {
277
+ console.log(chalk_1.default.bold("\nActive addresses\n"));
278
+ }
279
+ for (const derivationMode of derivationModes) {
280
+ // loop over the derivation modes and `scan` the addresses belonging to
281
+ // the current derivation mode
282
+ // (that is: derive them and identify the active ones)
283
+ const results = yield deriveAndScanAddressesByDerivationMode(derivationMode, xpub, balanceOnly, scanLimits);
284
+ activeAddresses = activeAddresses.concat(results.addresses);
285
+ summary.push({
286
+ derivationMode,
287
+ balance: results.balance,
288
+ });
289
+ // Stop scanning other derivation modes if we found active addresses
290
+ // (an xpub will only have addresses in one derivation mode)
291
+ if (results.addresses.length > 0) {
292
+ break;
293
+ }
294
+ }
295
+ return {
296
+ addresses: activeAddresses,
297
+ summary,
298
+ };
299
+ });
300
+ }
@@ -0,0 +1,17 @@
1
+ import { DerivationMode } from "../configuration/currencies";
2
+ /**
3
+ * derive an address at a given account and index positions
4
+ * @param derivationMode the derivation mode used to derive the address
5
+ * @param xpub the xpub from which to derive the address
6
+ * @param account account number from which to derive the address
7
+ * @param index index number from which to derive the address
8
+ * @returns the derived address
9
+ */
10
+ declare function deriveAddress(derivationMode: DerivationMode, xpub: string, account?: number, index?: number): string;
11
+ /**
12
+ * infer the derivation mode from the address syntax
13
+ * @param address any address (Bitcoin, Ethereum, etc.)
14
+ * @returns the derivation mode associated with the address
15
+ */
16
+ declare function getDerivationMode(address: string): DerivationMode.LEGACY | DerivationMode.NATIVE | DerivationMode.SEGWIT | DerivationMode.TAPROOT | DerivationMode.DOGECOIN;
17
+ export { getDerivationMode, deriveAddress };