Standalone Non-Custodial Cryptocurrency Wallet Generator
CryptoCalc is a non-custodial, standalone desktop application for generating cryptocurrency wallets. It is designed to run entirely offline ("cold wallet" philosophy) so that sensitive cryptographic material — Private Keys, WIF, and Secret phrases — never leaves the user's machine and is never exposed to a remote server.
Wallets can be Non-Deterministic (Simple Wallet) or
Hierarchical Deterministic (HD Wallet, BIP32).
CryptoCalc supports 23 cryptocurrencies and 18 Secret phrase languages.
The application is built with JavaScript / ElectronJS and is available as a
standalone installer (Windows) as well as via source clone (Windows & Linux).
Wallet output is saved as .wits JSON files under a timestamped
_output/ subfolder.
All wallet generation starts with a high-quality Entropy value. CryptoCalc provides four distinct entropy sources:
The user rolls virtual six-sided dice. The number of rolls is determined by the chosen Entropy Size (e.g. 100 rolls for 256-bit entropy).
Entropy bytes are captured from the user's mouse pointer movements, providing a non-deterministic physical source.
The user can drag-and-drop any PNG, JPG, or
SVG image onto the application. Pressing [Generate] draws a
random cryptocurrency logo from www/img/CryptoCurrency.
Sample images are provided in www/img/.
A corpus of 12 803 quotes is used as a source. A random quote is drawn each time [Generate] is pressed.
Regardless of the source, CryptoCalc combines the raw entropy value with a
Salt (a dynamically generated UUID, 128 bits of additional entropy)
using SHA-256:
Salted Entropy = SHA256(Salt + Entropy_source_value)
This guarantees uniqueness at every [Generate] press, even when the same image or fortune cookie is reused.
The user can choose an entropy size between 128 and 256 bits (32 to 64 hex digits), which corresponds to a Secret phrase of 12 to 24 words. Both representations remain synchronized in real time.
Three wallet generation modes are available, selectable from the Wallet tab page.
Default mode. Each wallet is independent; no BIP32 hierarchy or Derivation Path is involved. Ideal for beginners. Less resistant to dictionary attacks if low-entropy inputs are used.
Full Hierarchical Deterministic wallet tree. All wallets share the same Secret phrase. Navigation via Account and Address Index fields (0–999 999). Supports BIP44, BIP49, BIP84 purposes.
Simple Wallet Over Randomized Deterministic — a hybrid. Generates all HD-wallet-compatible cryptocurrencies without exposing the BIP32 derivation path complexity to the user.
For Cardano HD wallets, Account and Address Index are hard-coded to 0 to match the behavior of Guarda and Yoroi wallet managers.
Entropy is converted to a BIP39 Secret phrase (also called Mnemonics
or Secret Recovery Passphrase) of 12 to 24 words. The conversion appends a
checksum (first N bits of SHA256(entropy)) to pad to a multiple of 11 bits,
then each 11-bit segment selects a word from the 2048-word BIP39 dictionary.
Because only the first 4 characters of each BIP39 mnemonic are significant, CryptoCalc derives a Shortened Secret phrase where each word is abbreviated to its first 4 characters (first letter capitalized):
Example (12 words):
Full: rent expand super sea summer pull catalog mobile proud solve oven goose
Short: RentExpaSupeSeaSummPullCataMobiProuSolvOvenGoos
NTAG213 NFC SmartRing with
144 bytes capacity — 24 words × 4 chars = 96 bytes max).
Word indexes (0–2047) are displayed alongside the Secret phrase in Decimal or Binary format. In Binary mode, the checksum bits appended to the last word are clearly visible. Word indexes are language-independent — the same entropy yields the same indexes regardless of the chosen wordlist language.
Pasting a raw hex entropy value automatically recomputes the Secret phrase, and vice versa.
An optional BIP39 Passphrase (acting as an additional password) shifts the
entire BIP32 hierarchy to a completely different tree. To avoid accidental application,
the field is readonly by default:
CryptoCalc uses the standard BIP44 derivation path structure:
m / purpose' / coin_type' / account' / change / address_index
Example for Ethereum: m/44'/60'/0'/0/0
| Segment | Meaning |
|---|---|
m | Master key (root of the tree) |
44' | BIP44 purpose (hardened) |
60' | Coin type (e.g. 60 = Ethereum) |
0' | Account (hardened by default) |
0 | External chain (public addresses) |
0 | Address index |
BIP44 — legacy (all supported coins)BIP49 — SegWit P2SH-P2WPKHBIP84 — native SegWit (Bitcoin and Litecoin only)Both fields accept decimal values from 0 to 999 999 (6 digits max), giving 1 000 000 possible values each. Editing either field displays a [Refresh] button (also triggered by Enter) to recompute the wallet address.
Hardened derivation is mandatory and on by default (since v0.3.18) for security: a compromised child key cannot be used to derive sibling or parent keys.
CryptoCalc supports BIP38 NON-EC encryption: the Private Key is symmetrically encrypted with a user-supplied passphrase, producing a BIP38 Encrypted Private Key.
A dedicated dialog (Tools / BIP38 Encrypt/Decrypt) is provided to:
A progress bar gives visual feedback during the time-consuming scrypt computation.
The BIP38 Encrypted PK allows outsourcing paper wallet printing (with premium features: watermark, hologram, embossing…) without disclosing the raw Private Key to the printer. The Passphrase is communicated via a separate channel.
wallet_info.txt.
The BIP38 Encrypted PK may be shared in certain use cases; the raw Private Key must
never be disclosed, nor should the BIP38 Passphrase.
A new QR code for the BIP38 Encrypted PK is generated as both a PNG file
and an SVG file (in the xtras/ subfolder).
A real-time visual indicator evaluates the strength of the BIP39 and
BIP38 passphrases using the
zxcvbn
library (developed by Dropbox). It accounts for dictionary words, common patterns,
keyboard walks, and Leetspeak substitutions.
| Score | Label | Color |
|---|---|---|
| 0 | Very Weak | Red |
| 1 | Weak | Orange |
| 2 | Fair | Yellow |
| 3 | Good | Green |
| 4 | Strong | Violet |
The score is displayed as a colored bar (length proportional to score) and a text label. Hovering over the label shows the entropy in bits.
QR codes are automatically generated and saved when a wallet is saved. They cover the following data fields:
xtras/ subfolder)R15x59 or R15x77
depending on entropy size) of the Entropy valuewallet_info.txt / wallet_info.wits.
Use File / Save or the Save toolbar icon.
A timestamped subfolder is created under _output/
(e.g. 2024_10_07_21h-4m-4s-3_BTC_EN), containing:
wallet_info.txt — human-readable wallet fieldswallet.wits — machine-readable JSON (all entropy + wallet data)xtras/ — SVG, rMQR and Ultracode QR variantsA popup dialog confirms saving and offers to open the output folder.
Use File / Open… or the Open toolbar icon to load a
.wits file. On Windows, double-clicking a .wits file
launches Cryptocalc.exe and opens it directly (file-extension association).
Via Tools / Database Management, previously saved
.wits files can be imported into a local SQLite database.
The database can then be explored and queried with an external tool such as
DBeaver
(which also provides a visual DB schema).
.wits JSON files in timestamped subfolders under _output/Tools / Secret Phrase Translator)Translates a Secret phrase from one BIP39 language to another. Primary use case: a Secret phrase written in a non-English language (e.g. Russian) must be translated to English before it can be imported into a Wallet Manager.
Word Indexes pseudo-language outputs the numeric word indexes
(language-independent).Tools / BIP38 Encrypt/Decrypt)Standalone dialog to encrypt a raw Private Key to a BIP38 Encrypted PK, or conversely decrypt a BIP38 Encrypted PK back to the raw Private Key. A progress bar shows the computation progress.
Tools / Options)Set persistent defaults for:
Options are stored in www/config/options.json and can be reset to
factory defaults (from www/config/defaults/options.json).
CryptoCalc provides contextual links that open the relevant page in the user's browser:
An icon on the right side of the main toolbar permanently displays the network status:
A text label Online / Offline accompanies the icon. This reinforces the cold-wallet, non-custodial philosophy of the application.
Options (Tools / Options) persist across sessions in
www/config/options.json:
| Option | Values | Default |
|---|---|---|
| Default Blockchain | Any of the 23 supported coins | BTC |
| Wallet Mode | Simple / HD / SWORD | Simple |
| Entropy Size | 128 / 160 / 192 / 224 / 256 bits | 256 bits |
A Reset to Defaults action restores the values from
www/config/defaults/options.json.
GUI labels are translated to the user's system language via JSON localization files
located in www/js/L10n/. The locale is determined from the host OS
environment (2-letter ISO code, e.g. en, fr).
gui-msg-en.json)
and French (gui-msg-fr.json).gui-msg-XX.json file.CryptoCalc supports wallet generation for the following 23 cryptocurrencies:
BTC — Bitcoin ETH — Ethereum XRP — Ripple BNB — Binance Smart Chain SOL — Solana DOGE — Dogecoin TRX — TRON ADA — Cardano XLM — Stellar SUI — Sui BCH — Bitcoin Cash AVAX — Avalanche TON — Toncoin LTC — Litecoin ETC — Ethereum Classic POL — Polygon VET — VeChain BSV — Bitcoin SV DASH — Dash RVN — Ravencoin ZEN — Horizen LUNA — Terra 2.0 FIRO — Firo
_doc/top_50_marketcap_coins.txt.
English French Spanish Italian Czech Portuguese Simplified Chinese Traditional Chinese Japanese Korean
Deutsch Russian Esperanto Latin Greek Hindi Gujarati Bengali
Using a non-English language is an obfuscation layer: the Secret phrase must be translated back to English (via the Secret Phrase Translator tool) before it can be imported into a Wallet Manager. The reference data is always the Word Indexes, which are language-independent.
CryptoCalc is designed to be used offline. The Internet Connection Status indicator warns the user when the machine is online. Generating wallets while connected to the Internet is actively discouraged.
HD wallets use hardened derivation by default, preventing a compromised child key from being used to derive sibling or parent keys.
The Private Key can be encrypted with a BIP38 Passphrase, adding a second layer of protection if the wallet file is ever exposed (see §6).
Storing the Secret phrase in a non-English language makes it unusable without knowledge of the language used. Combined with the BIP39 Passphrase, this provides a multi-layer security approach.
The Shortened Secret phrase (max 96 bytes for 24 words) can be stored on an entry-level
NTAG213 NFC SmartRing (144 bytes usable capacity), providing a wearable
physical backup.
Download the installer from SourceForge. Generated with electron packager and Inno Setup.
C:\Users\<USER>\AppData\Local\Programs\Cryptocalc…\resources\app\_output\ (not removed on uninstall)_inno_setup/ howto.Prerequisites: NodeJS, Git
git clone https://github.com/ALADAS-org/cryptocalc.git cd cryptocalc npm install _runW.bat # or: npm start
Prerequisites: NodeJS, Git, npm, xdg-utils
sudo apt-get install nodejs git npm xdg-utils git clone https://github.com/ALADAS-org/Cryptocalc.git cd Cryptocalc npm install chmod 777 ./_runX.sh ./_runX.sh # or: npm start
Unit tests use the Jest framework (ongoing effort since v0.5.4). They run in a Node.js environment (no browser / Electron renderer required) and cover cryptographic utilities, wallet generators, and general-purpose helpers.
npm test # run all Jest unit tests npx jest <path/to/file.test.js> # run a single file npm run test:jest:watch # watch mode npm run test:jest:coverage # with HTML coverage report
The HTML coverage report is generated at
tests/coverage/jest/test-report.html.
Coverage thresholds: ≥ 70% for all four metrics
(branches, functions, lines, statements).
jest.config.js)| Setting | Value |
|---|---|
| Test environment | Node.js |
| Timeout | 10 000 ms |
| Test root | tests/jest/ |
| Workers | 50% of available CPUs |
| Coverage reporters | text, text-summary, lcov, html, json, cobertura |
| HTML test report | tests/coverage/jest/test-report.html (via jest-html-reporter) |
| Alias | Resolved path |
|---|---|
@/ | www/ |
@crypto/ | www/js/crypto/ |
@api/ | www/js/api/ |
@util/ | www/js/util/ |
@tests/ | tests/jest/ |
@fixtures/ | tests/jest/fixtures/ |
tests/jest/setup.js)Provides shared helpers and custom matchers available to all test files:
loadFixture(filename) — load JSON from fixtures/inputs/loadExpected(filename) — load JSON from fixtures/expected/saveFixture(filename, data, type) — persist fixture filestoBeValidHash(length) — validates a hex string of exact lengthtoBeValidBitcoinAddress() — legacy, P2SH & SegWit formatstoBeValidWIF() — WIF private key formattoBeValidEthereumAddress() — checksummed ETH addresstoBeValidMnemonic() — 12/15/18/21/24-word BIP39 phrase| File | Scope | Key tested functions | # tests |
|---|---|---|---|
crypto/bip39_utils.test.js |
BIP39 core | EntropyToMnemonics, MnemonicsToEntropyInfo, GetWordIndexes, GuessMnemonicsLang, CheckMnemonics, MnemonicsAs4letter, GetBIP39Dictionary… | 77 |
crypto/bip39_extras.test.js |
BIP39 extras | PrivateKeyToMnemonics, MnemonicsAs4letter, MnemonicsAsTwoParts, LabelWithSize | 32 |
crypto/bip32_utils.test.js |
BIP32 / HD Wallet | MnemonicsToHDWalletInfo (BTC/ETH/+), GetDerivationPath, account & address index derivation, BIP39 passphrase, BIP44/49/84 purposes, reference vectors | 112 |
crypto/bip38_utils.test.js |
BIP38 encryption | encrypt(), decrypt(), scrypt params, round-trip, wrong passphrase, progress callback (bip38 mocked) | 79 |
crypto/crypto_services.test.js |
CryptoServices singleton | getUUID() (UUID v4 format), pk2WIF(), singleton pattern | ~15 |
crypto/entropy_size.test.js |
Entropy ↔ size helpers | GetBitCount, GetWordCount, GetExpectedWordCount, GetChecksumBitCount, GetSHA256Substring | 29 |
crypto/password_strength_evaluator.test.js |
Passphrase strength | is_binary_string, is_hexa_string, is_base58/64/octal, getPasswordStrengthAsBits, getPasswordStrengthScore (zxcvbn), getPasswordStrengthInfo | 79 |
crypto/base58_utils.test.js |
Base58 encoding | isBase58String, hexToB58, b58ToHex, round-trip, alphabet validation | 18 |
utils/hex_utils.test.js |
Hex utilities | hexWithPrefix/WithoutPrefix, isHexString, hexToBinary, hexToUint8Array, hexToB64, getRandomHexValue, round-trips | 43 |
utils/number_utils.test.js |
Number helpers | stringToInt, stringToFloat, valueIsNumber, stringIsNumber | 28 |
utils/string_utils.test.js |
String helpers | isString, stringify (circular ref), insertAfterEveryN, stringToHex, getShortenedString, asTwoParts | 35 |
wallet/address_validation.test.js |
Address formats | Bitcoin (legacy/P2SH/SegWit), Ethereum checksummed, WIF (compressed/uncompressed), BIP39 mnemonic (12/24 words) | 8 |
wallet/simple_wallet.test.js |
Simple Wallet generation | Wallet structure for BTC, ETH, DOGE, LTC, SOL, AVAX, POL, TON, LUNA, ZEN — validates address, private key, public key, mnemonics, wallet mode | 10+ |
smoke.test.js |
Jest config sanity | TEST_MODE global, CRYPTO_CONFIG (SUPPORTED_COINS, ENTROPY_SIZES), TEST_PATHS | 4 |
The BIP32 tests include deterministic reference checks with known expected values
(account=2, address_index=5, passphrase="my secret passphrase"):
| Coin | Address | Private Key (prefix) |
|---|---|---|
| Bitcoin | 1BQQ4VjXtPGd3YEV45vuMkNKjo42pjLLUB |
ca2dc38… / WIF L3ziiGb… |
| Ethereum | 0x67C0ae27e79Ba1B6f58af5DCF3d893f7394ac0e5 |
cfe5210… |
End-to-end tests launch the full Electron application and interact with it programmatically via Playwright. They validate complete user workflows — from entropy generation to wallet save/reload.
npm run test:playwright # headless npm run test:playwright:ui # interactive Playwright UI npm run test:playwright:debug # step-by-step debugger
tests/playwright/playwright.config.js)| Setting | Value |
|---|---|
| Test directory | tests/playwright/e2e/ |
| Workers | 1 (Electron limitation — no parallel tests) |
| Retries on failure | 2 |
| Test timeout | 30 s |
| Action timeout | 10 s |
| Navigation timeout | 30 s |
| Screenshots/Videos/Traces | Only on failure |
| Report | HTML — tests/playwright/playwright-report/ |
| Raw results | tests/playwright/test-results/ |
tests/playwright/setup.js)electronApp — launches www/js/_main/electron_main.jspage — waits for DOM content loadedappScreenshot — timestamped screenshotswaitForAppReady(page) — DOM ready + 3 s Electron init delayloadFixture(filename) — loads JSON from fixtures/hd_wallet_usecase.test.js)Tests BIP32 HD wallet derivation for Bitcoin with fixed, reproducible input data (fixed salt + entropy, account=2, address_index=5).
| Test case | What is verified |
|---|---|
| Bitcoin address validity | Derived address matches legacy/P2SH/Bech32 regex |
| BIP39 passphrase effect | Same account/index with different passphrase → different address |
Page interaction helpers used:
switchToHDWallet, deriveFromFixedEntropy,
setBip39Passphrase, clickRefresh, getDisplayedAddress,
setFieldValue.
dogecoin_hd_wallet_usecase.test.js)Tests the complete wallet lifecycle for Dogecoin HD wallets.
| Step | Action | Assertion |
|---|---|---|
| 1 | Select Dogecoin, HD mode, account=1, index=3 | Address generated and displayed |
| 2 | Save wallet (/tmp/cryptocalc_test_doge_1.json, dialogs mocked) | File written successfully |
| 3 | Open saved .wits file | Address and fields reload correctly |
| 4 | Change account → 4, address index → 7, press [Refresh] | New address is different from original |
| 5 | Save As (/tmp/cryptocalc_test_doge_2.json) | Second file contains updated address |
Page interaction helpers used:
switchToHDWallet, selectBlockchain, setFieldValue.
tests/playwright/_doc/hd_wallet_usecase_test_protocols.html.
bs58 v5.0.0 (pinned — ESM incompatibility in v6+)ed25519-hd-key, @mysten/sui, @ton/cryptozxcvbn — passphrase strengthcashaddrjs — Bitcoin Cash address encodingtests/coverage/jest/test-report.html| Layer | Entry Point / Key File | Role |
|---|---|---|
| Main Process | www/js/_main/electron_main.js | BrowserWindow, IPC, file I/O, menus |
| Preload | www/js/_main/preload.js | Safe IPC bridge to renderer |
| Renderer | www/index.html | UI entry point |
| Crypto | www/js/crypto/ | BIP32/39/38, wallet generators |
| View | www/js/view/main_gui.js | MainGUI singleton, dialogs |
| Model | www/js/model/ | QR codes, wallet persistence, SQLite |
| Utils | www/js/util/ | Logging, HTML helpers, value utils |
| L10n | www/js/L10n/ | GUI label translations (JSON) |