holosphere 2.0.0-alpha4 → 2.0.0-alpha6

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.
Files changed (35) hide show
  1. package/dist/cjs/holosphere.cjs +1 -1
  2. package/dist/esm/holosphere.js +1 -1
  3. package/dist/index-BtKHqqet.cjs +5 -0
  4. package/dist/index-BtKHqqet.cjs.map +1 -0
  5. package/dist/{index-CBitK71M.cjs → index-CmzkI7SI.cjs} +2 -2
  6. package/dist/{index-CBitK71M.cjs.map → index-CmzkI7SI.cjs.map} +1 -1
  7. package/dist/{index-Cz-PLCUR.js → index-JFz-dW43.js} +2 -2
  8. package/dist/{index-Cz-PLCUR.js.map → index-JFz-dW43.js.map} +1 -1
  9. package/dist/{index-CV0eOogK.js → index-NOravBLu.js} +733 -164
  10. package/dist/index-NOravBLu.js.map +1 -0
  11. package/dist/{indexeddb-storage-CRsZyB2f.cjs → indexeddb-storage-C4HsulhA.cjs} +2 -2
  12. package/dist/{indexeddb-storage-CRsZyB2f.cjs.map → indexeddb-storage-C4HsulhA.cjs.map} +1 -1
  13. package/dist/{indexeddb-storage-DZaGlY_a.js → indexeddb-storage-OtSAVDZY.js} +2 -2
  14. package/dist/{indexeddb-storage-DZaGlY_a.js.map → indexeddb-storage-OtSAVDZY.js.map} +1 -1
  15. package/dist/{memory-storage-BkUi6sZG.js → memory-storage-ChpcYvxA.js} +2 -2
  16. package/dist/{memory-storage-BkUi6sZG.js.map → memory-storage-ChpcYvxA.js.map} +1 -1
  17. package/dist/{memory-storage-C0DuUsdY.cjs → memory-storage-MD6ED00P.cjs} +2 -2
  18. package/dist/{memory-storage-C0DuUsdY.cjs.map → memory-storage-MD6ED00P.cjs.map} +1 -1
  19. package/dist/{secp256k1-0kPdAVkK.cjs → secp256k1-DcTYQrqC.cjs} +2 -2
  20. package/dist/{secp256k1-0kPdAVkK.cjs.map → secp256k1-DcTYQrqC.cjs.map} +1 -1
  21. package/dist/{secp256k1-DN4FVXcv.js → secp256k1-PfNOEI7a.js} +2 -2
  22. package/dist/{secp256k1-DN4FVXcv.js.map → secp256k1-PfNOEI7a.js.map} +1 -1
  23. package/package.json +1 -1
  24. package/src/contracts/abis/Bundle.json +1438 -1435
  25. package/src/contracts/deployer.js +32 -3
  26. package/src/federation/handshake.js +13 -5
  27. package/src/index.js +9 -1
  28. package/src/storage/gun-async.js +55 -6
  29. package/src/storage/gun-auth.js +81 -30
  30. package/src/storage/gun-wrapper.js +56 -48
  31. package/src/storage/nostr-async.js +149 -14
  32. package/src/storage/nostr-client.js +574 -48
  33. package/dist/index-BB_vVJgv.cjs +0 -5
  34. package/dist/index-BB_vVJgv.cjs.map +0 -1
  35. package/dist/index-CV0eOogK.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { getPublicKey as getPublicKey$2, SimplePool, finalizeEvent, nip19, nip04, verifyEvent as verifyEvent$1 } from "nostr-tools";
1
+ import { getPublicKey as getPublicKey$2, finalizeEvent, SimplePool, nip19, nip04, verifyEvent as verifyEvent$1 } from "nostr-tools";
2
2
  import * as h3 from "h3-js";
3
3
  import { latLngToCell, getResolution, cellToParent, cellToChildren, isValidCell } from "h3-js";
4
4
  import Ajv from "ajv";
@@ -361,17 +361,84 @@ async function createPersistentStorage(namespace, options = {}) {
361
361
  const isBrowser = typeof window !== "undefined" && typeof window.indexedDB !== "undefined";
362
362
  typeof process !== "undefined" && process.versions && void 0;
363
363
  if (isBrowser) {
364
- const { IndexedDBStorage } = await import("./indexeddb-storage-DZaGlY_a.js");
364
+ const { IndexedDBStorage } = await import("./indexeddb-storage-OtSAVDZY.js");
365
365
  const storage = new IndexedDBStorage();
366
366
  await storage.init(namespace);
367
367
  return storage;
368
368
  } else {
369
- const { MemoryStorage } = await import("./memory-storage-BkUi6sZG.js");
369
+ const { MemoryStorage } = await import("./memory-storage-ChpcYvxA.js");
370
370
  const storage = new MemoryStorage();
371
371
  await storage.init(namespace);
372
372
  return storage;
373
373
  }
374
374
  }
375
+ let globalPool = null;
376
+ let globalPoolRelays = /* @__PURE__ */ new Set();
377
+ function getGlobalPool(config = {}) {
378
+ if (!globalPool) {
379
+ globalPool = new SimplePool({
380
+ enableReconnect: config.enableReconnect !== false,
381
+ enablePing: config.enablePing !== false
382
+ });
383
+ }
384
+ return globalPool;
385
+ }
386
+ const pendingQueries$1 = /* @__PURE__ */ new Map();
387
+ const PENDING_QUERY_TIMEOUT = 5e3;
388
+ const activeSubscriptions = /* @__PURE__ */ new Map();
389
+ const backgroundRefreshThrottle = /* @__PURE__ */ new Map();
390
+ const BACKGROUND_REFRESH_INTERVAL = 3e4;
391
+ const pendingWrites = /* @__PURE__ */ new Map();
392
+ const WRITE_DEBOUNCE_MS = 500;
393
+ const authorSubscriptions = /* @__PURE__ */ new Map();
394
+ const AUTHOR_SUB_INIT_TIMEOUT = 5e3;
395
+ class LRUCache {
396
+ constructor(maxSize = 500) {
397
+ this.maxSize = maxSize;
398
+ this.cache = /* @__PURE__ */ new Map();
399
+ }
400
+ get(key) {
401
+ if (!this.cache.has(key)) return void 0;
402
+ const value = this.cache.get(key);
403
+ this.cache.delete(key);
404
+ this.cache.set(key, value);
405
+ return value;
406
+ }
407
+ set(key, value) {
408
+ if (this.cache.has(key)) {
409
+ this.cache.delete(key);
410
+ }
411
+ this.cache.set(key, value);
412
+ while (this.cache.size > this.maxSize) {
413
+ const oldestKey = this.cache.keys().next().value;
414
+ this.cache.delete(oldestKey);
415
+ }
416
+ }
417
+ has(key) {
418
+ return this.cache.has(key);
419
+ }
420
+ delete(key) {
421
+ return this.cache.delete(key);
422
+ }
423
+ clear() {
424
+ this.cache.clear();
425
+ }
426
+ get size() {
427
+ return this.cache.size;
428
+ }
429
+ keys() {
430
+ return this.cache.keys();
431
+ }
432
+ values() {
433
+ return this.cache.values();
434
+ }
435
+ entries() {
436
+ return this.cache.entries();
437
+ }
438
+ forEach(callback) {
439
+ this.cache.forEach(callback);
440
+ }
441
+ }
375
442
  let webSocketPolyfillPromise = null;
376
443
  function ensureWebSocket() {
377
444
  if (typeof globalThis.WebSocket !== "undefined") {
@@ -407,8 +474,12 @@ class NostrClient {
407
474
  this.publicKey = getPublicKey$2(this.privateKey);
408
475
  this.config = config;
409
476
  this._subscriptions = /* @__PURE__ */ new Map();
410
- this._eventCache = /* @__PURE__ */ new Map();
477
+ this._eventCache = new LRUCache(config.cacheSize || 500);
478
+ this._cacheIndex = /* @__PURE__ */ new Map();
411
479
  this.persistentStorage = null;
480
+ this._persistQueue = /* @__PURE__ */ new Map();
481
+ this._persistTimer = null;
482
+ this._persistBatchMs = config.persistBatchMs || 100;
412
483
  this._initReady = this._initialize();
413
484
  }
414
485
  /**
@@ -418,10 +489,8 @@ class NostrClient {
418
489
  async _initialize() {
419
490
  await ensureWebSocket();
420
491
  if (this.relays.length > 0) {
421
- this.pool = new SimplePool({
422
- enableReconnect: this.config.enableReconnect !== false,
423
- enablePing: this.config.enablePing !== false
424
- });
492
+ this.pool = getGlobalPool(this.config);
493
+ this.relays.forEach((r) => globalPoolRelays.add(r));
425
494
  } else {
426
495
  this.pool = {
427
496
  publish: (relays, event) => [Promise.resolve()],
@@ -433,6 +502,69 @@ class NostrClient {
433
502
  };
434
503
  }
435
504
  await this._initPersistentStorage();
505
+ if (this.relays.length > 0) {
506
+ this._initLongLivedSubscription();
507
+ }
508
+ }
509
+ /**
510
+ * Initialize a long-lived subscription to keep cache fresh
511
+ * This replaces polling with a single persistent subscription
512
+ * @private
513
+ */
514
+ _initLongLivedSubscription() {
515
+ const subKey = this.publicKey;
516
+ if (authorSubscriptions.has(subKey)) {
517
+ return;
518
+ }
519
+ const subInfo = {
520
+ subscription: null,
521
+ initialized: false,
522
+ initPromise: null,
523
+ initResolve: null
524
+ };
525
+ subInfo.initPromise = new Promise((resolve) => {
526
+ subInfo.initResolve = resolve;
527
+ });
528
+ authorSubscriptions.set(subKey, subInfo);
529
+ const filter = {
530
+ kinds: [3e4],
531
+ authors: [this.publicKey]
532
+ };
533
+ const sub = this.pool.subscribeMany(
534
+ this.relays,
535
+ [filter],
536
+ {
537
+ onevent: (event) => {
538
+ if (event.pubkey !== this.publicKey) {
539
+ return;
540
+ }
541
+ this._cacheEvent(event);
542
+ },
543
+ oneose: () => {
544
+ if (!subInfo.initialized) {
545
+ subInfo.initialized = true;
546
+ subInfo.initResolve();
547
+ }
548
+ }
549
+ }
550
+ );
551
+ subInfo.subscription = sub;
552
+ setTimeout(() => {
553
+ if (!subInfo.initialized) {
554
+ subInfo.initialized = true;
555
+ subInfo.initResolve();
556
+ }
557
+ }, AUTHOR_SUB_INIT_TIMEOUT);
558
+ }
559
+ /**
560
+ * Wait for long-lived subscription to complete initial load
561
+ * @private
562
+ */
563
+ async _waitForSubscriptionInit() {
564
+ const subInfo = authorSubscriptions.get(this.publicKey);
565
+ if (subInfo && subInfo.initPromise) {
566
+ await subInfo.initPromise;
567
+ }
436
568
  }
437
569
  /**
438
570
  * Initialize persistent storage
@@ -542,15 +674,63 @@ class NostrClient {
542
674
  }
543
675
  /**
544
676
  * Publish event to relays
677
+ * Supports debouncing for replaceable events (kind 30000-39999) to avoid rapid updates
545
678
  * @param {Object} event - Unsigned event object
546
679
  * @param {Object} options - Publish options
547
680
  * @param {boolean} options.waitForRelays - Wait for relay confirmation (default: false for speed)
681
+ * @param {boolean} options.debounce - Debounce rapid writes to same d-tag (default: true for replaceable events)
548
682
  * @returns {Promise<Object>} Signed event with relay publish results
549
683
  */
550
684
  async publish(event, options = {}) {
551
685
  await this._initReady;
552
686
  const waitForRelays = options.waitForRelays || false;
553
- const signedEvent = finalizeEvent(event, this.privateKey);
687
+ const isReplaceable = event.kind >= 3e4 && event.kind < 4e4;
688
+ const shouldDebounce = isReplaceable && options.debounce !== false && !waitForRelays;
689
+ if (shouldDebounce) {
690
+ const dTag = event.tags?.find((t) => t[0] === "d");
691
+ if (dTag && dTag[1]) {
692
+ return this._debouncedPublish(event, dTag[1], options);
693
+ }
694
+ }
695
+ return this._doPublish(event, options);
696
+ }
697
+ /**
698
+ * Debounced publish - coalesces rapid writes to the same d-tag
699
+ * @private
700
+ */
701
+ _debouncedPublish(event, dTagPath, options) {
702
+ return new Promise((resolve, reject) => {
703
+ const existing = pendingWrites.get(dTagPath);
704
+ if (existing) {
705
+ clearTimeout(existing.timer);
706
+ existing.resolve({
707
+ event: null,
708
+ results: [],
709
+ debounced: true,
710
+ supersededBy: event
711
+ });
712
+ }
713
+ const timer = setTimeout(async () => {
714
+ pendingWrites.delete(dTagPath);
715
+ try {
716
+ const result = await this._doPublish(event, options);
717
+ resolve(result);
718
+ } catch (err) {
719
+ reject(err);
720
+ }
721
+ }, WRITE_DEBOUNCE_MS);
722
+ pendingWrites.set(dTagPath, { event, timer, resolve, reject });
723
+ const signedEvent = finalizeEvent(event, this.privateKey);
724
+ this._cacheEvent(signedEvent);
725
+ });
726
+ }
727
+ /**
728
+ * Internal publish implementation
729
+ * @private
730
+ */
731
+ async _doPublish(event, options = {}) {
732
+ const waitForRelays = options.waitForRelays || false;
733
+ const signedEvent = event.id && event.sig ? event : finalizeEvent(event, this.privateKey);
554
734
  await this._cacheEvent(signedEvent);
555
735
  if (this.outboxQueue) {
556
736
  await this.outboxQueue.enqueue(signedEvent, this.relays);
@@ -613,20 +793,39 @@ class NostrClient {
613
793
  }
614
794
  /**
615
795
  * Query events from relays
796
+ * Uses long-lived subscription for cache updates - avoids polling
616
797
  * @param {Object} filter - Nostr filter object
617
798
  * @param {Object} options - Query options
618
799
  * @param {number} options.timeout - Query timeout in ms (default: 30000, set to 0 for no timeout)
619
800
  * @param {boolean} options.localFirst - Return local cache immediately, refresh in background (default: true)
801
+ * @param {boolean} options.forceRelay - Force relay query even if subscription cache is available (default: false)
620
802
  * @returns {Promise<Array>} Array of events
621
803
  */
622
804
  async query(filter, options = {}) {
623
805
  await this._initReady;
624
806
  const timeout = options.timeout !== void 0 ? options.timeout : 3e4;
625
807
  const localFirst = options.localFirst !== false;
808
+ const forceRelay = options.forceRelay === true;
626
809
  if (this.relays.length === 0) {
627
810
  const matchingEvents = this._getMatchingCachedEvents(filter);
628
811
  return matchingEvents;
629
812
  }
813
+ const subInfo = authorSubscriptions.get(this.publicKey);
814
+ const isOwnDataQuery = filter.authors && filter.authors.length === 1 && filter.authors[0] === this.publicKey;
815
+ if (!forceRelay && isOwnDataQuery && subInfo && subInfo.initialized) {
816
+ const matchingEvents = this._getMatchingCachedEvents(filter);
817
+ return matchingEvents;
818
+ }
819
+ if (isOwnDataQuery && subInfo && !subInfo.initialized) {
820
+ await Promise.race([
821
+ subInfo.initPromise,
822
+ new Promise((resolve) => setTimeout(resolve, Math.min(timeout, AUTHOR_SUB_INIT_TIMEOUT)))
823
+ ]);
824
+ if (subInfo.initialized) {
825
+ const matchingEvents = this._getMatchingCachedEvents(filter);
826
+ return matchingEvents;
827
+ }
828
+ }
630
829
  if (filter["#d"] && filter["#d"].length === 1 && filter.kinds && filter.kinds.length === 1) {
631
830
  const dTagKey = `d:${filter.kinds[0]}:${filter["#d"][0]}`;
632
831
  const dTagCached = this._eventCache.get(dTagKey);
@@ -647,22 +846,78 @@ class NostrClient {
647
846
  }
648
847
  /**
649
848
  * Query relays and update cache
849
+ * Uses global pending queries map to deduplicate identical concurrent queries
650
850
  * @private
651
851
  */
652
852
  async _queryRelaysAndCache(filter, cacheKey, timeout) {
653
- let events = await this.pool.querySync(this.relays, filter, { timeout });
654
- if (filter.authors && filter.authors.length > 0) {
655
- events = events.filter((event) => filter.authors.includes(event.pubkey));
853
+ const pending = pendingQueries$1.get(cacheKey);
854
+ if (pending && Date.now() - pending.timestamp < PENDING_QUERY_TIMEOUT) {
855
+ return pending.promise;
656
856
  }
657
- this._eventCache.set(cacheKey, {
658
- events,
857
+ const queryPromise = (async () => {
858
+ try {
859
+ let events = await this.pool.querySync(this.relays, filter, { timeout });
860
+ if (filter.authors && filter.authors.length > 0) {
861
+ events = events.filter((event) => filter.authors.includes(event.pubkey));
862
+ }
863
+ this._eventCache.set(cacheKey, {
864
+ events,
865
+ timestamp: Date.now()
866
+ });
867
+ this._indexCacheEntry(cacheKey, filter);
868
+ return events;
869
+ } finally {
870
+ pendingQueries$1.delete(cacheKey);
871
+ }
872
+ })();
873
+ pendingQueries$1.set(cacheKey, {
874
+ promise: queryPromise,
659
875
  timestamp: Date.now()
660
876
  });
661
- if (this._eventCache.size > 100) {
662
- const firstKey = this._eventCache.keys().next().value;
663
- this._eventCache.delete(firstKey);
877
+ return queryPromise;
878
+ }
879
+ /**
880
+ * Limit cache size (called after cache operations)
881
+ * Note: LRU cache handles this automatically, kept for API compatibility
882
+ * @private
883
+ */
884
+ _limitCacheSize() {
885
+ }
886
+ /**
887
+ * Add cache entry to reverse index for fast invalidation
888
+ * @private
889
+ */
890
+ _indexCacheEntry(cacheKey, filter) {
891
+ if (filter.kinds) {
892
+ for (const kind2 of filter.kinds) {
893
+ if (!this._cacheIndex.has(kind2)) {
894
+ this._cacheIndex.set(kind2, /* @__PURE__ */ new Set());
895
+ }
896
+ this._cacheIndex.get(kind2).add(cacheKey);
897
+ }
898
+ }
899
+ }
900
+ /**
901
+ * Remove cache entry from reverse index
902
+ * @private
903
+ */
904
+ _unindexCacheEntry(cacheKey) {
905
+ if (!cacheKey.startsWith("{")) return;
906
+ try {
907
+ const filter = JSON.parse(cacheKey);
908
+ if (filter.kinds) {
909
+ for (const kind2 of filter.kinds) {
910
+ const indexSet = this._cacheIndex.get(kind2);
911
+ if (indexSet) {
912
+ indexSet.delete(cacheKey);
913
+ if (indexSet.size === 0) {
914
+ this._cacheIndex.delete(kind2);
915
+ }
916
+ }
917
+ }
918
+ }
919
+ } catch {
664
920
  }
665
- return events;
666
921
  }
667
922
  /**
668
923
  * Refresh cache in background (fire and forget)
@@ -687,18 +942,33 @@ class NostrClient {
687
942
  }
688
943
  /**
689
944
  * Internal method to refresh a path from relays
945
+ * Throttled to avoid flooding the relay with repeated requests
690
946
  * @private
691
947
  */
692
948
  async _doBackgroundPathRefresh(path, kind2, options) {
693
949
  if (this.relays.length === 0) return;
950
+ const lastRefresh = backgroundRefreshThrottle.get(path);
951
+ if (lastRefresh && Date.now() - lastRefresh < BACKGROUND_REFRESH_INTERVAL) {
952
+ return;
953
+ }
954
+ backgroundRefreshThrottle.set(path, Date.now());
955
+ if (backgroundRefreshThrottle.size > 1e3) {
956
+ const cutoff = Date.now() - BACKGROUND_REFRESH_INTERVAL;
957
+ for (const [key, timestamp] of backgroundRefreshThrottle) {
958
+ if (timestamp < cutoff) {
959
+ backgroundRefreshThrottle.delete(key);
960
+ }
961
+ }
962
+ }
694
963
  const filter = {
695
964
  kinds: [kind2],
696
965
  authors: options.authors || [this.publicKey],
697
966
  "#d": [path],
698
967
  limit: 1
699
968
  };
969
+ const cacheKey = JSON.stringify(filter);
700
970
  const timeout = options.timeout || 3e4;
701
- const events = await this.pool.querySync(this.relays, filter, { timeout });
971
+ const events = await this._queryRelaysAndCache(filter, cacheKey, timeout);
702
972
  const authorFiltered = events.filter(
703
973
  (e) => (options.authors || [this.publicKey]).includes(e.pubkey)
704
974
  );
@@ -720,17 +990,25 @@ class NostrClient {
720
990
  }
721
991
  /**
722
992
  * Internal method to refresh a prefix from relays
993
+ * Throttled to avoid flooding the relay with repeated requests
723
994
  * @private
724
995
  */
725
996
  async _doBackgroundPrefixRefresh(prefix, kind2, options) {
726
997
  if (this.relays.length === 0) return;
998
+ const throttleKey = `prefix:${prefix}`;
999
+ const lastRefresh = backgroundRefreshThrottle.get(throttleKey);
1000
+ if (lastRefresh && Date.now() - lastRefresh < BACKGROUND_REFRESH_INTERVAL) {
1001
+ return;
1002
+ }
1003
+ backgroundRefreshThrottle.set(throttleKey, Date.now());
727
1004
  const filter = {
728
1005
  kinds: [kind2],
729
1006
  authors: options.authors || [this.publicKey],
730
1007
  limit: options.limit || 1e3
731
1008
  };
1009
+ const cacheKey = JSON.stringify(filter);
732
1010
  const timeout = options.timeout || 3e4;
733
- let events = await this.pool.querySync(this.relays, filter, { timeout });
1011
+ let events = await this._queryRelaysAndCache(filter, cacheKey, timeout);
734
1012
  events = events.filter(
735
1013
  (e) => (options.authors || [this.publicKey]).includes(e.pubkey)
736
1014
  );
@@ -805,6 +1083,7 @@ class NostrClient {
805
1083
  }
806
1084
  /**
807
1085
  * Subscribe to events
1086
+ * Uses subscription deduplication to avoid creating multiple identical subscriptions
808
1087
  * @param {Object} filter - Nostr filter object
809
1088
  * @param {Function} onEvent - Callback for each event
810
1089
  * @param {Object} options - Subscription options
@@ -812,7 +1091,8 @@ class NostrClient {
812
1091
  */
813
1092
  async subscribe(filter, onEvent, options = {}) {
814
1093
  await this._initReady;
815
- const subId = `sub-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
1094
+ const subId = `sub-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
1095
+ const filterKey = JSON.stringify(filter);
816
1096
  if (this.relays.length === 0) {
817
1097
  const matchingEvents = this._getMatchingCachedEvents(filter);
818
1098
  const mockSub = {
@@ -837,6 +1117,32 @@ class NostrClient {
837
1117
  }
838
1118
  };
839
1119
  }
1120
+ const existing = activeSubscriptions.get(filterKey);
1121
+ if (existing) {
1122
+ existing.callbacks.add(onEvent);
1123
+ existing.refCount++;
1124
+ return {
1125
+ id: subId,
1126
+ unsubscribe: () => {
1127
+ existing.callbacks.delete(onEvent);
1128
+ existing.refCount--;
1129
+ if (existing.refCount === 0) {
1130
+ if (existing.subscription && existing.subscription.close) {
1131
+ existing.subscription.close();
1132
+ }
1133
+ activeSubscriptions.delete(filterKey);
1134
+ }
1135
+ this._subscriptions.delete(subId);
1136
+ }
1137
+ };
1138
+ }
1139
+ const callbacks = /* @__PURE__ */ new Set([onEvent]);
1140
+ const subscriptionInfo = {
1141
+ callbacks,
1142
+ refCount: 1,
1143
+ subscription: null
1144
+ };
1145
+ activeSubscriptions.set(filterKey, subscriptionInfo);
840
1146
  const sub = this.pool.subscribeMany(
841
1147
  this.relays,
842
1148
  [filter],
@@ -848,18 +1154,33 @@ class NostrClient {
848
1154
  }
849
1155
  }
850
1156
  this._cacheEvent(event);
851
- onEvent(event);
1157
+ const subInfo = activeSubscriptions.get(filterKey);
1158
+ if (subInfo) {
1159
+ for (const cb of subInfo.callbacks) {
1160
+ try {
1161
+ cb(event);
1162
+ } catch (err) {
1163
+ console.warn("[nostr] Subscription callback error:", err.message);
1164
+ }
1165
+ }
1166
+ }
852
1167
  },
853
1168
  oneose: () => {
854
1169
  if (options.onEOSE) options.onEOSE();
855
1170
  }
856
1171
  }
857
1172
  );
1173
+ subscriptionInfo.subscription = sub;
858
1174
  this._subscriptions.set(subId, sub);
859
1175
  return {
860
1176
  id: subId,
861
1177
  unsubscribe: () => {
862
- if (sub.close) sub.close();
1178
+ callbacks.delete(onEvent);
1179
+ subscriptionInfo.refCount--;
1180
+ if (subscriptionInfo.refCount === 0) {
1181
+ if (sub.close) sub.close();
1182
+ activeSubscriptions.delete(filterKey);
1183
+ }
863
1184
  this._subscriptions.delete(subId);
864
1185
  }
865
1186
  };
@@ -895,42 +1216,52 @@ class NostrClient {
895
1216
  }
896
1217
  /**
897
1218
  * Invalidate query caches that might be affected by a new event
1219
+ * Uses reverse index for O(1) lookup instead of O(n) scan
898
1220
  * @private
899
1221
  */
900
1222
  _invalidateQueryCachesForEvent(event) {
1223
+ const indexedKeys = this._cacheIndex.get(event.kind);
1224
+ if (!indexedKeys || indexedKeys.size === 0) {
1225
+ return;
1226
+ }
901
1227
  const keysToDelete = [];
902
- for (const [cacheKey, cached] of this._eventCache.entries()) {
903
- if (!cacheKey.startsWith("{")) continue;
1228
+ for (const cacheKey of indexedKeys) {
1229
+ const cached = this._eventCache.get(cacheKey);
1230
+ if (!cached) {
1231
+ indexedKeys.delete(cacheKey);
1232
+ continue;
1233
+ }
904
1234
  try {
905
1235
  const filter = JSON.parse(cacheKey);
906
1236
  if (this._eventMatchesFilter(event, filter)) {
907
1237
  keysToDelete.push(cacheKey);
908
1238
  }
909
1239
  } catch {
1240
+ indexedKeys.delete(cacheKey);
910
1241
  }
911
1242
  }
912
1243
  for (const key of keysToDelete) {
913
1244
  this._eventCache.delete(key);
1245
+ this._unindexCacheEntry(key);
914
1246
  }
915
1247
  }
916
1248
  /**
917
- * Cache event in memory and persist
1249
+ * Cache event in memory and persist (batched)
918
1250
  * @private
919
1251
  */
920
1252
  async _cacheEvent(event) {
921
1253
  this._cacheEventSync(event);
922
1254
  if (this.persistentStorage) {
923
- try {
924
- let storageKey = event.id;
925
- if (event.kind >= 3e4 && event.kind < 4e4) {
926
- const dTag = event.tags.find((t) => t[0] === "d");
927
- if (dTag && dTag[1]) {
928
- storageKey = dTag[1];
929
- }
1255
+ let storageKey = event.id;
1256
+ if (event.kind >= 3e4 && event.kind < 4e4) {
1257
+ const dTag = event.tags.find((t) => t[0] === "d");
1258
+ if (dTag && dTag[1]) {
1259
+ storageKey = dTag[1];
930
1260
  }
931
- await this.persistentStorage.put(storageKey, event);
932
- } catch (error) {
933
- console.warn("Failed to persist event:", error);
1261
+ }
1262
+ this._persistQueue.set(storageKey, event);
1263
+ if (!this._persistTimer) {
1264
+ this._persistTimer = setTimeout(() => this._flushPersistQueue(), this._persistBatchMs);
934
1265
  }
935
1266
  }
936
1267
  if (this.relays.length === 0) {
@@ -945,6 +1276,26 @@ class NostrClient {
945
1276
  }
946
1277
  }
947
1278
  }
1279
+ /**
1280
+ * Flush batched persistent writes
1281
+ * @private
1282
+ */
1283
+ async _flushPersistQueue() {
1284
+ this._persistTimer = null;
1285
+ if (!this.persistentStorage || this._persistQueue.size === 0) {
1286
+ return;
1287
+ }
1288
+ const toWrite = Array.from(this._persistQueue.entries());
1289
+ this._persistQueue.clear();
1290
+ const writePromises = toWrite.map(async ([key, event]) => {
1291
+ try {
1292
+ await this.persistentStorage.put(key, event);
1293
+ } catch (error) {
1294
+ console.warn(`Failed to persist event ${key}:`, error.message);
1295
+ }
1296
+ });
1297
+ await Promise.all(writePromises);
1298
+ }
948
1299
  /**
949
1300
  * Get cached events matching a filter
950
1301
  * @private
@@ -1032,11 +1383,25 @@ class NostrClient {
1032
1383
  }
1033
1384
  /**
1034
1385
  * Close all connections and subscriptions
1386
+ * @param {Object} options - Close options
1387
+ * @param {boolean} options.flush - Flush pending writes before closing (default: true)
1035
1388
  */
1036
- close() {
1389
+ async close(options = {}) {
1390
+ const shouldFlush = options.flush !== false;
1391
+ if (shouldFlush && this._persistTimer) {
1392
+ clearTimeout(this._persistTimer);
1393
+ await this._flushPersistQueue();
1394
+ }
1037
1395
  if (this.syncService) {
1038
1396
  this.syncService.stop();
1039
1397
  }
1398
+ const authorSub = authorSubscriptions.get(this.publicKey);
1399
+ if (authorSub && authorSub.subscription) {
1400
+ if (authorSub.subscription.close) {
1401
+ authorSub.subscription.close();
1402
+ }
1403
+ authorSubscriptions.delete(this.publicKey);
1404
+ }
1040
1405
  for (const sub of this._subscriptions.values()) {
1041
1406
  if (sub.close) {
1042
1407
  sub.close();
@@ -1047,6 +1412,7 @@ class NostrClient {
1047
1412
  this._subscriptions.clear();
1048
1413
  this.pool.close(this.relays);
1049
1414
  this._eventCache.clear();
1415
+ this._cacheIndex.clear();
1050
1416
  }
1051
1417
  /**
1052
1418
  * Get relay status
@@ -1194,16 +1560,53 @@ function gunPut(gunChain, data, timeout = 1e3) {
1194
1560
  });
1195
1561
  });
1196
1562
  }
1197
- async function gunMap(gunChain, timeout = 300) {
1563
+ async function gunMap(gunChain, timeout = 5e3) {
1198
1564
  return new Promise((resolve) => {
1199
1565
  const items = {};
1200
- gunChain.map().once((data, key) => {
1201
- if (data && !key.startsWith("_")) {
1202
- items[key] = data;
1566
+ let settled = false;
1567
+ let expectedCount = 0;
1568
+ let receivedCount = 0;
1569
+ const tryResolve = () => {
1570
+ if (settled) return;
1571
+ if (expectedCount > 0 && receivedCount >= expectedCount) {
1572
+ settled = true;
1573
+ resolve(items);
1574
+ }
1575
+ };
1576
+ gunChain.once((parentData) => {
1577
+ if (settled) return;
1578
+ if (!parentData) {
1579
+ settled = true;
1580
+ resolve({});
1581
+ return;
1582
+ }
1583
+ const keys = Object.keys(parentData).filter((k) => k !== "_");
1584
+ if (keys.length === 0) {
1585
+ settled = true;
1586
+ resolve({});
1587
+ return;
1588
+ }
1589
+ expectedCount = keys.filter((k) => {
1590
+ const item = parentData[k];
1591
+ return item && typeof item === "object" && item["#"];
1592
+ }).length;
1593
+ if (expectedCount === 0) {
1594
+ settled = true;
1595
+ resolve({});
1596
+ return;
1203
1597
  }
1598
+ gunChain.map().once((data, key) => {
1599
+ if (settled || !data || key.startsWith("_")) return;
1600
+ items[key] = data;
1601
+ receivedCount++;
1602
+ tryResolve();
1603
+ });
1204
1604
  });
1205
1605
  setTimeout(() => {
1206
- resolve(items);
1606
+ if (!settled) {
1607
+ settled = true;
1608
+ resolve(items);
1609
+ }
1207
1610
  }, timeout);
1208
1611
  });
1209
1612
  }
@@ -1298,11 +1701,36 @@ async function read$2(gun, path) {
1298
1701
  }
1299
1702
  return data;
1300
1703
  }
1301
- async function readAll$2(gun, path) {
1704
+ async function readAll$2(gun, path, timeout = 5e3) {
1302
1705
  return new Promise((resolve) => {
1303
1706
  const output2 = /* @__PURE__ */ new Map();
1304
1707
  let settled = false;
1708
+ let expectedCount = 0;
1709
+ let receivedCount = 0;
1305
1710
  const ref = gun.get(path);
1711
+ const tryResolve = () => {
1712
+ if (settled) return;
1713
+ if (expectedCount > 0 && receivedCount >= expectedCount) {
1714
+ settled = true;
1715
+ resolve(Array.from(output2.values()));
1716
+ }
1717
+ };
1718
+ const parseItem = (data) => {
1719
+ if (!data) return null;
1720
+ let item = null;
1721
+ if (data._json && typeof data._json === "string") {
1722
+ try {
1723
+ item = JSON.parse(data._json);
1724
+ } catch (e) {
1725
+ }
1726
+ } else if (typeof data === "string") {
1727
+ try {
1728
+ item = JSON.parse(data);
1729
+ } catch (e) {
1730
+ }
1731
+ }
1732
+ return item;
1733
+ };
1306
1734
  ref.once((parentData) => {
1307
1735
  if (settled) return;
1308
1736
  if (!parentData) {
@@ -1316,64 +1744,47 @@ async function readAll$2(gun, path) {
1316
1744
  resolve([]);
1317
1745
  return;
1318
1746
  }
1747
+ const referenceKeys = [];
1319
1748
  for (const key of keys) {
1320
1749
  const rawItem = parentData[key];
1321
1750
  if (!rawItem) continue;
1322
- let item = null;
1323
1751
  if (typeof rawItem === "object" && rawItem["#"]) {
1752
+ referenceKeys.push(key);
1324
1753
  continue;
1325
1754
  }
1326
- if (rawItem._json && typeof rawItem._json === "string") {
1327
- try {
1328
- item = JSON.parse(rawItem._json);
1329
- } catch (e) {
1330
- }
1331
- } else if (typeof rawItem === "string") {
1332
- try {
1333
- item = JSON.parse(rawItem);
1334
- } catch (e) {
1335
- }
1336
- }
1755
+ const item = parseItem(rawItem);
1337
1756
  if (item && item.id && !item._deleted) {
1338
1757
  output2.set(item.id, item);
1758
+ receivedCount++;
1339
1759
  }
1340
1760
  }
1341
- if (output2.size > 0) {
1761
+ expectedCount = referenceKeys.length;
1762
+ if (expectedCount === 0) {
1342
1763
  settled = true;
1343
1764
  resolve(Array.from(output2.values()));
1344
1765
  return;
1345
1766
  }
1346
1767
  ref.map().once((data, key) => {
1347
1768
  if (settled || !data || key === "_") return;
1348
- let item = null;
1349
- if (data._json && typeof data._json === "string") {
1350
- try {
1351
- item = JSON.parse(data._json);
1352
- } catch (e) {
1353
- }
1354
- } else if (typeof data === "string") {
1355
- try {
1356
- item = JSON.parse(data);
1357
- } catch (e) {
1358
- }
1359
- }
1769
+ const item = parseItem(data);
1360
1770
  if (item && item.id && !item._deleted) {
1361
- output2.set(item.id, item);
1771
+ if (!output2.has(item.id)) {
1772
+ output2.set(item.id, item);
1773
+ receivedCount++;
1774
+ tryResolve();
1775
+ }
1776
+ } else {
1777
+ receivedCount++;
1778
+ tryResolve();
1362
1779
  }
1363
1780
  });
1364
- setTimeout(() => {
1365
- if (!settled) {
1366
- settled = true;
1367
- resolve(Array.from(output2.values()));
1368
- }
1369
- }, 500);
1370
1781
  });
1371
1782
  setTimeout(() => {
1372
1783
  if (!settled) {
1373
1784
  settled = true;
1374
1785
  resolve(Array.from(output2.values()));
1375
1786
  }
1376
- }, 2e3);
1787
+ }, timeout);
1377
1788
  });
1378
1789
  }
1379
1790
  async function update$2(gun, path, updates) {
@@ -2457,11 +2868,12 @@ class GunAuth {
2457
2868
  }
2458
2869
  /**
2459
2870
  * Read all data from a private lens
2871
+ * First gets count of items, then collects until count is reached
2460
2872
  * @param {string} lens - Lens name
2461
- * @param {number} timeout - Timeout in ms
2873
+ * @param {number} timeout - Timeout in ms (fallback)
2462
2874
  * @returns {Promise<Object[]>} Array of data objects
2463
2875
  */
2464
- async readAllPrivate(lens, timeout = 2e3) {
2876
+ async readAllPrivate(lens, timeout = 5e3) {
2465
2877
  if (!this.isAuthenticated()) {
2466
2878
  throw new Error("Not authenticated");
2467
2879
  }
@@ -2469,37 +2881,75 @@ class GunAuth {
2469
2881
  const results = [];
2470
2882
  const seen = /* @__PURE__ */ new Set();
2471
2883
  const ref = this.getPrivatePath(lens);
2472
- setTimeout(() => {
2473
- try {
2474
- ref.off();
2475
- } catch (e) {
2884
+ let settled = false;
2885
+ let expectedCount = 0;
2886
+ let receivedCount = 0;
2887
+ const tryResolve = () => {
2888
+ if (settled) return;
2889
+ if (expectedCount > 0 && receivedCount >= expectedCount) {
2890
+ settled = true;
2891
+ resolve(results);
2476
2892
  }
2477
- resolve(results);
2478
- }, timeout);
2479
- ref.map().once((data, key) => {
2480
- if (data && !key.startsWith("_") && !seen.has(key)) {
2481
- seen.add(key);
2482
- let parsed = null;
2483
- if (typeof data === "string") {
2484
- try {
2485
- parsed = JSON.parse(data);
2486
- } catch (e) {
2487
- parsed = data;
2488
- }
2489
- } else if (data._json && typeof data._json === "string") {
2490
- try {
2491
- parsed = JSON.parse(data._json);
2492
- } catch (e) {
2493
- }
2494
- } else if (typeof data === "object") {
2495
- parsed = { ...data };
2496
- delete parsed["_"];
2893
+ };
2894
+ const parseItem = (data) => {
2895
+ if (!data) return null;
2896
+ let parsed = null;
2897
+ if (typeof data === "string") {
2898
+ try {
2899
+ parsed = JSON.parse(data);
2900
+ } catch (e) {
2901
+ parsed = data;
2902
+ }
2903
+ } else if (data._json && typeof data._json === "string") {
2904
+ try {
2905
+ parsed = JSON.parse(data._json);
2906
+ } catch (e) {
2497
2907
  }
2908
+ } else if (typeof data === "object") {
2909
+ parsed = { ...data };
2910
+ delete parsed["_"];
2911
+ }
2912
+ return parsed;
2913
+ };
2914
+ ref.once((parentData) => {
2915
+ if (settled) return;
2916
+ if (!parentData) {
2917
+ settled = true;
2918
+ resolve([]);
2919
+ return;
2920
+ }
2921
+ const keys = Object.keys(parentData).filter((k) => k !== "_");
2922
+ if (keys.length === 0) {
2923
+ settled = true;
2924
+ resolve([]);
2925
+ return;
2926
+ }
2927
+ expectedCount = keys.filter((k) => {
2928
+ const item = parentData[k];
2929
+ return item && typeof item === "object" && item["#"];
2930
+ }).length;
2931
+ if (expectedCount === 0) {
2932
+ settled = true;
2933
+ resolve([]);
2934
+ return;
2935
+ }
2936
+ ref.map().once((data, key) => {
2937
+ if (settled || !data || key.startsWith("_") || seen.has(key)) return;
2938
+ seen.add(key);
2939
+ const parsed = parseItem(data);
2498
2940
  if (parsed && !parsed._deleted) {
2499
2941
  results.push(parsed);
2500
2942
  }
2501
- }
2943
+ receivedCount++;
2944
+ tryResolve();
2945
+ });
2502
2946
  });
2947
+ setTimeout(() => {
2948
+ if (!settled) {
2949
+ settled = true;
2950
+ resolve(results);
2951
+ }
2952
+ }, timeout);
2503
2953
  });
2504
2954
  }
2505
2955
  /**
@@ -3196,11 +3646,11 @@ class GunDBBackend extends StorageBackend {
3196
3646
  }
3197
3647
  }
3198
3648
  const name = "holosphere";
3199
- const version$1 = "2.0.0-alpha4";
3649
+ const version$1 = "2.0.0-alpha6";
3200
3650
  const description = "Holonic geospatial communication infrastructure combining H3 hexagonal indexing with distributed P2P storage";
3201
3651
  const type = "module";
3202
3652
  const bin = {
3203
- "holosphere-activitypub": "./bin/holosphere-activitypub.js"
3653
+ "holosphere-activitypub": "bin/holosphere-activitypub.js"
3204
3654
  };
3205
3655
  const main = "./dist/cjs/holosphere.cjs";
3206
3656
  const module = "./dist/esm/holosphere.js";
@@ -3941,6 +4391,9 @@ const h3Operations = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.define
3941
4391
  toHolon
3942
4392
  }, Symbol.toStringTag, { value: "Module" }));
3943
4393
  const globalSubscriptions = /* @__PURE__ */ new Map();
4394
+ const singlePathSubscriptions = /* @__PURE__ */ new Map();
4395
+ const pendingQueries = /* @__PURE__ */ new Map();
4396
+ const QUERY_DEDUP_WINDOW = 2e3;
3944
4397
  async function nostrPut(client, path, data, kind2 = 3e4) {
3945
4398
  const dataEvent = {
3946
4399
  kind: kind2,
@@ -3977,6 +4430,27 @@ async function nostrGet(client, path, kind2 = 3e4, options = {}) {
3977
4430
  }
3978
4431
  }
3979
4432
  }
4433
+ const queryKey = `get:${client.publicKey}:${kind2}:${path}:${authors.join(",")}`;
4434
+ const pending = pendingQueries.get(queryKey);
4435
+ if (pending && Date.now() - pending.timestamp < QUERY_DEDUP_WINDOW) {
4436
+ return pending.promise;
4437
+ }
4438
+ const queryPromise = _executeNostrGet(client, path, kind2, authors, timeout, options);
4439
+ pendingQueries.set(queryKey, {
4440
+ promise: queryPromise,
4441
+ timestamp: Date.now()
4442
+ });
4443
+ queryPromise.finally(() => {
4444
+ setTimeout(() => {
4445
+ const current = pendingQueries.get(queryKey);
4446
+ if (current && current.promise === queryPromise) {
4447
+ pendingQueries.delete(queryKey);
4448
+ }
4449
+ }, QUERY_DEDUP_WINDOW);
4450
+ });
4451
+ return queryPromise;
4452
+ }
4453
+ async function _executeNostrGet(client, path, kind2, authors, timeout, options) {
3980
4454
  const filter = {
3981
4455
  kinds: [kind2],
3982
4456
  authors,
@@ -4010,24 +4484,24 @@ async function nostrGetAll(client, pathPrefix, kind2 = 3e4, options = {}) {
4010
4484
  if (!options.skipPersistent && client.persistentGetAll) {
4011
4485
  const persistedEvents = await client.persistentGetAll(pathPrefix);
4012
4486
  if (persistedEvents.length > 0) {
4013
- const byPath2 = /* @__PURE__ */ new Map();
4487
+ const byPath = /* @__PURE__ */ new Map();
4014
4488
  for (const event of persistedEvents) {
4015
4489
  if (!event || !event.tags) continue;
4016
4490
  const dTag = event.tags.find((t) => t[0] === "d");
4017
4491
  if (!dTag || !dTag[1] || !dTag[1].startsWith(pathPrefix)) continue;
4018
4492
  const path = dTag[1];
4019
- const existing = byPath2.get(path);
4493
+ const existing = byPath.get(path);
4020
4494
  if (!existing || event.created_at > existing.created_at) {
4021
4495
  try {
4022
4496
  const data = JSON.parse(event.content);
4023
4497
  if (data._deleted) {
4024
- byPath2.delete(path);
4498
+ byPath.delete(path);
4025
4499
  continue;
4026
4500
  }
4027
4501
  if (options.includeAuthor) {
4028
4502
  data._author = event.pubkey;
4029
4503
  }
4030
- byPath2.set(path, { data, created_at: event.created_at });
4504
+ byPath.set(path, { data, created_at: event.created_at });
4031
4505
  } catch (error) {
4032
4506
  }
4033
4507
  }
@@ -4035,9 +4509,30 @@ async function nostrGetAll(client, pathPrefix, kind2 = 3e4, options = {}) {
4035
4509
  if (client.refreshPrefixInBackground) {
4036
4510
  client.refreshPrefixInBackground(pathPrefix, kind2, { authors, timeout, limit: limit2 });
4037
4511
  }
4038
- return Array.from(byPath2.values()).map((item) => item.data);
4512
+ return Array.from(byPath.values()).map((item) => item.data);
4039
4513
  }
4040
4514
  }
4515
+ const queryKey = `getAll:${client.publicKey}:${kind2}:${pathPrefix}:${authors.join(",")}`;
4516
+ const pending = pendingQueries.get(queryKey);
4517
+ if (pending && Date.now() - pending.timestamp < QUERY_DEDUP_WINDOW) {
4518
+ return pending.promise;
4519
+ }
4520
+ const queryPromise = _executeNostrGetAll(client, pathPrefix, kind2, authors, timeout, limit2, options);
4521
+ pendingQueries.set(queryKey, {
4522
+ promise: queryPromise,
4523
+ timestamp: Date.now()
4524
+ });
4525
+ queryPromise.finally(() => {
4526
+ setTimeout(() => {
4527
+ const current = pendingQueries.get(queryKey);
4528
+ if (current && current.promise === queryPromise) {
4529
+ pendingQueries.delete(queryKey);
4530
+ }
4531
+ }, QUERY_DEDUP_WINDOW);
4532
+ });
4533
+ return queryPromise;
4534
+ }
4535
+ async function _executeNostrGetAll(client, pathPrefix, kind2, authors, timeout, limit2, options) {
4041
4536
  const filter = {
4042
4537
  kinds: [kind2],
4043
4538
  authors,
@@ -4226,7 +4721,29 @@ async function nostrDeleteAll(client, pathPrefix, kind2 = 3e4) {
4226
4721
  }
4227
4722
  function nostrSubscribe(client, path, callback, options = {}) {
4228
4723
  const kind2 = options.kind || 3e4;
4229
- options.includeInitial !== false;
4724
+ const subscriptionKey = `single:${client.publicKey}:${kind2}:${path}`;
4725
+ const existing = singlePathSubscriptions.get(subscriptionKey);
4726
+ if (existing) {
4727
+ existing.callbacks.push(callback);
4728
+ return {
4729
+ unsubscribe: () => {
4730
+ const idx = existing.callbacks.indexOf(callback);
4731
+ if (idx > -1) {
4732
+ existing.callbacks.splice(idx, 1);
4733
+ }
4734
+ if (existing.callbacks.length === 0) {
4735
+ existing.subscription.unsubscribe();
4736
+ singlePathSubscriptions.delete(subscriptionKey);
4737
+ }
4738
+ }
4739
+ };
4740
+ }
4741
+ const callbacks = [callback];
4742
+ const subscriptionInfo = {
4743
+ callbacks,
4744
+ subscription: null
4745
+ };
4746
+ singlePathSubscriptions.set(subscriptionKey, subscriptionInfo);
4230
4747
  const filter = {
4231
4748
  kinds: [kind2],
4232
4749
  authors: [client.publicKey],
@@ -4239,11 +4756,6 @@ function nostrSubscribe(client, path, callback, options = {}) {
4239
4756
  filter,
4240
4757
  (event) => {
4241
4758
  if (event.pubkey !== client.publicKey) {
4242
- console.warn("[nostrSubscribe] Rejecting event from different author:", {
4243
- expected: client.publicKey,
4244
- received: event.pubkey,
4245
- eventId: event.id
4246
- });
4247
4759
  return;
4248
4760
  }
4249
4761
  try {
@@ -4251,7 +4763,13 @@ function nostrSubscribe(client, path, callback, options = {}) {
4251
4763
  if (data._deleted) {
4252
4764
  return;
4253
4765
  }
4254
- callback(data, event);
4766
+ for (const cb of callbacks) {
4767
+ try {
4768
+ cb(data, event);
4769
+ } catch (err) {
4770
+ console.error("Subscription callback error:", err);
4771
+ }
4772
+ }
4255
4773
  } catch (error) {
4256
4774
  console.error("Failed to parse event in subscription:", error);
4257
4775
  }
@@ -4261,7 +4779,19 @@ function nostrSubscribe(client, path, callback, options = {}) {
4261
4779
  }
4262
4780
  }
4263
4781
  );
4264
- return subscription;
4782
+ subscriptionInfo.subscription = subscription;
4783
+ return {
4784
+ unsubscribe: () => {
4785
+ const idx = callbacks.indexOf(callback);
4786
+ if (idx > -1) {
4787
+ callbacks.splice(idx, 1);
4788
+ }
4789
+ if (callbacks.length === 0) {
4790
+ subscription.unsubscribe();
4791
+ singlePathSubscriptions.delete(subscriptionKey);
4792
+ }
4793
+ }
4794
+ };
4265
4795
  }
4266
4796
  async function nostrSubscribeMany(client, pathPrefix, callback, options = {}) {
4267
4797
  const kind2 = options.kind || 3e4;
@@ -5013,7 +5543,7 @@ const sha256 = sha256$1;
5013
5543
  let secp256k1 = null;
5014
5544
  async function loadCrypto() {
5015
5545
  if (!secp256k1) {
5016
- const module2 = await import("./secp256k1-DN4FVXcv.js");
5546
+ const module2 = await import("./secp256k1-PfNOEI7a.js");
5017
5547
  secp256k1 = module2.secp256k1;
5018
5548
  }
5019
5549
  return secp256k1;
@@ -6228,8 +6758,8 @@ async function initiateFederationHandshake(holosphere, privateKey, params) {
6228
6758
  message
6229
6759
  } = params;
6230
6760
  try {
6231
- const { getPublicKeyFromPrivate } = await Promise.resolve().then(() => nostrUtils);
6232
- const senderPubKey = getPublicKeyFromPrivate(privateKey);
6761
+ const { getPublicKey: getPublicKey2 } = await Promise.resolve().then(() => nostrUtils);
6762
+ const senderPubKey = getPublicKey2(privateKey);
6233
6763
  const request = createFederationRequest({
6234
6764
  senderHolonId: holonId,
6235
6765
  senderHolonName: holonName,
@@ -6264,8 +6794,8 @@ async function acceptFederationRequest$1(holosphere, privateKey, params) {
6264
6794
  message
6265
6795
  } = params;
6266
6796
  try {
6267
- const { getPublicKeyFromPrivate } = await Promise.resolve().then(() => nostrUtils);
6268
- const responderPubKey = getPublicKeyFromPrivate(privateKey);
6797
+ const { getPublicKey: getPublicKey2 } = await Promise.resolve().then(() => nostrUtils);
6798
+ const responderPubKey = getPublicKey2(privateKey);
6269
6799
  if (holosphere.client) {
6270
6800
  await addFederatedPartner(holosphere.client, holosphere.config.appName, senderPubKey, {
6271
6801
  alias: request.senderHolonName,
@@ -6277,6 +6807,10 @@ async function acceptFederationRequest$1(holosphere, privateKey, params) {
6277
6807
  }
6278
6808
  }
6279
6809
  }
6810
+ await holosphere.federateHolon(holonId, senderPubKey, {
6811
+ lensConfig,
6812
+ partnerName: request.senderHolonName
6813
+ });
6280
6814
  const response = createFederationResponse({
6281
6815
  requestId: request.requestId,
6282
6816
  status: "accepted",
@@ -16643,7 +17177,7 @@ class ChainManager {
16643
17177
  */
16644
17178
  async _loadEthers() {
16645
17179
  if (!this.ethers) {
16646
- const ethersModule = await import("./index-Cz-PLCUR.js");
17180
+ const ethersModule = await import("./index-JFz-dW43.js");
16647
17181
  this.ethers = ethersModule;
16648
17182
  }
16649
17183
  return this.ethers;
@@ -16827,11 +17361,11 @@ class ChainManager {
16827
17361
  * @param {boolean} [useSigner=true] - Use signer for write operations
16828
17362
  * @returns {Contract}
16829
17363
  */
16830
- async getContract(address, abi, useSigner = true) {
17364
+ async getContract(address, abi2, useSigner = true) {
16831
17365
  this._requireInitialized();
16832
17366
  const ethers = await this._loadEthers();
16833
17367
  const signerOrProvider = useSigner && this.signer ? this.signer : this.provider;
16834
- return new ethers.Contract(address, abi, signerOrProvider);
17368
+ return new ethers.Contract(address, abi2, signerOrProvider);
16835
17369
  }
16836
17370
  /**
16837
17371
  * Deploy a contract
@@ -16840,13 +17374,13 @@ class ChainManager {
16840
17374
  * @param {Array} [constructorArgs=[]] - Constructor arguments
16841
17375
  * @returns {Promise<{contract, address, deployTx}>}
16842
17376
  */
16843
- async deployContract(abi, bytecode, constructorArgs = []) {
17377
+ async deployContract(abi2, bytecode2, constructorArgs = []) {
16844
17378
  this._requireInitialized();
16845
17379
  if (!this.signer) {
16846
17380
  throw new Error("Signer required for contract deployment");
16847
17381
  }
16848
17382
  const ethers = await this._loadEthers();
16849
- const factory = new ethers.ContractFactory(abi, bytecode, this.signer);
17383
+ const factory = new ethers.ContractFactory(abi2, bytecode2, this.signer);
16850
17384
  const contract = await factory.deploy(...constructorArgs);
16851
17385
  await contract.waitForDeployment();
16852
17386
  const address = await contract.getAddress();
@@ -22565,7 +23099,7 @@ const AppreciativeABI = [
22565
23099
  anonymous: false
22566
23100
  }
22567
23101
  ];
22568
- const BundleABI = [
23102
+ const abi = [
22569
23103
  {
22570
23104
  type: "constructor",
22571
23105
  inputs: [
@@ -24000,6 +24534,11 @@ const BundleABI = [
24000
24534
  ]
24001
24535
  }
24002
24536
  ];
24537
+ const bytecode = "0x60806040526040518060400160405280600681526020017f42756e646c650000000000000000000000000000000000000000000000000000815250600290816200004a9190620006da565b5034801562000057575f80fd5b5060405162007e0338038062007e0383398181016040528101906200007d9190620009a6565b60015f819055505f821180156200009b5750670de0b6b3a764000082105b620000dd576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620000d49062000ac6565b60405180910390fd5b8460035f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508460045f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503360055f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508260019081620001ae9190620006da565b5081601381905550806014819055506001600985604051620001d1919062000b26565b90815260200160405180910390205f6101000a81548160ff021916908315150217905550600684908060018154018082558091505060019003905f5260205f20015f909190919091509081620002289190620006da565b50620002396200024460201b60201c565b505050505062000d3a565b60185f620002539190620003e9565b600160145462000264919062000b6b565b67ffffffffffffffff81111562000280576200027f62000480565b5b604051908082528060200260200182016040528015620002af5781602001602082028036833780820191505090505b5060189080519060200190620002c792919062000409565b505f6019819055505f670de0b6b3a764000090505f5b60145481116200037b578160188281548110620002ff57620002fe62000ba5565b5b905f5260205f2001819055508160155f8381526020019081526020015f20805490506200032d919062000bd2565b60195f8282546200033f919062000b6b565b925050819055506200036382601354670de0b6b3a76400006200037f60201b60201c565b91508080620003729062000c1c565b915050620002dd565b5050565b5f808211620003c5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620003bc9062000cb6565b60405180910390fd5b818385620003d4919062000bd2565b620003e0919062000d03565b90509392505050565b5080545f8255905f5260205f209081019062000406919062000459565b50565b828054828255905f5260205f2090810192821562000446579160200282015b828111156200044557825182559160200191906001019062000428565b5b50905062000455919062000459565b5090565b5b8082111562000472575f815f9055506001016200045a565b5090565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680620004f257607f821691505b602082108103620005085762000507620004ad565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026200056c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826200052f565b6200057886836200052f565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620005c2620005bc620005b68462000590565b62000599565b62000590565b9050919050565b5f819050919050565b620005dd83620005a2565b620005f5620005ec82620005c9565b8484546200053b565b825550505050565b5f90565b6200060b620005fd565b62000618818484620005d2565b505050565b5b818110156200063f57620006335f8262000601565b6001810190506200061e565b5050565b601f8211156200068e5762000658816200050e565b620006638462000520565b8101602085101562000673578190505b6200068b620006828562000520565b8301826200061d565b50505b505050565b5f82821c905092915050565b5f620006b05f198460080262000693565b1980831691505092915050565b5f620006ca83836200069f565b9150826002028217905092915050565b620006e58262000476565b67ffffffffffffffff81111562000701576200070062000480565b5b6200070d8254620004da565b6200071a82828562000643565b5f60209050601f83116001811462000750575f84156200073b578287015190505b620007478582620006bd565b865550620007b6565b601f19841662000760866200050e565b5f5b82811015620007895784890151825560018201915060208501945060208101905062000762565b86831015620007a95784890151620007a5601f8916826200069f565b8355505b6001600288020188555050505b505050505050565b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f620007fa82620007cf565b9050919050565b6200080c81620007ee565b811462000817575f80fd5b50565b5f815190506200082a8162000801565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b620008538262000838565b810181811067ffffffffffffffff8211171562000875576200087462000480565b5b80604052505050565b5f62000889620007be565b905062000897828262000848565b919050565b5f67ffffffffffffffff821115620008b957620008b862000480565b5b620008c48262000838565b9050602081019050919050565b5f5b83811015620008f0578082015181840152602081019050620008d3565b5f8484015250505050565b5f620009116200090b846200089c565b6200087e565b90508281526020810184848401111562000930576200092f62000834565b5b6200093d848285620008d1565b509392505050565b5f82601f8301126200095c576200095b62000830565b5b81516200096e848260208601620008fb565b91505092915050565b620009828162000590565b81146200098d575f80fd5b50565b5f81519050620009a08162000977565b92915050565b5f805f805f60a08688031215620009c257620009c1620007c7565b5b5f620009d1888289016200081a565b955050602086015167ffffffffffffffff811115620009f557620009f4620007cb565b5b62000a038882890162000945565b945050604086015167ffffffffffffffff81111562000a275762000a26620007cb565b5b62000a358882890162000945565b935050606062000a488882890162000990565b925050608062000a5b8882890162000990565b9150509295509295909350565b5f82825260208201905092915050565b7f53746565706e657373206d7573742062652030203c2073203c203165313800005f82015250565b5f62000aae601e8362000a68565b915062000abb8262000a78565b602082019050919050565b5f6020820190508181035f83015262000adf8162000aa0565b9050919050565b5f81905092915050565b5f62000afc8262000476565b62000b08818562000ae6565b935062000b1a818560208601620008d1565b80840191505092915050565b5f62000b33828462000af0565b915081905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f62000b778262000590565b915062000b848362000590565b925082820190508082111562000b9f5762000b9e62000b3e565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f62000bde8262000590565b915062000beb8362000590565b925082820262000bfb8162000590565b9150828204841483151762000c155762000c1462000b3e565b5b5092915050565b5f62000c288262000590565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820362000c5d5762000c5c62000b3e565b5b600182019050919050565b7f4469766973696f6e206279207a65726f000000000000000000000000000000005f82015250565b5f62000c9e60108362000a68565b915062000cab8262000c68565b602082019050919050565b5f6020820190508181035f83015262000ccf8162000c90565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f62000d0f8262000590565b915062000d1c8362000590565b92508262000d2f5762000d2e62000cd6565b5b828204905092915050565b6170bb8062000d485f395ff3fe608060405260043610610338575f3560e01c806377e7fca2116101aa578063c21be6ae116100f6578063dad4ae3711610094578063e1f1c4a71161006e578063e1f1c4a714610ca5578063e8d5940d14610ccf578063ead6252014610cf7578063f59e4f6514610d3357610349565b8063dad4ae3714610c03578063de8fa43114610c3f578063e15b3f5f14610c6957610349565b8063cafa4ffa116100d0578063cafa4ffa14610b4b578063cc18494814610b75578063d118a53f14610bb1578063d7841a1c14610bdb57610349565b8063c21be6ae14610aa9578063c45a015514610ae5578063ca22431314610b0f57610349565b8063873010d911610163578063918f96871161013d578063918f9687146109e1578063981a63dc14610a09578063b801603514610a45578063b99ef1fa14610a6d57610349565b8063873010d91461095157806387563ec11461098d5780638da5cb5b146109b757610349565b806377e7fca2146108355780637a3c22eb1461085f5780637b0a7e33146108875780637c59dd5f146108c35780637ed628bb146108ff578063804d74701461092957610349565b806339bfeae31161028457806357a32534116102225780636a146024116101fc5780636a1460241461078f5780636bd39c52146107b95780636cc6837e146107e35780636d2687c0146107f957610349565b806357a325341461071557806360a1da831461075157806362d73eb81461077957610349565b80634635fd681161025e5780634635fd681461064b57806351599b151461068757806353055481146106c35780635744426e146106ff57610349565b806339bfeae3146105ab578063404880d9146105e7578063441179e51461062357610349565b806321670f22116102f15780633477ee2e116102cb5780633477ee2e146104e3578063363454ee1461051f578063372c3e7c1461055b57806338eccabd1461058357610349565b806321670f221461046157806328c317d71461047d57806333d6e65f146104b957610349565b806302d05d3f1461035557806306a49fce1461037f57806306fdde03146103a957806312feb905146103d35780631821fcab146103fd5780631a2f4ee51461043957610349565b36610349576103475f34610d5d565b005b6103535f34610d5d565b005b348015610360575f80fd5b50610369611149565b604051610376919061509c565b60405180910390f35b34801561038a575f80fd5b5061039361116e565b6040516103a091906151fa565b60405180910390f35b3480156103b4575f80fd5b506103bd611242565b6040516103ca9190615262565b60405180910390f35b3480156103de575f80fd5b506103e76112ce565b6040516103f491906151fa565b60405180910390f35b348015610408575f80fd5b50610423600480360381019061041e91906153bf565b6113a2565b604051610430919061541e565b60405180910390f35b348015610444575f80fd5b5061045f600480360381019061045a9190615461565b6113cf565b005b61047b600480360381019061047691906154c9565b610d5d565b005b348015610488575f80fd5b506104a3600480360381019061049e91906153bf565b6114f8565b6040516104b091906155be565b60405180910390f35b3480156104c4575f80fd5b506104cd6115a1565b6040516104da919061541e565b60405180910390f35b3480156104ee575f80fd5b50610509600480360381019061050491906155de565b6115a7565b6040516105169190615262565b60405180910390f35b34801561052a575f80fd5b50610545600480360381019061054091906153bf565b61164d565b6040516105529190615623565b60405180910390f35b348015610566575f80fd5b50610581600480360381019061057c91906155de565b611682565b005b34801561058e575f80fd5b506105a960048036038101906105a491906153bf565b611723565b005b3480156105b6575f80fd5b506105d160048036038101906105cc91906153bf565b61189c565b6040516105de9190615623565b60405180910390f35b3480156105f2575f80fd5b5061060d600480360381019061060891906153bf565b6118d1565b60405161061a9190615623565b60405180910390f35b34801561062e575f80fd5b506106496004803603810190610644919061563c565b611906565b005b348015610656575f80fd5b50610671600480360381019061066c91906155de565b611c2b565b60405161067e9190615262565b60405180910390f35b348015610692575f80fd5b506106ad60048036038101906106a891906153bf565b611cd1565b6040516106ba9190615623565b60405180910390f35b3480156106ce575f80fd5b506106e960048036038101906106e49190615696565b611d06565b6040516106f6919061541e565b60405180910390f35b34801561070a575f80fd5b50610713611d1b565b005b348015610720575f80fd5b5061073b600480360381019061073691906153bf565b611e3f565b604051610748919061541e565b60405180910390f35b34801561075c575f80fd5b50610777600480360381019061077291906156c1565b611e6c565b005b348015610784575f80fd5b5061078d612201565b005b34801561079a575f80fd5b506107a361245e565b6040516107b0919061541e565b60405180910390f35b3480156107c4575f80fd5b506107cd61246a565b6040516107da919061541e565b60405180910390f35b3480156107ee575f80fd5b506107f7612470565b005b348015610804575f80fd5b5061081f600480360381019061081a91906155de565b612987565b60405161082c9190615262565b60405180910390f35b348015610840575f80fd5b50610849612a2d565b604051610856919061541e565b60405180910390f35b34801561086a575f80fd5b50610885600480360381019061088091906153bf565b612a33565b005b348015610892575f80fd5b506108ad60048036038101906108a891906155de565b612c87565b6040516108ba91906151fa565b60405180910390f35b3480156108ce575f80fd5b506108e960048036038101906108e491906153bf565b612d6c565b6040516108f69190615623565b60405180910390f35b34801561090a575f80fd5b50610913612da1565b6040516109209190615623565b60405180910390f35b348015610934575f80fd5b5061094f600480360381019061094a91906157fd565b612db3565b005b34801561095c575f80fd5b50610977600480360381019061097291906155de565b612fac565b604051610984919061541e565b60405180910390f35b348015610998575f80fd5b506109a1612fcc565b6040516109ae919061541e565b60405180910390f35b3480156109c2575f80fd5b506109cb612fd2565b6040516109d8919061509c565b60405180910390f35b3480156109ec575f80fd5b50610a076004803603810190610a0291906153bf565b612ff7565b005b348015610a14575f80fd5b50610a2f6004803603810190610a2a91906153bf565b6131ab565b604051610a3c9190615262565b60405180910390f35b348015610a50575f80fd5b50610a6b6004803603810190610a669190615904565b61325e565b005b348015610a78575f80fd5b50610a936004803603810190610a8e91906153bf565b61368c565b604051610aa0919061541e565b60405180910390f35b348015610ab4575f80fd5b50610acf6004803603810190610aca91906153bf565b6136b9565b604051610adc919061509c565b60405180910390f35b348015610af0575f80fd5b50610af9613701565b604051610b06919061509c565b60405180910390f35b348015610b1a575f80fd5b50610b356004803603810190610b3091906156c1565b613726565b604051610b42919061541e565b60405180910390f35b348015610b56575f80fd5b50610b5f61375e565b604051610b6c919061541e565b60405180910390f35b348015610b80575f80fd5b50610b9b6004803603810190610b96919061563c565b613764565b604051610ba8919061509c565b60405180910390f35b348015610bbc575f80fd5b50610bc56137c4565b604051610bd29190615a31565b60405180910390f35b348015610be6575f80fd5b50610c016004803603810190610bfc91906155de565b61381a565b005b348015610c0e575f80fd5b50610c296004803603810190610c2491906153bf565b613947565b604051610c369190615623565b60405180910390f35b348015610c4a575f80fd5b50610c5361397c565b604051610c60919061541e565b60405180910390f35b348015610c74575f80fd5b50610c8f6004803603810190610c8a91906153bf565b613988565b604051610c9c919061541e565b60405180910390f35b348015610cb0575f80fd5b50610cb96139b5565b604051610cc6919061541e565b60405180910390f35b348015610cda575f80fd5b50610cf56004803603810190610cf09190615a51565b6139bb565b005b348015610d02575f80fd5b50610d1d6004803603810190610d189190615461565b613ca9565b604051610d2a9190615262565b60405180910390f35b348015610d3e575f80fd5b50610d47613d5a565b604051610d549190615262565b60405180910390f35b610d65613de6565b5f8034118015610da057505f73ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16145b90505f8115610e18573492503373ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff167f37de26b9f29dba736fb8291dad9ace7d2f6dc76e3e5faaeca097145b5298f94b5f86604051610e0b929190615ac7565b60405180910390a3610f8a565b8390505f600d5f8673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f20548273ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610e93919061509c565b602060405180830381865afa158015610eae573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ed29190615b02565b610edc9190615b5a565b905083811015610f21576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f1890615bd7565b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff167f37de26b9f29dba736fb8291dad9ace7d2f6dc76e3e5faaeca097145b5298f94b8787604051610f80929190615ac7565b60405180910390a3505b5f610f9a84600e54612710613e2a565b90505f8185610fa99190615b5a565b90505f8083118015610fbf57505f601080549050115b15610fde57610fd087848787613e8d565b81610fdb9190615bf5565b90505b5f82118015610fee57505f601954115b1561100d57610fff87838787613fb6565b8161100a9190615bf5565b90505b3073ffffffffffffffffffffffffffffffffffffffff167fd83308e9158842b2115ec5e6f6395db42fff24e4b5506ef42fe33854fafad655878388611087576040518060400160405280600581526020017f45524332300000000000000000000000000000000000000000000000000000008152506110be565b6040518060400160405280600381526020017f45544800000000000000000000000000000000000000000000000000000000008152505b6040516110cd93929190615c28565b60405180910390a23073ffffffffffffffffffffffffffffffffffffffff167fc2d2b959adca67349f750fed9ef9eaefbec59f24fa563c7fa410157130395c4060018761111a578961111c565b5f5b89855f604051611130959493929190615d96565b60405180910390a25050505050611145614184565b5050565b60045f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060601b805480602002602001604051908101604052809291908181526020015f905b82821015611239578382905f5260205f200180546111ae90615c91565b80601f01602080910402602001604051908101604052809291908181526020018280546111da90615c91565b80156112255780601f106111fc57610100808354040283529160200191611225565b820191905f5260205f20905b81548152906001019060200180831161120857829003601f168201915b505050505081526020019060010190611191565b50505050905090565b6001805461124f90615c91565b80601f016020809104026020016040519081016040528092919081815260200182805461127b90615c91565b80156112c65780601f1061129d576101008083540402835291602001916112c6565b820191905f5260205f20905b8154815290600101906020018083116112a957829003601f168201915b505050505081565b60606010805480602002602001604051908101604052809291908181526020015f905b82821015611399578382905f5260205f2001805461130e90615c91565b80601f016020809104026020016040519081016040528092919081815260200182805461133a90615c91565b80156113855780601f1061135c57610100808354040283529160200191611385565b820191905f5260205f20905b81548152906001019060200180831161136857829003601f168201915b5050505050815260200190600101906112f1565b50505050905090565b6016818051602081018201805184825260208301602085012081835280955050505050505f915090505481565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461145e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161145590615e38565b60405180910390fd5b612710818361146d9190615bf5565b146114ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114a490615ea0565b60405180910390fd5b81600e8190555080600f819055507f29dd4a83e71eab2a472a8bb7a7bb9467395098acb48b07c8d9ea7294de0ba0ae82826040516114ec929190615ebe565b60405180910390a15050565b6060600c8260405161150a9190615f1f565b908152602001604051809103902080548060200260200160405190810160405280929190818152602001828054801561159557602002820191905f5260205f20905b815f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161154c575b50505050509050919050565b60145481565b601b81815481106115b6575f80fd5b905f5260205f20015f9150905080546115ce90615c91565b80601f01602080910402602001604051908101604052809291908181526020018280546115fa90615c91565b80156116455780601f1061161c57610100808354040283529160200191611645565b820191905f5260205f20905b81548152906001019060200180831161162857829003601f168201915b505050505081565b6008818051602081018201805184825260208301602085012081835280955050505050505f915054906101000a900460ff1681565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611711576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161170890615e38565b60405180910390fd5b8060148190555061172061418d565b50565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146117b2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016117a990615e38565b60405180910390fd5b6017816040516117c29190615f1f565b90815260200160405180910390205f9054906101000a900460ff1661181c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161181390615f7f565b60405180910390fd5b611825816142ab565b5f6017826040516118369190615f1f565b90815260200160405180910390205f6101000a81548160ff02191690831515021790555061186261418d565b7fe6db474363a3116f4cf525fc0d78d9dd53e214383db31efef8368f08c4bf7991816040516118919190615262565b60405180910390a150565b601e818051602081018201805184825260208301602085012081835280955050505050505f915054906101000a900460ff1681565b6009818051602081018201805184825260208301602085012081835280955050505050505f915054906101000a900460ff1681565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611995576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161198c90615e38565b60405180910390fd5b6009826040516119a59190615f1f565b90815260200160405180910390205f9054906101000a900460ff166119ff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119f690615fe7565b60405180910390fd5b601454811115611a44576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a3b9061604f565b60405180910390fd5b5f601783604051611a559190615f1f565b90815260200160405180910390205f9054906101000a900460ff16611a9a577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff611aba565b601683604051611aaa9190615f1f565b9081526020016040518091039020545b9050601783604051611acc9190615f1f565b90815260200160405180910390205f9054906101000a900460ff1615611af657611af5836142ab565b5b81601684604051611b079190615f1f565b90815260200160405180910390208190555060155f8381526020019081526020015f2083908060018154018082558091505060019003905f5260205f20015f909190919091509081611b5991906161ef565b506001601784604051611b6c9190615f1f565b90815260200160405180910390205f6101000a81548160ff021916908315150217905550611b9861418d565b7f9bca84993827a6bd2bc824f45db9e0ae931cf1f7b1706b7c735ba0102300b4ca8383604051611bc99291906162be565b60405180910390a13073ffffffffffffffffffffffffffffffffffffffff167f7d482c93a88a92e4d27ba697265785162f607bc0ac6c55b868232b3ba3fbb63d6001858486604051611c1e94939291906162ec565b60405180910390a2505050565b60068181548110611c3a575f80fd5b905f5260205f20015f915090508054611c5290615c91565b80601f0160208091040260200160405190810160405280929190818152602001828054611c7e90615c91565b8015611cc95780601f10611ca057610100808354040283529160200191611cc9565b820191905f5260205f20905b815481529060010190602001808311611cac57829003601f168201915b505050505081565b601c818051602081018201805184825260208301602085012081835280955050505050505f915054906101000a900460ff1681565b600d602052805f5260405f205f915090505481565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611daa576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611da190615e38565b60405180910390fd5b601a5f9054906101000a900460ff16611df8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611def90616387565b60405180910390fd5b5f601a5f6101000a81548160ff0219169083151502179055507f990bb31515adefe5ecd88a4dca3e9772b32d484c81e22cae38d5482e0d9f35e460405160405180910390a1565b600a818051602081018201805184825260208301602085012081835280955050505050505f915090505481565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611efb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611ef290615e38565b60405180910390fd5b611f03613de6565b600882604051611f139190615f1f565b90815260200160405180910390205f9054906101000a900460ff1615611f6e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f65906163ef565b60405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603611fdc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611fd390616457565b60405180910390fd5b600982604051611fec9190615f1f565b90815260200160405180910390205f9054906101000a900460ff16612046576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161203d906164bf565b60405180910390fd5b5f600a836040516120579190615f1f565b9081526020016040518091039020549050600160088460405161207a9190615f1f565b90815260200160405180910390205f6101000a81548160ff0219169083151502179055505f73ffffffffffffffffffffffffffffffffffffffff166007846040516120c59190615f1f565b90815260200160405180910390205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff160361216b578160078460405161211f9190615f1f565b90815260200160405180910390205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b61217583836143c9565b61217f83836144c5565b5f8111156121f4578173ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff167f605614956ef211f270ab0b08a13dd69698dc38d70e20899e1051ae2e1ae6cd5b6001865f866040516121eb94939291906164dd565b60405180910390a35b506121fd614184565b5050565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612290576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161228790615e38565b60405180910390fd5b5f5b601b80549050811015612340575f601d601b83815481106122b6576122b561652e565b5b905f5260205f20016040516122cb91906165db565b9081526020016040518091039020819055505f601c601b83815481106122f4576122f361652e565b5b905f5260205f200160405161230991906165db565b90815260200160405180910390205f6101000a81548160ff0219169083151502179055508080612338906165f1565b915050612292565b50601b5f61234e9190614f5b565b5f5b600680549050811015612415575f601e600683815481106123745761237361652e565b5b905f5260205f200160405161238991906165db565b90815260200160405180910390205f6101000a81548160ff02191690831515021790555060405180602001604052805f815250601f600683815481106123d2576123d161652e565b5b905f5260205f20016040516123e791906165db565b9081526020016040518091039020908161240191906161ef565b50808061240d906165f1565b915050612350565b506001601a5f6101000a81548160ff0219169083151502179055507fc475c26aa25ed8e85a7faa790e5f63b5d7ee1025f12821f4c9bfddd36ab4d11260405160405180910390a1565b670de0b6b3a764000081565b600e5481565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146124ff576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016124f690615e38565b60405180910390fd5b601a5f9054906101000a900460ff1661254d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161254490616387565b60405180910390fd5b5f601b8054905011612594576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161258b90616682565b60405180910390fd5b5f601b5f815481106125a9576125a861652e565b5b905f5260205f200180546125bc90615c91565b80601f01602080910402602001604051908101604052809291908181526020018280546125e890615c91565b80156126335780601f1061260a57610100808354040283529160200191612633565b820191905f5260205f20905b81548152906001019060200180831161261657829003601f168201915b505050505090505f601d601b5f815481106126515761265061652e565b5b905f5260205f200160405161266691906165db565b90815260200160405180910390205490505f600190505b601b805490508110156127c05781601d601b83815481106126a1576126a061652e565b5b905f5260205f20016040516126b691906165db565b90815260200160405180910390205411156127ad57601d601b82815481106126e1576126e061652e565b5b905f5260205f20016040516126f691906165db565b9081526020016040518091039020549150601b818154811061271b5761271a61652e565b5b905f5260205f2001805461272e90615c91565b80601f016020809104026020016040519081016040528092919081815260200182805461275a90615c91565b80156127a55780601f1061277c576101008083540402835291602001916127a5565b820191905f5260205f20905b81548152906001019060200180831161278857829003601f168201915b505050505092505b80806127b8906165f1565b91505061267d565b505f6007836040516127d29190615f1f565b90815260200160405180910390205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603612870576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161286790616710565b60405180910390fd5b5f60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508160035f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505f601a5f6101000a81548160ff0219169083151502179055507f5e99cd5235d5c8c4f0f8dc2626c83130cf843047e4ecbe4c111076e2b31a0d52848460405161291f9291906162be565b60405180910390a18173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a350505050565b60108181548110612996575f80fd5b905f5260205f20015f9150905080546129ae90615c91565b80601f01602080910402602001604051908101604052809291908181526020018280546129da90615c91565b8015612a255780601f106129fc57610100808354040283529160200191612a25565b820191905f5260205f20905b815481529060010190602001808311612a0857829003601f168201915b505050505081565b600f5481565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612ac2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ab990615e38565b60405180910390fd5b601a5f9054906101000a900460ff16612b10576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b0790616387565b60405180910390fd5b600981604051612b209190615f1f565b90815260200160405180910390205f9054906101000a900460ff16612b7a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612b7190616778565b60405180910390fd5b601c81604051612b8a9190615f1f565b90815260200160405180910390205f9054906101000a900460ff1615612be5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612bdc906167e0565b60405180910390fd5b601b81908060018154018082558091505060019003905f5260205f20015f909190919091509081612c1691906161ef565b506001601c82604051612c299190615f1f565b90815260200160405180910390205f6101000a81548160ff0219169083151502179055507f134c877b5bd4770ffc56867fc5d2c549dd813a5db276d378f8d855a04fc7aa1d81604051612c7c9190615262565b60405180910390a150565b606060155f8381526020019081526020015f20805480602002602001604051908101604052809291908181526020015f905b82821015612d61578382905f5260205f20018054612cd690615c91565b80601f0160208091040260200160405190810160405280929190818152602001828054612d0290615c91565b8015612d4d5780601f10612d2457610100808354040283529160200191612d4d565b820191905f5260205f20905b815481529060010190602001808311612d3057829003601f168201915b505050505081526020019060010190612cb9565b505050509050919050565b6011818051602081018201805184825260208301602085012081835280955050505050505f915054906101000a900460ff1681565b601a5f9054906101000a900460ff1681565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612e42576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612e3990615e38565b60405180910390fd5b5f5b8151811015612fa8575f828281518110612e6157612e6061652e565b5b60200260200101519050600981604051612e7b9190615f1f565b90815260200160405180910390205f9054906101000a900460ff1615612ea15750612f95565b6001600982604051612eb39190615f1f565b90815260200160405180910390205f6101000a81548160ff021916908315150217905550600681908060018154018082558091505060019003905f5260205f20015f909190919091509081612f0891906161ef565b507fdfaa3c24567cbffbf024ddfd8dfca7a224b307e94b0df5437facad0f2fea2f9b81604051612f389190615262565b60405180910390a13073ffffffffffffffffffffffffffffffffffffffff167f954917c11fc57299ff064c483b4113756221231fcea3a1a3aa54ebd605a9c1e860018333604051612f8b939291906167fe565b60405180910390a2505b8080612fa0906165f1565b915050612e44565b5050565b60188181548110612fbb575f80fd5b905f5260205f20015f915090505481565b60135481565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614613086576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161307d90615e38565b60405180910390fd5b6009816040516130969190615f1f565b90815260200160405180910390205f9054906101000a900460ff166131a85760016009826040516130c79190615f1f565b90815260200160405180910390205f6101000a81548160ff021916908315150217905550600681908060018154018082558091505060019003905f5260205f20015f90919091909150908161311c91906161ef565b507fdfaa3c24567cbffbf024ddfd8dfca7a224b307e94b0df5437facad0f2fea2f9b8160405161314c9190615262565b60405180910390a13073ffffffffffffffffffffffffffffffffffffffff167f954917c11fc57299ff064c483b4113756221231fcea3a1a3aa54ebd605a9c1e86001833360405161319f939291906167fe565b60405180910390a25b50565b601f818051602081018201805184825260208301602085012081835280955050505050505f9150905080546131df90615c91565b80601f016020809104026020016040519081016040528092919081815260200182805461320b90615c91565b80156132565780601f1061322d57610100808354040283529160200191613256565b820191905f5260205f20905b81548152906001019060200180831161323957829003601f168201915b505050505081565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146132ed576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016132e490615e38565b60405180910390fd5b8051825114613331576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016133289061688b565b60405180910390fd5b5f5b6010805490508110156133e1575f6012601083815481106133575761335661652e565b5b905f5260205f200160405161336c91906165db565b9081526020016040518091039020819055505f6011601083815481106133955761339461652e565b5b905f5260205f20016040516133aa91906165db565b90815260200160405180910390205f6101000a81548160ff02191690831515021790555080806133d9906165f1565b915050613333565b5060105f6133ef9190614f5b565b5f805b83518110156136095760098482815181106134105761340f61652e565b5b60200260200101516040516134259190615f1f565b90815260200160405180910390205f9054906101000a900460ff1661347f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613476906168f3565b60405180910390fd5b5f8382815181106134935761349261652e565b5b6020026020010151116134db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016134d29061695b565b60405180910390fd5b60108482815181106134f0576134ef61652e565b5b6020026020010151908060018154018082558091505060019003905f5260205f20015f90919091909150908161352691906161ef565b506001601185838151811061353e5761353d61652e565b5b60200260200101516040516135539190615f1f565b90815260200160405180910390205f6101000a81548160ff02191690831515021790555082818151811061358a5761358961652e565b5b602002602001015160128583815181106135a7576135a661652e565b5b60200260200101516040516135bc9190615f1f565b9081526020016040518091039020819055508281815181106135e1576135e061652e565b5b6020026020010151826135f49190615bf5565b91508080613601906165f1565b9150506133f2565b50612710811461364e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613645906169c3565b60405180910390fd5b7f7749ddba77d660c1351ff548a0bf5b09743d6ad44914e3cb9a29a1f805fb09fe838360405161367f9291906169e1565b60405180910390a1505050565b601d818051602081018201805184825260208301602085012081835280955050505050505f915090505481565b6007818051602081018201805184825260208301602085012081835280955050505050505f915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60055f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600b82805160208101820180518482526020830160208501208183528095505050505050602052805f5260405f205f91509150505481565b60195481565b600c828051602081018201805184825260208301602085012081835280955050505050508181548110613795575f80fd5b905f5260205f20015f915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060601880548060200260200160405190810160405280929190818152602001828054801561381057602002820191905f5260205f20905b8154815260200190600101908083116137fc575b5050505050905090565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146138a9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016138a090615e38565b60405180910390fd5b5f811180156138bf5750670de0b6b3a764000081105b6138fe576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016138f590616a60565b60405180910390fd5b8060138190555061390d61418d565b7f5fad7fda6b8fb652ca59780d340772fed3852ba698b3a4cd40ebc117b8ec57508160405161393c919061541e565b60405180910390a150565b6017818051602081018201805184825260208301602085012081835280955050505050505f915054906101000a900460ff1681565b5f600680549050905090565b6012818051602081018201805184825260208301602085012081835280955050505050505f915090505481565b61271081565b60035f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614613a4a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613a4190615e38565b60405180910390fd5b601a5f9054906101000a900460ff16613a98576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613a8f90616387565b60405180910390fd5b600982604051613aa89190615f1f565b90815260200160405180910390205f9054906101000a900460ff16613b02576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613af990616ac8565b60405180910390fd5b601c81604051613b129190615f1f565b90815260200160405180910390205f9054906101000a900460ff16613b6c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613b6390616b30565b60405180910390fd5b601e82604051613b7c9190615f1f565b90815260200160405180910390205f9054906101000a900460ff1615613bd7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613bce90616b98565b60405180910390fd5b6001601e83604051613be99190615f1f565b90815260200160405180910390205f6101000a81548160ff02191690831515021790555080601f83604051613c1e9190615f1f565b90815260200160405180910390209081613c3891906161ef565b50601d81604051613c499190615f1f565b90815260200160405180910390205f815480929190613c67906165f1565b91905055507f137ad23732468494166ec34d1367743690ffa17f6ceac8429cba8fc35d088b6e8282604051613c9d929190616bb6565b60405180910390a15050565b6015602052815f5260405f208181548110613cc2575f80fd5b905f5260205f20015f91509150508054613cdb90615c91565b80601f0160208091040260200160405190810160405280929190818152602001828054613d0790615c91565b8015613d525780601f10613d2957610100808354040283529160200191613d52565b820191905f5260205f20905b815481529060010190602001808311613d3557829003601f168201915b505050505081565b60028054613d6790615c91565b80601f0160208091040260200160405190810160405280929190818152602001828054613d9390615c91565b8015613dde5780601f10613db557610100808354040283529160200191613dde565b820191905f5260205f20905b815481529060010190602001808311613dc157829003601f168201915b505050505081565b60025f5403613e21576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025f81905550565b5f808211613e6d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401613e6490616c35565b60405180910390fd5b818385613e7a9190616c53565b613e849190616cc1565b90509392505050565b5f805f90505b601080549050811015613fad575f60108281548110613eb557613eb461652e565b5b905f5260205f20018054613ec890615c91565b80601f0160208091040260200160405190810160405280929190818152602001828054613ef490615c91565b8015613f3f5780601f10613f1657610100808354040283529160200191613f3f565b820191905f5260205f20905b815481529060010190602001808311613f2257829003601f168201915b505050505090505f613f7287601284604051613f5b9190615f1f565b908152602001604051809103902054612710613e2a565b90505f811115613f9857613f898289838989614737565b8380613f94906165f1565b9450505b50508080613fa5906165f1565b915050613e93565b50949350505050565b5f805f90505b601454811161417b575f60155f8381526020019081526020015f208054905090505f8103613fea5750614168565b5f60188381548110613fff57613ffe61652e565b5b905f5260205f200154826140139190616c53565b90505f6140238883601954613e2a565b90505f83826140329190616cc1565b90505f805b85811015614161575f60018761404d9190615b5a565b820361406657828561405f9190615b5a565b9050614078565b83905083836140759190615bf5565b92505b5f81111561414d5761413e60155f8a81526020019081526020015f2083815481106140a6576140a561652e565b5b905f5260205f200180546140b990615c91565b80601f01602080910402602001604051908101604052809291908181526020018280546140e590615c91565b80156141305780601f1061410757610100808354040283529160200191614130565b820191905f5260205f20905b81548152906001019060200180831161411357829003601f168201915b50505050508e838e8e614737565b8880614149906165f1565b9950505b508080614159906165f1565b915050614037565b5050505050505b8080614173906165f1565b915050613fbc565b50949350505050565b60015f81905550565b60185f61419a9190614f79565b60016014546141a99190615bf5565b67ffffffffffffffff8111156141c2576141c161529b565b5b6040519080825280602002602001820160405280156141f05781602001602082028036833780820191505090505b5060189080519060200190614206929190614f97565b505f6019819055505f670de0b6b3a764000090505f5b60145481116142a757816018828154811061423a5761423961652e565b5b905f5260205f2001819055508160155f8381526020019081526020015f20805490506142669190616c53565b60195f8282546142769190615bf5565b9250508190555061429282601354670de0b6b3a7640000613e2a565b9150808061429f906165f1565b91505061421c565b5050565b5f6016826040516142bc9190615f1f565b90815260200160405180910390205490505f60155f8381526020019081526020015f2090505f5b81805490508110156143c357838051906020012082828154811061430a5761430961652e565b5b905f5260205f200160405161431f9190616d8d565b6040518091039020036143b057816001838054905061433e9190615b5a565b8154811061434f5761434e61652e565b5b905f5260205f200182828154811061436a5761436961652e565b5b905f5260205f2001908161437e9190616dca565b50818054806143905761438f616eaf565b5b600190038181905f5260205f20015f6143a99190614fe2565b90556143c3565b80806143bb906165f1565b9150506142e3565b50505050565b5f600a836040516143da9190615f1f565b90815260200160405180910390205490505f8111156144c0575f600a846040516144049190615f1f565b9081526020016040518091039020819055505f8273ffffffffffffffffffffffffffffffffffffffff168260405161443b90616eff565b5f6040518083038185875af1925050503d805f8114614475576040519150601f19603f3d011682016040523d82523d5f602084013e61447a565b606091505b50509050806144be576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016144b590616f5d565b60405180910390fd5b505b505050565b5f600c836040516144d69190615f1f565b908152602001604051809103902080548060200260200160405190810160405280929190818152602001828054801561456157602002820191905f5260205f20905b815f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311614518575b505050505090505f5b8151811015614731575f600b856040516145849190615f1f565b90815260200160405180910390205f8484815181106145a6576145a561652e565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205490505f81111561471d575f600b866040516146039190615f1f565b90815260200160405180910390205f8585815181106146255761462461652e565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f208190555080600d5f8585815181106146815761468061652e565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f8282546146d09190615b5a565b9250508190555061471c84828585815181106146ef576146ee61652e565b5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16614e419092919063ffffffff16565b5b508080614729906165f1565b91505061456a565b50505050565b5f6007866040516147489190615f1f565b90815260200160405180910390205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505f8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141580156147cb57505f8273ffffffffffffffffffffffffffffffffffffffff163b115b90506008876040516147dd9190615f1f565b90815260200160405180910390205f9054906101000a900460ff16801561483057505f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614155b15614a715783156148e9575f8273ffffffffffffffffffffffffffffffffffffffff168660405161486090616eff565b5f6040518083038185875af1925050503d805f811461489a576040519150601f19603f3d011682016040523d82523d5f602084013e61489f565b606091505b50509050806148e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016148da90616fc5565b60405180910390fd5b50614915565b61491482868573ffffffffffffffffffffffffffffffffffffffff16614e419092919063ffffffff16565b5b8173ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff167f7d6a6b7452c2fec7981096e354fb3311f3f3b800ff27def1017b22cbff2751908784886149a6576040518060400160405280600581526020017f45524332300000000000000000000000000000000000000000000000000000008152506149dd565b6040518060400160405280600381526020017f45544800000000000000000000000000000000000000000000000000000000008152505b6040516149ec93929190616fe3565b60405180910390a38173ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff167f71e611184866cc19c5f49af6c856ce9bde5a13bcd197c7499b61ab410ac9439960018a88614a51578a614a53565b5f5b8a604051614a6494939291906164dd565b60405180910390a3614e38565b8315614ab05784600a88604051614a889190615f1f565b90815260200160405180910390205f828254614aa49190615bf5565b92505081905550614c50565b5f600b88604051614ac19190615f1f565b90815260200160405180910390205f8873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205403614b8d57600c87604051614b209190615f1f565b908152602001604051809103902086908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b84600b88604051614b9e9190615f1f565b90815260200160405180910390205f8873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254614bf59190615bf5565b9250508190555084600d5f8873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020015f205f828254614c489190615bf5565b925050819055505b5f73ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff167f7d6a6b7452c2fec7981096e354fb3311f3f3b800ff27def1017b22cbff275190875f88614ce1576040518060400160405280600c81526020017f53544f5245445f45524332300000000000000000000000000000000000000000815250614d18565b6040518060400160405280600a81526020017f53544f5245445f455448000000000000000000000000000000000000000000008152505b604051614d2793929190616fe3565b60405180910390a35f601188604051614d409190615f1f565b90815260200160405180910390205f9054906101000a900460ff16614d9a576040518060400160405280600481526020017f7a6f6e6500000000000000000000000000000000000000000000000000000000815250614dd1565b6040518060400160405280600a81526020017f70657263656e74616765000000000000000000000000000000000000000000008152505b90503073ffffffffffffffffffffffffffffffffffffffff167ff9700e8ac41cc506d3f79229bad9981619140b63838136cdb10ac6ba5f6976ab60018a88614e19578a614e1b565b5f5b8a86604051614e2e95949392919061701f565b60405180910390a2505b50505050505050565b614ebb838473ffffffffffffffffffffffffffffffffffffffff1663a9059cbb8585604051602401614e74929190615ac7565b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050614ec0565b505050565b5f8060205f8451602086015f885af180614edf576040513d5f823e3d81fd5b3d92505f519150505f8214614ef8576001811415614f13565b5f8473ffffffffffffffffffffffffffffffffffffffff163b145b15614f5557836040517f5274afe7000000000000000000000000000000000000000000000000000000008152600401614f4c919061509c565b60405180910390fd5b50505050565b5080545f8255905f5260205f2090810190614f76919061501f565b50565b5080545f8255905f5260205f2090810190614f949190615042565b50565b828054828255905f5260205f20908101928215614fd1579160200282015b82811115614fd0578251825591602001919060010190614fb5565b5b509050614fde9190615042565b5090565b508054614fee90615c91565b5f825580601f10614fff575061501c565b601f0160209004905f5260205f209081019061501b9190615042565b5b50565b5b8082111561503e575f81816150359190614fe2565b50600101615020565b5090565b5b80821115615059575f815f905550600101615043565b5090565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6150868261505d565b9050919050565b6150968161507c565b82525050565b5f6020820190506150af5f83018461508d565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156151155780820151818401526020810190506150fa565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61513a826150de565b61514481856150e8565b93506151548185602086016150f8565b61515d81615120565b840191505092915050565b5f6151738383615130565b905092915050565b5f602082019050919050565b5f615191826150b5565b61519b81856150bf565b9350836020820285016151ad856150cf565b805f5b858110156151e857848403895281516151c98582615168565b94506151d48361517b565b925060208a019950506001810190506151b0565b50829750879550505050505092915050565b5f6020820190508181035f8301526152128184615187565b905092915050565b5f82825260208201905092915050565b5f615234826150de565b61523e818561521a565b935061524e8185602086016150f8565b61525781615120565b840191505092915050565b5f6020820190508181035f83015261527a818461522a565b905092915050565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6152d182615120565b810181811067ffffffffffffffff821117156152f0576152ef61529b565b5b80604052505050565b5f615302615282565b905061530e82826152c8565b919050565b5f67ffffffffffffffff82111561532d5761532c61529b565b5b61533682615120565b9050602081019050919050565b828183375f83830152505050565b5f61536361535e84615313565b6152f9565b90508281526020810184848401111561537f5761537e615297565b5b61538a848285615343565b509392505050565b5f82601f8301126153a6576153a5615293565b5b81356153b6848260208601615351565b91505092915050565b5f602082840312156153d4576153d361528b565b5b5f82013567ffffffffffffffff8111156153f1576153f061528f565b5b6153fd84828501615392565b91505092915050565b5f819050919050565b61541881615406565b82525050565b5f6020820190506154315f83018461540f565b92915050565b61544081615406565b811461544a575f80fd5b50565b5f8135905061545b81615437565b92915050565b5f80604083850312156154775761547661528b565b5b5f6154848582860161544d565b92505060206154958582860161544d565b9150509250929050565b6154a88161507c565b81146154b2575f80fd5b50565b5f813590506154c38161549f565b92915050565b5f80604083850312156154df576154de61528b565b5b5f6154ec858286016154b5565b92505060206154fd8582860161544d565b9150509250929050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6155398161507c565b82525050565b5f61554a8383615530565b60208301905092915050565b5f602082019050919050565b5f61556c82615507565b6155768185615511565b935061558183615521565b805f5b838110156155b1578151615598888261553f565b97506155a383615556565b925050600181019050615584565b5085935050505092915050565b5f6020820190508181035f8301526155d68184615562565b905092915050565b5f602082840312156155f3576155f261528b565b5b5f6156008482850161544d565b91505092915050565b5f8115159050919050565b61561d81615609565b82525050565b5f6020820190506156365f830184615614565b92915050565b5f80604083850312156156525761565161528b565b5b5f83013567ffffffffffffffff81111561566f5761566e61528f565b5b61567b85828601615392565b925050602061568c8582860161544d565b9150509250929050565b5f602082840312156156ab576156aa61528b565b5b5f6156b8848285016154b5565b91505092915050565b5f80604083850312156156d7576156d661528b565b5b5f83013567ffffffffffffffff8111156156f4576156f361528f565b5b61570085828601615392565b9250506020615711858286016154b5565b9150509250929050565b5f67ffffffffffffffff8211156157355761573461529b565b5b602082029050602081019050919050565b5f80fd5b5f61575c6157578461571b565b6152f9565b9050808382526020820190506020840283018581111561577f5761577e615746565b5b835b818110156157c657803567ffffffffffffffff8111156157a4576157a3615293565b5b8086016157b18982615392565b85526020850194505050602081019050615781565b5050509392505050565b5f82601f8301126157e4576157e3615293565b5b81356157f484826020860161574a565b91505092915050565b5f602082840312156158125761581161528b565b5b5f82013567ffffffffffffffff81111561582f5761582e61528f565b5b61583b848285016157d0565b91505092915050565b5f67ffffffffffffffff82111561585e5761585d61529b565b5b602082029050602081019050919050565b5f61588161587c84615844565b6152f9565b905080838252602082019050602084028301858111156158a4576158a3615746565b5b835b818110156158cd57806158b9888261544d565b8452602084019350506020810190506158a6565b5050509392505050565b5f82601f8301126158eb576158ea615293565b5b81356158fb84826020860161586f565b91505092915050565b5f806040838503121561591a5761591961528b565b5b5f83013567ffffffffffffffff8111156159375761593661528f565b5b615943858286016157d0565b925050602083013567ffffffffffffffff8111156159645761596361528f565b5b615970858286016158d7565b9150509250929050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6159ac81615406565b82525050565b5f6159bd83836159a3565b60208301905092915050565b5f602082019050919050565b5f6159df8261597a565b6159e98185615984565b93506159f483615994565b805f5b83811015615a24578151615a0b88826159b2565b9750615a16836159c9565b9250506001810190506159f7565b5085935050505092915050565b5f6020820190508181035f830152615a4981846159d5565b905092915050565b5f8060408385031215615a6757615a6661528b565b5b5f83013567ffffffffffffffff811115615a8457615a8361528f565b5b615a9085828601615392565b925050602083013567ffffffffffffffff811115615ab157615ab061528f565b5b615abd85828601615392565b9150509250929050565b5f604082019050615ada5f83018561508d565b615ae7602083018461540f565b9392505050565b5f81519050615afc81615437565b92915050565b5f60208284031215615b1757615b1661528b565b5b5f615b2484828501615aee565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f615b6482615406565b9150615b6f83615406565b9250828203905081811115615b8757615b86615b2d565b5b92915050565b7f496e73756666696369656e7420746f6b656e2062616c616e63650000000000005f82015250565b5f615bc1601a8361521a565b9150615bcc82615b8d565b602082019050919050565b5f6020820190508181035f830152615bee81615bb5565b9050919050565b5f615bff82615406565b9150615c0a83615406565b9250828201905080821115615c2257615c21615b2d565b5b92915050565b5f606082019050615c3b5f83018661540f565b615c48602083018561540f565b8181036040830152615c5a818461522a565b9050949350505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f6002820490506001821680615ca857607f821691505b602082108103615cbb57615cba615c64565b5b50919050565b5f819050815f5260205f209050919050565b5f8154615cdf81615c91565b615ce9818661521a565b9450600182165f8114615d035760018114615d1957615d4b565b60ff198316865281151560200286019350615d4b565b615d2285615cc1565b5f5b83811015615d4357815481890152600182019150602081019050615d24565b808801955050505b50505092915050565b5f819050919050565b5f819050919050565b5f615d80615d7b615d7684615d54565b615d5d565b615406565b9050919050565b615d9081615d66565b82525050565b5f60a0820190508181035f830152615dae8188615cd3565b9050615dbd602083018761508d565b615dca604083018661540f565b615dd7606083018561540f565b615de46080830184615d87565b9695505050505050565b7f4f6e6c79206f776e6572000000000000000000000000000000000000000000005f82015250565b5f615e22600a8361521a565b9150615e2d82615dee565b602082019050919050565b5f6020820190508181035f830152615e4f81615e16565b9050919050565b7f4d7573742073756d20746f2031303030300000000000000000000000000000005f82015250565b5f615e8a60118361521a565b9150615e9582615e56565b602082019050919050565b5f6020820190508181035f830152615eb781615e7e565b9050919050565b5f604082019050615ed15f83018561540f565b615ede602083018461540f565b9392505050565b5f81905092915050565b5f615ef9826150de565b615f038185615ee5565b9350615f138185602086016150f8565b80840191505092915050565b5f615f2a8284615eef565b915081905092915050565b7f4e6f7420616e206578746572696f72206d656d626572000000000000000000005f82015250565b5f615f6960168361521a565b9150615f7482615f35565b602082019050919050565b5f6020820190508181035f830152615f9681615f5d565b9050919050565b7f4d75737420626520612062756e646c65206d656d6265720000000000000000005f82015250565b5f615fd160178361521a565b9150615fdc82615f9d565b602082019050919050565b5f6020820190508181035f830152615ffe81615fc5565b9050919050565b7f5a6f6e65206f7574206f662072616e67650000000000000000000000000000005f82015250565b5f61603960118361521a565b915061604482616005565b602082019050919050565b5f6020820190508181035f8301526160668161602d565b9050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026160b77fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8261607c565b6160c1868361607c565b95508019841693508086168417925050509392505050565b5f6160f36160ee6160e984615406565b615d5d565b615406565b9050919050565b5f819050919050565b61610c836160d9565b616120616118826160fa565b848454616088565b825550505050565b5f90565b616134616128565b61613f818484616103565b505050565b5b81811015616162576161575f8261612c565b600181019050616145565b5050565b601f8211156161a75761617881615cc1565b6161818461606d565b81016020851015616190578190505b6161a461619c8561606d565b830182616144565b50505b505050565b5f82821c905092915050565b5f6161c75f19846008026161ac565b1980831691505092915050565b5f6161df83836161b8565b9150826002028217905092915050565b6161f8826150de565b67ffffffffffffffff8111156162115761621061529b565b5b61621b8254615c91565b616226828285616166565b5f60209050601f831160018114616257575f8415616245578287015190505b61624f85826161d4565b8655506162b6565b601f19841661626586615cc1565b5f5b8281101561628c57848901518255600182019150602085019450602081019050616267565b868310156162a957848901516162a5601f8916826161b8565b8355505b6001600288020188555050505b505050505050565b5f6040820190508181035f8301526162d6818561522a565b90506162e5602083018461540f565b9392505050565b5f6080820190508181035f8301526163048187615cd3565b90508181036020830152616318818661522a565b9050616327604083018561540f565b616334606083018461540f565b95945050505050565b7f4e6f2061637469766520656c656374696f6e00000000000000000000000000005f82015250565b5f61637160128361521a565b915061637c8261633d565b602082019050919050565b5f6020820190508181035f83015261639e81616365565b9050919050565b7f416c726561647920636c61696d656400000000000000000000000000000000005f82015250565b5f6163d9600f8361521a565b91506163e4826163a5565b602082019050919050565b5f6020820190508181035f830152616406816163cd565b9050919050565b7f496e76616c69642062656e6566696369617279000000000000000000000000005f82015250565b5f61644160138361521a565b915061644c8261640d565b602082019050919050565b5f6020820190508181035f83015261646e81616435565b9050919050565b7f4e6f742061206d656d62657200000000000000000000000000000000000000005f82015250565b5f6164a9600c8361521a565b91506164b482616475565b602082019050919050565b5f6020820190508181035f8301526164d68161649d565b9050919050565b5f6080820190508181035f8301526164f58187615cd3565b90508181036020830152616509818661522a565b9050616518604083018561508d565b616525606083018461540f565b95945050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f815461656781615c91565b6165718186615ee5565b9450600182165f811461658b57600181146165a0576165d2565b60ff19831686528115158202860193506165d2565b6165a985615cc1565b5f5b838110156165ca578154818901526001820191506020810190506165ab565b838801955050505b50505092915050565b5f6165e6828461655b565b915081905092915050565b5f6165fb82615406565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361662d5761662c615b2d565b5b600182019050919050565b7f4e6f2063616e64696461746573000000000000000000000000000000000000005f82015250565b5f61666c600d8361521a565b915061667782616638565b602082019050919050565b5f6020820190508181035f83015261669981616660565b9050919050565b7f57696e6e6572206d757374206861766520636c61696d656420746f20726563655f8201527f697665206f776e65727368697000000000000000000000000000000000000000602082015250565b5f6166fa602d8361521a565b9150616705826166a0565b604082019050919050565b5f6020820190508181035f830152616727816166ee565b9050919050565b7f4d7573742062652061206d656d626572000000000000000000000000000000005f82015250565b5f61676260108361521a565b915061676d8261672e565b602082019050919050565b5f6020820190508181035f83015261678f81616756565b9050919050565b7f416c726561647920612063616e646964617465000000000000000000000000005f82015250565b5f6167ca60138361521a565b91506167d582616796565b602082019050919050565b5f6020820190508181035f8301526167f7816167be565b9050919050565b5f6060820190508181035f8301526168168186615cd3565b9050818103602083015261682a818561522a565b9050616839604083018461508d565b949350505050565b7f4172726179206c656e677468206d69736d6174636800000000000000000000005f82015250565b5f61687560158361521a565b915061688082616841565b602082019050919050565b5f6020820190508181035f8301526168a281616869565b9050919050565b7f55736572206d75737420626520612062756e646c65206d656d626572000000005f82015250565b5f6168dd601c8361521a565b91506168e8826168a9565b602082019050919050565b5f6020820190508181035f83015261690a816168d1565b9050919050565b7f50657263656e74616765206d75737420626520706f73697469766500000000005f82015250565b5f616945601b8361521a565b915061695082616911565b602082019050919050565b5f6020820190508181035f83015261697281616939565b9050919050565b7f50657263656e7461676573206d7573742073756d20746f2031303030300000005f82015250565b5f6169ad601d8361521a565b91506169b882616979565b602082019050919050565b5f6020820190508181035f8301526169da816169a1565b9050919050565b5f6040820190508181035f8301526169f98185615187565b90508181036020830152616a0d81846159d5565b90509392505050565b7f53746565706e657373206d7573742062652030203c2073203c203165313800005f82015250565b5f616a4a601e8361521a565b9150616a5582616a16565b602082019050919050565b5f6020820190508181035f830152616a7781616a3e565b9050919050565b7f566f746572206d7573742062652061206d656d626572000000000000000000005f82015250565b5f616ab260168361521a565b9150616abd82616a7e565b602082019050919050565b5f6020820190508181035f830152616adf81616aa6565b9050919050565b7f4e6f7420612076616c69642063616e64696461746500000000000000000000005f82015250565b5f616b1a60158361521a565b9150616b2582616ae6565b602082019050919050565b5f6020820190508181035f830152616b4781616b0e565b9050919050565b7f416c726561647920766f746564000000000000000000000000000000000000005f82015250565b5f616b82600d8361521a565b9150616b8d82616b4e565b602082019050919050565b5f6020820190508181035f830152616baf81616b76565b9050919050565b5f6040820190508181035f830152616bce818561522a565b90508181036020830152616be2818461522a565b90509392505050565b7f4469766973696f6e206279207a65726f000000000000000000000000000000005f82015250565b5f616c1f60108361521a565b9150616c2a82616beb565b602082019050919050565b5f6020820190508181035f830152616c4c81616c13565b9050919050565b5f616c5d82615406565b9150616c6883615406565b9250828202616c7681615406565b91508282048414831517616c8d57616c8c615b2d565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f616ccb82615406565b9150616cd683615406565b925082616ce657616ce5616c94565b5b828204905092915050565b5f81905092915050565b5f819050815f5260205f209050919050565b5f8154616d1981615c91565b616d238186616cf1565b9450600182165f8114616d3d5760018114616d5257616d84565b60ff1983168652811515820286019350616d84565b616d5b85616cfb565b5f5b83811015616d7c57815481890152600182019150602081019050616d5d565b838801955050505b50505092915050565b5f616d988284616d0d565b915081905092915050565b5f81549050616db181615c91565b9050919050565b5f819050815f5260205f209050919050565b818103616dd8575050616ead565b616de182616da3565b67ffffffffffffffff811115616dfa57616df961529b565b5b616e048254615c91565b616e0f828285616166565b5f601f831160018114616e3c575f8415616e2a578287015490505b616e3485826161d4565b865550616ea6565b601f198416616e4a87616db8565b9650616e5586615cc1565b5f5b82811015616e7c57848901548255600182019150600185019450602081019050616e57565b86831015616e995784890154616e95601f8916826161b8565b8355505b6001600288020188555050505b5050505050505b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b50565b5f616eea5f83616cf1565b9150616ef582616edc565b5f82019050919050565b5f616f0982616edf565b9150819050919050565b7f45544820636c61696d206661696c6564000000000000000000000000000000005f82015250565b5f616f4760108361521a565b9150616f5282616f13565b602082019050919050565b5f6020820190508181035f830152616f7481616f3b565b9050919050565b7f455448207472616e73666572206661696c6564000000000000000000000000005f82015250565b5f616faf60138361521a565b9150616fba82616f7b565b602082019050919050565b5f6020820190508181035f830152616fdc81616fa3565b9050919050565b5f606082019050616ff65f83018661540f565b6170036020830185615614565b8181036040830152617015818461522a565b9050949350505050565b5f60a0820190508181035f8301526170378188615cd3565b9050818103602083015261704b818761522a565b905061705a604083018661508d565b617067606083018561540f565b8181036080830152617079818461522a565b9050969550505050505056fea2646970667358221220e61ca743da8bf74312a4d4f6b348b3282428098da8234b281a8987056df9bb6364736f6c63430008140033";
24538
+ const BundleABI = {
24539
+ abi,
24540
+ bytecode
24541
+ };
24003
24542
  const HolonsABI = [
24004
24543
  {
24005
24544
  type: "function",
@@ -25407,7 +25946,7 @@ class ContractDeployer {
25407
25946
  */
25408
25947
  async _loadEthers() {
25409
25948
  if (!this.ethers) {
25410
- this.ethers = await import("./index-Cz-PLCUR.js");
25949
+ this.ethers = await import("./index-JFz-dW43.js");
25411
25950
  }
25412
25951
  return this.ethers;
25413
25952
  }
@@ -25615,6 +26154,7 @@ class ContractDeployer {
25615
26154
  }
25616
26155
  /**
25617
26156
  * Deploy a Bundle contract (2-way split with steepness)
26157
+ * This is the simplest way to deploy a holon - no registry needed!
25618
26158
  * @param {string} name - Contract name
25619
26159
  * @param {number} [steepness=500000000000000000n] - Steepness factor (0.5e18 = 50% decay)
25620
26160
  * @param {number} [nZones=6] - Number of zones
@@ -25627,18 +26167,41 @@ class ContractDeployer {
25627
26167
  const deployerAddress = await signer.getAddress();
25628
26168
  const creatorUserId = options.creatorUserId || "creator";
25629
26169
  const steepnessValue = steepness || ethers.parseEther("0.5");
26170
+ const abi2 = BundleABI.abi || BundleABI;
26171
+ const bytecode2 = BundleABI.bytecode;
26172
+ if (!bytecode2) {
26173
+ throw new Error("Bundle bytecode not available. Please rebuild contracts.");
26174
+ }
25630
26175
  const result = await this._deployContract(
25631
- BundleABI.abi,
25632
- BundleABI.bytecode,
26176
+ abi2,
26177
+ bytecode2,
25633
26178
  [deployerAddress, creatorUserId, name2, steepnessValue, nZones]
25634
26179
  );
25635
26180
  return {
25636
26181
  ...result,
25637
26182
  type: "Bundle",
25638
26183
  steepness: steepnessValue,
25639
- nZones
26184
+ nZones,
26185
+ creatorUserId,
26186
+ name: name2
25640
26187
  };
25641
26188
  }
26189
+ /**
26190
+ * Deploy a Bundle contract directly (simplified 1-click deployment)
26191
+ * No registry needed - just deploys and returns the address
26192
+ * @param {string} holonId - Unique identifier for the holon
26193
+ * @param {string} name - Display name for the holon
26194
+ * @param {Object} [options] - Options { steepness, nZones }
26195
+ * @returns {Promise<{address, txHash, contract}>}
26196
+ */
26197
+ async deployBundleDirect(holonId, name2, options = {}) {
26198
+ const steepness = options.steepness;
26199
+ const nZones = options.nZones || 6;
26200
+ return this.deployBundle(name2, steepness, nZones, {
26201
+ creatorUserId: holonId,
26202
+ ...options
26203
+ });
26204
+ }
25642
26205
  /**
25643
26206
  * Deploy a TestToken (ERC20 for testing)
25644
26207
  * @param {string} [initialSupply='1000000'] - Initial supply in whole tokens
@@ -25698,11 +26261,11 @@ class ContractDeployer {
25698
26261
  * @returns {Promise<Contract>}
25699
26262
  */
25700
26263
  async getContract(address, type2) {
25701
- const abi = ABIs[type2];
25702
- if (!abi) {
26264
+ const abi2 = ABIs[type2];
26265
+ if (!abi2) {
25703
26266
  throw new Error(`Unknown contract type: ${type2}`);
25704
26267
  }
25705
- return this.chainManager.getContract(address, abi.abi);
26268
+ return this.chainManager.getContract(address, abi2.abi);
25706
26269
  }
25707
26270
  /**
25708
26271
  * Get deployed contract addresses
@@ -25722,8 +26285,8 @@ class ContractDeployer {
25722
26285
  * Internal: Deploy a contract
25723
26286
  * @private
25724
26287
  */
25725
- async _deployContract(abi, bytecode, constructorArgs) {
25726
- const result = await this.chainManager.deployContract(abi, bytecode, constructorArgs);
26288
+ async _deployContract(abi2, bytecode2, constructorArgs) {
26289
+ const result = await this.chainManager.deployContract(abi2, bytecode2, constructorArgs);
25727
26290
  return {
25728
26291
  contract: result.contract,
25729
26292
  address: result.address,
@@ -25745,7 +26308,7 @@ class ContractOperations {
25745
26308
  */
25746
26309
  async _loadEthers() {
25747
26310
  if (!this.ethers) {
25748
- this.ethers = await import("./index-Cz-PLCUR.js");
26311
+ this.ethers = await import("./index-JFz-dW43.js");
25749
26312
  }
25750
26313
  return this.ethers;
25751
26314
  }
@@ -26420,11 +26983,11 @@ class HolonContracts {
26420
26983
  if (this.holonContracts.has(holonId)) {
26421
26984
  throw new Error(`Holon ${holonId} already has a contract. Use unlink() first.`);
26422
26985
  }
26423
- const abi = ABIs[type2];
26424
- if (!abi) {
26986
+ const abi2 = ABIs[type2];
26987
+ if (!abi2) {
26425
26988
  throw new Error(`Unknown contract type: ${type2}`);
26426
26989
  }
26427
- const contract = await this.chainManager.getContract(contractAddress, abi.abi);
26990
+ const contract = await this.chainManager.getContract(contractAddress, abi2.abi);
26428
26991
  try {
26429
26992
  await contract.name();
26430
26993
  } catch (error) {
@@ -26555,9 +27118,9 @@ class HolonContracts {
26555
27118
  try {
26556
27119
  const data = await this.storage.get?.(`holon_contract:${holonId}`);
26557
27120
  if (data && data.address && data.type) {
26558
- const abi = ABIs[data.type];
26559
- if (abi) {
26560
- const contract = await this.chainManager.getContract(data.address, abi.abi);
27121
+ const abi2 = ABIs[data.type];
27122
+ if (abi2) {
27123
+ const contract = await this.chainManager.getContract(data.address, abi2.abi);
26561
27124
  const operations = new ContractOperations(contract, data.type, this.chainManager);
26562
27125
  this.holonContracts.set(holonId, {
26563
27126
  ...data,
@@ -31861,17 +32424,17 @@ class Interface {
31861
32424
  * Create a new Interface for the %%fragments%%.
31862
32425
  */
31863
32426
  constructor(fragments) {
31864
- let abi = [];
32427
+ let abi2 = [];
31865
32428
  if (typeof fragments === "string") {
31866
- abi = JSON.parse(fragments);
32429
+ abi2 = JSON.parse(fragments);
31867
32430
  } else {
31868
- abi = fragments;
32431
+ abi2 = fragments;
31869
32432
  }
31870
32433
  this.#functions = /* @__PURE__ */ new Map();
31871
32434
  this.#errors = /* @__PURE__ */ new Map();
31872
32435
  this.#events = /* @__PURE__ */ new Map();
31873
32436
  const frags = [];
31874
- for (const a of abi) {
32437
+ for (const a of abi2) {
31875
32438
  try {
31876
32439
  frags.push(Fragment.from(a));
31877
32440
  } catch (error) {
@@ -31935,16 +32498,16 @@ class Interface {
31935
32498
  */
31936
32499
  format(minimal) {
31937
32500
  const format = minimal ? "minimal" : "full";
31938
- const abi = this.fragments.map((f) => f.format(format));
31939
- return abi;
32501
+ const abi2 = this.fragments.map((f) => f.format(format));
32502
+ return abi2;
31940
32503
  }
31941
32504
  /**
31942
32505
  * Return the JSON-encoded ABI. This is the format Solidiy
31943
32506
  * returns.
31944
32507
  */
31945
32508
  formatJson() {
31946
- const abi = this.fragments.map((f) => f.format("json"));
31947
- return JSON.stringify(abi.map((j) => JSON.parse(j)));
32509
+ const abi2 = this.fragments.map((f) => f.format("json"));
32510
+ return JSON.stringify(abi2.map((j) => JSON.parse(j)));
31948
32511
  }
31949
32512
  /**
31950
32513
  * The ABI coder that will be used to encode and decode binary
@@ -34598,12 +35161,12 @@ class BaseContract {
34598
35161
  * optionally connected to a %%runner%% to perform operations on behalf
34599
35162
  * of.
34600
35163
  */
34601
- constructor(target, abi, runner, _deployTx) {
35164
+ constructor(target, abi2, runner, _deployTx) {
34602
35165
  assertArgument(typeof target === "string" || isAddressable(target), "invalid value for Contract target", "target", target);
34603
35166
  if (runner == null) {
34604
35167
  runner = null;
34605
35168
  }
34606
- const iface = Interface.from(abi);
35169
+ const iface = Interface.from(abi2);
34607
35170
  defineProperties(this, { target, runner, interface: iface });
34608
35171
  Object.defineProperty(this, internal, { value: {} });
34609
35172
  let addrPromise;
@@ -34966,10 +35529,10 @@ class BaseContract {
34966
35529
  /**
34967
35530
  * Create a new Class for the %%abi%%.
34968
35531
  */
34969
- static buildClass(abi) {
35532
+ static buildClass(abi2) {
34970
35533
  class CustomContract extends BaseContract {
34971
35534
  constructor(address, runner = null) {
34972
- super(address, abi, runner);
35535
+ super(address, abi2, runner);
34973
35536
  }
34974
35537
  }
34975
35538
  return CustomContract;
@@ -34977,11 +35540,11 @@ class BaseContract {
34977
35540
  /**
34978
35541
  * Create a new BaseContract with a specified Interface.
34979
35542
  */
34980
- static from(target, abi, runner) {
35543
+ static from(target, abi2, runner) {
34981
35544
  if (runner == null) {
34982
35545
  runner = null;
34983
35546
  }
34984
- const contract = new this(target, abi, runner);
35547
+ const contract = new this(target, abi2, runner);
34985
35548
  return contract;
34986
35549
  }
34987
35550
  }
@@ -35016,11 +35579,11 @@ class ContractQueries {
35016
35579
  * @returns {ethers.Contract}
35017
35580
  */
35018
35581
  getContract(address, flavor) {
35019
- const abi = ContractABIs[flavor];
35020
- if (!abi) {
35582
+ const abi2 = ContractABIs[flavor];
35583
+ if (!abi2) {
35021
35584
  throw new Error(`Unknown contract flavor: ${flavor}`);
35022
35585
  }
35023
- return new Contract(address, abi, this.provider);
35586
+ return new Contract(address, abi2, this.provider);
35024
35587
  }
35025
35588
  /**
35026
35589
  * Auto-detect contract flavor by calling flavor()
@@ -36993,6 +37556,7 @@ class HoloSphereBase extends HoloSphere$1 {
36993
37556
  if (!Array.isArray(data.inbound)) data.inbound = [];
36994
37557
  if (!Array.isArray(data.outbound)) data.outbound = [];
36995
37558
  if (!data.lensConfig || typeof data.lensConfig !== "object") data.lensConfig = {};
37559
+ if (!data.partnerNames || typeof data.partnerNames !== "object") data.partnerNames = {};
36996
37560
  if (!Array.isArray(data.federated)) {
36997
37561
  const allFederated = /* @__PURE__ */ new Set([...data.inbound, ...data.outbound]);
36998
37562
  data.federated = Array.from(allFederated);
@@ -37000,7 +37564,7 @@ class HoloSphereBase extends HoloSphere$1 {
37000
37564
  return data;
37001
37565
  }
37002
37566
  async federateHolon(sourceHolon, targetHolon, options = {}) {
37003
- const { lensConfig = { inbound: [], outbound: [] } } = options;
37567
+ const { lensConfig = { inbound: [], outbound: [] }, partnerName = null } = options;
37004
37568
  if (sourceHolon === targetHolon) {
37005
37569
  throw new Error("Cannot federate a holon with itself");
37006
37570
  }
@@ -37011,15 +37575,20 @@ class HoloSphereBase extends HoloSphere$1 {
37011
37575
  inbound: [],
37012
37576
  outbound: [],
37013
37577
  lensConfig: {},
37578
+ partnerNames: {},
37014
37579
  timestamp: Date.now()
37015
37580
  };
37016
37581
  if (!Array.isArray(federationData.federated)) federationData.federated = [];
37017
37582
  if (!Array.isArray(federationData.inbound)) federationData.inbound = [];
37018
37583
  if (!Array.isArray(federationData.outbound)) federationData.outbound = [];
37019
37584
  if (!federationData.lensConfig || typeof federationData.lensConfig !== "object") federationData.lensConfig = {};
37585
+ if (!federationData.partnerNames || typeof federationData.partnerNames !== "object") federationData.partnerNames = {};
37020
37586
  if (!federationData.federated.includes(targetHolon)) {
37021
37587
  federationData.federated.push(targetHolon);
37022
37588
  }
37589
+ if (partnerName) {
37590
+ federationData.partnerNames[targetHolon] = partnerName;
37591
+ }
37023
37592
  if (lensConfig.outbound && lensConfig.outbound.length > 0) {
37024
37593
  if (!federationData.outbound.includes(targetHolon)) {
37025
37594
  federationData.outbound.push(targetHolon);
@@ -37420,4 +37989,4 @@ export {
37420
37989
  exists as y,
37421
37990
  bytes as z
37422
37991
  };
37423
- //# sourceMappingURL=index-CV0eOogK.js.map
37992
+ //# sourceMappingURL=index-NOravBLu.js.map