@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.
@@ -593,6 +593,8 @@ function disconnect() {
593
593
  if (cb.timeoutId) clearTimeout(cb.timeoutId);
594
594
  });
595
595
  connectionCallbacks.length = 0;
596
+ blockSubscribers.length = 0;
597
+ lastBlockHeader = null;
596
598
  }
597
599
  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;
598
600
  var init_network = __esm({
@@ -3416,7 +3418,7 @@ var InstantSplitExecutor = class {
3416
3418
  token: JSON.stringify(bundle),
3417
3419
  proof: null,
3418
3420
  // Proof is included in the bundle
3419
- memo: "INSTANT_SPLIT_V5",
3421
+ memo: options?.memo,
3420
3422
  sender: {
3421
3423
  transportPubkey: senderPubkey
3422
3424
  }
@@ -3975,6 +3977,11 @@ import { MintCommitment as MintCommitment3 } from "@unicitylabs/state-transition
3975
3977
  import { MintTransactionData as MintTransactionData3 } from "@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData";
3976
3978
  import { waitInclusionProof as waitInclusionProof5 } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils";
3977
3979
  import { InclusionProof } from "@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof";
3980
+ function computeHistoryDedupKey(type, tokenId, transferId) {
3981
+ if (type === "SENT" && transferId) return `${type}_transfer_${transferId}`;
3982
+ if (tokenId) return `${type}_${tokenId}`;
3983
+ return `${type}_${crypto.randomUUID()}`;
3984
+ }
3978
3985
  function enrichWithRegistry(info) {
3979
3986
  const registry = TokenRegistry.getInstance();
3980
3987
  const def = registry.getDefinition(info.coinId);
@@ -4273,7 +4280,7 @@ var PaymentsModule = class _PaymentsModule {
4273
4280
  tombstones = [];
4274
4281
  archivedTokens = /* @__PURE__ */ new Map();
4275
4282
  forkedTokens = /* @__PURE__ */ new Map();
4276
- transactionHistory = [];
4283
+ _historyCache = [];
4277
4284
  nametags = [];
4278
4285
  // Payment Requests State (Incoming)
4279
4286
  paymentRequests = [];
@@ -4342,7 +4349,7 @@ var PaymentsModule = class _PaymentsModule {
4342
4349
  this.tombstones = [];
4343
4350
  this.archivedTokens.clear();
4344
4351
  this.forkedTokens.clear();
4345
- this.transactionHistory = [];
4352
+ this._historyCache = [];
4346
4353
  this.nametags = [];
4347
4354
  this.deps = deps;
4348
4355
  this.priceProvider = deps.price ?? null;
@@ -4393,14 +4400,7 @@ var PaymentsModule = class _PaymentsModule {
4393
4400
  }
4394
4401
  }
4395
4402
  await this.loadPendingV5Tokens();
4396
- const historyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
4397
- if (historyData) {
4398
- try {
4399
- this.transactionHistory = JSON.parse(historyData);
4400
- } catch {
4401
- this.transactionHistory = [];
4402
- }
4403
- }
4403
+ await this.loadHistory();
4404
4404
  const pending2 = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
4405
4405
  if (pending2) {
4406
4406
  const transfers = JSON.parse(pending2);
@@ -4487,7 +4487,7 @@ var PaymentsModule = class _PaymentsModule {
4487
4487
  }
4488
4488
  await this.saveToOutbox(result, recipientPubkey);
4489
4489
  result.status = "submitted";
4490
- const recipientNametag = request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0;
4490
+ const recipientNametag = peerInfo?.nametag || (request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0);
4491
4491
  const transferMode = request.transferMode ?? "instant";
4492
4492
  if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
4493
4493
  if (transferMode === "conservative") {
@@ -4518,7 +4518,7 @@ var PaymentsModule = class _PaymentsModule {
4518
4518
  updatedAt: Date.now(),
4519
4519
  sdkData: JSON.stringify(changeTokenData)
4520
4520
  };
4521
- await this.addToken(changeUiToken, true);
4521
+ await this.addToken(changeUiToken);
4522
4522
  this.log(`Conservative split: change token saved: ${changeUiToken.id}`);
4523
4523
  await this.deps.transport.sendTokenTransfer(recipientPubkey, {
4524
4524
  sourceToken: JSON.stringify(splitResult.tokenForRecipient.toJSON()),
@@ -4527,7 +4527,7 @@ var PaymentsModule = class _PaymentsModule {
4527
4527
  });
4528
4528
  const splitCommitmentRequestId = splitResult.recipientTransferTx?.data?.requestId ?? splitResult.recipientTransferTx?.requestId;
4529
4529
  const splitRequestIdHex = splitCommitmentRequestId instanceof Uint8Array ? Array.from(splitCommitmentRequestId).map((b) => b.toString(16).padStart(2, "0")).join("") : splitCommitmentRequestId ? String(splitCommitmentRequestId) : void 0;
4530
- await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag, true);
4530
+ await this.removeToken(splitPlan.tokenToSplit.uiToken.id);
4531
4531
  result.tokenTransfers.push({
4532
4532
  sourceTokenId: splitPlan.tokenToSplit.uiToken.id,
4533
4533
  method: "split",
@@ -4552,6 +4552,7 @@ var PaymentsModule = class _PaymentsModule {
4552
4552
  this.deps.transport,
4553
4553
  recipientPubkey,
4554
4554
  {
4555
+ memo: request.memo,
4555
4556
  onChangeTokenCreated: async (changeToken) => {
4556
4557
  const changeTokenData = changeToken.toJSON();
4557
4558
  const uiToken = {
@@ -4567,7 +4568,7 @@ var PaymentsModule = class _PaymentsModule {
4567
4568
  updatedAt: Date.now(),
4568
4569
  sdkData: JSON.stringify(changeTokenData)
4569
4570
  };
4570
- await this.addToken(uiToken, true);
4571
+ await this.addToken(uiToken);
4571
4572
  this.log(`Change token saved via background: ${uiToken.id}`);
4572
4573
  },
4573
4574
  onStorageSync: async () => {
@@ -4582,7 +4583,7 @@ var PaymentsModule = class _PaymentsModule {
4582
4583
  if (instantResult.backgroundPromise) {
4583
4584
  this.pendingBackgroundTasks.push(instantResult.backgroundPromise);
4584
4585
  }
4585
- await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag);
4586
+ await this.removeToken(splitPlan.tokenToSplit.uiToken.id);
4586
4587
  result.tokenTransfers.push({
4587
4588
  sourceTokenId: splitPlan.tokenToSplit.uiToken.id,
4588
4589
  method: "split",
@@ -4629,20 +4630,25 @@ var PaymentsModule = class _PaymentsModule {
4629
4630
  requestIdHex
4630
4631
  });
4631
4632
  this.log(`Token ${token.id} sent via ${transferMode.toUpperCase()}, requestId: ${requestIdHex}`);
4632
- await this.removeToken(token.id, recipientNametag, true);
4633
+ await this.removeToken(token.id);
4633
4634
  }
4634
4635
  result.status = "delivered";
4635
4636
  await this.save();
4636
4637
  await this.removeFromOutbox(result.id);
4637
4638
  result.status = "completed";
4639
+ const sentTokenId = result.tokens[0] ? extractTokenIdFromSdkData(result.tokens[0].sdkData) : void 0;
4638
4640
  await this.addToHistory({
4639
4641
  type: "SENT",
4640
4642
  amount: request.amount,
4641
4643
  coinId: request.coinId,
4642
4644
  symbol: this.getCoinSymbol(request.coinId),
4643
4645
  timestamp: Date.now(),
4646
+ recipientPubkey,
4644
4647
  recipientNametag,
4645
- transferId: result.id
4648
+ recipientAddress: peerInfo?.directAddress || recipientAddress?.toString() || recipientPubkey,
4649
+ memo: request.memo,
4650
+ transferId: result.id,
4651
+ tokenId: sentTokenId || void 0
4646
4652
  });
4647
4653
  this.deps.emitEvent("transfer:confirmed", result);
4648
4654
  return result;
@@ -4753,6 +4759,7 @@ var PaymentsModule = class _PaymentsModule {
4753
4759
  recipientPubkey,
4754
4760
  {
4755
4761
  ...options,
4762
+ memo: request.memo,
4756
4763
  onChangeTokenCreated: async (changeToken) => {
4757
4764
  const changeTokenData = changeToken.toJSON();
4758
4765
  const uiToken = {
@@ -4768,7 +4775,7 @@ var PaymentsModule = class _PaymentsModule {
4768
4775
  updatedAt: Date.now(),
4769
4776
  sdkData: JSON.stringify(changeTokenData)
4770
4777
  };
4771
- await this.addToken(uiToken, true);
4778
+ await this.addToken(uiToken);
4772
4779
  this.log(`Change token saved via background: ${uiToken.id}`);
4773
4780
  },
4774
4781
  onStorageSync: async () => {
@@ -4781,15 +4788,20 @@ var PaymentsModule = class _PaymentsModule {
4781
4788
  if (result.backgroundPromise) {
4782
4789
  this.pendingBackgroundTasks.push(result.backgroundPromise);
4783
4790
  }
4784
- const recipientNametag = request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0;
4785
- await this.removeToken(tokenToSplit.id, recipientNametag, true);
4791
+ await this.removeToken(tokenToSplit.id);
4792
+ const recipientNametag = peerInfo?.nametag || (request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0);
4793
+ const splitTokenId = extractTokenIdFromSdkData(tokenToSplit.sdkData);
4786
4794
  await this.addToHistory({
4787
4795
  type: "SENT",
4788
4796
  amount: request.amount,
4789
4797
  coinId: request.coinId,
4790
4798
  symbol: this.getCoinSymbol(request.coinId),
4791
4799
  timestamp: Date.now(),
4792
- recipientNametag
4800
+ recipientPubkey,
4801
+ recipientNametag,
4802
+ recipientAddress: peerInfo?.directAddress || recipientAddress?.toString() || recipientPubkey,
4803
+ memo: request.memo,
4804
+ tokenId: splitTokenId || void 0
4793
4805
  });
4794
4806
  await this.save();
4795
4807
  } else {
@@ -4820,10 +4832,10 @@ var PaymentsModule = class _PaymentsModule {
4820
4832
  * @param senderPubkey - Sender's public key for verification
4821
4833
  * @returns Processing result with finalized token
4822
4834
  */
4823
- async processInstantSplitBundle(bundle, senderPubkey) {
4835
+ async processInstantSplitBundle(bundle, senderPubkey, memo) {
4824
4836
  this.ensureInitialized();
4825
4837
  if (!isInstantSplitBundleV5(bundle)) {
4826
- return this.processInstantSplitBundleSync(bundle, senderPubkey);
4838
+ return this.processInstantSplitBundleSync(bundle, senderPubkey, memo);
4827
4839
  }
4828
4840
  try {
4829
4841
  const deterministicId = `v5split_${bundle.splitGroupId}`;
@@ -4853,12 +4865,26 @@ var PaymentsModule = class _PaymentsModule {
4853
4865
  updatedAt: Date.now(),
4854
4866
  sdkData: JSON.stringify({ _pendingFinalization: pendingData })
4855
4867
  };
4856
- await this.addToken(uiToken, false);
4868
+ await this.addToken(uiToken);
4857
4869
  this.log(`V5 bundle saved as unconfirmed: ${uiToken.id.slice(0, 8)}...`);
4870
+ const senderInfo = await this.resolveSenderInfo(senderPubkey);
4871
+ await this.addToHistory({
4872
+ type: "RECEIVED",
4873
+ amount: bundle.amount,
4874
+ coinId: bundle.coinId,
4875
+ symbol: uiToken.symbol,
4876
+ timestamp: Date.now(),
4877
+ senderPubkey,
4878
+ ...senderInfo,
4879
+ memo,
4880
+ tokenId: deterministicId
4881
+ });
4858
4882
  this.deps.emitEvent("transfer:incoming", {
4859
4883
  id: bundle.splitGroupId,
4860
4884
  senderPubkey,
4885
+ senderNametag: senderInfo.senderNametag,
4861
4886
  tokens: [uiToken],
4887
+ memo,
4862
4888
  receivedAt: Date.now()
4863
4889
  });
4864
4890
  await this.save();
@@ -4878,7 +4904,7 @@ var PaymentsModule = class _PaymentsModule {
4878
4904
  * Synchronous V4 bundle processing (dev mode only).
4879
4905
  * Kept for backward compatibility with V4 bundles.
4880
4906
  */
4881
- async processInstantSplitBundleSync(bundle, senderPubkey) {
4907
+ async processInstantSplitBundleSync(bundle, senderPubkey, memo) {
4882
4908
  try {
4883
4909
  const signingService = await this.createSigningService();
4884
4910
  const stClient = this.deps.oracle.getStateTransitionClient?.();
@@ -4938,19 +4964,26 @@ var PaymentsModule = class _PaymentsModule {
4938
4964
  sdkData: JSON.stringify(tokenData)
4939
4965
  };
4940
4966
  await this.addToken(uiToken);
4967
+ const receivedTokenId = extractTokenIdFromSdkData(uiToken.sdkData);
4968
+ const senderInfo = await this.resolveSenderInfo(senderPubkey);
4941
4969
  await this.addToHistory({
4942
4970
  type: "RECEIVED",
4943
4971
  amount: bundle.amount,
4944
4972
  coinId: info.coinId,
4945
4973
  symbol: info.symbol,
4946
4974
  timestamp: Date.now(),
4947
- senderPubkey
4975
+ senderPubkey,
4976
+ ...senderInfo,
4977
+ memo,
4978
+ tokenId: receivedTokenId || uiToken.id
4948
4979
  });
4949
4980
  await this.save();
4950
4981
  this.deps.emitEvent("transfer:incoming", {
4951
4982
  id: bundle.splitGroupId,
4952
4983
  senderPubkey,
4984
+ senderNametag: senderInfo.senderNametag,
4953
4985
  tokens: [uiToken],
4986
+ memo,
4954
4987
  receivedAt: Date.now()
4955
4988
  });
4956
4989
  }
@@ -5693,14 +5726,6 @@ var PaymentsModule = class _PaymentsModule {
5693
5726
  sdkData: JSON.stringify(finalizedToken.toJSON())
5694
5727
  };
5695
5728
  this.tokens.set(tokenId, confirmedToken);
5696
- await this.addToHistory({
5697
- type: "RECEIVED",
5698
- amount: confirmedToken.amount,
5699
- coinId: confirmedToken.coinId,
5700
- symbol: confirmedToken.symbol || "UNK",
5701
- timestamp: Date.now(),
5702
- senderPubkey: pending2.senderPubkey
5703
- });
5704
5729
  this.log(`V5 token resolved: ${tokenId.slice(0, 8)}...`);
5705
5730
  return "resolved";
5706
5731
  }
@@ -5884,10 +5909,9 @@ var PaymentsModule = class _PaymentsModule {
5884
5909
  * the old state is archived and replaced with the incoming one.
5885
5910
  *
5886
5911
  * @param token - The token to add.
5887
- * @param skipHistory - When `true`, do not create a `RECEIVED` transaction history entry (default `false`).
5888
5912
  * @returns `true` if the token was added, `false` if rejected as duplicate or tombstoned.
5889
5913
  */
5890
- async addToken(token, skipHistory = false) {
5914
+ async addToken(token) {
5891
5915
  this.ensureInitialized();
5892
5916
  const incomingTokenId = extractTokenIdFromSdkData(token.sdkData);
5893
5917
  const incomingStateHash = extractStateHashFromSdkData(token.sdkData);
@@ -5933,15 +5957,6 @@ var PaymentsModule = class _PaymentsModule {
5933
5957
  }
5934
5958
  this.tokens.set(token.id, token);
5935
5959
  await this.archiveToken(token);
5936
- if (!skipHistory && token.coinId && token.amount) {
5937
- await this.addToHistory({
5938
- type: "RECEIVED",
5939
- amount: token.amount,
5940
- coinId: token.coinId,
5941
- symbol: token.symbol || "UNK",
5942
- timestamp: token.createdAt || Date.now()
5943
- });
5944
- }
5945
5960
  await this.save();
5946
5961
  this.log(`Added token ${token.id}, total: ${this.tokens.size}`);
5947
5962
  return true;
@@ -5968,7 +5983,7 @@ var PaymentsModule = class _PaymentsModule {
5968
5983
  }
5969
5984
  }
5970
5985
  if (!found) {
5971
- await this.addToken(token, true);
5986
+ await this.addToken(token);
5972
5987
  return;
5973
5988
  }
5974
5989
  await this.archiveToken(token);
@@ -5983,10 +5998,8 @@ var PaymentsModule = class _PaymentsModule {
5983
5998
  * entry is created unless `skipHistory` is `true`.
5984
5999
  *
5985
6000
  * @param tokenId - Local UUID of the token to remove.
5986
- * @param recipientNametag - Optional nametag of the transfer recipient (for history).
5987
- * @param skipHistory - When `true`, skip creating a transaction history entry (default `false`).
5988
6001
  */
5989
- async removeToken(tokenId, recipientNametag, skipHistory = false) {
6002
+ async removeToken(tokenId) {
5990
6003
  this.ensureInitialized();
5991
6004
  const token = this.tokens.get(tokenId);
5992
6005
  if (!token) return;
@@ -6004,16 +6017,6 @@ var PaymentsModule = class _PaymentsModule {
6004
6017
  this.log(`Warning: Could not create tombstone for token ${tokenId.slice(0, 8)}... (missing tokenId or stateHash)`);
6005
6018
  }
6006
6019
  this.tokens.delete(tokenId);
6007
- if (!skipHistory && token.coinId && token.amount) {
6008
- await this.addToHistory({
6009
- type: "SENT",
6010
- amount: token.amount,
6011
- coinId: token.coinId,
6012
- symbol: token.symbol || "UNK",
6013
- timestamp: Date.now(),
6014
- recipientNametag
6015
- });
6016
- }
6017
6020
  await this.save();
6018
6021
  }
6019
6022
  // ===========================================================================
@@ -6238,26 +6241,104 @@ var PaymentsModule = class _PaymentsModule {
6238
6241
  * @returns Array of {@link TransactionHistoryEntry} objects in descending timestamp order.
6239
6242
  */
6240
6243
  getHistory() {
6241
- return [...this.transactionHistory].sort((a, b) => b.timestamp - a.timestamp);
6244
+ return [...this._historyCache].sort((a, b) => b.timestamp - a.timestamp);
6245
+ }
6246
+ /**
6247
+ * Best-effort resolve sender's DIRECT address and nametag from their transport pubkey.
6248
+ * Returns empty object if transport doesn't support resolution or lookup fails.
6249
+ */
6250
+ async resolveSenderInfo(senderTransportPubkey) {
6251
+ try {
6252
+ if (this.deps?.transport?.resolveTransportPubkeyInfo) {
6253
+ const peerInfo = await this.deps.transport.resolveTransportPubkeyInfo(senderTransportPubkey);
6254
+ if (peerInfo) {
6255
+ return {
6256
+ senderAddress: peerInfo.directAddress || void 0,
6257
+ senderNametag: peerInfo.nametag || void 0
6258
+ };
6259
+ }
6260
+ }
6261
+ } catch {
6262
+ }
6263
+ return {};
6242
6264
  }
6243
6265
  /**
6244
6266
  * Append an entry to the transaction history.
6245
6267
  *
6246
- * A unique `id` is auto-generated. The entry is immediately persisted to storage.
6268
+ * A unique `id` and `dedupKey` are auto-generated. The entry is persisted to
6269
+ * the local token storage provider's `history` store (IndexedDB / file).
6270
+ * Duplicate entries with the same `dedupKey` are silently ignored (upsert).
6247
6271
  *
6248
- * @param entry - History entry fields (without `id`).
6272
+ * @param entry - History entry fields (without `id` and `dedupKey`).
6249
6273
  */
6250
6274
  async addToHistory(entry) {
6251
6275
  this.ensureInitialized();
6276
+ const dedupKey = computeHistoryDedupKey(entry.type, entry.tokenId, entry.transferId);
6252
6277
  const historyEntry = {
6253
6278
  id: crypto.randomUUID(),
6279
+ dedupKey,
6254
6280
  ...entry
6255
6281
  };
6256
- this.transactionHistory.push(historyEntry);
6257
- await this.deps.storage.set(
6258
- STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,
6259
- JSON.stringify(this.transactionHistory)
6260
- );
6282
+ const provider = this.getLocalTokenStorageProvider();
6283
+ if (provider?.addHistoryEntry) {
6284
+ await provider.addHistoryEntry(historyEntry);
6285
+ }
6286
+ const existingIdx = this._historyCache.findIndex((e) => e.dedupKey === dedupKey);
6287
+ if (existingIdx >= 0) {
6288
+ this._historyCache[existingIdx] = historyEntry;
6289
+ } else {
6290
+ this._historyCache.push(historyEntry);
6291
+ }
6292
+ this.deps.emitEvent("history:updated", historyEntry);
6293
+ }
6294
+ /**
6295
+ * Load history from the local token storage provider into the in-memory cache.
6296
+ * Also performs one-time migration from legacy KV storage.
6297
+ */
6298
+ async loadHistory() {
6299
+ const provider = this.getLocalTokenStorageProvider();
6300
+ if (provider?.getHistoryEntries) {
6301
+ this._historyCache = await provider.getHistoryEntries();
6302
+ const legacyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
6303
+ if (legacyData) {
6304
+ try {
6305
+ const legacyEntries = JSON.parse(legacyData);
6306
+ const records = legacyEntries.map((e) => ({
6307
+ ...e,
6308
+ dedupKey: e.dedupKey || computeHistoryDedupKey(e.type, e.tokenId, e.transferId)
6309
+ }));
6310
+ const imported = await provider.importHistoryEntries?.(records) ?? 0;
6311
+ if (imported > 0) {
6312
+ this._historyCache = await provider.getHistoryEntries();
6313
+ this.log(`Migrated ${imported} history entries from KV to history store`);
6314
+ }
6315
+ await this.deps.storage.remove(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
6316
+ } catch {
6317
+ }
6318
+ }
6319
+ } else {
6320
+ const historyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
6321
+ if (historyData) {
6322
+ try {
6323
+ this._historyCache = JSON.parse(historyData);
6324
+ } catch {
6325
+ this._historyCache = [];
6326
+ }
6327
+ }
6328
+ }
6329
+ }
6330
+ /**
6331
+ * Get the first local token storage provider (for history operations).
6332
+ */
6333
+ getLocalTokenStorageProvider() {
6334
+ const providers = this.getTokenStorageProviders();
6335
+ for (const [, provider] of providers) {
6336
+ if (provider.type === "local") return provider;
6337
+ }
6338
+ for (const [, provider] of providers) {
6339
+ return provider;
6340
+ }
6341
+ return null;
6261
6342
  }
6262
6343
  // ===========================================================================
6263
6344
  // Public API - Nametag
@@ -6800,14 +6881,27 @@ var PaymentsModule = class _PaymentsModule {
6800
6881
  this.tokens.set(token.id, token);
6801
6882
  await this.save();
6802
6883
  this.log(`NOSTR-FIRST: Token ${token.id.slice(0, 8)}... added as submitted (unconfirmed)`);
6884
+ const senderInfo = await this.resolveSenderInfo(transfer.senderTransportPubkey);
6803
6885
  const incomingTransfer = {
6804
6886
  id: transfer.id,
6805
6887
  senderPubkey: transfer.senderTransportPubkey,
6888
+ senderNametag: senderInfo.senderNametag,
6806
6889
  tokens: [token],
6807
6890
  memo: payload.memo,
6808
6891
  receivedAt: transfer.timestamp
6809
6892
  };
6810
6893
  this.deps.emitEvent("transfer:incoming", incomingTransfer);
6894
+ await this.addToHistory({
6895
+ type: "RECEIVED",
6896
+ amount: token.amount,
6897
+ coinId: token.coinId,
6898
+ symbol: token.symbol,
6899
+ timestamp: Date.now(),
6900
+ senderPubkey: transfer.senderTransportPubkey,
6901
+ ...senderInfo,
6902
+ memo: payload.memo,
6903
+ tokenId: nostrTokenId || token.id
6904
+ });
6811
6905
  try {
6812
6906
  const commitment = await TransferCommitment4.fromJSON(commitmentInput);
6813
6907
  const requestIdBytes = commitment.requestId;
@@ -6825,7 +6919,7 @@ var PaymentsModule = class _PaymentsModule {
6825
6919
  attemptCount: 0,
6826
6920
  lastAttemptAt: 0,
6827
6921
  onProofReceived: async (tokenId) => {
6828
- await this.finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput, transfer.senderTransportPubkey);
6922
+ await this.finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput);
6829
6923
  }
6830
6924
  });
6831
6925
  } catch (err) {
@@ -6884,7 +6978,7 @@ var PaymentsModule = class _PaymentsModule {
6884
6978
  /**
6885
6979
  * Finalize a received token after proof is available
6886
6980
  */
6887
- async finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput, senderPubkey) {
6981
+ async finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput) {
6888
6982
  try {
6889
6983
  const token = this.tokens.get(tokenId);
6890
6984
  if (!token) {
@@ -6932,14 +7026,6 @@ var PaymentsModule = class _PaymentsModule {
6932
7026
  tokens: [finalizedToken],
6933
7027
  tokenTransfers: []
6934
7028
  });
6935
- await this.addToHistory({
6936
- type: "RECEIVED",
6937
- amount: finalizedToken.amount,
6938
- coinId: finalizedToken.coinId,
6939
- symbol: finalizedToken.symbol,
6940
- timestamp: Date.now(),
6941
- senderPubkey
6942
- });
6943
7029
  } catch (error) {
6944
7030
  console.error("[Payments] Failed to finalize received token:", error);
6945
7031
  const token = this.tokens.get(tokenId);
@@ -6970,7 +7056,8 @@ var PaymentsModule = class _PaymentsModule {
6970
7056
  try {
6971
7057
  const result = await this.processInstantSplitBundle(
6972
7058
  instantBundle,
6973
- transfer.senderTransportPubkey
7059
+ transfer.senderTransportPubkey,
7060
+ payload.memo
6974
7061
  );
6975
7062
  if (result.success) {
6976
7063
  this.log("INSTANT_SPLIT processed successfully");
@@ -7088,10 +7175,26 @@ var PaymentsModule = class _PaymentsModule {
7088
7175
  updatedAt: Date.now(),
7089
7176
  sdkData: typeof tokenData === "string" ? tokenData : JSON.stringify(tokenData)
7090
7177
  };
7091
- await this.addToken(token);
7178
+ const added = await this.addToken(token);
7179
+ const senderInfo = await this.resolveSenderInfo(transfer.senderTransportPubkey);
7180
+ if (added) {
7181
+ const incomingTokenId = extractTokenIdFromSdkData(token.sdkData);
7182
+ await this.addToHistory({
7183
+ type: "RECEIVED",
7184
+ amount: token.amount,
7185
+ coinId: token.coinId,
7186
+ symbol: token.symbol,
7187
+ timestamp: Date.now(),
7188
+ senderPubkey: transfer.senderTransportPubkey,
7189
+ ...senderInfo,
7190
+ memo: payload.memo,
7191
+ tokenId: incomingTokenId || token.id
7192
+ });
7193
+ }
7092
7194
  const incomingTransfer = {
7093
7195
  id: transfer.id,
7094
7196
  senderPubkey: transfer.senderTransportPubkey,
7197
+ senderNametag: senderInfo.senderNametag,
7095
7198
  tokens: [token],
7096
7199
  memo: payload.memo,
7097
7200
  receivedAt: transfer.timestamp