bip-321 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +31 -0
- package/README.md +11 -8
- package/bun.lock +25 -2
- package/index.ts +38 -19
- package/oxlintrc.json +23 -0
- package/package.json +10 -7
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["**"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["**"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Setup Bun
|
|
17
|
+
uses: oven-sh/setup-bun@v1
|
|
18
|
+
with:
|
|
19
|
+
bun-version: latest
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: bun install
|
|
23
|
+
|
|
24
|
+
- name: Run lint
|
|
25
|
+
run: bun run lint
|
|
26
|
+
|
|
27
|
+
- name: Run type check
|
|
28
|
+
run: bun run check
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
run: bun test
|
package/README.md
CHANGED
|
@@ -13,11 +13,6 @@ A TypeScript/JavaScript library for parsing BIP-321 Bitcoin URI scheme. This lib
|
|
|
13
13
|
- ✅ **TypeScript support** - Fully typed with TypeScript definitions
|
|
14
14
|
- ✅ **Proof of payment** - Supports pop/req-pop parameters for payment callbacks
|
|
15
15
|
- ✅ **Comprehensive error handling** - Clear error messages for invalid URIs
|
|
16
|
-
- ✅ **Full type safety** - Passes strict TypeScript type checking with no errors
|
|
17
|
-
|
|
18
|
-
## TypeScript Type Safety
|
|
19
|
-
|
|
20
|
-
This library is built with full TypeScript support and passes strict type checking (`tsc --noEmit`) with zero errors. All functions return fully-typed objects, providing excellent IDE autocomplete and compile-time safety.
|
|
21
16
|
|
|
22
17
|
```typescript
|
|
23
18
|
import { parseBIP321, type BIP321ParseResult, type PaymentMethod } from "bip-321";
|
|
@@ -40,8 +35,6 @@ result.paymentMethods.forEach((method: PaymentMethod) => {
|
|
|
40
35
|
```
|
|
41
36
|
|
|
42
37
|
## Installation
|
|
43
|
-
</text>
|
|
44
|
-
|
|
45
38
|
|
|
46
39
|
```bash
|
|
47
40
|
bun add bip-321
|
|
@@ -53,6 +46,16 @@ Or with npm:
|
|
|
53
46
|
npm install bip-321
|
|
54
47
|
```
|
|
55
48
|
|
|
49
|
+
### Note on Dependencies
|
|
50
|
+
|
|
51
|
+
This library uses modern, browser-native dependencies:
|
|
52
|
+
- **`@scure/base`** - Pure JavaScript base58, bech32, and bech32m encoding (no browserify needed)
|
|
53
|
+
- **`@noble/hashes`** - Pure JavaScript cryptographic hashing
|
|
54
|
+
- **`bitcoinjs-lib`** - Bitcoin address validation
|
|
55
|
+
- **`light-bolt11-decoder`** - Lightning invoice parsing
|
|
56
|
+
|
|
57
|
+
All dependencies work natively in Node.js, browsers, and React Native without any build tools or polyfills required.
|
|
58
|
+
|
|
56
59
|
## Quick Start
|
|
57
60
|
|
|
58
61
|
```typescript
|
|
@@ -333,4 +336,4 @@ BSD-2-Clause (same as BIP-321)
|
|
|
333
336
|
## Related
|
|
334
337
|
|
|
335
338
|
- [BIP-321 Specification](https://bips.dev/321/)
|
|
336
|
-
- [BIP-21 (Original)](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki)
|
|
339
|
+
- [BIP-21 (Original)](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki)
|
package/bun.lock
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
"": {
|
|
5
5
|
"name": "bip-321",
|
|
6
6
|
"dependencies": {
|
|
7
|
+
"@noble/hashes": "^2.0.1",
|
|
7
8
|
"@scure/base": "^2.0.0",
|
|
8
9
|
"bitcoinjs-lib": "^7.0.0",
|
|
9
|
-
"bs58check": "^4.0.0",
|
|
10
10
|
"light-bolt11-decoder": "^3.2.0",
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@types/bun": "latest",
|
|
14
|
+
"oxlint": "^1.26.0",
|
|
14
15
|
},
|
|
15
16
|
"peerDependencies": {
|
|
16
17
|
"typescript": "^5",
|
|
@@ -18,7 +19,23 @@
|
|
|
18
19
|
},
|
|
19
20
|
},
|
|
20
21
|
"packages": {
|
|
21
|
-
"@noble/hashes": ["@noble/hashes@
|
|
22
|
+
"@noble/hashes": ["@noble/hashes@2.0.1", "", {}, "sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw=="],
|
|
23
|
+
|
|
24
|
+
"@oxlint/darwin-arm64": ["@oxlint/darwin-arm64@1.26.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kTmm1opqyn7iZopWHO3Ml4D/44pA5eknZBepgxCnTaPrW8XgCEUI85Q5AvOOvoNve8NziTYb8ax+CyuGJIgn/Q=="],
|
|
25
|
+
|
|
26
|
+
"@oxlint/darwin-x64": ["@oxlint/darwin-x64@1.26.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-/hMfZ9j7ZzVPRmMm02PHNc6MIMk0QYv5VowZJRIp40YLqLPvFfGNGZBj8e1fDVgZMFEGWDQK3yrt1uBKxXAK4Q=="],
|
|
27
|
+
|
|
28
|
+
"@oxlint/linux-arm64-gnu": ["@oxlint/linux-arm64-gnu@1.26.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-iv4wdrwdCa8bhJxOpKlvfxqTs0LgW5tKBUMvH9B13zREHm1xT9JRZ8cQbbKiyC6LNdggwu5S6TSvODgAu7/DlA=="],
|
|
29
|
+
|
|
30
|
+
"@oxlint/linux-arm64-musl": ["@oxlint/linux-arm64-musl@1.26.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-a3gTbnN1JzedxqYeGTkg38BAs/r3Krd2DPNs/MF7nnHthT3RzkPUk47isMePLuNc4e/Weljn7m2m/Onx22tiNg=="],
|
|
31
|
+
|
|
32
|
+
"@oxlint/linux-x64-gnu": ["@oxlint/linux-x64-gnu@1.26.0", "", { "os": "linux", "cpu": "x64" }, "sha512-cCAyqyuKpFImjlgiBuuwSF+aDBW2h19/aCmHMTMSp6KXwhoQK7/Xx7/EhZKP5wiQJzVUYq5fXr0D8WmpLGsjRg=="],
|
|
33
|
+
|
|
34
|
+
"@oxlint/linux-x64-musl": ["@oxlint/linux-x64-musl@1.26.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8VOJ4vQo0G1tNdaghxrWKjKZGg73tv+FoMDrtNYuUesqBHZN68FkYCsgPwEsacLhCmtoZrkF3ePDWDuWEpDyAg=="],
|
|
35
|
+
|
|
36
|
+
"@oxlint/win32-arm64": ["@oxlint/win32-arm64@1.26.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-N8KUtzP6gfEHKvaIBZCS9g8wRfqV5v55a/B8iJjIEhtMehcEM+UX+aYRsQ4dy5oBCrK3FEp4Yy/jHgb0moLm3Q=="],
|
|
37
|
+
|
|
38
|
+
"@oxlint/win32-x64": ["@oxlint/win32-x64@1.26.0", "", { "os": "win32", "cpu": "x64" }, "sha512-7tCyG0laduNQ45vzB9blVEGq/6DOvh7AFmiUAana8mTp0zIKQQmwJ21RqhazH0Rk7O6lL7JYzKcu+zaJHGpRLA=="],
|
|
22
39
|
|
|
23
40
|
"@scure/base": ["@scure/base@2.0.0", "", {}, "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w=="],
|
|
24
41
|
|
|
@@ -46,6 +63,8 @@
|
|
|
46
63
|
|
|
47
64
|
"light-bolt11-decoder": ["light-bolt11-decoder@3.2.0", "", { "dependencies": { "@scure/base": "1.1.1" } }, "sha512-3QEofgiBOP4Ehs9BI+RkZdXZNtSys0nsJ6fyGeSiAGCBsMwHGUDS/JQlY/sTnWs91A2Nh0S9XXfA8Sy9g6QpuQ=="],
|
|
48
65
|
|
|
66
|
+
"oxlint": ["oxlint@1.26.0", "", { "optionalDependencies": { "@oxlint/darwin-arm64": "1.26.0", "@oxlint/darwin-x64": "1.26.0", "@oxlint/linux-arm64-gnu": "1.26.0", "@oxlint/linux-arm64-musl": "1.26.0", "@oxlint/linux-x64-gnu": "1.26.0", "@oxlint/linux-x64-musl": "1.26.0", "@oxlint/win32-arm64": "1.26.0", "@oxlint/win32-x64": "1.26.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.4.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint", "oxc_language_server": "bin/oxc_language_server" } }, "sha512-KRpL+SMi07JQyggv5ldIF+wt2pnrKm8NLW0B+8bK+0HZsLmH9/qGA+qMWie5Vf7lnlMBllJmsuzHaKFEGY3rIA=="],
|
|
67
|
+
|
|
49
68
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
50
69
|
|
|
51
70
|
"uint8array-tools": ["uint8array-tools@0.0.9", "", {}, "sha512-9vqDWmoSXOoi+K14zNaf6LBV51Q8MayF0/IiQs3GlygIKUYtog603e6virExkjjFosfJUBI4LhbQK1iq8IG11A=="],
|
|
@@ -56,6 +75,10 @@
|
|
|
56
75
|
|
|
57
76
|
"varuint-bitcoin": ["varuint-bitcoin@2.0.0", "", { "dependencies": { "uint8array-tools": "^0.0.8" } }, "sha512-6QZbU/rHO2ZQYpWFDALCDSRsXbAs1VOEmXAxtbtjLtKuMJ/FQ8YbhfxlaiKv5nklci0M6lZtlZyxo9Q+qNnyog=="],
|
|
58
77
|
|
|
78
|
+
"bitcoinjs-lib/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
|
|
79
|
+
|
|
80
|
+
"bs58check/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
|
|
81
|
+
|
|
59
82
|
"light-bolt11-decoder/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
|
|
60
83
|
|
|
61
84
|
"varuint-bitcoin/uint8array-tools": ["uint8array-tools@0.0.8", "", {}, "sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g=="],
|
package/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as bitcoin from "bitcoinjs-lib";
|
|
2
2
|
import { decode as decodeLightning } from "light-bolt11-decoder";
|
|
3
|
-
import
|
|
4
|
-
import { bech32, bech32m } from "@scure/base";
|
|
3
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
4
|
+
import { base58, bech32, bech32m } from "@scure/base";
|
|
5
5
|
|
|
6
6
|
export interface PaymentMethod {
|
|
7
7
|
type:
|
|
@@ -46,7 +46,7 @@ function detectAddressNetwork(
|
|
|
46
46
|
// Try using bitcoinjs-lib first (works for non-taproot)
|
|
47
47
|
bitcoin.address.toOutputScript(address, bitcoin.networks.bitcoin);
|
|
48
48
|
return "mainnet";
|
|
49
|
-
} catch
|
|
49
|
+
} catch {
|
|
50
50
|
// Fallback to manual bech32/bech32m validation for taproot
|
|
51
51
|
try {
|
|
52
52
|
const decoded = lowerAddress.startsWith("bc1p")
|
|
@@ -63,7 +63,7 @@ function detectAddressNetwork(
|
|
|
63
63
|
try {
|
|
64
64
|
bitcoin.address.toOutputScript(address, bitcoin.networks.testnet);
|
|
65
65
|
return "testnet";
|
|
66
|
-
} catch
|
|
66
|
+
} catch {
|
|
67
67
|
try {
|
|
68
68
|
const decoded = lowerAddress.startsWith("tb1p")
|
|
69
69
|
? bech32m.decode(address as `${string}1${string}`, 90)
|
|
@@ -79,7 +79,7 @@ function detectAddressNetwork(
|
|
|
79
79
|
try {
|
|
80
80
|
bitcoin.address.toOutputScript(address, bitcoin.networks.regtest);
|
|
81
81
|
return "regtest";
|
|
82
|
-
} catch
|
|
82
|
+
} catch {
|
|
83
83
|
try {
|
|
84
84
|
const decoded = lowerAddress.startsWith("bcrt1p")
|
|
85
85
|
? bech32m.decode(address as `${string}1${string}`, 90)
|
|
@@ -93,19 +93,38 @@ function detectAddressNetwork(
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
// Base58 addresses (P2PKH, P2SH)
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
// Base58 addresses (P2PKH, P2SH) - manual validation with checksum
|
|
97
|
+
try {
|
|
98
|
+
const decoded = base58.decode(address);
|
|
99
|
+
if (decoded.length < 25) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
99
102
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
// Verify checksum
|
|
104
|
+
const payload = decoded.slice(0, -4);
|
|
105
|
+
const checksum = decoded.slice(-4);
|
|
106
|
+
const hash = sha256(sha256(payload));
|
|
107
|
+
|
|
108
|
+
for (let i = 0; i < 4; i++) {
|
|
109
|
+
if (checksum[i] !== hash[i]) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const version = payload[0];
|
|
115
|
+
|
|
116
|
+
// Mainnet: P2PKH (0x00), P2SH (0x05)
|
|
117
|
+
if (version === 0x00 || version === 0x05) {
|
|
118
|
+
return "mainnet";
|
|
119
|
+
}
|
|
120
|
+
// Testnet: P2PKH (0x6f), P2SH (0xc4)
|
|
121
|
+
else if (version === 0x6f || version === 0xc4) {
|
|
122
|
+
return "testnet";
|
|
123
|
+
}
|
|
124
|
+
} catch (e) {
|
|
125
|
+
return undefined;
|
|
107
126
|
}
|
|
108
|
-
} catch
|
|
127
|
+
} catch {
|
|
109
128
|
return undefined;
|
|
110
129
|
}
|
|
111
130
|
return undefined;
|
|
@@ -134,7 +153,7 @@ function validateLightningInvoice(invoice: string): {
|
|
|
134
153
|
error?: string;
|
|
135
154
|
} {
|
|
136
155
|
try {
|
|
137
|
-
const
|
|
156
|
+
const _decoded = decodeLightning(invoice);
|
|
138
157
|
let network: "mainnet" | "testnet" | "regtest" | "signet" | undefined;
|
|
139
158
|
|
|
140
159
|
const lowerInvoice = invoice.toLowerCase();
|
|
@@ -174,7 +193,7 @@ function validatePopUri(popUri: string): { valid: boolean; error?: string } {
|
|
|
174
193
|
}
|
|
175
194
|
|
|
176
195
|
return { valid: true };
|
|
177
|
-
} catch
|
|
196
|
+
} catch {
|
|
178
197
|
return { valid: false, error: "Invalid pop URI encoding" };
|
|
179
198
|
}
|
|
180
199
|
}
|
|
@@ -284,7 +303,7 @@ export function parseBIP321(uri: string): BIP321ParseResult {
|
|
|
284
303
|
}
|
|
285
304
|
}
|
|
286
305
|
} else if (lowerKey === "pop" || lowerKey === "req-pop") {
|
|
287
|
-
const
|
|
306
|
+
const _popKey = lowerKey === "req-pop" ? "req-pop" : "pop";
|
|
288
307
|
if (result.pop !== undefined || result.popRequired !== undefined) {
|
|
289
308
|
result.errors.push("Multiple pop/req-pop parameters not allowed");
|
|
290
309
|
result.valid = false;
|
package/oxlintrc.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"categories": {
|
|
3
|
+
"correctness": "warn",
|
|
4
|
+
"suspicious": "warn",
|
|
5
|
+
"pedantic": "warn",
|
|
6
|
+
"perf": "warn",
|
|
7
|
+
"style": "warn",
|
|
8
|
+
"restriction": "warn"
|
|
9
|
+
},
|
|
10
|
+
"rules": {
|
|
11
|
+
"typescript/no-explicit-any": "error",
|
|
12
|
+
"typescript/no-non-null-assertion": "error",
|
|
13
|
+
"no-console": "warn",
|
|
14
|
+
"no-debugger": "error",
|
|
15
|
+
"eqeqeq": "error"
|
|
16
|
+
},
|
|
17
|
+
"ignorePatterns": [
|
|
18
|
+
"**/node_modules/**",
|
|
19
|
+
"**/dist/**",
|
|
20
|
+
"**/build/**",
|
|
21
|
+
"**/*.min.js"
|
|
22
|
+
]
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bip-321",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "A TypeScript/JavaScript library for parsing BIP-321 Bitcoin URI scheme with support for multiple payment methods",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"test": "bun test",
|
|
17
|
-
"example": "bun example.ts"
|
|
17
|
+
"example": "bun example.ts",
|
|
18
|
+
"check": "tsc --noEmit",
|
|
19
|
+
"lint": "oxlint"
|
|
18
20
|
},
|
|
19
21
|
"keywords": [
|
|
20
22
|
"bitcoin",
|
|
@@ -40,25 +42,26 @@
|
|
|
40
42
|
"author": "",
|
|
41
43
|
"license": "BSD-2-Clause",
|
|
42
44
|
"dependencies": {
|
|
45
|
+
"@noble/hashes": "^2.0.1",
|
|
43
46
|
"@scure/base": "^2.0.0",
|
|
44
47
|
"bitcoinjs-lib": "^7.0.0",
|
|
45
|
-
"bs58check": "^4.0.0",
|
|
46
48
|
"light-bolt11-decoder": "^3.2.0"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
|
-
"@types/bun": "latest"
|
|
51
|
+
"@types/bun": "latest",
|
|
52
|
+
"oxlint": "^1.26.0"
|
|
50
53
|
},
|
|
51
54
|
"peerDependencies": {
|
|
52
55
|
"typescript": "^5"
|
|
53
56
|
},
|
|
54
57
|
"repository": {
|
|
55
58
|
"type": "git",
|
|
56
|
-
"url": "https://github.com/
|
|
59
|
+
"url": "https://github.com/niteshbalusu11/bip-321.git"
|
|
57
60
|
},
|
|
58
61
|
"bugs": {
|
|
59
|
-
"url": "https://github.com/
|
|
62
|
+
"url": "https://github.com/niteshbalusu11/bip-321/issues"
|
|
60
63
|
},
|
|
61
|
-
"homepage": "https://github.com/
|
|
64
|
+
"homepage": "https://github.com/niteshbalusu11/bip-321#readme",
|
|
62
65
|
"engines": {
|
|
63
66
|
"node": ">=16.0.0"
|
|
64
67
|
}
|