@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.
- package/.claude/settings.local.json +8 -0
- package/CONTRIBUTING.md +130 -0
- package/LICENSE +23 -0
- package/README.md +215 -0
- package/__tests__/checkAddresses/checkBitcoinAddressesNegative.test.ts +75 -0
- package/__tests__/checkAddresses/checkBitcoinAddressesPositive.test.ts +87 -0
- package/__tests__/checkAddresses/checkDogeAddressesPositive.test.ts +43 -0
- package/__tests__/checkAddresses/checkLitecoinAddressesNegative.test.ts +75 -0
- package/__tests__/checkAddresses/checkLitecoinAddressesPositive.test.ts +87 -0
- package/__tests__/checkModels/address.test.ts +183 -0
- package/__tests__/checkModels/fakeRawTransactions.json +182 -0
- package/__tests__/deriveAddresses/deriveBitcoinAddresses.test.ts +207 -0
- package/__tests__/deriveAddresses/deriveBitcoinCashAddresses.test copy.ts +79 -0
- package/__tests__/deriveAddresses/deriveDogecoinAddresses.test.ts +43 -0
- package/__tests__/deriveAddresses/deriveEthereumAddresses.test.ts +26 -0
- package/__tests__/deriveAddresses/deriveLitecoinAddresses.test.ts +110 -0
- package/__tests__/helpers.test.ts +274 -0
- package/__tests__/test-utils.ts +3 -0
- package/babel.config.js +6 -0
- package/jest.config.ts +5 -0
- package/ledgerhq-xpub-scan-1.0.4.tgz +0 -0
- package/lib/actions/checkAddress.d.ts +29 -0
- package/lib/actions/checkAddress.js +122 -0
- package/lib/actions/checkBalance.d.ts +20 -0
- package/lib/actions/checkBalance.js +300 -0
- package/lib/actions/deriveAddresses.d.ts +17 -0
- package/lib/actions/deriveAddresses.js +239 -0
- package/lib/actions/processTransactions.d.ts +29 -0
- package/lib/actions/processTransactions.js +289 -0
- package/lib/actions/saveAnalysis.d.ts +2 -0
- package/lib/actions/saveAnalysis.js +800 -0
- package/lib/actions/scanner.d.ts +15 -0
- package/lib/actions/scanner.js +152 -0
- package/lib/api/customProvider.d.ts +19 -0
- package/lib/api/customProvider.js +434 -0
- package/lib/api/defaultProvider.d.ts +23 -0
- package/lib/api/defaultProvider.js +275 -0
- package/lib/comparison/compareOperations.d.ts +13 -0
- package/lib/comparison/compareOperations.js +500 -0
- package/lib/comparison/diffs.d.ts +18 -0
- package/lib/comparison/diffs.js +70 -0
- package/lib/configuration/currencies.d.ts +55 -0
- package/lib/configuration/currencies.js +72 -0
- package/lib/configuration/settings.d.ts +51 -0
- package/lib/configuration/settings.js +113 -0
- package/lib/display.d.ts +12 -0
- package/lib/display.js +251 -0
- package/lib/helpers.d.ts +27 -0
- package/lib/helpers.js +255 -0
- package/lib/input/args.d.ts +6 -0
- package/lib/input/args.js +129 -0
- package/lib/input/check.d.ts +6 -0
- package/lib/input/check.js +217 -0
- package/lib/input/importOperations.d.ts +11 -0
- package/lib/input/importOperations.js +406 -0
- package/lib/models/address.d.ts +40 -0
- package/lib/models/address.js +101 -0
- package/lib/models/comparison.d.ts +8 -0
- package/lib/models/comparison.js +6 -0
- package/lib/models/currency.d.ts +11 -0
- package/lib/models/currency.js +6 -0
- package/lib/models/operation.d.ts +33 -0
- package/lib/models/operation.js +80 -0
- package/lib/models/ownAddresses.d.ts +11 -0
- package/lib/models/ownAddresses.js +31 -0
- package/lib/models/scanLimits.d.ts +7 -0
- package/lib/models/scanLimits.js +6 -0
- package/lib/models/stats.d.ts +7 -0
- package/lib/models/stats.js +6 -0
- package/lib/models/transaction.d.ts +10 -0
- package/lib/models/transaction.js +13 -0
- package/lib/scan.d.ts +2 -0
- package/lib/scan.js +31 -0
- package/lib/templates/logos.base64.d.ts +2 -0
- package/lib/templates/logos.base64.js +9 -0
- package/lib/templates/report.html.d.ts +1 -0
- package/lib/templates/report.html.js +393 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/types.d.ts +55 -0
- package/lib/types.js +2 -0
- package/npm-shrinkwrap.json +12323 -0
- package/package.json +81 -0
- package/sonar-project.properties +15 -0
|
@@ -0,0 +1,72 @@
|
|
|
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.currencies = exports.DerivationMode = void 0;
|
|
7
|
+
const coininfo_1 = __importDefault(require("coininfo"));
|
|
8
|
+
var DerivationMode;
|
|
9
|
+
(function (DerivationMode) {
|
|
10
|
+
DerivationMode["LEGACY"] = "Legacy";
|
|
11
|
+
DerivationMode["NATIVE"] = "Native SegWit";
|
|
12
|
+
DerivationMode["SEGWIT"] = "SegWit";
|
|
13
|
+
DerivationMode["TAPROOT"] = "Taproot";
|
|
14
|
+
DerivationMode["BCH"] = "Bitcoin Cash";
|
|
15
|
+
DerivationMode["ETHEREUM"] = "Ethereum";
|
|
16
|
+
DerivationMode["DOGECOIN"] = "Dogecoin";
|
|
17
|
+
DerivationMode["UNKNOWN"] = "Unknown";
|
|
18
|
+
})(DerivationMode || (exports.DerivationMode = DerivationMode = {}));
|
|
19
|
+
exports.currencies = {
|
|
20
|
+
btc: {
|
|
21
|
+
name: "Bitcoin",
|
|
22
|
+
symbol: "BTC",
|
|
23
|
+
network_mainnet: coininfo_1.default.bitcoin.main.toBitcoinJS(),
|
|
24
|
+
network_testnet: coininfo_1.default.bitcoin.test.toBitcoinJS(),
|
|
25
|
+
derivationModes: [
|
|
26
|
+
DerivationMode.NATIVE,
|
|
27
|
+
DerivationMode.TAPROOT,
|
|
28
|
+
DerivationMode.SEGWIT,
|
|
29
|
+
DerivationMode.LEGACY,
|
|
30
|
+
],
|
|
31
|
+
precision: Math.pow(10, 8),
|
|
32
|
+
utxo_based: true,
|
|
33
|
+
},
|
|
34
|
+
bch: {
|
|
35
|
+
name: "Bitcoin Cash",
|
|
36
|
+
symbol: "BCH",
|
|
37
|
+
network_mainnet: coininfo_1.default.bitcoincash.main.toBitcoinJS(),
|
|
38
|
+
network_testnet: coininfo_1.default.bitcoincash.test.toBitcoinJS(),
|
|
39
|
+
derivationModes: [DerivationMode.BCH],
|
|
40
|
+
precision: Math.pow(10, 8),
|
|
41
|
+
utxo_based: true,
|
|
42
|
+
},
|
|
43
|
+
ltc: {
|
|
44
|
+
name: "Litecoin",
|
|
45
|
+
symbol: "LTC",
|
|
46
|
+
network_mainnet: coininfo_1.default.litecoin.main.toBitcoinJS(),
|
|
47
|
+
network_testnet: coininfo_1.default.litecoin.test.toBitcoinJS(),
|
|
48
|
+
derivationModes: [
|
|
49
|
+
DerivationMode.NATIVE,
|
|
50
|
+
DerivationMode.SEGWIT,
|
|
51
|
+
DerivationMode.LEGACY,
|
|
52
|
+
],
|
|
53
|
+
precision: Math.pow(10, 8),
|
|
54
|
+
utxo_based: true,
|
|
55
|
+
},
|
|
56
|
+
eth: {
|
|
57
|
+
name: "Ethereum",
|
|
58
|
+
symbol: "ETH",
|
|
59
|
+
precision: Math.pow(10, 18),
|
|
60
|
+
utxo_based: false,
|
|
61
|
+
derivationModes: [DerivationMode.ETHEREUM],
|
|
62
|
+
},
|
|
63
|
+
doge: {
|
|
64
|
+
name: "Dogecoin",
|
|
65
|
+
symbol: "DOGE",
|
|
66
|
+
network_mainnet: coininfo_1.default.dogecoin.main.toBitcoinJS(),
|
|
67
|
+
network_testnet: coininfo_1.default.dogecoin.test.toBitcoinJS(),
|
|
68
|
+
precision: Math.pow(10, 8),
|
|
69
|
+
utxo_based: true,
|
|
70
|
+
derivationModes: [DerivationMode.DOGECOIN],
|
|
71
|
+
},
|
|
72
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Currency } from "../models/currency";
|
|
2
|
+
declare const VERBOSE = false;
|
|
3
|
+
declare const ETH_FIXED_PRECISION = 10;
|
|
4
|
+
declare const DEFAULT_API_URLS: {
|
|
5
|
+
general: string;
|
|
6
|
+
bch: string;
|
|
7
|
+
eth: string;
|
|
8
|
+
};
|
|
9
|
+
declare const CRYPTOAPIS_URL = "https://rest.cryptoapis.io/v2/blockchain-data/{currency}/{network}";
|
|
10
|
+
declare const DERIVATION_SCOPE: {
|
|
11
|
+
quick_search: {
|
|
12
|
+
account: {
|
|
13
|
+
min: number;
|
|
14
|
+
max: number;
|
|
15
|
+
};
|
|
16
|
+
index: {
|
|
17
|
+
min: number;
|
|
18
|
+
max: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
deep_search: {
|
|
22
|
+
account: {
|
|
23
|
+
min: number;
|
|
24
|
+
max: number;
|
|
25
|
+
};
|
|
26
|
+
index: {
|
|
27
|
+
min: number;
|
|
28
|
+
max: number;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
declare const EXTERNAL_EXPLORERS_URLS: {
|
|
33
|
+
general: string;
|
|
34
|
+
bch: string;
|
|
35
|
+
eth: string;
|
|
36
|
+
};
|
|
37
|
+
export declare const configuration: {
|
|
38
|
+
currency: Currency;
|
|
39
|
+
testnet: boolean;
|
|
40
|
+
specificDerivationMode: string;
|
|
41
|
+
externalProviderURL: string;
|
|
42
|
+
APIKey: string | undefined;
|
|
43
|
+
providerType: string;
|
|
44
|
+
silent: boolean;
|
|
45
|
+
quiet: boolean;
|
|
46
|
+
commandLineMode: boolean;
|
|
47
|
+
gap_limit: string | number;
|
|
48
|
+
augmentedImport: boolean;
|
|
49
|
+
blockHeightUpperLimit: number;
|
|
50
|
+
};
|
|
51
|
+
export { DEFAULT_API_URLS, CRYPTOAPIS_URL, VERBOSE, ETH_FIXED_PRECISION, DERIVATION_SCOPE, EXTERNAL_EXPLORERS_URLS, };
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.EXTERNAL_EXPLORERS_URLS = exports.DERIVATION_SCOPE = exports.ETH_FIXED_PRECISION = exports.VERBOSE = exports.CRYPTOAPIS_URL = exports.DEFAULT_API_URLS = exports.configuration = void 0;
|
|
27
|
+
const dotenv = __importStar(require("dotenv"));
|
|
28
|
+
const currency_1 = require("../models/currency");
|
|
29
|
+
// ┏━━━━━━━━━┓
|
|
30
|
+
// ┃ GENERAL ┃
|
|
31
|
+
// ┗━━━━━━━━━┛
|
|
32
|
+
const VERBOSE = false;
|
|
33
|
+
exports.VERBOSE = VERBOSE;
|
|
34
|
+
const ETH_FIXED_PRECISION = 10; // Decimal places for ETH (recommended for Crypto APIs provider: 10)
|
|
35
|
+
exports.ETH_FIXED_PRECISION = ETH_FIXED_PRECISION;
|
|
36
|
+
// max number of addresses to probe when checking a possible gap between derivation indices
|
|
37
|
+
// (that is: range of indices not used for derivation)
|
|
38
|
+
const DEFAULT_GAP_LIMIT = 20;
|
|
39
|
+
// ┏━━━━━━━━━━━┓
|
|
40
|
+
// ┃ PROVIDERS ┃
|
|
41
|
+
// ┗━━━━━━━━━━━┛
|
|
42
|
+
// use {currency} and {address} as placeholders for the currency name and the address;
|
|
43
|
+
// {type} for the transaction type, and {item} for either an address or a transaction
|
|
44
|
+
const DEFAULT_API_URLS = {
|
|
45
|
+
general: "https://blockstream.info/{network}/api/address/{address}",
|
|
46
|
+
bch: "https://rest.bitcoin.com/v2/address/{type}/bitcoincash:{address}",
|
|
47
|
+
eth: "https://api.blockcypher.com/v1/eth/main/{type}/{item}",
|
|
48
|
+
};
|
|
49
|
+
exports.DEFAULT_API_URLS = DEFAULT_API_URLS;
|
|
50
|
+
// use {currency} and {network} as placeholders for the currency name and the network (mainnet v. testnet)
|
|
51
|
+
const CRYPTOAPIS_URL = "https://rest.cryptoapis.io/v2/blockchain-data/{currency}/{network}";
|
|
52
|
+
exports.CRYPTOAPIS_URL = CRYPTOAPIS_URL;
|
|
53
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
54
|
+
// ┃ XPUB <=> ADDRESS COMPARISON ┃
|
|
55
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
56
|
+
// scope of the derivation for the comparison
|
|
57
|
+
// only concerning xpub-search
|
|
58
|
+
const DERIVATION_SCOPE = {
|
|
59
|
+
// _quick search_
|
|
60
|
+
// the common range from which addresses
|
|
61
|
+
// are generally derived
|
|
62
|
+
quick_search: {
|
|
63
|
+
account: {
|
|
64
|
+
min: 0,
|
|
65
|
+
max: 4,
|
|
66
|
+
},
|
|
67
|
+
index: {
|
|
68
|
+
min: 0,
|
|
69
|
+
max: 1000,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
// _deep search_
|
|
73
|
+
// an extended range for a deeper analysis,
|
|
74
|
+
// initiated when quick search fails
|
|
75
|
+
deep_search: {
|
|
76
|
+
account: {
|
|
77
|
+
min: 0,
|
|
78
|
+
max: 1000,
|
|
79
|
+
},
|
|
80
|
+
index: {
|
|
81
|
+
min: 0,
|
|
82
|
+
max: 100000,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
exports.DERIVATION_SCOPE = DERIVATION_SCOPE;
|
|
87
|
+
// ┏━━━━━━━━━━━━━┓
|
|
88
|
+
// ┃ HTML REPORT ┃
|
|
89
|
+
// ┗━━━━━━━━━━━━━┛
|
|
90
|
+
const EXTERNAL_EXPLORERS_URLS = {
|
|
91
|
+
general: "https://live.blockcypher.com/{currency}/{type}/{item}",
|
|
92
|
+
bch: "https://blockchair.com/{currency}/{type}/{item}",
|
|
93
|
+
eth: "https://etherscan.io/{type}/{item}",
|
|
94
|
+
};
|
|
95
|
+
exports.EXTERNAL_EXPLORERS_URLS = EXTERNAL_EXPLORERS_URLS;
|
|
96
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━┓
|
|
97
|
+
// ┃ CONFIGURATION OBJECT ┃
|
|
98
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━┛
|
|
99
|
+
dotenv.config();
|
|
100
|
+
exports.configuration = {
|
|
101
|
+
currency: new currency_1.Currency(),
|
|
102
|
+
testnet: false,
|
|
103
|
+
specificDerivationMode: "",
|
|
104
|
+
externalProviderURL: "",
|
|
105
|
+
APIKey: process.env.XPUB_SCAN_CUSTOM_API_KEY_V2,
|
|
106
|
+
providerType: "default",
|
|
107
|
+
silent: false,
|
|
108
|
+
quiet: false,
|
|
109
|
+
commandLineMode: false,
|
|
110
|
+
gap_limit: process.env.GAP_LIMIT || DEFAULT_GAP_LIMIT,
|
|
111
|
+
augmentedImport: false,
|
|
112
|
+
blockHeightUpperLimit: 0, // comparison mode: block height limit
|
|
113
|
+
};
|
package/lib/display.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Address } from "./models/address";
|
|
2
|
+
import { Operation } from "./models/operation";
|
|
3
|
+
import { Summary } from "./types";
|
|
4
|
+
import { DerivationMode } from "./configuration/currencies";
|
|
5
|
+
import BigNumber from "bignumber.js";
|
|
6
|
+
declare function updateAddressDetails(address: Address): void;
|
|
7
|
+
declare function showSortedOperations(sortedOperations: Array<Operation>): void;
|
|
8
|
+
declare function showSummary(derivationMode: DerivationMode, totalBalance: BigNumber): void;
|
|
9
|
+
declare function logStatus(status: string): void;
|
|
10
|
+
declare function transientLine(message?: string): void;
|
|
11
|
+
declare function showResults(sortedUTXOs: Array<Address>, sortedOperations: Array<Operation>, summary: Array<Summary>, balanceOnly: boolean): void;
|
|
12
|
+
export { showSummary, logStatus, updateAddressDetails, showSortedOperations, transientLine, showResults, };
|
package/lib/display.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
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.showResults = exports.transientLine = exports.showSortedOperations = exports.updateAddressDetails = exports.logStatus = exports.showSummary = void 0;
|
|
7
|
+
const readline_1 = __importDefault(require("readline"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const settings_1 = require("./configuration/settings");
|
|
10
|
+
const bignumber_js_1 = __importDefault(require("bignumber.js"));
|
|
11
|
+
function renderAmount(amount) {
|
|
12
|
+
// Currently, this function does not convert the amounts
|
|
13
|
+
// into relevant units. But in the future, if the API
|
|
14
|
+
// changes, it would allow to change the unit
|
|
15
|
+
// depending on the network.
|
|
16
|
+
// For example:
|
|
17
|
+
// if (configuration.currency.symbol === currencies.btc.symbol) {
|
|
18
|
+
// return sb.toAccountUnit(amount);
|
|
19
|
+
// }
|
|
20
|
+
if (amount.isZero()) {
|
|
21
|
+
return String(amount);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
// 8 digital places max without trailing 0s
|
|
25
|
+
return amount.toFixed(8);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// display the active/probed address with its stats
|
|
29
|
+
function updateAddressDetails(address) {
|
|
30
|
+
// silent mode: do not display anything
|
|
31
|
+
if (settings_1.configuration.silent) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// quiet mode: only display full information, once
|
|
35
|
+
if (settings_1.configuration.quiet && typeof address.getStats() === "undefined") {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const derivationMode = address.getDerivationMode();
|
|
39
|
+
const account = address.getDerivation().account;
|
|
40
|
+
const index = address.getDerivation().index;
|
|
41
|
+
const derivationPath = "m/"
|
|
42
|
+
.concat(String(account))
|
|
43
|
+
.concat("/")
|
|
44
|
+
.concat(String(index));
|
|
45
|
+
const addressStats = address.getStats();
|
|
46
|
+
// _type_ path address ...
|
|
47
|
+
let stats = "";
|
|
48
|
+
if (settings_1.configuration.currency.utxo_based) {
|
|
49
|
+
// _{derivation mode}_ {derivation path} {address} [{cash address}]...
|
|
50
|
+
stats = stats
|
|
51
|
+
.concat(chalk_1.default.italic(derivationMode.padEnd(16, " ")))
|
|
52
|
+
.concat(derivationPath.padEnd(12, " "));
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
stats = stats.concat("\t");
|
|
56
|
+
}
|
|
57
|
+
const cashAddress = address.asCashAddress();
|
|
58
|
+
if (typeof cashAddress !== "undefined") {
|
|
59
|
+
stats = stats
|
|
60
|
+
.concat(address.toString().padEnd(36, " "))
|
|
61
|
+
.concat(cashAddress.padEnd(46, " "));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
stats = stats.concat(address.toString().padEnd(46, " "));
|
|
65
|
+
}
|
|
66
|
+
if (typeof address.getStats() === "undefined") {
|
|
67
|
+
// if no stats, display just half of the line
|
|
68
|
+
if (settings_1.configuration.commandLineMode) {
|
|
69
|
+
process.stdout.write(stats);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// else, display the full line
|
|
75
|
+
const balance = address.getBalance();
|
|
76
|
+
const fundedSum = renderAmount(addressStats.funded);
|
|
77
|
+
transientLine( /* delete line to display complete info */);
|
|
78
|
+
// ... +{total funded} ←
|
|
79
|
+
stats = stats
|
|
80
|
+
.concat(balance.toString().padEnd(16, " "))
|
|
81
|
+
.concat("+")
|
|
82
|
+
.concat(fundedSum.padEnd(14, " ")) // an active address has necessarily been funded,
|
|
83
|
+
.concat(" ←"); // thus this information is mandatory
|
|
84
|
+
}
|
|
85
|
+
// optional: spent sum
|
|
86
|
+
if (typeof addressStats.spent !== "undefined") {
|
|
87
|
+
const spentSum = renderAmount(addressStats.spent);
|
|
88
|
+
// ... -{total spent} →
|
|
89
|
+
stats = stats.concat("\t-").concat(spentSum.padEnd(14, " ")).concat(" →");
|
|
90
|
+
}
|
|
91
|
+
console.log(stats);
|
|
92
|
+
}
|
|
93
|
+
exports.updateAddressDetails = updateAddressDetails;
|
|
94
|
+
// display the list of UTXOs sorted by date (reverse chronological order)
|
|
95
|
+
function showSortedUTXOs(sortedUTXOs) {
|
|
96
|
+
if (settings_1.configuration.silent || !settings_1.configuration.currency.utxo_based) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
console.log(chalk_1.default.bold("\nUTXOs\n"));
|
|
100
|
+
if (sortedUTXOs.length === 0) {
|
|
101
|
+
console.log(chalk_1.default.gray("(no UTXO)"));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
sortedUTXOs.forEach((utxo) => {
|
|
105
|
+
updateAddressDetails(utxo);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// display the list of operations sorted by date (reverse chronological order)
|
|
109
|
+
function showSortedOperations(sortedOperations) {
|
|
110
|
+
if (settings_1.configuration.silent) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
process.stdout.write(chalk_1.default.bold("\nOperations History"));
|
|
114
|
+
if (typeof settings_1.configuration.APIKey === "undefined") {
|
|
115
|
+
// warning related to the limitations of the default provider
|
|
116
|
+
process.stdout.write(chalk_1.default.redBright(" (only the last ~50 operations by address are displayed)\n"));
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
process.stdout.write("\n");
|
|
120
|
+
}
|
|
121
|
+
const header = "\ndate\t\t\tblock\t\taddress\t\t\t\t\t\treceived (←) [as change from non-sibling (c)] | sent (→) to self (⮂) or sibling (↺)";
|
|
122
|
+
console.log(chalk_1.default.grey(header));
|
|
123
|
+
sortedOperations.forEach((op) => {
|
|
124
|
+
const amount = renderAmount(op.amount).padEnd(12, " ");
|
|
125
|
+
// {date} {block} {address} [{cash address}]
|
|
126
|
+
let status = op.date
|
|
127
|
+
.padEnd(20, " ")
|
|
128
|
+
.concat("\t")
|
|
129
|
+
.concat(String(op.block).padEnd(8, " "));
|
|
130
|
+
const address = op.address;
|
|
131
|
+
const cashAddress = op.cashAddress;
|
|
132
|
+
if (typeof cashAddress !== "undefined") {
|
|
133
|
+
status = status
|
|
134
|
+
.concat(address.padEnd(36, " "))
|
|
135
|
+
.concat(cashAddress.padEnd(46, " "));
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
status = status.concat("\t").concat(address.padEnd(42, " ")).concat("\t");
|
|
139
|
+
}
|
|
140
|
+
if (op.operationType.includes("Received")) {
|
|
141
|
+
// ... +{amount} ←
|
|
142
|
+
status = status.concat("+").concat(amount.padEnd(14, " ")).concat(" ←");
|
|
143
|
+
if (op.operationType === "Received (non-sibling to change)") {
|
|
144
|
+
status = status.concat(" c");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// ... -{amount} →|⮂|↺
|
|
149
|
+
status = status.concat("-").concat(amount.padEnd(14, " "));
|
|
150
|
+
const operationType = op.getOperationType();
|
|
151
|
+
if (operationType === "Sent to self") {
|
|
152
|
+
// case 1. Sent to the same address
|
|
153
|
+
status = status.concat(" ⮂");
|
|
154
|
+
}
|
|
155
|
+
else if (operationType === "Sent to sibling") {
|
|
156
|
+
// case 2. Sent to a sibling address
|
|
157
|
+
// (different non-change address belonging to same xpub)
|
|
158
|
+
status = status.concat(" ↺");
|
|
159
|
+
}
|
|
160
|
+
else if (operationType === "Failed to send") {
|
|
161
|
+
// case 3. Failed to send (Ethereum)
|
|
162
|
+
status = status.concat(" x");
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
// case 4. Sent to external address
|
|
166
|
+
status = status.concat(" →");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (op.operationType.includes("token")) {
|
|
170
|
+
// Token (Ethereum)
|
|
171
|
+
status = status.concat(" token");
|
|
172
|
+
}
|
|
173
|
+
if (op.operationType.includes("dapp")) {
|
|
174
|
+
// Token (Ethereum)
|
|
175
|
+
status = status.concat(" dapp");
|
|
176
|
+
}
|
|
177
|
+
if (op.operationType.includes("Swapped")) {
|
|
178
|
+
// Swapped (Ethereum)
|
|
179
|
+
status = status.concat(" Swapped");
|
|
180
|
+
}
|
|
181
|
+
if (op.operationType.includes("SCI")) {
|
|
182
|
+
// SCI (Ethereum)
|
|
183
|
+
status = status.concat(" sci");
|
|
184
|
+
}
|
|
185
|
+
console.log(status);
|
|
186
|
+
});
|
|
187
|
+
console.log(chalk_1.default.bold("\nNumber of transactions\n"));
|
|
188
|
+
console.log(chalk_1.default.whiteBright(sortedOperations.length));
|
|
189
|
+
}
|
|
190
|
+
exports.showSortedOperations = showSortedOperations;
|
|
191
|
+
// display the summary: total balance by address type
|
|
192
|
+
function showSummary(derivationMode, totalBalance) {
|
|
193
|
+
if (settings_1.configuration.silent) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const derivation = derivationMode.toString();
|
|
197
|
+
const balance = renderAmount(new bignumber_js_1.default(totalBalance));
|
|
198
|
+
if (balance === "0") {
|
|
199
|
+
console.log(chalk_1.default.grey(derivation.padEnd(16, " ").concat(balance.padEnd(12, " "))));
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
console.log(chalk_1.default
|
|
203
|
+
.whiteBright(derivation.padEnd(16, " "))
|
|
204
|
+
.concat(chalk_1.default.greenBright(balance.padEnd(12, " "))));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.showSummary = showSummary;
|
|
208
|
+
function logStatus(status) {
|
|
209
|
+
if (settings_1.configuration.silent) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
console.log(chalk_1.default.dim(status));
|
|
213
|
+
}
|
|
214
|
+
exports.logStatus = logStatus;
|
|
215
|
+
// overwrite last displayed line
|
|
216
|
+
// (no message: delete the line)
|
|
217
|
+
//
|
|
218
|
+
// note: if this implementation is modified,
|
|
219
|
+
// always check the resulting behavior in
|
|
220
|
+
// Docker
|
|
221
|
+
function transientLine(message) {
|
|
222
|
+
if (settings_1.configuration.silent || settings_1.configuration.quiet) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
readline_1.default.cursorTo(process.stdout, 0);
|
|
226
|
+
if (typeof message !== "undefined") {
|
|
227
|
+
process.stdout.write(message);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// blank line
|
|
231
|
+
// ! solution implemented this way to be
|
|
232
|
+
// ! compatible with Docker
|
|
233
|
+
process.stdout.write("".padEnd(140, " "));
|
|
234
|
+
readline_1.default.cursorTo(process.stdout, 0);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
exports.transientLine = transientLine;
|
|
238
|
+
function showResults(sortedUTXOs, sortedOperations, summary, balanceOnly) {
|
|
239
|
+
if (settings_1.configuration.silent) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
showSortedUTXOs(sortedUTXOs);
|
|
243
|
+
if (!balanceOnly) {
|
|
244
|
+
showSortedOperations(sortedOperations);
|
|
245
|
+
}
|
|
246
|
+
console.log(chalk_1.default.bold("\nSummary\n"));
|
|
247
|
+
for (const total of summary) {
|
|
248
|
+
showSummary(total.derivationMode, total.balance);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
exports.showResults = showResults;
|
package/lib/helpers.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
declare function getJSON<T>(url: string, APIKey?: string, { retries, retryDelayMS }?: {
|
|
3
|
+
retries?: number;
|
|
4
|
+
retryDelayMS?: number;
|
|
5
|
+
}): Promise<T>;
|
|
6
|
+
declare function retry<T>(job: () => Promise<T>, { retries, retryDelayMS }?: {
|
|
7
|
+
retries?: number | undefined;
|
|
8
|
+
retryDelayMS?: number | undefined;
|
|
9
|
+
}): Promise<T>;
|
|
10
|
+
declare function setNetwork(xpub: string, currency?: string, testnet?: boolean): void;
|
|
11
|
+
declare function init(xpub: string, silent?: boolean, quiet?: boolean, currency?: string, testnet?: boolean, derivationMode?: string): void;
|
|
12
|
+
declare function toUnprefixedCashAddress(address: string): string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Convert from unit of account to base unit (e.g. bitcoins to satoshis)
|
|
15
|
+
* @param amount the amount (in unit of account) to convert
|
|
16
|
+
* @returns the converted amount, in base unit
|
|
17
|
+
*/
|
|
18
|
+
declare function toBaseUnit(amount: BigNumber): string;
|
|
19
|
+
/**
|
|
20
|
+
* Convert from base unit to unit of account (e.g. satoshis to bitcoins)
|
|
21
|
+
* @param amount the amount (in base unit) to convert
|
|
22
|
+
* @param decimalPlaces (optional) decimal precision
|
|
23
|
+
* @returns the converted amount, in unit of account
|
|
24
|
+
*/
|
|
25
|
+
declare function toAccountUnit(amount: BigNumber, decimalPlaces?: number): string;
|
|
26
|
+
declare function getNetworkLabel(): "ropsten" | "testnet" | "mainnet";
|
|
27
|
+
export { init, getJSON, getNetworkLabel, retry, setNetwork, toUnprefixedCashAddress, toBaseUnit, toAccountUnit, };
|