@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,239 @@
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 __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.deriveAddress = exports.getDerivationMode = void 0;
30
+ const bjs = __importStar(require("bitcoinjs-lib"));
31
+ const bchaddrjs_1 = __importDefault(require("bchaddrjs"));
32
+ const bitcore_lib_cash_1 = __importDefault(require("bitcore-lib-cash"));
33
+ const ethereumjs_wallet_1 = __importDefault(require("ethereumjs-wallet"));
34
+ const currencies_1 = require("../configuration/currencies");
35
+ const settings_1 = require("../configuration/settings");
36
+ const create_hmac_1 = __importDefault(require("create-hmac"));
37
+ const bip32_1 = __importDefault(require("bip32"));
38
+ const ecc = __importStar(require("tiny-secp256k1"));
39
+ const bs58check_1 = __importDefault(require("bs58check"));
40
+ const secp256k1_1 = require("secp256k1");
41
+ const bip32 = (0, bip32_1.default)(ecc);
42
+ // Initialize ECC library for Taproot (p2tr) support
43
+ bjs.initEccLib(ecc);
44
+ /**
45
+ * Convert a public key to x-only format (32 bytes) for Taproot
46
+ * Compressed public keys are 33 bytes (02/03 prefix + 32 bytes x-coordinate)
47
+ * X-only public keys are 32 bytes (just the x-coordinate)
48
+ */
49
+ function toXOnly(pubKey) {
50
+ return pubKey.length === 32 ? pubKey : pubKey.subarray(1, 33);
51
+ }
52
+ class BIP32 {
53
+ constructor(publicKey, chainCode, network, depth = 0, index = 0) {
54
+ this.publicKey = publicKey;
55
+ this.chainCode = chainCode;
56
+ this.network = network;
57
+ this.depth = depth;
58
+ this.index = index;
59
+ }
60
+ derive(index) {
61
+ const data = Buffer.allocUnsafe(37);
62
+ this.publicKey.copy(data, 0);
63
+ data.writeUInt32BE(index, 33);
64
+ const I = (0, create_hmac_1.default)("sha512", this.chainCode).update(data).digest();
65
+ const IL = I.slice(0, 32);
66
+ const IR = I.slice(32);
67
+ const Ki = Buffer.from((0, secp256k1_1.publicKeyTweakAdd)(this.publicKey, IL));
68
+ return new BIP32(Ki, IR, this.network, this.depth + 1, index);
69
+ }
70
+ }
71
+ const getPubkeyAt = (xpub, account, index) => {
72
+ const buffer = Buffer.from(bs58check_1.default.decode(xpub));
73
+ const depth = buffer[4];
74
+ const i = buffer.readUInt32BE(9);
75
+ const chainCode = buffer.slice(13, 45);
76
+ const publicKey = buffer.slice(45, 78);
77
+ return new BIP32(publicKey, chainCode, settings_1.configuration.currency.network, depth, i)
78
+ .derive(account)
79
+ .derive(index).publicKey;
80
+ };
81
+ /**
82
+ * derive a legacy address at a given account and index positions
83
+ * @param xpub the xpub from which to derive the legacy address
84
+ * @param account account number from which to derive the legacy address
85
+ * @param index index number from which to derive the legacy address
86
+ * @returns the derived legacy address
87
+ */
88
+ function getLegacyAddress(xpub, account, index) {
89
+ const publicKeyBuffer = getPubkeyAt(xpub, account, index);
90
+ const publicKeyHash160 = bjs.crypto.hash160(publicKeyBuffer);
91
+ return bjs.address.toBase58Check(publicKeyHash160, settings_1.configuration.currency.network.pubKeyHash);
92
+ }
93
+ /**
94
+ * derive a SegWit address at a given account and index positions
95
+ * @param xpub the xpub from which to derive the SegWit address
96
+ * @param account account number from which to derive the SegWit address
97
+ * @param index index number from which to derive the SegWit address
98
+ * @returns the derived SegWit address
99
+ */
100
+ function getSegWitAddress(xpub, account, index) {
101
+ const { address } = bjs.payments.p2sh({
102
+ redeem: bjs.payments.p2wpkh({
103
+ pubkey: bip32
104
+ .fromBase58(xpub, settings_1.configuration.currency.network)
105
+ .derive(account)
106
+ .derive(index).publicKey,
107
+ network: settings_1.configuration.currency.network,
108
+ }),
109
+ });
110
+ return String(address);
111
+ }
112
+ /**
113
+ * derive a native SegWit address at a given account and index positions
114
+ * @param xpub the xpub from which to derive the native SegWit address
115
+ * @param account account number from which to derive the native SegWit address
116
+ * @param index index number from which to derive the native SegWit address
117
+ * @returns the derived native SegWit address
118
+ */
119
+ function getNativeSegWitAddress(xpub, account, index) {
120
+ const { address } = bjs.payments.p2wpkh({
121
+ pubkey: bip32
122
+ .fromBase58(xpub, settings_1.configuration.currency.network)
123
+ .derive(account)
124
+ .derive(index).publicKey,
125
+ network: settings_1.configuration.currency.network,
126
+ });
127
+ return String(address);
128
+ }
129
+ /**
130
+ * derive a Taproot address at a given account and index positions
131
+ * @param xpub the xpub from which to derive the Taproot address
132
+ * @param account account number (0 = external/receiving, 1 = internal/change)
133
+ * @param index index number from which to derive the Taproot address
134
+ * @returns the derived Taproot address (bc1p... for mainnet, tb1p... for testnet)
135
+ */
136
+ function getTaprootAddress(xpub, account, index) {
137
+ const childPubKey = bip32
138
+ .fromBase58(xpub, settings_1.configuration.currency.network)
139
+ .derive(account)
140
+ .derive(index).publicKey;
141
+ const { address } = bjs.payments.p2tr({
142
+ internalPubkey: toXOnly(childPubKey),
143
+ network: settings_1.configuration.currency.network,
144
+ });
145
+ return String(address);
146
+ }
147
+ /**
148
+ * derive a Bitcoin Cash address at a given account and index positions
149
+ * @param xpub the xpub from which to derive the Bitcoin Cash address
150
+ * @param account account number from which to derive the Bitcoin Cash address
151
+ * @param index index number from which to derive the Bitcoin Cash address
152
+ * @returns the derived Bitcoin Cash address
153
+ * note: based on https://github.com/go-faast/bitcoin-cash-payments/blob/54397eb97c7a9bf08b32e10bef23d5f27aa5ab01/index.js#L63-L73
154
+ */
155
+ function getLegacyBitcoinCashAddress(xpub, account, index) {
156
+ const node = new bitcore_lib_cash_1.default.HDPublicKey(xpub);
157
+ const child = node.derive(account).derive(index);
158
+ const address = new bitcore_lib_cash_1.default.Address(child.publicKey, bitcore_lib_cash_1.default.Networks.livenet);
159
+ const addrstr = address.toString().split(":");
160
+ if (addrstr.length === 2) {
161
+ return bchaddrjs_1.default.toLegacyAddress(addrstr[1]);
162
+ }
163
+ else {
164
+ throw new Error("Unable to derive cash address for " + address);
165
+ }
166
+ }
167
+ /**
168
+ * derive a unique Ethereum address (the first one)
169
+ * @param xpub the xpub from which to derive the Ethereum address
170
+ * @returns the first derived Ethereum address
171
+ */
172
+ function getEthereumAddress(xpub) {
173
+ return ethereumjs_wallet_1.default.fromExtendedPublicKey(xpub).getAddressString();
174
+ }
175
+ /**
176
+ * derive an address at a given account and index positions
177
+ * @param derivationMode the derivation mode used to derive the address
178
+ * @param xpub the xpub from which to derive the address
179
+ * @param account account number from which to derive the address
180
+ * @param index index number from which to derive the address
181
+ * @returns the derived address
182
+ */
183
+ function deriveAddress(derivationMode, xpub, account, index) {
184
+ if (typeof account === "undefined") {
185
+ account = 0;
186
+ }
187
+ if (typeof index === "undefined") {
188
+ index = 0;
189
+ }
190
+ switch (derivationMode) {
191
+ case currencies_1.DerivationMode.LEGACY:
192
+ return getLegacyAddress(xpub, account, index);
193
+ case currencies_1.DerivationMode.SEGWIT:
194
+ return getSegWitAddress(xpub, account, index);
195
+ case currencies_1.DerivationMode.NATIVE:
196
+ return getNativeSegWitAddress(xpub, account, index);
197
+ case currencies_1.DerivationMode.TAPROOT:
198
+ return getTaprootAddress(xpub, account, index);
199
+ case currencies_1.DerivationMode.BCH:
200
+ return getLegacyBitcoinCashAddress(xpub, account, index);
201
+ case currencies_1.DerivationMode.DOGECOIN:
202
+ return getLegacyAddress(xpub, account, index);
203
+ case currencies_1.DerivationMode.ETHEREUM:
204
+ return getEthereumAddress(xpub);
205
+ case currencies_1.DerivationMode.UNKNOWN:
206
+ /* fallthrough */
207
+ default:
208
+ throw new Error("Unknown derivation mode");
209
+ }
210
+ }
211
+ exports.deriveAddress = deriveAddress;
212
+ /**
213
+ * infer the derivation mode from the address syntax
214
+ * @param address any address (Bitcoin, Ethereum, etc.)
215
+ * @returns the derivation mode associated with the address
216
+ */
217
+ function getDerivationMode(address) {
218
+ if (address.match("^(bc1q|tb1|ltc1).*")) {
219
+ return currencies_1.DerivationMode.NATIVE;
220
+ }
221
+ else if (address.match("^(bc1p|tb1p).*")) {
222
+ return currencies_1.DerivationMode.TAPROOT;
223
+ }
224
+ else if (address.match("^[32M].*")) {
225
+ return currencies_1.DerivationMode.SEGWIT;
226
+ }
227
+ else if (address.match("^[1nmL].*")) {
228
+ return currencies_1.DerivationMode.LEGACY;
229
+ }
230
+ else if (address.match("^(D).*")) {
231
+ return currencies_1.DerivationMode.DOGECOIN;
232
+ }
233
+ else {
234
+ throw new Error("INVALID ADDRESS: "
235
+ .concat(address)
236
+ .concat(" is not a valid or a supported address"));
237
+ }
238
+ }
239
+ exports.getDerivationMode = getDerivationMode;
@@ -0,0 +1,29 @@
1
+ import { Address } from "../models/address";
2
+ import { OwnAddresses } from "../models/ownAddresses";
3
+ import { Operation } from "../models/operation";
4
+ /**
5
+ * fetch the processed basic stats related to an address
6
+ * its balance, funded and spend sums and counts
7
+ * @param address the address being analyzed
8
+ * @param balanceOnly an option to return only the balance (only for Crypto APIs)
9
+ */
10
+ declare function getStats(address: Address, balanceOnly: boolean): Promise<void>;
11
+ /**
12
+ * get all processed transactions related to an address
13
+ * @param address the address being analyzed
14
+ * @param ownAddresses (optional) list of addresses derived from the same xpub as `address`
15
+ */
16
+ declare function getTransactions(address: Address, ownAddresses?: OwnAddresses): void;
17
+ /**
18
+ * Returns an array of ordered operations
19
+ * @param {Array<Address>} addresses - all active addresses belonging to the xpub
20
+ * @returns {Array<Address>} Array of operations in reverse chronological order
21
+ */
22
+ declare function getSortedOperations(addresses: Array<Address>): Array<Operation>;
23
+ /**
24
+ * Returns an array of ordered UTXOs
25
+ * @param {Array<Address>} addresses - all active addresses belonging to the xpub
26
+ * @returns {Array<Address>} Array of UTXOs in reverse chronological order
27
+ */
28
+ declare function getSortedUTXOS(addresses: Array<Address>): Array<Address>;
29
+ export { getStats, getTransactions, getSortedOperations, getSortedUTXOS };
@@ -0,0 +1,289 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.getSortedUTXOS = exports.getSortedOperations = exports.getTransactions = exports.getStats = void 0;
36
+ const settings_1 = require("../configuration/settings");
37
+ const operation_1 = require("../models/operation");
38
+ const defaultProvider = __importStar(require("../api/defaultProvider"));
39
+ const customProvider = __importStar(require("../api/customProvider"));
40
+ const currencies_1 = require("../configuration/currencies");
41
+ /**
42
+ * fetch the processed basic stats related to an address
43
+ * its balance, funded and spend sums and counts
44
+ * @param address the address being analyzed
45
+ * @param balanceOnly an option to return only the balance (only for Crypto APIs)
46
+ */
47
+ function getStats(address, balanceOnly) {
48
+ return __awaiter(this, void 0, void 0, function* () {
49
+ switch (settings_1.configuration.providerType) {
50
+ case "default":
51
+ yield defaultProvider.getStats(address);
52
+ break;
53
+ case "Crypto APIs":
54
+ yield customProvider.getStats(address, balanceOnly);
55
+ break;
56
+ default:
57
+ throw new Error("Should not be reachable: providerType should be 'default' or 'Crypto APIs'");
58
+ }
59
+ });
60
+ }
61
+ exports.getStats = getStats;
62
+ /**
63
+ * get all transactions associated with an UTXO-based address
64
+ * @param address the UTXO-based address being analyzed
65
+ */
66
+ function getUTXOBasedTransactions(address) {
67
+ switch (settings_1.configuration.providerType) {
68
+ case "default":
69
+ defaultProvider.getTransactions(address);
70
+ break;
71
+ case "Crypto APIs":
72
+ customProvider.getTransactions(address);
73
+ break;
74
+ default:
75
+ throw new Error("Should not be reachable: providerType should be 'default' or 'Crypto APIs'");
76
+ }
77
+ }
78
+ /**
79
+ * get all transactions associated with an account-based address (typically Ethereum)
80
+ * @param address the account-based address being analyzed
81
+ */
82
+ function getAccountBasedTransactions(address) {
83
+ switch (settings_1.configuration.providerType) {
84
+ case "default":
85
+ defaultProvider.getAccountBasedTransactions(address);
86
+ break;
87
+ case "Crypto APIs":
88
+ customProvider.getAccountBasedTransactions(address);
89
+ break;
90
+ default:
91
+ throw new Error("Should not be reachable: providerType should be 'default' or 'Crypto APIs'");
92
+ }
93
+ }
94
+ /**
95
+ * get all processed transactions related to an address
96
+ * @param address the address being analyzed
97
+ * @param ownAddresses (optional) list of addresses derived from the same xpub as `address`
98
+ */
99
+ function getTransactions(address, ownAddresses) {
100
+ if (settings_1.configuration.currency.utxo_based) {
101
+ // ┏━━━━━━━━━━━━━━━━━━━━━┓
102
+ // ┃ UTXO-BASED CURRENCY ┃
103
+ // ┗━━━━━━━━━━━━━━━━━━━━━┛
104
+ getUTXOBasedTransactions(address);
105
+ // important step: distinguish funded from sent transactions
106
+ processFundedTransactions(address, ownAddresses);
107
+ processSentTransactions(address, ownAddresses);
108
+ }
109
+ else {
110
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━┓
111
+ // ┃ ACCOUNT-BASED CURRENCY ┃
112
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━┛
113
+ getAccountBasedTransactions(address);
114
+ }
115
+ }
116
+ exports.getTransactions = getTransactions;
117
+ /**
118
+ * identify _funded_ transactions among the address' transactions
119
+ * @param address the address being analyzed
120
+ * @param ownAddresses list of addresses derived from the same xpub as `address`
121
+ */
122
+ function processFundedTransactions(address, ownAddresses) {
123
+ // all transactions associated with the address, without distinction
124
+ const transactions = address.getTransactions();
125
+ // all addresses derived from the same xpub as `address`
126
+ const allOwnAddresses = ownAddresses.getAllAddresses();
127
+ const accountNumber = address.getDerivation().account;
128
+ let isFunded;
129
+ for (const tx of transactions) {
130
+ isFunded = true;
131
+ if (typeof tx.ins !== "undefined" && tx.ins.length > 0) {
132
+ if (accountNumber === 1) {
133
+ // when account is internal (i.e., 1), and
134
+ // - has a sibling as sender: not externally funded (expected behavior: sent to change)
135
+ // - has no sibling as sender: process the operation (edge case: non-sibling to change)
136
+ for (const txin of tx.ins) {
137
+ if (allOwnAddresses.includes(txin.address)) {
138
+ // has a sibling as sender: not funded
139
+ isFunded = false;
140
+ break;
141
+ }
142
+ }
143
+ }
144
+ if (isFunded) {
145
+ // this is a _funded_ operation...
146
+ const op = new operation_1.Operation(tx.date, tx.ins[0].amount);
147
+ op.setTxid(tx.txid);
148
+ op.setBlockNumber(tx.blockHeight);
149
+ // ... that has to be categorized:
150
+ // there are 2 types of received transaction:
151
+ // case 1 — received to an address that is NOT a change address: default received
152
+ // case 2 — received to a change address (account #1): received from non-sibling to change
153
+ op.setOperationType(accountNumber !== 1 ? "Received" : "Received (non-sibling to change)");
154
+ // associate this funded operation with the address
155
+ address.addFundedOperation(op);
156
+ }
157
+ }
158
+ }
159
+ if (settings_1.VERBOSE) {
160
+ console.log("FUNDED\t", address.getFundedOperations());
161
+ }
162
+ }
163
+ /**
164
+ * identify _sent_ transactions among the address' transactions
165
+ * @param address the address being analyzed
166
+ * @param ownAddresses list of addresses derived from the same xpub as `address`
167
+ */
168
+ function processSentTransactions(address, ownAddresses) {
169
+ // all transactions associated with the address, without distinction
170
+ const transactions = address.getTransactions();
171
+ // addresses derived from the same xpub as `address`,
172
+ // either external or internal (i.e., change)
173
+ const externalAddresses = ownAddresses.getExternalAddresses();
174
+ const internalAddresses = ownAddresses.getInternalAddresses();
175
+ for (const tx of transactions) {
176
+ const outs = tx.outs;
177
+ outs.forEach((out) => {
178
+ // analyse all recipient addresses and identify whether they
179
+ // are external/internal/third-party addresses
180
+ const isInternalAddress = internalAddresses.includes(out.address);
181
+ const isExternalAddress = externalAddresses.includes(out.address);
182
+ // exclude internal addresses—by definition (i.e., change addresses)
183
+ if (!isInternalAddress) {
184
+ // at this stage, the recipient is either an external address or a
185
+ // third-party address (i.e., not belonging to the same xpub)...
186
+ const op = new operation_1.Operation(tx.date, out.amount);
187
+ op.setTxid(tx.txid);
188
+ op.setBlockNumber(tx.blockHeight);
189
+ // ... and has to be categorized:
190
+ // there are 3 types of sent transaction:
191
+ if (out.address === address.toString()) {
192
+ // case 1 — sent to self: sent to same address
193
+ op.setOperationType("Sent to self");
194
+ }
195
+ else if (isExternalAddress) {
196
+ // case 2 — sent to a sibling: sent to an address belonging to the same xpub
197
+ // _while not being a change address_
198
+ op.setOperationType("Sent to sibling");
199
+ }
200
+ else {
201
+ // case 3 — sent to an address not belonging to the xpub
202
+ op.setOperationType("Sent");
203
+ }
204
+ // associate this sent operation with the address
205
+ address.addSentOperation(op);
206
+ }
207
+ });
208
+ }
209
+ if (settings_1.VERBOSE) {
210
+ console.log("SENT\t", address.getSentOperations());
211
+ }
212
+ }
213
+ /**
214
+ * sort by block number and, _then, if needed_, by date
215
+ * @param A first operation
216
+ * @param B second operation
217
+ * @returns -1|0|1, depending on the ordering
218
+ */
219
+ function compareOpsByBlockThenDate(A, B) {
220
+ // block number
221
+ if (A.block > B.block) {
222
+ return -1;
223
+ }
224
+ if (A.block < B.block) {
225
+ return 1;
226
+ }
227
+ // date
228
+ if (A.date > B.date) {
229
+ return -1;
230
+ }
231
+ if (A.date < B.date) {
232
+ return 1;
233
+ }
234
+ return 0;
235
+ }
236
+ /**
237
+ * Returns an array of ordered operations
238
+ * @param {Array<Address>} addresses - all active addresses belonging to the xpub
239
+ * @returns {Array<Address>} Array of operations in reverse chronological order
240
+ */
241
+ function getSortedOperations(addresses) {
242
+ const operations = [];
243
+ const processedTxids = [];
244
+ // flatten the array of arrays in one dimension, and loop over
245
+ [].concat(addresses).forEach((address) => {
246
+ address.getFundedOperations().forEach((op) => {
247
+ op.setAddress(address.toString());
248
+ if (settings_1.configuration.currency.symbol === currencies_1.currencies.bch.symbol) {
249
+ op.setCashAddress(address.asCashAddress());
250
+ }
251
+ operations.push(op);
252
+ });
253
+ address.getSentOperations().forEach((op) => {
254
+ // only process a given txid once
255
+ if (!processedTxids.includes(op.txid)) {
256
+ op.setAddress(address.toString());
257
+ if (settings_1.configuration.currency.symbol === currencies_1.currencies.bch.symbol) {
258
+ op.setCashAddress(address.asCashAddress());
259
+ }
260
+ operations.push(op);
261
+ processedTxids.push(op.txid);
262
+ }
263
+ });
264
+ });
265
+ // reverse chronological order
266
+ operations.sort(compareOpsByBlockThenDate);
267
+ return operations;
268
+ }
269
+ exports.getSortedOperations = getSortedOperations;
270
+ /**
271
+ * Returns an array of ordered UTXOs
272
+ * @param {Array<Address>} addresses - all active addresses belonging to the xpub
273
+ * @returns {Array<Address>} Array of UTXOs in reverse chronological order
274
+ */
275
+ // (reverse chronological order)
276
+ function getSortedUTXOS(addresses) {
277
+ // note: no need to explicitely sort the UTXOs as they inherit
278
+ // the order from the addresses themselves
279
+ const utxos = [];
280
+ // flatten the array of arrays in one dimension, and loop over
281
+ [].concat(addresses).forEach((address) => {
282
+ if (address.isUTXO()) {
283
+ // if the address is an UTXO, just add it to the list of UTXOs
284
+ utxos.push(address);
285
+ }
286
+ });
287
+ return utxos;
288
+ }
289
+ exports.getSortedUTXOS = getSortedUTXOS;
@@ -0,0 +1,2 @@
1
+ declare function save(meta: any, data: any, directory: string): void;
2
+ export { save };