Bech32 and Bech32m encoding/decoding library for Bitcoin addresses
npx @tessl/cli install tessl/npm-bech32@2.0.0Bech32 is a TypeScript library providing BIP173/BIP350 compatible Bech32 and Bech32m encoding and decoding for Bitcoin addresses. It supports both the original Bech32 standard (const=1) and the newer Bech32m standard (const=0x2bc830a3), with comprehensive error handling, checksum validation, and bit conversion utilities.
npm install bech32import { bech32, bech32m } from "bech32";For CommonJS:
const { bech32, bech32m } = require("bech32");import { bech32, bech32m } from "bech32";
// Decode Bech32 address
const decoded = bech32.decode('abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw');
// => { prefix: 'abcdef', words: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31] }
// Decode Bech32m address
const decodedM = bech32m.decode('abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx');
// => { prefix: 'abcdef', words: [31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0] }
// Convert bytes to words and encode
const words = bech32.toWords(Buffer.from('foobar', 'utf8'));
const encoded = bech32.encode('foo', words);
// => 'foo1vehk7cnpwgry9h96'
const encodedM = bech32m.encode('foo', words);
// => 'foo1vehk7cnpwgkc4mqc'The library is structured around two main library objects (bech32 and bech32m) that share the same interface but use different encoding constants:
1 for original Bech32 standard (BIP173)0x2bc830a3 for Bech32m standard (BIP350)Both libraries provide the same API for encoding, decoding, and bit conversion utilities.
Encodes a human-readable prefix and 5-bit words into a Bech32/Bech32m string.
/**
* Encodes a prefix and 5-bit words into a Bech32/Bech32m string
* @param prefix - Human-readable prefix (will be converted to lowercase)
* @param words - Array of 5-bit words (values 0-31)
* @param LIMIT - Optional length limit (default: 90, max recommended: 1023)
* @returns Encoded Bech32/Bech32m string
* @throws TypeError if exceeds length limit
* @throws Error for invalid prefix or non-5-bit words
*/
function encode(prefix: string, words: ArrayLike<number>, LIMIT?: number): string;Usage Examples:
import { bech32, bech32m } from "bech32";
// Basic encoding
const words = [15, 25, 9, 5, 23, 15, 17];
const bech32Encoded = bech32.encode('bc', words);
const bech32mEncoded = bech32m.encode('bc', words);
// With custom length limit
const longEncoded = bech32.encode('test', words, 100);Decodes a Bech32/Bech32m string with error throwing on invalid input.
/**
* Decodes a Bech32/Bech32m string (throws on error)
* @param str - Bech32/Bech32m encoded string
* @param LIMIT - Optional length limit (default: 90)
* @returns Decoded object with prefix and words
* @throws Error for invalid strings, checksum failures, etc.
*/
function decode(str: string, LIMIT?: number): Decoded;Usage Examples:
import { bech32, bech32m } from "bech32";
try {
const result = bech32.decode('bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4');
console.log(result.prefix); // 'bc'
console.log(result.words); // Array of 5-bit words
} catch (error) {
console.error('Invalid Bech32 string:', error.message);
}Decodes a Bech32/Bech32m string returning undefined on invalid input instead of throwing.
/**
* Decodes a Bech32/Bech32m string (returns undefined on error)
* @param str - Bech32/Bech32m encoded string
* @param LIMIT - Optional length limit (default: 90)
* @returns Decoded object or undefined if invalid
*/
function decodeUnsafe(str: string, LIMIT?: number): Decoded | undefined;Usage Examples:
import { bech32 } from "bech32";
const result = bech32.decodeUnsafe('invalid-bech32-string');
if (result) {
console.log('Valid:', result);
} else {
console.log('Invalid string');
}Converts 8-bit bytes to 5-bit words for Bech32 encoding.
/**
* Converts 8-bit bytes to 5-bit words
* @param bytes - Array of 8-bit bytes
* @returns Array of 5-bit words with padding
*/
function toWords(bytes: ArrayLike<number>): number[];Usage Examples:
import { bech32 } from "bech32";
// Convert string to bytes then to words
const bytes = Buffer.from('hello', 'utf8');
const words = bech32.toWords(bytes);
console.log(words); // Array of 5-bit values
// Convert hex to words
const hexBytes = Buffer.from('deadbeef', 'hex');
const hexWords = bech32.toWords(hexBytes);Converts 5-bit words to 8-bit bytes with error throwing on invalid input.
/**
* Converts 5-bit words to 8-bit bytes (throws on error)
* @param words - Array of 5-bit words
* @returns Array of 8-bit bytes
* @throws Error for excess padding or non-zero padding
*/
function fromWords(words: ArrayLike<number>): number[];Usage Examples:
import { bech32 } from "bech32";
try {
const words = [15, 25, 9, 5, 23, 15, 17, 4, 8];
const bytes = bech32.fromWords(words);
const text = Buffer.from(bytes).toString('utf8');
console.log(text);
} catch (error) {
console.error('Invalid word padding:', error.message);
}Converts 5-bit words to 8-bit bytes returning undefined on invalid input.
/**
* Converts 5-bit words to 8-bit bytes (returns undefined on error)
* @param words - Array of 5-bit words
* @returns Array of 8-bit bytes or undefined if invalid
*/
function fromWordsUnsafe(words: ArrayLike<number>): number[] | undefined;Usage Examples:
import { bech32 } from "bech32";
const words = [15, 25, 9, 5, 23, 15, 17, 4, 8];
const bytes = bech32.fromWordsUnsafe(words);
if (bytes) {
const text = Buffer.from(bytes).toString('utf8');
console.log(text);
} else {
console.log('Invalid padding');
}/**
* Result structure for decoded Bech32/Bech32m strings
*/
interface Decoded {
/** Human-readable prefix */
prefix: string;
/** Array of 5-bit words (values 0-31) */
words: number[];
}
/**
* Interface defining the structure of bech32/bech32m library objects
*/
interface BechLib {
/** Decode with undefined return on error */
decodeUnsafe: (str: string, LIMIT?: number) => Decoded | undefined;
/** Decode with error throwing */
decode: (str: string, LIMIT?: number) => Decoded;
/** Encode prefix and words to string */
encode: (prefix: string, words: ArrayLike<number>, LIMIT?: number) => string;
/** Convert 8-bit bytes to 5-bit words */
toWords: (bytes: ArrayLike<number>) => number[];
/** Convert 5-bit words to 8-bit bytes (unsafe) */
fromWordsUnsafe: (words: ArrayLike<number>) => number[] | undefined;
/** Convert 5-bit words to 8-bit bytes (safe) */
fromWords: (words: ArrayLike<number>) => number[];
}The library provides comprehensive error handling for various invalid input scenarios:
Common error patterns:
// Length limit exceeded
bech32.encode('test', words, 50); // May throw TypeError
// Invalid checksum
bech32.decode('bc1invalid'); // Throws Error with descriptive message
// Padding errors
bech32.fromWords([31, 31, 31]); // May throw Error for invalid padding