@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/core/index.cjs
CHANGED
|
@@ -478,6 +478,7 @@ __export(core_exports, {
|
|
|
478
478
|
base58Decode: () => base58Decode,
|
|
479
479
|
base58Encode: () => base58Encode,
|
|
480
480
|
bytesToHex: () => bytesToHex2,
|
|
481
|
+
checkNetworkHealth: () => checkNetworkHealth,
|
|
481
482
|
computeHash160: () => computeHash160,
|
|
482
483
|
convertBits: () => convertBits,
|
|
483
484
|
createAddress: () => createAddress,
|
|
@@ -1547,6 +1548,7 @@ async function sendAlpha(wallet, toAddress, amountAlpha, fromAddress) {
|
|
|
1547
1548
|
// modules/payments/L1PaymentsModule.ts
|
|
1548
1549
|
var L1PaymentsModule = class {
|
|
1549
1550
|
_initialized = false;
|
|
1551
|
+
_disabled = false;
|
|
1550
1552
|
_config;
|
|
1551
1553
|
_identity;
|
|
1552
1554
|
_addresses = [];
|
|
@@ -1594,6 +1596,9 @@ var L1PaymentsModule = class {
|
|
|
1594
1596
|
* (e.g. by the address scanner), this is a no-op.
|
|
1595
1597
|
*/
|
|
1596
1598
|
async ensureConnected() {
|
|
1599
|
+
if (this._disabled) {
|
|
1600
|
+
throw new Error("L1 provider is disabled");
|
|
1601
|
+
}
|
|
1597
1602
|
if (!isWebSocketConnected() && this._config.electrumUrl) {
|
|
1598
1603
|
await connect(this._config.electrumUrl);
|
|
1599
1604
|
}
|
|
@@ -1607,6 +1612,24 @@ var L1PaymentsModule = class {
|
|
|
1607
1612
|
this._addresses = [];
|
|
1608
1613
|
this._wallet = void 0;
|
|
1609
1614
|
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Disable this module — disconnect WebSocket and block operations until re-enabled.
|
|
1617
|
+
*/
|
|
1618
|
+
disable() {
|
|
1619
|
+
this._disabled = true;
|
|
1620
|
+
if (isWebSocketConnected()) {
|
|
1621
|
+
disconnect();
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
/**
|
|
1625
|
+
* Re-enable this module. Connection will be established lazily on next operation.
|
|
1626
|
+
*/
|
|
1627
|
+
enable() {
|
|
1628
|
+
this._disabled = false;
|
|
1629
|
+
}
|
|
1630
|
+
get disabled() {
|
|
1631
|
+
return this._disabled;
|
|
1632
|
+
}
|
|
1610
1633
|
/**
|
|
1611
1634
|
* Check if a string looks like an L1 address (alpha1... or alphat1...)
|
|
1612
1635
|
*/
|
|
@@ -2481,9 +2504,7 @@ var DEFAULT_AGGREGATOR_URL = "https://aggregator.unicity.network/rpc";
|
|
|
2481
2504
|
var DEV_AGGREGATOR_URL = "https://dev-aggregator.dyndns.org/rpc";
|
|
2482
2505
|
var TEST_AGGREGATOR_URL = "https://goggregator-test.unicity.network";
|
|
2483
2506
|
var DEFAULT_IPFS_GATEWAYS = [
|
|
2484
|
-
"https://
|
|
2485
|
-
"https://dweb.link",
|
|
2486
|
-
"https://ipfs.io"
|
|
2507
|
+
"https://unicity-ipfs1.dyndns.org"
|
|
2487
2508
|
];
|
|
2488
2509
|
var DEFAULT_BASE_PATH = "m/44'/0'/0'";
|
|
2489
2510
|
var DEFAULT_DERIVATION_PATH2 = `${DEFAULT_BASE_PATH}/0/0`;
|
|
@@ -5326,7 +5347,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5326
5347
|
*/
|
|
5327
5348
|
async getFiatBalance() {
|
|
5328
5349
|
const assets = await this.getAssets();
|
|
5329
|
-
if (!this.priceProvider) {
|
|
5350
|
+
if (!this.priceProvider || this.isPriceDisabled()) {
|
|
5330
5351
|
return null;
|
|
5331
5352
|
}
|
|
5332
5353
|
let total = 0;
|
|
@@ -5361,7 +5382,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5361
5382
|
*/
|
|
5362
5383
|
async getAssets(coinId) {
|
|
5363
5384
|
const rawAssets = this.aggregateTokens(coinId);
|
|
5364
|
-
if (!this.priceProvider || rawAssets.length === 0) {
|
|
5385
|
+
if (!this.priceProvider || this.isPriceDisabled() || rawAssets.length === 0) {
|
|
5365
5386
|
return rawAssets;
|
|
5366
5387
|
}
|
|
5367
5388
|
try {
|
|
@@ -6455,18 +6476,38 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6455
6476
|
}, _PaymentsModule.SYNC_DEBOUNCE_MS);
|
|
6456
6477
|
}
|
|
6457
6478
|
/**
|
|
6458
|
-
* Get all active token storage providers
|
|
6479
|
+
* Get all active (non-disabled) token storage providers
|
|
6459
6480
|
*/
|
|
6460
6481
|
getTokenStorageProviders() {
|
|
6482
|
+
let providers;
|
|
6461
6483
|
if (this.deps.tokenStorageProviders && this.deps.tokenStorageProviders.size > 0) {
|
|
6462
|
-
|
|
6463
|
-
}
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
return
|
|
6484
|
+
providers = this.deps.tokenStorageProviders;
|
|
6485
|
+
} else if (this.deps.tokenStorage) {
|
|
6486
|
+
providers = /* @__PURE__ */ new Map();
|
|
6487
|
+
providers.set(this.deps.tokenStorage.id, this.deps.tokenStorage);
|
|
6488
|
+
} else {
|
|
6489
|
+
return /* @__PURE__ */ new Map();
|
|
6490
|
+
}
|
|
6491
|
+
const disabled = this.deps.disabledProviderIds;
|
|
6492
|
+
if (disabled && disabled.size > 0) {
|
|
6493
|
+
const filtered = /* @__PURE__ */ new Map();
|
|
6494
|
+
for (const [id, provider] of providers) {
|
|
6495
|
+
if (!disabled.has(id)) {
|
|
6496
|
+
filtered.set(id, provider);
|
|
6497
|
+
}
|
|
6498
|
+
}
|
|
6499
|
+
return filtered;
|
|
6468
6500
|
}
|
|
6469
|
-
return
|
|
6501
|
+
return providers;
|
|
6502
|
+
}
|
|
6503
|
+
/**
|
|
6504
|
+
* Check if the price provider is disabled via the disabled providers set.
|
|
6505
|
+
*/
|
|
6506
|
+
isPriceDisabled() {
|
|
6507
|
+
const disabled = this.deps?.disabledProviderIds;
|
|
6508
|
+
if (!disabled || disabled.size === 0) return false;
|
|
6509
|
+
const priceId = this.priceProvider?.id ?? "price";
|
|
6510
|
+
return disabled.has(priceId);
|
|
6470
6511
|
}
|
|
6471
6512
|
/**
|
|
6472
6513
|
* Replace the set of token storage providers at runtime.
|
|
@@ -9054,6 +9095,9 @@ async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
|
9054
9095
|
};
|
|
9055
9096
|
}
|
|
9056
9097
|
|
|
9098
|
+
// core/Sphere.ts
|
|
9099
|
+
init_network();
|
|
9100
|
+
|
|
9057
9101
|
// serialization/wallet-text.ts
|
|
9058
9102
|
var import_crypto_js7 = __toESM(require("crypto-js"), 1);
|
|
9059
9103
|
|
|
@@ -9782,6 +9826,10 @@ var Sphere = class _Sphere {
|
|
|
9782
9826
|
_groupChat = null;
|
|
9783
9827
|
// Events
|
|
9784
9828
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
9829
|
+
// Provider management
|
|
9830
|
+
_disabledProviders = /* @__PURE__ */ new Set();
|
|
9831
|
+
_providerEventCleanups = [];
|
|
9832
|
+
_lastProviderConnected = /* @__PURE__ */ new Map();
|
|
9785
9833
|
// ===========================================================================
|
|
9786
9834
|
// Constructor (private)
|
|
9787
9835
|
// ===========================================================================
|
|
@@ -9988,9 +10036,14 @@ var Sphere = class _Sphere {
|
|
|
9988
10036
|
throw new Error("Either mnemonic or masterKey is required");
|
|
9989
10037
|
}
|
|
9990
10038
|
console.log("[Sphere.import] Starting import...");
|
|
9991
|
-
|
|
9992
|
-
|
|
9993
|
-
|
|
10039
|
+
const needsClear = _Sphere.instance !== null || await _Sphere.exists(options.storage);
|
|
10040
|
+
if (needsClear) {
|
|
10041
|
+
console.log("[Sphere.import] Clearing existing wallet data...");
|
|
10042
|
+
await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
|
|
10043
|
+
console.log("[Sphere.import] Clear done");
|
|
10044
|
+
} else {
|
|
10045
|
+
console.log("[Sphere.import] No existing wallet \u2014 skipping clear");
|
|
10046
|
+
}
|
|
9994
10047
|
if (!options.storage.isConnected()) {
|
|
9995
10048
|
console.log("[Sphere.import] Reconnecting storage...");
|
|
9996
10049
|
await options.storage.connect();
|
|
@@ -11217,20 +11270,193 @@ var Sphere = class _Sphere {
|
|
|
11217
11270
|
// ===========================================================================
|
|
11218
11271
|
// Public Methods - Status
|
|
11219
11272
|
// ===========================================================================
|
|
11273
|
+
/**
|
|
11274
|
+
* Get aggregated status of all providers, grouped by role.
|
|
11275
|
+
*
|
|
11276
|
+
* @example
|
|
11277
|
+
* ```ts
|
|
11278
|
+
* const status = sphere.getStatus();
|
|
11279
|
+
* // status.transport[0].connected // true/false
|
|
11280
|
+
* // status.transport[0].metadata?.relays // { total: 3, connected: 2 }
|
|
11281
|
+
* // status.tokenStorage // all registered token storage providers
|
|
11282
|
+
* ```
|
|
11283
|
+
*/
|
|
11220
11284
|
getStatus() {
|
|
11285
|
+
const mkInfo = (provider, role, metadata) => ({
|
|
11286
|
+
id: provider.id,
|
|
11287
|
+
name: provider.name,
|
|
11288
|
+
role,
|
|
11289
|
+
status: provider.getStatus(),
|
|
11290
|
+
connected: provider.isConnected(),
|
|
11291
|
+
enabled: !this._disabledProviders.has(provider.id),
|
|
11292
|
+
...metadata ? { metadata } : {}
|
|
11293
|
+
});
|
|
11294
|
+
let transportMeta;
|
|
11295
|
+
const transport = this._transport;
|
|
11296
|
+
if (typeof transport.getRelays === "function") {
|
|
11297
|
+
const total = transport.getRelays().length;
|
|
11298
|
+
const connected = typeof transport.getConnectedRelays === "function" ? transport.getConnectedRelays().length : 0;
|
|
11299
|
+
transportMeta = { relays: { total, connected } };
|
|
11300
|
+
}
|
|
11301
|
+
const l1Module = this._payments.l1;
|
|
11302
|
+
const l1Providers = [];
|
|
11303
|
+
if (l1Module) {
|
|
11304
|
+
const wsConnected = isWebSocketConnected();
|
|
11305
|
+
l1Providers.push({
|
|
11306
|
+
id: "l1-alpha",
|
|
11307
|
+
name: "ALPHA L1",
|
|
11308
|
+
role: "l1",
|
|
11309
|
+
status: wsConnected ? "connected" : "disconnected",
|
|
11310
|
+
connected: wsConnected,
|
|
11311
|
+
enabled: !this._disabledProviders.has("l1-alpha")
|
|
11312
|
+
});
|
|
11313
|
+
}
|
|
11314
|
+
const priceProviders = [];
|
|
11315
|
+
if (this._priceProvider) {
|
|
11316
|
+
priceProviders.push({
|
|
11317
|
+
id: this._priceProviderId,
|
|
11318
|
+
name: this._priceProvider.platform ?? "Price",
|
|
11319
|
+
role: "price",
|
|
11320
|
+
status: "connected",
|
|
11321
|
+
connected: true,
|
|
11322
|
+
enabled: !this._disabledProviders.has(this._priceProviderId)
|
|
11323
|
+
});
|
|
11324
|
+
}
|
|
11221
11325
|
return {
|
|
11222
|
-
storage:
|
|
11223
|
-
|
|
11224
|
-
|
|
11326
|
+
storage: [mkInfo(this._storage, "storage")],
|
|
11327
|
+
tokenStorage: Array.from(this._tokenStorageProviders.values()).map(
|
|
11328
|
+
(p) => mkInfo(p, "token-storage")
|
|
11329
|
+
),
|
|
11330
|
+
transport: [mkInfo(this._transport, "transport", transportMeta)],
|
|
11331
|
+
oracle: [mkInfo(this._oracle, "oracle")],
|
|
11332
|
+
l1: l1Providers,
|
|
11333
|
+
price: priceProviders
|
|
11225
11334
|
};
|
|
11226
11335
|
}
|
|
11227
11336
|
async reconnect() {
|
|
11228
11337
|
await this._transport.disconnect();
|
|
11229
11338
|
await this._transport.connect();
|
|
11339
|
+
}
|
|
11340
|
+
// ===========================================================================
|
|
11341
|
+
// Public Methods - Provider Management
|
|
11342
|
+
// ===========================================================================
|
|
11343
|
+
/**
|
|
11344
|
+
* Disable a provider at runtime. The provider stays registered but is disconnected
|
|
11345
|
+
* and skipped during operations (e.g., sync).
|
|
11346
|
+
*
|
|
11347
|
+
* Main storage provider cannot be disabled.
|
|
11348
|
+
*
|
|
11349
|
+
* @returns true if successfully disabled, false if provider not found
|
|
11350
|
+
*/
|
|
11351
|
+
async disableProvider(providerId) {
|
|
11352
|
+
if (providerId === this._storage.id) {
|
|
11353
|
+
throw new Error("Cannot disable the main storage provider");
|
|
11354
|
+
}
|
|
11355
|
+
const provider = this.findProviderById(providerId);
|
|
11356
|
+
if (!provider) return false;
|
|
11357
|
+
this._disabledProviders.add(providerId);
|
|
11358
|
+
try {
|
|
11359
|
+
if ("disable" in provider && typeof provider.disable === "function") {
|
|
11360
|
+
provider.disable();
|
|
11361
|
+
} else if ("shutdown" in provider && typeof provider.shutdown === "function") {
|
|
11362
|
+
await provider.shutdown();
|
|
11363
|
+
} else if ("disconnect" in provider && typeof provider.disconnect === "function") {
|
|
11364
|
+
await provider.disconnect();
|
|
11365
|
+
} else if ("clearCache" in provider && typeof provider.clearCache === "function") {
|
|
11366
|
+
provider.clearCache();
|
|
11367
|
+
}
|
|
11368
|
+
} catch {
|
|
11369
|
+
}
|
|
11370
|
+
this.emitEvent("connection:changed", {
|
|
11371
|
+
provider: providerId,
|
|
11372
|
+
connected: false,
|
|
11373
|
+
status: "disconnected",
|
|
11374
|
+
enabled: false
|
|
11375
|
+
});
|
|
11376
|
+
return true;
|
|
11377
|
+
}
|
|
11378
|
+
/**
|
|
11379
|
+
* Re-enable a previously disabled provider. Reconnects and resumes operations.
|
|
11380
|
+
*
|
|
11381
|
+
* @returns true if successfully enabled, false if provider not found
|
|
11382
|
+
*/
|
|
11383
|
+
async enableProvider(providerId) {
|
|
11384
|
+
const provider = this.findProviderById(providerId);
|
|
11385
|
+
if (!provider) return false;
|
|
11386
|
+
this._disabledProviders.delete(providerId);
|
|
11387
|
+
if ("enable" in provider && typeof provider.enable === "function") {
|
|
11388
|
+
provider.enable();
|
|
11389
|
+
this.emitEvent("connection:changed", {
|
|
11390
|
+
provider: providerId,
|
|
11391
|
+
connected: false,
|
|
11392
|
+
status: "disconnected",
|
|
11393
|
+
enabled: true
|
|
11394
|
+
});
|
|
11395
|
+
return true;
|
|
11396
|
+
}
|
|
11397
|
+
const hasLifecycle = "connect" in provider && typeof provider.connect === "function" || "initialize" in provider && typeof provider.initialize === "function";
|
|
11398
|
+
if (hasLifecycle) {
|
|
11399
|
+
try {
|
|
11400
|
+
if ("connect" in provider && typeof provider.connect === "function") {
|
|
11401
|
+
await provider.connect();
|
|
11402
|
+
} else if ("initialize" in provider && typeof provider.initialize === "function") {
|
|
11403
|
+
await provider.initialize();
|
|
11404
|
+
}
|
|
11405
|
+
} catch (err) {
|
|
11406
|
+
this.emitEvent("connection:changed", {
|
|
11407
|
+
provider: providerId,
|
|
11408
|
+
connected: false,
|
|
11409
|
+
status: "error",
|
|
11410
|
+
enabled: true,
|
|
11411
|
+
error: err instanceof Error ? err.message : String(err)
|
|
11412
|
+
});
|
|
11413
|
+
return false;
|
|
11414
|
+
}
|
|
11415
|
+
}
|
|
11230
11416
|
this.emitEvent("connection:changed", {
|
|
11231
|
-
provider:
|
|
11232
|
-
connected: true
|
|
11417
|
+
provider: providerId,
|
|
11418
|
+
connected: true,
|
|
11419
|
+
status: "connected",
|
|
11420
|
+
enabled: true
|
|
11233
11421
|
});
|
|
11422
|
+
return true;
|
|
11423
|
+
}
|
|
11424
|
+
/**
|
|
11425
|
+
* Check if a provider is currently enabled
|
|
11426
|
+
*/
|
|
11427
|
+
isProviderEnabled(providerId) {
|
|
11428
|
+
return !this._disabledProviders.has(providerId);
|
|
11429
|
+
}
|
|
11430
|
+
/**
|
|
11431
|
+
* Get the set of disabled provider IDs (for passing to modules)
|
|
11432
|
+
*/
|
|
11433
|
+
getDisabledProviderIds() {
|
|
11434
|
+
return this._disabledProviders;
|
|
11435
|
+
}
|
|
11436
|
+
/** Get the price provider's ID (implementation detail — not on PriceProvider interface) */
|
|
11437
|
+
get _priceProviderId() {
|
|
11438
|
+
if (!this._priceProvider) return "price";
|
|
11439
|
+
const p = this._priceProvider;
|
|
11440
|
+
return typeof p.id === "string" ? p.id : "price";
|
|
11441
|
+
}
|
|
11442
|
+
/**
|
|
11443
|
+
* Find a provider by ID across all provider collections
|
|
11444
|
+
*/
|
|
11445
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11446
|
+
findProviderById(providerId) {
|
|
11447
|
+
if (this._storage.id === providerId) return this._storage;
|
|
11448
|
+
if (this._transport.id === providerId) return this._transport;
|
|
11449
|
+
if (this._oracle.id === providerId) return this._oracle;
|
|
11450
|
+
if (this._tokenStorageProviders.has(providerId)) {
|
|
11451
|
+
return this._tokenStorageProviders.get(providerId);
|
|
11452
|
+
}
|
|
11453
|
+
if (this._priceProvider && this._priceProviderId === providerId) {
|
|
11454
|
+
return this._priceProvider;
|
|
11455
|
+
}
|
|
11456
|
+
if (providerId === "l1-alpha" && this._payments.l1) {
|
|
11457
|
+
return this._payments.l1;
|
|
11458
|
+
}
|
|
11459
|
+
return null;
|
|
11234
11460
|
}
|
|
11235
11461
|
// ===========================================================================
|
|
11236
11462
|
// Public Methods - Events
|
|
@@ -11573,6 +11799,33 @@ var Sphere = class _Sphere {
|
|
|
11573
11799
|
return;
|
|
11574
11800
|
}
|
|
11575
11801
|
try {
|
|
11802
|
+
if (this._identity?.directAddress && this._transport.resolve) {
|
|
11803
|
+
try {
|
|
11804
|
+
const existing = await this._transport.resolve(this._identity.directAddress);
|
|
11805
|
+
if (existing) {
|
|
11806
|
+
if (existing.nametag && !this._identity.nametag) {
|
|
11807
|
+
this._identity.nametag = existing.nametag;
|
|
11808
|
+
await this._updateCachedProxyAddress();
|
|
11809
|
+
const entry = await this.ensureAddressTracked(this._currentAddressIndex);
|
|
11810
|
+
let nametags = this._addressNametags.get(entry.addressId);
|
|
11811
|
+
if (!nametags) {
|
|
11812
|
+
nametags = /* @__PURE__ */ new Map();
|
|
11813
|
+
this._addressNametags.set(entry.addressId, nametags);
|
|
11814
|
+
}
|
|
11815
|
+
if (!nametags.has(0)) {
|
|
11816
|
+
nametags.set(0, existing.nametag);
|
|
11817
|
+
await this.persistAddressNametags();
|
|
11818
|
+
}
|
|
11819
|
+
this.emitEvent("nametag:recovered", { nametag: existing.nametag });
|
|
11820
|
+
}
|
|
11821
|
+
console.log("[Sphere] Existing binding found, skipping re-publish");
|
|
11822
|
+
return;
|
|
11823
|
+
}
|
|
11824
|
+
} catch (e) {
|
|
11825
|
+
console.warn("[Sphere] resolve() failed, skipping publish to avoid overwrite", e);
|
|
11826
|
+
return;
|
|
11827
|
+
}
|
|
11828
|
+
}
|
|
11576
11829
|
const nametag = this._identity?.nametag;
|
|
11577
11830
|
const success = await this._transport.publishIdentityBinding(
|
|
11578
11831
|
this._identity.chainPubkey,
|
|
@@ -11646,17 +11899,26 @@ var Sphere = class _Sphere {
|
|
|
11646
11899
|
// Public Methods - Lifecycle
|
|
11647
11900
|
// ===========================================================================
|
|
11648
11901
|
async destroy() {
|
|
11902
|
+
this.cleanupProviderEventSubscriptions();
|
|
11649
11903
|
this._payments.destroy();
|
|
11650
11904
|
this._communications.destroy();
|
|
11651
11905
|
this._groupChat?.destroy();
|
|
11652
11906
|
await this._transport.disconnect();
|
|
11653
11907
|
await this._storage.disconnect();
|
|
11654
11908
|
await this._oracle.disconnect();
|
|
11909
|
+
for (const provider of this._tokenStorageProviders.values()) {
|
|
11910
|
+
try {
|
|
11911
|
+
await provider.shutdown();
|
|
11912
|
+
} catch {
|
|
11913
|
+
}
|
|
11914
|
+
}
|
|
11915
|
+
this._tokenStorageProviders.clear();
|
|
11655
11916
|
this._initialized = false;
|
|
11656
11917
|
this._identity = null;
|
|
11657
11918
|
this._trackedAddresses.clear();
|
|
11658
11919
|
this._addressIdToIndex.clear();
|
|
11659
11920
|
this._addressNametags.clear();
|
|
11921
|
+
this._disabledProviders.clear();
|
|
11660
11922
|
this.eventHandlers.clear();
|
|
11661
11923
|
if (_Sphere.instance === this) {
|
|
11662
11924
|
_Sphere.instance = null;
|
|
@@ -11849,6 +12111,79 @@ var Sphere = class _Sphere {
|
|
|
11849
12111
|
for (const provider of this._tokenStorageProviders.values()) {
|
|
11850
12112
|
await provider.initialize();
|
|
11851
12113
|
}
|
|
12114
|
+
this.subscribeToProviderEvents();
|
|
12115
|
+
}
|
|
12116
|
+
/**
|
|
12117
|
+
* Subscribe to provider-level events and bridge them to Sphere connection:changed events.
|
|
12118
|
+
* Uses deduplication to avoid emitting duplicate events.
|
|
12119
|
+
*/
|
|
12120
|
+
subscribeToProviderEvents() {
|
|
12121
|
+
this.cleanupProviderEventSubscriptions();
|
|
12122
|
+
const transportAny = this._transport;
|
|
12123
|
+
if (typeof transportAny.onEvent === "function") {
|
|
12124
|
+
const unsub = transportAny.onEvent((event) => {
|
|
12125
|
+
const type = event?.type;
|
|
12126
|
+
if (type === "transport:connected") {
|
|
12127
|
+
this.emitConnectionChanged(this._transport.id, true, "connected");
|
|
12128
|
+
} else if (type === "transport:disconnected") {
|
|
12129
|
+
this.emitConnectionChanged(this._transport.id, false, "disconnected");
|
|
12130
|
+
} else if (type === "transport:reconnecting") {
|
|
12131
|
+
this.emitConnectionChanged(this._transport.id, false, "connecting");
|
|
12132
|
+
} else if (type === "transport:error") {
|
|
12133
|
+
this.emitConnectionChanged(this._transport.id, false, "error", event?.error);
|
|
12134
|
+
}
|
|
12135
|
+
});
|
|
12136
|
+
if (unsub) this._providerEventCleanups.push(unsub);
|
|
12137
|
+
}
|
|
12138
|
+
const oracleAny = this._oracle;
|
|
12139
|
+
if (typeof oracleAny.onEvent === "function") {
|
|
12140
|
+
const unsub = oracleAny.onEvent((event) => {
|
|
12141
|
+
const type = event?.type;
|
|
12142
|
+
if (type === "oracle:connected") {
|
|
12143
|
+
this.emitConnectionChanged(this._oracle.id, true, "connected");
|
|
12144
|
+
} else if (type === "oracle:disconnected") {
|
|
12145
|
+
this.emitConnectionChanged(this._oracle.id, false, "disconnected");
|
|
12146
|
+
} else if (type === "oracle:error") {
|
|
12147
|
+
this.emitConnectionChanged(this._oracle.id, false, "error", event?.error);
|
|
12148
|
+
}
|
|
12149
|
+
});
|
|
12150
|
+
if (unsub) this._providerEventCleanups.push(unsub);
|
|
12151
|
+
}
|
|
12152
|
+
for (const [providerId, provider] of this._tokenStorageProviders) {
|
|
12153
|
+
if (typeof provider.onEvent === "function") {
|
|
12154
|
+
const unsub = provider.onEvent((event) => {
|
|
12155
|
+
if (event.type === "storage:error" || event.type === "sync:error") {
|
|
12156
|
+
this.emitConnectionChanged(providerId, provider.isConnected(), provider.getStatus(), event.error);
|
|
12157
|
+
}
|
|
12158
|
+
});
|
|
12159
|
+
if (unsub) this._providerEventCleanups.push(unsub);
|
|
12160
|
+
}
|
|
12161
|
+
}
|
|
12162
|
+
}
|
|
12163
|
+
/**
|
|
12164
|
+
* Emit connection:changed with deduplication — only emits if status actually changed.
|
|
12165
|
+
*/
|
|
12166
|
+
emitConnectionChanged(providerId, connected, status, error) {
|
|
12167
|
+
const lastConnected = this._lastProviderConnected.get(providerId);
|
|
12168
|
+
if (lastConnected === connected) return;
|
|
12169
|
+
this._lastProviderConnected.set(providerId, connected);
|
|
12170
|
+
this.emitEvent("connection:changed", {
|
|
12171
|
+
provider: providerId,
|
|
12172
|
+
connected,
|
|
12173
|
+
status,
|
|
12174
|
+
enabled: !this._disabledProviders.has(providerId),
|
|
12175
|
+
...error ? { error } : {}
|
|
12176
|
+
});
|
|
12177
|
+
}
|
|
12178
|
+
cleanupProviderEventSubscriptions() {
|
|
12179
|
+
for (const cleanup of this._providerEventCleanups) {
|
|
12180
|
+
try {
|
|
12181
|
+
cleanup();
|
|
12182
|
+
} catch {
|
|
12183
|
+
}
|
|
12184
|
+
}
|
|
12185
|
+
this._providerEventCleanups = [];
|
|
12186
|
+
this._lastProviderConnected.clear();
|
|
11852
12187
|
}
|
|
11853
12188
|
async initializeModules() {
|
|
11854
12189
|
const emitEvent = this.emitEvent.bind(this);
|
|
@@ -11861,7 +12196,8 @@ var Sphere = class _Sphere {
|
|
|
11861
12196
|
emitEvent,
|
|
11862
12197
|
// Pass chain code for L1 HD derivation
|
|
11863
12198
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
11864
|
-
price: this._priceProvider ?? void 0
|
|
12199
|
+
price: this._priceProvider ?? void 0,
|
|
12200
|
+
disabledProviderIds: this._disabledProviders
|
|
11865
12201
|
});
|
|
11866
12202
|
this._communications.initialize({
|
|
11867
12203
|
identity: this._identity,
|
|
@@ -11956,6 +12292,192 @@ var CurrencyUtils = {
|
|
|
11956
12292
|
|
|
11957
12293
|
// core/index.ts
|
|
11958
12294
|
init_bech32();
|
|
12295
|
+
|
|
12296
|
+
// core/network-health.ts
|
|
12297
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
12298
|
+
async function checkNetworkHealth(network = "testnet", options) {
|
|
12299
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
12300
|
+
const servicesToCheck = options?.services ?? ["relay", "oracle", "l1"];
|
|
12301
|
+
const networkConfig = NETWORKS[network];
|
|
12302
|
+
const customUrls = options?.urls;
|
|
12303
|
+
const startTime = Date.now();
|
|
12304
|
+
const allChecks = [];
|
|
12305
|
+
if (servicesToCheck.includes("relay")) {
|
|
12306
|
+
const relayUrl = customUrls?.relay ?? networkConfig.nostrRelays[0];
|
|
12307
|
+
allChecks.push(checkWebSocket(relayUrl, timeoutMs).then((r) => ["relay", r]));
|
|
12308
|
+
}
|
|
12309
|
+
if (servicesToCheck.includes("oracle")) {
|
|
12310
|
+
const oracleUrl = customUrls?.oracle ?? networkConfig.aggregatorUrl;
|
|
12311
|
+
allChecks.push(checkOracle(oracleUrl, timeoutMs).then((r) => ["oracle", r]));
|
|
12312
|
+
}
|
|
12313
|
+
if (servicesToCheck.includes("l1")) {
|
|
12314
|
+
const l1Url = customUrls?.l1 ?? networkConfig.electrumUrl;
|
|
12315
|
+
allChecks.push(checkWebSocket(l1Url, timeoutMs).then((r) => ["l1", r]));
|
|
12316
|
+
}
|
|
12317
|
+
if (options?.checks) {
|
|
12318
|
+
for (const [name, checkFn] of Object.entries(options.checks)) {
|
|
12319
|
+
allChecks.push(
|
|
12320
|
+
runCustomCheck(name, checkFn, timeoutMs).then((r) => [name, r])
|
|
12321
|
+
);
|
|
12322
|
+
}
|
|
12323
|
+
}
|
|
12324
|
+
const results = await Promise.allSettled(allChecks);
|
|
12325
|
+
const services = {};
|
|
12326
|
+
let allHealthy = true;
|
|
12327
|
+
for (const result of results) {
|
|
12328
|
+
if (result.status === "fulfilled") {
|
|
12329
|
+
const [name, healthResult] = result.value;
|
|
12330
|
+
services[name] = healthResult;
|
|
12331
|
+
if (!healthResult.healthy) allHealthy = false;
|
|
12332
|
+
} else {
|
|
12333
|
+
allHealthy = false;
|
|
12334
|
+
}
|
|
12335
|
+
}
|
|
12336
|
+
return {
|
|
12337
|
+
healthy: allHealthy,
|
|
12338
|
+
services,
|
|
12339
|
+
totalTimeMs: Date.now() - startTime
|
|
12340
|
+
};
|
|
12341
|
+
}
|
|
12342
|
+
async function checkWebSocket(url, timeoutMs) {
|
|
12343
|
+
const startTime = Date.now();
|
|
12344
|
+
const WS = typeof globalThis !== "undefined" && globalThis.WebSocket;
|
|
12345
|
+
if (!WS) {
|
|
12346
|
+
return {
|
|
12347
|
+
healthy: false,
|
|
12348
|
+
url,
|
|
12349
|
+
responseTimeMs: null,
|
|
12350
|
+
error: "WebSocket not available in this environment"
|
|
12351
|
+
};
|
|
12352
|
+
}
|
|
12353
|
+
return new Promise((resolve) => {
|
|
12354
|
+
let resolved = false;
|
|
12355
|
+
const timer = setTimeout(() => {
|
|
12356
|
+
if (!resolved) {
|
|
12357
|
+
resolved = true;
|
|
12358
|
+
try {
|
|
12359
|
+
ws2.close();
|
|
12360
|
+
} catch {
|
|
12361
|
+
}
|
|
12362
|
+
resolve({
|
|
12363
|
+
healthy: false,
|
|
12364
|
+
url,
|
|
12365
|
+
responseTimeMs: null,
|
|
12366
|
+
error: `Connection timeout after ${timeoutMs}ms`
|
|
12367
|
+
});
|
|
12368
|
+
}
|
|
12369
|
+
}, timeoutMs);
|
|
12370
|
+
let ws2;
|
|
12371
|
+
try {
|
|
12372
|
+
ws2 = new WS(url);
|
|
12373
|
+
} catch (err) {
|
|
12374
|
+
clearTimeout(timer);
|
|
12375
|
+
return resolve({
|
|
12376
|
+
healthy: false,
|
|
12377
|
+
url,
|
|
12378
|
+
responseTimeMs: null,
|
|
12379
|
+
error: `Failed to create WebSocket: ${err instanceof Error ? err.message : String(err)}`
|
|
12380
|
+
});
|
|
12381
|
+
}
|
|
12382
|
+
ws2.onopen = () => {
|
|
12383
|
+
if (!resolved) {
|
|
12384
|
+
resolved = true;
|
|
12385
|
+
clearTimeout(timer);
|
|
12386
|
+
const responseTimeMs = Date.now() - startTime;
|
|
12387
|
+
try {
|
|
12388
|
+
ws2.close();
|
|
12389
|
+
} catch {
|
|
12390
|
+
}
|
|
12391
|
+
resolve({ healthy: true, url, responseTimeMs });
|
|
12392
|
+
}
|
|
12393
|
+
};
|
|
12394
|
+
ws2.onerror = (event) => {
|
|
12395
|
+
if (!resolved) {
|
|
12396
|
+
resolved = true;
|
|
12397
|
+
clearTimeout(timer);
|
|
12398
|
+
try {
|
|
12399
|
+
ws2.close();
|
|
12400
|
+
} catch {
|
|
12401
|
+
}
|
|
12402
|
+
resolve({
|
|
12403
|
+
healthy: false,
|
|
12404
|
+
url,
|
|
12405
|
+
responseTimeMs: null,
|
|
12406
|
+
error: "WebSocket connection error"
|
|
12407
|
+
});
|
|
12408
|
+
}
|
|
12409
|
+
};
|
|
12410
|
+
ws2.onclose = (event) => {
|
|
12411
|
+
if (!resolved) {
|
|
12412
|
+
resolved = true;
|
|
12413
|
+
clearTimeout(timer);
|
|
12414
|
+
resolve({
|
|
12415
|
+
healthy: false,
|
|
12416
|
+
url,
|
|
12417
|
+
responseTimeMs: null,
|
|
12418
|
+
error: `WebSocket closed: ${event.reason || `code ${event.code}`}`
|
|
12419
|
+
});
|
|
12420
|
+
}
|
|
12421
|
+
};
|
|
12422
|
+
});
|
|
12423
|
+
}
|
|
12424
|
+
async function checkOracle(url, timeoutMs) {
|
|
12425
|
+
const startTime = Date.now();
|
|
12426
|
+
try {
|
|
12427
|
+
const controller = new AbortController();
|
|
12428
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
12429
|
+
const response = await fetch(url, {
|
|
12430
|
+
method: "POST",
|
|
12431
|
+
headers: { "Content-Type": "application/json" },
|
|
12432
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "get_round_number", params: {} }),
|
|
12433
|
+
signal: controller.signal
|
|
12434
|
+
});
|
|
12435
|
+
clearTimeout(timer);
|
|
12436
|
+
const responseTimeMs = Date.now() - startTime;
|
|
12437
|
+
if (response.ok) {
|
|
12438
|
+
return { healthy: true, url, responseTimeMs };
|
|
12439
|
+
}
|
|
12440
|
+
return {
|
|
12441
|
+
healthy: false,
|
|
12442
|
+
url,
|
|
12443
|
+
responseTimeMs,
|
|
12444
|
+
error: `HTTP ${response.status} ${response.statusText}`
|
|
12445
|
+
};
|
|
12446
|
+
} catch (err) {
|
|
12447
|
+
return {
|
|
12448
|
+
healthy: false,
|
|
12449
|
+
url,
|
|
12450
|
+
responseTimeMs: null,
|
|
12451
|
+
error: err instanceof Error ? err.name === "AbortError" ? `Connection timeout after ${timeoutMs}ms` : err.message : String(err)
|
|
12452
|
+
};
|
|
12453
|
+
}
|
|
12454
|
+
}
|
|
12455
|
+
async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
12456
|
+
try {
|
|
12457
|
+
const result = await Promise.race([
|
|
12458
|
+
checkFn(timeoutMs),
|
|
12459
|
+
new Promise(
|
|
12460
|
+
(resolve) => setTimeout(
|
|
12461
|
+
() => resolve({
|
|
12462
|
+
healthy: false,
|
|
12463
|
+
url: name,
|
|
12464
|
+
responseTimeMs: null,
|
|
12465
|
+
error: `Custom check '${name}' timeout after ${timeoutMs}ms`
|
|
12466
|
+
}),
|
|
12467
|
+
timeoutMs
|
|
12468
|
+
)
|
|
12469
|
+
)
|
|
12470
|
+
]);
|
|
12471
|
+
return result;
|
|
12472
|
+
} catch (err) {
|
|
12473
|
+
return {
|
|
12474
|
+
healthy: false,
|
|
12475
|
+
url: name,
|
|
12476
|
+
responseTimeMs: null,
|
|
12477
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12478
|
+
};
|
|
12479
|
+
}
|
|
12480
|
+
}
|
|
11959
12481
|
// Annotate the CommonJS export names for ESM import in node:
|
|
11960
12482
|
0 && (module.exports = {
|
|
11961
12483
|
CHARSET,
|
|
@@ -11966,6 +12488,7 @@ init_bech32();
|
|
|
11966
12488
|
base58Decode,
|
|
11967
12489
|
base58Encode,
|
|
11968
12490
|
bytesToHex,
|
|
12491
|
+
checkNetworkHealth,
|
|
11969
12492
|
computeHash160,
|
|
11970
12493
|
convertBits,
|
|
11971
12494
|
createAddress,
|