@unicitylabs/sphere-sdk 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connect/index.cjs +3 -1
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.js +3 -1
- package/dist/connect/index.js.map +1 -1
- package/dist/core/index.cjs +205 -41
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +22 -0
- package/dist/core/index.d.ts +22 -0
- package/dist/core/index.js +205 -41
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/connect/index.cjs +3 -1
- package/dist/impl/browser/connect/index.cjs.map +1 -1
- package/dist/impl/browser/connect/index.js +3 -1
- package/dist/impl/browser/connect/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +3 -1
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +3 -1
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs +3 -1
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js +3 -1
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/connect/index.cjs +3 -1
- package/dist/impl/nodejs/connect/index.cjs.map +1 -1
- package/dist/impl/nodejs/connect/index.js +3 -1
- package/dist/impl/nodejs/connect/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +3 -1
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.js +3 -1
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +205 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +205 -41
- package/dist/index.js.map +1 -1
- package/dist/l1/index.cjs +3 -1
- package/dist/l1/index.cjs.map +1 -1
- package/dist/l1/index.js +3 -1
- package/dist/l1/index.js.map +1 -1
- package/package.json +1 -1
package/dist/core/index.cjs
CHANGED
|
@@ -103,7 +103,9 @@ var init_constants = __esm({
|
|
|
103
103
|
/** Group chat: members for this address */
|
|
104
104
|
GROUP_CHAT_MEMBERS: "group_chat_members",
|
|
105
105
|
/** Group chat: processed event IDs for deduplication */
|
|
106
|
-
GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events"
|
|
106
|
+
GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events",
|
|
107
|
+
/** Processed V5 split group IDs for Nostr re-delivery dedup */
|
|
108
|
+
PROCESSED_SPLIT_GROUP_IDS: "processed_split_group_ids"
|
|
107
109
|
};
|
|
108
110
|
STORAGE_KEYS = {
|
|
109
111
|
...STORAGE_KEYS_GLOBAL,
|
|
@@ -4395,6 +4397,17 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4395
4397
|
// Poll every 2s
|
|
4396
4398
|
static PROOF_POLLING_MAX_ATTEMPTS = 30;
|
|
4397
4399
|
// Max 30 attempts (~60s)
|
|
4400
|
+
// Periodic retry for resolveUnconfirmed (V5 lazy finalization)
|
|
4401
|
+
resolveUnconfirmedTimer = null;
|
|
4402
|
+
static RESOLVE_UNCONFIRMED_INTERVAL_MS = 1e4;
|
|
4403
|
+
// Retry every 10s
|
|
4404
|
+
// Guard: ensure load() completes before processing incoming bundles
|
|
4405
|
+
loadedPromise = null;
|
|
4406
|
+
loaded = false;
|
|
4407
|
+
// Persistent dedup: tracks splitGroupIds that have been fully processed.
|
|
4408
|
+
// Survives page reloads via KV storage so Nostr re-deliveries are ignored
|
|
4409
|
+
// even when the confirmed token's in-memory ID differs from v5split_{id}.
|
|
4410
|
+
processedSplitGroupIds = /* @__PURE__ */ new Set();
|
|
4398
4411
|
// Storage event subscriptions (push-based sync)
|
|
4399
4412
|
storageEventUnsubscribers = [];
|
|
4400
4413
|
syncDebounceTimer = null;
|
|
@@ -4480,31 +4493,40 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4480
4493
|
*/
|
|
4481
4494
|
async load() {
|
|
4482
4495
|
this.ensureInitialized();
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4496
|
+
const doLoad = async () => {
|
|
4497
|
+
await TokenRegistry.waitForReady();
|
|
4498
|
+
const providers = this.getTokenStorageProviders();
|
|
4499
|
+
for (const [id, provider] of providers) {
|
|
4500
|
+
try {
|
|
4501
|
+
const result = await provider.load();
|
|
4502
|
+
if (result.success && result.data) {
|
|
4503
|
+
this.loadFromStorageData(result.data);
|
|
4504
|
+
this.log(`Loaded metadata from provider ${id}`);
|
|
4505
|
+
break;
|
|
4506
|
+
}
|
|
4507
|
+
} catch (err) {
|
|
4508
|
+
console.error(`[Payments] Failed to load from provider ${id}:`, err);
|
|
4492
4509
|
}
|
|
4493
|
-
} catch (err) {
|
|
4494
|
-
console.error(`[Payments] Failed to load from provider ${id}:`, err);
|
|
4495
4510
|
}
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
const
|
|
4502
|
-
|
|
4503
|
-
|
|
4511
|
+
const loadedTokens = Array.from(this.tokens.values()).map((t) => `${t.id.slice(0, 12)}(${t.status})`);
|
|
4512
|
+
console.log(`[Payments][DEBUG] load(): from TXF providers: ${this.tokens.size} tokens [${loadedTokens.join(", ")}]`);
|
|
4513
|
+
await this.loadPendingV5Tokens();
|
|
4514
|
+
await this.loadProcessedSplitGroupIds();
|
|
4515
|
+
await this.loadHistory();
|
|
4516
|
+
const pending2 = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
|
|
4517
|
+
if (pending2) {
|
|
4518
|
+
const transfers = JSON.parse(pending2);
|
|
4519
|
+
for (const transfer of transfers) {
|
|
4520
|
+
this.pendingTransfers.set(transfer.id, transfer);
|
|
4521
|
+
}
|
|
4504
4522
|
}
|
|
4505
|
-
|
|
4523
|
+
this.loaded = true;
|
|
4524
|
+
};
|
|
4525
|
+
this.loadedPromise = doLoad();
|
|
4526
|
+
await this.loadedPromise;
|
|
4506
4527
|
this.resolveUnconfirmed().catch(() => {
|
|
4507
4528
|
});
|
|
4529
|
+
this.scheduleResolveUnconfirmed();
|
|
4508
4530
|
}
|
|
4509
4531
|
/**
|
|
4510
4532
|
* Cleanup all subscriptions, polling jobs, and pending resolvers.
|
|
@@ -4523,6 +4545,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4523
4545
|
this.paymentRequestResponseHandlers.clear();
|
|
4524
4546
|
this.stopProofPolling();
|
|
4525
4547
|
this.proofPollingJobs.clear();
|
|
4548
|
+
this.stopResolveUnconfirmedPolling();
|
|
4526
4549
|
for (const [, resolver] of this.pendingResponseResolvers) {
|
|
4527
4550
|
clearTimeout(resolver.timeout);
|
|
4528
4551
|
resolver.reject(new Error("Module destroyed"));
|
|
@@ -4929,13 +4952,16 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4929
4952
|
*/
|
|
4930
4953
|
async processInstantSplitBundle(bundle, senderPubkey, memo) {
|
|
4931
4954
|
this.ensureInitialized();
|
|
4955
|
+
if (!this.loaded && this.loadedPromise) {
|
|
4956
|
+
await this.loadedPromise;
|
|
4957
|
+
}
|
|
4932
4958
|
if (!isInstantSplitBundleV5(bundle)) {
|
|
4933
4959
|
return this.processInstantSplitBundleSync(bundle, senderPubkey, memo);
|
|
4934
4960
|
}
|
|
4935
4961
|
try {
|
|
4936
4962
|
const deterministicId = `v5split_${bundle.splitGroupId}`;
|
|
4937
|
-
if (this.tokens.has(deterministicId)) {
|
|
4938
|
-
|
|
4963
|
+
if (this.tokens.has(deterministicId) || this.processedSplitGroupIds.has(bundle.splitGroupId)) {
|
|
4964
|
+
console.log(`[Payments] V5 bundle ${bundle.splitGroupId.slice(0, 12)}... already processed, skipping`);
|
|
4939
4965
|
return { success: true, durationMs: 0 };
|
|
4940
4966
|
}
|
|
4941
4967
|
const registry = TokenRegistry.getInstance();
|
|
@@ -4961,7 +4987,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4961
4987
|
sdkData: JSON.stringify({ _pendingFinalization: pendingData })
|
|
4962
4988
|
};
|
|
4963
4989
|
await this.addToken(uiToken);
|
|
4964
|
-
this.
|
|
4990
|
+
this.processedSplitGroupIds.add(bundle.splitGroupId);
|
|
4991
|
+
await this.saveProcessedSplitGroupIds();
|
|
4965
4992
|
const senderInfo = await this.resolveSenderInfo(senderPubkey);
|
|
4966
4993
|
await this.addToHistory({
|
|
4967
4994
|
type: "RECEIVED",
|
|
@@ -4985,6 +5012,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4985
5012
|
await this.save();
|
|
4986
5013
|
this.resolveUnconfirmed().catch(() => {
|
|
4987
5014
|
});
|
|
5015
|
+
this.scheduleResolveUnconfirmed();
|
|
4988
5016
|
return { success: true, durationMs: 0 };
|
|
4989
5017
|
} catch (error) {
|
|
4990
5018
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -5731,28 +5759,70 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5731
5759
|
};
|
|
5732
5760
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
5733
5761
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
5734
|
-
if (!stClient || !trustBase)
|
|
5762
|
+
if (!stClient || !trustBase) {
|
|
5763
|
+
console.log(`[V5-RESOLVE] resolveUnconfirmed: EARLY EXIT \u2014 stClient=${!!stClient} trustBase=${!!trustBase}`);
|
|
5764
|
+
return result;
|
|
5765
|
+
}
|
|
5735
5766
|
const signingService = await this.createSigningService();
|
|
5767
|
+
const submittedCount = Array.from(this.tokens.values()).filter((t) => t.status === "submitted").length;
|
|
5768
|
+
console.log(`[V5-RESOLVE] resolveUnconfirmed: ${submittedCount} submitted token(s) to process`);
|
|
5736
5769
|
for (const [tokenId, token] of this.tokens) {
|
|
5737
5770
|
if (token.status !== "submitted") continue;
|
|
5738
5771
|
const pending2 = this.parsePendingFinalization(token.sdkData);
|
|
5739
5772
|
if (!pending2) {
|
|
5773
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 16)}: no pending finalization metadata, skipping`);
|
|
5740
5774
|
result.stillPending++;
|
|
5741
5775
|
continue;
|
|
5742
5776
|
}
|
|
5743
5777
|
if (pending2.type === "v5_bundle") {
|
|
5778
|
+
console.log(`[V5-RESOLVE] Processing ${tokenId.slice(0, 16)}... stage=${pending2.stage} attempt=${pending2.attemptCount}`);
|
|
5744
5779
|
const progress = await this.resolveV5Token(tokenId, token, pending2, stClient, trustBase, signingService);
|
|
5780
|
+
console.log(`[V5-RESOLVE] Result for ${tokenId.slice(0, 16)}...: ${progress} (stage now: ${pending2.stage})`);
|
|
5745
5781
|
result.details.push({ tokenId, stage: pending2.stage, status: progress });
|
|
5746
5782
|
if (progress === "resolved") result.resolved++;
|
|
5747
5783
|
else if (progress === "failed") result.failed++;
|
|
5748
5784
|
else result.stillPending++;
|
|
5749
5785
|
}
|
|
5750
5786
|
}
|
|
5751
|
-
if (result.resolved > 0 || result.failed > 0) {
|
|
5787
|
+
if (result.resolved > 0 || result.failed > 0 || result.stillPending > 0) {
|
|
5788
|
+
console.log(`[V5-RESOLVE] Saving: resolved=${result.resolved} failed=${result.failed} stillPending=${result.stillPending}`);
|
|
5752
5789
|
await this.save();
|
|
5753
5790
|
}
|
|
5754
5791
|
return result;
|
|
5755
5792
|
}
|
|
5793
|
+
/**
|
|
5794
|
+
* Start a periodic interval that retries resolveUnconfirmed() until all
|
|
5795
|
+
* tokens are confirmed or failed. Stops automatically when nothing is
|
|
5796
|
+
* pending and is cleaned up by destroy().
|
|
5797
|
+
*/
|
|
5798
|
+
scheduleResolveUnconfirmed() {
|
|
5799
|
+
if (this.resolveUnconfirmedTimer) return;
|
|
5800
|
+
const hasUnconfirmed = Array.from(this.tokens.values()).some(
|
|
5801
|
+
(t) => t.status === "submitted"
|
|
5802
|
+
);
|
|
5803
|
+
if (!hasUnconfirmed) {
|
|
5804
|
+
console.log(`[V5-RESOLVE] scheduleResolveUnconfirmed: no submitted tokens, not starting timer`);
|
|
5805
|
+
return;
|
|
5806
|
+
}
|
|
5807
|
+
console.log(`[V5-RESOLVE] scheduleResolveUnconfirmed: starting periodic retry (every ${_PaymentsModule.RESOLVE_UNCONFIRMED_INTERVAL_MS}ms)`);
|
|
5808
|
+
this.resolveUnconfirmedTimer = setInterval(async () => {
|
|
5809
|
+
try {
|
|
5810
|
+
const result = await this.resolveUnconfirmed();
|
|
5811
|
+
if (result.stillPending === 0) {
|
|
5812
|
+
console.log(`[V5-RESOLVE] All tokens resolved, stopping periodic retry`);
|
|
5813
|
+
this.stopResolveUnconfirmedPolling();
|
|
5814
|
+
}
|
|
5815
|
+
} catch (err) {
|
|
5816
|
+
console.log(`[V5-RESOLVE] Periodic retry error:`, err);
|
|
5817
|
+
}
|
|
5818
|
+
}, _PaymentsModule.RESOLVE_UNCONFIRMED_INTERVAL_MS);
|
|
5819
|
+
}
|
|
5820
|
+
stopResolveUnconfirmedPolling() {
|
|
5821
|
+
if (this.resolveUnconfirmedTimer) {
|
|
5822
|
+
clearInterval(this.resolveUnconfirmedTimer);
|
|
5823
|
+
this.resolveUnconfirmedTimer = null;
|
|
5824
|
+
}
|
|
5825
|
+
}
|
|
5756
5826
|
// ===========================================================================
|
|
5757
5827
|
// Private - V5 Lazy Resolution Helpers
|
|
5758
5828
|
// ===========================================================================
|
|
@@ -5765,10 +5835,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5765
5835
|
pending2.lastAttemptAt = Date.now();
|
|
5766
5836
|
try {
|
|
5767
5837
|
if (pending2.stage === "RECEIVED") {
|
|
5838
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: RECEIVED \u2192 submitting mint commitment...`);
|
|
5768
5839
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
5769
5840
|
const mintData = await import_MintTransactionData3.MintTransactionData.fromJSON(mintDataJson);
|
|
5770
5841
|
const mintCommitment = await import_MintCommitment3.MintCommitment.create(mintData);
|
|
5771
5842
|
const mintResponse = await stClient.submitMintCommitment(mintCommitment);
|
|
5843
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint response status=${mintResponse.status}`);
|
|
5772
5844
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
5773
5845
|
throw new Error(`Mint submission failed: ${mintResponse.status}`);
|
|
5774
5846
|
}
|
|
@@ -5776,22 +5848,27 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5776
5848
|
this.updatePendingFinalization(token, pending2);
|
|
5777
5849
|
}
|
|
5778
5850
|
if (pending2.stage === "MINT_SUBMITTED") {
|
|
5851
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_SUBMITTED \u2192 checking mint proof...`);
|
|
5779
5852
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
5780
5853
|
const mintData = await import_MintTransactionData3.MintTransactionData.fromJSON(mintDataJson);
|
|
5781
5854
|
const mintCommitment = await import_MintCommitment3.MintCommitment.create(mintData);
|
|
5782
5855
|
const proof = await this.quickProofCheck(stClient, trustBase, mintCommitment);
|
|
5783
5856
|
if (!proof) {
|
|
5857
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof not yet available, staying MINT_SUBMITTED`);
|
|
5784
5858
|
this.updatePendingFinalization(token, pending2);
|
|
5785
5859
|
return "pending";
|
|
5786
5860
|
}
|
|
5861
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof obtained!`);
|
|
5787
5862
|
pending2.mintProofJson = JSON.stringify(proof);
|
|
5788
5863
|
pending2.stage = "MINT_PROVEN";
|
|
5789
5864
|
this.updatePendingFinalization(token, pending2);
|
|
5790
5865
|
}
|
|
5791
5866
|
if (pending2.stage === "MINT_PROVEN") {
|
|
5867
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_PROVEN \u2192 submitting transfer commitment...`);
|
|
5792
5868
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
5793
5869
|
const transferCommitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferCommitmentJson);
|
|
5794
5870
|
const transferResponse = await stClient.submitTransferCommitment(transferCommitment);
|
|
5871
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer response status=${transferResponse.status}`);
|
|
5795
5872
|
if (transferResponse.status !== "SUCCESS" && transferResponse.status !== "REQUEST_ID_EXISTS") {
|
|
5796
5873
|
throw new Error(`Transfer submission failed: ${transferResponse.status}`);
|
|
5797
5874
|
}
|
|
@@ -5799,13 +5876,16 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5799
5876
|
this.updatePendingFinalization(token, pending2);
|
|
5800
5877
|
}
|
|
5801
5878
|
if (pending2.stage === "TRANSFER_SUBMITTED") {
|
|
5879
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: TRANSFER_SUBMITTED \u2192 checking transfer proof...`);
|
|
5802
5880
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
5803
5881
|
const transferCommitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferCommitmentJson);
|
|
5804
5882
|
const proof = await this.quickProofCheck(stClient, trustBase, transferCommitment);
|
|
5805
5883
|
if (!proof) {
|
|
5884
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer proof not yet available, staying TRANSFER_SUBMITTED`);
|
|
5806
5885
|
this.updatePendingFinalization(token, pending2);
|
|
5807
5886
|
return "pending";
|
|
5808
5887
|
}
|
|
5888
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer proof obtained! Finalizing...`);
|
|
5809
5889
|
const finalizedToken = await this.finalizeFromV5Bundle(bundle, pending2, signingService, stClient, trustBase);
|
|
5810
5890
|
const confirmedToken = {
|
|
5811
5891
|
id: token.id,
|
|
@@ -5821,6 +5901,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5821
5901
|
sdkData: JSON.stringify(finalizedToken.toJSON())
|
|
5822
5902
|
};
|
|
5823
5903
|
this.tokens.set(tokenId, confirmedToken);
|
|
5904
|
+
this.deps.emitEvent("transfer:confirmed", {
|
|
5905
|
+
id: crypto.randomUUID(),
|
|
5906
|
+
status: "completed",
|
|
5907
|
+
tokens: [confirmedToken],
|
|
5908
|
+
tokenTransfers: []
|
|
5909
|
+
});
|
|
5824
5910
|
this.log(`V5 token resolved: ${tokenId.slice(0, 8)}...`);
|
|
5825
5911
|
return "resolved";
|
|
5826
5912
|
}
|
|
@@ -5962,11 +6048,20 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5962
6048
|
}
|
|
5963
6049
|
}
|
|
5964
6050
|
if (pendingTokens.length > 0) {
|
|
6051
|
+
const json = JSON.stringify(pendingTokens);
|
|
6052
|
+
this.log(`[V5-PERSIST] Saving ${pendingTokens.length} pending V5 token(s): ${pendingTokens.map((t) => t.id.slice(0, 16)).join(", ")} (${json.length} bytes)`);
|
|
5965
6053
|
await this.deps.storage.set(
|
|
5966
6054
|
STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS,
|
|
5967
|
-
|
|
6055
|
+
json
|
|
5968
6056
|
);
|
|
6057
|
+
const verify = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS);
|
|
6058
|
+
if (!verify) {
|
|
6059
|
+
console.error("[Payments][V5-PERSIST] CRITICAL: KV write succeeded but read-back is empty!");
|
|
6060
|
+
} else {
|
|
6061
|
+
this.log(`[V5-PERSIST] Verified: read-back ${verify.length} bytes`);
|
|
6062
|
+
}
|
|
5969
6063
|
} else {
|
|
6064
|
+
this.log(`[V5-PERSIST] No pending V5 tokens to save (total tokens: ${this.tokens.size}), clearing KV`);
|
|
5970
6065
|
await this.deps.storage.set(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS, "");
|
|
5971
6066
|
}
|
|
5972
6067
|
}
|
|
@@ -5976,16 +6071,47 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5976
6071
|
*/
|
|
5977
6072
|
async loadPendingV5Tokens() {
|
|
5978
6073
|
const data = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS);
|
|
6074
|
+
this.log(`[V5-PERSIST] loadPendingV5Tokens: KV data = ${data ? `${data.length} bytes` : "null/empty"}`);
|
|
5979
6075
|
if (!data) return;
|
|
5980
6076
|
try {
|
|
5981
6077
|
const pendingTokens = JSON.parse(data);
|
|
6078
|
+
this.log(`[V5-PERSIST] Parsed ${pendingTokens.length} pending V5 token(s): ${pendingTokens.map((t) => t.id.slice(0, 16)).join(", ")}`);
|
|
5982
6079
|
for (const token of pendingTokens) {
|
|
5983
6080
|
if (!this.tokens.has(token.id)) {
|
|
5984
6081
|
this.tokens.set(token.id, token);
|
|
6082
|
+
this.log(`[V5-PERSIST] Restored token ${token.id.slice(0, 16)} (status=${token.status})`);
|
|
6083
|
+
} else {
|
|
6084
|
+
this.log(`[V5-PERSIST] Token ${token.id.slice(0, 16)} already in map, skipping`);
|
|
5985
6085
|
}
|
|
5986
6086
|
}
|
|
5987
|
-
|
|
5988
|
-
|
|
6087
|
+
} catch (err) {
|
|
6088
|
+
console.error("[Payments][V5-PERSIST] Failed to parse pending V5 tokens:", err);
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
/**
|
|
6092
|
+
* Persist the set of processed splitGroupIds to KV storage.
|
|
6093
|
+
* This ensures Nostr re-deliveries are ignored across page reloads,
|
|
6094
|
+
* even when the confirmed token's in-memory ID differs from v5split_{id}.
|
|
6095
|
+
*/
|
|
6096
|
+
async saveProcessedSplitGroupIds() {
|
|
6097
|
+
const ids = Array.from(this.processedSplitGroupIds);
|
|
6098
|
+
if (ids.length > 0) {
|
|
6099
|
+
await this.deps.storage.set(
|
|
6100
|
+
STORAGE_KEYS_ADDRESS.PROCESSED_SPLIT_GROUP_IDS,
|
|
6101
|
+
JSON.stringify(ids)
|
|
6102
|
+
);
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
6105
|
+
/**
|
|
6106
|
+
* Load processed splitGroupIds from KV storage.
|
|
6107
|
+
*/
|
|
6108
|
+
async loadProcessedSplitGroupIds() {
|
|
6109
|
+
const data = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PROCESSED_SPLIT_GROUP_IDS);
|
|
6110
|
+
if (!data) return;
|
|
6111
|
+
try {
|
|
6112
|
+
const ids = JSON.parse(data);
|
|
6113
|
+
for (const id of ids) {
|
|
6114
|
+
this.processedSplitGroupIds.add(id);
|
|
5989
6115
|
}
|
|
5990
6116
|
} catch {
|
|
5991
6117
|
}
|
|
@@ -6640,7 +6766,32 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6640
6766
|
try {
|
|
6641
6767
|
const result = await provider.sync(localData);
|
|
6642
6768
|
if (result.success && result.merged) {
|
|
6769
|
+
const savedTokens = new Map(this.tokens);
|
|
6643
6770
|
this.loadFromStorageData(result.merged);
|
|
6771
|
+
let restoredCount = 0;
|
|
6772
|
+
for (const [tokenId, token] of savedTokens) {
|
|
6773
|
+
if (this.tokens.has(tokenId)) continue;
|
|
6774
|
+
const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);
|
|
6775
|
+
const stateHash = extractStateHashFromSdkData(token.sdkData);
|
|
6776
|
+
if (sdkTokenId && stateHash && this.isStateTombstoned(sdkTokenId, stateHash)) {
|
|
6777
|
+
continue;
|
|
6778
|
+
}
|
|
6779
|
+
if (sdkTokenId) {
|
|
6780
|
+
let hasEquivalent = false;
|
|
6781
|
+
for (const existing of this.tokens.values()) {
|
|
6782
|
+
if (extractTokenIdFromSdkData(existing.sdkData) === sdkTokenId) {
|
|
6783
|
+
hasEquivalent = true;
|
|
6784
|
+
break;
|
|
6785
|
+
}
|
|
6786
|
+
}
|
|
6787
|
+
if (hasEquivalent) continue;
|
|
6788
|
+
}
|
|
6789
|
+
this.tokens.set(tokenId, token);
|
|
6790
|
+
restoredCount++;
|
|
6791
|
+
}
|
|
6792
|
+
if (restoredCount > 0) {
|
|
6793
|
+
console.log(`[Payments] Sync: restored ${restoredCount} token(s) lost by loadFromStorageData`);
|
|
6794
|
+
}
|
|
6644
6795
|
if (this.nametags.length === 0 && savedNametags.length > 0) {
|
|
6645
6796
|
this.nametags = savedNametags;
|
|
6646
6797
|
}
|
|
@@ -6974,8 +7125,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6974
7125
|
return;
|
|
6975
7126
|
}
|
|
6976
7127
|
this.tokens.set(token.id, token);
|
|
7128
|
+
console.log(`[Payments][DEBUG] NOSTR-FIRST: saving token id=${token.id.slice(0, 16)} status=${token.status} sdkData.length=${token.sdkData?.length}`);
|
|
6977
7129
|
await this.save();
|
|
6978
|
-
|
|
7130
|
+
console.log(`[Payments][DEBUG] NOSTR-FIRST: save() completed, tokens.size=${this.tokens.size}`);
|
|
6979
7131
|
const senderInfo = await this.resolveSenderInfo(transfer.senderTransportPubkey);
|
|
6980
7132
|
const incomingTransfer = {
|
|
6981
7133
|
id: transfer.id,
|
|
@@ -7132,8 +7284,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7132
7284
|
}
|
|
7133
7285
|
}
|
|
7134
7286
|
async handleIncomingTransfer(transfer) {
|
|
7287
|
+
if (!this.loaded && this.loadedPromise) {
|
|
7288
|
+
await this.loadedPromise;
|
|
7289
|
+
}
|
|
7135
7290
|
try {
|
|
7136
7291
|
const payload = transfer.payload;
|
|
7292
|
+
console.log("[Payments][DEBUG] handleIncomingTransfer: keys=", Object.keys(payload).join(","));
|
|
7137
7293
|
let instantBundle = null;
|
|
7138
7294
|
if (isInstantSplitBundle(payload)) {
|
|
7139
7295
|
instantBundle = payload;
|
|
@@ -7165,7 +7321,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7165
7321
|
return;
|
|
7166
7322
|
}
|
|
7167
7323
|
if (payload.sourceToken && payload.commitmentData && !payload.transferTx) {
|
|
7168
|
-
|
|
7324
|
+
console.log("[Payments][DEBUG] >>> NOSTR-FIRST commitment-only transfer detected");
|
|
7169
7325
|
await this.handleCommitmentOnlyTransfer(transfer, payload);
|
|
7170
7326
|
return;
|
|
7171
7327
|
}
|
|
@@ -7328,17 +7484,24 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7328
7484
|
// ===========================================================================
|
|
7329
7485
|
async save() {
|
|
7330
7486
|
const providers = this.getTokenStorageProviders();
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
return
|
|
7334
|
-
}
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7487
|
+
const tokenStats = Array.from(this.tokens.values()).map((t) => {
|
|
7488
|
+
const txf = tokenToTxf(t);
|
|
7489
|
+
return `${t.id.slice(0, 12)}(${t.status},txf=${!!txf})`;
|
|
7490
|
+
});
|
|
7491
|
+
console.log(`[Payments][DEBUG] save(): providers=${providers.size}, tokens=[${tokenStats.join(", ")}]`);
|
|
7492
|
+
if (providers.size > 0) {
|
|
7493
|
+
const data = await this.createStorageData();
|
|
7494
|
+
const dataKeys = Object.keys(data).filter((k) => k.startsWith("token-"));
|
|
7495
|
+
console.log(`[Payments][DEBUG] save(): TXF keys=${dataKeys.length} (${dataKeys.join(", ")})`);
|
|
7496
|
+
for (const [id, provider] of providers) {
|
|
7497
|
+
try {
|
|
7498
|
+
await provider.save(data);
|
|
7499
|
+
} catch (err) {
|
|
7500
|
+
console.error(`[Payments] Failed to save to provider ${id}:`, err);
|
|
7501
|
+
}
|
|
7341
7502
|
}
|
|
7503
|
+
} else {
|
|
7504
|
+
console.log("[Payments][DEBUG] save(): No token storage providers - TXF not persisted");
|
|
7342
7505
|
}
|
|
7343
7506
|
await this.savePendingV5Tokens();
|
|
7344
7507
|
}
|
|
@@ -7374,6 +7537,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7374
7537
|
}
|
|
7375
7538
|
loadFromStorageData(data) {
|
|
7376
7539
|
const parsed = parseTxfStorageData(data);
|
|
7540
|
+
console.log(`[Payments][DEBUG] loadFromStorageData: parsed ${parsed.tokens.length} tokens, ${parsed.tombstones.length} tombstones, errors=[${parsed.validationErrors.join("; ")}]`);
|
|
7377
7541
|
this.tombstones = parsed.tombstones;
|
|
7378
7542
|
this.tokens.clear();
|
|
7379
7543
|
for (const token of parsed.tokens) {
|