nostr-tools 1.4.1 → 1.5.0
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/README.md +9 -5
- package/event.ts +32 -11
- package/index.ts +1 -0
- package/lib/nostr.bundle.js +220 -70
- package/lib/nostr.bundle.js.map +4 -4
- package/lib/nostr.cjs.js +220 -70
- package/lib/nostr.cjs.js.map +4 -4
- package/lib/nostr.esm.js +220 -70
- package/lib/nostr.esm.js.map +4 -4
- package/nip19.test.js +22 -0
- package/nip19.ts +70 -27
- package/nip57.ts +138 -0
- package/package.json +1 -1
- package/relay.ts +18 -40
package/README.md
CHANGED
|
@@ -4,6 +4,13 @@ Tools for developing [Nostr](https://github.com/fiatjaf/nostr) clients.
|
|
|
4
4
|
|
|
5
5
|
Only depends on _@scure_ and _@noble_ packages.
|
|
6
6
|
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install nostr-tools # or yarn add nostr-tools
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
|
|
7
14
|
## Usage
|
|
8
15
|
|
|
9
16
|
### Generating a private key and a public key
|
|
@@ -53,8 +60,6 @@ import {
|
|
|
53
60
|
} from 'nostr-tools'
|
|
54
61
|
|
|
55
62
|
const relay = relayInit('wss://relay.example.com')
|
|
56
|
-
await relay.connect()
|
|
57
|
-
|
|
58
63
|
relay.on('connect', () => {
|
|
59
64
|
console.log(`connected to ${relay.url}`)
|
|
60
65
|
})
|
|
@@ -62,6 +67,8 @@ relay.on('error', () => {
|
|
|
62
67
|
console.log(`failed to connect to ${relay.url}`)
|
|
63
68
|
})
|
|
64
69
|
|
|
70
|
+
await relay.connect()
|
|
71
|
+
|
|
65
72
|
// let's query for an event that exists
|
|
66
73
|
let sub = relay.sub([
|
|
67
74
|
{
|
|
@@ -104,9 +111,6 @@ let pub = relay.publish(event)
|
|
|
104
111
|
pub.on('ok', () => {
|
|
105
112
|
console.log(`${relay.url} has accepted our event`)
|
|
106
113
|
})
|
|
107
|
-
pub.on('seen', () => {
|
|
108
|
-
console.log(`we saw the event on ${relay.url}`)
|
|
109
|
-
})
|
|
110
114
|
pub.on('failed', reason => {
|
|
111
115
|
console.log(`failed to publish to ${relay.url}: ${reason}`)
|
|
112
116
|
})
|
package/event.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as secp256k1 from '@noble/secp256k1'
|
|
|
2
2
|
import {sha256} from '@noble/hashes/sha256'
|
|
3
3
|
|
|
4
4
|
import {utf8Encoder} from './utils'
|
|
5
|
+
import {getPublicKey} from './keys'
|
|
5
6
|
|
|
6
7
|
/* eslint-disable no-unused-vars */
|
|
7
8
|
export enum Kind {
|
|
@@ -16,30 +17,49 @@ export enum Kind {
|
|
|
16
17
|
ChannelMetadata = 41,
|
|
17
18
|
ChannelMessage = 42,
|
|
18
19
|
ChannelHideMessage = 43,
|
|
19
|
-
ChannelMuteUser = 44
|
|
20
|
+
ChannelMuteUser = 44,
|
|
21
|
+
Report = 1984,
|
|
22
|
+
ZapRequest = 9734,
|
|
23
|
+
Zap = 9735,
|
|
24
|
+
RelayList = 10002,
|
|
25
|
+
ClientAuth = 22242,
|
|
26
|
+
Article = 30023
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
export type
|
|
23
|
-
id?: string
|
|
24
|
-
sig?: string
|
|
29
|
+
export type EventTemplate = {
|
|
25
30
|
kind: Kind
|
|
26
31
|
tags: string[][]
|
|
27
|
-
pubkey: string
|
|
28
32
|
content: string
|
|
29
33
|
created_at: number
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
export
|
|
36
|
+
export type UnsignedEvent = EventTemplate & {
|
|
37
|
+
pubkey: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type Event = UnsignedEvent & {
|
|
41
|
+
id: string
|
|
42
|
+
sig: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getBlankEvent(): EventTemplate {
|
|
33
46
|
return {
|
|
34
47
|
kind: 255,
|
|
35
|
-
pubkey: '',
|
|
36
48
|
content: '',
|
|
37
49
|
tags: [],
|
|
38
50
|
created_at: 0
|
|
39
51
|
}
|
|
40
52
|
}
|
|
41
53
|
|
|
42
|
-
export function
|
|
54
|
+
export function finishEvent(t: EventTemplate, privateKey: string): Event {
|
|
55
|
+
let event = t as Event
|
|
56
|
+
event.pubkey = getPublicKey(privateKey)
|
|
57
|
+
event.id = getEventHash(event)
|
|
58
|
+
event.sig = signEvent(event, privateKey)
|
|
59
|
+
return event
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function serializeEvent(evt: UnsignedEvent): string {
|
|
43
63
|
if (!validateEvent(evt))
|
|
44
64
|
throw new Error("can't serialize event with wrong or missing properties")
|
|
45
65
|
|
|
@@ -53,12 +73,13 @@ export function serializeEvent(evt: Event): string {
|
|
|
53
73
|
])
|
|
54
74
|
}
|
|
55
75
|
|
|
56
|
-
export function getEventHash(event:
|
|
76
|
+
export function getEventHash(event: UnsignedEvent): string {
|
|
57
77
|
let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)))
|
|
58
78
|
return secp256k1.utils.bytesToHex(eventHash)
|
|
59
79
|
}
|
|
60
80
|
|
|
61
|
-
export function validateEvent(event:
|
|
81
|
+
export function validateEvent(event: UnsignedEvent): boolean {
|
|
82
|
+
if (typeof event !== 'object') return false
|
|
62
83
|
if (typeof event.content !== 'string') return false
|
|
63
84
|
if (typeof event.created_at !== 'number') return false
|
|
64
85
|
if (typeof event.pubkey !== 'string') return false
|
|
@@ -84,7 +105,7 @@ export function verifySignature(event: Event & {sig: string}): boolean {
|
|
|
84
105
|
)
|
|
85
106
|
}
|
|
86
107
|
|
|
87
|
-
export function signEvent(event:
|
|
108
|
+
export function signEvent(event: UnsignedEvent, key: string): string {
|
|
88
109
|
return secp256k1.utils.bytesToHex(
|
|
89
110
|
secp256k1.schnorr.signSync(getEventHash(event), key)
|
|
90
111
|
)
|
package/index.ts
CHANGED
package/lib/nostr.bundle.js
CHANGED
|
@@ -3605,6 +3605,7 @@ zoo`.split("\n");
|
|
|
3605
3605
|
__export(nostr_tools_exports, {
|
|
3606
3606
|
Kind: () => Kind,
|
|
3607
3607
|
SimplePool: () => SimplePool,
|
|
3608
|
+
finishEvent: () => finishEvent,
|
|
3608
3609
|
fj: () => fakejson_exports,
|
|
3609
3610
|
generatePrivateKey: () => generatePrivateKey,
|
|
3610
3611
|
getBlankEvent: () => getBlankEvent,
|
|
@@ -3617,6 +3618,7 @@ zoo`.split("\n");
|
|
|
3617
3618
|
nip06: () => nip06_exports,
|
|
3618
3619
|
nip19: () => nip19_exports,
|
|
3619
3620
|
nip26: () => nip26_exports,
|
|
3621
|
+
nip57: () => nip57_exports,
|
|
3620
3622
|
relayInit: () => relayInit,
|
|
3621
3623
|
serializeEvent: () => serializeEvent,
|
|
3622
3624
|
signEvent: () => signEvent,
|
|
@@ -5100,17 +5102,29 @@ zoo`.split("\n");
|
|
|
5100
5102
|
Kind2[Kind2["ChannelMessage"] = 42] = "ChannelMessage";
|
|
5101
5103
|
Kind2[Kind2["ChannelHideMessage"] = 43] = "ChannelHideMessage";
|
|
5102
5104
|
Kind2[Kind2["ChannelMuteUser"] = 44] = "ChannelMuteUser";
|
|
5105
|
+
Kind2[Kind2["Report"] = 1984] = "Report";
|
|
5106
|
+
Kind2[Kind2["ZapRequest"] = 9734] = "ZapRequest";
|
|
5107
|
+
Kind2[Kind2["Zap"] = 9735] = "Zap";
|
|
5108
|
+
Kind2[Kind2["RelayList"] = 10002] = "RelayList";
|
|
5109
|
+
Kind2[Kind2["ClientAuth"] = 22242] = "ClientAuth";
|
|
5110
|
+
Kind2[Kind2["Article"] = 30023] = "Article";
|
|
5103
5111
|
return Kind2;
|
|
5104
5112
|
})(Kind || {});
|
|
5105
5113
|
function getBlankEvent() {
|
|
5106
5114
|
return {
|
|
5107
5115
|
kind: 255,
|
|
5108
|
-
pubkey: "",
|
|
5109
5116
|
content: "",
|
|
5110
5117
|
tags: [],
|
|
5111
5118
|
created_at: 0
|
|
5112
5119
|
};
|
|
5113
5120
|
}
|
|
5121
|
+
function finishEvent(t, privateKey) {
|
|
5122
|
+
let event = t;
|
|
5123
|
+
event.pubkey = getPublicKey(privateKey);
|
|
5124
|
+
event.id = getEventHash(event);
|
|
5125
|
+
event.sig = signEvent(event, privateKey);
|
|
5126
|
+
return event;
|
|
5127
|
+
}
|
|
5114
5128
|
function serializeEvent(evt) {
|
|
5115
5129
|
if (!validateEvent(evt))
|
|
5116
5130
|
throw new Error("can't serialize event with wrong or missing properties");
|
|
@@ -5128,6 +5142,8 @@ zoo`.split("\n");
|
|
|
5128
5142
|
return utils.bytesToHex(eventHash);
|
|
5129
5143
|
}
|
|
5130
5144
|
function validateEvent(event) {
|
|
5145
|
+
if (typeof event !== "object")
|
|
5146
|
+
return false;
|
|
5131
5147
|
if (typeof event.content !== "string")
|
|
5132
5148
|
return false;
|
|
5133
5149
|
if (typeof event.created_at !== "number")
|
|
@@ -5308,17 +5324,24 @@ zoo`.split("\n");
|
|
|
5308
5324
|
return;
|
|
5309
5325
|
case "EOSE": {
|
|
5310
5326
|
let id2 = data[1];
|
|
5311
|
-
(
|
|
5327
|
+
if (id2 in subListeners) {
|
|
5328
|
+
subListeners[id2].eose.forEach((cb) => cb());
|
|
5329
|
+
subListeners[id2].eose = [];
|
|
5330
|
+
}
|
|
5312
5331
|
return;
|
|
5313
5332
|
}
|
|
5314
5333
|
case "OK": {
|
|
5315
5334
|
let id2 = data[1];
|
|
5316
5335
|
let ok = data[2];
|
|
5317
5336
|
let reason = data[3] || "";
|
|
5318
|
-
if (
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5337
|
+
if (id2 in pubListeners) {
|
|
5338
|
+
if (ok)
|
|
5339
|
+
pubListeners[id2].ok.forEach((cb) => cb());
|
|
5340
|
+
else
|
|
5341
|
+
pubListeners[id2].failed.forEach((cb) => cb(reason));
|
|
5342
|
+
pubListeners[id2].ok = [];
|
|
5343
|
+
pubListeners[id2].failed = [];
|
|
5344
|
+
}
|
|
5322
5345
|
return;
|
|
5323
5346
|
}
|
|
5324
5347
|
case "NOTICE":
|
|
@@ -5430,46 +5453,14 @@ zoo`.split("\n");
|
|
|
5430
5453
|
if (!event.id)
|
|
5431
5454
|
throw new Error(`event ${event} has no id`);
|
|
5432
5455
|
let id = event.id;
|
|
5433
|
-
|
|
5434
|
-
var mustMonitor = false;
|
|
5435
|
-
trySend(["EVENT", event]).then(() => {
|
|
5436
|
-
sent = true;
|
|
5437
|
-
if (mustMonitor) {
|
|
5438
|
-
startMonitoring();
|
|
5439
|
-
mustMonitor = false;
|
|
5440
|
-
}
|
|
5441
|
-
}).catch(() => {
|
|
5442
|
-
});
|
|
5443
|
-
const startMonitoring = () => {
|
|
5444
|
-
let monitor = sub([{ ids: [id] }], {
|
|
5445
|
-
id: `monitor-${id.slice(0, 5)}`
|
|
5446
|
-
});
|
|
5447
|
-
let willUnsub = setTimeout(() => {
|
|
5448
|
-
;
|
|
5449
|
-
(pubListeners[id]?.failed || []).forEach(
|
|
5450
|
-
(cb) => cb("event not seen after 5 seconds")
|
|
5451
|
-
);
|
|
5452
|
-
monitor.unsub();
|
|
5453
|
-
}, 5e3);
|
|
5454
|
-
monitor.on("event", () => {
|
|
5455
|
-
clearTimeout(willUnsub);
|
|
5456
|
-
(pubListeners[id]?.seen || []).forEach((cb) => cb());
|
|
5457
|
-
});
|
|
5458
|
-
};
|
|
5456
|
+
trySend(["EVENT", event]);
|
|
5459
5457
|
return {
|
|
5460
5458
|
on: (type, cb) => {
|
|
5461
5459
|
pubListeners[id] = pubListeners[id] || {
|
|
5462
5460
|
ok: [],
|
|
5463
|
-
seen: [],
|
|
5464
5461
|
failed: []
|
|
5465
5462
|
};
|
|
5466
5463
|
pubListeners[id][type].push(cb);
|
|
5467
|
-
if (type === "seen") {
|
|
5468
|
-
if (sent)
|
|
5469
|
-
startMonitoring();
|
|
5470
|
-
else
|
|
5471
|
-
mustMonitor = true;
|
|
5472
|
-
}
|
|
5473
5464
|
},
|
|
5474
5465
|
off: (type, cb) => {
|
|
5475
5466
|
let listeners2 = pubListeners[id];
|
|
@@ -5483,6 +5474,9 @@ zoo`.split("\n");
|
|
|
5483
5474
|
},
|
|
5484
5475
|
connect,
|
|
5485
5476
|
close() {
|
|
5477
|
+
listeners = { connect: [], disconnect: [], error: [], notice: [] };
|
|
5478
|
+
subListeners = {};
|
|
5479
|
+
pubListeners = {};
|
|
5486
5480
|
if (ws.readyState > 1)
|
|
5487
5481
|
return Promise.resolve();
|
|
5488
5482
|
ws.close();
|
|
@@ -8267,6 +8261,7 @@ zoo`.split("\n");
|
|
|
8267
8261
|
var nip19_exports = {};
|
|
8268
8262
|
__export(nip19_exports, {
|
|
8269
8263
|
decode: () => decode,
|
|
8264
|
+
naddrEncode: () => naddrEncode,
|
|
8270
8265
|
neventEncode: () => neventEncode,
|
|
8271
8266
|
noteEncode: () => noteEncode,
|
|
8272
8267
|
nprofileEncode: () => nprofileEncode,
|
|
@@ -8278,38 +8273,64 @@ zoo`.split("\n");
|
|
|
8278
8273
|
function decode(nip19) {
|
|
8279
8274
|
let { prefix, words } = bech32.decode(nip19, Bech32MaxSize);
|
|
8280
8275
|
let data = new Uint8Array(bech32.fromWords(words));
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8276
|
+
switch (prefix) {
|
|
8277
|
+
case "nprofile": {
|
|
8278
|
+
let tlv = parseTLV(data);
|
|
8279
|
+
if (!tlv[0]?.[0])
|
|
8280
|
+
throw new Error("missing TLV 0 for nprofile");
|
|
8281
|
+
if (tlv[0][0].length !== 32)
|
|
8282
|
+
throw new Error("TLV 0 should be 32 bytes");
|
|
8283
|
+
return {
|
|
8284
|
+
type: "nprofile",
|
|
8285
|
+
data: {
|
|
8286
|
+
pubkey: utils.bytesToHex(tlv[0][0]),
|
|
8287
|
+
relays: tlv[1].map((d) => utf8Decoder.decode(d))
|
|
8288
|
+
}
|
|
8289
|
+
};
|
|
8290
|
+
}
|
|
8291
|
+
case "nevent": {
|
|
8292
|
+
let tlv = parseTLV(data);
|
|
8293
|
+
if (!tlv[0]?.[0])
|
|
8294
|
+
throw new Error("missing TLV 0 for nevent");
|
|
8295
|
+
if (tlv[0][0].length !== 32)
|
|
8296
|
+
throw new Error("TLV 0 should be 32 bytes");
|
|
8297
|
+
return {
|
|
8298
|
+
type: "nevent",
|
|
8299
|
+
data: {
|
|
8300
|
+
id: utils.bytesToHex(tlv[0][0]),
|
|
8301
|
+
relays: tlv[1].map((d) => utf8Decoder.decode(d))
|
|
8302
|
+
}
|
|
8303
|
+
};
|
|
8304
|
+
}
|
|
8305
|
+
case "naddr": {
|
|
8306
|
+
let tlv = parseTLV(data);
|
|
8307
|
+
if (!tlv[0]?.[0])
|
|
8308
|
+
throw new Error("missing TLV 0 for naddr");
|
|
8309
|
+
if (!tlv[2]?.[0])
|
|
8310
|
+
throw new Error("missing TLV 2 for naddr");
|
|
8311
|
+
if (tlv[2][0].length !== 32)
|
|
8312
|
+
throw new Error("TLV 2 should be 32 bytes");
|
|
8313
|
+
if (!tlv[3]?.[0])
|
|
8314
|
+
throw new Error("missing TLV 3 for naddr");
|
|
8315
|
+
if (tlv[3][0].length !== 4)
|
|
8316
|
+
throw new Error("TLV 3 should be 4 bytes");
|
|
8317
|
+
return {
|
|
8318
|
+
type: "naddr",
|
|
8319
|
+
data: {
|
|
8320
|
+
identifier: utf8Decoder.decode(tlv[0][0]),
|
|
8321
|
+
pubkey: utils.bytesToHex(tlv[2][0]),
|
|
8322
|
+
kind: parseInt(utils.bytesToHex(tlv[3][0]), 16),
|
|
8323
|
+
relays: tlv[1].map((d) => utf8Decoder.decode(d))
|
|
8324
|
+
}
|
|
8325
|
+
};
|
|
8326
|
+
}
|
|
8327
|
+
case "nsec":
|
|
8328
|
+
case "npub":
|
|
8329
|
+
case "note":
|
|
8330
|
+
return { type: prefix, data: utils.bytesToHex(data) };
|
|
8331
|
+
default:
|
|
8332
|
+
throw new Error(`unknown prefix ${prefix}`);
|
|
8311
8333
|
}
|
|
8312
|
-
throw new Error(`unknown prefix ${prefix}`);
|
|
8313
8334
|
}
|
|
8314
8335
|
function parseTLV(data) {
|
|
8315
8336
|
let result = {};
|
|
@@ -8356,6 +8377,18 @@ zoo`.split("\n");
|
|
|
8356
8377
|
let words = bech32.toWords(data);
|
|
8357
8378
|
return bech32.encode("nevent", words, Bech32MaxSize);
|
|
8358
8379
|
}
|
|
8380
|
+
function naddrEncode(addr) {
|
|
8381
|
+
let kind = new ArrayBuffer(4);
|
|
8382
|
+
new DataView(kind).setUint32(0, addr.kind, false);
|
|
8383
|
+
let data = encodeTLV({
|
|
8384
|
+
0: [utf8Encoder.encode(addr.identifier)],
|
|
8385
|
+
1: (addr.relays || []).map((url) => utf8Encoder.encode(url)),
|
|
8386
|
+
2: [utils.hexToBytes(addr.pubkey)],
|
|
8387
|
+
3: [new Uint8Array(kind)]
|
|
8388
|
+
});
|
|
8389
|
+
let words = bech32.toWords(data);
|
|
8390
|
+
return bech32.encode("naddr", words, Bech32MaxSize);
|
|
8391
|
+
}
|
|
8359
8392
|
function encodeTLV(tlv) {
|
|
8360
8393
|
let entries = [];
|
|
8361
8394
|
Object.entries(tlv).forEach(([t, vs]) => {
|
|
@@ -8428,6 +8461,123 @@ zoo`.split("\n");
|
|
|
8428
8461
|
return pubkey;
|
|
8429
8462
|
}
|
|
8430
8463
|
|
|
8464
|
+
// nip57.ts
|
|
8465
|
+
var nip57_exports = {};
|
|
8466
|
+
__export(nip57_exports, {
|
|
8467
|
+
getZapEndpoint: () => getZapEndpoint,
|
|
8468
|
+
makeZapReceipt: () => makeZapReceipt,
|
|
8469
|
+
makeZapRequest: () => makeZapRequest,
|
|
8470
|
+
useFetchImplementation: () => useFetchImplementation2,
|
|
8471
|
+
validateZapRequest: () => validateZapRequest
|
|
8472
|
+
});
|
|
8473
|
+
init_define_process();
|
|
8474
|
+
var _fetch2;
|
|
8475
|
+
try {
|
|
8476
|
+
_fetch2 = fetch;
|
|
8477
|
+
} catch {
|
|
8478
|
+
}
|
|
8479
|
+
function useFetchImplementation2(fetchImplementation) {
|
|
8480
|
+
_fetch2 = fetchImplementation;
|
|
8481
|
+
}
|
|
8482
|
+
async function getZapEndpoint(metadata) {
|
|
8483
|
+
try {
|
|
8484
|
+
let lnurl = "";
|
|
8485
|
+
let { lud06, lud16 } = JSON.parse(metadata.content);
|
|
8486
|
+
if (lud06) {
|
|
8487
|
+
let { words } = bech32.decode(lud06, 1e3);
|
|
8488
|
+
let data = bech32.fromWords(words);
|
|
8489
|
+
lnurl = utf8Decoder.decode(data);
|
|
8490
|
+
} else if (lud16) {
|
|
8491
|
+
let [name, domain] = lud16.split("@");
|
|
8492
|
+
lnurl = `https://${domain}/.well-known/lnurlp/${name}`;
|
|
8493
|
+
} else {
|
|
8494
|
+
return null;
|
|
8495
|
+
}
|
|
8496
|
+
let res = await _fetch2(lnurl);
|
|
8497
|
+
let body = await res.json();
|
|
8498
|
+
if (body.allowsNostr && body.nostrPubkey) {
|
|
8499
|
+
return body.callback;
|
|
8500
|
+
}
|
|
8501
|
+
} catch (err) {
|
|
8502
|
+
}
|
|
8503
|
+
return null;
|
|
8504
|
+
}
|
|
8505
|
+
function makeZapRequest({
|
|
8506
|
+
profile,
|
|
8507
|
+
event,
|
|
8508
|
+
amount,
|
|
8509
|
+
relays,
|
|
8510
|
+
comment = ""
|
|
8511
|
+
}) {
|
|
8512
|
+
if (!amount)
|
|
8513
|
+
throw new Error("amount not given");
|
|
8514
|
+
if (!profile)
|
|
8515
|
+
throw new Error("profile not given");
|
|
8516
|
+
let zr = {
|
|
8517
|
+
kind: 9734,
|
|
8518
|
+
created_at: Math.round(Date.now() / 1e3),
|
|
8519
|
+
content: comment,
|
|
8520
|
+
tags: [
|
|
8521
|
+
["p", profile],
|
|
8522
|
+
["amount", amount.toString()],
|
|
8523
|
+
["relays", ...relays]
|
|
8524
|
+
]
|
|
8525
|
+
};
|
|
8526
|
+
if (event) {
|
|
8527
|
+
zr.tags.push(["e", event]);
|
|
8528
|
+
}
|
|
8529
|
+
return zr;
|
|
8530
|
+
}
|
|
8531
|
+
function validateZapRequest(zapRequestString) {
|
|
8532
|
+
let zapRequest;
|
|
8533
|
+
try {
|
|
8534
|
+
zapRequest = JSON.parse(zapRequestString);
|
|
8535
|
+
} catch (err) {
|
|
8536
|
+
return "Invalid zap request JSON.";
|
|
8537
|
+
}
|
|
8538
|
+
if (!validateEvent(zapRequest))
|
|
8539
|
+
return "Zap request is not a valid Nostr event.";
|
|
8540
|
+
if (!verifySignature(zapRequest))
|
|
8541
|
+
return "Invalid signature on zap request.";
|
|
8542
|
+
let p = zapRequest.tags.find(([t, v]) => t === "p" && v);
|
|
8543
|
+
if (!p)
|
|
8544
|
+
return "Zap request doesn't have a 'p' tag.";
|
|
8545
|
+
if (!p[1].match(/^[a-f0-9]{64}$/))
|
|
8546
|
+
return "Zap request 'p' tag is not valid hex.";
|
|
8547
|
+
let e = zapRequest.tags.find(([t, v]) => t === "e" && v);
|
|
8548
|
+
if (e && !e[1].match(/^[a-f0-9]{64}$/))
|
|
8549
|
+
return "Zap request 'e' tag is not valid hex.";
|
|
8550
|
+
let relays = zapRequest.tags.find(([t, v]) => t === "relays" && v);
|
|
8551
|
+
if (!relays)
|
|
8552
|
+
return "Zap request doesn't have a 'relays' tag.";
|
|
8553
|
+
return null;
|
|
8554
|
+
}
|
|
8555
|
+
function makeZapReceipt({
|
|
8556
|
+
zapRequest,
|
|
8557
|
+
preimage,
|
|
8558
|
+
bolt11,
|
|
8559
|
+
paidAt
|
|
8560
|
+
}) {
|
|
8561
|
+
let zr = JSON.parse(zapRequest);
|
|
8562
|
+
let tagsFromZapRequest = zr.tags.filter(
|
|
8563
|
+
([t]) => t === "e" || t === "p" || t === "a"
|
|
8564
|
+
);
|
|
8565
|
+
let zap = {
|
|
8566
|
+
kind: 9735,
|
|
8567
|
+
created_at: Math.round(paidAt.getTime() / 1e3),
|
|
8568
|
+
content: "",
|
|
8569
|
+
tags: [
|
|
8570
|
+
...tagsFromZapRequest,
|
|
8571
|
+
["bolt11", bolt11],
|
|
8572
|
+
["description", zapRequest]
|
|
8573
|
+
]
|
|
8574
|
+
};
|
|
8575
|
+
if (preimage) {
|
|
8576
|
+
zap.tags.push(["preimage", preimage]);
|
|
8577
|
+
}
|
|
8578
|
+
return zap;
|
|
8579
|
+
}
|
|
8580
|
+
|
|
8431
8581
|
// node_modules/@noble/hashes/esm/hmac.js
|
|
8432
8582
|
init_define_process();
|
|
8433
8583
|
var HMAC2 = class extends Hash {
|