@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.js CHANGED
@@ -646,6 +646,8 @@ function disconnect() {
646
646
  if (cb.timeoutId) clearTimeout(cb.timeoutId);
647
647
  });
648
648
  connectionCallbacks.length = 0;
649
+ blockSubscribers.length = 0;
650
+ lastBlockHeader = null;
649
651
  }
650
652
  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;
651
653
  var init_network = __esm({
@@ -3711,7 +3713,7 @@ var InstantSplitExecutor = class {
3711
3713
  token: JSON.stringify(bundle),
3712
3714
  proof: null,
3713
3715
  // Proof is included in the bundle
3714
- memo: "INSTANT_SPLIT_V5",
3716
+ memo: options?.memo,
3715
3717
  sender: {
3716
3718
  transportPubkey: senderPubkey
3717
3719
  }
@@ -4270,6 +4272,11 @@ import { MintCommitment as MintCommitment3 } from "@unicitylabs/state-transition
4270
4272
  import { MintTransactionData as MintTransactionData3 } from "@unicitylabs/state-transition-sdk/lib/transaction/MintTransactionData";
4271
4273
  import { waitInclusionProof as waitInclusionProof5 } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils";
4272
4274
  import { InclusionProof } from "@unicitylabs/state-transition-sdk/lib/transaction/InclusionProof";
4275
+ function computeHistoryDedupKey(type, tokenId, transferId) {
4276
+ if (type === "SENT" && transferId) return `${type}_transfer_${transferId}`;
4277
+ if (tokenId) return `${type}_${tokenId}`;
4278
+ return `${type}_${crypto.randomUUID()}`;
4279
+ }
4273
4280
  function enrichWithRegistry(info) {
4274
4281
  const registry = TokenRegistry.getInstance();
4275
4282
  const def = registry.getDefinition(info.coinId);
@@ -4568,7 +4575,7 @@ var PaymentsModule = class _PaymentsModule {
4568
4575
  tombstones = [];
4569
4576
  archivedTokens = /* @__PURE__ */ new Map();
4570
4577
  forkedTokens = /* @__PURE__ */ new Map();
4571
- transactionHistory = [];
4578
+ _historyCache = [];
4572
4579
  nametags = [];
4573
4580
  // Payment Requests State (Incoming)
4574
4581
  paymentRequests = [];
@@ -4637,7 +4644,7 @@ var PaymentsModule = class _PaymentsModule {
4637
4644
  this.tombstones = [];
4638
4645
  this.archivedTokens.clear();
4639
4646
  this.forkedTokens.clear();
4640
- this.transactionHistory = [];
4647
+ this._historyCache = [];
4641
4648
  this.nametags = [];
4642
4649
  this.deps = deps;
4643
4650
  this.priceProvider = deps.price ?? null;
@@ -4688,14 +4695,7 @@ var PaymentsModule = class _PaymentsModule {
4688
4695
  }
4689
4696
  }
4690
4697
  await this.loadPendingV5Tokens();
4691
- const historyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
4692
- if (historyData) {
4693
- try {
4694
- this.transactionHistory = JSON.parse(historyData);
4695
- } catch {
4696
- this.transactionHistory = [];
4697
- }
4698
- }
4698
+ await this.loadHistory();
4699
4699
  const pending2 = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
4700
4700
  if (pending2) {
4701
4701
  const transfers = JSON.parse(pending2);
@@ -4782,7 +4782,7 @@ var PaymentsModule = class _PaymentsModule {
4782
4782
  }
4783
4783
  await this.saveToOutbox(result, recipientPubkey);
4784
4784
  result.status = "submitted";
4785
- const recipientNametag = request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0;
4785
+ const recipientNametag = peerInfo?.nametag || (request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0);
4786
4786
  const transferMode = request.transferMode ?? "instant";
4787
4787
  if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
4788
4788
  if (transferMode === "conservative") {
@@ -4813,7 +4813,7 @@ var PaymentsModule = class _PaymentsModule {
4813
4813
  updatedAt: Date.now(),
4814
4814
  sdkData: JSON.stringify(changeTokenData)
4815
4815
  };
4816
- await this.addToken(changeUiToken, true);
4816
+ await this.addToken(changeUiToken);
4817
4817
  this.log(`Conservative split: change token saved: ${changeUiToken.id}`);
4818
4818
  await this.deps.transport.sendTokenTransfer(recipientPubkey, {
4819
4819
  sourceToken: JSON.stringify(splitResult.tokenForRecipient.toJSON()),
@@ -4822,7 +4822,7 @@ var PaymentsModule = class _PaymentsModule {
4822
4822
  });
4823
4823
  const splitCommitmentRequestId = splitResult.recipientTransferTx?.data?.requestId ?? splitResult.recipientTransferTx?.requestId;
4824
4824
  const splitRequestIdHex = splitCommitmentRequestId instanceof Uint8Array ? Array.from(splitCommitmentRequestId).map((b) => b.toString(16).padStart(2, "0")).join("") : splitCommitmentRequestId ? String(splitCommitmentRequestId) : void 0;
4825
- await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag, true);
4825
+ await this.removeToken(splitPlan.tokenToSplit.uiToken.id);
4826
4826
  result.tokenTransfers.push({
4827
4827
  sourceTokenId: splitPlan.tokenToSplit.uiToken.id,
4828
4828
  method: "split",
@@ -4847,6 +4847,7 @@ var PaymentsModule = class _PaymentsModule {
4847
4847
  this.deps.transport,
4848
4848
  recipientPubkey,
4849
4849
  {
4850
+ memo: request.memo,
4850
4851
  onChangeTokenCreated: async (changeToken) => {
4851
4852
  const changeTokenData = changeToken.toJSON();
4852
4853
  const uiToken = {
@@ -4862,7 +4863,7 @@ var PaymentsModule = class _PaymentsModule {
4862
4863
  updatedAt: Date.now(),
4863
4864
  sdkData: JSON.stringify(changeTokenData)
4864
4865
  };
4865
- await this.addToken(uiToken, true);
4866
+ await this.addToken(uiToken);
4866
4867
  this.log(`Change token saved via background: ${uiToken.id}`);
4867
4868
  },
4868
4869
  onStorageSync: async () => {
@@ -4877,7 +4878,7 @@ var PaymentsModule = class _PaymentsModule {
4877
4878
  if (instantResult.backgroundPromise) {
4878
4879
  this.pendingBackgroundTasks.push(instantResult.backgroundPromise);
4879
4880
  }
4880
- await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag);
4881
+ await this.removeToken(splitPlan.tokenToSplit.uiToken.id);
4881
4882
  result.tokenTransfers.push({
4882
4883
  sourceTokenId: splitPlan.tokenToSplit.uiToken.id,
4883
4884
  method: "split",
@@ -4924,20 +4925,25 @@ var PaymentsModule = class _PaymentsModule {
4924
4925
  requestIdHex
4925
4926
  });
4926
4927
  this.log(`Token ${token.id} sent via ${transferMode.toUpperCase()}, requestId: ${requestIdHex}`);
4927
- await this.removeToken(token.id, recipientNametag, true);
4928
+ await this.removeToken(token.id);
4928
4929
  }
4929
4930
  result.status = "delivered";
4930
4931
  await this.save();
4931
4932
  await this.removeFromOutbox(result.id);
4932
4933
  result.status = "completed";
4934
+ const sentTokenId = result.tokens[0] ? extractTokenIdFromSdkData(result.tokens[0].sdkData) : void 0;
4933
4935
  await this.addToHistory({
4934
4936
  type: "SENT",
4935
4937
  amount: request.amount,
4936
4938
  coinId: request.coinId,
4937
4939
  symbol: this.getCoinSymbol(request.coinId),
4938
4940
  timestamp: Date.now(),
4941
+ recipientPubkey,
4939
4942
  recipientNametag,
4940
- transferId: result.id
4943
+ recipientAddress: peerInfo?.directAddress || recipientAddress?.toString() || recipientPubkey,
4944
+ memo: request.memo,
4945
+ transferId: result.id,
4946
+ tokenId: sentTokenId || void 0
4941
4947
  });
4942
4948
  this.deps.emitEvent("transfer:confirmed", result);
4943
4949
  return result;
@@ -5048,6 +5054,7 @@ var PaymentsModule = class _PaymentsModule {
5048
5054
  recipientPubkey,
5049
5055
  {
5050
5056
  ...options,
5057
+ memo: request.memo,
5051
5058
  onChangeTokenCreated: async (changeToken) => {
5052
5059
  const changeTokenData = changeToken.toJSON();
5053
5060
  const uiToken = {
@@ -5063,7 +5070,7 @@ var PaymentsModule = class _PaymentsModule {
5063
5070
  updatedAt: Date.now(),
5064
5071
  sdkData: JSON.stringify(changeTokenData)
5065
5072
  };
5066
- await this.addToken(uiToken, true);
5073
+ await this.addToken(uiToken);
5067
5074
  this.log(`Change token saved via background: ${uiToken.id}`);
5068
5075
  },
5069
5076
  onStorageSync: async () => {
@@ -5076,15 +5083,20 @@ var PaymentsModule = class _PaymentsModule {
5076
5083
  if (result.backgroundPromise) {
5077
5084
  this.pendingBackgroundTasks.push(result.backgroundPromise);
5078
5085
  }
5079
- const recipientNametag = request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0;
5080
- await this.removeToken(tokenToSplit.id, recipientNametag, true);
5086
+ await this.removeToken(tokenToSplit.id);
5087
+ const recipientNametag = peerInfo?.nametag || (request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0);
5088
+ const splitTokenId = extractTokenIdFromSdkData(tokenToSplit.sdkData);
5081
5089
  await this.addToHistory({
5082
5090
  type: "SENT",
5083
5091
  amount: request.amount,
5084
5092
  coinId: request.coinId,
5085
5093
  symbol: this.getCoinSymbol(request.coinId),
5086
5094
  timestamp: Date.now(),
5087
- recipientNametag
5095
+ recipientPubkey,
5096
+ recipientNametag,
5097
+ recipientAddress: peerInfo?.directAddress || recipientAddress?.toString() || recipientPubkey,
5098
+ memo: request.memo,
5099
+ tokenId: splitTokenId || void 0
5088
5100
  });
5089
5101
  await this.save();
5090
5102
  } else {
@@ -5115,10 +5127,10 @@ var PaymentsModule = class _PaymentsModule {
5115
5127
  * @param senderPubkey - Sender's public key for verification
5116
5128
  * @returns Processing result with finalized token
5117
5129
  */
5118
- async processInstantSplitBundle(bundle, senderPubkey) {
5130
+ async processInstantSplitBundle(bundle, senderPubkey, memo) {
5119
5131
  this.ensureInitialized();
5120
5132
  if (!isInstantSplitBundleV5(bundle)) {
5121
- return this.processInstantSplitBundleSync(bundle, senderPubkey);
5133
+ return this.processInstantSplitBundleSync(bundle, senderPubkey, memo);
5122
5134
  }
5123
5135
  try {
5124
5136
  const deterministicId = `v5split_${bundle.splitGroupId}`;
@@ -5148,12 +5160,26 @@ var PaymentsModule = class _PaymentsModule {
5148
5160
  updatedAt: Date.now(),
5149
5161
  sdkData: JSON.stringify({ _pendingFinalization: pendingData })
5150
5162
  };
5151
- await this.addToken(uiToken, false);
5163
+ await this.addToken(uiToken);
5152
5164
  this.log(`V5 bundle saved as unconfirmed: ${uiToken.id.slice(0, 8)}...`);
5165
+ const senderInfo = await this.resolveSenderInfo(senderPubkey);
5166
+ await this.addToHistory({
5167
+ type: "RECEIVED",
5168
+ amount: bundle.amount,
5169
+ coinId: bundle.coinId,
5170
+ symbol: uiToken.symbol,
5171
+ timestamp: Date.now(),
5172
+ senderPubkey,
5173
+ ...senderInfo,
5174
+ memo,
5175
+ tokenId: deterministicId
5176
+ });
5153
5177
  this.deps.emitEvent("transfer:incoming", {
5154
5178
  id: bundle.splitGroupId,
5155
5179
  senderPubkey,
5180
+ senderNametag: senderInfo.senderNametag,
5156
5181
  tokens: [uiToken],
5182
+ memo,
5157
5183
  receivedAt: Date.now()
5158
5184
  });
5159
5185
  await this.save();
@@ -5173,7 +5199,7 @@ var PaymentsModule = class _PaymentsModule {
5173
5199
  * Synchronous V4 bundle processing (dev mode only).
5174
5200
  * Kept for backward compatibility with V4 bundles.
5175
5201
  */
5176
- async processInstantSplitBundleSync(bundle, senderPubkey) {
5202
+ async processInstantSplitBundleSync(bundle, senderPubkey, memo) {
5177
5203
  try {
5178
5204
  const signingService = await this.createSigningService();
5179
5205
  const stClient = this.deps.oracle.getStateTransitionClient?.();
@@ -5233,19 +5259,26 @@ var PaymentsModule = class _PaymentsModule {
5233
5259
  sdkData: JSON.stringify(tokenData)
5234
5260
  };
5235
5261
  await this.addToken(uiToken);
5262
+ const receivedTokenId = extractTokenIdFromSdkData(uiToken.sdkData);
5263
+ const senderInfo = await this.resolveSenderInfo(senderPubkey);
5236
5264
  await this.addToHistory({
5237
5265
  type: "RECEIVED",
5238
5266
  amount: bundle.amount,
5239
5267
  coinId: info.coinId,
5240
5268
  symbol: info.symbol,
5241
5269
  timestamp: Date.now(),
5242
- senderPubkey
5270
+ senderPubkey,
5271
+ ...senderInfo,
5272
+ memo,
5273
+ tokenId: receivedTokenId || uiToken.id
5243
5274
  });
5244
5275
  await this.save();
5245
5276
  this.deps.emitEvent("transfer:incoming", {
5246
5277
  id: bundle.splitGroupId,
5247
5278
  senderPubkey,
5279
+ senderNametag: senderInfo.senderNametag,
5248
5280
  tokens: [uiToken],
5281
+ memo,
5249
5282
  receivedAt: Date.now()
5250
5283
  });
5251
5284
  }
@@ -5988,14 +6021,6 @@ var PaymentsModule = class _PaymentsModule {
5988
6021
  sdkData: JSON.stringify(finalizedToken.toJSON())
5989
6022
  };
5990
6023
  this.tokens.set(tokenId, confirmedToken);
5991
- await this.addToHistory({
5992
- type: "RECEIVED",
5993
- amount: confirmedToken.amount,
5994
- coinId: confirmedToken.coinId,
5995
- symbol: confirmedToken.symbol || "UNK",
5996
- timestamp: Date.now(),
5997
- senderPubkey: pending2.senderPubkey
5998
- });
5999
6024
  this.log(`V5 token resolved: ${tokenId.slice(0, 8)}...`);
6000
6025
  return "resolved";
6001
6026
  }
@@ -6179,10 +6204,9 @@ var PaymentsModule = class _PaymentsModule {
6179
6204
  * the old state is archived and replaced with the incoming one.
6180
6205
  *
6181
6206
  * @param token - The token to add.
6182
- * @param skipHistory - When `true`, do not create a `RECEIVED` transaction history entry (default `false`).
6183
6207
  * @returns `true` if the token was added, `false` if rejected as duplicate or tombstoned.
6184
6208
  */
6185
- async addToken(token, skipHistory = false) {
6209
+ async addToken(token) {
6186
6210
  this.ensureInitialized();
6187
6211
  const incomingTokenId = extractTokenIdFromSdkData(token.sdkData);
6188
6212
  const incomingStateHash = extractStateHashFromSdkData(token.sdkData);
@@ -6228,15 +6252,6 @@ var PaymentsModule = class _PaymentsModule {
6228
6252
  }
6229
6253
  this.tokens.set(token.id, token);
6230
6254
  await this.archiveToken(token);
6231
- if (!skipHistory && token.coinId && token.amount) {
6232
- await this.addToHistory({
6233
- type: "RECEIVED",
6234
- amount: token.amount,
6235
- coinId: token.coinId,
6236
- symbol: token.symbol || "UNK",
6237
- timestamp: token.createdAt || Date.now()
6238
- });
6239
- }
6240
6255
  await this.save();
6241
6256
  this.log(`Added token ${token.id}, total: ${this.tokens.size}`);
6242
6257
  return true;
@@ -6263,7 +6278,7 @@ var PaymentsModule = class _PaymentsModule {
6263
6278
  }
6264
6279
  }
6265
6280
  if (!found) {
6266
- await this.addToken(token, true);
6281
+ await this.addToken(token);
6267
6282
  return;
6268
6283
  }
6269
6284
  await this.archiveToken(token);
@@ -6278,10 +6293,8 @@ var PaymentsModule = class _PaymentsModule {
6278
6293
  * entry is created unless `skipHistory` is `true`.
6279
6294
  *
6280
6295
  * @param tokenId - Local UUID of the token to remove.
6281
- * @param recipientNametag - Optional nametag of the transfer recipient (for history).
6282
- * @param skipHistory - When `true`, skip creating a transaction history entry (default `false`).
6283
6296
  */
6284
- async removeToken(tokenId, recipientNametag, skipHistory = false) {
6297
+ async removeToken(tokenId) {
6285
6298
  this.ensureInitialized();
6286
6299
  const token = this.tokens.get(tokenId);
6287
6300
  if (!token) return;
@@ -6299,16 +6312,6 @@ var PaymentsModule = class _PaymentsModule {
6299
6312
  this.log(`Warning: Could not create tombstone for token ${tokenId.slice(0, 8)}... (missing tokenId or stateHash)`);
6300
6313
  }
6301
6314
  this.tokens.delete(tokenId);
6302
- if (!skipHistory && token.coinId && token.amount) {
6303
- await this.addToHistory({
6304
- type: "SENT",
6305
- amount: token.amount,
6306
- coinId: token.coinId,
6307
- symbol: token.symbol || "UNK",
6308
- timestamp: Date.now(),
6309
- recipientNametag
6310
- });
6311
- }
6312
6315
  await this.save();
6313
6316
  }
6314
6317
  // ===========================================================================
@@ -6533,26 +6536,104 @@ var PaymentsModule = class _PaymentsModule {
6533
6536
  * @returns Array of {@link TransactionHistoryEntry} objects in descending timestamp order.
6534
6537
  */
6535
6538
  getHistory() {
6536
- return [...this.transactionHistory].sort((a, b) => b.timestamp - a.timestamp);
6539
+ return [...this._historyCache].sort((a, b) => b.timestamp - a.timestamp);
6540
+ }
6541
+ /**
6542
+ * Best-effort resolve sender's DIRECT address and nametag from their transport pubkey.
6543
+ * Returns empty object if transport doesn't support resolution or lookup fails.
6544
+ */
6545
+ async resolveSenderInfo(senderTransportPubkey) {
6546
+ try {
6547
+ if (this.deps?.transport?.resolveTransportPubkeyInfo) {
6548
+ const peerInfo = await this.deps.transport.resolveTransportPubkeyInfo(senderTransportPubkey);
6549
+ if (peerInfo) {
6550
+ return {
6551
+ senderAddress: peerInfo.directAddress || void 0,
6552
+ senderNametag: peerInfo.nametag || void 0
6553
+ };
6554
+ }
6555
+ }
6556
+ } catch {
6557
+ }
6558
+ return {};
6537
6559
  }
6538
6560
  /**
6539
6561
  * Append an entry to the transaction history.
6540
6562
  *
6541
- * A unique `id` is auto-generated. The entry is immediately persisted to storage.
6563
+ * A unique `id` and `dedupKey` are auto-generated. The entry is persisted to
6564
+ * the local token storage provider's `history` store (IndexedDB / file).
6565
+ * Duplicate entries with the same `dedupKey` are silently ignored (upsert).
6542
6566
  *
6543
- * @param entry - History entry fields (without `id`).
6567
+ * @param entry - History entry fields (without `id` and `dedupKey`).
6544
6568
  */
6545
6569
  async addToHistory(entry) {
6546
6570
  this.ensureInitialized();
6571
+ const dedupKey = computeHistoryDedupKey(entry.type, entry.tokenId, entry.transferId);
6547
6572
  const historyEntry = {
6548
6573
  id: crypto.randomUUID(),
6574
+ dedupKey,
6549
6575
  ...entry
6550
6576
  };
6551
- this.transactionHistory.push(historyEntry);
6552
- await this.deps.storage.set(
6553
- STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,
6554
- JSON.stringify(this.transactionHistory)
6555
- );
6577
+ const provider = this.getLocalTokenStorageProvider();
6578
+ if (provider?.addHistoryEntry) {
6579
+ await provider.addHistoryEntry(historyEntry);
6580
+ }
6581
+ const existingIdx = this._historyCache.findIndex((e) => e.dedupKey === dedupKey);
6582
+ if (existingIdx >= 0) {
6583
+ this._historyCache[existingIdx] = historyEntry;
6584
+ } else {
6585
+ this._historyCache.push(historyEntry);
6586
+ }
6587
+ this.deps.emitEvent("history:updated", historyEntry);
6588
+ }
6589
+ /**
6590
+ * Load history from the local token storage provider into the in-memory cache.
6591
+ * Also performs one-time migration from legacy KV storage.
6592
+ */
6593
+ async loadHistory() {
6594
+ const provider = this.getLocalTokenStorageProvider();
6595
+ if (provider?.getHistoryEntries) {
6596
+ this._historyCache = await provider.getHistoryEntries();
6597
+ const legacyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
6598
+ if (legacyData) {
6599
+ try {
6600
+ const legacyEntries = JSON.parse(legacyData);
6601
+ const records = legacyEntries.map((e) => ({
6602
+ ...e,
6603
+ dedupKey: e.dedupKey || computeHistoryDedupKey(e.type, e.tokenId, e.transferId)
6604
+ }));
6605
+ const imported = await provider.importHistoryEntries?.(records) ?? 0;
6606
+ if (imported > 0) {
6607
+ this._historyCache = await provider.getHistoryEntries();
6608
+ this.log(`Migrated ${imported} history entries from KV to history store`);
6609
+ }
6610
+ await this.deps.storage.remove(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
6611
+ } catch {
6612
+ }
6613
+ }
6614
+ } else {
6615
+ const historyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
6616
+ if (historyData) {
6617
+ try {
6618
+ this._historyCache = JSON.parse(historyData);
6619
+ } catch {
6620
+ this._historyCache = [];
6621
+ }
6622
+ }
6623
+ }
6624
+ }
6625
+ /**
6626
+ * Get the first local token storage provider (for history operations).
6627
+ */
6628
+ getLocalTokenStorageProvider() {
6629
+ const providers = this.getTokenStorageProviders();
6630
+ for (const [, provider] of providers) {
6631
+ if (provider.type === "local") return provider;
6632
+ }
6633
+ for (const [, provider] of providers) {
6634
+ return provider;
6635
+ }
6636
+ return null;
6556
6637
  }
6557
6638
  // ===========================================================================
6558
6639
  // Public API - Nametag
@@ -7095,14 +7176,27 @@ var PaymentsModule = class _PaymentsModule {
7095
7176
  this.tokens.set(token.id, token);
7096
7177
  await this.save();
7097
7178
  this.log(`NOSTR-FIRST: Token ${token.id.slice(0, 8)}... added as submitted (unconfirmed)`);
7179
+ const senderInfo = await this.resolveSenderInfo(transfer.senderTransportPubkey);
7098
7180
  const incomingTransfer = {
7099
7181
  id: transfer.id,
7100
7182
  senderPubkey: transfer.senderTransportPubkey,
7183
+ senderNametag: senderInfo.senderNametag,
7101
7184
  tokens: [token],
7102
7185
  memo: payload.memo,
7103
7186
  receivedAt: transfer.timestamp
7104
7187
  };
7105
7188
  this.deps.emitEvent("transfer:incoming", incomingTransfer);
7189
+ await this.addToHistory({
7190
+ type: "RECEIVED",
7191
+ amount: token.amount,
7192
+ coinId: token.coinId,
7193
+ symbol: token.symbol,
7194
+ timestamp: Date.now(),
7195
+ senderPubkey: transfer.senderTransportPubkey,
7196
+ ...senderInfo,
7197
+ memo: payload.memo,
7198
+ tokenId: nostrTokenId || token.id
7199
+ });
7106
7200
  try {
7107
7201
  const commitment = await TransferCommitment4.fromJSON(commitmentInput);
7108
7202
  const requestIdBytes = commitment.requestId;
@@ -7120,7 +7214,7 @@ var PaymentsModule = class _PaymentsModule {
7120
7214
  attemptCount: 0,
7121
7215
  lastAttemptAt: 0,
7122
7216
  onProofReceived: async (tokenId) => {
7123
- await this.finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput, transfer.senderTransportPubkey);
7217
+ await this.finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput);
7124
7218
  }
7125
7219
  });
7126
7220
  } catch (err) {
@@ -7179,7 +7273,7 @@ var PaymentsModule = class _PaymentsModule {
7179
7273
  /**
7180
7274
  * Finalize a received token after proof is available
7181
7275
  */
7182
- async finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput, senderPubkey) {
7276
+ async finalizeReceivedToken(tokenId, sourceTokenInput, commitmentInput) {
7183
7277
  try {
7184
7278
  const token = this.tokens.get(tokenId);
7185
7279
  if (!token) {
@@ -7227,14 +7321,6 @@ var PaymentsModule = class _PaymentsModule {
7227
7321
  tokens: [finalizedToken],
7228
7322
  tokenTransfers: []
7229
7323
  });
7230
- await this.addToHistory({
7231
- type: "RECEIVED",
7232
- amount: finalizedToken.amount,
7233
- coinId: finalizedToken.coinId,
7234
- symbol: finalizedToken.symbol,
7235
- timestamp: Date.now(),
7236
- senderPubkey
7237
- });
7238
7324
  } catch (error) {
7239
7325
  console.error("[Payments] Failed to finalize received token:", error);
7240
7326
  const token = this.tokens.get(tokenId);
@@ -7265,7 +7351,8 @@ var PaymentsModule = class _PaymentsModule {
7265
7351
  try {
7266
7352
  const result = await this.processInstantSplitBundle(
7267
7353
  instantBundle,
7268
- transfer.senderTransportPubkey
7354
+ transfer.senderTransportPubkey,
7355
+ payload.memo
7269
7356
  );
7270
7357
  if (result.success) {
7271
7358
  this.log("INSTANT_SPLIT processed successfully");
@@ -7383,10 +7470,26 @@ var PaymentsModule = class _PaymentsModule {
7383
7470
  updatedAt: Date.now(),
7384
7471
  sdkData: typeof tokenData === "string" ? tokenData : JSON.stringify(tokenData)
7385
7472
  };
7386
- await this.addToken(token);
7473
+ const added = await this.addToken(token);
7474
+ const senderInfo = await this.resolveSenderInfo(transfer.senderTransportPubkey);
7475
+ if (added) {
7476
+ const incomingTokenId = extractTokenIdFromSdkData(token.sdkData);
7477
+ await this.addToHistory({
7478
+ type: "RECEIVED",
7479
+ amount: token.amount,
7480
+ coinId: token.coinId,
7481
+ symbol: token.symbol,
7482
+ timestamp: Date.now(),
7483
+ senderPubkey: transfer.senderTransportPubkey,
7484
+ ...senderInfo,
7485
+ memo: payload.memo,
7486
+ tokenId: incomingTokenId || token.id
7487
+ });
7488
+ }
7387
7489
  const incomingTransfer = {
7388
7490
  id: transfer.id,
7389
7491
  senderPubkey: transfer.senderTransportPubkey,
7492
+ senderNametag: senderInfo.senderNametag,
7390
7493
  tokens: [token],
7391
7494
  memo: payload.memo,
7392
7495
  receivedAt: transfer.timestamp