@unicitylabs/sphere-sdk 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1754,7 +1754,7 @@ var L1PaymentsModule = class {
1754
1754
  _transport;
1755
1755
  constructor(config) {
1756
1756
  this._config = {
1757
- electrumUrl: config?.electrumUrl ?? "wss://fulcrum.alpha.unicity.network:50004",
1757
+ electrumUrl: config?.electrumUrl ?? "wss://fulcrum.unicity.network:50004",
1758
1758
  network: config?.network ?? "mainnet",
1759
1759
  defaultFeeRate: config?.defaultFeeRate ?? 10,
1760
1760
  enableVesting: config?.enableVesting ?? true
@@ -1786,10 +1786,17 @@ var L1PaymentsModule = class {
1786
1786
  });
1787
1787
  }
1788
1788
  }
1789
- if (this._config.electrumUrl) {
1789
+ this._initialized = true;
1790
+ }
1791
+ /**
1792
+ * Ensure the Fulcrum WebSocket is connected. Called lazily before any
1793
+ * operation that needs the network. If the singleton is already connected
1794
+ * (e.g. by the address scanner), this is a no-op.
1795
+ */
1796
+ async ensureConnected() {
1797
+ if (!isWebSocketConnected() && this._config.electrumUrl) {
1790
1798
  await connect(this._config.electrumUrl);
1791
1799
  }
1792
- this._initialized = true;
1793
1800
  }
1794
1801
  destroy() {
1795
1802
  if (isWebSocketConnected()) {
@@ -1847,6 +1854,7 @@ var L1PaymentsModule = class {
1847
1854
  }
1848
1855
  async send(request) {
1849
1856
  this.ensureInitialized();
1857
+ await this.ensureConnected();
1850
1858
  if (!this._wallet || !this._identity) {
1851
1859
  return { success: false, error: "No wallet available" };
1852
1860
  }
@@ -1881,6 +1889,7 @@ var L1PaymentsModule = class {
1881
1889
  }
1882
1890
  async getBalance() {
1883
1891
  this.ensureInitialized();
1892
+ await this.ensureConnected();
1884
1893
  const addresses = this._getWatchedAddresses();
1885
1894
  let totalAlpha = 0;
1886
1895
  let vestedSats = BigInt(0);
@@ -1912,6 +1921,7 @@ var L1PaymentsModule = class {
1912
1921
  }
1913
1922
  async getUtxos() {
1914
1923
  this.ensureInitialized();
1924
+ await this.ensureConnected();
1915
1925
  const result = [];
1916
1926
  const currentHeight = await getCurrentBlockHeight();
1917
1927
  const allUtxos = await this._getAllUtxos();
@@ -1947,42 +1957,73 @@ var L1PaymentsModule = class {
1947
1957
  return result;
1948
1958
  }
1949
1959
  async getHistory(limit) {
1960
+ await this.ensureConnected();
1950
1961
  this.ensureInitialized();
1951
1962
  const addresses = this._getWatchedAddresses();
1952
1963
  const transactions = [];
1953
1964
  const seenTxids = /* @__PURE__ */ new Set();
1954
1965
  const currentHeight = await getCurrentBlockHeight();
1966
+ const txCache = /* @__PURE__ */ new Map();
1967
+ const fetchTx = async (txid) => {
1968
+ if (txCache.has(txid)) return txCache.get(txid);
1969
+ const detail = await getTransaction(txid);
1970
+ txCache.set(txid, detail);
1971
+ return detail;
1972
+ };
1973
+ const addressSet = new Set(addresses.map((a) => a.toLowerCase()));
1955
1974
  for (const address of addresses) {
1956
1975
  const history = await getTransactionHistory(address);
1957
1976
  for (const item of history) {
1958
1977
  if (seenTxids.has(item.tx_hash)) continue;
1959
1978
  seenTxids.add(item.tx_hash);
1960
- const tx = await getTransaction(item.tx_hash);
1979
+ const tx = await fetchTx(item.tx_hash);
1961
1980
  if (!tx) continue;
1962
- const isSend = tx.vin?.some(
1963
- (vin) => addresses.includes(vin.txid ?? "")
1964
- );
1965
- let amount = "0";
1981
+ let isSend = false;
1982
+ for (const vin of tx.vin ?? []) {
1983
+ if (!vin.txid) continue;
1984
+ const prevTx = await fetchTx(vin.txid);
1985
+ if (prevTx?.vout?.[vin.vout]) {
1986
+ const prevOut = prevTx.vout[vin.vout];
1987
+ const prevAddrs = [
1988
+ ...prevOut.scriptPubKey?.addresses ?? [],
1989
+ ...prevOut.scriptPubKey?.address ? [prevOut.scriptPubKey.address] : []
1990
+ ];
1991
+ if (prevAddrs.some((a) => addressSet.has(a.toLowerCase()))) {
1992
+ isSend = true;
1993
+ break;
1994
+ }
1995
+ }
1996
+ }
1997
+ let amountToUs = 0;
1998
+ let amountToOthers = 0;
1966
1999
  let txAddress = address;
2000
+ let externalAddress = "";
1967
2001
  if (tx.vout) {
1968
2002
  for (const vout of tx.vout) {
1969
- const voutAddresses = vout.scriptPubKey?.addresses ?? [];
1970
- if (vout.scriptPubKey?.address) {
1971
- voutAddresses.push(vout.scriptPubKey.address);
1972
- }
1973
- const matchedAddr = voutAddresses.find((a) => addresses.includes(a));
1974
- if (matchedAddr) {
1975
- amount = Math.floor((vout.value ?? 0) * 1e8).toString();
1976
- txAddress = matchedAddr;
1977
- break;
2003
+ const voutAddresses = [
2004
+ ...vout.scriptPubKey?.addresses ?? [],
2005
+ ...vout.scriptPubKey?.address ? [vout.scriptPubKey.address] : []
2006
+ ];
2007
+ const isOurs = voutAddresses.some((a) => addressSet.has(a.toLowerCase()));
2008
+ const valueSats = Math.floor((vout.value ?? 0) * 1e8);
2009
+ if (isOurs) {
2010
+ amountToUs += valueSats;
2011
+ if (!txAddress) txAddress = voutAddresses[0];
2012
+ } else {
2013
+ amountToOthers += valueSats;
2014
+ if (!externalAddress && voutAddresses.length > 0) {
2015
+ externalAddress = voutAddresses[0];
2016
+ }
1978
2017
  }
1979
2018
  }
1980
2019
  }
2020
+ const amount = isSend ? amountToOthers.toString() : amountToUs.toString();
2021
+ const displayAddress = isSend ? externalAddress || txAddress : txAddress;
1981
2022
  transactions.push({
1982
2023
  txid: item.tx_hash,
1983
2024
  type: isSend ? "send" : "receive",
1984
2025
  amount,
1985
- address: txAddress,
2026
+ address: displayAddress,
1986
2027
  confirmations: item.height > 0 ? currentHeight - item.height : 0,
1987
2028
  timestamp: tx.time ? tx.time * 1e3 : Date.now(),
1988
2029
  blockHeight: item.height > 0 ? item.height : void 0
@@ -1994,6 +2035,7 @@ var L1PaymentsModule = class {
1994
2035
  }
1995
2036
  async getTransaction(txid) {
1996
2037
  this.ensureInitialized();
2038
+ await this.ensureConnected();
1997
2039
  const tx = await getTransaction(txid);
1998
2040
  if (!tx) return null;
1999
2041
  const addresses = this._getWatchedAddresses();
@@ -2029,6 +2071,7 @@ var L1PaymentsModule = class {
2029
2071
  }
2030
2072
  async estimateFee(to, amount) {
2031
2073
  this.ensureInitialized();
2074
+ await this.ensureConnected();
2032
2075
  if (!this._wallet) {
2033
2076
  return { fee: "0", feeRate: this._config.defaultFeeRate ?? 10 };
2034
2077
  }
@@ -2552,7 +2595,9 @@ var STORAGE_KEYS_GLOBAL = {
2552
2595
  /** Nametag cache per address (separate from tracked addresses registry) */
2553
2596
  ADDRESS_NAMETAGS: "address_nametags",
2554
2597
  /** Active addresses registry (JSON: TrackedAddressesStorage) */
2555
- TRACKED_ADDRESSES: "tracked_addresses"
2598
+ TRACKED_ADDRESSES: "tracked_addresses",
2599
+ /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */
2600
+ LAST_WALLET_EVENT_TS: "last_wallet_event_ts"
2556
2601
  };
2557
2602
  var STORAGE_KEYS_ADDRESS = {
2558
2603
  /** Pending transfers for this address */
@@ -4125,7 +4170,7 @@ async function parseTokenInfo(tokenData) {
4125
4170
  try {
4126
4171
  const sdkToken = await import_Token6.Token.fromJSON(data);
4127
4172
  if (sdkToken.id) {
4128
- defaultInfo.tokenId = sdkToken.id.toString();
4173
+ defaultInfo.tokenId = sdkToken.id.toJSON();
4129
4174
  }
4130
4175
  if (sdkToken.coins && sdkToken.coins.coins) {
4131
4176
  const rawCoins = sdkToken.coins.coins;
@@ -4416,8 +4461,7 @@ var PaymentsModule = class _PaymentsModule {
4416
4461
  maxRetries: config?.maxRetries ?? 3,
4417
4462
  debug: config?.debug ?? false
4418
4463
  };
4419
- const l1Enabled = config?.l1?.electrumUrl && config.l1.electrumUrl.length > 0;
4420
- this.l1 = l1Enabled ? new L1PaymentsModule(config?.l1) : null;
4464
+ this.l1 = config?.l1 === null ? null : new L1PaymentsModule(config?.l1);
4421
4465
  }
4422
4466
  /** Get module configuration */
4423
4467
  getConfig() {
@@ -5079,13 +5123,16 @@ var PaymentsModule = class _PaymentsModule {
5079
5123
  if (this.paymentRequests.find((r) => r.id === transportRequest.id)) {
5080
5124
  return;
5081
5125
  }
5126
+ const coinId = transportRequest.request.coinId;
5127
+ const registry = TokenRegistry.getInstance();
5128
+ const coinDef = registry.getDefinition(coinId);
5082
5129
  const request = {
5083
5130
  id: transportRequest.id,
5084
5131
  senderPubkey: transportRequest.senderTransportPubkey,
5132
+ senderNametag: transportRequest.senderNametag,
5085
5133
  amount: transportRequest.request.amount,
5086
- coinId: transportRequest.request.coinId,
5087
- symbol: transportRequest.request.coinId,
5088
- // Use coinId as symbol for now
5134
+ coinId,
5135
+ symbol: coinDef?.symbol || coinId.slice(0, 8),
5089
5136
  message: transportRequest.request.message,
5090
5137
  recipientNametag: transportRequest.request.recipientNametag,
5091
5138
  requestId: transportRequest.request.requestId,
@@ -8019,8 +8066,8 @@ var Sphere = class _Sphere {
8019
8066
  if (options.nametag) {
8020
8067
  await sphere.registerNametag(options.nametag);
8021
8068
  } else {
8022
- await sphere.syncIdentityWithTransport();
8023
8069
  await sphere.recoverNametagFromTransport();
8070
+ await sphere.syncIdentityWithTransport();
8024
8071
  }
8025
8072
  return sphere;
8026
8073
  }
@@ -8067,9 +8114,14 @@ var Sphere = class _Sphere {
8067
8114
  if (!options.mnemonic && !options.masterKey) {
8068
8115
  throw new Error("Either mnemonic or masterKey is required");
8069
8116
  }
8117
+ console.log("[Sphere.import] Starting import...");
8118
+ console.log("[Sphere.import] Clearing existing wallet data...");
8070
8119
  await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
8120
+ console.log("[Sphere.import] Clear done");
8071
8121
  if (!options.storage.isConnected()) {
8122
+ console.log("[Sphere.import] Reconnecting storage...");
8072
8123
  await options.storage.connect();
8124
+ console.log("[Sphere.import] Storage reconnected");
8073
8125
  }
8074
8126
  const sphere = new _Sphere(
8075
8127
  options.storage,
@@ -8083,9 +8135,12 @@ var Sphere = class _Sphere {
8083
8135
  if (!_Sphere.validateMnemonic(options.mnemonic)) {
8084
8136
  throw new Error("Invalid mnemonic");
8085
8137
  }
8138
+ console.log("[Sphere.import] Storing mnemonic...");
8086
8139
  await sphere.storeMnemonic(options.mnemonic, options.derivationPath, options.basePath);
8140
+ console.log("[Sphere.import] Initializing identity from mnemonic...");
8087
8141
  await sphere.initializeIdentityFromMnemonic(options.mnemonic, options.derivationPath);
8088
8142
  } else if (options.masterKey) {
8143
+ console.log("[Sphere.import] Storing master key...");
8089
8144
  await sphere.storeMasterKey(
8090
8145
  options.masterKey,
8091
8146
  options.chainCode,
@@ -8093,24 +8148,35 @@ var Sphere = class _Sphere {
8093
8148
  options.basePath,
8094
8149
  options.derivationMode
8095
8150
  );
8151
+ console.log("[Sphere.import] Initializing identity from master key...");
8096
8152
  await sphere.initializeIdentityFromMasterKey(
8097
8153
  options.masterKey,
8098
8154
  options.chainCode,
8099
8155
  options.derivationPath
8100
8156
  );
8101
8157
  }
8158
+ console.log("[Sphere.import] Initializing providers...");
8102
8159
  await sphere.initializeProviders();
8160
+ console.log("[Sphere.import] Providers initialized. Initializing modules...");
8103
8161
  await sphere.initializeModules();
8162
+ console.log("[Sphere.import] Modules initialized");
8104
8163
  if (!options.nametag) {
8164
+ console.log("[Sphere.import] Recovering nametag from transport...");
8105
8165
  await sphere.recoverNametagFromTransport();
8166
+ console.log("[Sphere.import] Nametag recovery done");
8167
+ await sphere.syncIdentityWithTransport();
8106
8168
  }
8169
+ console.log("[Sphere.import] Finalizing wallet creation...");
8107
8170
  await sphere.finalizeWalletCreation();
8108
8171
  sphere._initialized = true;
8109
8172
  _Sphere.instance = sphere;
8173
+ console.log("[Sphere.import] Tracking address 0...");
8110
8174
  await sphere.ensureAddressTracked(0);
8111
8175
  if (options.nametag) {
8176
+ console.log("[Sphere.import] Registering nametag...");
8112
8177
  await sphere.registerNametag(options.nametag);
8113
8178
  }
8179
+ console.log("[Sphere.import] Import complete");
8114
8180
  return sphere;
8115
8181
  }
8116
8182
  /**
@@ -8135,6 +8201,10 @@ var Sphere = class _Sphere {
8135
8201
  static async clear(storageOrOptions) {
8136
8202
  const storage = "get" in storageOrOptions ? storageOrOptions : storageOrOptions.storage;
8137
8203
  const tokenStorage = "get" in storageOrOptions ? void 0 : storageOrOptions.tokenStorage;
8204
+ if (!storage.isConnected()) {
8205
+ await storage.connect();
8206
+ }
8207
+ console.log("[Sphere.clear] Removing storage keys...");
8138
8208
  await storage.remove(STORAGE_KEYS_GLOBAL.MNEMONIC);
8139
8209
  await storage.remove(STORAGE_KEYS_GLOBAL.MASTER_KEY);
8140
8210
  await storage.remove(STORAGE_KEYS_GLOBAL.CHAIN_CODE);
@@ -8147,12 +8217,30 @@ var Sphere = class _Sphere {
8147
8217
  await storage.remove(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);
8148
8218
  await storage.remove(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
8149
8219
  await storage.remove(STORAGE_KEYS_ADDRESS.OUTBOX);
8220
+ console.log("[Sphere.clear] Storage keys removed");
8150
8221
  if (tokenStorage?.clear) {
8151
- await tokenStorage.clear();
8222
+ console.log("[Sphere.clear] Clearing token storage...");
8223
+ try {
8224
+ await Promise.race([
8225
+ tokenStorage.clear(),
8226
+ new Promise(
8227
+ (_, reject) => setTimeout(() => reject(new Error("tokenStorage.clear() timed out after 2s")), 2e3)
8228
+ )
8229
+ ]);
8230
+ console.log("[Sphere.clear] Token storage cleared");
8231
+ } catch (err) {
8232
+ console.warn("[Sphere.clear] Token storage clear failed/timed out:", err);
8233
+ }
8152
8234
  }
8235
+ console.log("[Sphere.clear] Destroying vesting classifier...");
8153
8236
  await vestingClassifier.destroy();
8237
+ console.log("[Sphere.clear] Vesting classifier destroyed");
8154
8238
  if (_Sphere.instance) {
8239
+ console.log("[Sphere.clear] Destroying Sphere instance...");
8155
8240
  await _Sphere.instance.destroy();
8241
+ console.log("[Sphere.clear] Sphere instance destroyed");
8242
+ } else {
8243
+ console.log("[Sphere.clear] No Sphere instance to destroy");
8156
8244
  }
8157
8245
  }
8158
8246
  /**
@@ -8533,7 +8621,8 @@ var Sphere = class _Sphere {
8533
8621
  storage: options.storage,
8534
8622
  transport: options.transport,
8535
8623
  oracle: options.oracle,
8536
- tokenStorage: options.tokenStorage
8624
+ tokenStorage: options.tokenStorage,
8625
+ l1: options.l1
8537
8626
  });
8538
8627
  return { success: true, mnemonic };
8539
8628
  }
@@ -8546,7 +8635,8 @@ var Sphere = class _Sphere {
8546
8635
  storage: options.storage,
8547
8636
  transport: options.transport,
8548
8637
  oracle: options.oracle,
8549
- tokenStorage: options.tokenStorage
8638
+ tokenStorage: options.tokenStorage,
8639
+ l1: options.l1
8550
8640
  });
8551
8641
  return { success: true };
8552
8642
  }
@@ -8605,7 +8695,8 @@ var Sphere = class _Sphere {
8605
8695
  transport: options.transport,
8606
8696
  oracle: options.oracle,
8607
8697
  tokenStorage: options.tokenStorage,
8608
- nametag: options.nametag
8698
+ nametag: options.nametag,
8699
+ l1: options.l1
8609
8700
  });
8610
8701
  return { success: true, sphere, mnemonic };
8611
8702
  }
@@ -8634,7 +8725,8 @@ var Sphere = class _Sphere {
8634
8725
  transport: options.transport,
8635
8726
  oracle: options.oracle,
8636
8727
  tokenStorage: options.tokenStorage,
8637
- nametag: options.nametag
8728
+ nametag: options.nametag,
8729
+ l1: options.l1
8638
8730
  });
8639
8731
  return { success: true, sphere };
8640
8732
  }
@@ -8665,7 +8757,8 @@ var Sphere = class _Sphere {
8665
8757
  transport: options.transport,
8666
8758
  oracle: options.oracle,
8667
8759
  tokenStorage: options.tokenStorage,
8668
- nametag: options.nametag
8760
+ nametag: options.nametag,
8761
+ l1: options.l1
8669
8762
  });
8670
8763
  return { success: true, sphere };
8671
8764
  }
@@ -8684,7 +8777,8 @@ var Sphere = class _Sphere {
8684
8777
  storage: options.storage,
8685
8778
  transport: options.transport,
8686
8779
  oracle: options.oracle,
8687
- tokenStorage: options.tokenStorage
8780
+ tokenStorage: options.tokenStorage,
8781
+ l1: options.l1
8688
8782
  });
8689
8783
  if (result.success) {
8690
8784
  const sphere2 = _Sphere.getInstance();
@@ -8733,7 +8827,8 @@ var Sphere = class _Sphere {
8733
8827
  transport: options.transport,
8734
8828
  oracle: options.oracle,
8735
8829
  tokenStorage: options.tokenStorage,
8736
- nametag: options.nametag
8830
+ nametag: options.nametag,
8831
+ l1: options.l1
8737
8832
  });
8738
8833
  return { success: true, sphere: sphere2, mnemonic };
8739
8834
  }
@@ -8746,7 +8841,8 @@ var Sphere = class _Sphere {
8746
8841
  transport: options.transport,
8747
8842
  oracle: options.oracle,
8748
8843
  tokenStorage: options.tokenStorage,
8749
- nametag: options.nametag
8844
+ nametag: options.nametag,
8845
+ l1: options.l1
8750
8846
  });
8751
8847
  return { success: true, sphere };
8752
8848
  }
@@ -9609,35 +9705,40 @@ var Sphere = class _Sphere {
9609
9705
  if (this._identity?.nametag) {
9610
9706
  return;
9611
9707
  }
9612
- if (!this._transport.recoverNametag) {
9708
+ let recoveredNametag = null;
9709
+ if (this._transport.recoverNametag) {
9710
+ try {
9711
+ recoveredNametag = await this._transport.recoverNametag();
9712
+ } catch {
9713
+ }
9714
+ }
9715
+ if (!recoveredNametag && this._transport.resolveAddressInfo && this._identity?.l1Address) {
9716
+ try {
9717
+ const info = await this._transport.resolveAddressInfo(this._identity.l1Address);
9718
+ if (info?.nametag) {
9719
+ recoveredNametag = info.nametag;
9720
+ }
9721
+ } catch {
9722
+ }
9723
+ }
9724
+ if (!recoveredNametag) {
9613
9725
  return;
9614
9726
  }
9615
9727
  try {
9616
- const recoveredNametag = await this._transport.recoverNametag();
9617
- if (recoveredNametag) {
9618
- if (this._identity) {
9619
- this._identity.nametag = recoveredNametag;
9620
- await this._updateCachedProxyAddress();
9621
- }
9622
- const entry = await this.ensureAddressTracked(this._currentAddressIndex);
9623
- let nametags = this._addressNametags.get(entry.addressId);
9624
- if (!nametags) {
9625
- nametags = /* @__PURE__ */ new Map();
9626
- this._addressNametags.set(entry.addressId, nametags);
9627
- }
9628
- const nextIndex = nametags.size;
9629
- nametags.set(nextIndex, recoveredNametag);
9630
- await this.persistAddressNametags();
9631
- if (this._transport.publishIdentityBinding) {
9632
- await this._transport.publishIdentityBinding(
9633
- this._identity.chainPubkey,
9634
- this._identity.l1Address,
9635
- this._identity.directAddress || "",
9636
- recoveredNametag
9637
- );
9638
- }
9639
- this.emitEvent("nametag:recovered", { nametag: recoveredNametag });
9728
+ if (this._identity) {
9729
+ this._identity.nametag = recoveredNametag;
9730
+ await this._updateCachedProxyAddress();
9640
9731
  }
9732
+ const entry = await this.ensureAddressTracked(this._currentAddressIndex);
9733
+ let nametags = this._addressNametags.get(entry.addressId);
9734
+ if (!nametags) {
9735
+ nametags = /* @__PURE__ */ new Map();
9736
+ this._addressNametags.set(entry.addressId, nametags);
9737
+ }
9738
+ const nextIndex = nametags.size;
9739
+ nametags.set(nextIndex, recoveredNametag);
9740
+ await this.persistAddressNametags();
9741
+ this.emitEvent("nametag:recovered", { nametag: recoveredNametag });
9641
9742
  } catch {
9642
9743
  }
9643
9744
  }
@@ -9846,8 +9947,12 @@ var Sphere = class _Sphere {
9846
9947
  for (const provider of this._tokenStorageProviders.values()) {
9847
9948
  provider.setIdentity(this._identity);
9848
9949
  }
9849
- await this._storage.connect();
9850
- await this._transport.connect();
9950
+ if (!this._storage.isConnected()) {
9951
+ await this._storage.connect();
9952
+ }
9953
+ if (!this._transport.isConnected()) {
9954
+ await this._transport.connect();
9955
+ }
9851
9956
  await this._oracle.initialize();
9852
9957
  for (const provider of this._tokenStorageProviders.values()) {
9853
9958
  await provider.initialize();