@btc-vision/ecpair - v4.0.2
    Preparing search index...

    @btc-vision/ecpair - v4.0.2

    @btc-vision/ecpair

    Bitcoin TypeScript NodeJS NPM

    code style: prettier

    Universal Bitcoin signer library with branded types and a pluggable CryptoBackend. Written in TypeScript with zero CommonJS. Ships with a pure-JS backend (@noble/curves) and a legacy adapter for tiny-secp256k1.

    What is ecpair?

    @btc-vision/ecpair provides secp256k1 key management, ECDSA signing, BIP-340 Schnorr signing, Taproot-style key tweaking, and WIF import/export. It is designed for use with @btc-vision/bitcoin and the broader OPNet ecosystem, but works with any Bitcoin library that consumes standard key types.

    Why branded types?

    PrivateKey, PublicKey, XOnlyPublicKey, Signature, and other key types are nominal (branded) Uint8Array subtypes. This prevents accidentally passing a raw hash where a private key is expected, or mixing up compressed and x-only public keys. Mistakes are caught at compile time, not at runtime in production.

    Why pluggable backends?

    CryptoBackend is an interface. The library ships two implementations:

    • NobleBackend: pure JavaScript via @noble/curves/secp256k1, zero native dependencies
    • LegacyBackend: adapter for existing tiny-secp256k1 installations (WASM or ASM.js)

    Swap backends without changing application code.

    No hardcoded networks

    The library does not ship Bitcoin, testnet, or regtest constants. Consumers must provide a Network object to every factory method. This keeps the library network-agnostic and avoids accidental mainnet usage in test environments.

    npm install @btc-vision/ecpair
    

    Requires Node.js >= 24.0.0. The package is ESM-only ("type": "module").

    import {
    ECPairSigner,
    createNobleBackend,
    createPrivateKey,
    createMessageHash,
    verifyCryptoBackend,
    } from '@btc-vision/ecpair';
    import type { Network } from '@btc-vision/ecpair';

    // Define your network
    const bitcoin: Network = {
    messagePrefix: '\x18Bitcoin Signed Message:\n',
    bech32: 'bc',
    bech32Opnet: 'op',
    bip32: { public: 0x0488b21e, private: 0x0488ade4 },
    pubKeyHash: 0x00,
    scriptHash: 0x05,
    wif: 0x80,
    };

    // Create backend and verify integrity
    const backend = createNobleBackend();
    verifyCryptoBackend(backend);

    // Generate a random signer (FIPS 186-5 B.4.2 key generation)
    const signer = ECPairSigner.makeRandom(backend, bitcoin);
    console.log(signer.toWIF());

    // Sign and verify
    const hash = createMessageHash(new Uint8Array(32));
    const sig = signer.sign(hash);
    console.log(signer.verify(hash, sig)); // true

    // Schnorr (BIP-340)
    const schnorrSig = signer.signSchnorr(hash);
    console.log(signer.verifySchnorr(hash, schnorrSig)); // true
    Old API (v3) New API (v4)
    ECPairFactory(tinysecp) createNobleBackend() or createLegacyBackend(tinysecp)
    ECPair.makeRandom() ECPairSigner.makeRandom(backend, network)
    ECPair.fromPrivateKey(buf, opts) ECPairSigner.fromPrivateKey(backend, privateKey, network)
    ECPair.fromPublicKey(buf, opts) ECPairSigner.fromPublicKey(backend, publicKey, network)
    ECPair.fromWIF(str, network) ECPairSigner.fromWIF(backend, str, network)
    keyPair.network (optional) signer.network (always set, required parameter)
    { network } in options Separate network parameter on every factory method
    Set<SignerCapability> number bitmask of SignerCapability flags
    const signer = ECPairSigner.fromWIF(backend, 'KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn', bitcoin);
    console.log(signer.compressed); // true
    console.log(signer.toWIF());
    const signer = ECPairSigner.fromPrivateKey(
    backend,
    createPrivateKey(new Uint8Array(32).fill(1)),
    bitcoin,
    );
    import { createPublicKey } from '@btc-vision/ecpair';

    const pubOnly = ECPairSigner.fromPublicKey(backend, createPublicKey(pubKeyBytes), bitcoin);
    console.log(pubOnly.privateKey); // undefined
    console.log(pubOnly.verify(hash, sig)); // true
    import type { Bytes32 } from '@btc-vision/ecpair';

    const tweakScalar = new Uint8Array(32).fill(2) as Bytes32;
    const tweaked = signer.tweak(tweakScalar);
    console.log(tweaked.toWIF());
    import { createLegacyBackend } from '@btc-vision/ecpair';
    import type { TinySecp256k1Interface } from '@btc-vision/ecpair';
    import * as tinysecp from 'tiny-secp256k1';

    const legacy = createLegacyBackend(tinysecp as unknown as TinySecp256k1Interface);
    const kp = ECPairSigner.makeRandom(legacy, bitcoin);
    import { randomBytes } from 'node:crypto';

    const kp = ECPairSigner.makeRandom(backend, bitcoin, {
    rng: (size: number) => new Uint8Array(randomBytes(size).buffer),
    });

    The rng function receives 48 bytes (FIPS 186-5 seed length) and must return exactly size bytes.

    const testnet: Network = {
    messagePrefix: '\x18Bitcoin Signed Message:\n',
    bech32: 'tb',
    bech32Opnet: 'opt',
    bip32: { public: 0x043587cf, private: 0x04358394 },
    pubKeyHash: 0x6f,
    scriptHash: 0xc4,
    wif: 0xef,
    };

    // fromWIF accepts an array of candidate networks
    const kp = ECPairSigner.fromWIF(backend, wifString, [bitcoin, testnet]);
    console.log(kp.network === bitcoin); // true if mainnet WIF
    import { SignerCapability } from '@btc-vision/ecpair';

    const kp = ECPairSigner.makeRandom(backend, bitcoin);

    if (kp.capabilities & SignerCapability.SchnorrSign) {
    console.log('Schnorr signing available');
    }

    // Or use the convenience method
    kp.hasCapability(SignerCapability.EcdsaSign); // true
    kp.hasCapability(SignerCapability.PrivateKeyExport); // true
    import { encodeWIF, decodeWIF, createPrivateKey } from '@btc-vision/ecpair';

    const wif = encodeWIF(createPrivateKey(keyBytes), true, bitcoin);
    const decoded = decodeWIF(wif, bitcoin);
    // decoded.privateKey, decoded.compressed, decoded.network

    Visit our API documentation generated by TypeDoc.

    npm test
    npm run lint
    npm run lint:tests
    npm run format:ci
    1. Fork the repository
    2. Create a feature branch
    3. Make your changes
    4. Run tests: npm test
    5. Submit a pull request

    See CONTRIBUTING.md for details.

    • Bugs: Open an issue
    • Security: See SECURITY.md - do not open public issues for vulnerabilities

    MIT