bip-321 0.0.1
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/CLAUDE.md +107 -0
- package/README.md +336 -0
- package/bun.lock +63 -0
- package/example.ts +161 -0
- package/index.test.ts +361 -0
- package/index.ts +471 -0
- package/package.json +65 -0
- package/tsconfig.json +29 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
Default to using Bun instead of Node.js.
|
|
4
|
+
|
|
5
|
+
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
|
6
|
+
- Use `bun test` instead of `jest` or `vitest`
|
|
7
|
+
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
|
8
|
+
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
|
9
|
+
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
|
10
|
+
- Bun automatically loads .env, so don't use dotenv.
|
|
11
|
+
|
|
12
|
+
## APIs
|
|
13
|
+
|
|
14
|
+
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
|
15
|
+
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
|
16
|
+
- `Bun.redis` for Redis. Don't use `ioredis`.
|
|
17
|
+
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
|
18
|
+
- `WebSocket` is built-in. Don't use `ws`.
|
|
19
|
+
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
|
20
|
+
- Bun.$`ls` instead of execa.
|
|
21
|
+
|
|
22
|
+
## Testing
|
|
23
|
+
|
|
24
|
+
Use `bun test` to run tests.
|
|
25
|
+
|
|
26
|
+
```ts#index.test.ts
|
|
27
|
+
import { test, expect } from "bun:test";
|
|
28
|
+
|
|
29
|
+
test("hello world", () => {
|
|
30
|
+
expect(1).toBe(1);
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Frontend
|
|
35
|
+
|
|
36
|
+
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
|
37
|
+
|
|
38
|
+
Server:
|
|
39
|
+
|
|
40
|
+
```ts#index.ts
|
|
41
|
+
import index from "./index.html"
|
|
42
|
+
|
|
43
|
+
Bun.serve({
|
|
44
|
+
routes: {
|
|
45
|
+
"/": index,
|
|
46
|
+
"/api/users/:id": {
|
|
47
|
+
GET: (req) => {
|
|
48
|
+
return new Response(JSON.stringify({ id: req.params.id }));
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
// optional websocket support
|
|
53
|
+
websocket: {
|
|
54
|
+
open: (ws) => {
|
|
55
|
+
ws.send("Hello, world!");
|
|
56
|
+
},
|
|
57
|
+
message: (ws, message) => {
|
|
58
|
+
ws.send(message);
|
|
59
|
+
},
|
|
60
|
+
close: (ws) => {
|
|
61
|
+
// handle close
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
development: {
|
|
65
|
+
hmr: true,
|
|
66
|
+
console: true,
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
|
72
|
+
|
|
73
|
+
```html#index.html
|
|
74
|
+
<html>
|
|
75
|
+
<body>
|
|
76
|
+
<h1>Hello, world!</h1>
|
|
77
|
+
<script type="module" src="./frontend.tsx"></script>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
With the following `frontend.tsx`:
|
|
83
|
+
|
|
84
|
+
```tsx#frontend.tsx
|
|
85
|
+
import React from "react";
|
|
86
|
+
|
|
87
|
+
// import .css files directly and it works
|
|
88
|
+
import './index.css';
|
|
89
|
+
|
|
90
|
+
import { createRoot } from "react-dom/client";
|
|
91
|
+
|
|
92
|
+
const root = createRoot(document.body);
|
|
93
|
+
|
|
94
|
+
export default function Frontend() {
|
|
95
|
+
return <h1>Hello, world!</h1>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
root.render(<Frontend />);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Then, run index.ts
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
bun --hot ./index.ts
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
|
package/README.md
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# BIP-321 Parser
|
|
2
|
+
|
|
3
|
+
A TypeScript/JavaScript library for parsing BIP-321 Bitcoin URI scheme. This library validates and extracts payment information from Bitcoin URIs, supporting multiple payment methods including on-chain addresses, Lightning invoices, BOLT12 offers, and silent payments.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Complete BIP-321 compliance** - Implements the full BIP-321 specification
|
|
8
|
+
- ✅ **Multiple payment methods** - Supports on-chain, Lightning (BOLT11), BOLT12 offers, silent payments, and private payments
|
|
9
|
+
- ✅ **Network detection** - Automatically detects mainnet, testnet, regtest, and signet networks
|
|
10
|
+
- ✅ **Address validation** - Validates Bitcoin addresses (P2PKH, P2SH, Segwit v0, Taproot)
|
|
11
|
+
- ✅ **Lightning invoice validation** - Validates BOLT11 Lightning invoices
|
|
12
|
+
- ✅ **Cross-platform** - Works in Node.js, browsers, and React Native
|
|
13
|
+
- ✅ **TypeScript support** - Fully typed with TypeScript definitions
|
|
14
|
+
- ✅ **Proof of payment** - Supports pop/req-pop parameters for payment callbacks
|
|
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
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { parseBIP321, type BIP321ParseResult, type PaymentMethod } from "bip-321";
|
|
24
|
+
|
|
25
|
+
// Fully typed result with autocomplete support
|
|
26
|
+
const result: BIP321ParseResult = parseBIP321("bitcoin:...");
|
|
27
|
+
|
|
28
|
+
// TypeScript knows all available properties
|
|
29
|
+
result.valid; // boolean
|
|
30
|
+
result.network; // "mainnet" | "testnet" | "regtest" | "signet" | undefined
|
|
31
|
+
result.paymentMethods; // PaymentMethod[]
|
|
32
|
+
result.errors; // string[]
|
|
33
|
+
|
|
34
|
+
// Payment methods are also fully typed
|
|
35
|
+
result.paymentMethods.forEach((method: PaymentMethod) => {
|
|
36
|
+
method.type; // "onchain" | "lightning" | "lno" | "silent-payment" | "private-payment" | "other"
|
|
37
|
+
method.network; // "mainnet" | "testnet" | "regtest" | "signet" | undefined
|
|
38
|
+
method.valid; // boolean
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
</text>
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bun add bip-321
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or with npm:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install bip-321
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { parseBIP321 } from "bip-321";
|
|
60
|
+
|
|
61
|
+
// Parse a simple Bitcoin address
|
|
62
|
+
const result = parseBIP321("bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa");
|
|
63
|
+
|
|
64
|
+
console.log(result.valid); // true
|
|
65
|
+
console.log(result.network); // "mainnet"
|
|
66
|
+
console.log(result.address); // "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
|
|
67
|
+
console.log(result.paymentMethods); // Array of payment methods
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage Examples
|
|
71
|
+
|
|
72
|
+
### Basic On-Chain Payment
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { parseBIP321 } from "bip-321";
|
|
76
|
+
|
|
77
|
+
const result = parseBIP321("bitcoin:bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq");
|
|
78
|
+
|
|
79
|
+
if (result.valid) {
|
|
80
|
+
console.log(`Network: ${result.network}`); // mainnet
|
|
81
|
+
console.log(`Address: ${result.address}`);
|
|
82
|
+
console.log(`Payment methods: ${result.paymentMethods.length}`);
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Payment with Amount and Label
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const result = parseBIP321(
|
|
90
|
+
"bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?amount=0.5&label=Donation&message=Thank%20you"
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
console.log(`Amount: ${result.amount} BTC`); // 0.5 BTC
|
|
94
|
+
console.log(`Label: ${result.label}`); // "Donation"
|
|
95
|
+
console.log(`Message: ${result.message}`); // "Thank you"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Lightning Invoice with On-Chain Fallback
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const result = parseBIP321(
|
|
102
|
+
"bitcoin:bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq?lightning=lnbc15u1p3xnhl2pp5..."
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Returns 2 payment methods: onchain and lightning
|
|
106
|
+
result.paymentMethods.forEach((method) => {
|
|
107
|
+
console.log(`Type: ${method.type}, Network: ${method.network}, Valid: ${method.valid}`);
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Lightning-Only Payment
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const result = parseBIP321(
|
|
115
|
+
"bitcoin:?lightning=lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3s..."
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
console.log(result.paymentMethods[0].type); // "lightning"
|
|
119
|
+
console.log(result.paymentMethods[0].network); // "mainnet"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Multiple Payment Methods
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const result = parseBIP321(
|
|
126
|
+
"bitcoin:?lightning=lnbc...&lno=lno1bogusoffer&sp=sp1qsilentpayment"
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// Returns 3 payment methods: lightning, lno (BOLT12), and silent-payment
|
|
130
|
+
console.log(`Total payment methods: ${result.paymentMethods.length}`);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Network-Specific Parameters
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// Mainnet and testnet addresses in one URI
|
|
137
|
+
const result = parseBIP321(
|
|
138
|
+
"bitcoin:?bc=bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq&tb=tb1qghfhmd4zh7ncpmxl3qzhmq566jk8ckq4gafnmg"
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const byNetwork = getPaymentMethodsByNetwork(result);
|
|
142
|
+
console.log(`Mainnet methods: ${byNetwork.mainnet.length}`);
|
|
143
|
+
console.log(`Testnet methods: ${byNetwork.testnet.length}`);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Error Handling
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const result = parseBIP321("bitcoin:invalidaddress");
|
|
150
|
+
|
|
151
|
+
if (!result.valid) {
|
|
152
|
+
console.log("Errors:");
|
|
153
|
+
result.errors.forEach((error) => console.log(` - ${error}`));
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## API Reference
|
|
158
|
+
|
|
159
|
+
### `parseBIP321(uri: string): BIP321ParseResult`
|
|
160
|
+
|
|
161
|
+
Parses a BIP-321 URI and returns detailed information about the payment request.
|
|
162
|
+
|
|
163
|
+
**Parameters:**
|
|
164
|
+
- `uri` - The Bitcoin URI string to parse
|
|
165
|
+
|
|
166
|
+
**Returns:** `BIP321ParseResult` object containing:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
interface BIP321ParseResult {
|
|
170
|
+
// Basic information
|
|
171
|
+
address?: string; // Main Bitcoin address from URI path
|
|
172
|
+
network?: "mainnet" | "testnet" | "regtest" | "signet";
|
|
173
|
+
amount?: number; // Amount in BTC
|
|
174
|
+
label?: string; // Label for the recipient
|
|
175
|
+
message?: string; // Message describing the transaction
|
|
176
|
+
|
|
177
|
+
// Proof of payment
|
|
178
|
+
pop?: string; // Proof of payment callback URI
|
|
179
|
+
popRequired?: boolean; // Whether pop callback is required
|
|
180
|
+
|
|
181
|
+
// Payment methods
|
|
182
|
+
paymentMethods: PaymentMethod[]; // All available payment methods
|
|
183
|
+
|
|
184
|
+
// Parameters
|
|
185
|
+
requiredParams: string[]; // Unknown required parameters (req-*)
|
|
186
|
+
optionalParams: Record<string, string[]>; // Unknown optional parameters
|
|
187
|
+
|
|
188
|
+
// Validation
|
|
189
|
+
valid: boolean; // Whether the URI is valid
|
|
190
|
+
errors: string[]; // Array of error messages
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### `PaymentMethod` Interface
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
interface PaymentMethod {
|
|
198
|
+
type: "onchain" | "lightning" | "lno" | "silent-payment" | "private-payment" | "other";
|
|
199
|
+
value: string; // The actual address/invoice value
|
|
200
|
+
network?: "mainnet" | "testnet" | "regtest" | "signet";
|
|
201
|
+
valid: boolean; // Whether this payment method is valid
|
|
202
|
+
error?: string; // Error message if invalid
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Helper Functions
|
|
207
|
+
|
|
208
|
+
#### `getPaymentMethodsByNetwork(result: BIP321ParseResult)`
|
|
209
|
+
|
|
210
|
+
Groups payment methods by network.
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
const byNetwork = getPaymentMethodsByNetwork(result);
|
|
214
|
+
// Returns: { mainnet: [], testnet: [], regtest: [], signet: [], unknown: [] }
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### `getValidPaymentMethods(result: BIP321ParseResult)`
|
|
218
|
+
|
|
219
|
+
Returns only valid payment methods.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const valid = getValidPaymentMethods(result);
|
|
223
|
+
// Returns: PaymentMethod[]
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### `formatPaymentMethodsSummary(result: BIP321ParseResult)`
|
|
227
|
+
|
|
228
|
+
Generates a human-readable summary of the parsed URI.
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
const summary = formatPaymentMethodsSummary(result);
|
|
232
|
+
console.log(summary);
|
|
233
|
+
// Outputs:
|
|
234
|
+
// Valid: true
|
|
235
|
+
// Amount: 0.5 BTC
|
|
236
|
+
// Label: Donation
|
|
237
|
+
// Payment Methods: 2
|
|
238
|
+
// ✓ onchain (mainnet)
|
|
239
|
+
// ✓ lightning (mainnet)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Supported Payment Methods
|
|
243
|
+
|
|
244
|
+
| Method | Parameter Key | Description |
|
|
245
|
+
|--------|--------------|-------------|
|
|
246
|
+
| On-chain | `address` or `bc`/`tb`/`bcrt`/`tbs` | Bitcoin addresses (P2PKH, P2SH, Segwit, Taproot) |
|
|
247
|
+
| Lightning | `lightning` | BOLT11 Lightning invoices |
|
|
248
|
+
| BOLT12 | `lno` | Lightning BOLT12 offers |
|
|
249
|
+
| Silent Payments | `sp` | BIP352 Silent Payment addresses |
|
|
250
|
+
| Private Payments | `pay` | BIP351 Private Payment addresses |
|
|
251
|
+
|
|
252
|
+
## Network Detection
|
|
253
|
+
|
|
254
|
+
The library automatically detects the network from:
|
|
255
|
+
|
|
256
|
+
### Bitcoin Addresses
|
|
257
|
+
- **Mainnet**: `1...`, `3...`, `bc1...`
|
|
258
|
+
- **Testnet**: `m...`, `n...`, `2...`, `tb1...`
|
|
259
|
+
- **Regtest**: `bcrt1...`
|
|
260
|
+
|
|
261
|
+
### Lightning Invoices
|
|
262
|
+
- **Mainnet**: `lnbc...`
|
|
263
|
+
- **Testnet**: `lntb...`
|
|
264
|
+
- **Regtest**: `lnbcrt...`
|
|
265
|
+
- **Signet**: `lntbs...`
|
|
266
|
+
|
|
267
|
+
## Validation Rules
|
|
268
|
+
|
|
269
|
+
The parser enforces BIP-321 validation rules:
|
|
270
|
+
|
|
271
|
+
1. ✅ URI must start with `bitcoin:` (case-insensitive)
|
|
272
|
+
2. ✅ Address in URI path must be valid or empty
|
|
273
|
+
3. ✅ `amount` must be decimal BTC (no commas)
|
|
274
|
+
4. ✅ `label`, `message`, and `amount` cannot appear multiple times
|
|
275
|
+
5. ✅ `pop` and `req-pop` cannot both be present
|
|
276
|
+
6. ✅ Required parameters (`req-*`) must be understood or URI is invalid
|
|
277
|
+
7. ✅ Network-specific parameters (`bc`, `tb`, etc.) must match address network
|
|
278
|
+
8. ✅ `pop` URI scheme must not be forbidden (http, https, file, javascript, mailto)
|
|
279
|
+
|
|
280
|
+
## Browser Usage
|
|
281
|
+
|
|
282
|
+
```html
|
|
283
|
+
<!DOCTYPE html>
|
|
284
|
+
<html>
|
|
285
|
+
<head>
|
|
286
|
+
<script type="module">
|
|
287
|
+
import { parseBIP321 } from './index.js';
|
|
288
|
+
|
|
289
|
+
const uri = prompt("Enter Bitcoin URI:");
|
|
290
|
+
const result = parseBIP321(uri);
|
|
291
|
+
|
|
292
|
+
if (result.valid) {
|
|
293
|
+
alert(`Valid payment request!\nNetwork: ${result.network}\nMethods: ${result.paymentMethods.length}`);
|
|
294
|
+
} else {
|
|
295
|
+
alert(`Invalid URI:\n${result.errors.join('\n')}`);
|
|
296
|
+
}
|
|
297
|
+
</script>
|
|
298
|
+
</head>
|
|
299
|
+
<body>
|
|
300
|
+
<h1>BIP-321 Parser Demo</h1>
|
|
301
|
+
</body>
|
|
302
|
+
</html>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## React Native Usage
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
import { parseBIP321 } from "bip-321";
|
|
309
|
+
import { Alert } from "react-native";
|
|
310
|
+
|
|
311
|
+
function parseQRCode(data: string) {
|
|
312
|
+
const result = parseBIP321(data);
|
|
313
|
+
|
|
314
|
+
if (result.valid) {
|
|
315
|
+
Alert.alert(
|
|
316
|
+
"Payment Request",
|
|
317
|
+
`Network: ${result.network}\nAmount: ${result.amount || 'Not specified'} BTC`
|
|
318
|
+
);
|
|
319
|
+
} else {
|
|
320
|
+
Alert.alert("Invalid QR Code", result.errors.join("\n"));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Contributing
|
|
326
|
+
|
|
327
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
328
|
+
|
|
329
|
+
## License
|
|
330
|
+
|
|
331
|
+
BSD-2-Clause (same as BIP-321)
|
|
332
|
+
|
|
333
|
+
## Related
|
|
334
|
+
|
|
335
|
+
- [BIP-321 Specification](https://bips.dev/321/)
|
|
336
|
+
- [BIP-21 (Original)](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki)
|
package/bun.lock
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"workspaces": {
|
|
4
|
+
"": {
|
|
5
|
+
"name": "bip-321",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@scure/base": "^2.0.0",
|
|
8
|
+
"bitcoinjs-lib": "^7.0.0",
|
|
9
|
+
"bs58check": "^4.0.0",
|
|
10
|
+
"light-bolt11-decoder": "^3.2.0",
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"@types/bun": "latest",
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"typescript": "^5",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
"packages": {
|
|
21
|
+
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
|
|
22
|
+
|
|
23
|
+
"@scure/base": ["@scure/base@2.0.0", "", {}, "sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w=="],
|
|
24
|
+
|
|
25
|
+
"@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
|
|
26
|
+
|
|
27
|
+
"@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="],
|
|
28
|
+
|
|
29
|
+
"@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
|
|
30
|
+
|
|
31
|
+
"base-x": ["base-x@5.0.1", "", {}, "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="],
|
|
32
|
+
|
|
33
|
+
"bech32": ["bech32@2.0.0", "", {}, "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="],
|
|
34
|
+
|
|
35
|
+
"bip174": ["bip174@3.0.0", "", { "dependencies": { "uint8array-tools": "^0.0.9", "varuint-bitcoin": "^2.0.0" } }, "sha512-N3vz3rqikLEu0d6yQL8GTrSkpYb35NQKWMR7Hlza0lOj6ZOlvQ3Xr7N9Y+JPebaCVoEUHdBeBSuLxcHr71r+Lw=="],
|
|
36
|
+
|
|
37
|
+
"bitcoinjs-lib": ["bitcoinjs-lib@7.0.0", "", { "dependencies": { "@noble/hashes": "^1.2.0", "bech32": "^2.0.0", "bip174": "^3.0.0", "bs58check": "^4.0.0", "uint8array-tools": "^0.0.9", "valibot": "^0.38.0", "varuint-bitcoin": "^2.0.0" } }, "sha512-2W6dGXFd1KG3Bs90Bzb5+ViCeSKNIYkCUWZ4cvUzUgwnneiNNZ6Sk8twGNcjlesmxC0JyLc/958QycfpvXLg7A=="],
|
|
38
|
+
|
|
39
|
+
"bs58": ["bs58@6.0.0", "", { "dependencies": { "base-x": "^5.0.0" } }, "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw=="],
|
|
40
|
+
|
|
41
|
+
"bs58check": ["bs58check@4.0.0", "", { "dependencies": { "@noble/hashes": "^1.2.0", "bs58": "^6.0.0" } }, "sha512-FsGDOnFg9aVI9erdriULkd/JjEWONV/lQE5aYziB5PoBsXRind56lh8doIZIc9X4HoxT5x4bLjMWN1/NB8Zp5g=="],
|
|
42
|
+
|
|
43
|
+
"bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
|
44
|
+
|
|
45
|
+
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
|
46
|
+
|
|
47
|
+
"light-bolt11-decoder": ["light-bolt11-decoder@3.2.0", "", { "dependencies": { "@scure/base": "1.1.1" } }, "sha512-3QEofgiBOP4Ehs9BI+RkZdXZNtSys0nsJ6fyGeSiAGCBsMwHGUDS/JQlY/sTnWs91A2Nh0S9XXfA8Sy9g6QpuQ=="],
|
|
48
|
+
|
|
49
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
50
|
+
|
|
51
|
+
"uint8array-tools": ["uint8array-tools@0.0.9", "", {}, "sha512-9vqDWmoSXOoi+K14zNaf6LBV51Q8MayF0/IiQs3GlygIKUYtog603e6virExkjjFosfJUBI4LhbQK1iq8IG11A=="],
|
|
52
|
+
|
|
53
|
+
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
54
|
+
|
|
55
|
+
"valibot": ["valibot@0.38.0", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-RCJa0fetnzp+h+KN9BdgYOgtsMAG9bfoJ9JSjIhFHobKWVWyzM3jjaeNTdpFK9tQtf3q1sguXeERJ/LcmdFE7w=="],
|
|
56
|
+
|
|
57
|
+
"varuint-bitcoin": ["varuint-bitcoin@2.0.0", "", { "dependencies": { "uint8array-tools": "^0.0.8" } }, "sha512-6QZbU/rHO2ZQYpWFDALCDSRsXbAs1VOEmXAxtbtjLtKuMJ/FQ8YbhfxlaiKv5nklci0M6lZtlZyxo9Q+qNnyog=="],
|
|
58
|
+
|
|
59
|
+
"light-bolt11-decoder/@scure/base": ["@scure/base@1.1.1", "", {}, "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="],
|
|
60
|
+
|
|
61
|
+
"varuint-bitcoin/uint8array-tools": ["uint8array-tools@0.0.8", "", {}, "sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g=="],
|
|
62
|
+
}
|
|
63
|
+
}
|
package/example.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseBIP321,
|
|
3
|
+
getPaymentMethodsByNetwork,
|
|
4
|
+
getValidPaymentMethods,
|
|
5
|
+
formatPaymentMethodsSummary,
|
|
6
|
+
} from "./index";
|
|
7
|
+
|
|
8
|
+
console.log("=== BIP-321 Parser Examples ===\n");
|
|
9
|
+
|
|
10
|
+
// Example 1: Simple mainnet address
|
|
11
|
+
console.log("1. Simple Mainnet Address");
|
|
12
|
+
console.log("URI: bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa");
|
|
13
|
+
const example1 = parseBIP321("bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa");
|
|
14
|
+
console.log(`Valid: ${example1.valid}`);
|
|
15
|
+
console.log(`Network: ${example1.network}`);
|
|
16
|
+
console.log(`Address: ${example1.address}`);
|
|
17
|
+
console.log();
|
|
18
|
+
|
|
19
|
+
// Example 2: Payment with amount and label
|
|
20
|
+
console.log("2. Payment with Amount and Label");
|
|
21
|
+
const example2 = parseBIP321(
|
|
22
|
+
"bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?amount=0.5&label=Donation&message=Thank%20you%20for%20your%20support",
|
|
23
|
+
);
|
|
24
|
+
console.log(`Valid: ${example2.valid}`);
|
|
25
|
+
console.log(`Amount: ${example2.amount} BTC`);
|
|
26
|
+
console.log(`Label: ${example2.label}`);
|
|
27
|
+
console.log(`Message: ${example2.message}`);
|
|
28
|
+
console.log();
|
|
29
|
+
|
|
30
|
+
// Example 3: Lightning invoice with fallback
|
|
31
|
+
console.log("3. Lightning Invoice with On-Chain Fallback");
|
|
32
|
+
const example3 = parseBIP321(
|
|
33
|
+
"bitcoin:bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq?lightning=lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs",
|
|
34
|
+
);
|
|
35
|
+
console.log(`Valid: ${example3.valid}`);
|
|
36
|
+
console.log(`Payment methods: ${example3.paymentMethods.length}`);
|
|
37
|
+
example3.paymentMethods.forEach((method, i) => {
|
|
38
|
+
console.log(
|
|
39
|
+
` ${i + 1}. ${method.type} (${method.network}) - Valid: ${method.valid}`,
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
console.log();
|
|
43
|
+
|
|
44
|
+
// Example 4: Lightning-only payment
|
|
45
|
+
console.log("4. Lightning-Only Payment");
|
|
46
|
+
const example4 = parseBIP321(
|
|
47
|
+
"bitcoin:?lightning=lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs",
|
|
48
|
+
);
|
|
49
|
+
console.log(`Valid: ${example4.valid}`);
|
|
50
|
+
console.log(`Address in path: ${example4.address || "none"}`);
|
|
51
|
+
console.log(`Payment type: ${example4.paymentMethods[0]?.type}`);
|
|
52
|
+
console.log(`Network: ${example4.paymentMethods[0]?.network}`);
|
|
53
|
+
console.log();
|
|
54
|
+
|
|
55
|
+
// Example 5: Multiple payment methods
|
|
56
|
+
console.log("5. Multiple Payment Methods");
|
|
57
|
+
const example5 = parseBIP321(
|
|
58
|
+
"bitcoin:?lightning=lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs&lno=lno1bogusoffer&sp=sp1qsilentpayment",
|
|
59
|
+
);
|
|
60
|
+
console.log(`Valid: ${example5.valid}`);
|
|
61
|
+
console.log(`Total payment methods: ${example5.paymentMethods.length}`);
|
|
62
|
+
example5.paymentMethods.forEach((method) => {
|
|
63
|
+
console.log(` - ${method.type}: ${method.valid ? "valid" : "invalid"}`);
|
|
64
|
+
});
|
|
65
|
+
console.log();
|
|
66
|
+
|
|
67
|
+
// Example 6: Network-specific parameters
|
|
68
|
+
console.log("6. Network-Specific Parameters");
|
|
69
|
+
const example6 = parseBIP321(
|
|
70
|
+
"bitcoin:?bc=bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq&tb=tb1qghfhmd4zh7ncpmxl3qzhmq566jk8ckq4gafnmg",
|
|
71
|
+
);
|
|
72
|
+
const byNetwork = getPaymentMethodsByNetwork(example6);
|
|
73
|
+
console.log(`Valid: ${example6.valid}`);
|
|
74
|
+
console.log(`Mainnet methods: ${byNetwork.mainnet?.length || 0}`);
|
|
75
|
+
console.log(`Testnet methods: ${byNetwork.testnet?.length || 0}`);
|
|
76
|
+
console.log();
|
|
77
|
+
|
|
78
|
+
// Example 7: Testnet lightning invoice
|
|
79
|
+
console.log("7. Testnet Lightning Invoice");
|
|
80
|
+
const example7 = parseBIP321(
|
|
81
|
+
"bitcoin:?lightning=lntb2500n1pwxlkl5pp5g8hz28tlf950ps942lu3dknfete8yax2ctywpwjs872x9kngvvuqdqage5hyum5yp6x2um5yp5kuan0d93k2cqzyskdc5s2ltgm9kklz42x3e4tggdd9lcep2s9t2yk54gnfxg48wxushayrt52zjmua43gdnxmuc5s0c8g29ja9vnxs6x3kxgsha07htcacpmdyl64",
|
|
82
|
+
);
|
|
83
|
+
console.log(`Valid: ${example7.valid}`);
|
|
84
|
+
console.log(`Network: ${example7.paymentMethods[0]?.network}`);
|
|
85
|
+
console.log();
|
|
86
|
+
|
|
87
|
+
// Example 8: Taproot address
|
|
88
|
+
console.log("8. Taproot Address (bc1p...)");
|
|
89
|
+
const example8 = parseBIP321(
|
|
90
|
+
"bitcoin:?bc=bc1pdyp8m5mhurxa9mf822jegnhu49g2zcchgcq8jzrjxg58u2lvudyqftt43a",
|
|
91
|
+
);
|
|
92
|
+
console.log(`Valid: ${example8.valid}`);
|
|
93
|
+
console.log(`Network: ${example8.paymentMethods[0]?.network}`);
|
|
94
|
+
console.log(
|
|
95
|
+
`Address: ${example8.paymentMethods[0]?.value.substring(0, 20)}...`,
|
|
96
|
+
);
|
|
97
|
+
console.log();
|
|
98
|
+
|
|
99
|
+
// Example 9: Error handling - invalid address
|
|
100
|
+
console.log("9. Error Handling - Invalid Address");
|
|
101
|
+
const example9 = parseBIP321("bitcoin:invalidaddress123");
|
|
102
|
+
console.log(`Valid: ${example9.valid}`);
|
|
103
|
+
console.log("Errors:");
|
|
104
|
+
example9.errors.forEach((error) => console.log(` - ${error}`));
|
|
105
|
+
console.log();
|
|
106
|
+
|
|
107
|
+
// Example 10: Error handling - network mismatch
|
|
108
|
+
console.log("10. Error Handling - Network Mismatch");
|
|
109
|
+
const example10 = parseBIP321(
|
|
110
|
+
"bitcoin:?bc=tb1qghfhmd4zh7ncpmxl3qzhmq566jk8ckq4gafnmg",
|
|
111
|
+
);
|
|
112
|
+
console.log(`Valid: ${example10.valid}`);
|
|
113
|
+
console.log("Errors:");
|
|
114
|
+
example10.errors.forEach((error) => console.log(` - ${error}`));
|
|
115
|
+
console.log();
|
|
116
|
+
|
|
117
|
+
// Example 11: Using helper functions
|
|
118
|
+
console.log("11. Helper Functions");
|
|
119
|
+
const example11 = parseBIP321(
|
|
120
|
+
"bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?amount=1.5&label=Test%20Payment&lightning=lnbc15u1p3xnhl2pp5jptserfk3zk4qy42tlucycrfwxhydvlemu9pqr93tuzlv9cc7g3sdqsvfhkcap3xyhx7un8cqzpgxqzjcsp5f8c52y2stc300gl6s4xswtjpc37hrnnr3c9wvtgjfuvqmpm35evq9qyyssqy4lgd8tj637qcjp05rdpxxykjenthxftej7a2zzmwrmrl70fyj9hvj0rewhzj7jfyuwkwcg9g2jpwtk3wkjtwnkdks84hsnu8xps5vsq4gj5hs",
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
console.log("Valid payment methods:");
|
|
124
|
+
const validMethods = getValidPaymentMethods(example11);
|
|
125
|
+
validMethods.forEach((method) => {
|
|
126
|
+
console.log(` - ${method.type} (${method.network})`);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
console.log("\nPayment methods by network:");
|
|
130
|
+
const grouped = getPaymentMethodsByNetwork(example11);
|
|
131
|
+
Object.entries(grouped).forEach(([network, methods]) => {
|
|
132
|
+
if (methods.length > 0) {
|
|
133
|
+
console.log(` ${network}: ${methods.length} method(s)`);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
console.log("\nFormatted summary:");
|
|
138
|
+
console.log(formatPaymentMethodsSummary(example11));
|
|
139
|
+
console.log();
|
|
140
|
+
|
|
141
|
+
// Example 12: Proof of payment
|
|
142
|
+
console.log("12. Proof of Payment Callback");
|
|
143
|
+
const example12 = parseBIP321(
|
|
144
|
+
"bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?pop=myapp%3acallback",
|
|
145
|
+
);
|
|
146
|
+
console.log(`Valid: ${example12.valid}`);
|
|
147
|
+
console.log(`Pop URI: ${example12.pop}`);
|
|
148
|
+
console.log(`Pop required: ${example12.popRequired}`);
|
|
149
|
+
console.log();
|
|
150
|
+
|
|
151
|
+
// Example 13: Case insensitivity
|
|
152
|
+
console.log("13. Case Insensitivity");
|
|
153
|
+
const example13 = parseBIP321(
|
|
154
|
+
"BITCOIN:BC1QAR0SRRR7XFKVY5L643LYDNW9RE59GTZZWF5MDQ?AMOUNT=1.0&LABEL=Test",
|
|
155
|
+
);
|
|
156
|
+
console.log(`Valid: ${example13.valid}`);
|
|
157
|
+
console.log(`Amount: ${example13.amount}`);
|
|
158
|
+
console.log(`Label: ${example13.label}`);
|
|
159
|
+
console.log();
|
|
160
|
+
|
|
161
|
+
console.log("=== All examples completed ===");
|