ECPair: SECP256k1 Keypair Management for Bitcoin
ecpair is a JavaScript/TypeScript library designed for managing SECP256k1 keypairs, primarily used within the BitcoinJS ecosystem for client-side applications. Currently at stable version 3.0.1, it provides core functionalities for generating new keypairs, importing them from various formats like WIF (Wallet Import Format) or raw private/public keys, and deriving public keys. Releases are generally stable, driven by updates within the broader BitcoinJS ecosystem or critical bug fixes. A key differentiator is its modular architecture, which externalizes all elliptic curve cryptography operations to a separate ECC library (e.g., `tiny-secp256k1`), enhancing flexibility and allowing for specific backend optimizations or compliance. The library is written in TypeScript and ships with comprehensive type definitions, promoting robust development practices.
Common errors
-
TypeError: ECPair.makeRandom is not a function
cause The `ECPair` object itself is not directly imported but is the result of calling `ECPairFactory` with an ECC library.fixEnsure you initialize `ECPair` by calling `const ECPair = ECPairFactory(tinysecp);` after importing `ECPairFactory` and `tiny-secp256k1`. -
Error: Missing TinySecp256k1Interface implementation. Please provide an ECC library (e.g., tiny-secp256k1).
cause `ECPairFactory` was called without a valid implementation of `TinySecp256k1Interface` (most commonly `tiny-secp256k1`).fixInstall `tiny-secp256k1` (`npm install tiny-secp256k1`) and pass it to the factory: `const tinysecp = require('tiny-secp256k1'); const ECPair = ECPairFactory(tinysecp);` -
ReferenceError: require is not defined in ES module scope
cause Attempting to use `require()` to import `ecpair` in an ES Module context.fixUse ES Module `import` syntax: `import { ECPairFactory } from 'ecpair';`.
Warnings
- gotcha The `ECPair.makeRandom()` method, when no custom `rng` function is provided, internally uses `crypto.getRandomValues`. This API was experimental in Node.js 18.19.0 and earlier. Running on older Node versions without the `--experimental-global-webcrypto` flag or a polyfill can lead to issues.
- breaking Version 3.x of `ecpair` has moved to an ESM-first (ECMAScript Module) architecture and mandates Node.js >= 20.0.0. Direct `require()` statements for `ecpair` will generally not work in new projects or if your environment is configured for ESM. Similarly, the internal usage of `crypto.getRandomValues` instead of `randombytes` is a significant change.
- gotcha Passing a custom Random Number Generator (RNG) function to `ECPair.makeRandom({ rng: customRng })` requires extreme caution. A poorly implemented or compromised RNG can lead to predictable private keys, resulting in significant security vulnerabilities and loss of funds.
Install
-
npm install ecpair -
yarn add ecpair -
pnpm add ecpair
Imports
- ECPairFactory
const { ECPairFactory } = require('ecpair');import { ECPairFactory } from 'ecpair'; - ECPairAPI
import ECPair from 'ecpair'; // ECPairAPI is a type, not a runtime value
import { ECPairAPI } from 'ecpair'; - TinySecp256k1Interface
import { TinySecp256k1Interface } from 'ecpair';
Quickstart
import { ECPairFactory, TinySecp256k1Interface, ECPairInterface } from 'ecpair';
import * as crypto from 'crypto';
// You need to provide an ECC library that implements TinySecp256k1Interface
// This is typically 'tiny-secp256k1'
const tinysecp: TinySecp256k1Interface = require('tiny-secp256k1');
const ECPair = ECPairFactory(tinysecp);
// Generate a random key pair
const keyPair1: ECPairInterface = ECPair.makeRandom();
console.log('Random Private Key (HEX):', keyPair1.privateKey?.toString('hex'));
console.log('Random Public Key (HEX):', keyPair1.publicKey.toString('hex'));
// Import from WIF
const wifKey = 'L45P2H58kPq611cRjP5H3rXfRjP5H3rXfRjP5H3rXfRjP5H3rXfRjP5H3rXfRjP5H3rXfRjP5H3rXf'; // Example WIF (DO NOT USE FOR REAL ASSETS)
const keyPair2: ECPairInterface = ECPair.fromWIF(wifKey);
console.log('WIF Public Key (HEX):', keyPair2.publicKey.toString('hex'));
// Import from a private key buffer
const privateKeyBuffer = crypto.randomBytes(32);
const keyPair3: ECPairInterface = ECPair.fromPrivateKey(privateKeyBuffer);
console.log('Imported Private Key (HEX):', keyPair3.privateKey?.toString('hex'));
// Import from a public key buffer
const keyPair4: ECPairInterface = ECPair.fromPublicKey(keyPair1.publicKey);
console.log('Imported Public Key (HEX):', keyPair4.publicKey.toString('hex'));
// Demonstrate custom network and RNG
const customNetwork = { messagePrefix: '\x18Bitcoin Signed Message:\n', bip32: { public: 0x0488b21e, private: 0x0488ade4 }, pubKeyHash: 0x00, scriptHash: 0x05, wif: 0x80 };
const customRng = (size: number): Buffer => crypto.randomBytes(size);
const keyPair5 = ECPair.makeRandom({ network: customNetwork, rng: customRng });
console.log('Custom Network Public Key (HEX):', keyPair5.publicKey.toString('hex'));