├── dist ├── cjs │ ├── test │ │ ├── index.d.ts │ │ ├── types.d.ts │ │ ├── types.cjs │ │ └── index.cjs │ ├── index.d.ts │ └── index.cjs └── esm │ └── index.js ├── .gitignore ├── tsconfig.cjs.json ├── tsconfig.json ├── tsconfig.base.json ├── .travis.yml ├── .github └── workflows │ └── ci.yml ├── tslint.json ├── LICENSE ├── README.md ├── package.json └── src ├── test ├── types.ts ├── index.ts └── fixtures.json └── index.ts /dist/cjs/test/index.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /dist/cjs/test/types.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | node_modules/ 3 | .nyc_output 4 | -------------------------------------------------------------------------------- /tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "emitDeclarationOnly": false, 6 | "outDir": "dist/cjs", 7 | "module": "commonjs" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "dist/esm", 5 | "esModuleInterop": true, 6 | "resolveJsonModule": true, 7 | "module": "ESNext" 8 | }, 9 | "exclude": ["src/test/**/*"] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "resolveJsonModule": true, 5 | "target": "es2015", 6 | "moduleResolution": "node", 7 | "noImplicitAny": true, 8 | "preserveConstEnums": true 9 | }, 10 | "include": ["src"], 11 | "exclude": ["node_modules"] 12 | } 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "10" 5 | - "11" 6 | - "13" 7 | - "lts/*" 8 | matrix: 9 | include: 10 | - node_js: "lts/*" 11 | env: TEST_SUITE=lint 12 | - node_js: "lts/*" 13 | env: TEST_SUITE=format:ci 14 | - node_js: "lts/*" 15 | env: TEST_SUITE=gitdiff:ci 16 | - node_js: "lts/*" 17 | env: TEST_SUITE=coverage 18 | env: 19 | - TEST_SUITE=test 20 | script: npm run-script $TEST_SUITE 21 | -------------------------------------------------------------------------------- /dist/cjs/index.d.ts: -------------------------------------------------------------------------------- 1 | declare function toWords(bytes: ArrayLike): number[]; 2 | declare function fromWordsUnsafe(words: ArrayLike): number[] | undefined; 3 | declare function fromWords(words: ArrayLike): number[]; 4 | export declare const bech32: BechLib; 5 | export declare const bech32m: BechLib; 6 | export interface Decoded { 7 | prefix: string; 8 | words: number[]; 9 | } 10 | export interface BechLib { 11 | decodeUnsafe: (str: string, LIMIT?: number | undefined) => Decoded | undefined; 12 | decode: (str: string, LIMIT?: number | undefined) => Decoded; 13 | encode: (prefix: string, words: ArrayLike, LIMIT?: number | undefined) => string; 14 | toWords: typeof toWords; 15 | fromWordsUnsafe: typeof fromWordsUnsafe; 16 | fromWords: typeof fromWords; 17 | } 18 | export {}; 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | node-version: 14 | - 10 15 | - 11 16 | - 13 17 | - lts/* 18 | test-suite: 19 | - test 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - name: Install dependencies 28 | run: npm install 29 | - name: Setup 30 | run: npm run build 31 | - name: Run test suite 32 | run: npm run ${{ matrix.test-suite }} 33 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended"], 4 | "rules": { 5 | "arrow-parens": [true, "ban-single-arg-parens"], 6 | "curly": false, 7 | "indent": [true, "spaces", 2], 8 | "interface-name": [false], 9 | "match-default-export-name": true, 10 | "max-classes-per-file": [false], 11 | "member-access": [true, "no-public"], 12 | "no-bitwise": false, 13 | "no-console": false, 14 | "no-empty": [true, "allow-empty-catch"], 15 | "no-implicit-dependencies": [true, "dev"], 16 | "no-return-await": true, 17 | "no-var-requires": false, 18 | "no-unused-expression": false, 19 | "object-literal-sort-keys": false, 20 | "quotemark": [true, "single", "avoid-escape"], 21 | "typedef": [ 22 | true, 23 | "call-signature", 24 | "arrow-call-signature", 25 | "property-declaration" 26 | ], 27 | "variable-name": [ 28 | true, 29 | "ban-keywords", 30 | "check-format", 31 | "allow-leading-underscore", 32 | "allow-pascal-case" 33 | ], 34 | "prefer-for-of": false 35 | }, 36 | "rulesDirectory": [] 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Pieter Wuille 4 | Copyright (c) 2018 bitcoinjs contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bech32 2 | [![build status](https://secure.travis-ci.org/bitcoinjs/bech32.png)](http://travis-ci.org/bitcoinjs/bech32) 3 | [![Version](http://img.shields.io/npm/v/bech32.svg)](https://www.npmjs.org/package/bech32) 4 | 5 | A [BIP173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)/[BIP350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki) compatible Bech32/Bech32m encoding/decoding library. 6 | 7 | 8 | ## Example 9 | ``` javascript 10 | let { bech32, bech32m } = require('bech32') 11 | 12 | bech32.decode('abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw') 13 | // => { 14 | // prefix: 'abcdef', 15 | // 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] 16 | // } 17 | bech32m.decode('abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx') 18 | // => { 19 | // prefix: 'abcdef', 20 | // 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] 21 | // } 22 | 23 | // toWords etc. are available on both bech32 and bech32m objects 24 | let words = bech32.toWords(Buffer.from('foobar', 'utf8')) 25 | bech32.encode('foo', words) 26 | // => 'foo1vehk7cnpwgry9h96' 27 | bech32m.encode('foo', words) 28 | // => 'foo1vehk7cnpwgkc4mqc' 29 | ``` 30 | 31 | 32 | ### Advanced 33 | BIP173 enforces a limitation of 90 characters, if extend the `LIMIT` parameter beyond this, be aware that the [effectiveness of checksum decreases as the length increases](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#checksum-design). 34 | 35 | It is highly recommended **NOT** exceed 1023 characters, as the module could only guarantee detecting 1 error. 36 | 37 | 38 | ## Credits 39 | - [Peter Wuille](https://github.com/sipa/bech32) for the reference JavaScript implementation, and for authoring the Bech32 [BIP173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) and Bech32m [BIP350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki). 40 | 41 | 42 | ## License [MIT](LICENSE) 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bech32", 3 | "version": "2.0.0", 4 | "description": "Bech32 encoding / decoding", 5 | "type": "module", 6 | "keywords": [ 7 | "base32", 8 | "bech32", 9 | "bech32m", 10 | "bitcoin", 11 | "crypto", 12 | "crytography", 13 | "decode", 14 | "decoding", 15 | "encode", 16 | "encoding" 17 | ], 18 | "main": "dist/cjs/index.cjs", 19 | "module": "dist/esm/index.js", 20 | "types": "dist/index.d.ts", 21 | "files": [ 22 | "dist/cjs/index.cjs", 23 | "dist/cjs/index.d.ts", 24 | "dist/esm/index.js" 25 | ], 26 | "exports": { 27 | ".": { 28 | "require": "./dist/cjs/index.cjs", 29 | "import": "./dist/esm/index.js", 30 | "types": "./dist/cjs/index.d.ts" 31 | } 32 | }, 33 | "license": "MIT", 34 | "devDependencies": { 35 | "@types/node": "^14.0.14", 36 | "@types/tape": "^4.13.4", 37 | "nyc": "^15.0.0", 38 | "prettier": "^2.0.5", 39 | "rimraf": "^3.0.2", 40 | "tap-dot": "*", 41 | "tape": "^5.3.0", 42 | "ts-node": "^10.9.2", 43 | "tslint": "^6.1.3", 44 | "tsx": "^4.7.2", 45 | "typescript": "^3.9.5" 46 | }, 47 | "repository": { 48 | "url": "http://github.com/bitcoinjs/bech32", 49 | "type": "git" 50 | }, 51 | "scripts": { 52 | "build": "npm run clean && tsc -p tsconfig.json && tsc -p tsconfig.cjs.json", 53 | "postbuild": "find dist/cjs -type f -name \"*.js\" -exec bash -c 'mv \"$0\" \"${0%.js}.cjs\"' {} \\;", 54 | "clean": "rimraf dist", 55 | "coverage": "nyc -x [dist/cjs/test/*.ts] --check-coverage --branches 90 --functions 90 npm test", 56 | "format": "npm run prettier -- --write", 57 | "format:ci": "npm run prettier -- --check", 58 | "gitdiff:ci": "npm run build && git diff --exit-code", 59 | "lint:fix": "npm run lint -- --fix", 60 | "lint": "tslint -p tsconfig.cjs.json -c tslint.json", 61 | "prettier": "prettier --print-width 100 --single-quote --trailing-comma=all \"**/!(*.d).ts\"", 62 | "test": "tape dist/cjs/test/*.cjs | tap-dot" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/types.ts: -------------------------------------------------------------------------------- 1 | // This is a simple example file that shows the usage of this library in TypeScript. 2 | // When you open it in Visual Studio Code, the built-in TypeScript server should run all 3 | // the type checks. For manually runtime testing you can use ts-node to run this file. 4 | 5 | // @ts-ignore 6 | import { bech32 } from '../index.cjs'; 7 | 8 | function encodeUint8Array(prefix: string, data: Uint8Array): string { 9 | const address = bech32.encode(prefix, bech32.toWords(data)); 10 | return address; 11 | } 12 | 13 | function decodeUint8Array(address: string): { readonly prefix: string; readonly data: Uint8Array } { 14 | const decodedAddress = bech32.decode(address); 15 | return { 16 | prefix: decodedAddress.prefix, 17 | data: new Uint8Array(bech32.fromWords(decodedAddress.words)), 18 | }; 19 | } 20 | 21 | function encodeBuffer(prefix: string, data: Buffer): string { 22 | const address = bech32.encode(prefix, bech32.toWords(data)); 23 | return address; 24 | } 25 | 26 | function decodeBuffer(address: string): { readonly prefix: string; readonly data: Buffer } { 27 | const decodedAddress = bech32.decode(address); 28 | return { 29 | prefix: decodedAddress.prefix, 30 | data: Buffer.from(bech32.fromWords(decodedAddress.words)), 31 | }; 32 | } 33 | 34 | function encodeUnsafe(prefix: string, data: Uint8Array): string | undefined { 35 | const address = bech32.encode(prefix, bech32.toWords(data)); 36 | return address; 37 | } 38 | 39 | function decodeUnsafe(address: string): { readonly prefix: string; readonly data: Uint8Array } { 40 | const decodedAddress = bech32.decodeUnsafe(address)!; 41 | return { 42 | prefix: decodedAddress.prefix, 43 | data: new Uint8Array(bech32.fromWordsUnsafe(decodedAddress.words)!), 44 | }; 45 | } 46 | 47 | function main(): void { 48 | { 49 | const prefix = 'foo'; 50 | const data = new Uint8Array([0x00, 0x11, 0x22]); 51 | const address = encodeUint8Array(prefix, data); 52 | const decoded = decodeUint8Array(address); 53 | console.log(prefix, data, address, decoded); 54 | } 55 | { 56 | const prefix = 'foo'; 57 | const data = Buffer.from([0x00, 0x11, 0x22]); 58 | const address = encodeBuffer(prefix, data); 59 | const decoded = decodeBuffer(address); 60 | console.log(prefix, data, address, decoded); 61 | } 62 | { 63 | const prefix = 'foo'; 64 | const data = new Uint8Array([0x00, 0x11, 0x22]); 65 | const address = encodeUnsafe(prefix, data); 66 | const decoded = decodeUnsafe(address!); 67 | console.log(prefix, data, address, decoded); 68 | } 69 | } 70 | 71 | main(); 72 | -------------------------------------------------------------------------------- /dist/cjs/test/types.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // This is a simple example file that shows the usage of this library in TypeScript. 3 | // When you open it in Visual Studio Code, the built-in TypeScript server should run all 4 | // the type checks. For manually runtime testing you can use ts-node to run this file. 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | // @ts-ignore 7 | const index_cjs_1 = require("../index.cjs"); 8 | function encodeUint8Array(prefix, data) { 9 | const address = index_cjs_1.bech32.encode(prefix, index_cjs_1.bech32.toWords(data)); 10 | return address; 11 | } 12 | function decodeUint8Array(address) { 13 | const decodedAddress = index_cjs_1.bech32.decode(address); 14 | return { 15 | prefix: decodedAddress.prefix, 16 | data: new Uint8Array(index_cjs_1.bech32.fromWords(decodedAddress.words)), 17 | }; 18 | } 19 | function encodeBuffer(prefix, data) { 20 | const address = index_cjs_1.bech32.encode(prefix, index_cjs_1.bech32.toWords(data)); 21 | return address; 22 | } 23 | function decodeBuffer(address) { 24 | const decodedAddress = index_cjs_1.bech32.decode(address); 25 | return { 26 | prefix: decodedAddress.prefix, 27 | data: Buffer.from(index_cjs_1.bech32.fromWords(decodedAddress.words)), 28 | }; 29 | } 30 | function encodeUnsafe(prefix, data) { 31 | const address = index_cjs_1.bech32.encode(prefix, index_cjs_1.bech32.toWords(data)); 32 | return address; 33 | } 34 | function decodeUnsafe(address) { 35 | const decodedAddress = index_cjs_1.bech32.decodeUnsafe(address); 36 | return { 37 | prefix: decodedAddress.prefix, 38 | data: new Uint8Array(index_cjs_1.bech32.fromWordsUnsafe(decodedAddress.words)), 39 | }; 40 | } 41 | function main() { 42 | { 43 | const prefix = 'foo'; 44 | const data = new Uint8Array([0x00, 0x11, 0x22]); 45 | const address = encodeUint8Array(prefix, data); 46 | const decoded = decodeUint8Array(address); 47 | console.log(prefix, data, address, decoded); 48 | } 49 | { 50 | const prefix = 'foo'; 51 | const data = Buffer.from([0x00, 0x11, 0x22]); 52 | const address = encodeBuffer(prefix, data); 53 | const decoded = decodeBuffer(address); 54 | console.log(prefix, data, address, decoded); 55 | } 56 | { 57 | const prefix = 'foo'; 58 | const data = new Uint8Array([0x00, 0x11, 0x22]); 59 | const address = encodeUnsafe(prefix, data); 60 | const decoded = decodeUnsafe(address); 61 | console.log(prefix, data, address, decoded); 62 | } 63 | } 64 | main(); 65 | -------------------------------------------------------------------------------- /dist/cjs/test/index.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | // @ts-ignore 4 | const bech32Lib = require("../index.cjs"); 5 | const tape = require("tape"); 6 | const fixtures = require("../../../src/test/fixtures"); 7 | function testValidFixture(f, bech32) { 8 | if (f.hex) { 9 | tape(`fromWords/toWords ${f.hex}`, (t) => { 10 | t.plan(3); 11 | const words = bech32.toWords(Buffer.from(f.hex, "hex")); 12 | const bytes = Buffer.from(bech32.fromWords(f.words)); 13 | const bytes2 = Buffer.from(bech32.fromWordsUnsafe(f.words)); 14 | t.same(words, f.words); 15 | t.same(bytes.toString("hex"), f.hex); 16 | t.same(bytes2.toString("hex"), f.hex); 17 | }); 18 | } 19 | tape(`encode ${f.prefix} ${f.hex || f.words}`, (t) => { 20 | t.plan(1); 21 | t.strictEqual(bech32.encode(f.prefix, f.words, f.limit), f.string.toLowerCase()); 22 | }); 23 | tape(`decode ${f.string}`, (t) => { 24 | t.plan(2); 25 | const expected = { 26 | prefix: f.prefix.toLowerCase(), 27 | words: f.words, 28 | }; 29 | t.same(bech32.decodeUnsafe(f.string, f.limit), expected); 30 | t.same(bech32.decode(f.string, f.limit), expected); 31 | }); 32 | tape(`fails for ${f.string} with 1 bit flipped`, (t) => { 33 | t.plan(2); 34 | const buffer = Buffer.from(f.string, "utf8"); 35 | buffer[f.string.lastIndexOf("1") + 1] ^= 0x1; // flip a bit, after the prefix 36 | const str = buffer.toString("utf8"); 37 | t.equal(bech32.decodeUnsafe(str, f.limit), undefined); 38 | t.throws(() => { 39 | bech32.decode(str, f.limit); 40 | }, new RegExp("Invalid checksum|Unknown character")); 41 | }); 42 | // === compare of objects compares reference in memory, so this works 43 | const wrongBech32 = bech32 === bech32Lib.bech32 ? bech32Lib.bech32m : bech32Lib.bech32; 44 | tape(`fails for ${f.string} with wrong encoding`, (t) => { 45 | t.plan(2); 46 | t.equal(wrongBech32.decodeUnsafe(f.string, f.limit), undefined); 47 | t.throws(() => { 48 | wrongBech32.decode(f.string, f.limit); 49 | }, new RegExp("Invalid checksum")); 50 | }); 51 | } 52 | function testInvalidFixture(f, bech32) { 53 | if (f.prefix !== undefined && f.words !== undefined) { 54 | tape(`encode fails with (${f.exception})`, (t) => { 55 | t.plan(1); 56 | t.throws(() => { 57 | bech32.encode(f.prefix, f.words); 58 | }, new RegExp(f.exception)); 59 | }); 60 | } 61 | if (f.string !== undefined || f.stringHex) { 62 | const str = f.string || Buffer.from(f.stringHex, "hex").toString("binary"); 63 | tape(`decode fails for ${str} (${f.exception})`, (t) => { 64 | t.plan(2); 65 | t.equal(bech32.decodeUnsafe(str), undefined); 66 | t.throws(() => { 67 | bech32.decode(str); 68 | }, new RegExp(f.exception)); 69 | }); 70 | } 71 | } 72 | fixtures.bech32.valid.forEach((f) => { 73 | testValidFixture(f, bech32Lib.bech32); 74 | }); 75 | fixtures.bech32.invalid.forEach((f) => { 76 | testInvalidFixture(f, bech32Lib.bech32); 77 | }); 78 | fixtures.bech32m.valid.forEach((f) => { 79 | testValidFixture(f, bech32Lib.bech32m); 80 | }); 81 | fixtures.bech32m.invalid.forEach((f) => { 82 | testInvalidFixture(f, bech32Lib.bech32m); 83 | }); 84 | fixtures.fromWords.invalid.forEach((f) => { 85 | tape(`fromWords fails with ${f.exception}`, (t) => { 86 | t.plan(2); 87 | t.equal(bech32Lib.bech32.fromWordsUnsafe(f.words), undefined); 88 | t.throws(() => { 89 | bech32Lib.bech32.fromWords(f.words); 90 | }, new RegExp(f.exception)); 91 | }); 92 | }); 93 | tape("toWords/toWordsUnsafe accept bytes as ArrayLike", (t) => { 94 | // Ensures that only the two operations from 95 | // interface ArrayLike { 96 | // readonly length: number; 97 | // readonly [n: number]: T; 98 | // } 99 | // are used, which are common for the typical binary types Uint8Array, Buffer and 100 | // Array. 101 | const bytes = { 102 | length: 5, 103 | 0: 0x00, 104 | 1: 0x11, 105 | 2: 0x22, 106 | 3: 0x33, 107 | 4: 0xff, 108 | }; 109 | const words = bech32Lib.bech32.toWords(bytes); 110 | t.plan(1); 111 | t.same(words, [0, 0, 8, 18, 4, 12, 31, 31]); 112 | }); 113 | -------------------------------------------------------------------------------- /src/test/index.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // @ts-ignore 4 | import * as bech32Lib from '../index.cjs'; 5 | import * as tape from 'tape'; 6 | const fixtures = require('../../../src/test/fixtures'); 7 | 8 | type Fixture = { 9 | string: string; 10 | prefix: string; 11 | hex: string; 12 | words: number[]; 13 | limit?: number; 14 | }; 15 | 16 | type InvalidFixture = { 17 | string: string; 18 | stringHex: string; 19 | prefix: string; 20 | hex: string; 21 | words: number[]; 22 | limit?: number; 23 | exception: string; 24 | }; 25 | 26 | function testValidFixture(f: Fixture, bech32: any): void { 27 | if (f.hex) { 28 | tape(`fromWords/toWords ${f.hex}`, (t): void => { 29 | t.plan(3); 30 | 31 | const words = bech32.toWords(Buffer.from(f.hex, 'hex')); 32 | const bytes = Buffer.from(bech32.fromWords(f.words)); 33 | const bytes2 = Buffer.from(bech32.fromWordsUnsafe(f.words)); 34 | t.same(words, f.words); 35 | t.same(bytes.toString('hex'), f.hex); 36 | t.same(bytes2.toString('hex'), f.hex); 37 | }); 38 | } 39 | 40 | tape(`encode ${f.prefix} ${f.hex || f.words}`, (t): void => { 41 | t.plan(1); 42 | t.strictEqual(bech32.encode(f.prefix, f.words, f.limit), f.string.toLowerCase()); 43 | }); 44 | 45 | tape(`decode ${f.string}`, (t): void => { 46 | t.plan(2); 47 | 48 | const expected = { 49 | prefix: f.prefix.toLowerCase(), 50 | words: f.words, 51 | }; 52 | t.same(bech32.decodeUnsafe(f.string, f.limit), expected); 53 | t.same(bech32.decode(f.string, f.limit), expected); 54 | }); 55 | 56 | tape(`fails for ${f.string} with 1 bit flipped`, (t): void => { 57 | t.plan(2); 58 | 59 | const buffer = Buffer.from(f.string, 'utf8'); 60 | buffer[f.string.lastIndexOf('1') + 1] ^= 0x1; // flip a bit, after the prefix 61 | const str = buffer.toString('utf8'); 62 | t.equal(bech32.decodeUnsafe(str, f.limit), undefined); 63 | t.throws((): void => { 64 | bech32.decode(str, f.limit); 65 | }, new RegExp('Invalid checksum|Unknown character')); 66 | }); 67 | 68 | // === compare of objects compares reference in memory, so this works 69 | const wrongBech32 = bech32 === bech32Lib.bech32 ? bech32Lib.bech32m : bech32Lib.bech32; 70 | tape(`fails for ${f.string} with wrong encoding`, (t): void => { 71 | t.plan(2); 72 | 73 | t.equal(wrongBech32.decodeUnsafe(f.string, f.limit), undefined); 74 | t.throws((): void => { 75 | wrongBech32.decode(f.string, f.limit); 76 | }, new RegExp('Invalid checksum')); 77 | }); 78 | } 79 | 80 | function testInvalidFixture(f: InvalidFixture, bech32: any): void { 81 | if (f.prefix !== undefined && f.words !== undefined) { 82 | tape(`encode fails with (${f.exception})`, (t): void => { 83 | t.plan(1); 84 | 85 | t.throws((): void => { 86 | bech32.encode(f.prefix, f.words); 87 | }, new RegExp(f.exception)); 88 | }); 89 | } 90 | 91 | if (f.string !== undefined || f.stringHex) { 92 | const str = f.string || Buffer.from(f.stringHex, 'hex').toString('binary'); 93 | 94 | tape(`decode fails for ${str} (${f.exception})`, (t): void => { 95 | t.plan(2); 96 | t.equal(bech32.decodeUnsafe(str), undefined); 97 | t.throws((): void => { 98 | bech32.decode(str); 99 | }, new RegExp(f.exception)); 100 | }); 101 | } 102 | } 103 | 104 | fixtures.bech32.valid.forEach((f: Fixture): void => { 105 | testValidFixture(f, bech32Lib.bech32); 106 | }); 107 | 108 | fixtures.bech32.invalid.forEach((f: InvalidFixture): void => { 109 | testInvalidFixture(f, bech32Lib.bech32); 110 | }); 111 | 112 | fixtures.bech32m.valid.forEach((f: Fixture): void => { 113 | testValidFixture(f, bech32Lib.bech32m); 114 | }); 115 | 116 | fixtures.bech32m.invalid.forEach((f: InvalidFixture): void => { 117 | testInvalidFixture(f, bech32Lib.bech32m); 118 | }); 119 | 120 | fixtures.fromWords.invalid.forEach((f: InvalidFixture): void => { 121 | tape(`fromWords fails with ${f.exception}`, (t): void => { 122 | t.plan(2); 123 | t.equal(bech32Lib.bech32.fromWordsUnsafe(f.words), undefined); 124 | t.throws((): void => { 125 | bech32Lib.bech32.fromWords(f.words); 126 | }, new RegExp(f.exception)); 127 | }); 128 | }); 129 | 130 | tape('toWords/toWordsUnsafe accept bytes as ArrayLike', (t): void => { 131 | // Ensures that only the two operations from 132 | // interface ArrayLike { 133 | // readonly length: number; 134 | // readonly [n: number]: T; 135 | // } 136 | // are used, which are common for the typical binary types Uint8Array, Buffer and 137 | // Array. 138 | 139 | const bytes = { 140 | length: 5, 141 | 0: 0x00, 142 | 1: 0x11, 143 | 2: 0x22, 144 | 3: 0x33, 145 | 4: 0xff, 146 | }; 147 | const words = bech32Lib.bech32.toWords(bytes); 148 | t.plan(1); 149 | t.same(words, [0, 0, 8, 18, 4, 12, 31, 31]); 150 | }); 151 | -------------------------------------------------------------------------------- /dist/esm/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; 3 | const ALPHABET_MAP = {}; 4 | for (let z = 0; z < ALPHABET.length; z++) { 5 | const x = ALPHABET.charAt(z); 6 | ALPHABET_MAP[x] = z; 7 | } 8 | function polymodStep(pre) { 9 | const b = pre >> 25; 10 | return (((pre & 0x1ffffff) << 5) ^ 11 | (-((b >> 0) & 1) & 0x3b6a57b2) ^ 12 | (-((b >> 1) & 1) & 0x26508e6d) ^ 13 | (-((b >> 2) & 1) & 0x1ea119fa) ^ 14 | (-((b >> 3) & 1) & 0x3d4233dd) ^ 15 | (-((b >> 4) & 1) & 0x2a1462b3)); 16 | } 17 | function prefixChk(prefix) { 18 | let chk = 1; 19 | for (let i = 0; i < prefix.length; ++i) { 20 | const c = prefix.charCodeAt(i); 21 | if (c < 33 || c > 126) 22 | return 'Invalid prefix (' + prefix + ')'; 23 | chk = polymodStep(chk) ^ (c >> 5); 24 | } 25 | chk = polymodStep(chk); 26 | for (let i = 0; i < prefix.length; ++i) { 27 | const v = prefix.charCodeAt(i); 28 | chk = polymodStep(chk) ^ (v & 0x1f); 29 | } 30 | return chk; 31 | } 32 | function convert(data, inBits, outBits, pad) { 33 | let value = 0; 34 | let bits = 0; 35 | const maxV = (1 << outBits) - 1; 36 | const result = []; 37 | for (let i = 0; i < data.length; ++i) { 38 | value = (value << inBits) | data[i]; 39 | bits += inBits; 40 | while (bits >= outBits) { 41 | bits -= outBits; 42 | result.push((value >> bits) & maxV); 43 | } 44 | } 45 | if (pad) { 46 | if (bits > 0) { 47 | result.push((value << (outBits - bits)) & maxV); 48 | } 49 | } 50 | else { 51 | if (bits >= inBits) 52 | return 'Excess padding'; 53 | if ((value << (outBits - bits)) & maxV) 54 | return 'Non-zero padding'; 55 | } 56 | return result; 57 | } 58 | function toWords(bytes) { 59 | return convert(bytes, 8, 5, true); 60 | } 61 | function fromWordsUnsafe(words) { 62 | const res = convert(words, 5, 8, false); 63 | if (Array.isArray(res)) 64 | return res; 65 | } 66 | function fromWords(words) { 67 | const res = convert(words, 5, 8, false); 68 | if (Array.isArray(res)) 69 | return res; 70 | throw new Error(res); 71 | } 72 | function getLibraryFromEncoding(encoding) { 73 | let ENCODING_CONST; 74 | if (encoding === 'bech32') { 75 | ENCODING_CONST = 1; 76 | } 77 | else { 78 | ENCODING_CONST = 0x2bc830a3; 79 | } 80 | function encode(prefix, words, LIMIT) { 81 | LIMIT = LIMIT || 90; 82 | if (prefix.length + 7 + words.length > LIMIT) 83 | throw new TypeError('Exceeds length limit'); 84 | prefix = prefix.toLowerCase(); 85 | // determine chk mod 86 | let chk = prefixChk(prefix); 87 | if (typeof chk === 'string') 88 | throw new Error(chk); 89 | let result = prefix + '1'; 90 | for (let i = 0; i < words.length; ++i) { 91 | const x = words[i]; 92 | if (x >> 5 !== 0) 93 | throw new Error('Non 5-bit word'); 94 | chk = polymodStep(chk) ^ x; 95 | result += ALPHABET.charAt(x); 96 | } 97 | for (let i = 0; i < 6; ++i) { 98 | chk = polymodStep(chk); 99 | } 100 | chk ^= ENCODING_CONST; 101 | for (let i = 0; i < 6; ++i) { 102 | const v = (chk >> ((5 - i) * 5)) & 0x1f; 103 | result += ALPHABET.charAt(v); 104 | } 105 | return result; 106 | } 107 | function __decode(str, LIMIT) { 108 | LIMIT = LIMIT || 90; 109 | if (str.length < 8) 110 | return str + ' too short'; 111 | if (str.length > LIMIT) 112 | return 'Exceeds length limit'; 113 | // don't allow mixed case 114 | const lowered = str.toLowerCase(); 115 | const uppered = str.toUpperCase(); 116 | if (str !== lowered && str !== uppered) 117 | return 'Mixed-case string ' + str; 118 | str = lowered; 119 | const split = str.lastIndexOf('1'); 120 | if (split === -1) 121 | return 'No separator character for ' + str; 122 | if (split === 0) 123 | return 'Missing prefix for ' + str; 124 | const prefix = str.slice(0, split); 125 | const wordChars = str.slice(split + 1); 126 | if (wordChars.length < 6) 127 | return 'Data too short'; 128 | let chk = prefixChk(prefix); 129 | if (typeof chk === 'string') 130 | return chk; 131 | const words = []; 132 | for (let i = 0; i < wordChars.length; ++i) { 133 | const c = wordChars.charAt(i); 134 | const v = ALPHABET_MAP[c]; 135 | if (v === undefined) 136 | return 'Unknown character ' + c; 137 | chk = polymodStep(chk) ^ v; 138 | // not in the checksum? 139 | if (i + 6 >= wordChars.length) 140 | continue; 141 | words.push(v); 142 | } 143 | if (chk !== ENCODING_CONST) 144 | return 'Invalid checksum for ' + str; 145 | return { prefix, words }; 146 | } 147 | function decodeUnsafe(str, LIMIT) { 148 | const res = __decode(str, LIMIT); 149 | if (typeof res === 'object') 150 | return res; 151 | } 152 | function decode(str, LIMIT) { 153 | const res = __decode(str, LIMIT); 154 | if (typeof res === 'object') 155 | return res; 156 | throw new Error(res); 157 | } 158 | return { 159 | decodeUnsafe, 160 | decode, 161 | encode, 162 | toWords, 163 | fromWordsUnsafe, 164 | fromWords, 165 | }; 166 | } 167 | export const bech32 = getLibraryFromEncoding('bech32'); 168 | export const bech32m = getLibraryFromEncoding('bech32m'); 169 | -------------------------------------------------------------------------------- /dist/cjs/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.bech32m = exports.bech32 = void 0; 4 | const ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; 5 | const ALPHABET_MAP = {}; 6 | for (let z = 0; z < ALPHABET.length; z++) { 7 | const x = ALPHABET.charAt(z); 8 | ALPHABET_MAP[x] = z; 9 | } 10 | function polymodStep(pre) { 11 | const b = pre >> 25; 12 | return (((pre & 0x1ffffff) << 5) ^ 13 | (-((b >> 0) & 1) & 0x3b6a57b2) ^ 14 | (-((b >> 1) & 1) & 0x26508e6d) ^ 15 | (-((b >> 2) & 1) & 0x1ea119fa) ^ 16 | (-((b >> 3) & 1) & 0x3d4233dd) ^ 17 | (-((b >> 4) & 1) & 0x2a1462b3)); 18 | } 19 | function prefixChk(prefix) { 20 | let chk = 1; 21 | for (let i = 0; i < prefix.length; ++i) { 22 | const c = prefix.charCodeAt(i); 23 | if (c < 33 || c > 126) 24 | return 'Invalid prefix (' + prefix + ')'; 25 | chk = polymodStep(chk) ^ (c >> 5); 26 | } 27 | chk = polymodStep(chk); 28 | for (let i = 0; i < prefix.length; ++i) { 29 | const v = prefix.charCodeAt(i); 30 | chk = polymodStep(chk) ^ (v & 0x1f); 31 | } 32 | return chk; 33 | } 34 | function convert(data, inBits, outBits, pad) { 35 | let value = 0; 36 | let bits = 0; 37 | const maxV = (1 << outBits) - 1; 38 | const result = []; 39 | for (let i = 0; i < data.length; ++i) { 40 | value = (value << inBits) | data[i]; 41 | bits += inBits; 42 | while (bits >= outBits) { 43 | bits -= outBits; 44 | result.push((value >> bits) & maxV); 45 | } 46 | } 47 | if (pad) { 48 | if (bits > 0) { 49 | result.push((value << (outBits - bits)) & maxV); 50 | } 51 | } 52 | else { 53 | if (bits >= inBits) 54 | return 'Excess padding'; 55 | if ((value << (outBits - bits)) & maxV) 56 | return 'Non-zero padding'; 57 | } 58 | return result; 59 | } 60 | function toWords(bytes) { 61 | return convert(bytes, 8, 5, true); 62 | } 63 | function fromWordsUnsafe(words) { 64 | const res = convert(words, 5, 8, false); 65 | if (Array.isArray(res)) 66 | return res; 67 | } 68 | function fromWords(words) { 69 | const res = convert(words, 5, 8, false); 70 | if (Array.isArray(res)) 71 | return res; 72 | throw new Error(res); 73 | } 74 | function getLibraryFromEncoding(encoding) { 75 | let ENCODING_CONST; 76 | if (encoding === 'bech32') { 77 | ENCODING_CONST = 1; 78 | } 79 | else { 80 | ENCODING_CONST = 0x2bc830a3; 81 | } 82 | function encode(prefix, words, LIMIT) { 83 | LIMIT = LIMIT || 90; 84 | if (prefix.length + 7 + words.length > LIMIT) 85 | throw new TypeError('Exceeds length limit'); 86 | prefix = prefix.toLowerCase(); 87 | // determine chk mod 88 | let chk = prefixChk(prefix); 89 | if (typeof chk === 'string') 90 | throw new Error(chk); 91 | let result = prefix + '1'; 92 | for (let i = 0; i < words.length; ++i) { 93 | const x = words[i]; 94 | if (x >> 5 !== 0) 95 | throw new Error('Non 5-bit word'); 96 | chk = polymodStep(chk) ^ x; 97 | result += ALPHABET.charAt(x); 98 | } 99 | for (let i = 0; i < 6; ++i) { 100 | chk = polymodStep(chk); 101 | } 102 | chk ^= ENCODING_CONST; 103 | for (let i = 0; i < 6; ++i) { 104 | const v = (chk >> ((5 - i) * 5)) & 0x1f; 105 | result += ALPHABET.charAt(v); 106 | } 107 | return result; 108 | } 109 | function __decode(str, LIMIT) { 110 | LIMIT = LIMIT || 90; 111 | if (str.length < 8) 112 | return str + ' too short'; 113 | if (str.length > LIMIT) 114 | return 'Exceeds length limit'; 115 | // don't allow mixed case 116 | const lowered = str.toLowerCase(); 117 | const uppered = str.toUpperCase(); 118 | if (str !== lowered && str !== uppered) 119 | return 'Mixed-case string ' + str; 120 | str = lowered; 121 | const split = str.lastIndexOf('1'); 122 | if (split === -1) 123 | return 'No separator character for ' + str; 124 | if (split === 0) 125 | return 'Missing prefix for ' + str; 126 | const prefix = str.slice(0, split); 127 | const wordChars = str.slice(split + 1); 128 | if (wordChars.length < 6) 129 | return 'Data too short'; 130 | let chk = prefixChk(prefix); 131 | if (typeof chk === 'string') 132 | return chk; 133 | const words = []; 134 | for (let i = 0; i < wordChars.length; ++i) { 135 | const c = wordChars.charAt(i); 136 | const v = ALPHABET_MAP[c]; 137 | if (v === undefined) 138 | return 'Unknown character ' + c; 139 | chk = polymodStep(chk) ^ v; 140 | // not in the checksum? 141 | if (i + 6 >= wordChars.length) 142 | continue; 143 | words.push(v); 144 | } 145 | if (chk !== ENCODING_CONST) 146 | return 'Invalid checksum for ' + str; 147 | return { prefix, words }; 148 | } 149 | function decodeUnsafe(str, LIMIT) { 150 | const res = __decode(str, LIMIT); 151 | if (typeof res === 'object') 152 | return res; 153 | } 154 | function decode(str, LIMIT) { 155 | const res = __decode(str, LIMIT); 156 | if (typeof res === 'object') 157 | return res; 158 | throw new Error(res); 159 | } 160 | return { 161 | decodeUnsafe, 162 | decode, 163 | encode, 164 | toWords, 165 | fromWordsUnsafe, 166 | fromWords, 167 | }; 168 | } 169 | exports.bech32 = getLibraryFromEncoding('bech32'); 170 | exports.bech32m = getLibraryFromEncoding('bech32m'); 171 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; 3 | 4 | const ALPHABET_MAP: { [key: string]: number } = {}; 5 | for (let z = 0; z < ALPHABET.length; z++) { 6 | const x = ALPHABET.charAt(z); 7 | ALPHABET_MAP[x] = z; 8 | } 9 | 10 | function polymodStep(pre: number): number { 11 | const b = pre >> 25; 12 | return ( 13 | ((pre & 0x1ffffff) << 5) ^ 14 | (-((b >> 0) & 1) & 0x3b6a57b2) ^ 15 | (-((b >> 1) & 1) & 0x26508e6d) ^ 16 | (-((b >> 2) & 1) & 0x1ea119fa) ^ 17 | (-((b >> 3) & 1) & 0x3d4233dd) ^ 18 | (-((b >> 4) & 1) & 0x2a1462b3) 19 | ); 20 | } 21 | 22 | function prefixChk(prefix: string): number | string { 23 | let chk = 1; 24 | for (let i = 0; i < prefix.length; ++i) { 25 | const c = prefix.charCodeAt(i); 26 | if (c < 33 || c > 126) return 'Invalid prefix (' + prefix + ')'; 27 | 28 | chk = polymodStep(chk) ^ (c >> 5); 29 | } 30 | chk = polymodStep(chk); 31 | 32 | for (let i = 0; i < prefix.length; ++i) { 33 | const v = prefix.charCodeAt(i); 34 | chk = polymodStep(chk) ^ (v & 0x1f); 35 | } 36 | return chk; 37 | } 38 | 39 | function convert(data: ArrayLike, inBits: number, outBits: number, pad: true): number[]; 40 | function convert( 41 | data: ArrayLike, 42 | inBits: number, 43 | outBits: number, 44 | pad: false, 45 | ): number[] | string; 46 | function convert( 47 | data: ArrayLike, 48 | inBits: number, 49 | outBits: number, 50 | pad: boolean, 51 | ): number[] | string { 52 | let value = 0; 53 | let bits = 0; 54 | const maxV = (1 << outBits) - 1; 55 | 56 | const result: number[] = []; 57 | for (let i = 0; i < data.length; ++i) { 58 | value = (value << inBits) | data[i]; 59 | bits += inBits; 60 | 61 | while (bits >= outBits) { 62 | bits -= outBits; 63 | result.push((value >> bits) & maxV); 64 | } 65 | } 66 | 67 | if (pad) { 68 | if (bits > 0) { 69 | result.push((value << (outBits - bits)) & maxV); 70 | } 71 | } else { 72 | if (bits >= inBits) return 'Excess padding'; 73 | if ((value << (outBits - bits)) & maxV) return 'Non-zero padding'; 74 | } 75 | 76 | return result; 77 | } 78 | 79 | function toWords(bytes: ArrayLike): number[] { 80 | return convert(bytes, 8, 5, true); 81 | } 82 | 83 | function fromWordsUnsafe(words: ArrayLike): number[] | undefined { 84 | const res = convert(words, 5, 8, false); 85 | if (Array.isArray(res)) return res; 86 | } 87 | 88 | function fromWords(words: ArrayLike): number[] { 89 | const res = convert(words, 5, 8, false); 90 | if (Array.isArray(res)) return res; 91 | 92 | throw new Error(res); 93 | } 94 | 95 | function getLibraryFromEncoding(encoding: 'bech32' | 'bech32m'): BechLib { 96 | let ENCODING_CONST: number; 97 | if (encoding === 'bech32') { 98 | ENCODING_CONST = 1; 99 | } else { 100 | ENCODING_CONST = 0x2bc830a3; 101 | } 102 | 103 | function encode(prefix: string, words: ArrayLike, LIMIT?: number): string { 104 | LIMIT = LIMIT || 90; 105 | if (prefix.length + 7 + words.length > LIMIT) throw new TypeError('Exceeds length limit'); 106 | 107 | prefix = prefix.toLowerCase(); 108 | 109 | // determine chk mod 110 | let chk = prefixChk(prefix); 111 | if (typeof chk === 'string') throw new Error(chk); 112 | 113 | let result = prefix + '1'; 114 | for (let i = 0; i < words.length; ++i) { 115 | const x = words[i]; 116 | if (x >> 5 !== 0) throw new Error('Non 5-bit word'); 117 | 118 | chk = polymodStep(chk) ^ x; 119 | result += ALPHABET.charAt(x); 120 | } 121 | 122 | for (let i = 0; i < 6; ++i) { 123 | chk = polymodStep(chk); 124 | } 125 | chk ^= ENCODING_CONST; 126 | 127 | for (let i = 0; i < 6; ++i) { 128 | const v = (chk >> ((5 - i) * 5)) & 0x1f; 129 | result += ALPHABET.charAt(v); 130 | } 131 | 132 | return result; 133 | } 134 | 135 | function __decode(str: string, LIMIT?: number): Decoded | string { 136 | LIMIT = LIMIT || 90; 137 | if (str.length < 8) return str + ' too short'; 138 | if (str.length > LIMIT) return 'Exceeds length limit'; 139 | 140 | // don't allow mixed case 141 | const lowered = str.toLowerCase(); 142 | const uppered = str.toUpperCase(); 143 | if (str !== lowered && str !== uppered) return 'Mixed-case string ' + str; 144 | str = lowered; 145 | 146 | const split = str.lastIndexOf('1'); 147 | if (split === -1) return 'No separator character for ' + str; 148 | if (split === 0) return 'Missing prefix for ' + str; 149 | 150 | const prefix = str.slice(0, split); 151 | const wordChars = str.slice(split + 1); 152 | if (wordChars.length < 6) return 'Data too short'; 153 | 154 | let chk = prefixChk(prefix); 155 | if (typeof chk === 'string') return chk; 156 | 157 | const words = []; 158 | for (let i = 0; i < wordChars.length; ++i) { 159 | const c = wordChars.charAt(i); 160 | const v = ALPHABET_MAP[c]; 161 | if (v === undefined) return 'Unknown character ' + c; 162 | chk = polymodStep(chk) ^ v; 163 | 164 | // not in the checksum? 165 | if (i + 6 >= wordChars.length) continue; 166 | words.push(v); 167 | } 168 | 169 | if (chk !== ENCODING_CONST) return 'Invalid checksum for ' + str; 170 | return { prefix, words }; 171 | } 172 | 173 | function decodeUnsafe(str: string, LIMIT?: number): Decoded | undefined { 174 | const res = __decode(str, LIMIT); 175 | if (typeof res === 'object') return res; 176 | } 177 | 178 | function decode(str: string, LIMIT?: number): Decoded { 179 | const res = __decode(str, LIMIT); 180 | if (typeof res === 'object') return res; 181 | 182 | throw new Error(res); 183 | } 184 | 185 | return { 186 | decodeUnsafe, 187 | decode, 188 | encode, 189 | toWords, 190 | fromWordsUnsafe, 191 | fromWords, 192 | }; 193 | } 194 | 195 | export const bech32 = getLibraryFromEncoding('bech32'); 196 | export const bech32m = getLibraryFromEncoding('bech32m'); 197 | export interface Decoded { 198 | prefix: string; 199 | words: number[]; 200 | } 201 | export interface BechLib { 202 | decodeUnsafe: (str: string, LIMIT?: number | undefined) => Decoded | undefined; 203 | decode: (str: string, LIMIT?: number | undefined) => Decoded; 204 | encode: (prefix: string, words: ArrayLike, LIMIT?: number | undefined) => string; 205 | toWords: typeof toWords; 206 | fromWordsUnsafe: typeof fromWordsUnsafe; 207 | fromWords: typeof fromWords; 208 | } 209 | -------------------------------------------------------------------------------- /src/test/fixtures.json: -------------------------------------------------------------------------------- 1 | { 2 | "fromWords": { 3 | "invalid": [ 4 | { 5 | "exception": "Excess padding", 6 | "words": [14,20,15,7,13,26,0,25,18,6,11,13,8,21,4,20,3,17,2,29,3,0] 7 | }, 8 | { 9 | "exception": "Non-zero padding", 10 | "words": [3,1,17,17,8,15,0,20,24,20,11,6,16,1,5,29,3,4,16,3,6,21,22,26,2,13,22,9,16,21,19,24,25,21,6,18,15,8,13,24,24,24,25,9,12,1,4,16,6,9,17,1] 11 | } 12 | ] 13 | }, 14 | "bech32": { 15 | "valid": [ 16 | { 17 | "string": "A12UEL5L", 18 | "prefix": "A", 19 | "hex": "", 20 | "words": [] 21 | }, 22 | { 23 | "string": "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", 24 | "prefix": "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio", 25 | "hex": "", 26 | "words": [] 27 | }, 28 | { 29 | "string": "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", 30 | "prefix": "abcdef", 31 | "hex": "00443214c74254b635cf84653a56d7c675be77df", 32 | "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] 33 | }, 34 | { 35 | "string": "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", 36 | "prefix": "1", 37 | "hex": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 38 | "words": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] 39 | }, 40 | { 41 | "string": "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", 42 | "prefix": "split", 43 | "hex": "c5f38b70305f519bf66d85fb6cf03058f3dde463ecd7918f2dc743918f2d", 44 | "words": [24,23,25,24,22,28,1,16,11,29,8,25,23,29,19,13,16,23,29,22,25,28,1,16,11,3,25,29,27,25,3,3,29,19,11,25,3,3,25,13,24,29,1,25,3,3,25,13] 45 | }, 46 | { 47 | "string": "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq978ear", 48 | "prefix": "1", 49 | "hex": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 50 | "words": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 51 | "limit": 300 52 | } 53 | ], 54 | "invalid": [ 55 | { 56 | "string": "A12Uel5l", 57 | "exception": "Mixed-case string A12Uel5l" 58 | }, 59 | { 60 | "string": " 1nwldj5", 61 | "exception": "Invalid prefix" 62 | }, 63 | { 64 | "string": "abc1rzg", 65 | "exception": "abc1rzg too short" 66 | }, 67 | { 68 | "string": "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", 69 | "exception": "Exceeds length limit" 70 | }, 71 | { 72 | "string": "x1b4n0q5v", 73 | "exception": "Unknown character b" 74 | }, 75 | { 76 | "string": "1pzry9x0s0muk", 77 | "exception": "Missing prefix" 78 | }, 79 | { 80 | "string": "pzry9x0s0muk", 81 | "exception": "No separator character" 82 | }, 83 | { 84 | "string": "abc1rzgt4", 85 | "exception": "Data too short" 86 | }, 87 | { 88 | "string": "s1vcsyn", 89 | "exception": "s1vcsyn too short" 90 | }, 91 | { 92 | "prefix": "abc", 93 | "words": [128], 94 | "exception": "Non 5-bit word" 95 | }, 96 | { 97 | "prefix": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzfoobarfoobar", 98 | "words": [128], 99 | "exception": "Exceeds length limit" 100 | }, 101 | { 102 | "prefix": "foobar", 103 | "words": [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20], 104 | "exception": "Exceeds length limit" 105 | }, 106 | { 107 | "prefix": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzfoobarfoobarfoobarfoobar", 108 | "words": [128], 109 | "limit": 104, 110 | "exception": "Exceeds length limit" 111 | }, 112 | { 113 | "string": "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", 114 | "exception": "Exceeds length limit" 115 | }, 116 | { 117 | "prefix": "abc\u00ff", 118 | "words": [18], 119 | "exception": "Invalid prefix" 120 | }, 121 | { 122 | "string": "li1dgmt3", 123 | "exception": "Data too short" 124 | }, 125 | { 126 | "stringHex": "6465316c67377774ff", 127 | "exception": "Unknown character" 128 | } 129 | ] 130 | }, 131 | "bech32m": { 132 | "valid": [ 133 | { 134 | "string": "A1LQFN3A", 135 | "prefix": "A", 136 | "hex": "", 137 | "words": [] 138 | }, 139 | { 140 | "string": "a1lqfn3a", 141 | "prefix": "a", 142 | "hex": "", 143 | "words": [] 144 | }, 145 | { 146 | "string": "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", 147 | "prefix": "an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber1", 148 | "hex": "", 149 | "words": [] 150 | }, 151 | { 152 | "string": "abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", 153 | "prefix": "abcdef", 154 | "hex": "ffbbcdeb38bdab49ca307b9ac5a928398a418820", 155 | "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] 156 | }, 157 | { 158 | "string": "11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", 159 | "prefix": "1", 160 | "words": [31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31] 161 | }, 162 | { 163 | "string": "split1checkupstagehandshakeupstreamerranterredcaperredlc445v", 164 | "prefix": "split", 165 | "hex": "c5f38b70305f519bf66d85fb6cf03058f3dde463ecd7918f2dc743918f2d", 166 | "words": [24,23,25,24,22,28,1,16,11,29,8,25,23,29,19,13,16,23,29,22,25,28,1,16,11,3,25,29,27,25,3,3,29,19,11,25,3,3,25,13,24,29,1,25,3,3,25,13] 167 | }, 168 | { 169 | "string": "?1v759aa", 170 | "prefix": "?", 171 | "hex": "", 172 | "words": [] 173 | }, 174 | { 175 | "string": "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqszh4cp", 176 | "prefix": "1", 177 | "hex": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 178 | "words": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], 179 | "limit": 300 180 | } 181 | ], 182 | "invalid": [ 183 | { 184 | "string": "A1LQfN3A", 185 | "exception": "Mixed-case string A1LQfN3A" 186 | }, 187 | { 188 | "string": " 1xj0phk", 189 | "exception": "Invalid prefix \\( \\)" 190 | }, 191 | { 192 | "string": "abc1rzg", 193 | "exception": "abc1rzg too short" 194 | }, 195 | { 196 | "string": "an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", 197 | "exception": "Exceeds length limit" 198 | }, 199 | { 200 | "string": "qyrz8wqd2c9m", 201 | "exception": "No separator character for qyrz8wqd2c9m" 202 | }, 203 | { 204 | "string": "1qyrz8wqd2c9m", 205 | "exception": "Missing prefix for 1qyrz8wqd2c9m" 206 | }, 207 | { 208 | "string": "y1b0jsk6g", 209 | "exception": "Unknown character b" 210 | }, 211 | { 212 | "string": "lt1igcx5c0", 213 | "exception": "Unknown character i" 214 | }, 215 | { 216 | "string": "in1muywd", 217 | "exception": "Data too short" 218 | }, 219 | { 220 | "string": "mm1crxm3i", 221 | "exception": "Unknown character i" 222 | }, 223 | { 224 | "string": "au1s5cgom", 225 | "exception": "Unknown character o" 226 | }, 227 | { 228 | "string": "M1VUXWEZ", 229 | "exception": "Invalid checksum for m1vuxwez" 230 | }, 231 | { 232 | "string": "16plkw9", 233 | "exception": "16plkw9 too short" 234 | }, 235 | { 236 | "string": "1p2gdwpf", 237 | "exception": "Missing prefix for 1p2gdwpf" 238 | }, 239 | { 240 | "prefix": "abc", 241 | "words": [128], 242 | "exception": "Non 5-bit word" 243 | }, 244 | { 245 | "prefix": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzfoobarfoobar", 246 | "words": [128], 247 | "exception": "Exceeds length limit" 248 | }, 249 | { 250 | "prefix": "foobar", 251 | "words": [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20], 252 | "exception": "Exceeds length limit" 253 | }, 254 | { 255 | "prefix": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzfoobarfoobarfoobarfoobar", 256 | "words": [128], 257 | "limit": 104, 258 | "exception": "Exceeds length limit" 259 | }, 260 | { 261 | "string": "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", 262 | "exception": "Exceeds length limit" 263 | }, 264 | { 265 | "prefix": "abc\u00ff", 266 | "words": [18], 267 | "exception": "Invalid prefix \\(abc\u00ff\\)" 268 | }, 269 | { 270 | "string": "in1muywd", 271 | "exception": "Data too short" 272 | }, 273 | { 274 | "stringHex": "6465316c67377774ff", 275 | "exception": "Unknown character " 276 | } 277 | ] 278 | } 279 | } 280 | --------------------------------------------------------------------------------