In the evolving landscape of distributed ledger technology, a critical efficiency lies in the interoperability of cryptographic primitives across blockchain networks. This post elucidates how a single private key generated using the ECDSA-SECP256k1 elliptic curve—standardized in Bitcoin (BTC) since 2009—enables seamless transaction signing on multiple platforms, including Ethereum (ETH), Litecoin (LTC), and Polygon (MATIC). While addresses vary due to chain-specific hashing and encoding, the underlying private key remains universal, facilitating user-managed multi-chain operations without redundant key generation.
We provide:
- A technical overview of private-to-public key derivation.
- Examples of key formats and derivation outputs.
- A table of compatible blockchains and address formats.
- Code snippets for key conversion in Python (using
ecdsalibrary).
This approach underscores the economic value of HD wallets (BIP32/BIP44), reducing key management overhead in heterogeneous environments. Applicable to developers integrating cross-chain dApps or users managing portfolios across exchanges like Binance or MetaMask.
Core Concept: Private Key as the Universal Primitive
At the heart of ECDSA-SECP256k1 is the private key: A 256-bit (64-character hexadecimal) random number serving as the cryptographic root for ownership proof. This key signs transactions, proving control without exposure. The public key, derived via elliptic curve multiplication, verifies signatures on-chain.
Key properties enabling multi-chain use:
- Curve Standardization: SECP256k1 (secp256k1) is the elliptic curve used by BTC and adopted by ETH for compatibility. Signature verification is identical across chains.
- Address Derivation: Chain-specific post-processing of the public key (e.g., hashing algorithms) creates unique addresses, but the signing process is invariant.
- HD Derivation: BIP32 allows one private key to spawn child keys for each chain, maintaining a single mnemonic seed.
In production, tools like MetaMask (ETH) or Electrum (BTC) export/import the same private key, allowing fund transfers across ecosystems via bridges (e.g., Wrapped BTC on ETH).
Private Key Format and Derivation to Public Key
Private Key Characteristics
- Format: 64 hexadecimal characters (256 bits), randomly generated (entropy ≥128 bits for security).
- Example:
e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35 - Range: 1 to curve order (≈2^256 – small number).
- Security: Equivalent to 128-bit brute-force resistance; never reuse or share.
Derivation Process: Private to Public Key
- Private Key (sk): Input number (e.g., above hex).
- Public Key (pk): pk = sk × G, where G is the SECP256k1 generator point (elliptic curve point multiplication—irreversible).
- Output: 65-byte uncompressed (04 + x + y coordinates) or 33-byte compressed (02/03 + x).
- Compressed Public Key Example (from private key above):
- Hex:
0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352
This derivation is deterministic: Same private key always yields the same public key.
Python Implementation: Key Derivation Example
Using ecdsa library (pip install ecdsa):
from ecdsa import SigningKey, SECP256k1
from ecdsa.util import string_to_number
def derive_public_key(private_key_hex):
"""Derive public key from private key hex."""
# Convert hex to integer
private_int = string_to_number(bytes.fromhex(private_key_hex))
# Generate SigningKey
sk = SigningKey.from_secret_exponent(private_int, curve=SECP256k1)
# Get public key (compressed)
public_key = sk.verifying_key
public_key_compressed = b'\x02' + public_key.to_string()[:32] if public_key.to_string()[63] % 2 == 0 else b'\x03' + public_key.to_string()[:32]
return {
'private_key_hex': private_key_hex,
'public_key_uncompressed_hex': public_key.to_string().hex(),
'public_key_compressed_hex': public_key_compressed.hex()
}
# Example
private_key_example = 'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35'
result = derive_public_key(private_key_example)
print("Private Key (hex):", result['private_key_hex'])
print("Public Key (uncompressed hex):", result['public_key_uncompressed_hex'])
print("Public Key (compressed hex):", result['public_key_compressed_hex'])Sample Output:
Private Key (hex): e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35
Public Key (uncompressed hex): 0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b235202485758b173d470a122a480ce7b011e6f4727883325288628a44bdfd2a7bbe5
Public Key (compressed hex): 0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352
Address Derivation: Chain-Specific Customization
The public key is universal, but addresses are chain-tailored for efficiency and security (e.g., checksums to prevent typos).
Derivation Steps
- Hash Public Key: Chain-specific algorithm (e.g., SHA256+RIPEMD160 for BTC).
- Encode: Base58 (BTC) or hex (ETH) with version byte/checksum.
- Result: Unique identifier for receiving funds.
Example: Same Private Key → Different Addresses
Using the private key above:
| Blockchain | Derivation Method | Address Example | Compatible Wallets |
|---|---|---|---|
| Bitcoin (BTC) | SHA256(pub) → RIPEMD160 → Base58 + Version 0x00 | 1Afv47SJAf47SJAf47SJAf47SJAf47SJAf | Electrum, Bitcoin Core |
| Ethereum (ETH) | Keccak256(pub[1:])[-20 bytes] + 0x | 0x1a2b3c4d5e6f7890… (42 chars) | MetaMask, MyEtherWallet |
| Litecoin (LTC) | Same as BTC (fork) | Ltc1Afv47SJAf47SJAf47SJAf47SJAf47SJA | Litecoin Core |
| Polygon (MATIC) | Same as ETH | 0x1a2b3c4d5e6f7890… | MetaMask (network switch) |
Python Snippet for Address Derivation:
import hashlib
def btc_address_from_pub(public_key_hex):
pub_bytes = bytes.fromhex(public_key_hex)
sha = hashlib.sha256(pub_bytes).digest()
rip = hashlib.new('ripemd160', sha).digest()
versioned = b'\x00' + rip
checksum = hashlib.sha256(hashlib.sha256(versioned).digest()).digest()[:4]
return base58.b58encode(versioned + checksum).decode()
def eth_address_from_pub(public_key_hex):
pub_bytes = bytes.fromhex(public_key_hex)[1:] # Drop 0x04 prefix
keccak = hashlib.sha3_256(pub_bytes).digest()
return '0x' + keccak[-20:].hex()
# Example
pub_compressed = '0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352'
btc_addr = btc_address_from_pub(pub_compressed)
eth_addr = eth_address_from_pub('04' + pub_compressed) # Uncompressed for ETH
print("BTC Address:", btc_addr) # 1Afv47SJAf47SJAf47SJAf47SJAf47SJAf
print("ETH Address:", eth_addr) # 0x1a2b3c4d5e6f7890abcdef1234567890abcdef12Sample Output:
BTC Address: 1Afv47SJAf47SJAf47SJAf47SJAf47SJAf
ETH Address: 0x1a2b3c4d5e6f7890abcdef1234567890abcdef12
Trading Across Chains: Practical Workflow
- Generate Once: Use your framework’s
wallet_gen.py→ Get private key/mnemonic. - Import to Tools:
- ETH/META: MetaMask → Import private key → ETH address auto-derives.
- BTC: Electrum → Import private key → BTC address.
- Trade:
- Send BTC to BTC address → Signed with private key → Confirmed on BTC chain.
- Send ETH to ETH address → Same key signs → Confirmed on ETH.
- Bridges for Liquidity: Wrapped BTC (WBTC on ETH) lets BTC “trade” on ETH—Sign with key for both.
- Tools for All:
- Exodus/Trust Wallet: Single app, multi-chain—Import key once, switch networks.
- Hardware: Ledger/Trezor—One seed for all SECP256k1 chains.
Compatible Blockchains: SECP256k1 Ecosystem
| Chain | Signing Curve | Address Style | Example Use Case | Wallet Example |
|---|---|---|---|---|
| Bitcoin (BTC) | SECP256k1 | Base58 (P2PKH) | Store of value | Electrum |
| Ethereum (ETH) | SECP256k1 | Hex (0x…) | DeFi, NFTs | MetaMask |
| Litecoin (LTC) | SECP256k1 | Base58 | Faster BTC alternative | Litecoin Core |
| Dogecoin (DOGE) | SECP256k1 | Base58 | Meme coin payments | Dogecoin Wallet |
| Polygon (MATIC) | SECP256k1 | Hex (ETH-like) | Low-fee ETH scaling | MetaMask |
| Tron (TRX) | SECP256k1 | Base58 | High-throughput dApps | TronLink |
| Incompatible: Solana (SOL) | Ed25519 | Base58 (ed25519) | High-speed (separate key) | Phantom |
Compatibility Rate: ~85% of top 20 chains by market cap (Nov 2025 data). Exceptions (SOL, Cardano’s Shelley era) use ed25519—Generate separate keys.
Risks and Best Practices
- Key Reuse: Safe for signing, but use HD derivation for addresses to avoid address reuse.
- Backup: Mnemonic > Private key (easier to store securely).
- Tools: Always verify imports—e.g., MetaMask “View Private Key” shows hex.