https://github.com/niteshbalusu11/bip-321
A BIP-321 parsing and encoding library
https://github.com/niteshbalusu11/bip-321
ark bip-21 bip-321 bitcoin bolt-12 lightning silent-payments
Last synced: 2 months ago
JSON representation
A BIP-321 parsing and encoding library
- Host: GitHub
- URL: https://github.com/niteshbalusu11/bip-321
- Owner: niteshbalusu11
- License: mit
- Created: 2025-11-07T00:30:41.000Z (7 months ago)
- Default Branch: master
- Last Pushed: 2026-02-05T00:49:58.000Z (4 months ago)
- Last Synced: 2026-02-05T13:04:33.286Z (4 months ago)
- Topics: ark, bip-21, bip-321, bitcoin, bolt-12, lightning, silent-payments
- Language: TypeScript
- Homepage:
- Size: 210 KB
- Stars: 1
- Watchers: 0
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# BIP-321 Parsing and Encoding
A TypeScript/JavaScript library for parsing and encoding BIP-321 Bitcoin URI scheme. This library validates and extracts payment information from Bitcoin URIs or lets you encode information, supporting multiple payment methods including on-chain addresses, Lightning invoices, BOLT12 offers, and silent payments.
## Features
- ✅ **Complete BIP-321 compliance** - Implements the full BIP-321 specification
- ✅ **Multiple payment methods** - Supports on-chain, Lightning (BOLT11), BOLT12 offers, silent payments, and Ark
- ✅ **Network detection** - Automatically detects mainnet, testnet, regtest, and signet networks
- ✅ **Address validation** - Validates Bitcoin addresses (P2PKH, P2SH, Segwit v0, Taproot)
- ✅ **Lightning invoice validation** - Validates BOLT11 Lightning invoices
```typescript
import { parseBIP321, type BIP321ParseResult, type PaymentMethod } from "bip-321";
const result: BIP321ParseResult = parseBIP321("bitcoin:...");
result.valid; // boolean
result.network; // "mainnet" | "testnet" | "regtest" | "signet" | undefined
result.paymentMethods; // PaymentMethod[]
result.errors; // string[]
result.paymentMethods.forEach((method: PaymentMethod) => {
method.type; // "onchain" | "lightning" | "offer" | "silent-payment" | "ark"
method.network; // "mainnet" | "testnet" | "regtest" | "signet" | undefined
method.valid; // boolean
});
```
```typescript
import { encodeBIP321 } from "bip-321";
try {
const { uri } = encodeBIP321({ address: "bitcoin_address", label: "Label", message: "Message", amount: 0.5 });
// uri = bitcoin:bitcoin_address?label=Label&message=Message&amount=0.5
} catch (error) {
console.error(error)
}
```
## Installation
```bash
bun add bip-321
```
Or with npm:
```bash
npm install bip-321
```
## Quick Start
```typescript
import { parseBIP321, encodeBIP321 } from "bip-321";
// Parse a simple Bitcoin address
const result = parseBIP321("bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa");
console.log(result.valid); // true
console.log(result.network); // "mainnet"
console.log(result.address); // "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
console.log(result.paymentMethods); // Array of payment methods
// Encode a simple Bitcoin address
try {
const { uri } = encodeBIP321({ address: "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" });
console.log(uri); // bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
} catch (error) {
console.error(error)
}
```
## Validation Functions
The library also exports standalone validation functions that can be used independently:
```typescript
import {
validateBitcoinAddress,
validateLightningInvoice,
validateBolt12Offer,
validateSilentPaymentAddress,
validateArkAddress,
validatePopUri,
} from "bip-321";
// Validate a Bitcoin address
const btcResult = validateBitcoinAddress("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa");
console.log(btcResult.valid); // true
console.log(btcResult.network); // "mainnet"
// Validate a Lightning invoice
const lnResult = validateLightningInvoice("lnbc15u1p3xnhl2pp5...");
console.log(lnResult.valid); // true
console.log(lnResult.network); // "mainnet"
// Validate a BOLT12 offer
const offerResult = validateBolt12Offer("lno1qqqq02k20d");
console.log(offerResult.valid); // true
// Validate a Silent Payment address
const spResult = validateSilentPaymentAddress("sp1qq...");
console.log(spResult.valid); // true
console.log(spResult.network); // "mainnet"
// Validate an Ark address
const arkResult = validateArkAddress("ark1p...");
console.log(arkResult.valid); // true
console.log(arkResult.network); // "mainnet"
// Validate a pop URI
const popResult = validatePopUri("myapp://callback");
console.log(popResult.valid); // true
```
## Usage Examples
### Basic On-Chain Payment
```typescript
import { parseBIP321 } from "bip-321";
const result = parseBIP321("bitcoin:bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq");
if (result.valid) {
console.log(`Network: ${result.network}`); // mainnet
console.log(`Address: ${result.address}`);
console.log(`Payment methods: ${result.paymentMethods.length}`);
}
```
### Payment with Amount and Label
```typescript
const result = parseBIP321(
"bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?amount=0.5&label=Donation&message=Thank%20you"
);
console.log(`Amount: ${result.amount} BTC`); // 0.5 BTC
console.log(`Label: ${result.label}`); // "Donation"
console.log(`Message: ${result.message}`); // "Thank you"
```
### Lightning Invoice with On-Chain Fallback
```typescript
const result = parseBIP321(
"bitcoin:bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq?lightning=lnbc15u1p3xnhl2pp5..."
);
// Returns 2 payment methods: onchain and lightning
result.paymentMethods.forEach((method) => {
console.log(`Type: ${method.type}, Network: ${method.network}, Valid: ${method.valid}`);
});
```
### Lightning-Only Payment
```typescript
// Encode lightning-only payment (no on-chain address)
const { uri } = encodeBIP321({ lightning: "lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3s..." });
// Parse lightning-only payment
const result = parseBIP321(uri);
console.log(result.paymentMethods[0].type); // "lightning"
console.log(result.paymentMethods[0].network); // "mainnet"
```
### Ark Payment
```typescript
// Encode Ark-only payment (no on-chain address)
const { uri } = encodeBIP321({ ark: "ark1pwh9vsmezqqpjy9akejayl2vvcse6he97rn40g84xrlvrlnhayuuyefrp9nse2yspqqjl5wpy" });
// Parse Ark payment
const result = parseBIP321(uri);
console.log(result.paymentMethods[0].type); // "ark"
console.log(result.paymentMethods[0].network); // "mainnet"
// Testnet Ark address
const { uri: testnetUri } = encodeBIP321({ ark: "tark1pm6sr0fpzqqpnzzwxf209kju4qavs4gtumxk30yv2u5ncrvtp72z34axcvrydtdqpqq5838km" });
const testnetResult = parseBIP321(testnetUri);
console.log(testnetResult.paymentMethods[0].network); // "testnet"
```
### Network Validation
```typescript
// Ensure all payment methods are mainnet
const result = parseBIP321(
"bitcoin:bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq?lightning=lnbc...",
"mainnet"
);
if (result.valid) {
// All payment methods are guaranteed to be mainnet
console.log("All payment methods are mainnet");
}
// Reject testnet addresses when expecting mainnet
const invalid = parseBIP321(
"bitcoin:tb1qghfhmd4zh7ncpmxl3qzhmq566jk8ckq4gafnmg",
"mainnet"
);
console.log(invalid.valid); // false
console.log(invalid.errors); // ["Payment method network mismatch..."]
```
### Multiple Payment Methods
```typescript
const result = parseBIP321(
"bitcoin:?lightning=lnbc...&lno=lno1bogusoffer&sp=sp1qsilentpayment"
);
// Returns 3 payment methods: lightning, offer (BOLT12), and silent-payment
console.log(`Total payment methods: ${result.paymentMethods.length}`);
```
### Network-Specific Parameters
```typescript
// Mainnet and testnet addresses in one URI
const result = parseBIP321(
"bitcoin:?bc=bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq&tb=tb1qghfhmd4zh7ncpmxl3qzhmq566jk8ckq4gafnmg"
);
const byNetwork = getPaymentMethodsByNetwork(result);
console.log(`Mainnet methods: ${byNetwork.mainnet.length}`);
console.log(`Testnet methods: ${byNetwork.testnet.length}`);
```
### Error Handling
```typescript
const result = parseBIP321("bitcoin:invalidaddress");
if (!result.valid) {
console.log("Errors:");
result.errors.forEach((error) => console.log(` - ${error}`));
}
```
## API Reference
### `parseBIP321(uri: string, expectedNetwork?: "mainnet" | "testnet" | "regtest" | "signet"): BIP321ParseResult`
Parses a BIP-321 URI and returns detailed information about the payment request.
**Parameters:**
- `uri` - The Bitcoin URI string to parse
- `expectedNetwork` (optional) - Expected network for all payment methods. If specified, all payment methods must match this network or the URI will be marked invalid.
**Returns:** `BIP321ParseResult` object
### `encodeBIP321(params: BIP321EncodeParams): BIP321EncodeResult`
Encodes payment parameters into a BIP-321 URI string. Validates the generated URI before returning.
**Parameters:**
- `params` - The `BIP321EncodeParams` object containing payment details
**Returns:** `BIP321EncodeResult` object
**Throws:** `Error` if the generated URI is invalid or contains no valid payment methods
### `BIP321EncodeParams` Interface
```typescript
interface BIP321EncodeParams {
address?: string; // Main Bitcoin address
amount?: number; // Amount in BTC
label?: string; // Label for the recipient
message?: string; // Message describing the transaction
lightning?: string | string[]; // BOLT11 Lightning invoice(s)
lno?: string | string[]; // BOLT12 offer(s)
sp?: string | string[]; // Silent Payment address(es)
ark?: string | string[]; // Ark address(es)
bc?: string | string[]; // Mainnet address(es)
tb?: string | string[]; // Testnet address(es)
bcrt?: string | string[]; // Regtest address(es)
tbs?: string | string[]; // Signet address(es)
pop?: string; // Proof of payment callback URI
reqPop?: string; // Required proof of payment callback URI
optionalParams?: Record; // Additional optional parameters
}
```
**Note:** `pop` and `reqPop` are mutually exclusive - only one can be provided.
### `BIP321EncodeResult` Interface
```typescript
interface BIP321EncodeResult extends BIP321ParseResult {
uri: string; // The encoded BIP-321 URI string
}
```
The result includes all fields from `BIP321ParseResult` plus the generated `uri` string.
### Validation Functions
The library exports individual validation functions for each payment method type:
#### `validateBitcoinAddress(address: string)`
Validates a Bitcoin address and returns network information.
**Returns:**
```typescript
{
valid: boolean;
network?: "mainnet" | "testnet" | "regtest" | "signet";
error?: string;
}
```
#### `validateLightningInvoice(invoice: string)`
Validates a BOLT11 Lightning invoice and detects the network.
**Returns:**
```typescript
{
valid: boolean;
network?: "mainnet" | "testnet" | "regtest" | "signet";
error?: string;
}
```
#### `validateBolt12Offer(offer: string)`
Validates a BOLT12 offer. Note: BOLT12 offers are network-agnostic.
**Returns:**
```typescript
{
valid: boolean;
error?: string;
}
```
#### `validateSilentPaymentAddress(address: string)`
Validates a BIP-352 Silent Payment address.
**Returns:**
```typescript
{
valid: boolean;
network?: "mainnet" | "testnet";
error?: string;
}
```
**Note:** For Silent Payments, `testnet` covers testnet, signet, and regtest.
#### `validateArkAddress(address: string)`
Validates an Ark address (BOAT-0001).
**Returns:**
```typescript
{
valid: boolean;
network?: "mainnet" | "testnet";
error?: string;
}
```
**Note:** For Ark, `testnet` covers testnet, signet, and regtest.
#### `validatePopUri(uri: string)`
Validates a proof-of-payment URI and checks for forbidden schemes.
**Returns:**
```typescript
{
valid: boolean;
error?: string;
}
```
### BIP321ParseResult
The `parseBIP321` function returns a `BIP321ParseResult` object containing:
```typescript
interface BIP321ParseResult {
// Basic information
address?: string; // Main Bitcoin address from URI path
network?: "mainnet" | "testnet" | "regtest" | "signet";
amount?: number; // Amount in BTC
label?: string; // Label for the recipient
message?: string; // Message describing the transaction
// Proof of payment
pop?: string; // Proof of payment callback URI
popRequired?: boolean; // Whether pop callback is required
// Payment methods
paymentMethods: PaymentMethod[]; // All available payment methods
// Parameters
requiredParams: string[]; // Unknown required parameters (req-*)
optionalParams: Record; // Unknown optional parameters
// Validation
valid: boolean; // Whether the URI is valid
errors: string[]; // Array of error messages
}
```
### `PaymentMethod` Interface
```typescript
interface PaymentMethod {
type: "onchain" | "lightning" | "offer" | "silent-payment" | "ark";
value: string; // The actual address/invoice value
network?: "mainnet" | "testnet" | "regtest" | "signet";
valid: boolean; // Whether this payment method is valid
error?: string; // Error message if invalid
}
```
### Helper Functions
#### `getPaymentMethodsByNetwork(result: BIP321ParseResult)`
Groups payment methods by network.
```typescript
const byNetwork = getPaymentMethodsByNetwork(result);
// Returns: { mainnet: [], testnet: [], regtest: [], signet: [], unknown: [] }
```
#### `getValidPaymentMethods(result: BIP321ParseResult)`
Returns only valid payment methods.
```typescript
const valid = getValidPaymentMethods(result);
// Returns: PaymentMethod[]
```
#### `formatPaymentMethodsSummary(result: BIP321ParseResult)`
Generates a human-readable summary of the parsed URI.
```typescript
const summary = formatPaymentMethodsSummary(result);
console.log(summary);
// Outputs:
// Valid: true
// Amount: 0.5 BTC
// Label: Donation
// Payment Methods: 2
// ✓ onchain (mainnet)
// ✓ lightning (mainnet)
```
## Supported Payment Methods
| Method | Parameter Key | Description |
|--------|--------------|-------------|
| On-chain | `address` or `bc`/`tb`/`bcrt`/`tbs` | Bitcoin addresses (P2PKH, P2SH, Segwit, Taproot) |
| Lightning | `lightning` | BOLT11 Lightning invoices |
| BOLT12 Offer | `lno` | Lightning BOLT12 offers |
| Silent Payments | `sp` | BIP352 Silent Payment addresses |
| Ark | `ark` | Ark addresses (mainnet: `ark1...`, testnet: `tark1...`) |
## Network Detection
The library automatically detects the network from:
### Bitcoin Addresses
- **Mainnet**: `1...`, `3...`, `bc1...`
- **Testnet**: `m...`, `n...`, `2...`, `tb1...`
- **Regtest**: `bcrt1...`
### Lightning Invoices
- **Mainnet**: `lnbc...`
- **Testnet**: `lntb...`
- **Regtest**: `lnbcrt...`
- **Signet**: `lntbs...`
### Silent Payment Addresses
- **Mainnet**: `sp1q...`
- **Testnet**: `tsp1q...` (covers testnet, signet, and regtest)
### Ark Addresses
- **Mainnet**: `ark1...`
- **Testnet**: `tark1...` (covers testnet, signet, and regtest)
### BOLT12 Offers
- **Network-agnostic**: `lno...` (no network-specific prefix)
## Validation Rules
The parser enforces BIP-321 validation rules:
1. ✅ URI must start with `bitcoin:` (case-insensitive)
2. ✅ Address in URI path must be valid or empty
3. ✅ `amount` must be decimal BTC (no commas)
4. ✅ `label`, `message`, and `amount` cannot appear multiple times
5. ✅ `pop` and `req-pop` cannot both be present
6. ✅ Required parameters (`req-*`) must be understood or URI is invalid
7. ✅ Network-specific parameters (`bc`, `tb`, etc.) must match address network
8. ✅ `pop` URI scheme must not be forbidden (http, https, file, javascript, mailto)
9. ✅ If `expectedNetwork` is specified, all payment methods must match that network
## Browser Usage
```html
import { parseBIP321 } from './index.js';
const uri = prompt("Enter Bitcoin URI:");
const result = parseBIP321(uri);
if (result.valid) {
alert(`Valid payment request!\nNetwork: ${result.network}\nMethods: ${result.paymentMethods.length}`);
} else {
alert(`Invalid URI:\n${result.errors.join('\n')}`);
}
BIP-321 Parser Demo
```
## React Native Usage
```typescript
import { parseBIP321 } from "bip-321";
import { Alert } from "react-native";
function parseQRCode(data: string) {
const result = parseBIP321(data);
if (result.valid) {
Alert.alert(
"Payment Request",
`Network: ${result.network}\nAmount: ${result.amount || 'Not specified'} BTC`
);
} else {
Alert.alert("Invalid QR Code", result.errors.join("\n"));
}
}
```
## License
MIT
## Related
- [BIP-321 Specification](https://github.com/bitcoin/bips/blob/master/bip-0321.mediawiki)
- [BIP-21 (Original)](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki)
- [BIP-352 Silent Payments](https://github.com/bitcoin/bips/blob/master/bip-0352.mediawiki)
- [BOAT-0001 Ark Address Format](https://github.com/ark-protocol/boats/blob/master/boat-0001.md)
- [BOLT11 Lightning Invoices](https://github.com/lightning/bolts/blob/master/11-payment-encoding.md)
- [BOLT12 Lightning Offers](https://github.com/lightning/bolts/blob/master/12-offer-encoding.md)