npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@shakesco/silent

v1.1.4

Published

Bitcoin Silent Payments

Readme

@shakesco/silent

JavaScript SDK for receiving Bitcoin privately with silent payments (BIP-352).

Special thanks to Ruben Somsen and Josie Bake for their groundbreaking work on BIP-352.

What It Does

The @shakesco/silent SDK lets you implement Bitcoin silent payments, allowing users to:

  • ✅ Share a single address for all payments
  • ✅ Receive Bitcoin privately
  • ✅ Maintain transaction unlinkability
  • ✅ Avoid notification transaction fees

Learn more:

Installation

npm i @shakesco/silent

Quick Start

const shakesco = require("@shakesco/silent");
const {
  KeyGeneration,
  SilentPaymentDestination,
  SilentPaymentBuilder,
  ECPrivateInfo,
  Network,
  BitcoinScriptOutput,
  bip32,
  bip39,
} = shakesco;

Integration Workflow

  1. Generate silent payment address
  2. Create destination address for each payment
  3. Scan for incoming funds
  4. Spend received funds

1. Generate Silent Payment Address

From Private Keys (Recommended for Apps)

Best for: Non-wallet applications where users control their keys

const b_scan = ""; // Scan private key
const b_spend = ""; // Spend private key

const keys = KeyGeneration.fromPrivateKeys({
  b_scan: b_scan,
  b_spend: b_spend,
  network: "testnet",
});

const silentPaymentAddress = keys.toAddress();
console.log(silentPaymentAddress);

Pro tip: Make users sign a message, then derive b_scan and b_spend from the ECDSA signature:

  • Use r as b_scan
  • Use s as b_spend (or vice versa)

This ensures cryptographically secure randomness without storing additional keys.

From Mnemonic (For Wallets)

Best for: Wallet providers managing user funds

const mnemonic = ""; // 12, 15, or 24 word phrase
const keys = KeyGeneration.fromMnemonic(mnemonic);
const silentPaymentAddress = keys.toAddress();
console.log(silentPaymentAddress);

Alternative using HD key:

const seed = bip39.mnemonicToSeedSync(mnemonic);
const node = bip32.fromSeed(seed);
const keys = KeyGeneration.fromHd(node);
const silentPaymentAddress = keys.toAddress();

Security Note: If not using the signature-derived method, ensure you're using a cryptographically secure random number generator.

Create a Change Address

Critical for privacy: Never send change to a public address after making silent payments.

const keys = KeyGeneration.fromPrivateKeys({
  b_scan: b_scan,
  b_spend: b_spend,
  network: "testnet",
});

// Always use label 0 for change (per BIP-352 spec)
const changeSilentPaymentAddress = keys.toLabeledSilentPaymentAddress(0);
console.log(changeSilentPaymentAddress.toAddress());

Why this matters: If you send 10 silent payments to friends, then send change to your public address, you've exposed:

  • ❌ Your own private transaction history
  • ❌ Your friends' payment patterns
  • ❌ Links between all 10 transactions

Solution: Always use a labeled silent payment address for change.

Reference: BIP-352 Labels for Change


2. Create Destination Address

Generate a unique taproot address for the payment:

// Parse recipient's silent payment address
const addressPubKeys = KeyGeneration.fromAddress(silentPaymentAddress);

// Your UTXO details
const vinOutpoints = [
  {
    txid: "367e24cac43a7d77621ceb1cbc1cf4a7719fc81b05b07b38f99b043f4e8b95dc",
    index: 1,
  },
];

const pubkeys = [
  "025c471f0e7d30d6f9095058bbaedaf13e1de67dbfcbe8328e6378d2a3bfb5cfd0",
];

const UTXOPrivatekey = ""; // Your UTXO private key

// Build the destination
const builder = new SilentPaymentBuilder({
  vinOutpoints: vinOutpoints,
  pubkeys: pubkeys,
}).createOutputs(
  [
    new ECPrivateInfo(
      UTXOPrivatekey,
      false // Set true if output is from taproot
    ),
  ],
  [
    new SilentPaymentDestination({
      amount: 1000, // Satoshis (1 BTC = 100,000,000 sats)
      network: Network.Testnet,
      version: 0,
      scanPubkey: addressPubKeys.B_scan,
      spendPubkey: addressPubKeys.B_spend,
    }),
  ]
);

// Get the destination taproot address
const destinationAddress = builder[silentPaymentAddress][0];
console.log("Send 1000 sats to:", destinationAddress);

What you need:

  • UTXO transaction ID and output index
  • UTXO private key
  • Amount in satoshis
  • Recipient's scan and spend public keys (B_scan, B_spend)

3. Scan for Incoming Funds

Trade-off: This is the main drawback of silent payments - you must scan the blockchain to detect incoming transactions.

const vinOutpoints = [
  {
    txid: "367e24cac43a7d77621ceb1cbc1cf4a7719fc81b05b07b38f99b043f4e8b95dc",
    index: 1,
  },
];

const pubkeys = [
  "025c471f0e7d30d6f9095058bbaedaf13e1de67dbfcbe8328e6378d2a3bfb5cfd0",
];

const search = new SilentPaymentBuilder({
  vinOutpoints: vinOutpoints,
  pubkeys: pubkeys,
  network: Network.Testnet,
}).scanOutputs(
  keys.b_scan, // Your scan private key
  keys.B_spend, // Your spend public key
  [
    new BitcoinScriptOutput(
      "5120fdcb28bcea339a5d36d0c00a3e110b837bf1151be9e7ac9a8544e18b2f63307d",
      BigInt(1000)
    ),
  ]
);

const foundOutput =
  search[builder[keys.toAddress()][0].address.pubkey.toString("hex")].output;
console.log(foundOutput);

If the output matches the taproot address → it's yours! 🎉

What you need for scanning:

  • Transaction input's txid and output_index
  • Public key from the output
  • Script and amount from the taproot address

Learn more: BIP-352 Scanning


4. Spend the Funds

Once you've confirmed funds belong to you, derive the private key:

const vinOutpoints = [
  {
    txid: "367e24cac43a7d77621ceb1cbc1cf4a7719fc81b05b07b38f99b043f4e8b95dc",
    index: 1,
  },
];

const pubkeys = [
  "025c471f0e7d30d6f9095058bbaedaf13e1de67dbfcbe8328e6378d2a3bfb5cfd0",
];

const private_key = new SilentPaymentBuilder({
  vinOutpoints: vinOutpoints,
  pubkeys: pubkeys,
}).spendOutputs(keys.b_scan, keys.b_spend);

console.log("Private key:", private_key);

Tip: Use this private key with bitcoinjs-lib to build and sign your taproot transaction.


That's It!

You've successfully implemented Bitcoin silent payments. Your users can now receive Bitcoin privately without address reuse.

Documentation

For complete integration guides and examples, visit: docs.shakesco.com/silent-payments

Resources