new Custodial_Wallet()
Custodial wallet implementation supporting hierarchical deterministic key derivation and standard ECDSA signatures. Suitable for single-party control scenarios.
This class provides traditional Bitcoin wallet functionality with full control over private keys. It implements BIP32 hierarchical deterministic key derivation, allowing generation of unlimited child keys from a single seed. Perfect for individual users, mobile wallets, and applications requiring straightforward key management.
Key Features:
- BIP32 hierarchical deterministic key derivation
- BIP39 mnemonic phrase support for backup and recovery
- Standard ECDSA signature generation and verification
- Support for Bitcoin mainnet and testnet
- Child key derivation with configurable paths
- Address generation for receiving payments
Security Model:
- Single point of control (private key holder has full access)
- Suitable for individual users and trusted environments
- Mnemonic phrases enable secure backup and recovery
- Child keys provide address privacy without exposing master key
Use Cases:
- Personal Bitcoin wallets
- Mobile wallet applications
- Desktop wallet software
- Simple payment processing systems
- Development and testing environments
- Since:
- 1.0.0
- Source:
Examples
// Generate a new random wallet
const [mnemonic, wallet] = Custodial_Wallet.fromRandom('main');
console.log('Mnemonic:', mnemonic);
console.log('Address:', wallet.address);
// Import from existing mnemonic
const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
const wallet = Custodial_Wallet.fromMnemonic('main', mnemonic, 'password');
console.log('Imported address:', wallet.address);
// Derive child keys for different purposes
const wallet = Custodial_Wallet.fromRandom('main')[1];
// BIP44 Bitcoin receiving addresses
wallet.derive("m/44'/0'/0'/0/0", 'pri'); // First receiving address
wallet.derive("m/44'/0'/0'/0/1", 'pri'); // Second receiving address
// BIP44 Bitcoin change addresses
wallet.derive("m/44'/0'/0'/1/0", 'pri'); // First change address
console.log('Child keys:', Array.from(wallet.child_keys));
// Sign and verify messages
const wallet = Custodial_Wallet.fromRandom('main')[1];
const message = "Hello Bitcoin!";
// Sign message
const [signature, recoveryId] = wallet.sign(message);
// Verify signature
const isValid = wallet.verify(signature, message);
console.log('Signature valid:', isValid); // true
Members
(static, readonly) this.address :string
Bitcoin address for this wallet, derived from the master public key. Used for receiving payments and identifying the wallet on the blockchain. Format depends on network (1... for mainnet, m/n... for testnet).
Type:
- string
- Source:
Example
console.log(wallet.address); // "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2"
(static) this.child_keys :Set.<ChildKeyInfo>
Set of derived child keys from this wallet. Contains information about all child keys derived using the derive() method. Each entry includes depth, index, keys, and address information.
Type:
- Set.<ChildKeyInfo>
- Source:
Example
wallet.derive("m/0'/1", 'pri');
console.log(wallet.child_keys.size); // 1
for (const childKey of wallet.child_keys) {
console.log('Child address:', childKey.address);
console.log('Derivation depth:', childKey.depth);
}
(static, readonly) this.hdKey :HDKeys
Hierarchical deterministic key pair for this wallet. Contains both extended private and public keys in standard BIP32 format. Used for deriving child keys and maintaining the HD wallet structure.
Type:
- Source:
Example
console.log(wallet.hdKey.HDpri); // "xprv9s21ZrQH143K..."
console.log(wallet.hdKey.HDpub); // "xpub661MyMwAqRbcF..."
(static, readonly) this.keypair :KeyPair
Standard key pair for direct cryptographic operations. Contains WIF-encoded private key and hex-encoded compressed public key. Used for signing transactions and generating addresses.
Type:
- Source:
Example
console.log(wallet.keypair.pri); // "L5HgWvFghocq1FmxSjKNaGhVN8f67p6xYg5pY7M8FE77HXwHtGGu"
console.log(wallet.keypair.pub); // "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"
(static, readonly) this.net :string
Network type for this wallet instance. Determines address formats, version bytes, and network-specific parameters.
Type:
- string
- Source:
Example
console.log(wallet.net); // "main" or "test"
Methods
derive(pathopt, keyTypeopt) → {Custodial_Wallet}
Derives a child key from the current wallet using BIP32 hierarchical deterministic path.
This method implements BIP32 child key derivation, allowing generation of child keys from the master key or any previously derived key. It supports both hardened and non-hardened derivation paths, with automatic path parsing and validation.
Derivation Types:
- Hardened derivation (index ≥ 2³¹, marked with '): Requires private key, provides security isolation between parent and child
- Non-hardened derivation (index < 2³¹): Can derive from public key only, enables watch-only wallets but allows parent key compromise from child + chain code
Path Format:
- Standard BIP32 notation: "m/44'/0'/0'/0/0"
- m = master key
- Numbers = derivation indices
- ' (apostrophe) = hardened derivation (adds 2³¹ to index)
- / = path separator
Common Derivation Paths:
- BIP44 Bitcoin: "m/44'/0'/0'/0/0" (account 0, receiving address 0)
- BIP44 Bitcoin Change: "m/44'/0'/0'/1/0" (account 0, change address 0)
- BIP44 Bitcoin Cash: "m/44'/145'/0'/0/0"
- BIP44 Bitcoin SV: "m/44'/236'/0'/0/0"
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
path |
string |
<optional> |
"m/0'" | BIP32 derivation path (e.g., "m/44'/0'/0'/0/0") |
keyType |
string |
<optional> |
'pri' | Key type to derive ('pri' for private, 'pub' for public) |
- Source:
Throws:
-
-
"Public Key can't derive from hardened path" if attempting hardened derivation from public key
- Type
- Error
-
-
-
If derivation path format is invalid
- Type
- Error
-
-
-
If derived key is invalid (extremely rare: ~1 in 2^127)
- Type
- Error
-
Returns:
Returns this wallet instance for method chaining
- Type
- Custodial_Wallet
Examples
// Standard BIP44 Bitcoin address derivation
const wallet = Custodial_Wallet.fromRandom('main')[1];
// Derive first receiving address
wallet.derive("m/44'/0'/0'/0/0", 'pri');
// Derive first change address
wallet.derive("m/44'/0'/0'/1/0", 'pri');
console.log('Derived keys:', wallet.child_keys.size); // 2
// Method chaining for multiple derivations
const wallet = Custodial_Wallet.fromRandom('main')[1];
wallet
.derive("m/44'/0'/0'/0/0", 'pri') // First receiving
.derive("m/44'/0'/0'/0/1", 'pri') // Second receiving
.derive("m/44'/0'/0'/1/0", 'pri'); // First change
// Access all derived addresses
for (const child of wallet.child_keys) {
console.log(`Address ${child.childIndex}:`, child.address);
}
// Public key derivation (non-hardened only)
const wallet = Custodial_Wallet.fromRandom('main')[1];
// This works - non-hardened derivation
wallet.derive("m/0/1/2", 'pub');
// This fails - hardened derivation from public key
try {
wallet.derive("m/0'/1", 'pub');
} catch (error) {
console.log(error.message); // "Public Key can't derive from hardened path"
}
// Multi-currency wallet derivation
const wallet = Custodial_Wallet.fromRandom('main')[1];
// Bitcoin addresses
wallet.derive("m/44'/0'/0'/0/0", 'pri'); // BTC receiving
// Bitcoin Cash addresses (different coin type)
wallet.derive("m/44'/145'/0'/0/0", 'pri'); // BCH receiving
// Bitcoin SV addresses
wallet.derive("m/44'/236'/0'/0/0", 'pri'); // BSV receiving
// Generate multiple addresses for a service
const wallet = Custodial_Wallet.fromRandom('main')[1];
// Generate 10 unique receiving addresses
for (let i = 0; i < 10; i++) {
wallet.derive(`m/44'/0'/0'/0/${i}`, 'pri');
}
// Each customer gets a unique address
const addresses = Array.from(wallet.child_keys).map(child => child.address);
console.log('Customer addresses:', addresses);
sign(messageopt) → {ECDSASignatureResult}
Signs a message using ECDSA with the wallet's private key and deterministic nonce generation.
This method creates a cryptographically secure digital signature using the wallet's private key. It implements deterministic nonce generation (RFC 6979) to prevent nonce reuse attacks and ensure signature security. The signature can be verified by anyone with the corresponding public key.
Signature Process:
- Convert message to UTF-8 bytes
- Decode WIF private key to raw bytes
- Generate deterministic nonce using RFC 6979
- Compute ECDSA signature (r, s) values
- Include recovery ID for public key recovery
- Return DER-encoded signature with recovery information
Security Features:
- RFC 6979 deterministic nonce generation prevents nonce reuse
- Uses secp256k1 elliptic curve (Bitcoin standard)
- Compatible with Bitcoin transaction signing
- Includes recovery ID for public key derivation
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
message |
string |
<optional> |
'' | Message to sign (will be converted to UTF-8 bytes) |
- Source:
Throws:
-
-
If private key is invalid or signing fails
- Type
- Error
-
-
-
If message cannot be converted to bytes
- Type
- Error
-
Returns:
Tuple containing signature bytes and recovery ID
- Type
- ECDSASignatureResult
Examples
// Basic message signing
const wallet = Custodial_Wallet.fromRandom('main')[1];
const message = "Hello Bitcoin!";
const [signature, recoveryId] = wallet.sign(message);
console.log('Signature length:', signature.length); // ~71-73 bytes (DER format)
console.log('Recovery ID:', recoveryId); // 0, 1, 2, or 3
// Sign and verify workflow
const wallet = Custodial_Wallet.fromRandom('main')[1];
const message = "Transfer $100 to Alice";
// Create signature
const [signature, _] = wallet.sign(message);
// Verify signature (should return true)
const isValid = wallet.verify(signature, message);
console.log('Signature valid:', isValid); // true
// Verify with wrong message (should return false)
const isInvalid = wallet.verify(signature, "Transfer $200 to Bob");
console.log('Wrong message valid:', isInvalid); // false
// Transaction authorization pattern
const wallet = Custodial_Wallet.fromRandom('main')[1];
function authorizeTransaction(txData) {
const txMessage = JSON.stringify({
to: txData.recipient,
amount: txData.amount,
timestamp: Date.now(),
nonce: Math.random()
});
const [signature, recoveryId] = wallet.sign(txMessage);
return {
transaction: txData,
signature: signature,
recovery: recoveryId,
signer: wallet.address
};
}
const authorization = authorizeTransaction({
recipient: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
amount: 0.001
});
// Batch signing for multiple messages
const wallet = Custodial_Wallet.fromRandom('main')[1];
const messages = ["msg1", "msg2", "msg3"];
const signatures = messages.map(msg => {
const [sig, recovery] = wallet.sign(msg);
return { message: msg, signature: sig, recovery };
});
console.log(`Signed ${signatures.length} messages`);
verify(sig, msg) → {boolean}
Verifies an ECDSA signature against a message using the wallet's public key.
This method performs cryptographic verification to ensure that a given signature was created by the holder of this wallet's private key. It uses standard ECDSA verification on the secp256k1 curve and can verify signatures created by this wallet or any other ECDSA-compatible implementation.
Verification Process:
- Convert message to UTF-8 bytes (same as signing)
- Parse signature into r and s components
- Compute verification values using public key
- Check that signature equation holds on elliptic curve
- Return boolean result of verification
Security Properties:
- Mathematically proves signature was created with corresponding private key
- Cannot be forged without knowledge of private key
- Deterministic result for same signature/message/public key combination
- Compatible with Bitcoin transaction verification
Parameters:
| Name | Type | Description |
|---|---|---|
sig |
Uint8Array | Buffer | DER-encoded signature bytes to verify |
msg |
string | Original message that was signed (must match exactly) |
- Source:
Throws:
-
-
If signature format is invalid or corrupted
- Type
- Error
-
-
-
If message cannot be converted to bytes
- Type
- Error
-
Returns:
True if signature is valid for this wallet's public key, false otherwise
- Type
- boolean
Examples
// Basic signature verification
const wallet = Custodial_Wallet.fromRandom('main')[1];
const message = "Hello Bitcoin!";
// Sign message
const [signature, _] = wallet.sign(message);
// Verify signature
const isValid = wallet.verify(signature, message);
console.log('Signature valid:', isValid); // true
// Verify with modified message
const isInvalid = wallet.verify(signature, "Hello Ethereum!");
console.log('Modified message valid:', isInvalid); // false
// Cross-wallet verification (different wallets)
const wallet1 = Custodial_Wallet.fromRandom('main')[1];
const wallet2 = Custodial_Wallet.fromRandom('main')[1];
const message = "Cross-wallet test";
// Wallet1 signs message
const [signature, _] = wallet1.sign(message);
// Wallet1 can verify its own signature
console.log('Self verification:', wallet1.verify(signature, message)); // true
// Wallet2 cannot verify wallet1's signature
console.log('Cross verification:', wallet2.verify(signature, message)); // false
// Transaction verification workflow
function verifyTransactionSignature(txData, signature, senderAddress) {
// Reconstruct the exact message that was signed
const txMessage = JSON.stringify(txData);
// Find wallet for sender address (in real app, lookup from database)
const senderWallet = findWalletByAddress(senderAddress);
// Verify signature matches sender's private key
return senderWallet.verify(signature, txMessage);
}
// Batch verification for audit trail
const wallet = Custodial_Wallet.fromRandom('main')[1];
const signedMessages = [
{ msg: "tx1", sig: wallet.sign("tx1")[0] },
{ msg: "tx2", sig: wallet.sign("tx2")[0] },
{ msg: "tx3", sig: wallet.sign("tx3")[0] }
];
// Verify all signatures are valid
const allValid = signedMessages.every(item =>
wallet.verify(item.sig, item.msg)
);
console.log('All signatures valid:', allValid); // true
(static) fromMnemonic(netopt, mnemonicopt, passphraseopt) → {Custodial_Wallet}
Creates a wallet from an existing BIP39 mnemonic phrase with optional passphrase.
This static factory method restores a wallet from a previously generated mnemonic phrase. It validates the mnemonic checksum, derives the cryptographic seed using PBKDF2, and reconstructs the exact same wallet that was originally created. This enables secure backup and recovery of Bitcoin wallets.
Validation Process:
- Parse mnemonic into individual words
- Validate words exist in BIP39 wordlist
- Verify built-in checksum for error detection
- Derive seed using PBKDF2-HMAC-SHA512 with salt
- Generate identical master keys as original wallet
Compatibility:
- Works with any BIP39-compliant mnemonic
- Compatible with hardware wallets (Ledger, Trezor)
- Interoperable with other Bitcoin wallet software
- Supports 12-word mnemonics (this implementation)
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
net |
string |
<optional> |
'main' | Network type ('main' for mainnet, 'test' for testnet) |
mnemonic |
string |
<optional> |
'' | 12-word BIP39 mnemonic phrase (space-separated) |
passphrase |
string |
<optional> |
'' | Optional passphrase used during generation |
- Source:
Throws:
-
-
"invalid checksum" if mnemonic checksum validation fails
- Type
- Error
-
-
-
If mnemonic format is invalid or contains unknown words
- Type
- Error
-
-
-
If network parameter is invalid
- Type
- Error
-
Returns:
Restored wallet instance with identical keys
- Type
- Custodial_Wallet
Examples
// Restore wallet from mnemonic
const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
const wallet = Custodial_Wallet.fromMnemonic('main', mnemonic);
console.log('Restored address:', wallet.address);
// Restore with passphrase (must match original)
const mnemonicWithPass = "legal winner thank year wave sausage worth useful legal winner thank yellow";
const passphrase = "my-secure-passphrase";
const wallet = Custodial_Wallet.fromMnemonic('main', mnemonicWithPass, passphrase);
// Handle restoration errors gracefully
try {
const invalidMnemonic = "invalid mnemonic phrase with wrong checksum";
const wallet = Custodial_Wallet.fromMnemonic('main', invalidMnemonic);
} catch (error) {
console.error('Failed to restore wallet:', error.message);
// Handle error: show user-friendly message, request valid mnemonic
}
// Cross-platform wallet restoration
// Mnemonic generated on mobile app, restored on desktop
const mobileMnemonic = getUserInput('Enter your 12-word backup phrase:');
const desktopWallet = Custodial_Wallet.fromMnemonic('main', mobileMnemonic);
// Wallet will have identical addresses and keys as mobile version
console.log('Synced address:', desktopWallet.address);
(static) fromRandom(netopt, passphraseopt) → {Array|string|Custodial_Wallet}
Generates a new random wallet with cryptographically secure mnemonic phrase.
This static factory method creates a fresh wallet using BIP39 mnemonic generation and BIP32 hierarchical deterministic key derivation. The generated mnemonic provides a human-readable backup that can restore the entire wallet and all derived keys.
Process:
- Generate 128 bits of cryptographically secure entropy
- Create 12-word BIP39 mnemonic with checksum validation
- Derive 512-bit seed using PBKDF2-HMAC-SHA512
- Generate BIP32 master keys from seed
- Create wallet instance with generated keys
Security:
- Uses cryptographically secure random number generation
- Mnemonic includes built-in checksum for error detection
- Optional passphrase provides additional security layer
- Generated keys follow industry standard specifications
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
net |
string |
<optional> |
'main' | Network type ('main' for mainnet, 'test' for testnet) |
passphrase |
string |
<optional> |
'' | Optional passphrase for additional security (BIP39) |
- Source:
Throws:
-
-
If mnemonic generation fails or checksum is invalid
- Type
- Error
-
-
-
If network parameter is invalid
- Type
- Error
-
Returns:
-
Tuple containing mnemonic phrase and wallet instance
- Type
- Array
-
returns.0 - Generated 12-word mnemonic phrase
- Type
- string
-
returns.1 - New wallet instance
- Type
- Custodial_Wallet
Examples
// Generate mainnet wallet
const [mnemonic, wallet] = Custodial_Wallet.fromRandom('main');
console.log('Mnemonic:', mnemonic);
// "abandon ability able about above absent absorb abstract absurd abuse access accident"
console.log('Address:', wallet.address);
// "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2"
// Generate testnet wallet with passphrase
const [mnemonic, testWallet] = Custodial_Wallet.fromRandom('test', 'my-secure-passphrase');
console.log('Testnet address:', testWallet.address);
// "mgRpP3zP1hmxyoeYJgfbcmN3c2Qsurw48D"
// Store mnemonic securely for backup
const [mnemonic, wallet] = Custodial_Wallet.fromRandom('main', 'company-passphrase');
// Store mnemonic in secure location (encrypted, offline, etc.)
secureStorage.store('wallet-mnemonic', mnemonic);
secureStorage.store('wallet-passphrase', 'company-passphrase');
// Wallet can be restored later using mnemonic + passphrase
const restoredWallet = Custodial_Wallet.fromMnemonic('main', mnemonic, 'company-passphrase');
(static) fromSeed(netopt, seedopt) → {Custodial_Wallet}
Creates a wallet from a hex-encoded cryptographic seed (typically from BIP39).
This static factory method creates a wallet directly from a seed value, bypassing mnemonic processing. The seed is used to generate BIP32 master keys through HMAC-SHA512 computation. This method is typically used internally by other factory methods or when working with pre-computed seeds.
Seed Requirements:
- Must be hex-encoded string
- Recommended length: 128-512 bits (32-128 hex characters)
- Should be generated with cryptographically secure randomness
- BIP39 seeds are 512 bits (128 hex characters)
Key Generation Process:
- Convert hex seed to binary format
- Compute HMAC-SHA512 with "Bitcoin seed" as key
- Split result into private key (256 bits) and chain code (256 bits)
- Generate corresponding public key using secp256k1
- Create extended keys with network-specific version bytes
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
net |
string |
<optional> |
'main' | Network type ('main' for mainnet, 'test' for testnet) |
seed |
string |
<optional> |
"000102030405060708090a0b0c0d0e0f" | Hex-encoded cryptographic seed |
- Source:
Throws:
-
-
If seed is not valid hexadecimal format
- Type
- Error
-
-
-
If derived private key is invalid (extremely rare)
- Type
- Error
-
-
-
If network parameter is invalid
- Type
- Error
-
Returns:
New wallet instance derived from seed
- Type
- Custodial_Wallet
Examples
// Create wallet from hex seed
const seed = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
const wallet = Custodial_Wallet.fromSeed('main', seed);
console.log('Seed-derived address:', wallet.address);
// Use BIP39-derived seed
const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
const bip39Seed = bip39.mnemonic2seed(mnemonic);
const wallet = Custodial_Wallet.fromSeed('main', bip39Seed);
// Custom seed for testing (deterministic addresses)
const testSeed = "deadbeefcafebabe".repeat(8); // 128 hex chars
const testWallet = Custodial_Wallet.fromSeed('test', testSeed);
console.log('Test address:', testWallet.address);