@unicitylabs/sphere-sdk 0.1.2 → 0.1.4

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.
@@ -75,6 +75,9 @@ var FileStorageProvider = class {
75
75
  return this._identity;
76
76
  }
77
77
  async connect() {
78
+ if (this.status === "connected") {
79
+ return;
80
+ }
78
81
  if (!fs.existsSync(this.dataDir)) {
79
82
  fs.mkdirSync(this.dataDir, { recursive: true });
80
83
  }
@@ -183,7 +186,7 @@ var FileTokenStorageProvider = class {
183
186
  const data = {
184
187
  _meta: {
185
188
  version: 1,
186
- address: this.identity?.address ?? "",
189
+ address: this.identity?.l1Address ?? "",
187
190
  formatVersion: "2.0",
188
191
  updatedAt: Date.now()
189
192
  }
@@ -298,15 +301,556 @@ var import_ws = __toESM(require("ws"), 1);
298
301
 
299
302
  // transport/NostrTransportProvider.ts
300
303
  var import_buffer = require("buffer");
304
+
305
+ // node_modules/@noble/hashes/utils.js
306
+ function isBytes(a) {
307
+ return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
308
+ }
309
+ function anumber(n, title = "") {
310
+ if (!Number.isSafeInteger(n) || n < 0) {
311
+ const prefix = title && `"${title}" `;
312
+ throw new Error(`${prefix}expected integer >= 0, got ${n}`);
313
+ }
314
+ }
315
+ function abytes(value, length, title = "") {
316
+ const bytes = isBytes(value);
317
+ const len = value?.length;
318
+ const needsLen = length !== void 0;
319
+ if (!bytes || needsLen && len !== length) {
320
+ const prefix = title && `"${title}" `;
321
+ const ofLen = needsLen ? ` of length ${length}` : "";
322
+ const got = bytes ? `length=${len}` : `type=${typeof value}`;
323
+ throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
324
+ }
325
+ return value;
326
+ }
327
+ function ahash(h) {
328
+ if (typeof h !== "function" || typeof h.create !== "function")
329
+ throw new Error("Hash must wrapped by utils.createHasher");
330
+ anumber(h.outputLen);
331
+ anumber(h.blockLen);
332
+ }
333
+ function aexists(instance, checkFinished = true) {
334
+ if (instance.destroyed)
335
+ throw new Error("Hash instance has been destroyed");
336
+ if (checkFinished && instance.finished)
337
+ throw new Error("Hash#digest() has already been called");
338
+ }
339
+ function aoutput(out, instance) {
340
+ abytes(out, void 0, "digestInto() output");
341
+ const min = instance.outputLen;
342
+ if (out.length < min) {
343
+ throw new Error('"digestInto() output" expected to be of length >=' + min);
344
+ }
345
+ }
346
+ function clean(...arrays) {
347
+ for (let i = 0; i < arrays.length; i++) {
348
+ arrays[i].fill(0);
349
+ }
350
+ }
351
+ function createView(arr) {
352
+ return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
353
+ }
354
+ function rotr(word, shift) {
355
+ return word << 32 - shift | word >>> shift;
356
+ }
357
+ function createHasher(hashCons, info = {}) {
358
+ const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
359
+ const tmp = hashCons(void 0);
360
+ hashC.outputLen = tmp.outputLen;
361
+ hashC.blockLen = tmp.blockLen;
362
+ hashC.create = (opts) => hashCons(opts);
363
+ Object.assign(hashC, info);
364
+ return Object.freeze(hashC);
365
+ }
366
+ var oidNist = (suffix) => ({
367
+ oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
368
+ });
369
+
370
+ // node_modules/@noble/hashes/hmac.js
371
+ var _HMAC = class {
372
+ oHash;
373
+ iHash;
374
+ blockLen;
375
+ outputLen;
376
+ finished = false;
377
+ destroyed = false;
378
+ constructor(hash, key) {
379
+ ahash(hash);
380
+ abytes(key, void 0, "key");
381
+ this.iHash = hash.create();
382
+ if (typeof this.iHash.update !== "function")
383
+ throw new Error("Expected instance of class which extends utils.Hash");
384
+ this.blockLen = this.iHash.blockLen;
385
+ this.outputLen = this.iHash.outputLen;
386
+ const blockLen = this.blockLen;
387
+ const pad = new Uint8Array(blockLen);
388
+ pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
389
+ for (let i = 0; i < pad.length; i++)
390
+ pad[i] ^= 54;
391
+ this.iHash.update(pad);
392
+ this.oHash = hash.create();
393
+ for (let i = 0; i < pad.length; i++)
394
+ pad[i] ^= 54 ^ 92;
395
+ this.oHash.update(pad);
396
+ clean(pad);
397
+ }
398
+ update(buf) {
399
+ aexists(this);
400
+ this.iHash.update(buf);
401
+ return this;
402
+ }
403
+ digestInto(out) {
404
+ aexists(this);
405
+ abytes(out, this.outputLen, "output");
406
+ this.finished = true;
407
+ this.iHash.digestInto(out);
408
+ this.oHash.update(out);
409
+ this.oHash.digestInto(out);
410
+ this.destroy();
411
+ }
412
+ digest() {
413
+ const out = new Uint8Array(this.oHash.outputLen);
414
+ this.digestInto(out);
415
+ return out;
416
+ }
417
+ _cloneInto(to) {
418
+ to ||= Object.create(Object.getPrototypeOf(this), {});
419
+ const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
420
+ to = to;
421
+ to.finished = finished;
422
+ to.destroyed = destroyed;
423
+ to.blockLen = blockLen;
424
+ to.outputLen = outputLen;
425
+ to.oHash = oHash._cloneInto(to.oHash);
426
+ to.iHash = iHash._cloneInto(to.iHash);
427
+ return to;
428
+ }
429
+ clone() {
430
+ return this._cloneInto();
431
+ }
432
+ destroy() {
433
+ this.destroyed = true;
434
+ this.oHash.destroy();
435
+ this.iHash.destroy();
436
+ }
437
+ };
438
+ var hmac = (hash, key, message) => new _HMAC(hash, key).update(message).digest();
439
+ hmac.create = (hash, key) => new _HMAC(hash, key);
440
+
441
+ // node_modules/@noble/hashes/hkdf.js
442
+ function extract(hash, ikm, salt) {
443
+ ahash(hash);
444
+ if (salt === void 0)
445
+ salt = new Uint8Array(hash.outputLen);
446
+ return hmac(hash, salt, ikm);
447
+ }
448
+ var HKDF_COUNTER = /* @__PURE__ */ Uint8Array.of(0);
449
+ var EMPTY_BUFFER = /* @__PURE__ */ Uint8Array.of();
450
+ function expand(hash, prk, info, length = 32) {
451
+ ahash(hash);
452
+ anumber(length, "length");
453
+ const olen = hash.outputLen;
454
+ if (length > 255 * olen)
455
+ throw new Error("Length must be <= 255*HashLen");
456
+ const blocks = Math.ceil(length / olen);
457
+ if (info === void 0)
458
+ info = EMPTY_BUFFER;
459
+ else
460
+ abytes(info, void 0, "info");
461
+ const okm = new Uint8Array(blocks * olen);
462
+ const HMAC = hmac.create(hash, prk);
463
+ const HMACTmp = HMAC._cloneInto();
464
+ const T = new Uint8Array(HMAC.outputLen);
465
+ for (let counter = 0; counter < blocks; counter++) {
466
+ HKDF_COUNTER[0] = counter + 1;
467
+ HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T).update(info).update(HKDF_COUNTER).digestInto(T);
468
+ okm.set(T, olen * counter);
469
+ HMAC._cloneInto(HMACTmp);
470
+ }
471
+ HMAC.destroy();
472
+ HMACTmp.destroy();
473
+ clean(T, HKDF_COUNTER);
474
+ return okm.slice(0, length);
475
+ }
476
+ var hkdf = (hash, ikm, salt, info, length) => expand(hash, extract(hash, ikm, salt), info, length);
477
+
478
+ // node_modules/@noble/hashes/_md.js
479
+ function Chi(a, b, c) {
480
+ return a & b ^ ~a & c;
481
+ }
482
+ function Maj(a, b, c) {
483
+ return a & b ^ a & c ^ b & c;
484
+ }
485
+ var HashMD = class {
486
+ blockLen;
487
+ outputLen;
488
+ padOffset;
489
+ isLE;
490
+ // For partial updates less than block size
491
+ buffer;
492
+ view;
493
+ finished = false;
494
+ length = 0;
495
+ pos = 0;
496
+ destroyed = false;
497
+ constructor(blockLen, outputLen, padOffset, isLE) {
498
+ this.blockLen = blockLen;
499
+ this.outputLen = outputLen;
500
+ this.padOffset = padOffset;
501
+ this.isLE = isLE;
502
+ this.buffer = new Uint8Array(blockLen);
503
+ this.view = createView(this.buffer);
504
+ }
505
+ update(data) {
506
+ aexists(this);
507
+ abytes(data);
508
+ const { view, buffer, blockLen } = this;
509
+ const len = data.length;
510
+ for (let pos = 0; pos < len; ) {
511
+ const take = Math.min(blockLen - this.pos, len - pos);
512
+ if (take === blockLen) {
513
+ const dataView = createView(data);
514
+ for (; blockLen <= len - pos; pos += blockLen)
515
+ this.process(dataView, pos);
516
+ continue;
517
+ }
518
+ buffer.set(data.subarray(pos, pos + take), this.pos);
519
+ this.pos += take;
520
+ pos += take;
521
+ if (this.pos === blockLen) {
522
+ this.process(view, 0);
523
+ this.pos = 0;
524
+ }
525
+ }
526
+ this.length += data.length;
527
+ this.roundClean();
528
+ return this;
529
+ }
530
+ digestInto(out) {
531
+ aexists(this);
532
+ aoutput(out, this);
533
+ this.finished = true;
534
+ const { buffer, view, blockLen, isLE } = this;
535
+ let { pos } = this;
536
+ buffer[pos++] = 128;
537
+ clean(this.buffer.subarray(pos));
538
+ if (this.padOffset > blockLen - pos) {
539
+ this.process(view, 0);
540
+ pos = 0;
541
+ }
542
+ for (let i = pos; i < blockLen; i++)
543
+ buffer[i] = 0;
544
+ view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);
545
+ this.process(view, 0);
546
+ const oview = createView(out);
547
+ const len = this.outputLen;
548
+ if (len % 4)
549
+ throw new Error("_sha2: outputLen must be aligned to 32bit");
550
+ const outLen = len / 4;
551
+ const state = this.get();
552
+ if (outLen > state.length)
553
+ throw new Error("_sha2: outputLen bigger than state");
554
+ for (let i = 0; i < outLen; i++)
555
+ oview.setUint32(4 * i, state[i], isLE);
556
+ }
557
+ digest() {
558
+ const { buffer, outputLen } = this;
559
+ this.digestInto(buffer);
560
+ const res = buffer.slice(0, outputLen);
561
+ this.destroy();
562
+ return res;
563
+ }
564
+ _cloneInto(to) {
565
+ to ||= new this.constructor();
566
+ to.set(...this.get());
567
+ const { blockLen, buffer, length, finished, destroyed, pos } = this;
568
+ to.destroyed = destroyed;
569
+ to.finished = finished;
570
+ to.length = length;
571
+ to.pos = pos;
572
+ if (length % blockLen)
573
+ to.buffer.set(buffer);
574
+ return to;
575
+ }
576
+ clone() {
577
+ return this._cloneInto();
578
+ }
579
+ };
580
+ var SHA256_IV = /* @__PURE__ */ Uint32Array.from([
581
+ 1779033703,
582
+ 3144134277,
583
+ 1013904242,
584
+ 2773480762,
585
+ 1359893119,
586
+ 2600822924,
587
+ 528734635,
588
+ 1541459225
589
+ ]);
590
+
591
+ // node_modules/@noble/hashes/sha2.js
592
+ var SHA256_K = /* @__PURE__ */ Uint32Array.from([
593
+ 1116352408,
594
+ 1899447441,
595
+ 3049323471,
596
+ 3921009573,
597
+ 961987163,
598
+ 1508970993,
599
+ 2453635748,
600
+ 2870763221,
601
+ 3624381080,
602
+ 310598401,
603
+ 607225278,
604
+ 1426881987,
605
+ 1925078388,
606
+ 2162078206,
607
+ 2614888103,
608
+ 3248222580,
609
+ 3835390401,
610
+ 4022224774,
611
+ 264347078,
612
+ 604807628,
613
+ 770255983,
614
+ 1249150122,
615
+ 1555081692,
616
+ 1996064986,
617
+ 2554220882,
618
+ 2821834349,
619
+ 2952996808,
620
+ 3210313671,
621
+ 3336571891,
622
+ 3584528711,
623
+ 113926993,
624
+ 338241895,
625
+ 666307205,
626
+ 773529912,
627
+ 1294757372,
628
+ 1396182291,
629
+ 1695183700,
630
+ 1986661051,
631
+ 2177026350,
632
+ 2456956037,
633
+ 2730485921,
634
+ 2820302411,
635
+ 3259730800,
636
+ 3345764771,
637
+ 3516065817,
638
+ 3600352804,
639
+ 4094571909,
640
+ 275423344,
641
+ 430227734,
642
+ 506948616,
643
+ 659060556,
644
+ 883997877,
645
+ 958139571,
646
+ 1322822218,
647
+ 1537002063,
648
+ 1747873779,
649
+ 1955562222,
650
+ 2024104815,
651
+ 2227730452,
652
+ 2361852424,
653
+ 2428436474,
654
+ 2756734187,
655
+ 3204031479,
656
+ 3329325298
657
+ ]);
658
+ var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
659
+ var SHA2_32B = class extends HashMD {
660
+ constructor(outputLen) {
661
+ super(64, outputLen, 8, false);
662
+ }
663
+ get() {
664
+ const { A, B, C, D, E, F, G, H } = this;
665
+ return [A, B, C, D, E, F, G, H];
666
+ }
667
+ // prettier-ignore
668
+ set(A, B, C, D, E, F, G, H) {
669
+ this.A = A | 0;
670
+ this.B = B | 0;
671
+ this.C = C | 0;
672
+ this.D = D | 0;
673
+ this.E = E | 0;
674
+ this.F = F | 0;
675
+ this.G = G | 0;
676
+ this.H = H | 0;
677
+ }
678
+ process(view, offset) {
679
+ for (let i = 0; i < 16; i++, offset += 4)
680
+ SHA256_W[i] = view.getUint32(offset, false);
681
+ for (let i = 16; i < 64; i++) {
682
+ const W15 = SHA256_W[i - 15];
683
+ const W2 = SHA256_W[i - 2];
684
+ const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
685
+ const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
686
+ SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
687
+ }
688
+ let { A, B, C, D, E, F, G, H } = this;
689
+ for (let i = 0; i < 64; i++) {
690
+ const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
691
+ const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
692
+ const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
693
+ const T2 = sigma0 + Maj(A, B, C) | 0;
694
+ H = G;
695
+ G = F;
696
+ F = E;
697
+ E = D + T1 | 0;
698
+ D = C;
699
+ C = B;
700
+ B = A;
701
+ A = T1 + T2 | 0;
702
+ }
703
+ A = A + this.A | 0;
704
+ B = B + this.B | 0;
705
+ C = C + this.C | 0;
706
+ D = D + this.D | 0;
707
+ E = E + this.E | 0;
708
+ F = F + this.F | 0;
709
+ G = G + this.G | 0;
710
+ H = H + this.H | 0;
711
+ this.set(A, B, C, D, E, F, G, H);
712
+ }
713
+ roundClean() {
714
+ clean(SHA256_W);
715
+ }
716
+ destroy() {
717
+ this.set(0, 0, 0, 0, 0, 0, 0, 0);
718
+ clean(this.buffer);
719
+ }
720
+ };
721
+ var _SHA256 = class extends SHA2_32B {
722
+ // We cannot use array here since array allows indexing by variable
723
+ // which means optimizer/compiler cannot use registers.
724
+ A = SHA256_IV[0] | 0;
725
+ B = SHA256_IV[1] | 0;
726
+ C = SHA256_IV[2] | 0;
727
+ D = SHA256_IV[3] | 0;
728
+ E = SHA256_IV[4] | 0;
729
+ F = SHA256_IV[5] | 0;
730
+ G = SHA256_IV[6] | 0;
731
+ H = SHA256_IV[7] | 0;
732
+ constructor() {
733
+ super(32);
734
+ }
735
+ };
736
+ var sha256 = /* @__PURE__ */ createHasher(
737
+ () => new _SHA256(),
738
+ /* @__PURE__ */ oidNist(1)
739
+ );
740
+
741
+ // transport/NostrTransportProvider.ts
301
742
  var import_nostr_js_sdk = require("@unicitylabs/nostr-js-sdk");
302
743
 
744
+ // core/crypto.ts
745
+ var bip39 = __toESM(require("bip39"), 1);
746
+ var import_crypto_js = __toESM(require("crypto-js"), 1);
747
+ var import_elliptic = __toESM(require("elliptic"), 1);
748
+
749
+ // core/bech32.ts
750
+ var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
751
+ var GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
752
+ function convertBits(data, fromBits, toBits, pad) {
753
+ let acc = 0;
754
+ let bits = 0;
755
+ const ret = [];
756
+ const maxv = (1 << toBits) - 1;
757
+ for (let i = 0; i < data.length; i++) {
758
+ const value = data[i];
759
+ if (value < 0 || value >> fromBits !== 0) return null;
760
+ acc = acc << fromBits | value;
761
+ bits += fromBits;
762
+ while (bits >= toBits) {
763
+ bits -= toBits;
764
+ ret.push(acc >> bits & maxv);
765
+ }
766
+ }
767
+ if (pad) {
768
+ if (bits > 0) {
769
+ ret.push(acc << toBits - bits & maxv);
770
+ }
771
+ } else if (bits >= fromBits || acc << toBits - bits & maxv) {
772
+ return null;
773
+ }
774
+ return ret;
775
+ }
776
+ function hrpExpand(hrp) {
777
+ const ret = [];
778
+ for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) >> 5);
779
+ ret.push(0);
780
+ for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) & 31);
781
+ return ret;
782
+ }
783
+ function bech32Polymod(values) {
784
+ let chk = 1;
785
+ for (let p = 0; p < values.length; p++) {
786
+ const top = chk >> 25;
787
+ chk = (chk & 33554431) << 5 ^ values[p];
788
+ for (let i = 0; i < 5; i++) {
789
+ if (top >> i & 1) chk ^= GENERATOR[i];
790
+ }
791
+ }
792
+ return chk;
793
+ }
794
+ function bech32Checksum(hrp, data) {
795
+ const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);
796
+ const mod = bech32Polymod(values) ^ 1;
797
+ const ret = [];
798
+ for (let p = 0; p < 6; p++) {
799
+ ret.push(mod >> 5 * (5 - p) & 31);
800
+ }
801
+ return ret;
802
+ }
803
+ function encodeBech32(hrp, version, program) {
804
+ if (version < 0 || version > 16) {
805
+ throw new Error("Invalid witness version");
806
+ }
807
+ const converted = convertBits(Array.from(program), 8, 5, true);
808
+ if (!converted) {
809
+ throw new Error("Failed to convert bits");
810
+ }
811
+ const data = [version].concat(converted);
812
+ const checksum = bech32Checksum(hrp, data);
813
+ const combined = data.concat(checksum);
814
+ let out = hrp + "1";
815
+ for (let i = 0; i < combined.length; i++) {
816
+ out += CHARSET[combined[i]];
817
+ }
818
+ return out;
819
+ }
820
+
821
+ // core/crypto.ts
822
+ var ec = new import_elliptic.default.ec("secp256k1");
823
+ var CURVE_ORDER = BigInt(
824
+ "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
825
+ );
826
+ function getPublicKey(privateKey, compressed = true) {
827
+ const keyPair = ec.keyFromPrivate(privateKey, "hex");
828
+ return keyPair.getPublic(compressed, "hex");
829
+ }
830
+ function sha2562(data, inputEncoding = "hex") {
831
+ const parsed = inputEncoding === "hex" ? import_crypto_js.default.enc.Hex.parse(data) : import_crypto_js.default.enc.Utf8.parse(data);
832
+ return import_crypto_js.default.SHA256(parsed).toString();
833
+ }
834
+ function ripemd160(data, inputEncoding = "hex") {
835
+ const parsed = inputEncoding === "hex" ? import_crypto_js.default.enc.Hex.parse(data) : import_crypto_js.default.enc.Utf8.parse(data);
836
+ return import_crypto_js.default.RIPEMD160(parsed).toString();
837
+ }
838
+ function hash160(data) {
839
+ const sha = sha2562(data, "hex");
840
+ return ripemd160(sha, "hex");
841
+ }
842
+ function hash160ToBytes(hash160Hex) {
843
+ const matches = hash160Hex.match(/../g);
844
+ if (!matches) return new Uint8Array(0);
845
+ return Uint8Array.from(matches.map((x) => parseInt(x, 16)));
846
+ }
847
+ function publicKeyToAddress(publicKey, prefix = "alpha", witnessVersion = 0) {
848
+ const pubKeyHash = hash160(publicKey);
849
+ const programBytes = hash160ToBytes(pubKeyHash);
850
+ return encodeBech32(prefix, witnessVersion, programBytes);
851
+ }
852
+
303
853
  // transport/websocket.ts
304
- var WebSocketReadyState = {
305
- CONNECTING: 0,
306
- OPEN: 1,
307
- CLOSING: 2,
308
- CLOSED: 3
309
- };
310
854
  function defaultUUIDGenerator() {
311
855
  if (typeof crypto !== "undefined" && crypto.randomUUID) {
312
856
  return crypto.randomUUID();
@@ -436,6 +980,59 @@ var TIMEOUTS = {
436
980
 
437
981
  // transport/NostrTransportProvider.ts
438
982
  var EVENT_KINDS = NOSTR_EVENT_KINDS;
983
+ function deriveNametagEncryptionKey(privateKeyHex) {
984
+ const privateKeyBytes = import_buffer.Buffer.from(privateKeyHex, "hex");
985
+ const saltInput = new TextEncoder().encode("sphere-nametag-salt");
986
+ const salt = sha256(saltInput);
987
+ const info = new TextEncoder().encode("nametag-encryption");
988
+ return hkdf(sha256, privateKeyBytes, salt, info, 32);
989
+ }
990
+ async function encryptNametag(nametag, privateKeyHex) {
991
+ const key = deriveNametagEncryptionKey(privateKeyHex);
992
+ const iv = crypto.getRandomValues(new Uint8Array(12));
993
+ const encoder = new TextEncoder();
994
+ const data = encoder.encode(nametag);
995
+ const cryptoKey = await crypto.subtle.importKey(
996
+ "raw",
997
+ new Uint8Array(key).buffer,
998
+ { name: "AES-GCM" },
999
+ false,
1000
+ ["encrypt"]
1001
+ );
1002
+ const encrypted = await crypto.subtle.encrypt(
1003
+ { name: "AES-GCM", iv: new Uint8Array(iv).buffer },
1004
+ cryptoKey,
1005
+ new Uint8Array(data).buffer
1006
+ );
1007
+ const combined = new Uint8Array(iv.length + encrypted.byteLength);
1008
+ combined.set(iv, 0);
1009
+ combined.set(new Uint8Array(encrypted), iv.length);
1010
+ return import_buffer.Buffer.from(combined).toString("base64");
1011
+ }
1012
+ async function decryptNametag(encryptedBase64, privateKeyHex) {
1013
+ try {
1014
+ const key = deriveNametagEncryptionKey(privateKeyHex);
1015
+ const combined = import_buffer.Buffer.from(encryptedBase64, "base64");
1016
+ const iv = combined.slice(0, 12);
1017
+ const ciphertext = combined.slice(12);
1018
+ const cryptoKey = await crypto.subtle.importKey(
1019
+ "raw",
1020
+ new Uint8Array(key).buffer,
1021
+ { name: "AES-GCM" },
1022
+ false,
1023
+ ["decrypt"]
1024
+ );
1025
+ const decrypted = await crypto.subtle.decrypt(
1026
+ { name: "AES-GCM", iv: new Uint8Array(iv).buffer },
1027
+ cryptoKey,
1028
+ new Uint8Array(ciphertext).buffer
1029
+ );
1030
+ const decoder = new TextDecoder();
1031
+ return decoder.decode(decrypted);
1032
+ } catch {
1033
+ return null;
1034
+ }
1035
+ }
439
1036
  var NostrTransportProvider = class {
440
1037
  id = "nostr";
441
1038
  name = "Nostr Transport";
@@ -445,9 +1042,10 @@ var NostrTransportProvider = class {
445
1042
  identity = null;
446
1043
  keyManager = null;
447
1044
  status = "disconnected";
448
- // WebSocket connections to relays
449
- connections = /* @__PURE__ */ new Map();
450
- reconnectAttempts = /* @__PURE__ */ new Map();
1045
+ // NostrClient from nostr-js-sdk handles all WebSocket management,
1046
+ // keepalive pings, reconnection, and NIP-42 authentication
1047
+ nostrClient = null;
1048
+ mainSubscriptionId = null;
451
1049
  // Event handlers
452
1050
  messageHandlers = /* @__PURE__ */ new Set();
453
1051
  transferHandlers = /* @__PURE__ */ new Set();
@@ -455,9 +1053,6 @@ var NostrTransportProvider = class {
455
1053
  paymentRequestResponseHandlers = /* @__PURE__ */ new Set();
456
1054
  broadcastHandlers = /* @__PURE__ */ new Map();
457
1055
  eventCallbacks = /* @__PURE__ */ new Set();
458
- // Subscriptions
459
- subscriptions = /* @__PURE__ */ new Map();
460
- // subId -> relays
461
1056
  constructor(config) {
462
1057
  this.config = {
463
1058
  relays: config.relays ?? [...DEFAULT_NOSTR_RELAYS],
@@ -477,16 +1072,43 @@ var NostrTransportProvider = class {
477
1072
  if (this.status === "connected") return;
478
1073
  this.status = "connecting";
479
1074
  try {
480
- const connectPromises = this.config.relays.map(
481
- (relay) => this.connectToRelay(relay)
482
- );
483
- await Promise.allSettled(connectPromises);
484
- if (this.connections.size === 0) {
1075
+ if (!this.keyManager) {
1076
+ const tempKey = import_buffer.Buffer.alloc(32);
1077
+ crypto.getRandomValues(tempKey);
1078
+ this.keyManager = import_nostr_js_sdk.NostrKeyManager.fromPrivateKey(tempKey);
1079
+ }
1080
+ this.nostrClient = new import_nostr_js_sdk.NostrClient(this.keyManager, {
1081
+ autoReconnect: this.config.autoReconnect,
1082
+ reconnectIntervalMs: this.config.reconnectDelay,
1083
+ maxReconnectIntervalMs: this.config.reconnectDelay * 16,
1084
+ // exponential backoff cap
1085
+ pingIntervalMs: 15e3
1086
+ // 15 second keepalive pings (more aggressive to prevent drops)
1087
+ });
1088
+ this.nostrClient.addConnectionListener({
1089
+ onConnect: (url) => {
1090
+ this.log("NostrClient connected to relay:", url);
1091
+ this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
1092
+ },
1093
+ onDisconnect: (url, reason) => {
1094
+ this.log("NostrClient disconnected from relay:", url, "reason:", reason);
1095
+ },
1096
+ onReconnecting: (url, attempt) => {
1097
+ this.log("NostrClient reconnecting to relay:", url, "attempt:", attempt);
1098
+ this.emitEvent({ type: "transport:reconnecting", timestamp: Date.now() });
1099
+ },
1100
+ onReconnected: (url) => {
1101
+ this.log("NostrClient reconnected to relay:", url);
1102
+ this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
1103
+ }
1104
+ });
1105
+ await this.nostrClient.connect(...this.config.relays);
1106
+ if (!this.nostrClient.isConnected()) {
485
1107
  throw new Error("Failed to connect to any relay");
486
1108
  }
487
1109
  this.status = "connected";
488
1110
  this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
489
- this.log("Connected to", this.connections.size, "relays");
1111
+ this.log("Connected to", this.nostrClient.getConnectedRelays().size, "relays");
490
1112
  if (this.identity) {
491
1113
  this.subscribeToEvents();
492
1114
  }
@@ -496,17 +1118,19 @@ var NostrTransportProvider = class {
496
1118
  }
497
1119
  }
498
1120
  async disconnect() {
499
- for (const [url, ws] of this.connections) {
500
- ws.close();
501
- this.connections.delete(url);
1121
+ if (this.nostrClient) {
1122
+ this.nostrClient.disconnect();
1123
+ this.nostrClient = null;
502
1124
  }
503
- this.subscriptions.clear();
1125
+ this.mainSubscriptionId = null;
1126
+ this.walletSubscriptionId = null;
1127
+ this.chatSubscriptionId = null;
504
1128
  this.status = "disconnected";
505
1129
  this.emitEvent({ type: "transport:disconnected", timestamp: Date.now() });
506
1130
  this.log("Disconnected from all relays");
507
1131
  }
508
1132
  isConnected() {
509
- return this.status === "connected" && this.connections.size > 0;
1133
+ return this.status === "connected" && this.nostrClient?.isConnected() === true;
510
1134
  }
511
1135
  getStatus() {
512
1136
  return this.status;
@@ -524,7 +1148,8 @@ var NostrTransportProvider = class {
524
1148
  * Get list of currently connected relay URLs
525
1149
  */
526
1150
  getConnectedRelays() {
527
- return Array.from(this.connections.keys());
1151
+ if (!this.nostrClient) return [];
1152
+ return Array.from(this.nostrClient.getConnectedRelays());
528
1153
  }
529
1154
  /**
530
1155
  * Add a new relay dynamically
@@ -536,9 +1161,9 @@ var NostrTransportProvider = class {
536
1161
  return false;
537
1162
  }
538
1163
  this.config.relays.push(relayUrl);
539
- if (this.status === "connected") {
1164
+ if (this.status === "connected" && this.nostrClient) {
540
1165
  try {
541
- await this.connectToRelay(relayUrl);
1166
+ await this.nostrClient.connect(relayUrl);
542
1167
  this.log("Added and connected to relay:", relayUrl);
543
1168
  this.emitEvent({
544
1169
  type: "transport:relay_added",
@@ -566,6 +1191,8 @@ var NostrTransportProvider = class {
566
1191
  /**
567
1192
  * Remove a relay dynamically
568
1193
  * Will disconnect from the relay if connected
1194
+ * NOTE: NostrClient doesn't support removing individual relays at runtime.
1195
+ * We remove from config so it won't be used on next connect().
569
1196
  */
570
1197
  async removeRelay(relayUrl) {
571
1198
  const index = this.config.relays.indexOf(relayUrl);
@@ -574,19 +1201,13 @@ var NostrTransportProvider = class {
574
1201
  return false;
575
1202
  }
576
1203
  this.config.relays.splice(index, 1);
577
- const ws = this.connections.get(relayUrl);
578
- if (ws) {
579
- ws.close();
580
- this.connections.delete(relayUrl);
581
- this.reconnectAttempts.delete(relayUrl);
582
- this.log("Removed and disconnected from relay:", relayUrl);
583
- }
1204
+ this.log("Removed relay from config:", relayUrl);
584
1205
  this.emitEvent({
585
1206
  type: "transport:relay_removed",
586
1207
  timestamp: Date.now(),
587
1208
  data: { relay: relayUrl }
588
1209
  });
589
- if (this.connections.size === 0 && this.status === "connected") {
1210
+ if (this.nostrClient && !this.nostrClient.isConnected() && this.status === "connected") {
590
1211
  this.status = "error";
591
1212
  this.emitEvent({
592
1213
  type: "transport:error",
@@ -606,8 +1227,8 @@ var NostrTransportProvider = class {
606
1227
  * Check if a relay is currently connected
607
1228
  */
608
1229
  isRelayConnected(relayUrl) {
609
- const ws = this.connections.get(relayUrl);
610
- return ws !== void 0 && ws.readyState === WebSocketReadyState.OPEN;
1230
+ if (!this.nostrClient) return false;
1231
+ return this.nostrClient.getConnectedRelays().has(relayUrl);
611
1232
  }
612
1233
  // ===========================================================================
613
1234
  // TransportProvider Implementation
@@ -618,7 +1239,37 @@ var NostrTransportProvider = class {
618
1239
  this.keyManager = import_nostr_js_sdk.NostrKeyManager.fromPrivateKey(secretKey);
619
1240
  const nostrPubkey = this.keyManager.getPublicKeyHex();
620
1241
  this.log("Identity set, Nostr pubkey:", nostrPubkey.slice(0, 16) + "...");
621
- if (this.isConnected()) {
1242
+ if (this.nostrClient && this.status === "connected") {
1243
+ this.log("Identity changed while connected - recreating NostrClient");
1244
+ const oldClient = this.nostrClient;
1245
+ this.nostrClient = new import_nostr_js_sdk.NostrClient(this.keyManager, {
1246
+ autoReconnect: this.config.autoReconnect,
1247
+ reconnectIntervalMs: this.config.reconnectDelay,
1248
+ maxReconnectIntervalMs: this.config.reconnectDelay * 16,
1249
+ pingIntervalMs: 15e3
1250
+ // 15 second keepalive pings
1251
+ });
1252
+ this.nostrClient.addConnectionListener({
1253
+ onConnect: (url) => {
1254
+ this.log("NostrClient connected to relay:", url);
1255
+ },
1256
+ onDisconnect: (url, reason) => {
1257
+ this.log("NostrClient disconnected from relay:", url, "reason:", reason);
1258
+ },
1259
+ onReconnecting: (url, attempt) => {
1260
+ this.log("NostrClient reconnecting to relay:", url, "attempt:", attempt);
1261
+ },
1262
+ onReconnected: (url) => {
1263
+ this.log("NostrClient reconnected to relay:", url);
1264
+ }
1265
+ });
1266
+ this.nostrClient.connect(...this.config.relays).then(() => {
1267
+ this.subscribeToEvents();
1268
+ oldClient.disconnect();
1269
+ }).catch((err) => {
1270
+ this.log("Failed to reconnect with new identity:", err);
1271
+ });
1272
+ } else if (this.isConnected()) {
622
1273
  this.subscribeToEvents();
623
1274
  }
624
1275
  }
@@ -634,18 +1285,17 @@ var NostrTransportProvider = class {
634
1285
  }
635
1286
  async sendMessage(recipientPubkey, content) {
636
1287
  this.ensureReady();
637
- const event = await this.createEncryptedEvent(
638
- EVENT_KINDS.DIRECT_MESSAGE,
639
- content,
640
- [["p", recipientPubkey]]
641
- );
642
- await this.publishEvent(event);
1288
+ const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith("02") || recipientPubkey.startsWith("03")) ? recipientPubkey.slice(2) : recipientPubkey;
1289
+ const senderNametag = this.identity?.nametag;
1290
+ const wrappedContent = senderNametag ? JSON.stringify({ senderNametag, text: content }) : content;
1291
+ const giftWrap = import_nostr_js_sdk.NIP17.createGiftWrap(this.keyManager, nostrRecipient, wrappedContent);
1292
+ await this.publishEvent(giftWrap);
643
1293
  this.emitEvent({
644
1294
  type: "message:sent",
645
1295
  timestamp: Date.now(),
646
1296
  data: { recipient: recipientPubkey }
647
1297
  });
648
- return event.id;
1298
+ return giftWrap.id;
649
1299
  }
650
1300
  onMessage(handler) {
651
1301
  this.messageHandlers.add(handler);
@@ -762,6 +1412,118 @@ var NostrTransportProvider = class {
762
1412
  if (pubkeyTag?.[1]) return pubkeyTag[1];
763
1413
  return null;
764
1414
  }
1415
+ async resolveNametagInfo(nametag) {
1416
+ this.ensureReady();
1417
+ const hashedNametag = (0, import_nostr_js_sdk.hashNametag)(nametag);
1418
+ let events = await this.queryEvents({
1419
+ kinds: [EVENT_KINDS.NAMETAG_BINDING],
1420
+ "#t": [hashedNametag],
1421
+ limit: 1
1422
+ });
1423
+ if (events.length === 0) {
1424
+ events = await this.queryEvents({
1425
+ kinds: [EVENT_KINDS.NAMETAG_BINDING],
1426
+ "#d": [hashedNametag],
1427
+ limit: 1
1428
+ });
1429
+ }
1430
+ if (events.length === 0) return null;
1431
+ const bindingEvent = events[0];
1432
+ try {
1433
+ const content = JSON.parse(bindingEvent.content);
1434
+ if (content.public_key && content.l1_address) {
1435
+ const l3Address = `PROXY:${hashedNametag}`;
1436
+ return {
1437
+ nametag,
1438
+ transportPubkey: bindingEvent.pubkey,
1439
+ chainPubkey: content.public_key,
1440
+ l1Address: content.l1_address,
1441
+ directAddress: content.direct_address || "",
1442
+ proxyAddress: l3Address,
1443
+ timestamp: bindingEvent.created_at * 1e3
1444
+ };
1445
+ }
1446
+ this.log("Legacy nametag event without extended fields:", nametag);
1447
+ const pubkeyTag = bindingEvent.tags.find((t) => t[0] === "pubkey");
1448
+ const l1Tag = bindingEvent.tags.find((t) => t[0] === "l1");
1449
+ if (pubkeyTag?.[1] && l1Tag?.[1]) {
1450
+ const l3Address = `PROXY:${hashedNametag}`;
1451
+ return {
1452
+ nametag,
1453
+ transportPubkey: bindingEvent.pubkey,
1454
+ chainPubkey: pubkeyTag[1],
1455
+ l1Address: l1Tag[1],
1456
+ directAddress: "",
1457
+ proxyAddress: l3Address,
1458
+ timestamp: bindingEvent.created_at * 1e3
1459
+ };
1460
+ }
1461
+ return {
1462
+ nametag,
1463
+ transportPubkey: bindingEvent.pubkey,
1464
+ chainPubkey: "",
1465
+ // Cannot derive from 32-byte Nostr pubkey
1466
+ l1Address: "",
1467
+ // Cannot derive without 33-byte pubkey
1468
+ directAddress: "",
1469
+ proxyAddress: `PROXY:${hashedNametag}`,
1470
+ timestamp: bindingEvent.created_at * 1e3
1471
+ };
1472
+ } catch {
1473
+ return {
1474
+ nametag,
1475
+ transportPubkey: bindingEvent.pubkey,
1476
+ chainPubkey: "",
1477
+ l1Address: "",
1478
+ directAddress: "",
1479
+ proxyAddress: `PROXY:${hashedNametag}`,
1480
+ timestamp: bindingEvent.created_at * 1e3
1481
+ };
1482
+ }
1483
+ }
1484
+ /**
1485
+ * Recover nametag for the current identity by searching for encrypted nametag events
1486
+ * Used after wallet import to recover associated nametag
1487
+ * @returns Decrypted nametag or null if none found
1488
+ */
1489
+ async recoverNametag() {
1490
+ this.ensureReady();
1491
+ if (!this.identity || !this.keyManager) {
1492
+ throw new Error("Identity not set");
1493
+ }
1494
+ const nostrPubkey = this.getNostrPubkey();
1495
+ this.log("Searching for nametag events for pubkey:", nostrPubkey.slice(0, 16) + "...");
1496
+ const events = await this.queryEvents({
1497
+ kinds: [EVENT_KINDS.NAMETAG_BINDING],
1498
+ authors: [nostrPubkey],
1499
+ limit: 10
1500
+ // Get recent events in case of updates
1501
+ });
1502
+ if (events.length === 0) {
1503
+ this.log("No nametag events found for this pubkey");
1504
+ return null;
1505
+ }
1506
+ events.sort((a, b) => b.created_at - a.created_at);
1507
+ for (const event of events) {
1508
+ try {
1509
+ const content = JSON.parse(event.content);
1510
+ if (content.encrypted_nametag) {
1511
+ const decrypted = await decryptNametag(
1512
+ content.encrypted_nametag,
1513
+ this.identity.privateKey
1514
+ );
1515
+ if (decrypted) {
1516
+ this.log("Recovered nametag:", decrypted);
1517
+ return decrypted;
1518
+ }
1519
+ }
1520
+ } catch {
1521
+ continue;
1522
+ }
1523
+ }
1524
+ this.log("Could not decrypt nametag from any event");
1525
+ return null;
1526
+ }
765
1527
  async publishNametag(nametag, address) {
766
1528
  this.ensureReady();
767
1529
  const hashedNametag = (0, import_nostr_js_sdk.hashNametag)(nametag);
@@ -772,8 +1534,11 @@ var NostrTransportProvider = class {
772
1534
  await this.publishEvent(event);
773
1535
  this.log("Published nametag binding:", nametag);
774
1536
  }
775
- async registerNametag(nametag, _publicKey) {
1537
+ async registerNametag(nametag, _publicKey, directAddress = "") {
776
1538
  this.ensureReady();
1539
+ if (!this.identity) {
1540
+ throw new Error("Identity not set");
1541
+ }
777
1542
  const nostrPubkey = this.getNostrPubkey();
778
1543
  const existing = await this.resolveNametag(nametag);
779
1544
  this.log("registerNametag:", nametag, "existing:", existing, "myPubkey:", nostrPubkey);
@@ -781,27 +1546,42 @@ var NostrTransportProvider = class {
781
1546
  this.log("Nametag already taken:", nametag, "- owner:", existing);
782
1547
  return false;
783
1548
  }
1549
+ const privateKeyHex = this.identity.privateKey;
1550
+ const compressedPubkey = getPublicKey(privateKeyHex, true);
1551
+ const l1Address = publicKeyToAddress(compressedPubkey, "alpha");
1552
+ const encryptedNametag = await encryptNametag(nametag, privateKeyHex);
784
1553
  const hashedNametag = (0, import_nostr_js_sdk.hashNametag)(nametag);
785
1554
  const content = JSON.stringify({
786
1555
  nametag_hash: hashedNametag,
787
1556
  address: nostrPubkey,
788
- verified: Date.now()
1557
+ verified: Date.now(),
1558
+ // Extended fields for nametag recovery and address lookup
1559
+ encrypted_nametag: encryptedNametag,
1560
+ public_key: compressedPubkey,
1561
+ l1_address: l1Address,
1562
+ direct_address: directAddress
789
1563
  });
790
1564
  const event = await this.createEvent(EVENT_KINDS.NAMETAG_BINDING, content, [
791
1565
  ["d", hashedNametag],
792
1566
  ["nametag", hashedNametag],
793
1567
  ["t", hashedNametag],
794
- ["address", nostrPubkey]
1568
+ ["address", nostrPubkey],
1569
+ // Extended tags for indexing
1570
+ ["pubkey", compressedPubkey],
1571
+ ["l1", l1Address]
795
1572
  ]);
796
1573
  await this.publishEvent(event);
797
- this.log("Registered nametag:", nametag, "for pubkey:", nostrPubkey.slice(0, 16) + "...");
1574
+ this.log("Registered nametag:", nametag, "for pubkey:", nostrPubkey.slice(0, 16) + "...", "l1:", l1Address.slice(0, 12) + "...");
798
1575
  return true;
799
1576
  }
1577
+ // Track broadcast subscriptions
1578
+ broadcastSubscriptions = /* @__PURE__ */ new Map();
1579
+ // key -> subId
800
1580
  subscribeToBroadcast(tags, handler) {
801
1581
  const key = tags.sort().join(":");
802
1582
  if (!this.broadcastHandlers.has(key)) {
803
1583
  this.broadcastHandlers.set(key, /* @__PURE__ */ new Set());
804
- if (this.isConnected()) {
1584
+ if (this.isConnected() && this.nostrClient) {
805
1585
  this.subscribeToTags(tags);
806
1586
  }
807
1587
  }
@@ -810,6 +1590,11 @@ var NostrTransportProvider = class {
810
1590
  this.broadcastHandlers.get(key)?.delete(handler);
811
1591
  if (this.broadcastHandlers.get(key)?.size === 0) {
812
1592
  this.broadcastHandlers.delete(key);
1593
+ const subId = this.broadcastSubscriptions.get(key);
1594
+ if (subId && this.nostrClient) {
1595
+ this.nostrClient.unsubscribe(subId);
1596
+ this.broadcastSubscriptions.delete(key);
1597
+ }
813
1598
  }
814
1599
  };
815
1600
  }
@@ -828,81 +1613,19 @@ var NostrTransportProvider = class {
828
1613
  return () => this.eventCallbacks.delete(callback);
829
1614
  }
830
1615
  // ===========================================================================
831
- // Private: Connection Management
832
- // ===========================================================================
833
- async connectToRelay(url) {
834
- return new Promise((resolve, reject) => {
835
- const ws = this.config.createWebSocket(url);
836
- const timeout = setTimeout(() => {
837
- ws.close();
838
- reject(new Error(`Connection timeout: ${url}`));
839
- }, this.config.timeout);
840
- ws.onopen = () => {
841
- clearTimeout(timeout);
842
- this.connections.set(url, ws);
843
- this.reconnectAttempts.set(url, 0);
844
- this.log("Connected to relay:", url);
845
- resolve();
846
- };
847
- ws.onerror = (error) => {
848
- clearTimeout(timeout);
849
- this.log("Relay error:", url, error);
850
- reject(error);
851
- };
852
- ws.onclose = () => {
853
- this.connections.delete(url);
854
- if (this.config.autoReconnect && this.status === "connected") {
855
- this.scheduleReconnect(url);
856
- }
857
- };
858
- ws.onmessage = (event) => {
859
- this.handleRelayMessage(url, event.data);
860
- };
861
- });
862
- }
863
- scheduleReconnect(url) {
864
- const attempts = this.reconnectAttempts.get(url) ?? 0;
865
- if (attempts >= this.config.maxReconnectAttempts) {
866
- this.log("Max reconnect attempts reached for:", url);
867
- return;
868
- }
869
- this.reconnectAttempts.set(url, attempts + 1);
870
- const delay = this.config.reconnectDelay * Math.pow(2, attempts);
871
- this.emitEvent({ type: "transport:reconnecting", timestamp: Date.now() });
872
- setTimeout(() => {
873
- this.connectToRelay(url).catch(() => {
874
- });
875
- }, delay);
876
- }
877
- // ===========================================================================
878
1616
  // Private: Message Handling
879
1617
  // ===========================================================================
880
- handleRelayMessage(relay, data) {
881
- try {
882
- const message = JSON.parse(data);
883
- const [type, ...args] = message;
884
- switch (type) {
885
- case "EVENT":
886
- this.handleEvent(args[1]);
887
- break;
888
- case "EOSE":
889
- break;
890
- case "OK":
891
- break;
892
- case "NOTICE":
893
- this.log("Relay notice:", relay, args[0]);
894
- break;
895
- }
896
- } catch (error) {
897
- this.log("Failed to parse relay message:", error);
898
- }
899
- }
900
1618
  async handleEvent(event) {
1619
+ this.log("Processing event kind:", event.kind, "id:", event.id?.slice(0, 12));
901
1620
  try {
902
1621
  switch (event.kind) {
903
1622
  case EVENT_KINDS.DIRECT_MESSAGE:
904
1623
  await this.handleDirectMessage(event);
905
1624
  break;
1625
+ case import_nostr_js_sdk.EventKinds.GIFT_WRAP:
1626
+ this.log("Handling gift wrap (NIP-17 DM)");
1627
+ await this.handleGiftWrap(event);
1628
+ break;
906
1629
  case EVENT_KINDS.TOKEN_TRANSFER:
907
1630
  await this.handleTokenTransfer(event);
908
1631
  break;
@@ -921,23 +1644,54 @@ var NostrTransportProvider = class {
921
1644
  }
922
1645
  }
923
1646
  async handleDirectMessage(event) {
924
- if (!this.identity || !this.keyManager) return;
925
- if (event.pubkey === this.keyManager.getPublicKeyHex()) return;
926
- const content = await this.decryptContent(event.content, event.pubkey);
927
- const message = {
928
- id: event.id,
929
- senderPubkey: event.pubkey,
930
- content,
931
- timestamp: event.created_at * 1e3,
932
- encrypted: true
933
- };
934
- this.emitEvent({ type: "message:received", timestamp: Date.now() });
935
- for (const handler of this.messageHandlers) {
1647
+ this.log("Ignoring NIP-04 kind 4 event (DMs use NIP-17):", event.id?.slice(0, 12));
1648
+ }
1649
+ async handleGiftWrap(event) {
1650
+ if (!this.identity || !this.keyManager) {
1651
+ this.log("handleGiftWrap: no identity/keyManager");
1652
+ return;
1653
+ }
1654
+ try {
1655
+ const pm = import_nostr_js_sdk.NIP17.unwrap(event, this.keyManager);
1656
+ this.log("Gift wrap unwrapped, sender:", pm.senderPubkey?.slice(0, 16), "kind:", pm.kind);
1657
+ if (pm.senderPubkey === this.keyManager.getPublicKeyHex()) {
1658
+ this.log("Skipping own message");
1659
+ return;
1660
+ }
1661
+ if (pm.kind !== import_nostr_js_sdk.EventKinds.CHAT_MESSAGE) {
1662
+ this.log("Skipping non-chat message, kind:", pm.kind);
1663
+ return;
1664
+ }
1665
+ let content = pm.content;
1666
+ let senderNametag;
936
1667
  try {
937
- handler(message);
938
- } catch (error) {
939
- this.log("Message handler error:", error);
1668
+ const parsed = JSON.parse(content);
1669
+ if (typeof parsed === "object" && parsed.text !== void 0) {
1670
+ content = parsed.text;
1671
+ senderNametag = parsed.senderNametag || void 0;
1672
+ }
1673
+ } catch {
1674
+ }
1675
+ this.log("DM received from:", senderNametag || pm.senderPubkey?.slice(0, 16), "content:", content?.slice(0, 50));
1676
+ const message = {
1677
+ id: pm.eventId,
1678
+ senderTransportPubkey: pm.senderPubkey,
1679
+ senderNametag,
1680
+ content,
1681
+ timestamp: pm.timestamp * 1e3,
1682
+ encrypted: true
1683
+ };
1684
+ this.emitEvent({ type: "message:received", timestamp: Date.now() });
1685
+ this.log("Dispatching to", this.messageHandlers.size, "handlers");
1686
+ for (const handler of this.messageHandlers) {
1687
+ try {
1688
+ handler(message);
1689
+ } catch (error) {
1690
+ this.log("Message handler error:", error);
1691
+ }
940
1692
  }
1693
+ } catch (err) {
1694
+ this.log("Gift wrap decrypt failed (expected if not for us):", err?.message?.slice(0, 50));
941
1695
  }
942
1696
  }
943
1697
  async handleTokenTransfer(event) {
@@ -946,7 +1700,7 @@ var NostrTransportProvider = class {
946
1700
  const payload = JSON.parse(content);
947
1701
  const transfer = {
948
1702
  id: event.id,
949
- senderPubkey: event.pubkey,
1703
+ senderTransportPubkey: event.pubkey,
950
1704
  payload,
951
1705
  timestamp: event.created_at * 1e3
952
1706
  };
@@ -966,7 +1720,7 @@ var NostrTransportProvider = class {
966
1720
  const requestData = JSON.parse(content);
967
1721
  const request = {
968
1722
  id: event.id,
969
- senderPubkey: event.pubkey,
1723
+ senderTransportPubkey: event.pubkey,
970
1724
  request: {
971
1725
  requestId: requestData.requestId,
972
1726
  amount: requestData.amount,
@@ -996,7 +1750,7 @@ var NostrTransportProvider = class {
996
1750
  const responseData = JSON.parse(content);
997
1751
  const response = {
998
1752
  id: event.id,
999
- responderPubkey: event.pubkey,
1753
+ responderTransportPubkey: event.pubkey,
1000
1754
  response: {
1001
1755
  requestId: responseData.requestId,
1002
1756
  responseType: responseData.responseType,
@@ -1021,7 +1775,7 @@ var NostrTransportProvider = class {
1021
1775
  const tags = event.tags.filter((t) => t[0] === "t").map((t) => t[1]);
1022
1776
  const broadcast = {
1023
1777
  id: event.id,
1024
- authorPubkey: event.pubkey,
1778
+ authorTransportPubkey: event.pubkey,
1025
1779
  content: event.content,
1026
1780
  tags,
1027
1781
  timestamp: event.created_at * 1e3
@@ -1076,109 +1830,149 @@ var NostrTransportProvider = class {
1076
1830
  return this.createEvent(kind, encrypted, tags);
1077
1831
  }
1078
1832
  async publishEvent(event) {
1079
- const message = JSON.stringify(["EVENT", event]);
1080
- const publishPromises = Array.from(this.connections.values()).map((ws) => {
1081
- return new Promise((resolve, reject) => {
1082
- if (ws.readyState !== WebSocketReadyState.OPEN) {
1083
- reject(new Error("WebSocket not open"));
1084
- return;
1085
- }
1086
- ws.send(message);
1087
- resolve();
1088
- });
1089
- });
1090
- await Promise.any(publishPromises);
1833
+ if (!this.nostrClient) {
1834
+ throw new Error("NostrClient not initialized");
1835
+ }
1836
+ const sdkEvent = import_nostr_js_sdk.Event.fromJSON(event);
1837
+ await this.nostrClient.publishEvent(sdkEvent);
1091
1838
  }
1092
- async queryEvents(filter) {
1093
- if (this.connections.size === 0) {
1839
+ async queryEvents(filterObj) {
1840
+ if (!this.nostrClient || !this.nostrClient.isConnected()) {
1094
1841
  throw new Error("No connected relays");
1095
1842
  }
1096
- const queryPromises = Array.from(this.connections.values()).map(
1097
- (ws) => this.queryEventsFromRelay(ws, filter)
1098
- );
1099
- const results = await Promise.allSettled(queryPromises);
1100
- for (const result of results) {
1101
- if (result.status === "fulfilled" && result.value.length > 0) {
1102
- return result.value;
1103
- }
1104
- }
1105
- return [];
1106
- }
1107
- async queryEventsFromRelay(ws, filter) {
1108
- const subId = this.config.generateUUID().slice(0, 8);
1109
1843
  const events = [];
1844
+ const filter = new import_nostr_js_sdk.Filter(filterObj);
1110
1845
  return new Promise((resolve) => {
1111
1846
  const timeout = setTimeout(() => {
1112
- this.unsubscribeFromRelay(ws, subId);
1847
+ if (subId) {
1848
+ this.nostrClient?.unsubscribe(subId);
1849
+ }
1113
1850
  resolve(events);
1114
1851
  }, 5e3);
1115
- const originalHandler = ws.onmessage;
1116
- ws.onmessage = (event) => {
1117
- const message = JSON.parse(event.data);
1118
- const [type, sid, data] = message;
1119
- if (sid !== subId) {
1120
- originalHandler?.call(ws, event);
1121
- return;
1122
- }
1123
- if (type === "EVENT") {
1124
- events.push(data);
1125
- } else if (type === "EOSE") {
1852
+ const subId = this.nostrClient.subscribe(filter, {
1853
+ onEvent: (event) => {
1854
+ events.push({
1855
+ id: event.id,
1856
+ kind: event.kind,
1857
+ content: event.content,
1858
+ tags: event.tags,
1859
+ pubkey: event.pubkey,
1860
+ created_at: event.created_at,
1861
+ sig: event.sig
1862
+ });
1863
+ },
1864
+ onEndOfStoredEvents: () => {
1126
1865
  clearTimeout(timeout);
1127
- ws.onmessage = originalHandler;
1128
- this.unsubscribeFromRelay(ws, subId);
1866
+ this.nostrClient?.unsubscribe(subId);
1129
1867
  resolve(events);
1130
1868
  }
1131
- };
1132
- ws.send(JSON.stringify(["REQ", subId, filter]));
1869
+ });
1133
1870
  });
1134
1871
  }
1135
- unsubscribeFromRelay(ws, subId) {
1136
- if (ws.readyState === WebSocketReadyState.OPEN) {
1137
- ws.send(JSON.stringify(["CLOSE", subId]));
1138
- }
1139
- }
1140
1872
  // ===========================================================================
1141
1873
  // Private: Subscriptions
1142
1874
  // ===========================================================================
1875
+ // Track subscription IDs for cleanup
1876
+ walletSubscriptionId = null;
1877
+ chatSubscriptionId = null;
1143
1878
  subscribeToEvents() {
1144
- if (!this.identity || !this.keyManager) return;
1145
- const subId = "main";
1879
+ this.log("subscribeToEvents called, identity:", !!this.identity, "keyManager:", !!this.keyManager, "nostrClient:", !!this.nostrClient);
1880
+ if (!this.identity || !this.keyManager || !this.nostrClient) {
1881
+ this.log("subscribeToEvents: skipped - no identity, keyManager, or nostrClient");
1882
+ return;
1883
+ }
1884
+ if (this.walletSubscriptionId) {
1885
+ this.nostrClient.unsubscribe(this.walletSubscriptionId);
1886
+ this.walletSubscriptionId = null;
1887
+ }
1888
+ if (this.chatSubscriptionId) {
1889
+ this.nostrClient.unsubscribe(this.chatSubscriptionId);
1890
+ this.chatSubscriptionId = null;
1891
+ }
1892
+ if (this.mainSubscriptionId) {
1893
+ this.nostrClient.unsubscribe(this.mainSubscriptionId);
1894
+ this.mainSubscriptionId = null;
1895
+ }
1146
1896
  const nostrPubkey = this.keyManager.getPublicKeyHex();
1147
- const filter = {
1148
- kinds: [
1149
- EVENT_KINDS.DIRECT_MESSAGE,
1150
- EVENT_KINDS.TOKEN_TRANSFER,
1151
- EVENT_KINDS.PAYMENT_REQUEST,
1152
- EVENT_KINDS.PAYMENT_REQUEST_RESPONSE
1153
- ],
1154
- "#p": [nostrPubkey],
1155
- since: Math.floor(Date.now() / 1e3) - 86400
1156
- // Last 24h
1157
- };
1158
- const message = JSON.stringify(["REQ", subId, filter]);
1159
- for (const ws of this.connections.values()) {
1160
- if (ws.readyState === WebSocketReadyState.OPEN) {
1161
- ws.send(message);
1897
+ this.log("Subscribing with Nostr pubkey:", nostrPubkey);
1898
+ const walletFilter = new import_nostr_js_sdk.Filter();
1899
+ walletFilter.kinds = [
1900
+ EVENT_KINDS.DIRECT_MESSAGE,
1901
+ EVENT_KINDS.TOKEN_TRANSFER,
1902
+ EVENT_KINDS.PAYMENT_REQUEST,
1903
+ EVENT_KINDS.PAYMENT_REQUEST_RESPONSE
1904
+ ];
1905
+ walletFilter["#p"] = [nostrPubkey];
1906
+ walletFilter.since = Math.floor(Date.now() / 1e3) - 86400;
1907
+ this.walletSubscriptionId = this.nostrClient.subscribe(walletFilter, {
1908
+ onEvent: (event) => {
1909
+ this.log("Received wallet event kind:", event.kind, "id:", event.id?.slice(0, 12));
1910
+ this.handleEvent({
1911
+ id: event.id,
1912
+ kind: event.kind,
1913
+ content: event.content,
1914
+ tags: event.tags,
1915
+ pubkey: event.pubkey,
1916
+ created_at: event.created_at,
1917
+ sig: event.sig
1918
+ });
1919
+ },
1920
+ onEndOfStoredEvents: () => {
1921
+ this.log("Wallet subscription ready (EOSE)");
1922
+ },
1923
+ onError: (_subId, error) => {
1924
+ this.log("Wallet subscription error:", error);
1162
1925
  }
1163
- }
1164
- this.subscriptions.set(subId, Array.from(this.connections.keys()));
1165
- this.log("Subscribed to events");
1926
+ });
1927
+ this.log("Wallet subscription created, subId:", this.walletSubscriptionId);
1928
+ const chatFilter = new import_nostr_js_sdk.Filter();
1929
+ chatFilter.kinds = [import_nostr_js_sdk.EventKinds.GIFT_WRAP];
1930
+ chatFilter["#p"] = [nostrPubkey];
1931
+ this.chatSubscriptionId = this.nostrClient.subscribe(chatFilter, {
1932
+ onEvent: (event) => {
1933
+ this.log("Received chat event kind:", event.kind, "id:", event.id?.slice(0, 12));
1934
+ this.handleEvent({
1935
+ id: event.id,
1936
+ kind: event.kind,
1937
+ content: event.content,
1938
+ tags: event.tags,
1939
+ pubkey: event.pubkey,
1940
+ created_at: event.created_at,
1941
+ sig: event.sig
1942
+ });
1943
+ },
1944
+ onEndOfStoredEvents: () => {
1945
+ this.log("Chat subscription ready (EOSE)");
1946
+ },
1947
+ onError: (_subId, error) => {
1948
+ this.log("Chat subscription error:", error);
1949
+ }
1950
+ });
1951
+ this.log("Chat subscription created, subId:", this.chatSubscriptionId);
1166
1952
  }
1167
1953
  subscribeToTags(tags) {
1168
- const subId = `tags:${tags.join(":")}`;
1169
- const filter = {
1954
+ if (!this.nostrClient) return;
1955
+ const key = tags.sort().join(":");
1956
+ const filter = new import_nostr_js_sdk.Filter({
1170
1957
  kinds: [EVENT_KINDS.BROADCAST],
1171
1958
  "#t": tags,
1172
1959
  since: Math.floor(Date.now() / 1e3) - 3600
1173
1960
  // Last hour
1174
- };
1175
- const message = JSON.stringify(["REQ", subId, filter]);
1176
- for (const ws of this.connections.values()) {
1177
- if (ws.readyState === WebSocketReadyState.OPEN) {
1178
- ws.send(message);
1961
+ });
1962
+ const subId = this.nostrClient.subscribe(filter, {
1963
+ onEvent: (event) => {
1964
+ this.handleBroadcast({
1965
+ id: event.id,
1966
+ kind: event.kind,
1967
+ content: event.content,
1968
+ tags: event.tags,
1969
+ pubkey: event.pubkey,
1970
+ created_at: event.created_at,
1971
+ sig: event.sig
1972
+ });
1179
1973
  }
1180
- }
1181
- this.subscriptions.set(subId, Array.from(this.connections.keys()));
1974
+ });
1975
+ this.broadcastSubscriptions.set(key, subId);
1182
1976
  }
1183
1977
  // ===========================================================================
1184
1978
  // Private: Encryption
@@ -1639,13 +2433,72 @@ var UnicityAggregatorProvider = class {
1639
2433
  };
1640
2434
  var UnicityOracleProvider = UnicityAggregatorProvider;
1641
2435
 
2436
+ // assets/trustbase.ts
2437
+ var TRUSTBASE_TESTNET = {
2438
+ version: 1,
2439
+ networkId: 3,
2440
+ epoch: 1,
2441
+ epochStartRound: 1,
2442
+ rootNodes: [
2443
+ {
2444
+ nodeId: "16Uiu2HAkyQRiA7pMgzgLj9GgaBJEJa8zmx9dzqUDa6WxQPJ82ghU",
2445
+ sigKey: "0x039afb2acb65f5fbc272d8907f763d0a5d189aadc9b97afdcc5897ea4dd112e68b",
2446
+ stake: 1
2447
+ }
2448
+ ],
2449
+ quorumThreshold: 1,
2450
+ stateHash: "",
2451
+ changeRecordHash: "",
2452
+ previousEntryHash: "",
2453
+ signatures: {
2454
+ "16Uiu2HAkyQRiA7pMgzgLj9GgaBJEJa8zmx9dzqUDa6WxQPJ82ghU": "0xf157c9fdd8a378e3ca70d354ccc4475ab2cd8de360127bc46b0aeab4b453a80f07fd9136a5843b60a8babaff23e20acc8879861f7651440a5e2829f7541b31f100"
2455
+ }
2456
+ };
2457
+ var TRUSTBASE_MAINNET = null;
2458
+ var TRUSTBASE_DEV = TRUSTBASE_TESTNET;
2459
+
2460
+ // impl/shared/trustbase-loader.ts
2461
+ function getEmbeddedTrustBase(network) {
2462
+ switch (network) {
2463
+ case "mainnet":
2464
+ return TRUSTBASE_MAINNET;
2465
+ case "testnet":
2466
+ return TRUSTBASE_TESTNET;
2467
+ case "dev":
2468
+ return TRUSTBASE_DEV;
2469
+ default:
2470
+ return TRUSTBASE_TESTNET;
2471
+ }
2472
+ }
2473
+ var BaseTrustBaseLoader = class {
2474
+ network;
2475
+ constructor(network = "testnet") {
2476
+ this.network = network;
2477
+ }
2478
+ async load() {
2479
+ const external = await this.loadFromExternal();
2480
+ if (external) {
2481
+ return external;
2482
+ }
2483
+ return getEmbeddedTrustBase(this.network);
2484
+ }
2485
+ };
2486
+
1642
2487
  // impl/nodejs/oracle/index.ts
1643
- var NodeTrustBaseLoader = class {
2488
+ var NodeTrustBaseLoader = class extends BaseTrustBaseLoader {
1644
2489
  filePath;
1645
- constructor(filePath = "./trustbase-testnet.json") {
1646
- this.filePath = filePath;
2490
+ constructor(filePathOrNetwork) {
2491
+ if (!filePathOrNetwork) {
2492
+ super("testnet");
2493
+ } else if (filePathOrNetwork.includes("/") || filePathOrNetwork.includes(".")) {
2494
+ super("testnet");
2495
+ this.filePath = filePathOrNetwork;
2496
+ } else {
2497
+ super(filePathOrNetwork);
2498
+ }
1647
2499
  }
1648
- async load() {
2500
+ async loadFromExternal() {
2501
+ if (!this.filePath) return null;
1649
2502
  try {
1650
2503
  if (fs3.existsSync(this.filePath)) {
1651
2504
  const content = fs3.readFileSync(this.filePath, "utf-8");
@@ -1656,14 +2509,14 @@ var NodeTrustBaseLoader = class {
1656
2509
  return null;
1657
2510
  }
1658
2511
  };
1659
- function createNodeTrustBaseLoader(filePath) {
1660
- return new NodeTrustBaseLoader(filePath);
2512
+ function createNodeTrustBaseLoader(filePathOrNetwork) {
2513
+ return new NodeTrustBaseLoader(filePathOrNetwork);
1661
2514
  }
1662
2515
  function createUnicityAggregatorProvider(config) {
1663
- const { trustBasePath, ...restConfig } = config;
2516
+ const { trustBasePath, network, ...restConfig } = config;
1664
2517
  return new UnicityAggregatorProvider({
1665
2518
  ...restConfig,
1666
- trustBaseLoader: createNodeTrustBaseLoader(trustBasePath)
2519
+ trustBaseLoader: createNodeTrustBaseLoader(trustBasePath ?? network ?? "testnet")
1667
2520
  });
1668
2521
  }
1669
2522
  var createUnicityOracleProvider = createUnicityAggregatorProvider;
@@ -1742,7 +2595,8 @@ function createNodeProviders(config) {
1742
2595
  timeout: oracleConfig.timeout,
1743
2596
  trustBasePath: oracleConfig.trustBasePath,
1744
2597
  skipVerification: oracleConfig.skipVerification,
1745
- debug: oracleConfig.debug
2598
+ debug: oracleConfig.debug,
2599
+ network
1746
2600
  }),
1747
2601
  l1: l1Config
1748
2602
  };
@@ -1764,4 +2618,9 @@ function createNodeProviders(config) {
1764
2618
  createUnicityAggregatorProvider,
1765
2619
  createUnicityOracleProvider
1766
2620
  });
2621
+ /*! Bundled license information:
2622
+
2623
+ @noble/hashes/utils.js:
2624
+ (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
2625
+ */
1767
2626
  //# sourceMappingURL=index.cjs.map