Creating a secure, functional cryptocurrency wallet is a pivotal skill for developers entering the blockchain space. This comprehensive guide walks you through building a fully encrypted Bitcoin wallet using TypeScript and essential cryptographic libraries like BitcoinJS, ecpair, tiny-secp256k1, and CryptoJS. You'll learn how to generate keys, manage wallet data securely, store encrypted information, and maintain transaction history—all while prioritizing security at every step.
Whether you're a beginner exploring blockchain development or an experienced coder diving into decentralized systems, this tutorial provides practical insights into wallet architecture, encryption best practices, and local data persistence.
Core Components of a Bitcoin Wallet
A cryptocurrency wallet acts as the bridge between users and the blockchain. It enables key operations such as:
- Generating public and private key pairs
- Creating and signing transactions
- Managing digital assets (like BTC)
- Storing sensitive data securely
For most users, wallets are the primary interface for interacting with Bitcoin. As a developer, understanding how to build one ensures you grasp fundamental cryptographic principles and security considerations critical in decentralized applications.
Key Libraries Used
To streamline development, we use several well-maintained JavaScript/TypeScript libraries:
- BitcoinJS-lib: Enables Bitcoin transaction creation, address generation, and script handling.
- ecpair: Manages elliptic curve key pairs within the BitcoinJS ecosystem.
- tiny-secp256k1: Provides optimized cryptographic operations on the secp256k1 curve used by Bitcoin.
- CryptoJS: Handles symmetric encryption (AES) to protect stored wallet data.
- fs (Node.js native module): Allows reading/writing files for local storage.
- dotenv: Safely loads environment variables like encryption keys.
👉 Discover powerful tools to test your blockchain applications securely.
Project Setup and Dependencies
Start by creating a new directory:
mkdir Bitcoin_Cli_Wallet && cd Bitcoin_Cli_Wallet
npm init -yInstall TypeScript and execution tools:
npm install -D typescript ts-nodeAdd required dependencies:
npm install bitcoinjs-lib ecpair tiny-secp256k1 crypto-js dotenvCreate a tsconfig.json file to configure TypeScript compilation, then set up the following folder structure:
Bitcoin_Cli_Wallet/
├── wallets/
├── index.ts
└── .envThe wallets/ folder will store encrypted wallet files and history.
Environment Configuration
Create a .env file in the root directory:
ENCRYPTION_KEY=MySuperSecretKey
ALGORITHM=aes-256-cbc🔐 Never commit .env files to version control. These contain sensitive data.Load them in your code using dotenv.config().
Importing Dependencies
In index.ts, import all required modules:
import * as bitcoin from 'bitcoinjs-lib';
import { ECPairFactory, ECPairInterface } from 'ecpair';
import * as ecc from 'tiny-secp256k1';
import * as fs from 'fs';
import * as crypto from 'crypto-js';
import * as dotenv from 'dotenv';
dotenv.config();Initialize the EC pair factory with secp256k1 support:
const ECPair = ECPairFactory(ecc);Define network types for flexibility:
const TESTNET = bitcoin.networks.testnet;
const REGTEST = bitcoin.networks.regtest;
const BITCOIN = bitcoin.networks.bitcoin;Wallet Creation Logic
We define a createWallet function that takes three parameters:
walletname: A unique identifier for the walletnetwork: Target network (testnet,regtest, ormainnet)walletType: Address format (e.g.,p2pkh,p2wpkh)
function createWallet(walletname: string, network: string, walletType: string) {
let history = fetchWalletHistory();
let keyPair = decodeNetwork(network);
let walletData = decodeWalletType(walletType, keyPair, network);
let address = walletData.address;
// Update history
history.totalWallets += 1;
history.allWallets.push(walletname);
history.allAddresses.push(address);
// Save wallet and updated history
writeWalletDataToFile(walletname, { keyPair, walletData, walletType });
writeWalletHistoryToFile(history);
console.log("Wallet created successfully. Address:", address);
}This modular approach separates concerns: key generation, type decoding, and secure storage.
Decoding Network and Wallet Types
Selecting the Correct Blockchain Network
function decodeNetwork(network: string): ECPairInterface {
switch (network) {
case 'testnet': return ECPair.makeRandom({ network: TESTNET });
case 'bitcoin': return ECPair.makeRandom({ network: BITCOIN });
case 'regtest': return ECPair.makeRandom({ network: REGTEST });
default: throw new Error('Invalid network type');
}
}Supporting Multiple Address Formats
Bitcoin supports various output scripts. Our wallet handles common ones:
function decodeWalletType(type: string, keyPair: ECPairInterface, network: string) {
const net = network === 'testnet' ? TESTNET : BITCOIN;
switch (type) {
case 'p2pkh': return bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network: net });
case 'p2wpkh': return bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey, network: net });
case 'p2sh': return bitcoin.payments.p2sh({ redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }), network: net });
default: throw new Error('Unsupported wallet type');
}
}This extensible design allows future support for multisig or taproot (p2tr) wallets.
Secure Data Storage with Encryption
All wallet data must be encrypted before being written to disk.
Encrypting Wallet Data
function encryptData(data: string): string {
const cipher = crypto.AES.encrypt(data, process.env.ENCRYPTION_KEY!);
return cipher.toString();
}Decrypting Stored Files
function decryptData(cipherText: string): string {
const bytes = crypto.AES.decrypt(cipherText, process.env.ENCRYPTION_KEY!);
return bytes.toString(crypto.enc.Utf8);
}Files are saved as .json in the wallets/ directory with AES-256-CBC encryption.
Managing Wallet History
Track all created wallets with a central history.json file:
function writeWalletHistoryToFile(history: History): void {
const encrypted = encryptData(JSON.stringify(history));
fs.writeFileSync('./wallets/history.json', encrypted, 'utf8');
}
function fetchWalletHistory(): History {
const path = './wallets/history.json';
if (!fs.existsSync(path)) {
const init: History = { totalWallets: 0, allWallets: [], allAddresses: [] };
writeWalletHistoryToFile(init);
return init;
}
const data = fs.readFileSync(path, 'utf8');
const decrypted = decryptData(data);
return JSON.parse(decrypted);
}This ensures persistence across sessions and supports recovery workflows.
Frequently Asked Questions
Q: Can I use this wallet on mainnet?
A: Yes—but only after thorough testing on testnet or regtest. Always ensure your encryption is robust before handling real funds.
Q: How secure is AES-256 encryption for wallet storage?
A: AES-256 is industry-standard and considered highly secure when implemented correctly. The strength depends on your ENCRYPTION_KEY. Use a long, random secret—not a simple phrase.
Q: What happens if I lose my private key or encryption password?
A: There is no recovery mechanism in this CLI tool. Like all self-custodial wallets, you are your own bank. Losing keys means permanent loss of access.
Q: Is this wallet compatible with hardware wallets?
A: Not directly. This implementation is software-based. However, the same cryptographic principles apply to hardware integrations.
Q: Can I extend this to support Ethereum or other blockchains?
A: While the current code is Bitcoin-specific, similar patterns apply. Different chains use different libraries (e.g., ethers.js for Ethereum), but key management and encryption concepts remain consistent.
👉 Explore secure environments to test multi-chain applications.
Testing the Implementation
Add test code at the bottom of index.ts:
console.log("Starting wallet creation tests...");
createWallet("myWallet", "regtest", "p2pkh");
createWallet("BobWallet", "regtest", "p2pkh");
createWallet("AliceWallet", "regtest", "p2pkh");
listAllAddresses();Compile and run:
npx tsc index.ts
node index.jsYou should see success messages and a list of generated addresses.
Final Thoughts and Next Steps
This minimal CLI wallet demonstrates core concepts:
- Secure key generation
- Encrypted local storage
- Multi-network support
- Extensible address types
Future enhancements could include:
- Transaction signing and broadcasting
- UTXO tracking
- Backup/export functionality
- Mnemonic phrase recovery (BIP39)
While this version doesn’t handle spending yet, it lays the foundation for a full-featured Bitcoin client.
👉 Accelerate your blockchain development with advanced tools and APIs.
Core Keywords:
Bitcoin wallet development, TypeScript crypto wallet, encrypted wallet storage, BitcoinJS tutorial, blockchain security, CLI wallet creation, secp256k1 cryptography