Lightning JavaScript
Executable
Jan 31, 2026
Parsing BOLT11 Lightning Invoices
Decode and validate BOLT11 invoice strings in JavaScript
#bolt11
#invoice
#parsing
#bech32
Overview
BOLT11 is the standard invoice format for Lightning Network payments. This code demonstrates how to parse and validate BOLT11 invoices for use in agent payment flows.
The Code
/**
* BOLT11 Invoice Parser
* Decodes Lightning invoices for agents
*
* Note: This is a simplified implementation.
* For production use, use a library like 'bolt11' or 'light-bolt11-decoder'
*/
const ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';
/**
* Bech32 decode (simplified)
*/
function bech32Decode(str) {
const lower = str.toLowerCase();
const sepIndex = lower.lastIndexOf('1');
if (sepIndex < 1) throw new Error('Invalid bech32: no separator');
const hrp = lower.slice(0, sepIndex);
const data = lower.slice(sepIndex + 1);
const values = [];
for (const char of data) {
const val = ALPHABET.indexOf(char);
if (val === -1) throw new Error('Invalid bech32 character');
values.push(val);
}
return { hrp, data: values.slice(0, -6) }; // Remove checksum
}
/**
* Parse BOLT11 invoice
* @param {string} invoice - BOLT11 invoice string
* @returns {Object} Decoded invoice data
*/
function parseBolt11(invoice) {
// Validate prefix
if (!invoice.toLowerCase().startsWith('ln')) {
throw new Error('Invalid BOLT11: must start with "ln"');
}
const { hrp, data } = bech32Decode(invoice);
// Parse HRP: ln + network + amount (optional)
const networkMatch = hrp.match(/^ln(bc|tb|bcrt)(\d+[munp]?)?$/);
if (!networkMatch) {
throw new Error('Invalid BOLT11 network prefix');
}
const networks = {
'bc': 'mainnet',
'tb': 'testnet',
'bcrt': 'regtest'
};
const multipliers = {
'm': 0.001,
'u': 0.000001,
'n': 0.000000001,
'p': 0.000000000001
};
const network = networks[networkMatch[1]];
let amountBtc = null;
let amountMsat = null;
if (networkMatch[2]) {
const amountStr = networkMatch[2];
const multiplier = amountStr.slice(-1);
const value = parseInt(amountStr.slice(0, -1) || amountStr);
if (multipliers[multiplier]) {
amountBtc = value * multipliers[multiplier];
} else {
amountBtc = value;
}
amountMsat = Math.round(amountBtc * 100000000000);
}
// Extract timestamp (first 35 bits = 7 groups of 5 bits)
let timestamp = 0;
for (let i = 0; i < 7; i++) {
timestamp = timestamp * 32 + data[i];
}
return {
network,
amountBtc,
amountMsat,
timestamp,
timestampDate: new Date(timestamp * 1000).toISOString(),
raw: {
hrp,
dataLength: data.length
}
};
}
// Example usage
const testInvoice = 'lnbc10u1pjq2ywdpp5...'; // Truncated for example
try {
const decoded = parseBolt11(testInvoice);
console.log('Decoded invoice:', JSON.stringify(decoded, null, 2));
} catch (err) {
console.error('Parse error:', err.message);
}
module.exports = { parseBolt11, bech32Decode };
Usage
# Install as a module (if using in a project)
npm install light-bolt11-decoder # Recommended for production
# Or use the code above directly
node bolt11-parser.js
Example Output
{
"network": "mainnet",
"amountBtc": 0.00001,
"amountMsat": 1000000,
"timestamp": 1706659200,
"timestampDate": "2024-01-31T00:00:00.000Z",
"raw": {
"hrp": "lnbc10u",
"dataLength": 293
}
}
Agent Notes
Before paying an invoice:
-
Check expiry: Invoices have a default 1-hour expiry. Check
timestamp + expiryagainst current time. -
Verify amount: If
amountMsatis null, the invoice is “zero-amount” and the payer chooses the amount. -
Network match: Ensure your wallet network matches the invoice network (mainnet vs testnet).
-
Single use: BOLT11 invoices should only be paid once. Paying twice may fail or lose funds.
For production agents, use established libraries:
- JavaScript:
bolt11,light-bolt11-decoder - Python:
bolt11 - Rust:
lightning-invoice