@unicitylabs/sphere-sdk 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.cjs +545 -22
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +243 -16
- package/dist/core/index.d.ts +243 -16
- package/dist/core/index.js +544 -22
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +1 -3
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +1 -3
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +1 -3
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +3 -3
- package/dist/impl/nodejs/index.d.ts +3 -3
- package/dist/impl/nodejs/index.js +1 -3
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +544 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +244 -17
- package/dist/index.d.ts +244 -17
- package/dist/index.js +543 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1605,6 +1605,7 @@ async function sendAlpha(wallet, toAddress, amountAlpha, fromAddress) {
|
|
|
1605
1605
|
// modules/payments/L1PaymentsModule.ts
|
|
1606
1606
|
var L1PaymentsModule = class {
|
|
1607
1607
|
_initialized = false;
|
|
1608
|
+
_disabled = false;
|
|
1608
1609
|
_config;
|
|
1609
1610
|
_identity;
|
|
1610
1611
|
_addresses = [];
|
|
@@ -1652,6 +1653,9 @@ var L1PaymentsModule = class {
|
|
|
1652
1653
|
* (e.g. by the address scanner), this is a no-op.
|
|
1653
1654
|
*/
|
|
1654
1655
|
async ensureConnected() {
|
|
1656
|
+
if (this._disabled) {
|
|
1657
|
+
throw new Error("L1 provider is disabled");
|
|
1658
|
+
}
|
|
1655
1659
|
if (!isWebSocketConnected() && this._config.electrumUrl) {
|
|
1656
1660
|
await connect(this._config.electrumUrl);
|
|
1657
1661
|
}
|
|
@@ -1665,6 +1669,24 @@ var L1PaymentsModule = class {
|
|
|
1665
1669
|
this._addresses = [];
|
|
1666
1670
|
this._wallet = void 0;
|
|
1667
1671
|
}
|
|
1672
|
+
/**
|
|
1673
|
+
* Disable this module — disconnect WebSocket and block operations until re-enabled.
|
|
1674
|
+
*/
|
|
1675
|
+
disable() {
|
|
1676
|
+
this._disabled = true;
|
|
1677
|
+
if (isWebSocketConnected()) {
|
|
1678
|
+
disconnect();
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
/**
|
|
1682
|
+
* Re-enable this module. Connection will be established lazily on next operation.
|
|
1683
|
+
*/
|
|
1684
|
+
enable() {
|
|
1685
|
+
this._disabled = false;
|
|
1686
|
+
}
|
|
1687
|
+
get disabled() {
|
|
1688
|
+
return this._disabled;
|
|
1689
|
+
}
|
|
1668
1690
|
/**
|
|
1669
1691
|
* Check if a string looks like an L1 address (alpha1... or alphat1...)
|
|
1670
1692
|
*/
|
|
@@ -2558,9 +2580,7 @@ var DEV_AGGREGATOR_URL = "https://dev-aggregator.dyndns.org/rpc";
|
|
|
2558
2580
|
var TEST_AGGREGATOR_URL = "https://goggregator-test.unicity.network";
|
|
2559
2581
|
var DEFAULT_AGGREGATOR_TIMEOUT = 3e4;
|
|
2560
2582
|
var DEFAULT_IPFS_GATEWAYS = [
|
|
2561
|
-
"https://
|
|
2562
|
-
"https://dweb.link",
|
|
2563
|
-
"https://ipfs.io"
|
|
2583
|
+
"https://unicity-ipfs1.dyndns.org"
|
|
2564
2584
|
];
|
|
2565
2585
|
var DEFAULT_IPFS_BOOTSTRAP_PEERS = [
|
|
2566
2586
|
"/dns4/unicity-ipfs2.dyndns.org/tcp/4001/p2p/12D3KooWLNi5NDPPHbrfJakAQqwBqymYTTwMQXQKEWuCrJNDdmfh",
|
|
@@ -5525,7 +5545,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5525
5545
|
*/
|
|
5526
5546
|
async getFiatBalance() {
|
|
5527
5547
|
const assets = await this.getAssets();
|
|
5528
|
-
if (!this.priceProvider) {
|
|
5548
|
+
if (!this.priceProvider || this.isPriceDisabled()) {
|
|
5529
5549
|
return null;
|
|
5530
5550
|
}
|
|
5531
5551
|
let total = 0;
|
|
@@ -5560,7 +5580,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5560
5580
|
*/
|
|
5561
5581
|
async getAssets(coinId) {
|
|
5562
5582
|
const rawAssets = this.aggregateTokens(coinId);
|
|
5563
|
-
if (!this.priceProvider || rawAssets.length === 0) {
|
|
5583
|
+
if (!this.priceProvider || this.isPriceDisabled() || rawAssets.length === 0) {
|
|
5564
5584
|
return rawAssets;
|
|
5565
5585
|
}
|
|
5566
5586
|
try {
|
|
@@ -6654,18 +6674,38 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6654
6674
|
}, _PaymentsModule.SYNC_DEBOUNCE_MS);
|
|
6655
6675
|
}
|
|
6656
6676
|
/**
|
|
6657
|
-
* Get all active token storage providers
|
|
6677
|
+
* Get all active (non-disabled) token storage providers
|
|
6658
6678
|
*/
|
|
6659
6679
|
getTokenStorageProviders() {
|
|
6680
|
+
let providers;
|
|
6660
6681
|
if (this.deps.tokenStorageProviders && this.deps.tokenStorageProviders.size > 0) {
|
|
6661
|
-
|
|
6682
|
+
providers = this.deps.tokenStorageProviders;
|
|
6683
|
+
} else if (this.deps.tokenStorage) {
|
|
6684
|
+
providers = /* @__PURE__ */ new Map();
|
|
6685
|
+
providers.set(this.deps.tokenStorage.id, this.deps.tokenStorage);
|
|
6686
|
+
} else {
|
|
6687
|
+
return /* @__PURE__ */ new Map();
|
|
6662
6688
|
}
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6689
|
+
const disabled = this.deps.disabledProviderIds;
|
|
6690
|
+
if (disabled && disabled.size > 0) {
|
|
6691
|
+
const filtered = /* @__PURE__ */ new Map();
|
|
6692
|
+
for (const [id, provider] of providers) {
|
|
6693
|
+
if (!disabled.has(id)) {
|
|
6694
|
+
filtered.set(id, provider);
|
|
6695
|
+
}
|
|
6696
|
+
}
|
|
6697
|
+
return filtered;
|
|
6667
6698
|
}
|
|
6668
|
-
return
|
|
6699
|
+
return providers;
|
|
6700
|
+
}
|
|
6701
|
+
/**
|
|
6702
|
+
* Check if the price provider is disabled via the disabled providers set.
|
|
6703
|
+
*/
|
|
6704
|
+
isPriceDisabled() {
|
|
6705
|
+
const disabled = this.deps?.disabledProviderIds;
|
|
6706
|
+
if (!disabled || disabled.size === 0) return false;
|
|
6707
|
+
const priceId = this.priceProvider?.id ?? "price";
|
|
6708
|
+
return disabled.has(priceId);
|
|
6669
6709
|
}
|
|
6670
6710
|
/**
|
|
6671
6711
|
* Replace the set of token storage providers at runtime.
|
|
@@ -9172,6 +9212,9 @@ async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
|
9172
9212
|
};
|
|
9173
9213
|
}
|
|
9174
9214
|
|
|
9215
|
+
// core/Sphere.ts
|
|
9216
|
+
init_network();
|
|
9217
|
+
|
|
9175
9218
|
// serialization/wallet-text.ts
|
|
9176
9219
|
import CryptoJS7 from "crypto-js";
|
|
9177
9220
|
|
|
@@ -9900,6 +9943,10 @@ var Sphere = class _Sphere {
|
|
|
9900
9943
|
_groupChat = null;
|
|
9901
9944
|
// Events
|
|
9902
9945
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
9946
|
+
// Provider management
|
|
9947
|
+
_disabledProviders = /* @__PURE__ */ new Set();
|
|
9948
|
+
_providerEventCleanups = [];
|
|
9949
|
+
_lastProviderConnected = /* @__PURE__ */ new Map();
|
|
9903
9950
|
// ===========================================================================
|
|
9904
9951
|
// Constructor (private)
|
|
9905
9952
|
// ===========================================================================
|
|
@@ -10106,9 +10153,14 @@ var Sphere = class _Sphere {
|
|
|
10106
10153
|
throw new Error("Either mnemonic or masterKey is required");
|
|
10107
10154
|
}
|
|
10108
10155
|
console.log("[Sphere.import] Starting import...");
|
|
10109
|
-
|
|
10110
|
-
|
|
10111
|
-
|
|
10156
|
+
const needsClear = _Sphere.instance !== null || await _Sphere.exists(options.storage);
|
|
10157
|
+
if (needsClear) {
|
|
10158
|
+
console.log("[Sphere.import] Clearing existing wallet data...");
|
|
10159
|
+
await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
|
|
10160
|
+
console.log("[Sphere.import] Clear done");
|
|
10161
|
+
} else {
|
|
10162
|
+
console.log("[Sphere.import] No existing wallet \u2014 skipping clear");
|
|
10163
|
+
}
|
|
10112
10164
|
if (!options.storage.isConnected()) {
|
|
10113
10165
|
console.log("[Sphere.import] Reconnecting storage...");
|
|
10114
10166
|
await options.storage.connect();
|
|
@@ -11335,20 +11387,193 @@ var Sphere = class _Sphere {
|
|
|
11335
11387
|
// ===========================================================================
|
|
11336
11388
|
// Public Methods - Status
|
|
11337
11389
|
// ===========================================================================
|
|
11390
|
+
/**
|
|
11391
|
+
* Get aggregated status of all providers, grouped by role.
|
|
11392
|
+
*
|
|
11393
|
+
* @example
|
|
11394
|
+
* ```ts
|
|
11395
|
+
* const status = sphere.getStatus();
|
|
11396
|
+
* // status.transport[0].connected // true/false
|
|
11397
|
+
* // status.transport[0].metadata?.relays // { total: 3, connected: 2 }
|
|
11398
|
+
* // status.tokenStorage // all registered token storage providers
|
|
11399
|
+
* ```
|
|
11400
|
+
*/
|
|
11338
11401
|
getStatus() {
|
|
11402
|
+
const mkInfo = (provider, role, metadata) => ({
|
|
11403
|
+
id: provider.id,
|
|
11404
|
+
name: provider.name,
|
|
11405
|
+
role,
|
|
11406
|
+
status: provider.getStatus(),
|
|
11407
|
+
connected: provider.isConnected(),
|
|
11408
|
+
enabled: !this._disabledProviders.has(provider.id),
|
|
11409
|
+
...metadata ? { metadata } : {}
|
|
11410
|
+
});
|
|
11411
|
+
let transportMeta;
|
|
11412
|
+
const transport = this._transport;
|
|
11413
|
+
if (typeof transport.getRelays === "function") {
|
|
11414
|
+
const total = transport.getRelays().length;
|
|
11415
|
+
const connected = typeof transport.getConnectedRelays === "function" ? transport.getConnectedRelays().length : 0;
|
|
11416
|
+
transportMeta = { relays: { total, connected } };
|
|
11417
|
+
}
|
|
11418
|
+
const l1Module = this._payments.l1;
|
|
11419
|
+
const l1Providers = [];
|
|
11420
|
+
if (l1Module) {
|
|
11421
|
+
const wsConnected = isWebSocketConnected();
|
|
11422
|
+
l1Providers.push({
|
|
11423
|
+
id: "l1-alpha",
|
|
11424
|
+
name: "ALPHA L1",
|
|
11425
|
+
role: "l1",
|
|
11426
|
+
status: wsConnected ? "connected" : "disconnected",
|
|
11427
|
+
connected: wsConnected,
|
|
11428
|
+
enabled: !this._disabledProviders.has("l1-alpha")
|
|
11429
|
+
});
|
|
11430
|
+
}
|
|
11431
|
+
const priceProviders = [];
|
|
11432
|
+
if (this._priceProvider) {
|
|
11433
|
+
priceProviders.push({
|
|
11434
|
+
id: this._priceProviderId,
|
|
11435
|
+
name: this._priceProvider.platform ?? "Price",
|
|
11436
|
+
role: "price",
|
|
11437
|
+
status: "connected",
|
|
11438
|
+
connected: true,
|
|
11439
|
+
enabled: !this._disabledProviders.has(this._priceProviderId)
|
|
11440
|
+
});
|
|
11441
|
+
}
|
|
11339
11442
|
return {
|
|
11340
|
-
storage:
|
|
11341
|
-
|
|
11342
|
-
|
|
11443
|
+
storage: [mkInfo(this._storage, "storage")],
|
|
11444
|
+
tokenStorage: Array.from(this._tokenStorageProviders.values()).map(
|
|
11445
|
+
(p) => mkInfo(p, "token-storage")
|
|
11446
|
+
),
|
|
11447
|
+
transport: [mkInfo(this._transport, "transport", transportMeta)],
|
|
11448
|
+
oracle: [mkInfo(this._oracle, "oracle")],
|
|
11449
|
+
l1: l1Providers,
|
|
11450
|
+
price: priceProviders
|
|
11343
11451
|
};
|
|
11344
11452
|
}
|
|
11345
11453
|
async reconnect() {
|
|
11346
11454
|
await this._transport.disconnect();
|
|
11347
11455
|
await this._transport.connect();
|
|
11456
|
+
}
|
|
11457
|
+
// ===========================================================================
|
|
11458
|
+
// Public Methods - Provider Management
|
|
11459
|
+
// ===========================================================================
|
|
11460
|
+
/**
|
|
11461
|
+
* Disable a provider at runtime. The provider stays registered but is disconnected
|
|
11462
|
+
* and skipped during operations (e.g., sync).
|
|
11463
|
+
*
|
|
11464
|
+
* Main storage provider cannot be disabled.
|
|
11465
|
+
*
|
|
11466
|
+
* @returns true if successfully disabled, false if provider not found
|
|
11467
|
+
*/
|
|
11468
|
+
async disableProvider(providerId) {
|
|
11469
|
+
if (providerId === this._storage.id) {
|
|
11470
|
+
throw new Error("Cannot disable the main storage provider");
|
|
11471
|
+
}
|
|
11472
|
+
const provider = this.findProviderById(providerId);
|
|
11473
|
+
if (!provider) return false;
|
|
11474
|
+
this._disabledProviders.add(providerId);
|
|
11475
|
+
try {
|
|
11476
|
+
if ("disable" in provider && typeof provider.disable === "function") {
|
|
11477
|
+
provider.disable();
|
|
11478
|
+
} else if ("shutdown" in provider && typeof provider.shutdown === "function") {
|
|
11479
|
+
await provider.shutdown();
|
|
11480
|
+
} else if ("disconnect" in provider && typeof provider.disconnect === "function") {
|
|
11481
|
+
await provider.disconnect();
|
|
11482
|
+
} else if ("clearCache" in provider && typeof provider.clearCache === "function") {
|
|
11483
|
+
provider.clearCache();
|
|
11484
|
+
}
|
|
11485
|
+
} catch {
|
|
11486
|
+
}
|
|
11487
|
+
this.emitEvent("connection:changed", {
|
|
11488
|
+
provider: providerId,
|
|
11489
|
+
connected: false,
|
|
11490
|
+
status: "disconnected",
|
|
11491
|
+
enabled: false
|
|
11492
|
+
});
|
|
11493
|
+
return true;
|
|
11494
|
+
}
|
|
11495
|
+
/**
|
|
11496
|
+
* Re-enable a previously disabled provider. Reconnects and resumes operations.
|
|
11497
|
+
*
|
|
11498
|
+
* @returns true if successfully enabled, false if provider not found
|
|
11499
|
+
*/
|
|
11500
|
+
async enableProvider(providerId) {
|
|
11501
|
+
const provider = this.findProviderById(providerId);
|
|
11502
|
+
if (!provider) return false;
|
|
11503
|
+
this._disabledProviders.delete(providerId);
|
|
11504
|
+
if ("enable" in provider && typeof provider.enable === "function") {
|
|
11505
|
+
provider.enable();
|
|
11506
|
+
this.emitEvent("connection:changed", {
|
|
11507
|
+
provider: providerId,
|
|
11508
|
+
connected: false,
|
|
11509
|
+
status: "disconnected",
|
|
11510
|
+
enabled: true
|
|
11511
|
+
});
|
|
11512
|
+
return true;
|
|
11513
|
+
}
|
|
11514
|
+
const hasLifecycle = "connect" in provider && typeof provider.connect === "function" || "initialize" in provider && typeof provider.initialize === "function";
|
|
11515
|
+
if (hasLifecycle) {
|
|
11516
|
+
try {
|
|
11517
|
+
if ("connect" in provider && typeof provider.connect === "function") {
|
|
11518
|
+
await provider.connect();
|
|
11519
|
+
} else if ("initialize" in provider && typeof provider.initialize === "function") {
|
|
11520
|
+
await provider.initialize();
|
|
11521
|
+
}
|
|
11522
|
+
} catch (err) {
|
|
11523
|
+
this.emitEvent("connection:changed", {
|
|
11524
|
+
provider: providerId,
|
|
11525
|
+
connected: false,
|
|
11526
|
+
status: "error",
|
|
11527
|
+
enabled: true,
|
|
11528
|
+
error: err instanceof Error ? err.message : String(err)
|
|
11529
|
+
});
|
|
11530
|
+
return false;
|
|
11531
|
+
}
|
|
11532
|
+
}
|
|
11348
11533
|
this.emitEvent("connection:changed", {
|
|
11349
|
-
provider:
|
|
11350
|
-
connected: true
|
|
11534
|
+
provider: providerId,
|
|
11535
|
+
connected: true,
|
|
11536
|
+
status: "connected",
|
|
11537
|
+
enabled: true
|
|
11351
11538
|
});
|
|
11539
|
+
return true;
|
|
11540
|
+
}
|
|
11541
|
+
/**
|
|
11542
|
+
* Check if a provider is currently enabled
|
|
11543
|
+
*/
|
|
11544
|
+
isProviderEnabled(providerId) {
|
|
11545
|
+
return !this._disabledProviders.has(providerId);
|
|
11546
|
+
}
|
|
11547
|
+
/**
|
|
11548
|
+
* Get the set of disabled provider IDs (for passing to modules)
|
|
11549
|
+
*/
|
|
11550
|
+
getDisabledProviderIds() {
|
|
11551
|
+
return this._disabledProviders;
|
|
11552
|
+
}
|
|
11553
|
+
/** Get the price provider's ID (implementation detail — not on PriceProvider interface) */
|
|
11554
|
+
get _priceProviderId() {
|
|
11555
|
+
if (!this._priceProvider) return "price";
|
|
11556
|
+
const p = this._priceProvider;
|
|
11557
|
+
return typeof p.id === "string" ? p.id : "price";
|
|
11558
|
+
}
|
|
11559
|
+
/**
|
|
11560
|
+
* Find a provider by ID across all provider collections
|
|
11561
|
+
*/
|
|
11562
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11563
|
+
findProviderById(providerId) {
|
|
11564
|
+
if (this._storage.id === providerId) return this._storage;
|
|
11565
|
+
if (this._transport.id === providerId) return this._transport;
|
|
11566
|
+
if (this._oracle.id === providerId) return this._oracle;
|
|
11567
|
+
if (this._tokenStorageProviders.has(providerId)) {
|
|
11568
|
+
return this._tokenStorageProviders.get(providerId);
|
|
11569
|
+
}
|
|
11570
|
+
if (this._priceProvider && this._priceProviderId === providerId) {
|
|
11571
|
+
return this._priceProvider;
|
|
11572
|
+
}
|
|
11573
|
+
if (providerId === "l1-alpha" && this._payments.l1) {
|
|
11574
|
+
return this._payments.l1;
|
|
11575
|
+
}
|
|
11576
|
+
return null;
|
|
11352
11577
|
}
|
|
11353
11578
|
// ===========================================================================
|
|
11354
11579
|
// Public Methods - Events
|
|
@@ -11691,6 +11916,33 @@ var Sphere = class _Sphere {
|
|
|
11691
11916
|
return;
|
|
11692
11917
|
}
|
|
11693
11918
|
try {
|
|
11919
|
+
if (this._identity?.directAddress && this._transport.resolve) {
|
|
11920
|
+
try {
|
|
11921
|
+
const existing = await this._transport.resolve(this._identity.directAddress);
|
|
11922
|
+
if (existing) {
|
|
11923
|
+
if (existing.nametag && !this._identity.nametag) {
|
|
11924
|
+
this._identity.nametag = existing.nametag;
|
|
11925
|
+
await this._updateCachedProxyAddress();
|
|
11926
|
+
const entry = await this.ensureAddressTracked(this._currentAddressIndex);
|
|
11927
|
+
let nametags = this._addressNametags.get(entry.addressId);
|
|
11928
|
+
if (!nametags) {
|
|
11929
|
+
nametags = /* @__PURE__ */ new Map();
|
|
11930
|
+
this._addressNametags.set(entry.addressId, nametags);
|
|
11931
|
+
}
|
|
11932
|
+
if (!nametags.has(0)) {
|
|
11933
|
+
nametags.set(0, existing.nametag);
|
|
11934
|
+
await this.persistAddressNametags();
|
|
11935
|
+
}
|
|
11936
|
+
this.emitEvent("nametag:recovered", { nametag: existing.nametag });
|
|
11937
|
+
}
|
|
11938
|
+
console.log("[Sphere] Existing binding found, skipping re-publish");
|
|
11939
|
+
return;
|
|
11940
|
+
}
|
|
11941
|
+
} catch (e) {
|
|
11942
|
+
console.warn("[Sphere] resolve() failed, skipping publish to avoid overwrite", e);
|
|
11943
|
+
return;
|
|
11944
|
+
}
|
|
11945
|
+
}
|
|
11694
11946
|
const nametag = this._identity?.nametag;
|
|
11695
11947
|
const success = await this._transport.publishIdentityBinding(
|
|
11696
11948
|
this._identity.chainPubkey,
|
|
@@ -11764,17 +12016,26 @@ var Sphere = class _Sphere {
|
|
|
11764
12016
|
// Public Methods - Lifecycle
|
|
11765
12017
|
// ===========================================================================
|
|
11766
12018
|
async destroy() {
|
|
12019
|
+
this.cleanupProviderEventSubscriptions();
|
|
11767
12020
|
this._payments.destroy();
|
|
11768
12021
|
this._communications.destroy();
|
|
11769
12022
|
this._groupChat?.destroy();
|
|
11770
12023
|
await this._transport.disconnect();
|
|
11771
12024
|
await this._storage.disconnect();
|
|
11772
12025
|
await this._oracle.disconnect();
|
|
12026
|
+
for (const provider of this._tokenStorageProviders.values()) {
|
|
12027
|
+
try {
|
|
12028
|
+
await provider.shutdown();
|
|
12029
|
+
} catch {
|
|
12030
|
+
}
|
|
12031
|
+
}
|
|
12032
|
+
this._tokenStorageProviders.clear();
|
|
11773
12033
|
this._initialized = false;
|
|
11774
12034
|
this._identity = null;
|
|
11775
12035
|
this._trackedAddresses.clear();
|
|
11776
12036
|
this._addressIdToIndex.clear();
|
|
11777
12037
|
this._addressNametags.clear();
|
|
12038
|
+
this._disabledProviders.clear();
|
|
11778
12039
|
this.eventHandlers.clear();
|
|
11779
12040
|
if (_Sphere.instance === this) {
|
|
11780
12041
|
_Sphere.instance = null;
|
|
@@ -11967,6 +12228,79 @@ var Sphere = class _Sphere {
|
|
|
11967
12228
|
for (const provider of this._tokenStorageProviders.values()) {
|
|
11968
12229
|
await provider.initialize();
|
|
11969
12230
|
}
|
|
12231
|
+
this.subscribeToProviderEvents();
|
|
12232
|
+
}
|
|
12233
|
+
/**
|
|
12234
|
+
* Subscribe to provider-level events and bridge them to Sphere connection:changed events.
|
|
12235
|
+
* Uses deduplication to avoid emitting duplicate events.
|
|
12236
|
+
*/
|
|
12237
|
+
subscribeToProviderEvents() {
|
|
12238
|
+
this.cleanupProviderEventSubscriptions();
|
|
12239
|
+
const transportAny = this._transport;
|
|
12240
|
+
if (typeof transportAny.onEvent === "function") {
|
|
12241
|
+
const unsub = transportAny.onEvent((event) => {
|
|
12242
|
+
const type = event?.type;
|
|
12243
|
+
if (type === "transport:connected") {
|
|
12244
|
+
this.emitConnectionChanged(this._transport.id, true, "connected");
|
|
12245
|
+
} else if (type === "transport:disconnected") {
|
|
12246
|
+
this.emitConnectionChanged(this._transport.id, false, "disconnected");
|
|
12247
|
+
} else if (type === "transport:reconnecting") {
|
|
12248
|
+
this.emitConnectionChanged(this._transport.id, false, "connecting");
|
|
12249
|
+
} else if (type === "transport:error") {
|
|
12250
|
+
this.emitConnectionChanged(this._transport.id, false, "error", event?.error);
|
|
12251
|
+
}
|
|
12252
|
+
});
|
|
12253
|
+
if (unsub) this._providerEventCleanups.push(unsub);
|
|
12254
|
+
}
|
|
12255
|
+
const oracleAny = this._oracle;
|
|
12256
|
+
if (typeof oracleAny.onEvent === "function") {
|
|
12257
|
+
const unsub = oracleAny.onEvent((event) => {
|
|
12258
|
+
const type = event?.type;
|
|
12259
|
+
if (type === "oracle:connected") {
|
|
12260
|
+
this.emitConnectionChanged(this._oracle.id, true, "connected");
|
|
12261
|
+
} else if (type === "oracle:disconnected") {
|
|
12262
|
+
this.emitConnectionChanged(this._oracle.id, false, "disconnected");
|
|
12263
|
+
} else if (type === "oracle:error") {
|
|
12264
|
+
this.emitConnectionChanged(this._oracle.id, false, "error", event?.error);
|
|
12265
|
+
}
|
|
12266
|
+
});
|
|
12267
|
+
if (unsub) this._providerEventCleanups.push(unsub);
|
|
12268
|
+
}
|
|
12269
|
+
for (const [providerId, provider] of this._tokenStorageProviders) {
|
|
12270
|
+
if (typeof provider.onEvent === "function") {
|
|
12271
|
+
const unsub = provider.onEvent((event) => {
|
|
12272
|
+
if (event.type === "storage:error" || event.type === "sync:error") {
|
|
12273
|
+
this.emitConnectionChanged(providerId, provider.isConnected(), provider.getStatus(), event.error);
|
|
12274
|
+
}
|
|
12275
|
+
});
|
|
12276
|
+
if (unsub) this._providerEventCleanups.push(unsub);
|
|
12277
|
+
}
|
|
12278
|
+
}
|
|
12279
|
+
}
|
|
12280
|
+
/**
|
|
12281
|
+
* Emit connection:changed with deduplication — only emits if status actually changed.
|
|
12282
|
+
*/
|
|
12283
|
+
emitConnectionChanged(providerId, connected, status, error) {
|
|
12284
|
+
const lastConnected = this._lastProviderConnected.get(providerId);
|
|
12285
|
+
if (lastConnected === connected) return;
|
|
12286
|
+
this._lastProviderConnected.set(providerId, connected);
|
|
12287
|
+
this.emitEvent("connection:changed", {
|
|
12288
|
+
provider: providerId,
|
|
12289
|
+
connected,
|
|
12290
|
+
status,
|
|
12291
|
+
enabled: !this._disabledProviders.has(providerId),
|
|
12292
|
+
...error ? { error } : {}
|
|
12293
|
+
});
|
|
12294
|
+
}
|
|
12295
|
+
cleanupProviderEventSubscriptions() {
|
|
12296
|
+
for (const cleanup of this._providerEventCleanups) {
|
|
12297
|
+
try {
|
|
12298
|
+
cleanup();
|
|
12299
|
+
} catch {
|
|
12300
|
+
}
|
|
12301
|
+
}
|
|
12302
|
+
this._providerEventCleanups = [];
|
|
12303
|
+
this._lastProviderConnected.clear();
|
|
11970
12304
|
}
|
|
11971
12305
|
async initializeModules() {
|
|
11972
12306
|
const emitEvent = this.emitEvent.bind(this);
|
|
@@ -11979,7 +12313,8 @@ var Sphere = class _Sphere {
|
|
|
11979
12313
|
emitEvent,
|
|
11980
12314
|
// Pass chain code for L1 HD derivation
|
|
11981
12315
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
11982
|
-
price: this._priceProvider ?? void 0
|
|
12316
|
+
price: this._priceProvider ?? void 0,
|
|
12317
|
+
disabledProviderIds: this._disabledProviders
|
|
11983
12318
|
});
|
|
11984
12319
|
this._communications.initialize({
|
|
11985
12320
|
identity: this._identity,
|
|
@@ -12070,6 +12405,192 @@ function formatAmount(amount, options = {}) {
|
|
|
12070
12405
|
// core/index.ts
|
|
12071
12406
|
init_bech32();
|
|
12072
12407
|
|
|
12408
|
+
// core/network-health.ts
|
|
12409
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
12410
|
+
async function checkNetworkHealth(network = "testnet", options) {
|
|
12411
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
12412
|
+
const servicesToCheck = options?.services ?? ["relay", "oracle", "l1"];
|
|
12413
|
+
const networkConfig = NETWORKS[network];
|
|
12414
|
+
const customUrls = options?.urls;
|
|
12415
|
+
const startTime = Date.now();
|
|
12416
|
+
const allChecks = [];
|
|
12417
|
+
if (servicesToCheck.includes("relay")) {
|
|
12418
|
+
const relayUrl = customUrls?.relay ?? networkConfig.nostrRelays[0];
|
|
12419
|
+
allChecks.push(checkWebSocket(relayUrl, timeoutMs).then((r) => ["relay", r]));
|
|
12420
|
+
}
|
|
12421
|
+
if (servicesToCheck.includes("oracle")) {
|
|
12422
|
+
const oracleUrl = customUrls?.oracle ?? networkConfig.aggregatorUrl;
|
|
12423
|
+
allChecks.push(checkOracle(oracleUrl, timeoutMs).then((r) => ["oracle", r]));
|
|
12424
|
+
}
|
|
12425
|
+
if (servicesToCheck.includes("l1")) {
|
|
12426
|
+
const l1Url = customUrls?.l1 ?? networkConfig.electrumUrl;
|
|
12427
|
+
allChecks.push(checkWebSocket(l1Url, timeoutMs).then((r) => ["l1", r]));
|
|
12428
|
+
}
|
|
12429
|
+
if (options?.checks) {
|
|
12430
|
+
for (const [name, checkFn] of Object.entries(options.checks)) {
|
|
12431
|
+
allChecks.push(
|
|
12432
|
+
runCustomCheck(name, checkFn, timeoutMs).then((r) => [name, r])
|
|
12433
|
+
);
|
|
12434
|
+
}
|
|
12435
|
+
}
|
|
12436
|
+
const results = await Promise.allSettled(allChecks);
|
|
12437
|
+
const services = {};
|
|
12438
|
+
let allHealthy = true;
|
|
12439
|
+
for (const result of results) {
|
|
12440
|
+
if (result.status === "fulfilled") {
|
|
12441
|
+
const [name, healthResult] = result.value;
|
|
12442
|
+
services[name] = healthResult;
|
|
12443
|
+
if (!healthResult.healthy) allHealthy = false;
|
|
12444
|
+
} else {
|
|
12445
|
+
allHealthy = false;
|
|
12446
|
+
}
|
|
12447
|
+
}
|
|
12448
|
+
return {
|
|
12449
|
+
healthy: allHealthy,
|
|
12450
|
+
services,
|
|
12451
|
+
totalTimeMs: Date.now() - startTime
|
|
12452
|
+
};
|
|
12453
|
+
}
|
|
12454
|
+
async function checkWebSocket(url, timeoutMs) {
|
|
12455
|
+
const startTime = Date.now();
|
|
12456
|
+
const WS = typeof globalThis !== "undefined" && globalThis.WebSocket;
|
|
12457
|
+
if (!WS) {
|
|
12458
|
+
return {
|
|
12459
|
+
healthy: false,
|
|
12460
|
+
url,
|
|
12461
|
+
responseTimeMs: null,
|
|
12462
|
+
error: "WebSocket not available in this environment"
|
|
12463
|
+
};
|
|
12464
|
+
}
|
|
12465
|
+
return new Promise((resolve) => {
|
|
12466
|
+
let resolved = false;
|
|
12467
|
+
const timer = setTimeout(() => {
|
|
12468
|
+
if (!resolved) {
|
|
12469
|
+
resolved = true;
|
|
12470
|
+
try {
|
|
12471
|
+
ws2.close();
|
|
12472
|
+
} catch {
|
|
12473
|
+
}
|
|
12474
|
+
resolve({
|
|
12475
|
+
healthy: false,
|
|
12476
|
+
url,
|
|
12477
|
+
responseTimeMs: null,
|
|
12478
|
+
error: `Connection timeout after ${timeoutMs}ms`
|
|
12479
|
+
});
|
|
12480
|
+
}
|
|
12481
|
+
}, timeoutMs);
|
|
12482
|
+
let ws2;
|
|
12483
|
+
try {
|
|
12484
|
+
ws2 = new WS(url);
|
|
12485
|
+
} catch (err) {
|
|
12486
|
+
clearTimeout(timer);
|
|
12487
|
+
return resolve({
|
|
12488
|
+
healthy: false,
|
|
12489
|
+
url,
|
|
12490
|
+
responseTimeMs: null,
|
|
12491
|
+
error: `Failed to create WebSocket: ${err instanceof Error ? err.message : String(err)}`
|
|
12492
|
+
});
|
|
12493
|
+
}
|
|
12494
|
+
ws2.onopen = () => {
|
|
12495
|
+
if (!resolved) {
|
|
12496
|
+
resolved = true;
|
|
12497
|
+
clearTimeout(timer);
|
|
12498
|
+
const responseTimeMs = Date.now() - startTime;
|
|
12499
|
+
try {
|
|
12500
|
+
ws2.close();
|
|
12501
|
+
} catch {
|
|
12502
|
+
}
|
|
12503
|
+
resolve({ healthy: true, url, responseTimeMs });
|
|
12504
|
+
}
|
|
12505
|
+
};
|
|
12506
|
+
ws2.onerror = (event) => {
|
|
12507
|
+
if (!resolved) {
|
|
12508
|
+
resolved = true;
|
|
12509
|
+
clearTimeout(timer);
|
|
12510
|
+
try {
|
|
12511
|
+
ws2.close();
|
|
12512
|
+
} catch {
|
|
12513
|
+
}
|
|
12514
|
+
resolve({
|
|
12515
|
+
healthy: false,
|
|
12516
|
+
url,
|
|
12517
|
+
responseTimeMs: null,
|
|
12518
|
+
error: "WebSocket connection error"
|
|
12519
|
+
});
|
|
12520
|
+
}
|
|
12521
|
+
};
|
|
12522
|
+
ws2.onclose = (event) => {
|
|
12523
|
+
if (!resolved) {
|
|
12524
|
+
resolved = true;
|
|
12525
|
+
clearTimeout(timer);
|
|
12526
|
+
resolve({
|
|
12527
|
+
healthy: false,
|
|
12528
|
+
url,
|
|
12529
|
+
responseTimeMs: null,
|
|
12530
|
+
error: `WebSocket closed: ${event.reason || `code ${event.code}`}`
|
|
12531
|
+
});
|
|
12532
|
+
}
|
|
12533
|
+
};
|
|
12534
|
+
});
|
|
12535
|
+
}
|
|
12536
|
+
async function checkOracle(url, timeoutMs) {
|
|
12537
|
+
const startTime = Date.now();
|
|
12538
|
+
try {
|
|
12539
|
+
const controller = new AbortController();
|
|
12540
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
12541
|
+
const response = await fetch(url, {
|
|
12542
|
+
method: "POST",
|
|
12543
|
+
headers: { "Content-Type": "application/json" },
|
|
12544
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "get_round_number", params: {} }),
|
|
12545
|
+
signal: controller.signal
|
|
12546
|
+
});
|
|
12547
|
+
clearTimeout(timer);
|
|
12548
|
+
const responseTimeMs = Date.now() - startTime;
|
|
12549
|
+
if (response.ok) {
|
|
12550
|
+
return { healthy: true, url, responseTimeMs };
|
|
12551
|
+
}
|
|
12552
|
+
return {
|
|
12553
|
+
healthy: false,
|
|
12554
|
+
url,
|
|
12555
|
+
responseTimeMs,
|
|
12556
|
+
error: `HTTP ${response.status} ${response.statusText}`
|
|
12557
|
+
};
|
|
12558
|
+
} catch (err) {
|
|
12559
|
+
return {
|
|
12560
|
+
healthy: false,
|
|
12561
|
+
url,
|
|
12562
|
+
responseTimeMs: null,
|
|
12563
|
+
error: err instanceof Error ? err.name === "AbortError" ? `Connection timeout after ${timeoutMs}ms` : err.message : String(err)
|
|
12564
|
+
};
|
|
12565
|
+
}
|
|
12566
|
+
}
|
|
12567
|
+
async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
12568
|
+
try {
|
|
12569
|
+
const result = await Promise.race([
|
|
12570
|
+
checkFn(timeoutMs),
|
|
12571
|
+
new Promise(
|
|
12572
|
+
(resolve) => setTimeout(
|
|
12573
|
+
() => resolve({
|
|
12574
|
+
healthy: false,
|
|
12575
|
+
url: name,
|
|
12576
|
+
responseTimeMs: null,
|
|
12577
|
+
error: `Custom check '${name}' timeout after ${timeoutMs}ms`
|
|
12578
|
+
}),
|
|
12579
|
+
timeoutMs
|
|
12580
|
+
)
|
|
12581
|
+
)
|
|
12582
|
+
]);
|
|
12583
|
+
return result;
|
|
12584
|
+
} catch (err) {
|
|
12585
|
+
return {
|
|
12586
|
+
healthy: false,
|
|
12587
|
+
url: name,
|
|
12588
|
+
responseTimeMs: null,
|
|
12589
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12590
|
+
};
|
|
12591
|
+
}
|
|
12592
|
+
}
|
|
12593
|
+
|
|
12073
12594
|
// types/payment-session.ts
|
|
12074
12595
|
function createPaymentSession(params) {
|
|
12075
12596
|
const now = Date.now();
|
|
@@ -12618,6 +13139,7 @@ export {
|
|
|
12618
13139
|
base58Encode2 as base58Encode,
|
|
12619
13140
|
buildTxfStorageData,
|
|
12620
13141
|
bytesToHex2 as bytesToHex,
|
|
13142
|
+
checkNetworkHealth,
|
|
12621
13143
|
countCommittedTransactions,
|
|
12622
13144
|
createAddress,
|
|
12623
13145
|
createCommunicationsModule,
|