@unicitylabs/sphere-sdk 0.15.0 → 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 +14 -23
- 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 +14 -23
- 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 +14 -23
- package/dist/impl/browser/connect/index.cjs.map +1 -1
- package/dist/impl/browser/connect/index.js +14 -23
- 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 +13 -22
- package/dist/impl/nodejs/connect/index.cjs.map +1 -1
- package/dist/impl/nodejs/connect/index.js +13 -22
- 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 +1 -1
|
@@ -2610,9 +2610,9 @@ var require_dispatcher_base = __commonJS({
|
|
|
2610
2610
|
}
|
|
2611
2611
|
close(callback) {
|
|
2612
2612
|
if (callback === void 0) {
|
|
2613
|
-
return new Promise((
|
|
2613
|
+
return new Promise((resolve2, reject) => {
|
|
2614
2614
|
this.close((err, data) => {
|
|
2615
|
-
return err ? reject(err) :
|
|
2615
|
+
return err ? reject(err) : resolve2(data);
|
|
2616
2616
|
});
|
|
2617
2617
|
});
|
|
2618
2618
|
}
|
|
@@ -2650,9 +2650,9 @@ var require_dispatcher_base = __commonJS({
|
|
|
2650
2650
|
err = null;
|
|
2651
2651
|
}
|
|
2652
2652
|
if (callback === void 0) {
|
|
2653
|
-
return new Promise((
|
|
2653
|
+
return new Promise((resolve2, reject) => {
|
|
2654
2654
|
this.destroy(err, (err2, data) => {
|
|
2655
|
-
return err2 ? reject(err2) :
|
|
2655
|
+
return err2 ? reject(err2) : resolve2(data);
|
|
2656
2656
|
});
|
|
2657
2657
|
});
|
|
2658
2658
|
}
|
|
@@ -6106,8 +6106,8 @@ var require_promise = __commonJS({
|
|
|
6106
6106
|
function createDeferredPromise() {
|
|
6107
6107
|
let res;
|
|
6108
6108
|
let rej;
|
|
6109
|
-
const promise = new Promise((
|
|
6110
|
-
res =
|
|
6109
|
+
const promise = new Promise((resolve2, reject) => {
|
|
6110
|
+
res = resolve2;
|
|
6111
6111
|
rej = reject;
|
|
6112
6112
|
});
|
|
6113
6113
|
return { promise, resolve: res, reject: rej };
|
|
@@ -7414,12 +7414,12 @@ upgrade: ${upgrade}\r
|
|
|
7414
7414
|
cb();
|
|
7415
7415
|
}
|
|
7416
7416
|
}
|
|
7417
|
-
const waitForDrain = () => new Promise((
|
|
7417
|
+
const waitForDrain = () => new Promise((resolve2, reject) => {
|
|
7418
7418
|
assert(callback === null);
|
|
7419
7419
|
if (socket[kError]) {
|
|
7420
7420
|
reject(socket[kError]);
|
|
7421
7421
|
} else {
|
|
7422
|
-
callback =
|
|
7422
|
+
callback = resolve2;
|
|
7423
7423
|
}
|
|
7424
7424
|
});
|
|
7425
7425
|
socket.on("close", onDrain).on("drain", onDrain);
|
|
@@ -8231,12 +8231,12 @@ var require_client_h2 = __commonJS({
|
|
|
8231
8231
|
cb();
|
|
8232
8232
|
}
|
|
8233
8233
|
}
|
|
8234
|
-
const waitForDrain = () => new Promise((
|
|
8234
|
+
const waitForDrain = () => new Promise((resolve2, reject) => {
|
|
8235
8235
|
assert(callback === null);
|
|
8236
8236
|
if (socket[kError]) {
|
|
8237
8237
|
reject(socket[kError]);
|
|
8238
8238
|
} else {
|
|
8239
|
-
callback =
|
|
8239
|
+
callback = resolve2;
|
|
8240
8240
|
}
|
|
8241
8241
|
});
|
|
8242
8242
|
h2stream.on("close", onDrain).on("drain", onDrain);
|
|
@@ -8538,16 +8538,16 @@ var require_client = __commonJS({
|
|
|
8538
8538
|
return this[kNeedDrain] < 2;
|
|
8539
8539
|
}
|
|
8540
8540
|
[kClose]() {
|
|
8541
|
-
return new Promise((
|
|
8541
|
+
return new Promise((resolve2) => {
|
|
8542
8542
|
if (this[kSize]) {
|
|
8543
|
-
this[kClosedResolve] =
|
|
8543
|
+
this[kClosedResolve] = resolve2;
|
|
8544
8544
|
} else {
|
|
8545
|
-
|
|
8545
|
+
resolve2(null);
|
|
8546
8546
|
}
|
|
8547
8547
|
});
|
|
8548
8548
|
}
|
|
8549
8549
|
[kDestroy](err) {
|
|
8550
|
-
return new Promise((
|
|
8550
|
+
return new Promise((resolve2) => {
|
|
8551
8551
|
const requests = this[kQueue].splice(this[kPendingIdx]);
|
|
8552
8552
|
for (let i = 0; i < requests.length; i++) {
|
|
8553
8553
|
const request = requests[i];
|
|
@@ -8558,7 +8558,7 @@ var require_client = __commonJS({
|
|
|
8558
8558
|
this[kClosedResolve]();
|
|
8559
8559
|
this[kClosedResolve] = null;
|
|
8560
8560
|
}
|
|
8561
|
-
|
|
8561
|
+
resolve2(null);
|
|
8562
8562
|
};
|
|
8563
8563
|
if (this[kHTTPContext]) {
|
|
8564
8564
|
this[kHTTPContext].destroy(err, callback);
|
|
@@ -8949,8 +8949,8 @@ var require_pool_base = __commonJS({
|
|
|
8949
8949
|
}
|
|
8950
8950
|
return Promise.all(closeAll);
|
|
8951
8951
|
} else {
|
|
8952
|
-
return new Promise((
|
|
8953
|
-
this[kClosedResolve] =
|
|
8952
|
+
return new Promise((resolve2) => {
|
|
8953
|
+
this[kClosedResolve] = resolve2;
|
|
8954
8954
|
});
|
|
8955
8955
|
}
|
|
8956
8956
|
}
|
|
@@ -10479,7 +10479,7 @@ var require_readable = __commonJS({
|
|
|
10479
10479
|
if (this._readableState.closeEmitted) {
|
|
10480
10480
|
return Promise.resolve(null);
|
|
10481
10481
|
}
|
|
10482
|
-
return new Promise((
|
|
10482
|
+
return new Promise((resolve2, reject) => {
|
|
10483
10483
|
if (this[kContentLength] && this[kContentLength] > limit || this[kBytesRead] > limit) {
|
|
10484
10484
|
this.destroy(new AbortError());
|
|
10485
10485
|
}
|
|
@@ -10493,11 +10493,11 @@ var require_readable = __commonJS({
|
|
|
10493
10493
|
if (signal.aborted) {
|
|
10494
10494
|
reject(signal.reason ?? new AbortError());
|
|
10495
10495
|
} else {
|
|
10496
|
-
|
|
10496
|
+
resolve2(null);
|
|
10497
10497
|
}
|
|
10498
10498
|
});
|
|
10499
10499
|
} else {
|
|
10500
|
-
this.on("close",
|
|
10500
|
+
this.on("close", resolve2);
|
|
10501
10501
|
}
|
|
10502
10502
|
this.on("error", noop).on("data", () => {
|
|
10503
10503
|
if (this[kBytesRead] > limit) {
|
|
@@ -10525,7 +10525,7 @@ var require_readable = __commonJS({
|
|
|
10525
10525
|
}
|
|
10526
10526
|
function consume(stream, type) {
|
|
10527
10527
|
assert(!stream[kConsume]);
|
|
10528
|
-
return new Promise((
|
|
10528
|
+
return new Promise((resolve2, reject) => {
|
|
10529
10529
|
if (isUnusable(stream)) {
|
|
10530
10530
|
const rState = stream._readableState;
|
|
10531
10531
|
if (rState.destroyed && rState.closeEmitted === false) {
|
|
@@ -10540,7 +10540,7 @@ var require_readable = __commonJS({
|
|
|
10540
10540
|
stream[kConsume] = {
|
|
10541
10541
|
type,
|
|
10542
10542
|
stream,
|
|
10543
|
-
resolve,
|
|
10543
|
+
resolve: resolve2,
|
|
10544
10544
|
reject,
|
|
10545
10545
|
length: 0,
|
|
10546
10546
|
body: []
|
|
@@ -10614,18 +10614,18 @@ var require_readable = __commonJS({
|
|
|
10614
10614
|
return buffer;
|
|
10615
10615
|
}
|
|
10616
10616
|
function consumeEnd(consume2, encoding) {
|
|
10617
|
-
const { type, body, resolve, stream, length } = consume2;
|
|
10617
|
+
const { type, body, resolve: resolve2, stream, length } = consume2;
|
|
10618
10618
|
try {
|
|
10619
10619
|
if (type === "text") {
|
|
10620
|
-
|
|
10620
|
+
resolve2(chunksDecode(body, length, encoding));
|
|
10621
10621
|
} else if (type === "json") {
|
|
10622
|
-
|
|
10622
|
+
resolve2(JSON.parse(chunksDecode(body, length, encoding)));
|
|
10623
10623
|
} else if (type === "arrayBuffer") {
|
|
10624
|
-
|
|
10624
|
+
resolve2(chunksConcat(body, length).buffer);
|
|
10625
10625
|
} else if (type === "blob") {
|
|
10626
|
-
|
|
10626
|
+
resolve2(new Blob(body, { type: stream[kContentType] }));
|
|
10627
10627
|
} else if (type === "bytes") {
|
|
10628
|
-
|
|
10628
|
+
resolve2(chunksConcat(body, length));
|
|
10629
10629
|
}
|
|
10630
10630
|
consumeFinish(consume2);
|
|
10631
10631
|
} catch (err) {
|
|
@@ -10814,9 +10814,9 @@ var require_api_request = __commonJS({
|
|
|
10814
10814
|
};
|
|
10815
10815
|
function request(opts, callback) {
|
|
10816
10816
|
if (callback === void 0) {
|
|
10817
|
-
return new Promise((
|
|
10817
|
+
return new Promise((resolve2, reject) => {
|
|
10818
10818
|
request.call(this, opts, (err, data) => {
|
|
10819
|
-
return err ? reject(err) :
|
|
10819
|
+
return err ? reject(err) : resolve2(data);
|
|
10820
10820
|
});
|
|
10821
10821
|
});
|
|
10822
10822
|
}
|
|
@@ -11028,9 +11028,9 @@ var require_api_stream = __commonJS({
|
|
|
11028
11028
|
};
|
|
11029
11029
|
function stream(opts, factory, callback) {
|
|
11030
11030
|
if (callback === void 0) {
|
|
11031
|
-
return new Promise((
|
|
11031
|
+
return new Promise((resolve2, reject) => {
|
|
11032
11032
|
stream.call(this, opts, factory, (err, data) => {
|
|
11033
|
-
return err ? reject(err) :
|
|
11033
|
+
return err ? reject(err) : resolve2(data);
|
|
11034
11034
|
});
|
|
11035
11035
|
});
|
|
11036
11036
|
}
|
|
@@ -11318,9 +11318,9 @@ var require_api_upgrade = __commonJS({
|
|
|
11318
11318
|
};
|
|
11319
11319
|
function upgrade(opts, callback) {
|
|
11320
11320
|
if (callback === void 0) {
|
|
11321
|
-
return new Promise((
|
|
11321
|
+
return new Promise((resolve2, reject) => {
|
|
11322
11322
|
upgrade.call(this, opts, (err, data) => {
|
|
11323
|
-
return err ? reject(err) :
|
|
11323
|
+
return err ? reject(err) : resolve2(data);
|
|
11324
11324
|
});
|
|
11325
11325
|
});
|
|
11326
11326
|
}
|
|
@@ -11413,9 +11413,9 @@ var require_api_connect = __commonJS({
|
|
|
11413
11413
|
};
|
|
11414
11414
|
function connect(opts, callback) {
|
|
11415
11415
|
if (callback === void 0) {
|
|
11416
|
-
return new Promise((
|
|
11416
|
+
return new Promise((resolve2, reject) => {
|
|
11417
11417
|
connect.call(this, opts, (err, data) => {
|
|
11418
|
-
return err ? reject(err) :
|
|
11418
|
+
return err ? reject(err) : resolve2(data);
|
|
11419
11419
|
});
|
|
11420
11420
|
});
|
|
11421
11421
|
}
|
|
@@ -12663,7 +12663,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
12663
12663
|
"node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
|
|
12664
12664
|
"use strict";
|
|
12665
12665
|
var { writeFile, readFile, mkdir } = __require("fs/promises");
|
|
12666
|
-
var { dirname, resolve } = __require("path");
|
|
12666
|
+
var { dirname, resolve: resolve2 } = __require("path");
|
|
12667
12667
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("timers");
|
|
12668
12668
|
var { InvalidArgumentError, UndiciError } = require_errors();
|
|
12669
12669
|
var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
|
|
@@ -12864,7 +12864,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
12864
12864
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
12865
12865
|
}
|
|
12866
12866
|
try {
|
|
12867
|
-
const data = await readFile(
|
|
12867
|
+
const data = await readFile(resolve2(path2), "utf8");
|
|
12868
12868
|
const parsed = JSON.parse(data);
|
|
12869
12869
|
if (Array.isArray(parsed)) {
|
|
12870
12870
|
this.#snapshots.clear();
|
|
@@ -12893,7 +12893,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
12893
12893
|
if (!path2) {
|
|
12894
12894
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
12895
12895
|
}
|
|
12896
|
-
const resolvedPath =
|
|
12896
|
+
const resolvedPath = resolve2(path2);
|
|
12897
12897
|
await mkdir(dirname(resolvedPath), { recursive: true });
|
|
12898
12898
|
const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
|
|
12899
12899
|
hash,
|
|
@@ -19450,7 +19450,7 @@ var require_fetch = __commonJS({
|
|
|
19450
19450
|
function dispatch({ body }) {
|
|
19451
19451
|
const url = requestCurrentURL(request);
|
|
19452
19452
|
const agent = fetchParams.controller.dispatcher;
|
|
19453
|
-
return new Promise((
|
|
19453
|
+
return new Promise((resolve2, reject) => agent.dispatch(
|
|
19454
19454
|
{
|
|
19455
19455
|
path: url.pathname + url.search,
|
|
19456
19456
|
origin: url.origin,
|
|
@@ -19530,7 +19530,7 @@ var require_fetch = __commonJS({
|
|
|
19530
19530
|
}
|
|
19531
19531
|
}
|
|
19532
19532
|
const onError = this.onError.bind(this);
|
|
19533
|
-
|
|
19533
|
+
resolve2({
|
|
19534
19534
|
status,
|
|
19535
19535
|
statusText,
|
|
19536
19536
|
headersList,
|
|
@@ -19573,7 +19573,7 @@ var require_fetch = __commonJS({
|
|
|
19573
19573
|
for (let i = 0; i < rawHeaders.length; i += 2) {
|
|
19574
19574
|
headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString("latin1"), true);
|
|
19575
19575
|
}
|
|
19576
|
-
|
|
19576
|
+
resolve2({
|
|
19577
19577
|
status,
|
|
19578
19578
|
statusText: STATUS_CODES[status],
|
|
19579
19579
|
headersList,
|
|
@@ -23648,7 +23648,6 @@ function isNetworkScopedAddressKey(key) {
|
|
|
23648
23648
|
return false;
|
|
23649
23649
|
}
|
|
23650
23650
|
var DEFAULT_NOSTR_RELAYS = [
|
|
23651
|
-
"wss://relay.unicity.network",
|
|
23652
23651
|
"wss://relay.damus.io",
|
|
23653
23652
|
"wss://nos.lol",
|
|
23654
23653
|
"wss://relay.nostr.band"
|
|
@@ -23661,13 +23660,9 @@ var NOSTR_EVENT_KINDS = {
|
|
|
23661
23660
|
/** Public broadcast */
|
|
23662
23661
|
BROADCAST: 1
|
|
23663
23662
|
};
|
|
23664
|
-
var DEFAULT_AGGREGATOR_URL = "https://aggregator.unicity.network/rpc";
|
|
23665
|
-
var DEV_AGGREGATOR_URL = "https://dev-aggregator.dyndns.org/rpc";
|
|
23666
23663
|
var DEFAULT_AGGREGATOR_TIMEOUT = 3e4;
|
|
23667
23664
|
var DEFAULT_BASE_PATH = "m/44'/0'/0'";
|
|
23668
23665
|
var DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0`;
|
|
23669
|
-
var TOKEN_REGISTRY_URL = "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
|
|
23670
|
-
var TOKEN_REGISTRY_REFRESH_INTERVAL = 36e5;
|
|
23671
23666
|
var TEST_NOSTR_RELAYS = [
|
|
23672
23667
|
"wss://nostr-relay.testnet.unicity.network"
|
|
23673
23668
|
];
|
|
@@ -23677,10 +23672,19 @@ var DEFAULT_GROUP_RELAYS = [
|
|
|
23677
23672
|
var NETWORKS = {
|
|
23678
23673
|
mainnet: {
|
|
23679
23674
|
name: "Mainnet",
|
|
23680
|
-
|
|
23681
|
-
|
|
23675
|
+
networkId: 1,
|
|
23676
|
+
// v3 state-transition gateway (networkId 1 comes from the trust base). apiKey is env-injected;
|
|
23677
|
+
// unlike testnet2's, a mainnet gateway key is a SECRET — never commit one.
|
|
23678
|
+
aggregatorUrl: "https://gateway.mainnet.unicity.network",
|
|
23679
|
+
// Mainnet has no relay of its own yet — it shares the testnet relay until one is stood up.
|
|
23680
|
+
// Consequence while shared: nametag bindings for both networks live in ONE namespace, and
|
|
23681
|
+
// since bindings carry no network the cross-network recipient guard can only signal (#734).
|
|
23682
|
+
nostrRelays: TEST_NOSTR_RELAYS,
|
|
23682
23683
|
groupRelays: DEFAULT_GROUP_RELAYS,
|
|
23683
|
-
|
|
23684
|
+
// Published, but currently only the non-fungible base token type — no fungible
|
|
23685
|
+
// coins yet, so those still miss the registry (decimals 0, symbol falls back to
|
|
23686
|
+
// six hex chars). Presentation only: the money path treats coinId as opaque bytes.
|
|
23687
|
+
tokenRegistryUrl: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.mainnet.json"
|
|
23684
23688
|
},
|
|
23685
23689
|
// v1 cutover: 'testnet' now POINTS AT TESTNET2 (the v2 gateway network). The
|
|
23686
23690
|
// old goggregator testnet spoke the removed v1 protocol — a v2 engine cannot
|
|
@@ -23704,19 +23708,10 @@ var NETWORKS = {
|
|
|
23704
23708
|
// reuse testnet infra (shared relays/ipfs)
|
|
23705
23709
|
groupRelays: DEFAULT_GROUP_RELAYS,
|
|
23706
23710
|
tokenRegistryUrl: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet2.json"
|
|
23707
|
-
},
|
|
23708
|
-
// NOTE: mainnet/dev still point at v1-era aggregators. The v2 engine cannot
|
|
23709
|
-
// operate against them until their gateways are cut over to the v2 protocol —
|
|
23710
|
-
// wallet operations on these networks fail loudly (AGGREGATOR_ERROR) until then.
|
|
23711
|
-
dev: {
|
|
23712
|
-
name: "Development",
|
|
23713
|
-
aggregatorUrl: DEV_AGGREGATOR_URL,
|
|
23714
|
-
nostrRelays: TEST_NOSTR_RELAYS,
|
|
23715
|
-
groupRelays: DEFAULT_GROUP_RELAYS,
|
|
23716
|
-
tokenRegistryUrl: TOKEN_REGISTRY_URL
|
|
23717
23711
|
}
|
|
23718
23712
|
};
|
|
23719
23713
|
var SPHERE_NETWORKS = {
|
|
23714
|
+
mainnet: { id: NETWORKS.mainnet.networkId, name: "mainnet" },
|
|
23720
23715
|
testnet2: { id: NETWORKS.testnet2.networkId, name: "testnet2" }
|
|
23721
23716
|
};
|
|
23722
23717
|
var TIMEOUTS = {
|
|
@@ -23732,11 +23727,98 @@ var TIMEOUTS = {
|
|
|
23732
23727
|
SYNC_INTERVAL: 6e4
|
|
23733
23728
|
};
|
|
23734
23729
|
|
|
23730
|
+
// core/errors.ts
|
|
23731
|
+
var SphereError = class extends Error {
|
|
23732
|
+
code;
|
|
23733
|
+
cause;
|
|
23734
|
+
/**
|
|
23735
|
+
* #441 deferred-paid linkage: the transferId of the possibly-committed send,
|
|
23736
|
+
* stamped by `sendOnce` before the error leaves so a payment-request consumer
|
|
23737
|
+
* can durably journal request→transfer and resolve 'paid' only when that
|
|
23738
|
+
* transfer actually completes (never optimistically). Present ONLY on
|
|
23739
|
+
* possibly-committed outcomes ({@link isPossiblyCommittedSendOutcome}).
|
|
23740
|
+
*/
|
|
23741
|
+
transferId;
|
|
23742
|
+
constructor(message, code, cause) {
|
|
23743
|
+
super(message);
|
|
23744
|
+
this.name = "SphereError";
|
|
23745
|
+
this.code = code;
|
|
23746
|
+
this.cause = cause;
|
|
23747
|
+
}
|
|
23748
|
+
};
|
|
23749
|
+
|
|
23750
|
+
// storage/tracked-addresses.ts
|
|
23751
|
+
function isDerivableIndex(value) {
|
|
23752
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 4294967295;
|
|
23753
|
+
}
|
|
23754
|
+
function num(value, fallback) {
|
|
23755
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
23756
|
+
}
|
|
23757
|
+
function toEntry(value) {
|
|
23758
|
+
if (typeof value !== "object" || value === null) return null;
|
|
23759
|
+
const e = value;
|
|
23760
|
+
if (!isDerivableIndex(e.index)) return null;
|
|
23761
|
+
return {
|
|
23762
|
+
...e,
|
|
23763
|
+
index: e.index,
|
|
23764
|
+
hidden: e.hidden === true,
|
|
23765
|
+
createdAt: num(e.createdAt, 0),
|
|
23766
|
+
updatedAt: num(e.updatedAt, 0)
|
|
23767
|
+
};
|
|
23768
|
+
}
|
|
23769
|
+
function parseTrackedAddresses(raw) {
|
|
23770
|
+
if (!raw) return [];
|
|
23771
|
+
let parsed;
|
|
23772
|
+
try {
|
|
23773
|
+
parsed = JSON.parse(raw);
|
|
23774
|
+
} catch {
|
|
23775
|
+
return [];
|
|
23776
|
+
}
|
|
23777
|
+
if (typeof parsed !== "object" || parsed === null) return [];
|
|
23778
|
+
const addresses = parsed.addresses;
|
|
23779
|
+
if (!Array.isArray(addresses)) return [];
|
|
23780
|
+
const entries = [];
|
|
23781
|
+
for (const row of addresses) {
|
|
23782
|
+
const entry = toEntry(row);
|
|
23783
|
+
if (entry) entries.push(entry);
|
|
23784
|
+
}
|
|
23785
|
+
return entries;
|
|
23786
|
+
}
|
|
23787
|
+
function mergeTrackedAddresses(onDisk, incoming) {
|
|
23788
|
+
const merged = /* @__PURE__ */ new Map();
|
|
23789
|
+
for (const entry of onDisk) {
|
|
23790
|
+
if (isDerivableIndex(entry.index)) merged.set(entry.index, entry);
|
|
23791
|
+
}
|
|
23792
|
+
for (const entry of incoming) {
|
|
23793
|
+
if (!isDerivableIndex(entry.index)) {
|
|
23794
|
+
throw new SphereError(
|
|
23795
|
+
`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.`,
|
|
23796
|
+
"VALIDATION_ERROR"
|
|
23797
|
+
);
|
|
23798
|
+
}
|
|
23799
|
+
const existing = merged.get(entry.index);
|
|
23800
|
+
if (!existing) {
|
|
23801
|
+
merged.set(entry.index, entry);
|
|
23802
|
+
continue;
|
|
23803
|
+
}
|
|
23804
|
+
const winner = entry.updatedAt >= existing.updatedAt ? entry : existing;
|
|
23805
|
+
merged.set(entry.index, {
|
|
23806
|
+
...existing,
|
|
23807
|
+
...winner,
|
|
23808
|
+
index: entry.index,
|
|
23809
|
+
createdAt: Math.min(existing.createdAt, entry.createdAt)
|
|
23810
|
+
});
|
|
23811
|
+
}
|
|
23812
|
+
return Array.from(merged.values()).sort((a, b) => a.index - b.index);
|
|
23813
|
+
}
|
|
23814
|
+
|
|
23735
23815
|
// impl/nodejs/storage/FileStorageProvider.ts
|
|
23736
23816
|
var FileStorageProvider = class {
|
|
23737
23817
|
id = "file-storage";
|
|
23738
23818
|
name = "File Storage";
|
|
23739
23819
|
type = "local";
|
|
23820
|
+
/** The resolved wallet file — two providers over one path share erasure (#766). */
|
|
23821
|
+
backingStoreId;
|
|
23740
23822
|
dataDir;
|
|
23741
23823
|
filePath;
|
|
23742
23824
|
isTxtMode;
|
|
@@ -23746,14 +23828,15 @@ var FileStorageProvider = class {
|
|
|
23746
23828
|
_identity = null;
|
|
23747
23829
|
constructor(config) {
|
|
23748
23830
|
if (typeof config === "string") {
|
|
23749
|
-
this.dataDir = config;
|
|
23750
|
-
this.filePath = path.join(
|
|
23831
|
+
this.dataDir = path.resolve(config);
|
|
23832
|
+
this.filePath = path.join(this.dataDir, "wallet.json");
|
|
23751
23833
|
} else {
|
|
23752
|
-
this.dataDir = config.dataDir;
|
|
23753
|
-
this.filePath = path.join(
|
|
23834
|
+
this.dataDir = path.resolve(config.dataDir);
|
|
23835
|
+
this.filePath = path.join(this.dataDir, config.fileName ?? "wallet.json");
|
|
23754
23836
|
this.network = config.network;
|
|
23755
23837
|
}
|
|
23756
23838
|
this.isTxtMode = this.filePath.endsWith(".txt");
|
|
23839
|
+
this.backingStoreId = `file:${this.filePath}`;
|
|
23757
23840
|
}
|
|
23758
23841
|
setIdentity(identity) {
|
|
23759
23842
|
this._identity = identity;
|
|
@@ -23850,18 +23933,40 @@ var FileStorageProvider = class {
|
|
|
23850
23933
|
}
|
|
23851
23934
|
await this.save();
|
|
23852
23935
|
}
|
|
23936
|
+
/** Serializes the read-merge-write below, per provider instance. */
|
|
23937
|
+
trackedWrites = Promise.resolve();
|
|
23938
|
+
/**
|
|
23939
|
+
* Persist the tracked-address registry by MERGING, never replacing.
|
|
23940
|
+
*
|
|
23941
|
+
* Every Sphere over this storage holds its own snapshot and writes it in
|
|
23942
|
+
* full, so a wholesale write drops the addresses this writer never saw
|
|
23943
|
+
* (#766 item 5 — a lost update, reproducible on one network). Concurrent
|
|
23944
|
+
* calls are serialized on `trackedWrites` so a read can never interleave
|
|
23945
|
+
* with another call's write.
|
|
23946
|
+
*
|
|
23947
|
+
* Deliberately PER OBJECT, unlike the browser providers. Two objects over one
|
|
23948
|
+
* `dataDir` share a `backingStoreId` but not this cache, and `save()` rewrites the
|
|
23949
|
+
* WHOLE file from it — so a sibling's *unrelated* `set()` rolls the registry back
|
|
23950
|
+
* regardless of how this one write is serialized. Sharing the chain here would make
|
|
23951
|
+
* the cross-object contract case pass while leaving the provider unsafe. The real
|
|
23952
|
+
* fix is #771 (refresh from disk under a per-file lock, on every write); until then
|
|
23953
|
+
* `backingStoreId` scopes TEARDOWN only. Reviewers keep re-finding this — see the
|
|
23954
|
+
* `unsupported:` note in tests/unit/storage/tracked-addresses-providers.test.ts.
|
|
23955
|
+
*/
|
|
23853
23956
|
async saveTrackedAddresses(entries) {
|
|
23854
|
-
|
|
23957
|
+
const run = this.trackedWrites.then(async () => {
|
|
23958
|
+
const onDisk = parseTrackedAddresses(await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES));
|
|
23959
|
+
const file = {
|
|
23960
|
+
version: 1,
|
|
23961
|
+
addresses: mergeTrackedAddresses(onDisk, entries)
|
|
23962
|
+
};
|
|
23963
|
+
await this.set(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES, JSON.stringify(file));
|
|
23964
|
+
});
|
|
23965
|
+
this.trackedWrites = run.then(() => void 0, () => void 0);
|
|
23966
|
+
await run;
|
|
23855
23967
|
}
|
|
23856
23968
|
async loadTrackedAddresses() {
|
|
23857
|
-
|
|
23858
|
-
if (!data) return [];
|
|
23859
|
-
try {
|
|
23860
|
-
const parsed = JSON.parse(data);
|
|
23861
|
-
return parsed.addresses ?? [];
|
|
23862
|
-
} catch {
|
|
23863
|
-
return [];
|
|
23864
|
-
}
|
|
23969
|
+
return parseTrackedAddresses(await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES));
|
|
23865
23970
|
}
|
|
23866
23971
|
/**
|
|
23867
23972
|
* Get full storage key with address prefix for per-address keys
|
|
@@ -24392,26 +24497,6 @@ var logger = {
|
|
|
24392
24497
|
}
|
|
24393
24498
|
};
|
|
24394
24499
|
|
|
24395
|
-
// core/errors.ts
|
|
24396
|
-
var SphereError = class extends Error {
|
|
24397
|
-
code;
|
|
24398
|
-
cause;
|
|
24399
|
-
/**
|
|
24400
|
-
* #441 deferred-paid linkage: the transferId of the possibly-committed send,
|
|
24401
|
-
* stamped by `sendOnce` before the error leaves so a payment-request consumer
|
|
24402
|
-
* can durably journal request→transfer and resolve 'paid' only when that
|
|
24403
|
-
* transfer actually completes (never optimistically). Present ONLY on
|
|
24404
|
-
* possibly-committed outcomes ({@link isPossiblyCommittedSendOutcome}).
|
|
24405
|
-
*/
|
|
24406
|
-
transferId;
|
|
24407
|
-
constructor(message, code, cause) {
|
|
24408
|
-
super(message);
|
|
24409
|
-
this.name = "SphereError";
|
|
24410
|
-
this.code = code;
|
|
24411
|
-
this.cause = cause;
|
|
24412
|
-
}
|
|
24413
|
-
};
|
|
24414
|
-
|
|
24415
24500
|
// transport/websocket.ts
|
|
24416
24501
|
function defaultUUIDGenerator() {
|
|
24417
24502
|
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
@@ -24584,14 +24669,10 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
24584
24669
|
});
|
|
24585
24670
|
}
|
|
24586
24671
|
});
|
|
24587
|
-
await
|
|
24588
|
-
this.nostrClient
|
|
24589
|
-
|
|
24590
|
-
|
|
24591
|
-
`Transport connection timed out after ${this.config.timeout}ms`
|
|
24592
|
-
)), this.config.timeout)
|
|
24593
|
-
)
|
|
24594
|
-
]);
|
|
24672
|
+
await this.connectWithDeadline(
|
|
24673
|
+
this.nostrClient,
|
|
24674
|
+
`Transport connection timed out after ${this.config.timeout}ms`
|
|
24675
|
+
);
|
|
24595
24676
|
if (!this.nostrClient.isConnected()) {
|
|
24596
24677
|
throw new SphereError("Failed to connect to any relay", "TRANSPORT_ERROR");
|
|
24597
24678
|
}
|
|
@@ -24606,6 +24687,27 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
24606
24687
|
throw error;
|
|
24607
24688
|
}
|
|
24608
24689
|
}
|
|
24690
|
+
/**
|
|
24691
|
+
* Race one client's relay connect against the configured timeout.
|
|
24692
|
+
*
|
|
24693
|
+
* The timer is cleared in a `finally` on EVERY path. `Promise.race` does not
|
|
24694
|
+
* cancel the loser: an un-cleared `setTimeout` keeps Node's event loop pinned
|
|
24695
|
+
* for the whole `config.timeout` after the call has already returned (and
|
|
24696
|
+
* then rejects a promise nobody is listening to any more).
|
|
24697
|
+
*/
|
|
24698
|
+
async connectWithDeadline(client, timeoutMessage) {
|
|
24699
|
+
let timer;
|
|
24700
|
+
try {
|
|
24701
|
+
await Promise.race([
|
|
24702
|
+
client.connect(...this.config.relays),
|
|
24703
|
+
new Promise((_, reject) => {
|
|
24704
|
+
timer = setTimeout(() => reject(new Error(timeoutMessage)), this.config.timeout);
|
|
24705
|
+
})
|
|
24706
|
+
]);
|
|
24707
|
+
} finally {
|
|
24708
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
24709
|
+
}
|
|
24710
|
+
}
|
|
24609
24711
|
async disconnect() {
|
|
24610
24712
|
if (this.nostrClient) {
|
|
24611
24713
|
this.nostrClient.disconnect();
|
|
@@ -24724,19 +24826,22 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
24724
24826
|
// TransportProvider Implementation
|
|
24725
24827
|
// ===========================================================================
|
|
24726
24828
|
async setIdentity(identity) {
|
|
24727
|
-
this.identity = identity;
|
|
24728
|
-
this.processedEventIds.clear();
|
|
24729
|
-
this.lastEventTs = 0;
|
|
24730
|
-
this.lastDmEventTs = 0;
|
|
24731
|
-
this.fallbackDmSince = null;
|
|
24732
24829
|
const secretKey = Buffer2.from(identity.privateKey, "hex");
|
|
24733
|
-
|
|
24734
|
-
const nostrPubkey =
|
|
24735
|
-
logger.debug("Nostr", "Identity
|
|
24830
|
+
const nextKeyManager = NostrKeyManager.fromPrivateKey(secretKey);
|
|
24831
|
+
const nostrPubkey = nextKeyManager.getPublicKeyHex();
|
|
24832
|
+
logger.debug("Nostr", "Identity staged, Nostr pubkey:", nostrPubkey.slice(0, 16) + "...");
|
|
24833
|
+
const applyStagedIdentity = () => {
|
|
24834
|
+
this.identity = identity;
|
|
24835
|
+
this.keyManager = nextKeyManager;
|
|
24836
|
+
this.processedEventIds.clear();
|
|
24837
|
+
this.lastEventTs = 0;
|
|
24838
|
+
this.lastDmEventTs = 0;
|
|
24839
|
+
this.fallbackDmSince = null;
|
|
24840
|
+
};
|
|
24736
24841
|
if (this.nostrClient && this.status === "connected") {
|
|
24737
24842
|
logger.debug("Nostr", "Identity changed while connected - recreating NostrClient");
|
|
24738
24843
|
const oldClient = this.nostrClient;
|
|
24739
|
-
|
|
24844
|
+
const nextClient = new NostrClient(nextKeyManager, {
|
|
24740
24845
|
autoReconnect: this.config.autoReconnect,
|
|
24741
24846
|
reconnectIntervalMs: this.config.reconnectDelay,
|
|
24742
24847
|
maxReconnectIntervalMs: this.config.reconnectDelay * 16,
|
|
@@ -24745,7 +24850,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
24745
24850
|
queryTimeoutMs: 2e4
|
|
24746
24851
|
// see same option above for rationale
|
|
24747
24852
|
});
|
|
24748
|
-
|
|
24853
|
+
nextClient.addConnectionListener({
|
|
24749
24854
|
onConnect: (url) => {
|
|
24750
24855
|
logger.debug("Nostr", "NostrClient connected to relay:", url);
|
|
24751
24856
|
},
|
|
@@ -24759,18 +24864,43 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
24759
24864
|
logger.debug("Nostr", "NostrClient reconnected to relay:", url);
|
|
24760
24865
|
}
|
|
24761
24866
|
});
|
|
24762
|
-
|
|
24763
|
-
this.
|
|
24764
|
-
|
|
24765
|
-
|
|
24766
|
-
|
|
24767
|
-
|
|
24768
|
-
|
|
24769
|
-
|
|
24770
|
-
|
|
24771
|
-
|
|
24867
|
+
try {
|
|
24868
|
+
await this.connectWithDeadline(
|
|
24869
|
+
nextClient,
|
|
24870
|
+
`Transport reconnection timed out after ${this.config.timeout}ms`
|
|
24871
|
+
);
|
|
24872
|
+
} catch (error) {
|
|
24873
|
+
try {
|
|
24874
|
+
nextClient.disconnect();
|
|
24875
|
+
} catch {
|
|
24876
|
+
}
|
|
24877
|
+
throw error;
|
|
24878
|
+
}
|
|
24879
|
+
if (this.nostrClient !== oldClient || this.status !== "connected") {
|
|
24880
|
+
try {
|
|
24881
|
+
nextClient.disconnect();
|
|
24882
|
+
} catch {
|
|
24883
|
+
}
|
|
24884
|
+
throw new SphereError(
|
|
24885
|
+
"Transport client changed or left the connected state during setIdentity - identity not applied",
|
|
24886
|
+
"TRANSPORT_ERROR"
|
|
24887
|
+
);
|
|
24888
|
+
}
|
|
24889
|
+
this.nostrClient = nextClient;
|
|
24890
|
+
applyStagedIdentity();
|
|
24891
|
+
try {
|
|
24892
|
+
await this.subscribeToEvents();
|
|
24893
|
+
} finally {
|
|
24894
|
+
try {
|
|
24895
|
+
oldClient.disconnect();
|
|
24896
|
+
} catch {
|
|
24897
|
+
}
|
|
24898
|
+
}
|
|
24772
24899
|
} else if (this.isConnected()) {
|
|
24900
|
+
applyStagedIdentity();
|
|
24773
24901
|
await this.subscribeToEvents();
|
|
24902
|
+
} else {
|
|
24903
|
+
applyStagedIdentity();
|
|
24774
24904
|
}
|
|
24775
24905
|
}
|
|
24776
24906
|
setFallbackSince(sinceSeconds) {
|
|
@@ -25537,14 +25667,14 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
25537
25667
|
const client = this.nostrClient;
|
|
25538
25668
|
const events = [];
|
|
25539
25669
|
let subId;
|
|
25540
|
-
await new Promise((
|
|
25670
|
+
await new Promise((resolve2, reject) => {
|
|
25541
25671
|
let timeout;
|
|
25542
25672
|
let settled = false;
|
|
25543
25673
|
const settle = () => {
|
|
25544
25674
|
if (settled) return;
|
|
25545
25675
|
settled = true;
|
|
25546
25676
|
clearTimeout(timeout);
|
|
25547
|
-
|
|
25677
|
+
resolve2();
|
|
25548
25678
|
};
|
|
25549
25679
|
try {
|
|
25550
25680
|
subId = client.subscribe(walletFilter, {
|
|
@@ -25587,7 +25717,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
25587
25717
|
const events = [];
|
|
25588
25718
|
const filter = new Filter(filterObj);
|
|
25589
25719
|
let subId;
|
|
25590
|
-
return new Promise((
|
|
25720
|
+
return new Promise((resolve2) => {
|
|
25591
25721
|
let settled = false;
|
|
25592
25722
|
const timeout = setTimeout(() => {
|
|
25593
25723
|
if (settled) return;
|
|
@@ -25599,7 +25729,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
25599
25729
|
}
|
|
25600
25730
|
}
|
|
25601
25731
|
logger.warn("Nostr", `queryEvents timed out after 15s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });
|
|
25602
|
-
|
|
25732
|
+
resolve2(events);
|
|
25603
25733
|
}, 15e3);
|
|
25604
25734
|
const settle = () => {
|
|
25605
25735
|
if (settled) return;
|
|
@@ -25611,7 +25741,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
25611
25741
|
} catch {
|
|
25612
25742
|
}
|
|
25613
25743
|
}
|
|
25614
|
-
|
|
25744
|
+
resolve2(events);
|
|
25615
25745
|
};
|
|
25616
25746
|
try {
|
|
25617
25747
|
subId = client.subscribe(filter, {
|
|
@@ -25630,7 +25760,7 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
25630
25760
|
});
|
|
25631
25761
|
} catch {
|
|
25632
25762
|
clearTimeout(timeout);
|
|
25633
|
-
|
|
25763
|
+
resolve2(events);
|
|
25634
25764
|
return;
|
|
25635
25765
|
}
|
|
25636
25766
|
});
|
|
@@ -26070,26 +26200,6 @@ var UnicityAggregatorProvider = class {
|
|
|
26070
26200
|
var UnicityOracleProvider = UnicityAggregatorProvider;
|
|
26071
26201
|
|
|
26072
26202
|
// assets/trustbase.ts
|
|
26073
|
-
var TRUSTBASE_TESTNET = {
|
|
26074
|
-
version: 1,
|
|
26075
|
-
networkId: 3,
|
|
26076
|
-
epoch: 1,
|
|
26077
|
-
epochStartRound: 1,
|
|
26078
|
-
rootNodes: [
|
|
26079
|
-
{
|
|
26080
|
-
nodeId: "16Uiu2HAkyQRiA7pMgzgLj9GgaBJEJa8zmx9dzqUDa6WxQPJ82ghU",
|
|
26081
|
-
sigKey: "0x039afb2acb65f5fbc272d8907f763d0a5d189aadc9b97afdcc5897ea4dd112e68b",
|
|
26082
|
-
stake: 1
|
|
26083
|
-
}
|
|
26084
|
-
],
|
|
26085
|
-
quorumThreshold: 1,
|
|
26086
|
-
stateHash: "",
|
|
26087
|
-
changeRecordHash: "",
|
|
26088
|
-
previousEntryHash: "",
|
|
26089
|
-
signatures: {
|
|
26090
|
-
"16Uiu2HAkyQRiA7pMgzgLj9GgaBJEJa8zmx9dzqUDa6WxQPJ82ghU": "0xf157c9fdd8a378e3ca70d354ccc4475ab2cd8de360127bc46b0aeab4b453a80f07fd9136a5843b60a8babaff23e20acc8879861f7651440a5e2829f7541b31f100"
|
|
26091
|
-
}
|
|
26092
|
-
};
|
|
26093
26203
|
var TRUSTBASE_TESTNET2 = {
|
|
26094
26204
|
version: 1,
|
|
26095
26205
|
networkId: 4,
|
|
@@ -26128,8 +26238,44 @@ var TRUSTBASE_TESTNET2 = {
|
|
|
26128
26238
|
"16Uiu2HAmU5T1TqKyKkUB3sitWb431hVFfp9nqvaLgVagQF6ywLFF": "0x9f28f3e3c50d4bfdaf54d7290fbbe311fffb3be2281a1d211be56818fa43d06a2e9a81925e9d365e552c8b772c99285a686de1d9fb581920be7524c7a5d467a301"
|
|
26129
26239
|
}
|
|
26130
26240
|
};
|
|
26131
|
-
var TRUSTBASE_MAINNET =
|
|
26132
|
-
|
|
26241
|
+
var TRUSTBASE_MAINNET = {
|
|
26242
|
+
version: 1,
|
|
26243
|
+
networkId: 1,
|
|
26244
|
+
epoch: 1,
|
|
26245
|
+
epochStartRound: 1,
|
|
26246
|
+
rootNodes: [
|
|
26247
|
+
{
|
|
26248
|
+
nodeId: "16Uiu2HAkvk4EpXoyh7SVLTX3TcsZkjbni5jY1sBgJ7qCEWfx1Gma",
|
|
26249
|
+
sigKey: "0x03059e30b5c980ce92e2c6d4e14775838be91a6f0e993f3a4899ab459c4b5225f2",
|
|
26250
|
+
stake: 1
|
|
26251
|
+
},
|
|
26252
|
+
{
|
|
26253
|
+
nodeId: "16Uiu2HAmF9Mq9pH9yUTdao3TpJeFZY9oX9gyMqfxggVA4fNmzmXr",
|
|
26254
|
+
sigKey: "0x03a514db8b17b1c79b4a6affeb5d88a129c4d5ff0f92c8f86c311971b0beb94335",
|
|
26255
|
+
stake: 1
|
|
26256
|
+
},
|
|
26257
|
+
{
|
|
26258
|
+
nodeId: "16Uiu2HAmLAHTSMvZ2apRRtBN9piYVhaK2i7sTMaQeSWSxsLh75TG",
|
|
26259
|
+
sigKey: "0x02e5669b2daa5d1afccfe66603f7d3fe171ecbd7ed891487ab3683cdebc3c3cd8b",
|
|
26260
|
+
stake: 1
|
|
26261
|
+
},
|
|
26262
|
+
{
|
|
26263
|
+
nodeId: "16Uiu2HAmNNaWfK4RTTAq7Ap8w2yZUryrEejqmmGf44ogMaYEnkJ4",
|
|
26264
|
+
sigKey: "0x03ffee964da8ca276d4fc1cf391929e6759db106ef1d0aea998154b51e5a7fb1b7",
|
|
26265
|
+
stake: 1
|
|
26266
|
+
}
|
|
26267
|
+
],
|
|
26268
|
+
quorumThreshold: 3,
|
|
26269
|
+
stateHash: "",
|
|
26270
|
+
changeRecordHash: "",
|
|
26271
|
+
previousEntryHash: "",
|
|
26272
|
+
signatures: {
|
|
26273
|
+
"16Uiu2HAkvk4EpXoyh7SVLTX3TcsZkjbni5jY1sBgJ7qCEWfx1Gma": "0xc2f1d67dbb47fe4dcf502301d8cd5021b8903c0d3385781653d09c703d566a3a3a4e59c75792b36f929e32564ddc14c40ef728c0bde5859774d08d802de2318c00",
|
|
26274
|
+
"16Uiu2HAmF9Mq9pH9yUTdao3TpJeFZY9oX9gyMqfxggVA4fNmzmXr": "0x3e3ad3a7540daa0eded457e31d2ebcacfb354036798f49f63433d26c44857e76157e4abd36cbb1d39de8cdf6e1d33f88b250077d4347c3e5bf0f35a64f2d815300",
|
|
26275
|
+
"16Uiu2HAmLAHTSMvZ2apRRtBN9piYVhaK2i7sTMaQeSWSxsLh75TG": "0x6101bd9df8ab87439d90b52cd74727bc8bf31dd74173c8f731ec3ea501d3d86556ba7c0e84643337959f3be0e448637c301e8e0b28196e780d4f2ff930d9062100",
|
|
26276
|
+
"16Uiu2HAmNNaWfK4RTTAq7Ap8w2yZUryrEejqmmGf44ogMaYEnkJ4": "0xda13c777b832425e5cbd8d6e49eb08ddbde35c1e4a9bee8bd495c3883f039e434b64560865c3bc52b68d65780932b1f2bf8971f245d2244face822af5984945c00"
|
|
26277
|
+
}
|
|
26278
|
+
};
|
|
26133
26279
|
|
|
26134
26280
|
// impl/shared/trustbase-loader.ts
|
|
26135
26281
|
function getEmbeddedTrustBase(network) {
|
|
@@ -26142,8 +26288,6 @@ function getEmbeddedTrustBase(network) {
|
|
|
26142
26288
|
case "testnet":
|
|
26143
26289
|
case "testnet2":
|
|
26144
26290
|
return TRUSTBASE_TESTNET2;
|
|
26145
|
-
case "dev":
|
|
26146
|
-
return TRUSTBASE_DEV;
|
|
26147
26291
|
default:
|
|
26148
26292
|
throw new SphereError(`getEmbeddedTrustBase: unknown network "${network}"`, "INVALID_CONFIG");
|
|
26149
26293
|
}
|
|
@@ -26239,6 +26383,7 @@ function createWalletApiProviders(base, config) {
|
|
|
26239
26383
|
|
|
26240
26384
|
// impl/shared/network.ts
|
|
26241
26385
|
var EXPECTED_NETWORK_ID = {
|
|
26386
|
+
mainnet: 1,
|
|
26242
26387
|
// v1 cutover: 'testnet' is an alias of testnet2 (the v2 gateway network),
|
|
26243
26388
|
// so both expect the testnet2 trust base (networkId 4).
|
|
26244
26389
|
testnet: 4,
|
|
@@ -26259,7 +26404,7 @@ function assertNetworkConsistency(network) {
|
|
|
26259
26404
|
);
|
|
26260
26405
|
}
|
|
26261
26406
|
const expected = EXPECTED_NETWORK_ID[network];
|
|
26262
|
-
if (
|
|
26407
|
+
if (networkId !== expected) {
|
|
26263
26408
|
throw new SphereError(
|
|
26264
26409
|
`Network "${network}" trust base networkId ${networkId} does not match expected ${expected} (wrong trust base wired to this network).`,
|
|
26265
26410
|
"INVALID_CONFIG"
|
|
@@ -26524,419 +26669,6 @@ function createPriceProvider(config) {
|
|
|
26524
26669
|
}
|
|
26525
26670
|
}
|
|
26526
26671
|
|
|
26527
|
-
// registry/TokenRegistry.ts
|
|
26528
|
-
var FETCH_TIMEOUT_MS = 1e4;
|
|
26529
|
-
var TokenRegistry = class _TokenRegistry {
|
|
26530
|
-
static instance = null;
|
|
26531
|
-
definitionsById;
|
|
26532
|
-
definitionsBySymbol;
|
|
26533
|
-
definitionsByName;
|
|
26534
|
-
// Remote refresh state
|
|
26535
|
-
remoteUrl = null;
|
|
26536
|
-
storage = null;
|
|
26537
|
-
refreshIntervalMs = TOKEN_REGISTRY_REFRESH_INTERVAL;
|
|
26538
|
-
refreshTimer = null;
|
|
26539
|
-
lastRefreshAt = 0;
|
|
26540
|
-
refreshPromise = null;
|
|
26541
|
-
initialLoadPromise = null;
|
|
26542
|
-
constructor() {
|
|
26543
|
-
this.definitionsById = /* @__PURE__ */ new Map();
|
|
26544
|
-
this.definitionsBySymbol = /* @__PURE__ */ new Map();
|
|
26545
|
-
this.definitionsByName = /* @__PURE__ */ new Map();
|
|
26546
|
-
}
|
|
26547
|
-
/**
|
|
26548
|
-
* Get singleton instance of TokenRegistry
|
|
26549
|
-
*/
|
|
26550
|
-
static getInstance() {
|
|
26551
|
-
if (!_TokenRegistry.instance) {
|
|
26552
|
-
_TokenRegistry.instance = new _TokenRegistry();
|
|
26553
|
-
}
|
|
26554
|
-
return _TokenRegistry.instance;
|
|
26555
|
-
}
|
|
26556
|
-
/**
|
|
26557
|
-
* Configure remote registry refresh with persistent caching.
|
|
26558
|
-
*
|
|
26559
|
-
* On first call:
|
|
26560
|
-
* 1. Loads cached data from StorageProvider (if available and fresh)
|
|
26561
|
-
* 2. Starts periodic remote fetch (if autoRefresh is true, which is default)
|
|
26562
|
-
*
|
|
26563
|
-
* @param options - Configuration options
|
|
26564
|
-
* @param options.remoteUrl - Remote URL to fetch definitions from
|
|
26565
|
-
* @param options.storage - StorageProvider for persistent caching
|
|
26566
|
-
* @param options.refreshIntervalMs - Refresh interval in ms (default: 1 hour)
|
|
26567
|
-
* @param options.autoRefresh - Start auto-refresh immediately (default: true)
|
|
26568
|
-
*/
|
|
26569
|
-
static configure(options) {
|
|
26570
|
-
const instance = _TokenRegistry.getInstance();
|
|
26571
|
-
if (options.remoteUrl !== void 0) {
|
|
26572
|
-
instance.remoteUrl = options.remoteUrl;
|
|
26573
|
-
}
|
|
26574
|
-
if (options.storage !== void 0) {
|
|
26575
|
-
instance.storage = options.storage;
|
|
26576
|
-
}
|
|
26577
|
-
if (options.refreshIntervalMs !== void 0) {
|
|
26578
|
-
instance.refreshIntervalMs = options.refreshIntervalMs;
|
|
26579
|
-
}
|
|
26580
|
-
const autoRefresh = options.autoRefresh ?? true;
|
|
26581
|
-
instance.initialLoadPromise = instance.performInitialLoad(autoRefresh);
|
|
26582
|
-
}
|
|
26583
|
-
/**
|
|
26584
|
-
* Reset the singleton instance (useful for testing).
|
|
26585
|
-
* Stops auto-refresh if running.
|
|
26586
|
-
*/
|
|
26587
|
-
static resetInstance() {
|
|
26588
|
-
if (_TokenRegistry.instance) {
|
|
26589
|
-
_TokenRegistry.instance.stopAutoRefresh();
|
|
26590
|
-
}
|
|
26591
|
-
_TokenRegistry.instance = null;
|
|
26592
|
-
}
|
|
26593
|
-
/**
|
|
26594
|
-
* Destroy the singleton: stop auto-refresh and reset.
|
|
26595
|
-
*/
|
|
26596
|
-
static destroy() {
|
|
26597
|
-
_TokenRegistry.resetInstance();
|
|
26598
|
-
}
|
|
26599
|
-
/**
|
|
26600
|
-
* Wait for the initial data load (cache or remote) to complete.
|
|
26601
|
-
* Returns true if data was loaded, false if not (timeout or no data source).
|
|
26602
|
-
*
|
|
26603
|
-
* @param timeoutMs - Maximum wait time in ms (default: 10s). Set to 0 for no timeout.
|
|
26604
|
-
*/
|
|
26605
|
-
static async waitForReady(timeoutMs = 1e4) {
|
|
26606
|
-
const instance = _TokenRegistry.getInstance();
|
|
26607
|
-
if (!instance.initialLoadPromise) {
|
|
26608
|
-
return instance.definitionsById.size > 0;
|
|
26609
|
-
}
|
|
26610
|
-
if (timeoutMs <= 0) {
|
|
26611
|
-
return instance.initialLoadPromise;
|
|
26612
|
-
}
|
|
26613
|
-
return Promise.race([
|
|
26614
|
-
instance.initialLoadPromise,
|
|
26615
|
-
new Promise((resolve) => setTimeout(() => resolve(false), timeoutMs))
|
|
26616
|
-
]);
|
|
26617
|
-
}
|
|
26618
|
-
// ===========================================================================
|
|
26619
|
-
// Initial Load
|
|
26620
|
-
// ===========================================================================
|
|
26621
|
-
/**
|
|
26622
|
-
* Perform initial data load: try cache first, fall back to remote fetch.
|
|
26623
|
-
* After initial data is available, start periodic auto-refresh if configured.
|
|
26624
|
-
*/
|
|
26625
|
-
async performInitialLoad(autoRefresh) {
|
|
26626
|
-
let loaded = false;
|
|
26627
|
-
if (this.storage) {
|
|
26628
|
-
loaded = await this.loadFromCache();
|
|
26629
|
-
}
|
|
26630
|
-
if (loaded) {
|
|
26631
|
-
if (autoRefresh && this.remoteUrl) {
|
|
26632
|
-
this.startAutoRefresh();
|
|
26633
|
-
}
|
|
26634
|
-
return true;
|
|
26635
|
-
}
|
|
26636
|
-
if (autoRefresh && this.remoteUrl) {
|
|
26637
|
-
loaded = await this.refreshFromRemote();
|
|
26638
|
-
this.stopAutoRefresh();
|
|
26639
|
-
this.refreshTimer = setInterval(() => {
|
|
26640
|
-
this.refreshFromRemote();
|
|
26641
|
-
}, this.refreshIntervalMs);
|
|
26642
|
-
return loaded;
|
|
26643
|
-
}
|
|
26644
|
-
return false;
|
|
26645
|
-
}
|
|
26646
|
-
// ===========================================================================
|
|
26647
|
-
// Cache (StorageProvider)
|
|
26648
|
-
// ===========================================================================
|
|
26649
|
-
/**
|
|
26650
|
-
* Per-network cache keys. The registry cache MUST be namespaced by remoteUrl:
|
|
26651
|
-
* testnet and testnet2 ship DIFFERENT coinIds for the same symbol, so a single
|
|
26652
|
-
* global key let a stale testnet snapshot be served for a testnet2 request —
|
|
26653
|
-
* wrong/missing icons AND self-mint resolving the wrong-network coinId. Keying
|
|
26654
|
-
* by remoteUrl isolates the snapshots so a network can never read another's.
|
|
26655
|
-
*/
|
|
26656
|
-
cacheKeys() {
|
|
26657
|
-
const base = STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE;
|
|
26658
|
-
const baseTs = STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS;
|
|
26659
|
-
if (!this.remoteUrl) return { data: base, ts: baseTs };
|
|
26660
|
-
return { data: `${base}:${this.remoteUrl}`, ts: `${baseTs}:${this.remoteUrl}` };
|
|
26661
|
-
}
|
|
26662
|
-
/**
|
|
26663
|
-
* Load definitions from StorageProvider cache.
|
|
26664
|
-
* Only applies if cache exists and is fresh (within refreshIntervalMs).
|
|
26665
|
-
*/
|
|
26666
|
-
async loadFromCache() {
|
|
26667
|
-
if (!this.storage) return false;
|
|
26668
|
-
try {
|
|
26669
|
-
const { data: dataKey, ts: tsKey } = this.cacheKeys();
|
|
26670
|
-
const [cached, cachedTs] = await Promise.all([
|
|
26671
|
-
this.storage.get(dataKey),
|
|
26672
|
-
this.storage.get(tsKey)
|
|
26673
|
-
]);
|
|
26674
|
-
if (!cached || !cachedTs) return false;
|
|
26675
|
-
const ts = parseInt(cachedTs, 10);
|
|
26676
|
-
if (isNaN(ts)) return false;
|
|
26677
|
-
const age = Date.now() - ts;
|
|
26678
|
-
if (age > this.refreshIntervalMs) return false;
|
|
26679
|
-
if (this.lastRefreshAt > ts) return false;
|
|
26680
|
-
const data = JSON.parse(cached);
|
|
26681
|
-
if (!this.isValidDefinitionsArray(data)) return false;
|
|
26682
|
-
this.applyDefinitions(data);
|
|
26683
|
-
this.lastRefreshAt = ts;
|
|
26684
|
-
return true;
|
|
26685
|
-
} catch {
|
|
26686
|
-
return false;
|
|
26687
|
-
}
|
|
26688
|
-
}
|
|
26689
|
-
/**
|
|
26690
|
-
* Save definitions to StorageProvider cache.
|
|
26691
|
-
*/
|
|
26692
|
-
async saveToCache(definitions) {
|
|
26693
|
-
if (!this.storage) return;
|
|
26694
|
-
try {
|
|
26695
|
-
const { data: dataKey, ts: tsKey } = this.cacheKeys();
|
|
26696
|
-
await Promise.all([
|
|
26697
|
-
this.storage.set(dataKey, JSON.stringify(definitions)),
|
|
26698
|
-
this.storage.set(tsKey, String(Date.now()))
|
|
26699
|
-
]);
|
|
26700
|
-
} catch {
|
|
26701
|
-
}
|
|
26702
|
-
}
|
|
26703
|
-
// ===========================================================================
|
|
26704
|
-
// Remote Refresh
|
|
26705
|
-
// ===========================================================================
|
|
26706
|
-
/**
|
|
26707
|
-
* Apply an array of token definitions to the internal maps.
|
|
26708
|
-
* Clears existing data before applying.
|
|
26709
|
-
*/
|
|
26710
|
-
applyDefinitions(definitions) {
|
|
26711
|
-
this.definitionsById.clear();
|
|
26712
|
-
this.definitionsBySymbol.clear();
|
|
26713
|
-
this.definitionsByName.clear();
|
|
26714
|
-
for (const def of definitions) {
|
|
26715
|
-
const idLower = def.id.toLowerCase();
|
|
26716
|
-
this.definitionsById.set(idLower, def);
|
|
26717
|
-
if (def.symbol) {
|
|
26718
|
-
this.definitionsBySymbol.set(def.symbol.toUpperCase(), def);
|
|
26719
|
-
}
|
|
26720
|
-
this.definitionsByName.set(def.name.toLowerCase(), def);
|
|
26721
|
-
}
|
|
26722
|
-
}
|
|
26723
|
-
/**
|
|
26724
|
-
* Validate that data is an array of objects with 'id' field
|
|
26725
|
-
*/
|
|
26726
|
-
isValidDefinitionsArray(data) {
|
|
26727
|
-
return Array.isArray(data) && data.every((item) => item && typeof item === "object" && "id" in item);
|
|
26728
|
-
}
|
|
26729
|
-
/**
|
|
26730
|
-
* Fetch token definitions from the remote URL and update the registry.
|
|
26731
|
-
* On success, also persists to StorageProvider cache.
|
|
26732
|
-
* Returns true on success, false on failure. On failure, existing data is preserved.
|
|
26733
|
-
* Concurrent calls are deduplicated — only one fetch runs at a time.
|
|
26734
|
-
*/
|
|
26735
|
-
async refreshFromRemote() {
|
|
26736
|
-
if (!this.remoteUrl) {
|
|
26737
|
-
return false;
|
|
26738
|
-
}
|
|
26739
|
-
if (this.refreshPromise) {
|
|
26740
|
-
return this.refreshPromise;
|
|
26741
|
-
}
|
|
26742
|
-
this.refreshPromise = this.doRefresh();
|
|
26743
|
-
try {
|
|
26744
|
-
return await this.refreshPromise;
|
|
26745
|
-
} finally {
|
|
26746
|
-
this.refreshPromise = null;
|
|
26747
|
-
}
|
|
26748
|
-
}
|
|
26749
|
-
async doRefresh() {
|
|
26750
|
-
try {
|
|
26751
|
-
const controller = new AbortController();
|
|
26752
|
-
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
26753
|
-
let response;
|
|
26754
|
-
try {
|
|
26755
|
-
response = await fetch(this.remoteUrl, {
|
|
26756
|
-
headers: { Accept: "application/json" },
|
|
26757
|
-
signal: controller.signal
|
|
26758
|
-
});
|
|
26759
|
-
} finally {
|
|
26760
|
-
clearTimeout(timer);
|
|
26761
|
-
}
|
|
26762
|
-
if (!response.ok) {
|
|
26763
|
-
logger.warn("TokenRegistry", `Remote fetch failed: HTTP ${response.status} ${response.statusText}`);
|
|
26764
|
-
return false;
|
|
26765
|
-
}
|
|
26766
|
-
const data = await response.json();
|
|
26767
|
-
if (!this.isValidDefinitionsArray(data)) {
|
|
26768
|
-
logger.warn("TokenRegistry", "Remote data is not a valid token definitions array");
|
|
26769
|
-
return false;
|
|
26770
|
-
}
|
|
26771
|
-
const definitions = data;
|
|
26772
|
-
this.applyDefinitions(definitions);
|
|
26773
|
-
this.lastRefreshAt = Date.now();
|
|
26774
|
-
this.saveToCache(definitions);
|
|
26775
|
-
return true;
|
|
26776
|
-
} catch (error) {
|
|
26777
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
26778
|
-
logger.warn("TokenRegistry", `Remote refresh failed: ${message}`);
|
|
26779
|
-
return false;
|
|
26780
|
-
}
|
|
26781
|
-
}
|
|
26782
|
-
/**
|
|
26783
|
-
* Start periodic auto-refresh from the remote URL.
|
|
26784
|
-
* Does an immediate fetch, then repeats at the configured interval.
|
|
26785
|
-
*/
|
|
26786
|
-
startAutoRefresh(intervalMs) {
|
|
26787
|
-
this.stopAutoRefresh();
|
|
26788
|
-
if (intervalMs !== void 0) {
|
|
26789
|
-
this.refreshIntervalMs = intervalMs;
|
|
26790
|
-
}
|
|
26791
|
-
this.refreshFromRemote();
|
|
26792
|
-
this.refreshTimer = setInterval(() => {
|
|
26793
|
-
this.refreshFromRemote();
|
|
26794
|
-
}, this.refreshIntervalMs);
|
|
26795
|
-
}
|
|
26796
|
-
/**
|
|
26797
|
-
* Stop periodic auto-refresh
|
|
26798
|
-
*/
|
|
26799
|
-
stopAutoRefresh() {
|
|
26800
|
-
if (this.refreshTimer !== null) {
|
|
26801
|
-
clearInterval(this.refreshTimer);
|
|
26802
|
-
this.refreshTimer = null;
|
|
26803
|
-
}
|
|
26804
|
-
}
|
|
26805
|
-
/**
|
|
26806
|
-
* Timestamp of the last successful remote refresh (0 if never refreshed)
|
|
26807
|
-
*/
|
|
26808
|
-
getLastRefreshAt() {
|
|
26809
|
-
return this.lastRefreshAt;
|
|
26810
|
-
}
|
|
26811
|
-
// ===========================================================================
|
|
26812
|
-
// Lookup Methods
|
|
26813
|
-
// ===========================================================================
|
|
26814
|
-
/**
|
|
26815
|
-
* Get token definition by hex coin ID
|
|
26816
|
-
* @param coinId - 64-character hex string
|
|
26817
|
-
* @returns Token definition or undefined if not found
|
|
26818
|
-
*/
|
|
26819
|
-
getDefinition(coinId) {
|
|
26820
|
-
if (!coinId) return void 0;
|
|
26821
|
-
return this.definitionsById.get(coinId.toLowerCase());
|
|
26822
|
-
}
|
|
26823
|
-
/**
|
|
26824
|
-
* Get token definition by symbol (e.g., "UCT", "BTC")
|
|
26825
|
-
* @param symbol - Token symbol (case-insensitive)
|
|
26826
|
-
* @returns Token definition or undefined if not found
|
|
26827
|
-
*/
|
|
26828
|
-
getDefinitionBySymbol(symbol) {
|
|
26829
|
-
if (!symbol) return void 0;
|
|
26830
|
-
return this.definitionsBySymbol.get(symbol.toUpperCase());
|
|
26831
|
-
}
|
|
26832
|
-
/**
|
|
26833
|
-
* Get token definition by name (e.g., "bitcoin", "ethereum")
|
|
26834
|
-
* @param name - Token name (case-insensitive)
|
|
26835
|
-
* @returns Token definition or undefined if not found
|
|
26836
|
-
*/
|
|
26837
|
-
getDefinitionByName(name) {
|
|
26838
|
-
if (!name) return void 0;
|
|
26839
|
-
return this.definitionsByName.get(name.toLowerCase());
|
|
26840
|
-
}
|
|
26841
|
-
/**
|
|
26842
|
-
* Get token symbol for a coin ID
|
|
26843
|
-
* @param coinId - 64-character hex string
|
|
26844
|
-
* @returns Symbol (e.g., "UCT") or truncated ID if not found
|
|
26845
|
-
*/
|
|
26846
|
-
getSymbol(coinId) {
|
|
26847
|
-
const def = this.getDefinition(coinId);
|
|
26848
|
-
if (def?.symbol) {
|
|
26849
|
-
return def.symbol;
|
|
26850
|
-
}
|
|
26851
|
-
return coinId.slice(0, 6).toUpperCase();
|
|
26852
|
-
}
|
|
26853
|
-
/**
|
|
26854
|
-
* Get token name for a coin ID
|
|
26855
|
-
* @param coinId - 64-character hex string
|
|
26856
|
-
* @returns Name (e.g., "Bitcoin") or coin ID if not found
|
|
26857
|
-
*/
|
|
26858
|
-
getName(coinId) {
|
|
26859
|
-
const def = this.getDefinition(coinId);
|
|
26860
|
-
if (def?.name) {
|
|
26861
|
-
return def.name.charAt(0).toUpperCase() + def.name.slice(1);
|
|
26862
|
-
}
|
|
26863
|
-
return coinId;
|
|
26864
|
-
}
|
|
26865
|
-
/**
|
|
26866
|
-
* Get decimal places for a coin ID
|
|
26867
|
-
* @param coinId - 64-character hex string
|
|
26868
|
-
* @returns Decimals or 0 if not found
|
|
26869
|
-
*/
|
|
26870
|
-
getDecimals(coinId) {
|
|
26871
|
-
const def = this.getDefinition(coinId);
|
|
26872
|
-
return def?.decimals ?? 0;
|
|
26873
|
-
}
|
|
26874
|
-
/**
|
|
26875
|
-
* Get icon URL for a coin ID
|
|
26876
|
-
* @param coinId - 64-character hex string
|
|
26877
|
-
* @param preferPng - Prefer PNG format over SVG
|
|
26878
|
-
* @returns Icon URL or null if not found
|
|
26879
|
-
*/
|
|
26880
|
-
getIconUrl(coinId, preferPng = true) {
|
|
26881
|
-
const def = this.getDefinition(coinId);
|
|
26882
|
-
if (!def?.icons || def.icons.length === 0) {
|
|
26883
|
-
return null;
|
|
26884
|
-
}
|
|
26885
|
-
if (preferPng) {
|
|
26886
|
-
const pngIcon = def.icons.find((i) => i.url.toLowerCase().includes(".png"));
|
|
26887
|
-
if (pngIcon) return pngIcon.url;
|
|
26888
|
-
}
|
|
26889
|
-
return def.icons[0].url;
|
|
26890
|
-
}
|
|
26891
|
-
/**
|
|
26892
|
-
* Check if a coin ID is known in the registry
|
|
26893
|
-
* @param coinId - 64-character hex string
|
|
26894
|
-
* @returns true if the coin is in the registry
|
|
26895
|
-
*/
|
|
26896
|
-
isKnown(coinId) {
|
|
26897
|
-
return this.definitionsById.has(coinId.toLowerCase());
|
|
26898
|
-
}
|
|
26899
|
-
/**
|
|
26900
|
-
* Get all token definitions
|
|
26901
|
-
* @returns Array of all token definitions
|
|
26902
|
-
*/
|
|
26903
|
-
getAllDefinitions() {
|
|
26904
|
-
return Array.from(this.definitionsById.values());
|
|
26905
|
-
}
|
|
26906
|
-
/**
|
|
26907
|
-
* Get all fungible token definitions
|
|
26908
|
-
* @returns Array of fungible token definitions
|
|
26909
|
-
*/
|
|
26910
|
-
getFungibleTokens() {
|
|
26911
|
-
return this.getAllDefinitions().filter((def) => def.assetKind === "fungible");
|
|
26912
|
-
}
|
|
26913
|
-
/**
|
|
26914
|
-
* Get all non-fungible token definitions
|
|
26915
|
-
* @returns Array of non-fungible token definitions
|
|
26916
|
-
*/
|
|
26917
|
-
getNonFungibleTokens() {
|
|
26918
|
-
return this.getAllDefinitions().filter((def) => def.assetKind === "non-fungible");
|
|
26919
|
-
}
|
|
26920
|
-
/**
|
|
26921
|
-
* Get coin ID by symbol
|
|
26922
|
-
* @param symbol - Token symbol (e.g., "UCT")
|
|
26923
|
-
* @returns Coin ID hex string or undefined if not found
|
|
26924
|
-
*/
|
|
26925
|
-
getCoinIdBySymbol(symbol) {
|
|
26926
|
-
const def = this.getDefinitionBySymbol(symbol);
|
|
26927
|
-
return def?.id;
|
|
26928
|
-
}
|
|
26929
|
-
/**
|
|
26930
|
-
* Get coin ID by name
|
|
26931
|
-
* @param name - Token name (e.g., "bitcoin")
|
|
26932
|
-
* @returns Coin ID hex string or undefined if not found
|
|
26933
|
-
*/
|
|
26934
|
-
getCoinIdByName(name) {
|
|
26935
|
-
const def = this.getDefinitionByName(name);
|
|
26936
|
-
return def?.id;
|
|
26937
|
-
}
|
|
26938
|
-
};
|
|
26939
|
-
|
|
26940
26672
|
// impl/shared/resolvers.ts
|
|
26941
26673
|
function getNetworkConfig(network = "mainnet") {
|
|
26942
26674
|
return NETWORKS[network];
|
|
@@ -27028,8 +26760,7 @@ function createNodeProviders(config) {
|
|
|
27028
26760
|
}
|
|
27029
26761
|
const network = config.network;
|
|
27030
26762
|
assertNetworkConsistency(network);
|
|
27031
|
-
|
|
27032
|
-
logger.configure({ debug: globalDebug });
|
|
26763
|
+
if (config?.debug !== void 0) logger.configure({ debug: config.debug });
|
|
27033
26764
|
if (config?.transport?.debug) logger.setTagDebug("Nostr", true);
|
|
27034
26765
|
if (config?.oracle?.debug) logger.setTagDebug("Aggregator", true);
|
|
27035
26766
|
if (config?.price?.debug) logger.setTagDebug("Price", true);
|
|
@@ -27043,8 +26774,6 @@ function createNodeProviders(config) {
|
|
|
27043
26774
|
const priceConfig = resolvePriceConfig(config?.price, storage);
|
|
27044
26775
|
const groupChat = resolveGroupChatConfig(network, config?.groupChat);
|
|
27045
26776
|
const market = resolveMarketConfig(config?.market);
|
|
27046
|
-
const networkConfig = getNetworkConfig(network);
|
|
27047
|
-
TokenRegistry.configure({ remoteUrl: networkConfig.tokenRegistryUrl, storage });
|
|
27048
26777
|
return {
|
|
27049
26778
|
storage,
|
|
27050
26779
|
groupChat,
|