@unicitylabs/sphere-sdk 0.4.8 → 0.5.0

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
@@ -663,6 +663,8 @@ function disconnect() {
663
663
  if (cb.timeoutId) clearTimeout(cb.timeoutId);
664
664
  });
665
665
  connectionCallbacks.length = 0;
666
+ blockSubscribers.length = 0;
667
+ lastBlockHeader = null;
666
668
  }
667
669
  var DEFAULT_ENDPOINT, ws, isConnected, isConnecting, requestId, intentionalClose, reconnectAttempts, isBlockSubscribed, lastBlockHeader, pending, blockSubscribers, connectionCallbacks, MAX_RECONNECT_ATTEMPTS, BASE_DELAY, MAX_DELAY, RPC_TIMEOUT, CONNECTION_TIMEOUT;
668
670
  var init_network = __esm({
@@ -3872,7 +3874,7 @@ var InstantSplitExecutor = class {
3872
3874
  token: JSON.stringify(bundle),
3873
3875
  proof: null,
3874
3876
  // Proof is included in the bundle
3875
- memo: "INSTANT_SPLIT_V5",
3877
+ memo: options?.memo,
3876
3878
  sender: {
3877
3879
  transportPubkey: senderPubkey
3878
3880
  }
@@ -4431,6 +4433,11 @@ var import_MintCommitment3 = require("@unicitylabs/state-transition-sdk/lib/tran
4431
4433
  var import_MintTransactionData3 = require("@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData");
4432
4434
  var import_InclusionProofUtils5 = require("@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils");
4433
4435
  var import_InclusionProof = require("@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof");
4436
+ function computeHistoryDedupKey(type, tokenId, transferId) {
4437
+ if (type === "SENT" && transferId) return `${type}_transfer_${transferId}`;
4438
+ if (tokenId) return `${type}_${tokenId}`;
4439
+ return `${type}_${crypto.randomUUID()}`;
4440
+ }
4434
4441
  function enrichWithRegistry(info) {
4435
4442
  const registry = TokenRegistry.getInstance();
4436
4443
  const def = registry.getDefinition(info.coinId);
@@ -4729,7 +4736,7 @@ var PaymentsModule = class _PaymentsModule {
4729
4736
  tombstones = [];
4730
4737
  archivedTokens = /* @__PURE__ */ new Map();
4731
4738
  forkedTokens = /* @__PURE__ */ new Map();
4732
- transactionHistory = [];
4739
+ _historyCache = [];
4733
4740
  nametags = [];
4734
4741
  // Payment Requests State (Incoming)
4735
4742
  paymentRequests = [];
@@ -4798,7 +4805,7 @@ var PaymentsModule = class _PaymentsModule {
4798
4805
  this.tombstones = [];
4799
4806
  this.archivedTokens.clear();
4800
4807
  this.forkedTokens.clear();
4801
- this.transactionHistory = [];
4808
+ this._historyCache = [];
4802
4809
  this.nametags = [];
4803
4810
  this.deps = deps;
4804
4811
  this.priceProvider = deps.price ?? null;
@@ -4849,14 +4856,7 @@ var PaymentsModule = class _PaymentsModule {
4849
4856
  }
4850
4857
  }
4851
4858
  await this.loadPendingV5Tokens();
4852
- const historyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
4853
- if (historyData) {
4854
- try {
4855
- this.transactionHistory = JSON.parse(historyData);
4856
- } catch {
4857
- this.transactionHistory = [];
4858
- }
4859
- }
4859
+ await this.loadHistory();
4860
4860
  const pending2 = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
4861
4861
  if (pending2) {
4862
4862
  const transfers = JSON.parse(pending2);
@@ -4943,7 +4943,7 @@ var PaymentsModule = class _PaymentsModule {
4943
4943
  }
4944
4944
  await this.saveToOutbox(result, recipientPubkey);
4945
4945
  result.status = "submitted";
4946
- const recipientNametag = request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0;
4946
+ const recipientNametag = peerInfo?.nametag || (request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0);
4947
4947
  const transferMode = request.transferMode ?? "instant";
4948
4948
  if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
4949
4949
  if (transferMode === "conservative") {
@@ -4974,7 +4974,7 @@ var PaymentsModule = class _PaymentsModule {
4974
4974
  updatedAt: Date.now(),
4975
4975
  sdkData: JSON.stringify(changeTokenData)
4976
4976
  };
4977
- await this.addToken(changeUiToken, true);
4977
+ await this.addToken(changeUiToken);
4978
4978
  this.log(`Conservative split: change token saved: ${changeUiToken.id}`);
4979
4979
  await this.deps.transport.sendTokenTransfer(recipientPubkey, {
4980
4980
  sourceToken: JSON.stringify(splitResult.tokenForRecipient.toJSON()),
@@ -4983,7 +4983,7 @@ var PaymentsModule = class _PaymentsModule {
4983
4983
  });
4984
4984
  const splitCommitmentRequestId = splitResult.recipientTransferTx?.data?.requestId ?? splitResult.recipientTransferTx?.requestId;
4985
4985
  const splitRequestIdHex = splitCommitmentRequestId instanceof Uint8Array ? Array.from(splitCommitmentRequestId).map((b) => b.toString(16).padStart(2, "0")).join("") : splitCommitmentRequestId ? String(splitCommitmentRequestId) : void 0;
4986
- await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag, true);
4986
+ await this.removeToken(splitPlan.tokenToSplit.uiToken.id);
4987
4987
  result.tokenTransfers.push({
4988
4988
  sourceTokenId: splitPlan.tokenToSplit.uiToken.id,
4989
4989
  method: "split",
@@ -5008,6 +5008,7 @@ var PaymentsModule = class _PaymentsModule {
5008
5008
  this.deps.transport,
5009
5009
  recipientPubkey,
5010
5010
  {
5011
+ memo: request.memo,
5011
5012
  onChangeTokenCreated: async (changeToken) => {
5012
5013
  const changeTokenData = changeToken.toJSON();
5013
5014
  const uiToken = {
@@ -5023,7 +5024,7 @@ var PaymentsModule = class _PaymentsModule {
5023
5024
  updatedAt: Date.now(),
5024
5025
  sdkData: JSON.stringify(changeTokenData)
5025
5026
  };
5026
- await this.addToken(uiToken, true);
5027
+ await this.addToken(uiToken);
5027
5028
  this.log(`Change token saved via background: ${uiToken.id}`);
5028
5029
  },
5029
5030
  onStorageSync: async () => {
@@ -5038,7 +5039,7 @@ var PaymentsModule = class _PaymentsModule {
5038
5039
  if (instantResult.backgroundPromise) {
5039
5040
  this.pendingBackgroundTasks.push(instantResult.backgroundPromise);
5040
5041
  }
5041
- await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag);
5042
+ await this.removeToken(splitPlan.tokenToSplit.uiToken.id);
5042
5043
  result.tokenTransfers.push({
5043
5044
  sourceTokenId: splitPlan.tokenToSplit.uiToken.id,
5044
5045
  method: "split",
@@ -5085,20 +5086,25 @@ var PaymentsModule = class _PaymentsModule {
5085
5086
  requestIdHex
5086
5087
  });
5087
5088
  this.log(`Token ${token.id} sent via ${transferMode.toUpperCase()}, requestId: ${requestIdHex}`);
5088
- await this.removeToken(token.id, recipientNametag, true);
5089
+ await this.removeToken(token.id);
5089
5090
  }
5090
5091
  result.status = "delivered";
5091
5092
  await this.save();
5092
5093
  await this.removeFromOutbox(result.id);
5093
5094
  result.status = "completed";
5095
+ const sentTokenId = result.tokens[0] ? extractTokenIdFromSdkData(result.tokens[0].sdkData) : void 0;
5094
5096
  await this.addToHistory({
5095
5097
  type: "SENT",
5096
5098
  amount: request.amount,
5097
5099
  coinId: request.coinId,
5098
5100
  symbol: this.getCoinSymbol(request.coinId),
5099
5101
  timestamp: Date.now(),
5102
+ recipientPubkey,
5100
5103
  recipientNametag,
5101
- transferId: result.id
5104
+ recipientAddress: peerInfo?.directAddress || recipientAddress?.toString() || recipientPubkey,
5105
+ memo: request.memo,
5106
+ transferId: result.id,
5107
+ tokenId: sentTokenId || void 0
5102
5108
  });
5103
5109
  this.deps.emitEvent("transfer:confirmed", result);
5104
5110
  return result;
@@ -5209,6 +5215,7 @@ var PaymentsModule = class _PaymentsModule {
5209
5215
  recipientPubkey,
5210
5216
  {
5211
5217
  ...options,
5218
+ memo: request.memo,
5212
5219
  onChangeTokenCreated: async (changeToken) => {
5213
5220
  const changeTokenData = changeToken.toJSON();
5214
5221
  const uiToken = {
@@ -5224,7 +5231,7 @@ var PaymentsModule = class _PaymentsModule {
5224
5231
  updatedAt: Date.now(),
5225
5232
  sdkData: JSON.stringify(changeTokenData)
5226
5233
  };
5227
- await this.addToken(uiToken, true);
5234
+ await this.addToken(uiToken);
5228
5235
  this.log(`Change token saved via background: ${uiToken.id}`);
5229
5236
  },
5230
5237
  onStorageSync: async () => {
@@ -5237,15 +5244,20 @@ var PaymentsModule = class _PaymentsModule {
5237
5244
  if (result.backgroundPromise) {
5238
5245
  this.pendingBackgroundTasks.push(result.backgroundPromise);
5239
5246
  }
5240
- const recipientNametag = request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0;
5241
- await this.removeToken(tokenToSplit.id, recipientNametag, true);
5247
+ await this.removeToken(tokenToSplit.id);
5248
+ const recipientNametag = peerInfo?.nametag || (request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0);
5249
+ const splitTokenId = extractTokenIdFromSdkData(tokenToSplit.sdkData);
5242
5250
  await this.addToHistory({
5243
5251
  type: "SENT",
5244
5252
  amount: request.amount,
5245
5253
  coinId: request.coinId,
5246
5254
  symbol: this.getCoinSymbol(request.coinId),
5247
5255
  timestamp: Date.now(),
5248
- recipientNametag
5256
+ recipientPubkey,
5257
+ recipientNametag,
5258
+ recipientAddress: peerInfo?.directAddress || recipientAddress?.toString() || recipientPubkey,
5259
+ memo: request.memo,
5260
+ tokenId: splitTokenId || void 0
5249
5261
  });
5250
5262
  await this.save();
5251
5263
  } else {
@@ -5276,10 +5288,10 @@ var PaymentsModule = class _PaymentsModule {
5276
5288
  * @param senderPubkey - Sender's public key for verification
5277
5289
  * @returns Processing result with finalized token
5278
5290
  */
5279
- async processInstantSplitBundle(bundle, senderPubkey) {
5291
+ async processInstantSplitBundle(bundle, senderPubkey, memo) {
5280
5292
  this.ensureInitialized();
5281
5293
  if (!isInstantSplitBundleV5(bundle)) {
5282
- return this.processInstantSplitBundleSync(bundle, senderPubkey);
5294
+ return this.processInstantSplitBundleSync(bundle, senderPubkey, memo);
5283
5295
  }
5284
5296
  try {
5285
5297
  const deterministicId = `v5split_${bundle.splitGroupId}`;
@@ -5309,12 +5321,26 @@ var PaymentsModule = class _PaymentsModule {
5309
5321
  updatedAt: Date.now(),
5310
5322
  sdkData: JSON.stringify({ _pendingFinalization: pendingData })
5311
5323
  };
5312
- await this.addToken(uiToken, false);
5324
+ await this.addToken(uiToken);
5313
5325
  this.log(`V5 bundle saved as unconfirmed: ${uiToken.id.slice(0, 8)}...`);
5326
+ const senderInfo = await this.resolveSenderInfo(senderPubkey);
5327
+ await this.addToHistory({
5328
+ type: "RECEIVED",
5329
+ amount: bundle.amount,
5330
+ coinId: bundle.coinId,
5331
+ symbol: uiToken.symbol,
5332
+ timestamp: Date.now(),
5333
+ senderPubkey,
5334
+ ...senderInfo,
5335
+ memo,
5336
+ tokenId: deterministicId
5337
+ });
5314
5338
  this.deps.emitEvent("transfer:incoming", {
5315
5339
  id: bundle.splitGroupId,
5316
5340
  senderPubkey,
5341
+ senderNametag: senderInfo.senderNametag,
5317
5342
  tokens: [uiToken],
5343
+ memo,
5318
5344
  receivedAt: Date.now()
5319
5345
  });
5320
5346
  await this.save();
@@ -5334,7 +5360,7 @@ var PaymentsModule = class _PaymentsModule {
5334
5360
  * Synchronous V4 bundle processing (dev mode only).
5335
5361
  * Kept for backward compatibility with V4 bundles.
5336
5362
  */
5337
- async processInstantSplitBundleSync(bundle, senderPubkey) {
5363
+ async processInstantSplitBundleSync(bundle, senderPubkey, memo) {
5338
5364
  try {
5339
5365
  const signingService = await this.createSigningService();
5340
5366
  const stClient = this.deps.oracle.getStateTransitionClient?.();
@@ -5394,19 +5420,26 @@ var PaymentsModule = class _PaymentsModule {
5394
5420
  sdkData: JSON.stringify(tokenData)
5395
5421
  };
5396
5422
  await this.addToken(uiToken);
5423
+ const receivedTokenId = extractTokenIdFromSdkData(uiToken.sdkData);
5424
+ const senderInfo = await this.resolveSenderInfo(senderPubkey);
5397
5425
  await this.addToHistory({
5398
5426
  type: "RECEIVED",
5399
5427
  amount: bundle.amount,
5400
5428
  coinId: info.coinId,
5401
5429
  symbol: info.symbol,
5402
5430
  timestamp: Date.now(),
5403
- senderPubkey
5431
+ senderPubkey,
5432
+ ...senderInfo,
5433
+ memo,
5434
+ tokenId: receivedTokenId || uiToken.id
5404
5435
  });
5405
5436
  await this.save();
5406
5437
  this.deps.emitEvent("transfer:incoming", {
5407
5438
  id: bundle.splitGroupId,
5408
5439
  senderPubkey,
5440
+ senderNametag: senderInfo.senderNametag,
5409
5441
  tokens: [uiToken],
5442
+ memo,
5410
5443
  receivedAt: Date.now()
5411
5444
  });
5412
5445
  }
@@ -6149,14 +6182,6 @@ var PaymentsModule = class _PaymentsModule {
6149
6182
  sdkData: JSON.stringify(finalizedToken.toJSON())
6150
6183
  };
6151
6184
  this.tokens.set(tokenId, confirmedToken);
6152
- await this.addToHistory({
6153
- type: "RECEIVED",
6154
- amount: confirmedToken.amount,
6155
- coinId: confirmedToken.coinId,
6156
- symbol: confirmedToken.symbol || "UNK",
6157
- timestamp: Date.now(),
6158
- senderPubkey: pending2.senderPubkey
6159
- });
6160
6185
  this.log(`V5 token resolved: ${tokenId.slice(0, 8)}...`);
6161
6186
  return "resolved";
6162
6187
  }
@@ -6340,10 +6365,9 @@ var PaymentsModule = class _PaymentsModule {
6340
6365
  * the old state is archived and replaced with the incoming one.
6341
6366
  *
6342
6367
  * @param token - The token to add.
6343
- * @param skipHistory - When `true`, do not create a `RECEIVED` transaction history entry (default `false`).
6344
6368
  * @returns `true` if the token was added, `false` if rejected as duplicate or tombstoned.
6345
6369
  */
6346
- async addToken(token, skipHistory = false) {
6370
+ async addToken(token) {
6347
6371
  this.ensureInitialized();
6348
6372
  const incomingTokenId = extractTokenIdFromSdkData(token.sdkData);
6349
6373
  const incomingStateHash = extractStateHashFromSdkData(token.sdkData);
@@ -6389,15 +6413,6 @@ var PaymentsModule = class _PaymentsModule {
6389
6413
  }
6390
6414
  this.tokens.set(token.id, token);
6391
6415
  await this.archiveToken(token);
6392
- if (!skipHistory && token.coinId && token.amount) {
6393
- await this.addToHistory({
6394
- type: "RECEIVED",
6395
- amount: token.amount,
6396
- coinId: token.coinId,
6397
- symbol: token.symbol || "UNK",
6398
- timestamp: token.createdAt || Date.now()
6399
- });
6400
- }
6401
6416
  await this.save();
6402
6417
  this.log(`Added token ${token.id}, total: ${this.tokens.size}`);
6403
6418
  return true;
@@ -6424,7 +6439,7 @@ var PaymentsModule = class _PaymentsModule {
6424
6439
  }
6425
6440
  }
6426
6441
  if (!found) {
6427
- await this.addToken(token, true);
6442
+ await this.addToken(token);
6428
6443
  return;
6429
6444
  }
6430
6445
  await this.archiveToken(token);
@@ -6439,10 +6454,8 @@ var PaymentsModule = class _PaymentsModule {
6439
6454
  * entry is created unless `skipHistory` is `true`.
6440
6455
  *
6441
6456
  * @param tokenId - Local UUID of the token to remove.
6442
- * @param recipientNametag - Optional nametag of the transfer recipient (for history).
6443
- * @param skipHistory - When `true`, skip creating a transaction history entry (default `false`).
6444
6457
  */
6445
- async removeToken(tokenId, recipientNametag, skipHistory = false) {
6458
+ async removeToken(tokenId) {
6446
6459
  this.ensureInitialized();
6447
6460
  const token = this.tokens.get(tokenId);
6448
6461
  if (!token) return;
@@ -6460,16 +6473,6 @@ var PaymentsModule = class _PaymentsModule {
6460
6473
  this.log(`Warning: Could not create tombstone for token ${tokenId.slice(0, 8)}... (missing tokenId or stateHash)`);
6461
6474
  }
6462
6475
  this.tokens.delete(tokenId);
6463
- if (!skipHistory && token.coinId && token.amount) {
6464
- await this.addToHistory({
6465
- type: "SENT",
6466
- amount: token.amount,
6467
- coinId: token.coinId,
6468
- symbol: token.symbol || "UNK",
6469
- timestamp: Date.now(),
6470
- recipientNametag
6471
- });
6472
- }
6473
6476
  await this.save();
6474
6477
  }
6475
6478
  // ===========================================================================
@@ -6694,26 +6697,104 @@ var PaymentsModule = class _PaymentsModule {
6694
6697
  * @returns Array of {@link TransactionHistoryEntry} objects in descending timestamp order.
6695
6698
  */
6696
6699
  getHistory() {
6697
- return [...this.transactionHistory].sort((a, b) => b.timestamp - a.timestamp);
6700
+ return [...this._historyCache].sort((a, b) => b.timestamp - a.timestamp);
6701
+ }
6702
+ /**
6703
+ * Best-effort resolve sender's DIRECT address and nametag from their transport pubkey.
6704
+ * Returns empty object if transport doesn't support resolution or lookup fails.
6705
+ */
6706
+ async resolveSenderInfo(senderTransportPubkey) {
6707
+ try {
6708
+ if (this.deps?.transport?.resolveTransportPubkeyInfo) {
6709
+ const peerInfo = await this.deps.transport.resolveTransportPubkeyInfo(senderTransportPubkey);
6710
+ if (peerInfo) {
6711
+ return {
6712
+ senderAddress: peerInfo.directAddress || void 0,
6713
+ senderNametag: peerInfo.nametag || void 0
6714
+ };
6715
+ }
6716
+ }
6717
+ } catch {
6718
+ }
6719
+ return {};
6698
6720
  }
6699
6721
  /**
6700
6722
  * Append an entry to the transaction history.
6701
6723
  *
6702
- * A unique `id` is auto-generated. The entry is immediately persisted to storage.
6724
+ * A unique `id` and `dedupKey` are auto-generated. The entry is persisted to
6725
+ * the local token storage provider's `history` store (IndexedDB / file).
6726
+ * Duplicate entries with the same `dedupKey` are silently ignored (upsert).
6703
6727
  *
6704
- * @param entry - History entry fields (without `id`).
6728
+ * @param entry - History entry fields (without `id` and `dedupKey`).
6705
6729
  */
6706
6730
  async addToHistory(entry) {
6707
6731
  this.ensureInitialized();
6732
+ const dedupKey = computeHistoryDedupKey(entry.type, entry.tokenId, entry.transferId);
6708
6733
  const historyEntry = {
6709
6734
  id: crypto.randomUUID(),
6735
+ dedupKey,
6710
6736
  ...entry
6711
6737
  };
6712
- this.transactionHistory.push(historyEntry);
6713
- await this.deps.storage.set(
6714
- STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,
6715
- JSON.stringify(this.transactionHistory)
6716
- );
6738
+ const provider = this.getLocalTokenStorageProvider();
6739
+ if (provider?.addHistoryEntry) {
6740
+ await provider.addHistoryEntry(historyEntry);
6741
+ }
6742
+ const existingIdx = this._historyCache.findIndex((e) => e.dedupKey === dedupKey);
6743
+ if (existingIdx >= 0) {
6744
+ this._historyCache[existingIdx] = historyEntry;
6745
+ } else {
6746
+ this._historyCache.push(historyEntry);
6747
+ }
6748
+ this.deps.emitEvent("history:updated", historyEntry);
6749
+ }
6750
+ /**
6751
+ * Load history from the local token storage provider into the in-memory cache.
6752
+ * Also performs one-time migration from legacy KV storage.
6753
+ */
6754
+ async loadHistory() {
6755
+ const provider = this.getLocalTokenStorageProvider();
6756
+ if (provider?.getHistoryEntries) {
6757
+ this._historyCache = await provider.getHistoryEntries();
6758
+ const legacyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
6759
+ if (legacyData) {
6760
+ try {
6761
+ const legacyEntries = JSON.parse(legacyData);
6762
+ const records = legacyEntries.map((e) => ({
6763
+ ...e,
6764
+ dedupKey: e.dedupKey || computeHistoryDedupKey(e.type, e.tokenId, e.transferId)
6765
+ }));
6766
+ const imported = await provider.importHistoryEntries?.(records) ?? 0;
6767
+ if (imported > 0) {
6768
+ this._historyCache = await provider.getHistoryEntries();
6769
+ this.log(`Migrated ${imported} history entries from KV to history store`);
6770
+ }
6771
+ await this.deps.storage.remove(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
6772
+ } catch {
6773
+ }
6774
+ }
6775
+ } else {
6776
+ const historyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
6777
+ if (historyData) {
6778
+ try {
6779
+ this._historyCache = JSON.parse(historyData);
6780
+ } catch {
6781
+ this._historyCache = [];
6782
+ }
6783
+ }
6784
+ }
6785
+ }
6786
+ /**
6787
+ * Get the first local token storage provider (for history operations).
6788
+ */
6789
+ getLocalTokenStorageProvider() {
6790
+ const providers = this.getTokenStorageProviders();
6791
+ for (const [, provider] of providers) {
6792
+ if (provider.type === "local") return provider;
6793
+ }
6794
+ for (const [, provider] of providers) {
6795
+ return provider;
6796
+ }
6797
+ return null;
6717
6798
  }
6718
6799
  // ===========================================================================
6719
6800
  // Public API - Nametag
@@ -7256,14 +7337,27 @@ var PaymentsModule = class _PaymentsModule {
7256
7337
  this.tokens.set(token.id, token);
7257
7338
  await this.save();
7258
7339
  this.log(`NOSTR-FIRST: Token ${token.id.slice(0, 8)}... added as submitted (unconfirmed)`);
7340
+ const senderInfo = await this.resolveSenderInfo(transfer.senderTransportPubkey);
7259
7341
  const incomingTransfer = {
7260
7342
  id: transfer.id,
7261
7343
  senderPubkey: transfer.senderTransportPubkey,
7344
+ senderNametag: senderInfo.senderNametag,
7262
7345
  tokens: [token],
7263
7346
  memo: payload.memo,
7264
7347
  receivedAt: transfer.timestamp
7265
7348
  };
7266
7349
  this.deps.emitEvent("transfer:incoming", incomingTransfer);
7350
+ await this.addToHistory({
7351
+ type: "RECEIVED",
7352
+ amount: token.amount,
7353
+ coinId: token.coinId,
7354
+ symbol: token.symbol,
7355
+ timestamp: Date.now(),
7356
+ senderPubkey: transfer.senderTransportPubkey,
7357
+ ...senderInfo,
7358
+ memo: payload.memo,
7359
+ tokenId: nostrTokenId || token.id
7360
+ });
7267
7361
  try {
7268
7362
  const commitment = await import_TransferCommitment4.TransferCommitment.fromJSON(commitmentInput);
7269
7363
  const requestIdBytes = commitment.requestId;
@@ -7281,7 +7375,7 @@ var PaymentsModule = class _PaymentsModule {
7281
7375
  attemptCount: 0,
7282
7376
  lastAttemptAt: 0,
7283
7377
  onProofReceived: async (tokenId) => {
7284
- await this.finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput, transfer.senderTransportPubkey);
7378
+ await this.finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput);
7285
7379
  }
7286
7380
  });
7287
7381
  } catch (err) {
@@ -7340,7 +7434,7 @@ var PaymentsModule = class _PaymentsModule {
7340
7434
  /**
7341
7435
  * Finalize a received token after proof is available
7342
7436
  */
7343
- async finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput, senderPubkey) {
7437
+ async finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput) {
7344
7438
  try {
7345
7439
  const token = this.tokens.get(tokenId);
7346
7440
  if (!token) {
@@ -7388,14 +7482,6 @@ var PaymentsModule = class _PaymentsModule {
7388
7482
  tokens: [finalizedToken],
7389
7483
  tokenTransfers: []
7390
7484
  });
7391
- await this.addToHistory({
7392
- type: "RECEIVED",
7393
- amount: finalizedToken.amount,
7394
- coinId: finalizedToken.coinId,
7395
- symbol: finalizedToken.symbol,
7396
- timestamp: Date.now(),
7397
- senderPubkey
7398
- });
7399
7485
  } catch (error) {
7400
7486
  console.error("[Payments] Failed to finalize received token:", error);
7401
7487
  const token = this.tokens.get(tokenId);
@@ -7426,7 +7512,8 @@ var PaymentsModule = class _PaymentsModule {
7426
7512
  try {
7427
7513
  const result = await this.processInstantSplitBundle(
7428
7514
  instantBundle,
7429
- transfer.senderTransportPubkey
7515
+ transfer.senderTransportPubkey,
7516
+ payload.memo
7430
7517
  );
7431
7518
  if (result.success) {
7432
7519
  this.log("INSTANT_SPLIT processed successfully");
@@ -7544,10 +7631,26 @@ var PaymentsModule = class _PaymentsModule {
7544
7631
  updatedAt: Date.now(),
7545
7632
  sdkData: typeof tokenData === "string" ? tokenData : JSON.stringify(tokenData)
7546
7633
  };
7547
- await this.addToken(token);
7634
+ const added = await this.addToken(token);
7635
+ const senderInfo = await this.resolveSenderInfo(transfer.senderTransportPubkey);
7636
+ if (added) {
7637
+ const incomingTokenId = extractTokenIdFromSdkData(token.sdkData);
7638
+ await this.addToHistory({
7639
+ type: "RECEIVED",
7640
+ amount: token.amount,
7641
+ coinId: token.coinId,
7642
+ symbol: token.symbol,
7643
+ timestamp: Date.now(),
7644
+ senderPubkey: transfer.senderTransportPubkey,
7645
+ ...senderInfo,
7646
+ memo: payload.memo,
7647
+ tokenId: incomingTokenId || token.id
7648
+ });
7649
+ }
7548
7650
  const incomingTransfer = {
7549
7651
  id: transfer.id,
7550
7652
  senderPubkey: transfer.senderTransportPubkey,
7653
+ senderNametag: senderInfo.senderNametag,
7551
7654
  tokens: [token],
7552
7655
  memo: payload.memo,
7553
7656
  receivedAt: transfer.timestamp