https://github.com/shakesco/shakesco-silent
Bitcoin Silent Payments
https://github.com/shakesco/shakesco-silent
bitcoin payments privacy silent-payments stealth-payments
Last synced: 4 months ago
JSON representation
Bitcoin Silent Payments
- Host: GitHub
- URL: https://github.com/shakesco/shakesco-silent
- Owner: shakesco
- Created: 2024-09-17T20:20:24.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2026-02-05T11:45:16.000Z (5 months ago)
- Last Synced: 2026-02-05T23:10:48.415Z (5 months ago)
- Topics: bitcoin, payments, privacy, silent-payments, stealth-payments
- Language: JavaScript
- Homepage: https://docs.shakesco.com/docs/silent/
- Size: 128 KB
- Stars: 4
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# @shakesco/silent
JavaScript SDK for receiving Bitcoin privately with silent payments (BIP-352).
> Special thanks to [Ruben Somsen](https://x.com/SomsenRuben) and [Josie Bake](https://x.com/josibake) 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:**
- [BIP-352 Specification](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki)
- [Silent Payments Explained](https://silentpayments.xyz/docs/explained/)
- [Full documentation](https://docs.shakesco.com/silent-payments/)
## Installation
```bash
npm i @shakesco/silent
```
## Quick Start
```javascript
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
```javascript
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](https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages#ecdsa-sign):
- 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
```javascript
const mnemonic = ""; // 12, 15, or 24 word phrase
const keys = KeyGeneration.fromMnemonic(mnemonic);
const silentPaymentAddress = keys.toAddress();
console.log(silentPaymentAddress);
```
**Alternative using HD key:**
```javascript
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.
```javascript
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](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki#labels_for_change)
---
## 2. Create Destination Address
Generate a unique taproot address for the payment:
```javascript
// 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.
```javascript
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](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki#scanning-silent-payment-eligible-transactions)
---
## 4. Spend the Funds
Once you've confirmed funds belong to you, derive the private key:
```javascript
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](https://github.com/bitcoinjs/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](https://docs.shakesco.com/silent-payments/)
## Resources
- [BIP-352 Specification](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki) - Complete technical specification
- [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib) - Build Bitcoin transactions in JavaScript
- [Silent Payments Explained](https://silentpayments.xyz/docs/explained/) - Protocol deep dive
- [ECDSA Signatures](https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages) - Learn about signature-based key derivation