@unicitylabs/sphere-sdk 0.15.0-dev.1 → 0.16.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -5
- package/dist/connect/index.cjs +23 -25
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.d.cts +4 -0
- package/dist/connect/index.d.ts +4 -0
- package/dist/connect/index.js +23 -25
- package/dist/connect/index.js.map +1 -1
- package/dist/core/index.cjs +726 -225
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +172 -21
- package/dist/core/index.d.ts +172 -21
- package/dist/core/index.js +726 -224
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/connect/index.cjs +23 -25
- package/dist/impl/browser/connect/index.cjs.map +1 -1
- package/dist/impl/browser/connect/index.js +23 -25
- package/dist/impl/browser/connect/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +331 -504
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +331 -504
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/nodejs/connect/index.cjs +22 -24
- package/dist/impl/nodejs/connect/index.cjs.map +1 -1
- package/dist/impl/nodejs/connect/index.js +22 -24
- package/dist/impl/nodejs/connect/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +298 -569
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +90 -13
- package/dist/impl/nodejs/index.d.ts +90 -13
- package/dist/impl/nodejs/index.js +298 -569
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/impl/shared/wallet-api/index.d.cts +54 -2
- package/dist/impl/shared/wallet-api/index.d.ts +54 -2
- package/dist/index.cjs +728 -225
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +227 -108
- package/dist/index.d.ts +227 -108
- package/dist/index.js +728 -224
- package/dist/index.js.map +1 -1
- package/dist/modules/payments-v2/index.cjs +11 -7
- package/dist/modules/payments-v2/index.cjs.map +1 -1
- package/dist/modules/payments-v2/index.d.cts +56 -3
- package/dist/modules/payments-v2/index.d.ts +56 -3
- package/dist/modules/payments-v2/index.js +11 -7
- package/dist/modules/payments-v2/index.js.map +1 -1
- package/dist/token-engine/index.cjs +46 -0
- package/dist/token-engine/index.cjs.map +1 -1
- package/dist/token-engine/index.js +46 -0
- package/dist/token-engine/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -2631,9 +2631,9 @@ var require_dispatcher_base = __commonJS({
|
|
|
2631
2631
|
}
|
|
2632
2632
|
close(callback) {
|
|
2633
2633
|
if (callback === void 0) {
|
|
2634
|
-
return new Promise((
|
|
2634
|
+
return new Promise((resolve2, reject) => {
|
|
2635
2635
|
this.close((err, data) => {
|
|
2636
|
-
return err ? reject(err) :
|
|
2636
|
+
return err ? reject(err) : resolve2(data);
|
|
2637
2637
|
});
|
|
2638
2638
|
});
|
|
2639
2639
|
}
|
|
@@ -2671,9 +2671,9 @@ var require_dispatcher_base = __commonJS({
|
|
|
2671
2671
|
err = null;
|
|
2672
2672
|
}
|
|
2673
2673
|
if (callback === void 0) {
|
|
2674
|
-
return new Promise((
|
|
2674
|
+
return new Promise((resolve2, reject) => {
|
|
2675
2675
|
this.destroy(err, (err2, data) => {
|
|
2676
|
-
return err2 ? reject(err2) :
|
|
2676
|
+
return err2 ? reject(err2) : resolve2(data);
|
|
2677
2677
|
});
|
|
2678
2678
|
});
|
|
2679
2679
|
}
|
|
@@ -6127,8 +6127,8 @@ var require_promise = __commonJS({
|
|
|
6127
6127
|
function createDeferredPromise() {
|
|
6128
6128
|
let res;
|
|
6129
6129
|
let rej;
|
|
6130
|
-
const promise = new Promise((
|
|
6131
|
-
res =
|
|
6130
|
+
const promise = new Promise((resolve2, reject) => {
|
|
6131
|
+
res = resolve2;
|
|
6132
6132
|
rej = reject;
|
|
6133
6133
|
});
|
|
6134
6134
|
return { promise, resolve: res, reject: rej };
|
|
@@ -7435,12 +7435,12 @@ upgrade: ${upgrade}\r
|
|
|
7435
7435
|
cb();
|
|
7436
7436
|
}
|
|
7437
7437
|
}
|
|
7438
|
-
const waitForDrain = () => new Promise((
|
|
7438
|
+
const waitForDrain = () => new Promise((resolve2, reject) => {
|
|
7439
7439
|
assert(callback === null);
|
|
7440
7440
|
if (socket[kError]) {
|
|
7441
7441
|
reject(socket[kError]);
|
|
7442
7442
|
} else {
|
|
7443
|
-
callback =
|
|
7443
|
+
callback = resolve2;
|
|
7444
7444
|
}
|
|
7445
7445
|
});
|
|
7446
7446
|
socket.on("close", onDrain).on("drain", onDrain);
|
|
@@ -8252,12 +8252,12 @@ var require_client_h2 = __commonJS({
|
|
|
8252
8252
|
cb();
|
|
8253
8253
|
}
|
|
8254
8254
|
}
|
|
8255
|
-
const waitForDrain = () => new Promise((
|
|
8255
|
+
const waitForDrain = () => new Promise((resolve2, reject) => {
|
|
8256
8256
|
assert(callback === null);
|
|
8257
8257
|
if (socket[kError]) {
|
|
8258
8258
|
reject(socket[kError]);
|
|
8259
8259
|
} else {
|
|
8260
|
-
callback =
|
|
8260
|
+
callback = resolve2;
|
|
8261
8261
|
}
|
|
8262
8262
|
});
|
|
8263
8263
|
h2stream.on("close", onDrain).on("drain", onDrain);
|
|
@@ -8559,16 +8559,16 @@ var require_client = __commonJS({
|
|
|
8559
8559
|
return this[kNeedDrain] < 2;
|
|
8560
8560
|
}
|
|
8561
8561
|
[kClose]() {
|
|
8562
|
-
return new Promise((
|
|
8562
|
+
return new Promise((resolve2) => {
|
|
8563
8563
|
if (this[kSize]) {
|
|
8564
|
-
this[kClosedResolve] =
|
|
8564
|
+
this[kClosedResolve] = resolve2;
|
|
8565
8565
|
} else {
|
|
8566
|
-
|
|
8566
|
+
resolve2(null);
|
|
8567
8567
|
}
|
|
8568
8568
|
});
|
|
8569
8569
|
}
|
|
8570
8570
|
[kDestroy](err) {
|
|
8571
|
-
return new Promise((
|
|
8571
|
+
return new Promise((resolve2) => {
|
|
8572
8572
|
const requests = this[kQueue].splice(this[kPendingIdx]);
|
|
8573
8573
|
for (let i = 0; i < requests.length; i++) {
|
|
8574
8574
|
const request = requests[i];
|
|
@@ -8579,7 +8579,7 @@ var require_client = __commonJS({
|
|
|
8579
8579
|
this[kClosedResolve]();
|
|
8580
8580
|
this[kClosedResolve] = null;
|
|
8581
8581
|
}
|
|
8582
|
-
|
|
8582
|
+
resolve2(null);
|
|
8583
8583
|
};
|
|
8584
8584
|
if (this[kHTTPContext]) {
|
|
8585
8585
|
this[kHTTPContext].destroy(err, callback);
|
|
@@ -8970,8 +8970,8 @@ var require_pool_base = __commonJS({
|
|
|
8970
8970
|
}
|
|
8971
8971
|
return Promise.all(closeAll);
|
|
8972
8972
|
} else {
|
|
8973
|
-
return new Promise((
|
|
8974
|
-
this[kClosedResolve] =
|
|
8973
|
+
return new Promise((resolve2) => {
|
|
8974
|
+
this[kClosedResolve] = resolve2;
|
|
8975
8975
|
});
|
|
8976
8976
|
}
|
|
8977
8977
|
}
|
|
@@ -10500,7 +10500,7 @@ var require_readable = __commonJS({
|
|
|
10500
10500
|
if (this._readableState.closeEmitted) {
|
|
10501
10501
|
return Promise.resolve(null);
|
|
10502
10502
|
}
|
|
10503
|
-
return new Promise((
|
|
10503
|
+
return new Promise((resolve2, reject) => {
|
|
10504
10504
|
if (this[kContentLength] && this[kContentLength] > limit || this[kBytesRead] > limit) {
|
|
10505
10505
|
this.destroy(new AbortError());
|
|
10506
10506
|
}
|
|
@@ -10514,11 +10514,11 @@ var require_readable = __commonJS({
|
|
|
10514
10514
|
if (signal.aborted) {
|
|
10515
10515
|
reject(signal.reason ?? new AbortError());
|
|
10516
10516
|
} else {
|
|
10517
|
-
|
|
10517
|
+
resolve2(null);
|
|
10518
10518
|
}
|
|
10519
10519
|
});
|
|
10520
10520
|
} else {
|
|
10521
|
-
this.on("close",
|
|
10521
|
+
this.on("close", resolve2);
|
|
10522
10522
|
}
|
|
10523
10523
|
this.on("error", noop).on("data", () => {
|
|
10524
10524
|
if (this[kBytesRead] > limit) {
|
|
@@ -10546,7 +10546,7 @@ var require_readable = __commonJS({
|
|
|
10546
10546
|
}
|
|
10547
10547
|
function consume(stream, type) {
|
|
10548
10548
|
assert(!stream[kConsume]);
|
|
10549
|
-
return new Promise((
|
|
10549
|
+
return new Promise((resolve2, reject) => {
|
|
10550
10550
|
if (isUnusable(stream)) {
|
|
10551
10551
|
const rState = stream._readableState;
|
|
10552
10552
|
if (rState.destroyed && rState.closeEmitted === false) {
|
|
@@ -10561,7 +10561,7 @@ var require_readable = __commonJS({
|
|
|
10561
10561
|
stream[kConsume] = {
|
|
10562
10562
|
type,
|
|
10563
10563
|
stream,
|
|
10564
|
-
resolve,
|
|
10564
|
+
resolve: resolve2,
|
|
10565
10565
|
reject,
|
|
10566
10566
|
length: 0,
|
|
10567
10567
|
body: []
|
|
@@ -10635,18 +10635,18 @@ var require_readable = __commonJS({
|
|
|
10635
10635
|
return buffer;
|
|
10636
10636
|
}
|
|
10637
10637
|
function consumeEnd(consume2, encoding) {
|
|
10638
|
-
const { type, body, resolve, stream, length } = consume2;
|
|
10638
|
+
const { type, body, resolve: resolve2, stream, length } = consume2;
|
|
10639
10639
|
try {
|
|
10640
10640
|
if (type === "text") {
|
|
10641
|
-
|
|
10641
|
+
resolve2(chunksDecode(body, length, encoding));
|
|
10642
10642
|
} else if (type === "json") {
|
|
10643
|
-
|
|
10643
|
+
resolve2(JSON.parse(chunksDecode(body, length, encoding)));
|
|
10644
10644
|
} else if (type === "arrayBuffer") {
|
|
10645
|
-
|
|
10645
|
+
resolve2(chunksConcat(body, length).buffer);
|
|
10646
10646
|
} else if (type === "blob") {
|
|
10647
|
-
|
|
10647
|
+
resolve2(new Blob(body, { type: stream[kContentType] }));
|
|
10648
10648
|
} else if (type === "bytes") {
|
|
10649
|
-
|
|
10649
|
+
resolve2(chunksConcat(body, length));
|
|
10650
10650
|
}
|
|
10651
10651
|
consumeFinish(consume2);
|
|
10652
10652
|
} catch (err) {
|
|
@@ -10835,9 +10835,9 @@ var require_api_request = __commonJS({
|
|
|
10835
10835
|
};
|
|
10836
10836
|
function request(opts, callback) {
|
|
10837
10837
|
if (callback === void 0) {
|
|
10838
|
-
return new Promise((
|
|
10838
|
+
return new Promise((resolve2, reject) => {
|
|
10839
10839
|
request.call(this, opts, (err, data) => {
|
|
10840
|
-
return err ? reject(err) :
|
|
10840
|
+
return err ? reject(err) : resolve2(data);
|
|
10841
10841
|
});
|
|
10842
10842
|
});
|
|
10843
10843
|
}
|
|
@@ -11049,9 +11049,9 @@ var require_api_stream = __commonJS({
|
|
|
11049
11049
|
};
|
|
11050
11050
|
function stream(opts, factory, callback) {
|
|
11051
11051
|
if (callback === void 0) {
|
|
11052
|
-
return new Promise((
|
|
11052
|
+
return new Promise((resolve2, reject) => {
|
|
11053
11053
|
stream.call(this, opts, factory, (err, data) => {
|
|
11054
|
-
return err ? reject(err) :
|
|
11054
|
+
return err ? reject(err) : resolve2(data);
|
|
11055
11055
|
});
|
|
11056
11056
|
});
|
|
11057
11057
|
}
|
|
@@ -11339,9 +11339,9 @@ var require_api_upgrade = __commonJS({
|
|
|
11339
11339
|
};
|
|
11340
11340
|
function upgrade(opts, callback) {
|
|
11341
11341
|
if (callback === void 0) {
|
|
11342
|
-
return new Promise((
|
|
11342
|
+
return new Promise((resolve2, reject) => {
|
|
11343
11343
|
upgrade.call(this, opts, (err, data) => {
|
|
11344
|
-
return err ? reject(err) :
|
|
11344
|
+
return err ? reject(err) : resolve2(data);
|
|
11345
11345
|
});
|
|
11346
11346
|
});
|
|
11347
11347
|
}
|
|
@@ -11434,9 +11434,9 @@ var require_api_connect = __commonJS({
|
|
|
11434
11434
|
};
|
|
11435
11435
|
function connect(opts, callback) {
|
|
11436
11436
|
if (callback === void 0) {
|
|
11437
|
-
return new Promise((
|
|
11437
|
+
return new Promise((resolve2, reject) => {
|
|
11438
11438
|
connect.call(this, opts, (err, data) => {
|
|
11439
|
-
return err ? reject(err) :
|
|
11439
|
+
return err ? reject(err) : resolve2(data);
|
|
11440
11440
|
});
|
|
11441
11441
|
});
|
|
11442
11442
|
}
|
|
@@ -12684,7 +12684,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
12684
12684
|
"node_modules/undici/lib/mock/snapshot-recorder.js"(exports2, module2) {
|
|
12685
12685
|
"use strict";
|
|
12686
12686
|
var { writeFile, readFile, mkdir } = require("fs/promises");
|
|
12687
|
-
var { dirname, resolve } = require("path");
|
|
12687
|
+
var { dirname, resolve: resolve2 } = require("path");
|
|
12688
12688
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = require("timers");
|
|
12689
12689
|
var { InvalidArgumentError, UndiciError } = require_errors();
|
|
12690
12690
|
var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -12885,7 +12885,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
12885
12885
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
12886
12886
|
}
|
|
12887
12887
|
try {
|
|
12888
|
-
const data = await readFile(
|
|
12888
|
+
const data = await readFile(resolve2(path2), "utf8");
|
|
12889
12889
|
const parsed = JSON.parse(data);
|
|
12890
12890
|
if (Array.isArray(parsed)) {
|
|
12891
12891
|
this.#snapshots.clear();
|
|
@@ -12914,7 +12914,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
12914
12914
|
if (!path2) {
|
|
12915
12915
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
12916
12916
|
}
|
|
12917
|
-
const resolvedPath =
|
|
12917
|
+
const resolvedPath = resolve2(path2);
|
|
12918
12918
|
await mkdir(dirname(resolvedPath), { recursive: true });
|
|
12919
12919
|
const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
|
|
12920
12920
|
hash,
|
|
@@ -19471,7 +19471,7 @@ var require_fetch = __commonJS({
|
|
|
19471
19471
|
function dispatch({ body }) {
|
|
19472
19472
|
const url = requestCurrentURL(request);
|
|
19473
19473
|
const agent = fetchParams.controller.dispatcher;
|
|
19474
|
-
return new Promise((
|
|
19474
|
+
return new Promise((resolve2, reject) => agent.dispatch(
|
|
19475
19475
|
{
|
|
19476
19476
|
path: url.pathname + url.search,
|
|
19477
19477
|
origin: url.origin,
|
|
@@ -19551,7 +19551,7 @@ var require_fetch = __commonJS({
|
|
|
19551
19551
|
}
|
|
19552
19552
|
}
|
|
19553
19553
|
const onError = this.onError.bind(this);
|
|
19554
|
-
|
|
19554
|
+
resolve2({
|
|
19555
19555
|
status,
|
|
19556
19556
|
statusText,
|
|
19557
19557
|
headersList,
|
|
@@ -19594,7 +19594,7 @@ var require_fetch = __commonJS({
|
|
|
19594
19594
|
for (let i = 0; i < rawHeaders.length; i += 2) {
|
|
19595
19595
|
headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString("latin1"), true);
|
|
19596
19596
|
}
|
|
19597
|
-
|
|
19597
|
+
resolve2({
|
|
19598
19598
|
status,
|
|
19599
19599
|
statusText: STATUS_CODES[status],
|
|
19600
19600
|
headersList,
|
|
@@ -23689,7 +23689,6 @@ function isNetworkScopedAddressKey(key) {
|
|
|
23689
23689
|
return false;
|
|
23690
23690
|
}
|
|
23691
23691
|
var DEFAULT_NOSTR_RELAYS = [
|
|
23692
|
-
"wss://relay.unicity.network",
|
|
23693
23692
|
"wss://relay.damus.io",
|
|
23694
23693
|
"wss://nos.lol",
|
|
23695
23694
|
"wss://relay.nostr.band"
|
|
@@ -23702,13 +23701,9 @@ var NOSTR_EVENT_KINDS = {
|
|
|
23702
23701
|
/** Public broadcast */
|
|
23703
23702
|
BROADCAST: 1
|
|
23704
23703
|
};
|
|
23705
|
-
var DEFAULT_AGGREGATOR_URL = "https://aggregator.unicity.network/rpc";
|
|
23706
|
-
var DEV_AGGREGATOR_URL = "https://dev-aggregator.dyndns.org/rpc";
|
|
23707
23704
|
var DEFAULT_AGGREGATOR_TIMEOUT = 3e4;
|
|
23708
23705
|
var DEFAULT_BASE_PATH = "m/44'/0'/0'";
|
|
23709
23706
|
var DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0`;
|
|
23710
|
-
var TOKEN_REGISTRY_URL = "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
|
|
23711
|
-
var TOKEN_REGISTRY_REFRESH_INTERVAL = 36e5;
|
|
23712
23707
|
var TEST_NOSTR_RELAYS = [
|
|
23713
23708
|
"wss://nostr-relay.testnet.unicity.network"
|
|
23714
23709
|
];
|
|
@@ -23718,10 +23713,19 @@ var DEFAULT_GROUP_RELAYS = [
|
|
|
23718
23713
|
var NETWORKS = {
|
|
23719
23714
|
mainnet: {
|
|
23720
23715
|
name: "Mainnet",
|
|
23721
|
-
|
|
23722
|
-
|
|
23716
|
+
networkId: 1,
|
|
23717
|
+
// v3 state-transition gateway (networkId 1 comes from the trust base). apiKey is env-injected;
|
|
23718
|
+
// unlike testnet2's, a mainnet gateway key is a SECRET — never commit one.
|
|
23719
|
+
aggregatorUrl: "https://gateway.mainnet.unicity.network",
|
|
23720
|
+
// Mainnet has no relay of its own yet — it shares the testnet relay until one is stood up.
|
|
23721
|
+
// Consequence while shared: nametag bindings for both networks live in ONE namespace, and
|
|
23722
|
+
// since bindings carry no network the cross-network recipient guard can only signal (#734).
|
|
23723
|
+
nostrRelays: TEST_NOSTR_RELAYS,
|
|
23723
23724
|
groupRelays: DEFAULT_GROUP_RELAYS,
|
|
23724
|
-
|
|
23725
|
+
// Published, but currently only the non-fungible base token type — no fungible
|
|
23726
|
+
// coins yet, so those still miss the registry (decimals 0, symbol falls back to
|
|
23727
|
+
// six hex chars). Presentation only: the money path treats coinId as opaque bytes.
|
|
23728
|
+
tokenRegistryUrl: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.mainnet.json"
|
|
23725
23729
|
},
|
|
23726
23730
|
// v1 cutover: 'testnet' now POINTS AT TESTNET2 (the v2 gateway network). The
|
|
23727
23731
|
// old goggregator testnet spoke the removed v1 protocol — a v2 engine cannot
|
|
@@ -23745,19 +23749,10 @@ var NETWORKS = {
|
|
|
23745
23749
|
// reuse testnet infra (shared relays/ipfs)
|
|
23746
23750
|
groupRelays: DEFAULT_GROUP_RELAYS,
|
|
23747
23751
|
tokenRegistryUrl: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json"
|
|
23748
|
-
},
|
|
23749
|
-
// NOTE: mainnet/dev still point at v1-era aggregators. The v2 engine cannot
|
|
23750
|
-
// operate against them until their gateways are cut over to the v2 protocol —
|
|
23751
|
-
// wallet operations on these networks fail loudly (AGGREGATOR_ERROR) until then.
|
|
23752
|
-
dev: {
|
|
23753
|
-
name: "Development",
|
|
23754
|
-
aggregatorUrl: DEV_AGGREGATOR_URL,
|
|
23755
|
-
nostrRelays: TEST_NOSTR_RELAYS,
|
|
23756
|
-
groupRelays: DEFAULT_GROUP_RELAYS,
|
|
23757
|
-
tokenRegistryUrl: TOKEN_REGISTRY_URL
|
|
23758
23752
|
}
|
|
23759
23753
|
};
|
|
23760
23754
|
var SPHERE_NETWORKS = {
|
|
23755
|
+
mainnet: { id: NETWORKS.mainnet.networkId, name: "mainnet" },
|
|
23761
23756
|
testnet2: { id: NETWORKS.testnet2.networkId, name: "testnet2" }
|
|
23762
23757
|
};
|
|
23763
23758
|
var TIMEOUTS = {
|
|
@@ -23773,11 +23768,98 @@ var TIMEOUTS = {
|
|
|
23773
23768
|
SYNC_INTERVAL: 6e4
|
|
23774
23769
|
};
|
|
23775
23770
|
|
|
23771
|
+
// core/errors.ts
|
|
23772
|
+
var SphereError = class extends Error {
|
|
23773
|
+
code;
|
|
23774
|
+
cause;
|
|
23775
|
+
/**
|
|
23776
|
+
* #441 deferred-paid linkage: the transferId of the possibly-committed send,
|
|
23777
|
+
* stamped by `sendOnce` before the error leaves so a payment-request consumer
|
|
23778
|
+
* can durably journal request→transfer and resolve 'paid' only when that
|
|
23779
|
+
* transfer actually completes (never optimistically). Present ONLY on
|
|
23780
|
+
* possibly-committed outcomes ({@link isPossiblyCommittedSendOutcome}).
|
|
23781
|
+
*/
|
|
23782
|
+
transferId;
|
|
23783
|
+
constructor(message, code, cause) {
|
|
23784
|
+
super(message);
|
|
23785
|
+
this.name = "SphereError";
|
|
23786
|
+
this.code = code;
|
|
23787
|
+
this.cause = cause;
|
|
23788
|
+
}
|
|
23789
|
+
};
|
|
23790
|
+
|
|
23791
|
+
// storage/tracked-addresses.ts
|
|
23792
|
+
function isDerivableIndex(value) {
|
|
23793
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 4294967295;
|
|
23794
|
+
}
|
|
23795
|
+
function num(value, fallback) {
|
|
23796
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
23797
|
+
}
|
|
23798
|
+
function toEntry(value) {
|
|
23799
|
+
if (typeof value !== "object" || value === null) return null;
|
|
23800
|
+
const e = value;
|
|
23801
|
+
if (!isDerivableIndex(e.index)) return null;
|
|
23802
|
+
return {
|
|
23803
|
+
...e,
|
|
23804
|
+
index: e.index,
|
|
23805
|
+
hidden: e.hidden === true,
|
|
23806
|
+
createdAt: num(e.createdAt, 0),
|
|
23807
|
+
updatedAt: num(e.updatedAt, 0)
|
|
23808
|
+
};
|
|
23809
|
+
}
|
|
23810
|
+
function parseTrackedAddresses(raw) {
|
|
23811
|
+
if (!raw) return [];
|
|
23812
|
+
let parsed;
|
|
23813
|
+
try {
|
|
23814
|
+
parsed = JSON.parse(raw);
|
|
23815
|
+
} catch {
|
|
23816
|
+
return [];
|
|
23817
|
+
}
|
|
23818
|
+
if (typeof parsed !== "object" || parsed === null) return [];
|
|
23819
|
+
const addresses = parsed.addresses;
|
|
23820
|
+
if (!Array.isArray(addresses)) return [];
|
|
23821
|
+
const entries = [];
|
|
23822
|
+
for (const row of addresses) {
|
|
23823
|
+
const entry = toEntry(row);
|
|
23824
|
+
if (entry) entries.push(entry);
|
|
23825
|
+
}
|
|
23826
|
+
return entries;
|
|
23827
|
+
}
|
|
23828
|
+
function mergeTrackedAddresses(onDisk, incoming) {
|
|
23829
|
+
const merged = /* @__PURE__ */ new Map();
|
|
23830
|
+
for (const entry of onDisk) {
|
|
23831
|
+
if (isDerivableIndex(entry.index)) merged.set(entry.index, entry);
|
|
23832
|
+
}
|
|
23833
|
+
for (const entry of incoming) {
|
|
23834
|
+
if (!isDerivableIndex(entry.index)) {
|
|
23835
|
+
throw new SphereError(
|
|
23836
|
+
`Tracked address index ${String(entry.index)} is not a BIP32 child number: it must be an integer in 0\u20260xffffffff. Refusing the write \u2014 such a row derives another address's keys (1.5 parses to 1) instead of its own.`,
|
|
23837
|
+
"VALIDATION_ERROR"
|
|
23838
|
+
);
|
|
23839
|
+
}
|
|
23840
|
+
const existing = merged.get(entry.index);
|
|
23841
|
+
if (!existing) {
|
|
23842
|
+
merged.set(entry.index, entry);
|
|
23843
|
+
continue;
|
|
23844
|
+
}
|
|
23845
|
+
const winner = entry.updatedAt >= existing.updatedAt ? entry : existing;
|
|
23846
|
+
merged.set(entry.index, {
|
|
23847
|
+
...existing,
|
|
23848
|
+
...winner,
|
|
23849
|
+
index: entry.index,
|
|
23850
|
+
createdAt: Math.min(existing.createdAt, entry.createdAt)
|
|
23851
|
+
});
|
|
23852
|
+
}
|
|
23853
|
+
return Array.from(merged.values()).sort((a, b) => a.index - b.index);
|
|
23854
|
+
}
|
|
23855
|
+
|
|
23776
23856
|
// impl/nodejs/storage/FileStorageProvider.ts
|
|
23777
23857
|
var FileStorageProvider = class {
|
|
23778
23858
|
id = "file-storage";
|
|
23779
23859
|
name = "File Storage";
|
|
23780
23860
|
type = "local";
|
|
23861
|
+
/** The resolved wallet file — two providers over one path share erasure (#766). */
|
|
23862
|
+
backingStoreId;
|
|
23781
23863
|
dataDir;
|
|
23782
23864
|
filePath;
|
|
23783
23865
|
isTxtMode;
|
|
@@ -23787,14 +23869,15 @@ var FileStorageProvider = class {
|
|
|
23787
23869
|
_identity = null;
|
|
23788
23870
|
constructor(config) {
|
|
23789
23871
|
if (typeof config === "string") {
|
|
23790
|
-
this.dataDir = config;
|
|
23791
|
-
this.filePath = path.join(
|
|
23872
|
+
this.dataDir = path.resolve(config);
|
|
23873
|
+
this.filePath = path.join(this.dataDir, "wallet.json");
|
|
23792
23874
|
} else {
|
|
23793
|
-
this.dataDir = config.dataDir;
|
|
23794
|
-
this.filePath = path.join(
|
|
23875
|
+
this.dataDir = path.resolve(config.dataDir);
|
|
23876
|
+
this.filePath = path.join(this.dataDir, config.fileName ?? "wallet.json");
|
|
23795
23877
|
this.network = config.network;
|
|
23796
23878
|
}
|
|
23797
23879
|
this.isTxtMode = this.filePath.endsWith(".txt");
|
|
23880
|
+
this.backingStoreId = `file:${this.filePath}`;
|
|
23798
23881
|
}
|
|
23799
23882
|
setIdentity(identity) {
|
|
23800
23883
|
this._identity = identity;
|
|
@@ -23891,18 +23974,40 @@ var FileStorageProvider = class {
|
|
|
23891
23974
|
}
|
|
23892
23975
|
await this.save();
|
|
23893
23976
|
}
|
|
23977
|
+
/** Serializes the read-merge-write below, per provider instance. */
|
|
23978
|
+
trackedWrites = Promise.resolve();
|
|
23979
|
+
/**
|
|
23980
|
+
* Persist the tracked-address registry by MERGING, never replacing.
|
|
23981
|
+
*
|
|
23982
|
+
* Every Sphere over this storage holds its own snapshot and writes it in
|
|
23983
|
+
* full, so a wholesale write drops the addresses this writer never saw
|
|
23984
|
+
* (#766 item 5 — a lost update, reproducible on one network). Concurrent
|
|
23985
|
+
* calls are serialized on `trackedWrites` so a read can never interleave
|
|
23986
|
+
* with another call's write.
|
|
23987
|
+
*
|
|
23988
|
+
* Deliberately PER OBJECT, unlike the browser providers. Two objects over one
|
|
23989
|
+
* `dataDir` share a `backingStoreId` but not this cache, and `save()` rewrites the
|
|
23990
|
+
* WHOLE file from it — so a sibling's *unrelated* `set()` rolls the registry back
|
|
23991
|
+
* regardless of how this one write is serialized. Sharing the chain here would make
|
|
23992
|
+
* the cross-object contract case pass while leaving the provider unsafe. The real
|
|
23993
|
+
* fix is #771 (refresh from disk under a per-file lock, on every write); until then
|
|
23994
|
+
* `backingStoreId` scopes TEARDOWN only. Reviewers keep re-finding this — see the
|
|
23995
|
+
* `unsupported:` note in tests/unit/storage/tracked-addresses-providers.test.ts.
|
|
23996
|
+
*/
|
|
23894
23997
|
async saveTrackedAddresses(entries) {
|
|
23895
|
-
|
|
23998
|
+
const run = this.trackedWrites.then(async () => {
|
|
23999
|
+
const onDisk = parseTrackedAddresses(await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES));
|
|
24000
|
+
const file = {
|
|
24001
|
+
version: 1,
|
|
24002
|
+
addresses: mergeTrackedAddresses(onDisk, entries)
|
|
24003
|
+
};
|
|
24004
|
+
await this.set(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES, JSON.stringify(file));
|
|
24005
|
+
});
|
|
24006
|
+
this.trackedWrites = run.then(() => void 0, () => void 0);
|
|
24007
|
+
await run;
|
|
23896
24008
|
}
|
|
23897
24009
|
async loadTrackedAddresses() {
|
|
23898
|
-
|
|
23899
|
-
if (!data) return [];
|
|
23900
|
-
try {
|
|
23901
|
-
const parsed = JSON.parse(data);
|
|
23902
|
-
return parsed.addresses ?? [];
|
|
23903
|
-
} catch {
|
|
23904
|
-
return [];
|
|
23905
|
-
}
|
|
24010
|
+
return parseTrackedAddresses(await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES));
|
|
23906
24011
|
}
|
|
23907
24012
|
/**
|
|
23908
24013
|
* Get full storage key with address prefix for per-address keys
|
|
@@ -24421,26 +24526,6 @@ var logger = {
|
|
|
24421
24526
|
}
|
|
24422
24527
|
};
|
|
24423
24528
|
|
|
24424
|
-
// core/errors.ts
|
|
24425
|
-
var SphereError = class extends Error {
|
|
24426
|
-
code;
|
|
24427
|
-
cause;
|
|
24428
|
-
/**
|
|
24429
|
-
* #441 deferred-paid linkage: the transferId of the possibly-committed send,
|
|
24430
|
-
* stamped by `sendOnce` before the error leaves so a payment-request consumer
|
|
24431
|
-
* can durably journal request→transfer and resolve 'paid' only when that
|
|
24432
|
-
* transfer actually completes (never optimistically). Present ONLY on
|
|
24433
|
-
* possibly-committed outcomes ({@link isPossiblyCommittedSendOutcome}).
|
|
24434
|
-
*/
|
|
24435
|
-
transferId;
|
|
24436
|
-
constructor(message, code, cause) {
|
|
24437
|
-
super(message);
|
|
24438
|
-
this.name = "SphereError";
|
|
24439
|
-
this.code = code;
|
|
24440
|
-
this.cause = cause;
|
|
24441
|
-
}
|
|
24442
|
-
};
|
|
24443
|
-
|
|
24444
24529
|
// transport/websocket.ts
|
|
24445
24530
|
function defaultUUIDGenerator() {
|
|
24446
24531
|
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
@@ -24613,14 +24698,10 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
24613
24698
|
});
|
|
24614
24699
|
}
|
|
24615
24700
|
});
|
|
24616
|
-
await
|
|
24617
|
-
this.nostrClient
|
|
24618
|
-
|
|
24619
|
-
|
|
24620
|
-
`Transport connection timed out after ${this.config.timeout}ms`
|
|
24621
|
-
)), this.config.timeout)
|
|
24622
|
-
)
|
|
24623
|
-
]);
|
|
24701
|
+
await this.connectWithDeadline(
|
|
24702
|
+
this.nostrClient,
|
|
24703
|
+
`Transport connection timed out after ${this.config.timeout}ms`
|
|
24704
|
+
);
|
|
24624
24705
|
if (!this.nostrClient.isConnected()) {
|
|
24625
24706
|
throw new SphereError("Failed to connect to any relay", "TRANSPORT_ERROR");
|
|
24626
24707
|
}
|
|
@@ -24635,6 +24716,27 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
24635
24716
|
throw error;
|
|
24636
24717
|
}
|
|
24637
24718
|
}
|
|
24719
|
+
/**
|
|
24720
|
+
* Race one client's relay connect against the configured timeout.
|
|
24721
|
+
*
|
|
24722
|
+
* The timer is cleared in a `finally` on EVERY path. `Promise.race` does not
|
|
24723
|
+
* cancel the loser: an un-cleared `setTimeout` keeps Node's event loop pinned
|
|
24724
|
+
* for the whole `config.timeout` after the call has already returned (and
|
|
24725
|
+
* then rejects a promise nobody is listening to any more).
|
|
24726
|
+
*/
|
|
24727
|
+
async connectWithDeadline(client, timeoutMessage) {
|
|
24728
|
+
let timer;
|
|
24729
|
+
try {
|
|
24730
|
+
await Promise.race([
|
|
24731
|
+
client.connect(...this.config.relays),
|
|
24732
|
+
new Promise((_, reject) => {
|
|
24733
|
+
timer = setTimeout(() => reject(new Error(timeoutMessage)), this.config.timeout);
|
|
24734
|
+
})
|
|
24735
|
+
]);
|
|
24736
|
+
} finally {
|
|
24737
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
24738
|
+
}
|
|
24739
|
+
}
|
|
24638
24740
|
async disconnect() {
|
|
24639
24741
|
if (this.nostrClient) {
|
|
24640
24742
|
this.nostrClient.disconnect();
|
|
@@ -24753,19 +24855,22 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
24753
24855
|
// TransportProvider Implementation
|
|
24754
24856
|
// ===========================================================================
|
|
24755
24857
|
async setIdentity(identity) {
|
|
24756
|
-
this.identity = identity;
|
|
24757
|
-
this.processedEventIds.clear();
|
|
24758
|
-
this.lastEventTs = 0;
|
|
24759
|
-
this.lastDmEventTs = 0;
|
|
24760
|
-
this.fallbackDmSince = null;
|
|
24761
24858
|
const secretKey = import_buffer.Buffer.from(identity.privateKey, "hex");
|
|
24762
|
-
|
|
24763
|
-
const nostrPubkey =
|
|
24764
|
-
logger.debug("Nostr", "Identity
|
|
24859
|
+
const nextKeyManager = import_nostr_js_sdk.NostrKeyManager.fromPrivateKey(secretKey);
|
|
24860
|
+
const nostrPubkey = nextKeyManager.getPublicKeyHex();
|
|
24861
|
+
logger.debug("Nostr", "Identity staged, Nostr pubkey:", nostrPubkey.slice(0, 16) + "...");
|
|
24862
|
+
const applyStagedIdentity = () => {
|
|
24863
|
+
this.identity = identity;
|
|
24864
|
+
this.keyManager = nextKeyManager;
|
|
24865
|
+
this.processedEventIds.clear();
|
|
24866
|
+
this.lastEventTs = 0;
|
|
24867
|
+
this.lastDmEventTs = 0;
|
|
24868
|
+
this.fallbackDmSince = null;
|
|
24869
|
+
};
|
|
24765
24870
|
if (this.nostrClient && this.status === "connected") {
|
|
24766
24871
|
logger.debug("Nostr", "Identity changed while connected - recreating NostrClient");
|
|
24767
24872
|
const oldClient = this.nostrClient;
|
|
24768
|
-
|
|
24873
|
+
const nextClient = new import_nostr_js_sdk.NostrClient(nextKeyManager, {
|
|
24769
24874
|
autoReconnect: this.config.autoReconnect,
|
|
24770
24875
|
reconnectIntervalMs: this.config.reconnectDelay,
|
|
24771
24876
|
maxReconnectIntervalMs: this.config.reconnectDelay * 16,
|
|
@@ -24774,7 +24879,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
24774
24879
|
queryTimeoutMs: 2e4
|
|
24775
24880
|
// see same option above for rationale
|
|
24776
24881
|
});
|
|
24777
|
-
|
|
24882
|
+
nextClient.addConnectionListener({
|
|
24778
24883
|
onConnect: (url) => {
|
|
24779
24884
|
logger.debug("Nostr", "NostrClient connected to relay:", url);
|
|
24780
24885
|
},
|
|
@@ -24788,18 +24893,43 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
24788
24893
|
logger.debug("Nostr", "NostrClient reconnected to relay:", url);
|
|
24789
24894
|
}
|
|
24790
24895
|
});
|
|
24791
|
-
|
|
24792
|
-
this.
|
|
24793
|
-
|
|
24794
|
-
|
|
24795
|
-
|
|
24796
|
-
|
|
24797
|
-
|
|
24798
|
-
|
|
24799
|
-
|
|
24800
|
-
|
|
24896
|
+
try {
|
|
24897
|
+
await this.connectWithDeadline(
|
|
24898
|
+
nextClient,
|
|
24899
|
+
`Transport reconnection timed out after ${this.config.timeout}ms`
|
|
24900
|
+
);
|
|
24901
|
+
} catch (error) {
|
|
24902
|
+
try {
|
|
24903
|
+
nextClient.disconnect();
|
|
24904
|
+
} catch {
|
|
24905
|
+
}
|
|
24906
|
+
throw error;
|
|
24907
|
+
}
|
|
24908
|
+
if (this.nostrClient !== oldClient || this.status !== "connected") {
|
|
24909
|
+
try {
|
|
24910
|
+
nextClient.disconnect();
|
|
24911
|
+
} catch {
|
|
24912
|
+
}
|
|
24913
|
+
throw new SphereError(
|
|
24914
|
+
"Transport client changed or left the connected state during setIdentity - identity not applied",
|
|
24915
|
+
"TRANSPORT_ERROR"
|
|
24916
|
+
);
|
|
24917
|
+
}
|
|
24918
|
+
this.nostrClient = nextClient;
|
|
24919
|
+
applyStagedIdentity();
|
|
24920
|
+
try {
|
|
24921
|
+
await this.subscribeToEvents();
|
|
24922
|
+
} finally {
|
|
24923
|
+
try {
|
|
24924
|
+
oldClient.disconnect();
|
|
24925
|
+
} catch {
|
|
24926
|
+
}
|
|
24927
|
+
}
|
|
24801
24928
|
} else if (this.isConnected()) {
|
|
24929
|
+
applyStagedIdentity();
|
|
24802
24930
|
await this.subscribeToEvents();
|
|
24931
|
+
} else {
|
|
24932
|
+
applyStagedIdentity();
|
|
24803
24933
|
}
|
|
24804
24934
|
}
|
|
24805
24935
|
setFallbackSince(sinceSeconds) {
|
|
@@ -25566,14 +25696,14 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
25566
25696
|
const client = this.nostrClient;
|
|
25567
25697
|
const events = [];
|
|
25568
25698
|
let subId;
|
|
25569
|
-
await new Promise((
|
|
25699
|
+
await new Promise((resolve2, reject) => {
|
|
25570
25700
|
let timeout;
|
|
25571
25701
|
let settled = false;
|
|
25572
25702
|
const settle = () => {
|
|
25573
25703
|
if (settled) return;
|
|
25574
25704
|
settled = true;
|
|
25575
25705
|
clearTimeout(timeout);
|
|
25576
|
-
|
|
25706
|
+
resolve2();
|
|
25577
25707
|
};
|
|
25578
25708
|
try {
|
|
25579
25709
|
subId = client.subscribe(walletFilter, {
|
|
@@ -25616,7 +25746,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
25616
25746
|
const events = [];
|
|
25617
25747
|
const filter = new import_nostr_js_sdk.Filter(filterObj);
|
|
25618
25748
|
let subId;
|
|
25619
|
-
return new Promise((
|
|
25749
|
+
return new Promise((resolve2) => {
|
|
25620
25750
|
let settled = false;
|
|
25621
25751
|
const timeout = setTimeout(() => {
|
|
25622
25752
|
if (settled) return;
|
|
@@ -25628,7 +25758,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
25628
25758
|
}
|
|
25629
25759
|
}
|
|
25630
25760
|
logger.warn("Nostr", `queryEvents timed out after 15s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });
|
|
25631
|
-
|
|
25761
|
+
resolve2(events);
|
|
25632
25762
|
}, 15e3);
|
|
25633
25763
|
const settle = () => {
|
|
25634
25764
|
if (settled) return;
|
|
@@ -25640,7 +25770,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
25640
25770
|
} catch {
|
|
25641
25771
|
}
|
|
25642
25772
|
}
|
|
25643
|
-
|
|
25773
|
+
resolve2(events);
|
|
25644
25774
|
};
|
|
25645
25775
|
try {
|
|
25646
25776
|
subId = client.subscribe(filter, {
|
|
@@ -25659,7 +25789,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
25659
25789
|
});
|
|
25660
25790
|
} catch {
|
|
25661
25791
|
clearTimeout(timeout);
|
|
25662
|
-
|
|
25792
|
+
resolve2(events);
|
|
25663
25793
|
return;
|
|
25664
25794
|
}
|
|
25665
25795
|
});
|
|
@@ -26099,26 +26229,6 @@ var UnicityAggregatorProvider = class {
|
|
|
26099
26229
|
var UnicityOracleProvider = UnicityAggregatorProvider;
|
|
26100
26230
|
|
|
26101
26231
|
// assets/trustbase.ts
|
|
26102
|
-
var TRUSTBASE_TESTNET = {
|
|
26103
|
-
version: 1,
|
|
26104
|
-
networkId: 3,
|
|
26105
|
-
epoch: 1,
|
|
26106
|
-
epochStartRound: 1,
|
|
26107
|
-
rootNodes: [
|
|
26108
|
-
{
|
|
26109
|
-
nodeId: "16Uiu2HAkyQRiA7pMgzgLj9GgaBJEJa8zmx9dzqUDa6WxQPJ82ghU",
|
|
26110
|
-
sigKey: "0x039afb2acb65f5fbc272d8907f763d0a5d189aadc9b97afdcc5897ea4dd112e68b",
|
|
26111
|
-
stake: 1
|
|
26112
|
-
}
|
|
26113
|
-
],
|
|
26114
|
-
quorumThreshold: 1,
|
|
26115
|
-
stateHash: "",
|
|
26116
|
-
changeRecordHash: "",
|
|
26117
|
-
previousEntryHash: "",
|
|
26118
|
-
signatures: {
|
|
26119
|
-
"16Uiu2HAkyQRiA7pMgzgLj9GgaBJEJa8zmx9dzqUDa6WxQPJ82ghU": "0xf157c9fdd8a378e3ca70d354ccc4475ab2cd8de360127bc46b0aeab4b453a80f07fd9136a5843b60a8babaff23e20acc8879861f7651440a5e2829f7541b31f100"
|
|
26120
|
-
}
|
|
26121
|
-
};
|
|
26122
26232
|
var TRUSTBASE_TESTNET2 = {
|
|
26123
26233
|
version: 1,
|
|
26124
26234
|
networkId: 4,
|
|
@@ -26157,8 +26267,44 @@ var TRUSTBASE_TESTNET2 = {
|
|
|
26157
26267
|
"16Uiu2HAmU5T1TqKyKkUB3sitWb431hVFfp9nqvaLgVagQF6ywLFF": "0x9f28f3e3c50d4bfdaf54d7290fbbe311fffb3be2281a1d211be56818fa43d06a2e9a81925e9d365e552c8b772c99285a686de1d9fb581920be7524c7a5d467a301"
|
|
26158
26268
|
}
|
|
26159
26269
|
};
|
|
26160
|
-
var TRUSTBASE_MAINNET =
|
|
26161
|
-
|
|
26270
|
+
var TRUSTBASE_MAINNET = {
|
|
26271
|
+
version: 1,
|
|
26272
|
+
networkId: 1,
|
|
26273
|
+
epoch: 1,
|
|
26274
|
+
epochStartRound: 1,
|
|
26275
|
+
rootNodes: [
|
|
26276
|
+
{
|
|
26277
|
+
nodeId: "16Uiu2HAkvk4EpXoyh7SVLTX3TcsZkjbni5jY1sBgJ7qCEWfx1Gma",
|
|
26278
|
+
sigKey: "0x03059e30b5c980ce92e2c6d4e14775838be91a6f0e993f3a4899ab459c4b5225f2",
|
|
26279
|
+
stake: 1
|
|
26280
|
+
},
|
|
26281
|
+
{
|
|
26282
|
+
nodeId: "16Uiu2HAmF9Mq9pH9yUTdao3TpJeFZY9oX9gyMqfxggVA4fNmzmXr",
|
|
26283
|
+
sigKey: "0x03a514db8b17b1c79b4a6affeb5d88a129c4d5ff0f92c8f86c311971b0beb94335",
|
|
26284
|
+
stake: 1
|
|
26285
|
+
},
|
|
26286
|
+
{
|
|
26287
|
+
nodeId: "16Uiu2HAmLAHTSMvZ2apRRtBN9piYVhaK2i7sTMaQeSWSxsLh75TG",
|
|
26288
|
+
sigKey: "0x02e5669b2daa5d1afccfe66603f7d3fe171ecbd7ed891487ab3683cdebc3c3cd8b",
|
|
26289
|
+
stake: 1
|
|
26290
|
+
},
|
|
26291
|
+
{
|
|
26292
|
+
nodeId: "16Uiu2HAmNNaWfK4RTTAq7Ap8w2yZUryrEejqmmGf44ogMaYEnkJ4",
|
|
26293
|
+
sigKey: "0x03ffee964da8ca276d4fc1cf391929e6759db106ef1d0aea998154b51e5a7fb1b7",
|
|
26294
|
+
stake: 1
|
|
26295
|
+
}
|
|
26296
|
+
],
|
|
26297
|
+
quorumThreshold: 3,
|
|
26298
|
+
stateHash: "",
|
|
26299
|
+
changeRecordHash: "",
|
|
26300
|
+
previousEntryHash: "",
|
|
26301
|
+
signatures: {
|
|
26302
|
+
"16Uiu2HAkvk4EpXoyh7SVLTX3TcsZkjbni5jY1sBgJ7qCEWfx1Gma": "0xc2f1d67dbb47fe4dcf502301d8cd5021b8903c0d3385781653d09c703d566a3a3a4e59c75792b36f929e32564ddc14c40ef728c0bde5859774d08d802de2318c00",
|
|
26303
|
+
"16Uiu2HAmF9Mq9pH9yUTdao3TpJeFZY9oX9gyMqfxggVA4fNmzmXr": "0x3e3ad3a7540daa0eded457e31d2ebcacfb354036798f49f63433d26c44857e76157e4abd36cbb1d39de8cdf6e1d33f88b250077d4347c3e5bf0f35a64f2d815300",
|
|
26304
|
+
"16Uiu2HAmLAHTSMvZ2apRRtBN9piYVhaK2i7sTMaQeSWSxsLh75TG": "0x6101bd9df8ab87439d90b52cd74727bc8bf31dd74173c8f731ec3ea501d3d86556ba7c0e84643337959f3be0e448637c301e8e0b28196e780d4f2ff930d9062100",
|
|
26305
|
+
"16Uiu2HAmNNaWfK4RTTAq7Ap8w2yZUryrEejqmmGf44ogMaYEnkJ4": "0xda13c777b832425e5cbd8d6e49eb08ddbde35c1e4a9bee8bd495c3883f039e434b64560865c3bc52b68d65780932b1f2bf8971f245d2244face822af5984945c00"
|
|
26306
|
+
}
|
|
26307
|
+
};
|
|
26162
26308
|
|
|
26163
26309
|
// impl/shared/trustbase-loader.ts
|
|
26164
26310
|
function getEmbeddedTrustBase(network) {
|
|
@@ -26171,8 +26317,6 @@ function getEmbeddedTrustBase(network) {
|
|
|
26171
26317
|
case "testnet":
|
|
26172
26318
|
case "testnet2":
|
|
26173
26319
|
return TRUSTBASE_TESTNET2;
|
|
26174
|
-
case "dev":
|
|
26175
|
-
return TRUSTBASE_DEV;
|
|
26176
26320
|
default:
|
|
26177
26321
|
throw new SphereError(`getEmbeddedTrustBase: unknown network "${network}"`, "INVALID_CONFIG");
|
|
26178
26322
|
}
|
|
@@ -26268,6 +26412,7 @@ function createWalletApiProviders(base, config) {
|
|
|
26268
26412
|
|
|
26269
26413
|
// impl/shared/network.ts
|
|
26270
26414
|
var EXPECTED_NETWORK_ID = {
|
|
26415
|
+
mainnet: 1,
|
|
26271
26416
|
// v1 cutover: 'testnet' is an alias of testnet2 (the v2 gateway network),
|
|
26272
26417
|
// so both expect the testnet2 trust base (networkId 4).
|
|
26273
26418
|
testnet: 4,
|
|
@@ -26288,7 +26433,7 @@ function assertNetworkConsistency(network) {
|
|
|
26288
26433
|
);
|
|
26289
26434
|
}
|
|
26290
26435
|
const expected = EXPECTED_NETWORK_ID[network];
|
|
26291
|
-
if (
|
|
26436
|
+
if (networkId !== expected) {
|
|
26292
26437
|
throw new SphereError(
|
|
26293
26438
|
`Network "${network}" trust base networkId ${networkId} does not match expected ${expected} (wrong trust base wired to this network).`,
|
|
26294
26439
|
"INVALID_CONFIG"
|
|
@@ -26553,419 +26698,6 @@ function createPriceProvider(config) {
|
|
|
26553
26698
|
}
|
|
26554
26699
|
}
|
|
26555
26700
|
|
|
26556
|
-
// registry/TokenRegistry.ts
|
|
26557
|
-
var FETCH_TIMEOUT_MS = 1e4;
|
|
26558
|
-
var TokenRegistry = class _TokenRegistry {
|
|
26559
|
-
static instance = null;
|
|
26560
|
-
definitionsById;
|
|
26561
|
-
definitionsBySymbol;
|
|
26562
|
-
definitionsByName;
|
|
26563
|
-
// Remote refresh state
|
|
26564
|
-
remoteUrl = null;
|
|
26565
|
-
storage = null;
|
|
26566
|
-
refreshIntervalMs = TOKEN_REGISTRY_REFRESH_INTERVAL;
|
|
26567
|
-
refreshTimer = null;
|
|
26568
|
-
lastRefreshAt = 0;
|
|
26569
|
-
refreshPromise = null;
|
|
26570
|
-
initialLoadPromise = null;
|
|
26571
|
-
constructor() {
|
|
26572
|
-
this.definitionsById = /* @__PURE__ */ new Map();
|
|
26573
|
-
this.definitionsBySymbol = /* @__PURE__ */ new Map();
|
|
26574
|
-
this.definitionsByName = /* @__PURE__ */ new Map();
|
|
26575
|
-
}
|
|
26576
|
-
/**
|
|
26577
|
-
* Get singleton instance of TokenRegistry
|
|
26578
|
-
*/
|
|
26579
|
-
static getInstance() {
|
|
26580
|
-
if (!_TokenRegistry.instance) {
|
|
26581
|
-
_TokenRegistry.instance = new _TokenRegistry();
|
|
26582
|
-
}
|
|
26583
|
-
return _TokenRegistry.instance;
|
|
26584
|
-
}
|
|
26585
|
-
/**
|
|
26586
|
-
* Configure remote registry refresh with persistent caching.
|
|
26587
|
-
*
|
|
26588
|
-
* On first call:
|
|
26589
|
-
* 1. Loads cached data from StorageProvider (if available and fresh)
|
|
26590
|
-
* 2. Starts periodic remote fetch (if autoRefresh is true, which is default)
|
|
26591
|
-
*
|
|
26592
|
-
* @param options - Configuration options
|
|
26593
|
-
* @param options.remoteUrl - Remote URL to fetch definitions from
|
|
26594
|
-
* @param options.storage - StorageProvider for persistent caching
|
|
26595
|
-
* @param options.refreshIntervalMs - Refresh interval in ms (default: 1 hour)
|
|
26596
|
-
* @param options.autoRefresh - Start auto-refresh immediately (default: true)
|
|
26597
|
-
*/
|
|
26598
|
-
static configure(options) {
|
|
26599
|
-
const instance = _TokenRegistry.getInstance();
|
|
26600
|
-
if (options.remoteUrl !== void 0) {
|
|
26601
|
-
instance.remoteUrl = options.remoteUrl;
|
|
26602
|
-
}
|
|
26603
|
-
if (options.storage !== void 0) {
|
|
26604
|
-
instance.storage = options.storage;
|
|
26605
|
-
}
|
|
26606
|
-
if (options.refreshIntervalMs !== void 0) {
|
|
26607
|
-
instance.refreshIntervalMs = options.refreshIntervalMs;
|
|
26608
|
-
}
|
|
26609
|
-
const autoRefresh = options.autoRefresh ?? true;
|
|
26610
|
-
instance.initialLoadPromise = instance.performInitialLoad(autoRefresh);
|
|
26611
|
-
}
|
|
26612
|
-
/**
|
|
26613
|
-
* Reset the singleton instance (useful for testing).
|
|
26614
|
-
* Stops auto-refresh if running.
|
|
26615
|
-
*/
|
|
26616
|
-
static resetInstance() {
|
|
26617
|
-
if (_TokenRegistry.instance) {
|
|
26618
|
-
_TokenRegistry.instance.stopAutoRefresh();
|
|
26619
|
-
}
|
|
26620
|
-
_TokenRegistry.instance = null;
|
|
26621
|
-
}
|
|
26622
|
-
/**
|
|
26623
|
-
* Destroy the singleton: stop auto-refresh and reset.
|
|
26624
|
-
*/
|
|
26625
|
-
static destroy() {
|
|
26626
|
-
_TokenRegistry.resetInstance();
|
|
26627
|
-
}
|
|
26628
|
-
/**
|
|
26629
|
-
* Wait for the initial data load (cache or remote) to complete.
|
|
26630
|
-
* Returns true if data was loaded, false if not (timeout or no data source).
|
|
26631
|
-
*
|
|
26632
|
-
* @param timeoutMs - Maximum wait time in ms (default: 10s). Set to 0 for no timeout.
|
|
26633
|
-
*/
|
|
26634
|
-
static async waitForReady(timeoutMs = 1e4) {
|
|
26635
|
-
const instance = _TokenRegistry.getInstance();
|
|
26636
|
-
if (!instance.initialLoadPromise) {
|
|
26637
|
-
return instance.definitionsById.size > 0;
|
|
26638
|
-
}
|
|
26639
|
-
if (timeoutMs <= 0) {
|
|
26640
|
-
return instance.initialLoadPromise;
|
|
26641
|
-
}
|
|
26642
|
-
return Promise.race([
|
|
26643
|
-
instance.initialLoadPromise,
|
|
26644
|
-
new Promise((resolve) => setTimeout(() => resolve(false), timeoutMs))
|
|
26645
|
-
]);
|
|
26646
|
-
}
|
|
26647
|
-
// ===========================================================================
|
|
26648
|
-
// Initial Load
|
|
26649
|
-
// ===========================================================================
|
|
26650
|
-
/**
|
|
26651
|
-
* Perform initial data load: try cache first, fall back to remote fetch.
|
|
26652
|
-
* After initial data is available, start periodic auto-refresh if configured.
|
|
26653
|
-
*/
|
|
26654
|
-
async performInitialLoad(autoRefresh) {
|
|
26655
|
-
let loaded = false;
|
|
26656
|
-
if (this.storage) {
|
|
26657
|
-
loaded = await this.loadFromCache();
|
|
26658
|
-
}
|
|
26659
|
-
if (loaded) {
|
|
26660
|
-
if (autoRefresh && this.remoteUrl) {
|
|
26661
|
-
this.startAutoRefresh();
|
|
26662
|
-
}
|
|
26663
|
-
return true;
|
|
26664
|
-
}
|
|
26665
|
-
if (autoRefresh && this.remoteUrl) {
|
|
26666
|
-
loaded = await this.refreshFromRemote();
|
|
26667
|
-
this.stopAutoRefresh();
|
|
26668
|
-
this.refreshTimer = setInterval(() => {
|
|
26669
|
-
this.refreshFromRemote();
|
|
26670
|
-
}, this.refreshIntervalMs);
|
|
26671
|
-
return loaded;
|
|
26672
|
-
}
|
|
26673
|
-
return false;
|
|
26674
|
-
}
|
|
26675
|
-
// ===========================================================================
|
|
26676
|
-
// Cache (StorageProvider)
|
|
26677
|
-
// ===========================================================================
|
|
26678
|
-
/**
|
|
26679
|
-
* Per-network cache keys. The registry cache MUST be namespaced by remoteUrl:
|
|
26680
|
-
* testnet and testnet2 ship DIFFERENT coinIds for the same symbol, so a single
|
|
26681
|
-
* global key let a stale testnet snapshot be served for a testnet2 request —
|
|
26682
|
-
* wrong/missing icons AND self-mint resolving the wrong-network coinId. Keying
|
|
26683
|
-
* by remoteUrl isolates the snapshots so a network can never read another's.
|
|
26684
|
-
*/
|
|
26685
|
-
cacheKeys() {
|
|
26686
|
-
const base = STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE;
|
|
26687
|
-
const baseTs = STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS;
|
|
26688
|
-
if (!this.remoteUrl) return { data: base, ts: baseTs };
|
|
26689
|
-
return { data: `${base}:${this.remoteUrl}`, ts: `${baseTs}:${this.remoteUrl}` };
|
|
26690
|
-
}
|
|
26691
|
-
/**
|
|
26692
|
-
* Load definitions from StorageProvider cache.
|
|
26693
|
-
* Only applies if cache exists and is fresh (within refreshIntervalMs).
|
|
26694
|
-
*/
|
|
26695
|
-
async loadFromCache() {
|
|
26696
|
-
if (!this.storage) return false;
|
|
26697
|
-
try {
|
|
26698
|
-
const { data: dataKey, ts: tsKey } = this.cacheKeys();
|
|
26699
|
-
const [cached, cachedTs] = await Promise.all([
|
|
26700
|
-
this.storage.get(dataKey),
|
|
26701
|
-
this.storage.get(tsKey)
|
|
26702
|
-
]);
|
|
26703
|
-
if (!cached || !cachedTs) return false;
|
|
26704
|
-
const ts = parseInt(cachedTs, 10);
|
|
26705
|
-
if (isNaN(ts)) return false;
|
|
26706
|
-
const age = Date.now() - ts;
|
|
26707
|
-
if (age > this.refreshIntervalMs) return false;
|
|
26708
|
-
if (this.lastRefreshAt > ts) return false;
|
|
26709
|
-
const data = JSON.parse(cached);
|
|
26710
|
-
if (!this.isValidDefinitionsArray(data)) return false;
|
|
26711
|
-
this.applyDefinitions(data);
|
|
26712
|
-
this.lastRefreshAt = ts;
|
|
26713
|
-
return true;
|
|
26714
|
-
} catch {
|
|
26715
|
-
return false;
|
|
26716
|
-
}
|
|
26717
|
-
}
|
|
26718
|
-
/**
|
|
26719
|
-
* Save definitions to StorageProvider cache.
|
|
26720
|
-
*/
|
|
26721
|
-
async saveToCache(definitions) {
|
|
26722
|
-
if (!this.storage) return;
|
|
26723
|
-
try {
|
|
26724
|
-
const { data: dataKey, ts: tsKey } = this.cacheKeys();
|
|
26725
|
-
await Promise.all([
|
|
26726
|
-
this.storage.set(dataKey, JSON.stringify(definitions)),
|
|
26727
|
-
this.storage.set(tsKey, String(Date.now()))
|
|
26728
|
-
]);
|
|
26729
|
-
} catch {
|
|
26730
|
-
}
|
|
26731
|
-
}
|
|
26732
|
-
// ===========================================================================
|
|
26733
|
-
// Remote Refresh
|
|
26734
|
-
// ===========================================================================
|
|
26735
|
-
/**
|
|
26736
|
-
* Apply an array of token definitions to the internal maps.
|
|
26737
|
-
* Clears existing data before applying.
|
|
26738
|
-
*/
|
|
26739
|
-
applyDefinitions(definitions) {
|
|
26740
|
-
this.definitionsById.clear();
|
|
26741
|
-
this.definitionsBySymbol.clear();
|
|
26742
|
-
this.definitionsByName.clear();
|
|
26743
|
-
for (const def of definitions) {
|
|
26744
|
-
const idLower = def.id.toLowerCase();
|
|
26745
|
-
this.definitionsById.set(idLower, def);
|
|
26746
|
-
if (def.symbol) {
|
|
26747
|
-
this.definitionsBySymbol.set(def.symbol.toUpperCase(), def);
|
|
26748
|
-
}
|
|
26749
|
-
this.definitionsByName.set(def.name.toLowerCase(), def);
|
|
26750
|
-
}
|
|
26751
|
-
}
|
|
26752
|
-
/**
|
|
26753
|
-
* Validate that data is an array of objects with 'id' field
|
|
26754
|
-
*/
|
|
26755
|
-
isValidDefinitionsArray(data) {
|
|
26756
|
-
return Array.isArray(data) && data.every((item) => item && typeof item === "object" && "id" in item);
|
|
26757
|
-
}
|
|
26758
|
-
/**
|
|
26759
|
-
* Fetch token definitions from the remote URL and update the registry.
|
|
26760
|
-
* On success, also persists to StorageProvider cache.
|
|
26761
|
-
* Returns true on success, false on failure. On failure, existing data is preserved.
|
|
26762
|
-
* Concurrent calls are deduplicated — only one fetch runs at a time.
|
|
26763
|
-
*/
|
|
26764
|
-
async refreshFromRemote() {
|
|
26765
|
-
if (!this.remoteUrl) {
|
|
26766
|
-
return false;
|
|
26767
|
-
}
|
|
26768
|
-
if (this.refreshPromise) {
|
|
26769
|
-
return this.refreshPromise;
|
|
26770
|
-
}
|
|
26771
|
-
this.refreshPromise = this.doRefresh();
|
|
26772
|
-
try {
|
|
26773
|
-
return await this.refreshPromise;
|
|
26774
|
-
} finally {
|
|
26775
|
-
this.refreshPromise = null;
|
|
26776
|
-
}
|
|
26777
|
-
}
|
|
26778
|
-
async doRefresh() {
|
|
26779
|
-
try {
|
|
26780
|
-
const controller = new AbortController();
|
|
26781
|
-
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
26782
|
-
let response;
|
|
26783
|
-
try {
|
|
26784
|
-
response = await fetch(this.remoteUrl, {
|
|
26785
|
-
headers: { Accept: "application/json" },
|
|
26786
|
-
signal: controller.signal
|
|
26787
|
-
});
|
|
26788
|
-
} finally {
|
|
26789
|
-
clearTimeout(timer);
|
|
26790
|
-
}
|
|
26791
|
-
if (!response.ok) {
|
|
26792
|
-
logger.warn("TokenRegistry", `Remote fetch failed: HTTP ${response.status} ${response.statusText}`);
|
|
26793
|
-
return false;
|
|
26794
|
-
}
|
|
26795
|
-
const data = await response.json();
|
|
26796
|
-
if (!this.isValidDefinitionsArray(data)) {
|
|
26797
|
-
logger.warn("TokenRegistry", "Remote data is not a valid token definitions array");
|
|
26798
|
-
return false;
|
|
26799
|
-
}
|
|
26800
|
-
const definitions = data;
|
|
26801
|
-
this.applyDefinitions(definitions);
|
|
26802
|
-
this.lastRefreshAt = Date.now();
|
|
26803
|
-
this.saveToCache(definitions);
|
|
26804
|
-
return true;
|
|
26805
|
-
} catch (error) {
|
|
26806
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
26807
|
-
logger.warn("TokenRegistry", `Remote refresh failed: ${message}`);
|
|
26808
|
-
return false;
|
|
26809
|
-
}
|
|
26810
|
-
}
|
|
26811
|
-
/**
|
|
26812
|
-
* Start periodic auto-refresh from the remote URL.
|
|
26813
|
-
* Does an immediate fetch, then repeats at the configured interval.
|
|
26814
|
-
*/
|
|
26815
|
-
startAutoRefresh(intervalMs) {
|
|
26816
|
-
this.stopAutoRefresh();
|
|
26817
|
-
if (intervalMs !== void 0) {
|
|
26818
|
-
this.refreshIntervalMs = intervalMs;
|
|
26819
|
-
}
|
|
26820
|
-
this.refreshFromRemote();
|
|
26821
|
-
this.refreshTimer = setInterval(() => {
|
|
26822
|
-
this.refreshFromRemote();
|
|
26823
|
-
}, this.refreshIntervalMs);
|
|
26824
|
-
}
|
|
26825
|
-
/**
|
|
26826
|
-
* Stop periodic auto-refresh
|
|
26827
|
-
*/
|
|
26828
|
-
stopAutoRefresh() {
|
|
26829
|
-
if (this.refreshTimer !== null) {
|
|
26830
|
-
clearInterval(this.refreshTimer);
|
|
26831
|
-
this.refreshTimer = null;
|
|
26832
|
-
}
|
|
26833
|
-
}
|
|
26834
|
-
/**
|
|
26835
|
-
* Timestamp of the last successful remote refresh (0 if never refreshed)
|
|
26836
|
-
*/
|
|
26837
|
-
getLastRefreshAt() {
|
|
26838
|
-
return this.lastRefreshAt;
|
|
26839
|
-
}
|
|
26840
|
-
// ===========================================================================
|
|
26841
|
-
// Lookup Methods
|
|
26842
|
-
// ===========================================================================
|
|
26843
|
-
/**
|
|
26844
|
-
* Get token definition by hex coin ID
|
|
26845
|
-
* @param coinId - 64-character hex string
|
|
26846
|
-
* @returns Token definition or undefined if not found
|
|
26847
|
-
*/
|
|
26848
|
-
getDefinition(coinId) {
|
|
26849
|
-
if (!coinId) return void 0;
|
|
26850
|
-
return this.definitionsById.get(coinId.toLowerCase());
|
|
26851
|
-
}
|
|
26852
|
-
/**
|
|
26853
|
-
* Get token definition by symbol (e.g., "UCT", "BTC")
|
|
26854
|
-
* @param symbol - Token symbol (case-insensitive)
|
|
26855
|
-
* @returns Token definition or undefined if not found
|
|
26856
|
-
*/
|
|
26857
|
-
getDefinitionBySymbol(symbol) {
|
|
26858
|
-
if (!symbol) return void 0;
|
|
26859
|
-
return this.definitionsBySymbol.get(symbol.toUpperCase());
|
|
26860
|
-
}
|
|
26861
|
-
/**
|
|
26862
|
-
* Get token definition by name (e.g., "bitcoin", "ethereum")
|
|
26863
|
-
* @param name - Token name (case-insensitive)
|
|
26864
|
-
* @returns Token definition or undefined if not found
|
|
26865
|
-
*/
|
|
26866
|
-
getDefinitionByName(name) {
|
|
26867
|
-
if (!name) return void 0;
|
|
26868
|
-
return this.definitionsByName.get(name.toLowerCase());
|
|
26869
|
-
}
|
|
26870
|
-
/**
|
|
26871
|
-
* Get token symbol for a coin ID
|
|
26872
|
-
* @param coinId - 64-character hex string
|
|
26873
|
-
* @returns Symbol (e.g., "UCT") or truncated ID if not found
|
|
26874
|
-
*/
|
|
26875
|
-
getSymbol(coinId) {
|
|
26876
|
-
const def = this.getDefinition(coinId);
|
|
26877
|
-
if (def?.symbol) {
|
|
26878
|
-
return def.symbol;
|
|
26879
|
-
}
|
|
26880
|
-
return coinId.slice(0, 6).toUpperCase();
|
|
26881
|
-
}
|
|
26882
|
-
/**
|
|
26883
|
-
* Get token name for a coin ID
|
|
26884
|
-
* @param coinId - 64-character hex string
|
|
26885
|
-
* @returns Name (e.g., "Bitcoin") or coin ID if not found
|
|
26886
|
-
*/
|
|
26887
|
-
getName(coinId) {
|
|
26888
|
-
const def = this.getDefinition(coinId);
|
|
26889
|
-
if (def?.name) {
|
|
26890
|
-
return def.name.charAt(0).toUpperCase() + def.name.slice(1);
|
|
26891
|
-
}
|
|
26892
|
-
return coinId;
|
|
26893
|
-
}
|
|
26894
|
-
/**
|
|
26895
|
-
* Get decimal places for a coin ID
|
|
26896
|
-
* @param coinId - 64-character hex string
|
|
26897
|
-
* @returns Decimals or 0 if not found
|
|
26898
|
-
*/
|
|
26899
|
-
getDecimals(coinId) {
|
|
26900
|
-
const def = this.getDefinition(coinId);
|
|
26901
|
-
return def?.decimals ?? 0;
|
|
26902
|
-
}
|
|
26903
|
-
/**
|
|
26904
|
-
* Get icon URL for a coin ID
|
|
26905
|
-
* @param coinId - 64-character hex string
|
|
26906
|
-
* @param preferPng - Prefer PNG format over SVG
|
|
26907
|
-
* @returns Icon URL or null if not found
|
|
26908
|
-
*/
|
|
26909
|
-
getIconUrl(coinId, preferPng = true) {
|
|
26910
|
-
const def = this.getDefinition(coinId);
|
|
26911
|
-
if (!def?.icons || def.icons.length === 0) {
|
|
26912
|
-
return null;
|
|
26913
|
-
}
|
|
26914
|
-
if (preferPng) {
|
|
26915
|
-
const pngIcon = def.icons.find((i) => i.url.toLowerCase().includes(".png"));
|
|
26916
|
-
if (pngIcon) return pngIcon.url;
|
|
26917
|
-
}
|
|
26918
|
-
return def.icons[0].url;
|
|
26919
|
-
}
|
|
26920
|
-
/**
|
|
26921
|
-
* Check if a coin ID is known in the registry
|
|
26922
|
-
* @param coinId - 64-character hex string
|
|
26923
|
-
* @returns true if the coin is in the registry
|
|
26924
|
-
*/
|
|
26925
|
-
isKnown(coinId) {
|
|
26926
|
-
return this.definitionsById.has(coinId.toLowerCase());
|
|
26927
|
-
}
|
|
26928
|
-
/**
|
|
26929
|
-
* Get all token definitions
|
|
26930
|
-
* @returns Array of all token definitions
|
|
26931
|
-
*/
|
|
26932
|
-
getAllDefinitions() {
|
|
26933
|
-
return Array.from(this.definitionsById.values());
|
|
26934
|
-
}
|
|
26935
|
-
/**
|
|
26936
|
-
* Get all fungible token definitions
|
|
26937
|
-
* @returns Array of fungible token definitions
|
|
26938
|
-
*/
|
|
26939
|
-
getFungibleTokens() {
|
|
26940
|
-
return this.getAllDefinitions().filter((def) => def.assetKind === "fungible");
|
|
26941
|
-
}
|
|
26942
|
-
/**
|
|
26943
|
-
* Get all non-fungible token definitions
|
|
26944
|
-
* @returns Array of non-fungible token definitions
|
|
26945
|
-
*/
|
|
26946
|
-
getNonFungibleTokens() {
|
|
26947
|
-
return this.getAllDefinitions().filter((def) => def.assetKind === "non-fungible");
|
|
26948
|
-
}
|
|
26949
|
-
/**
|
|
26950
|
-
* Get coin ID by symbol
|
|
26951
|
-
* @param symbol - Token symbol (e.g., "UCT")
|
|
26952
|
-
* @returns Coin ID hex string or undefined if not found
|
|
26953
|
-
*/
|
|
26954
|
-
getCoinIdBySymbol(symbol) {
|
|
26955
|
-
const def = this.getDefinitionBySymbol(symbol);
|
|
26956
|
-
return def?.id;
|
|
26957
|
-
}
|
|
26958
|
-
/**
|
|
26959
|
-
* Get coin ID by name
|
|
26960
|
-
* @param name - Token name (e.g., "bitcoin")
|
|
26961
|
-
* @returns Coin ID hex string or undefined if not found
|
|
26962
|
-
*/
|
|
26963
|
-
getCoinIdByName(name) {
|
|
26964
|
-
const def = this.getDefinitionByName(name);
|
|
26965
|
-
return def?.id;
|
|
26966
|
-
}
|
|
26967
|
-
};
|
|
26968
|
-
|
|
26969
26701
|
// impl/shared/resolvers.ts
|
|
26970
26702
|
function getNetworkConfig(network = "mainnet") {
|
|
26971
26703
|
return NETWORKS[network];
|
|
@@ -27057,8 +26789,7 @@ function createNodeProviders(config) {
|
|
|
27057
26789
|
}
|
|
27058
26790
|
const network = config.network;
|
|
27059
26791
|
assertNetworkConsistency(network);
|
|
27060
|
-
|
|
27061
|
-
logger.configure({ debug: globalDebug });
|
|
26792
|
+
if (config?.debug !== void 0) logger.configure({ debug: config.debug });
|
|
27062
26793
|
if (config?.transport?.debug) logger.setTagDebug("Nostr", true);
|
|
27063
26794
|
if (config?.oracle?.debug) logger.setTagDebug("Aggregator", true);
|
|
27064
26795
|
if (config?.price?.debug) logger.setTagDebug("Price", true);
|
|
@@ -27072,8 +26803,6 @@ function createNodeProviders(config) {
|
|
|
27072
26803
|
const priceConfig = resolvePriceConfig(config?.price, storage);
|
|
27073
26804
|
const groupChat = resolveGroupChatConfig(network, config?.groupChat);
|
|
27074
26805
|
const market = resolveMarketConfig(config?.market);
|
|
27075
|
-
const networkConfig = getNetworkConfig(network);
|
|
27076
|
-
TokenRegistry.configure({ remoteUrl: networkConfig.tokenRegistryUrl, storage });
|
|
27077
26806
|
return {
|
|
27078
26807
|
storage,
|
|
27079
26808
|
groupChat,
|