@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/index.cjs
CHANGED
|
@@ -107,7 +107,9 @@ var init_constants = __esm({
|
|
|
107
107
|
/** Group chat: members for this address */
|
|
108
108
|
GROUP_CHAT_MEMBERS: "group_chat_members",
|
|
109
109
|
/** Group chat: processed event IDs for deduplication */
|
|
110
|
-
GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events"
|
|
110
|
+
GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events",
|
|
111
|
+
/** Processed V5 split group IDs for Nostr re-delivery dedup */
|
|
112
|
+
PROCESSED_SPLIT_GROUP_IDS: "processed_split_group_ids"
|
|
111
113
|
};
|
|
112
114
|
STORAGE_KEYS = {
|
|
113
115
|
...STORAGE_KEYS_GLOBAL,
|
|
@@ -4756,6 +4758,17 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4756
4758
|
// Poll every 2s
|
|
4757
4759
|
static PROOF_POLLING_MAX_ATTEMPTS = 30;
|
|
4758
4760
|
// Max 30 attempts (~60s)
|
|
4761
|
+
// Periodic retry for resolveUnconfirmed (V5 lazy finalization)
|
|
4762
|
+
resolveUnconfirmedTimer = null;
|
|
4763
|
+
static RESOLVE_UNCONFIRMED_INTERVAL_MS = 1e4;
|
|
4764
|
+
// Retry every 10s
|
|
4765
|
+
// Guard: ensure load() completes before processing incoming bundles
|
|
4766
|
+
loadedPromise = null;
|
|
4767
|
+
loaded = false;
|
|
4768
|
+
// Persistent dedup: tracks splitGroupIds that have been fully processed.
|
|
4769
|
+
// Survives page reloads via KV storage so Nostr re-deliveries are ignored
|
|
4770
|
+
// even when the confirmed token's in-memory ID differs from v5split_{id}.
|
|
4771
|
+
processedSplitGroupIds = /* @__PURE__ */ new Set();
|
|
4759
4772
|
// Storage event subscriptions (push-based sync)
|
|
4760
4773
|
storageEventUnsubscribers = [];
|
|
4761
4774
|
syncDebounceTimer = null;
|
|
@@ -4841,31 +4854,40 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4841
4854
|
*/
|
|
4842
4855
|
async load() {
|
|
4843
4856
|
this.ensureInitialized();
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4857
|
+
const doLoad = async () => {
|
|
4858
|
+
await TokenRegistry.waitForReady();
|
|
4859
|
+
const providers = this.getTokenStorageProviders();
|
|
4860
|
+
for (const [id, provider] of providers) {
|
|
4861
|
+
try {
|
|
4862
|
+
const result = await provider.load();
|
|
4863
|
+
if (result.success && result.data) {
|
|
4864
|
+
this.loadFromStorageData(result.data);
|
|
4865
|
+
this.log(`Loaded metadata from provider ${id}`);
|
|
4866
|
+
break;
|
|
4867
|
+
}
|
|
4868
|
+
} catch (err) {
|
|
4869
|
+
console.error(`[Payments] Failed to load from provider ${id}:`, err);
|
|
4853
4870
|
}
|
|
4854
|
-
} catch (err) {
|
|
4855
|
-
console.error(`[Payments] Failed to load from provider ${id}:`, err);
|
|
4856
4871
|
}
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
const
|
|
4863
|
-
|
|
4864
|
-
|
|
4872
|
+
const loadedTokens = Array.from(this.tokens.values()).map((t) => `${t.id.slice(0, 12)}(${t.status})`);
|
|
4873
|
+
console.log(`[Payments][DEBUG] load(): from TXF providers: ${this.tokens.size} tokens [${loadedTokens.join(", ")}]`);
|
|
4874
|
+
await this.loadPendingV5Tokens();
|
|
4875
|
+
await this.loadProcessedSplitGroupIds();
|
|
4876
|
+
await this.loadHistory();
|
|
4877
|
+
const pending2 = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
|
|
4878
|
+
if (pending2) {
|
|
4879
|
+
const transfers = JSON.parse(pending2);
|
|
4880
|
+
for (const transfer of transfers) {
|
|
4881
|
+
this.pendingTransfers.set(transfer.id, transfer);
|
|
4882
|
+
}
|
|
4865
4883
|
}
|
|
4866
|
-
|
|
4884
|
+
this.loaded = true;
|
|
4885
|
+
};
|
|
4886
|
+
this.loadedPromise = doLoad();
|
|
4887
|
+
await this.loadedPromise;
|
|
4867
4888
|
this.resolveUnconfirmed().catch(() => {
|
|
4868
4889
|
});
|
|
4890
|
+
this.scheduleResolveUnconfirmed();
|
|
4869
4891
|
}
|
|
4870
4892
|
/**
|
|
4871
4893
|
* Cleanup all subscriptions, polling jobs, and pending resolvers.
|
|
@@ -4884,6 +4906,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4884
4906
|
this.paymentRequestResponseHandlers.clear();
|
|
4885
4907
|
this.stopProofPolling();
|
|
4886
4908
|
this.proofPollingJobs.clear();
|
|
4909
|
+
this.stopResolveUnconfirmedPolling();
|
|
4887
4910
|
for (const [, resolver] of this.pendingResponseResolvers) {
|
|
4888
4911
|
clearTimeout(resolver.timeout);
|
|
4889
4912
|
resolver.reject(new Error("Module destroyed"));
|
|
@@ -5290,13 +5313,16 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5290
5313
|
*/
|
|
5291
5314
|
async processInstantSplitBundle(bundle, senderPubkey, memo) {
|
|
5292
5315
|
this.ensureInitialized();
|
|
5316
|
+
if (!this.loaded && this.loadedPromise) {
|
|
5317
|
+
await this.loadedPromise;
|
|
5318
|
+
}
|
|
5293
5319
|
if (!isInstantSplitBundleV5(bundle)) {
|
|
5294
5320
|
return this.processInstantSplitBundleSync(bundle, senderPubkey, memo);
|
|
5295
5321
|
}
|
|
5296
5322
|
try {
|
|
5297
5323
|
const deterministicId = `v5split_${bundle.splitGroupId}`;
|
|
5298
|
-
if (this.tokens.has(deterministicId)) {
|
|
5299
|
-
|
|
5324
|
+
if (this.tokens.has(deterministicId) || this.processedSplitGroupIds.has(bundle.splitGroupId)) {
|
|
5325
|
+
console.log(`[Payments] V5 bundle ${bundle.splitGroupId.slice(0, 12)}... already processed, skipping`);
|
|
5300
5326
|
return { success: true, durationMs: 0 };
|
|
5301
5327
|
}
|
|
5302
5328
|
const registry = TokenRegistry.getInstance();
|
|
@@ -5322,7 +5348,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5322
5348
|
sdkData: JSON.stringify({ _pendingFinalization: pendingData })
|
|
5323
5349
|
};
|
|
5324
5350
|
await this.addToken(uiToken);
|
|
5325
|
-
this.
|
|
5351
|
+
this.processedSplitGroupIds.add(bundle.splitGroupId);
|
|
5352
|
+
await this.saveProcessedSplitGroupIds();
|
|
5326
5353
|
const senderInfo = await this.resolveSenderInfo(senderPubkey);
|
|
5327
5354
|
await this.addToHistory({
|
|
5328
5355
|
type: "RECEIVED",
|
|
@@ -5346,6 +5373,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5346
5373
|
await this.save();
|
|
5347
5374
|
this.resolveUnconfirmed().catch(() => {
|
|
5348
5375
|
});
|
|
5376
|
+
this.scheduleResolveUnconfirmed();
|
|
5349
5377
|
return { success: true, durationMs: 0 };
|
|
5350
5378
|
} catch (error) {
|
|
5351
5379
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -6092,28 +6120,70 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6092
6120
|
};
|
|
6093
6121
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
6094
6122
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
6095
|
-
if (!stClient || !trustBase)
|
|
6123
|
+
if (!stClient || !trustBase) {
|
|
6124
|
+
console.log(`[V5-RESOLVE] resolveUnconfirmed: EARLY EXIT \u2014 stClient=${!!stClient} trustBase=${!!trustBase}`);
|
|
6125
|
+
return result;
|
|
6126
|
+
}
|
|
6096
6127
|
const signingService = await this.createSigningService();
|
|
6128
|
+
const submittedCount = Array.from(this.tokens.values()).filter((t) => t.status === "submitted").length;
|
|
6129
|
+
console.log(`[V5-RESOLVE] resolveUnconfirmed: ${submittedCount} submitted token(s) to process`);
|
|
6097
6130
|
for (const [tokenId, token] of this.tokens) {
|
|
6098
6131
|
if (token.status !== "submitted") continue;
|
|
6099
6132
|
const pending2 = this.parsePendingFinalization(token.sdkData);
|
|
6100
6133
|
if (!pending2) {
|
|
6134
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 16)}: no pending finalization metadata, skipping`);
|
|
6101
6135
|
result.stillPending++;
|
|
6102
6136
|
continue;
|
|
6103
6137
|
}
|
|
6104
6138
|
if (pending2.type === "v5_bundle") {
|
|
6139
|
+
console.log(`[V5-RESOLVE] Processing ${tokenId.slice(0, 16)}... stage=${pending2.stage} attempt=${pending2.attemptCount}`);
|
|
6105
6140
|
const progress = await this.resolveV5Token(tokenId, token, pending2, stClient, trustBase, signingService);
|
|
6141
|
+
console.log(`[V5-RESOLVE] Result for ${tokenId.slice(0, 16)}...: ${progress} (stage now: ${pending2.stage})`);
|
|
6106
6142
|
result.details.push({ tokenId, stage: pending2.stage, status: progress });
|
|
6107
6143
|
if (progress === "resolved") result.resolved++;
|
|
6108
6144
|
else if (progress === "failed") result.failed++;
|
|
6109
6145
|
else result.stillPending++;
|
|
6110
6146
|
}
|
|
6111
6147
|
}
|
|
6112
|
-
if (result.resolved > 0 || result.failed > 0) {
|
|
6148
|
+
if (result.resolved > 0 || result.failed > 0 || result.stillPending > 0) {
|
|
6149
|
+
console.log(`[V5-RESOLVE] Saving: resolved=${result.resolved} failed=${result.failed} stillPending=${result.stillPending}`);
|
|
6113
6150
|
await this.save();
|
|
6114
6151
|
}
|
|
6115
6152
|
return result;
|
|
6116
6153
|
}
|
|
6154
|
+
/**
|
|
6155
|
+
* Start a periodic interval that retries resolveUnconfirmed() until all
|
|
6156
|
+
* tokens are confirmed or failed. Stops automatically when nothing is
|
|
6157
|
+
* pending and is cleaned up by destroy().
|
|
6158
|
+
*/
|
|
6159
|
+
scheduleResolveUnconfirmed() {
|
|
6160
|
+
if (this.resolveUnconfirmedTimer) return;
|
|
6161
|
+
const hasUnconfirmed = Array.from(this.tokens.values()).some(
|
|
6162
|
+
(t) => t.status === "submitted"
|
|
6163
|
+
);
|
|
6164
|
+
if (!hasUnconfirmed) {
|
|
6165
|
+
console.log(`[V5-RESOLVE] scheduleResolveUnconfirmed: no submitted tokens, not starting timer`);
|
|
6166
|
+
return;
|
|
6167
|
+
}
|
|
6168
|
+
console.log(`[V5-RESOLVE] scheduleResolveUnconfirmed: starting periodic retry (every ${_PaymentsModule.RESOLVE_UNCONFIRMED_INTERVAL_MS}ms)`);
|
|
6169
|
+
this.resolveUnconfirmedTimer = setInterval(async () => {
|
|
6170
|
+
try {
|
|
6171
|
+
const result = await this.resolveUnconfirmed();
|
|
6172
|
+
if (result.stillPending === 0) {
|
|
6173
|
+
console.log(`[V5-RESOLVE] All tokens resolved, stopping periodic retry`);
|
|
6174
|
+
this.stopResolveUnconfirmedPolling();
|
|
6175
|
+
}
|
|
6176
|
+
} catch (err) {
|
|
6177
|
+
console.log(`[V5-RESOLVE] Periodic retry error:`, err);
|
|
6178
|
+
}
|
|
6179
|
+
}, _PaymentsModule.RESOLVE_UNCONFIRMED_INTERVAL_MS);
|
|
6180
|
+
}
|
|
6181
|
+
stopResolveUnconfirmedPolling() {
|
|
6182
|
+
if (this.resolveUnconfirmedTimer) {
|
|
6183
|
+
clearInterval(this.resolveUnconfirmedTimer);
|
|
6184
|
+
this.resolveUnconfirmedTimer = null;
|
|
6185
|
+
}
|
|
6186
|
+
}
|
|
6117
6187
|
// ===========================================================================
|
|
6118
6188
|
// Private - V5 Lazy Resolution Helpers
|
|
6119
6189
|
// ===========================================================================
|
|
@@ -6126,10 +6196,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6126
6196
|
pending2.lastAttemptAt = Date.now();
|
|
6127
6197
|
try {
|
|
6128
6198
|
if (pending2.stage === "RECEIVED") {
|
|
6199
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: RECEIVED \u2192 submitting mint commitment...`);
|
|
6129
6200
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
6130
6201
|
const mintData = await import_MintTransactionData3.MintTransactionData.fromJSON(mintDataJson);
|
|
6131
6202
|
const mintCommitment = await import_MintCommitment3.MintCommitment.create(mintData);
|
|
6132
6203
|
const mintResponse = await stClient.submitMintCommitment(mintCommitment);
|
|
6204
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint response status=${mintResponse.status}`);
|
|
6133
6205
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
6134
6206
|
throw new Error(`Mint submission failed: ${mintResponse.status}`);
|
|
6135
6207
|
}
|
|
@@ -6137,22 +6209,27 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6137
6209
|
this.updatePendingFinalization(token, pending2);
|
|
6138
6210
|
}
|
|
6139
6211
|
if (pending2.stage === "MINT_SUBMITTED") {
|
|
6212
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_SUBMITTED \u2192 checking mint proof...`);
|
|
6140
6213
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
6141
6214
|
const mintData = await import_MintTransactionData3.MintTransactionData.fromJSON(mintDataJson);
|
|
6142
6215
|
const mintCommitment = await import_MintCommitment3.MintCommitment.create(mintData);
|
|
6143
6216
|
const proof = await this.quickProofCheck(stClient, trustBase, mintCommitment);
|
|
6144
6217
|
if (!proof) {
|
|
6218
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof not yet available, staying MINT_SUBMITTED`);
|
|
6145
6219
|
this.updatePendingFinalization(token, pending2);
|
|
6146
6220
|
return "pending";
|
|
6147
6221
|
}
|
|
6222
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof obtained!`);
|
|
6148
6223
|
pending2.mintProofJson = JSON.stringify(proof);
|
|
6149
6224
|
pending2.stage = "MINT_PROVEN";
|
|
6150
6225
|
this.updatePendingFinalization(token, pending2);
|
|
6151
6226
|
}
|
|
6152
6227
|
if (pending2.stage === "MINT_PROVEN") {
|
|
6228
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_PROVEN \u2192 submitting transfer commitment...`);
|
|
6153
6229
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
6154
6230
|
const transferCommitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferCommitmentJson);
|
|
6155
6231
|
const transferResponse = await stClient.submitTransferCommitment(transferCommitment);
|
|
6232
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer response status=${transferResponse.status}`);
|
|
6156
6233
|
if (transferResponse.status !== "SUCCESS" && transferResponse.status !== "REQUEST_ID_EXISTS") {
|
|
6157
6234
|
throw new Error(`Transfer submission failed: ${transferResponse.status}`);
|
|
6158
6235
|
}
|
|
@@ -6160,13 +6237,16 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6160
6237
|
this.updatePendingFinalization(token, pending2);
|
|
6161
6238
|
}
|
|
6162
6239
|
if (pending2.stage === "TRANSFER_SUBMITTED") {
|
|
6240
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: TRANSFER_SUBMITTED \u2192 checking transfer proof...`);
|
|
6163
6241
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
6164
6242
|
const transferCommitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferCommitmentJson);
|
|
6165
6243
|
const proof = await this.quickProofCheck(stClient, trustBase, transferCommitment);
|
|
6166
6244
|
if (!proof) {
|
|
6245
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer proof not yet available, staying TRANSFER_SUBMITTED`);
|
|
6167
6246
|
this.updatePendingFinalization(token, pending2);
|
|
6168
6247
|
return "pending";
|
|
6169
6248
|
}
|
|
6249
|
+
console.log(`[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer proof obtained! Finalizing...`);
|
|
6170
6250
|
const finalizedToken = await this.finalizeFromV5Bundle(bundle, pending2, signingService, stClient, trustBase);
|
|
6171
6251
|
const confirmedToken = {
|
|
6172
6252
|
id: token.id,
|
|
@@ -6182,6 +6262,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6182
6262
|
sdkData: JSON.stringify(finalizedToken.toJSON())
|
|
6183
6263
|
};
|
|
6184
6264
|
this.tokens.set(tokenId, confirmedToken);
|
|
6265
|
+
this.deps.emitEvent("transfer:confirmed", {
|
|
6266
|
+
id: crypto.randomUUID(),
|
|
6267
|
+
status: "completed",
|
|
6268
|
+
tokens: [confirmedToken],
|
|
6269
|
+
tokenTransfers: []
|
|
6270
|
+
});
|
|
6185
6271
|
this.log(`V5 token resolved: ${tokenId.slice(0, 8)}...`);
|
|
6186
6272
|
return "resolved";
|
|
6187
6273
|
}
|
|
@@ -6323,11 +6409,20 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6323
6409
|
}
|
|
6324
6410
|
}
|
|
6325
6411
|
if (pendingTokens.length > 0) {
|
|
6412
|
+
const json = JSON.stringify(pendingTokens);
|
|
6413
|
+
this.log(`[V5-PERSIST] Saving ${pendingTokens.length} pending V5 token(s): ${pendingTokens.map((t) => t.id.slice(0, 16)).join(", ")} (${json.length} bytes)`);
|
|
6326
6414
|
await this.deps.storage.set(
|
|
6327
6415
|
STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS,
|
|
6328
|
-
|
|
6416
|
+
json
|
|
6329
6417
|
);
|
|
6418
|
+
const verify = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS);
|
|
6419
|
+
if (!verify) {
|
|
6420
|
+
console.error("[Payments][V5-PERSIST] CRITICAL: KV write succeeded but read-back is empty!");
|
|
6421
|
+
} else {
|
|
6422
|
+
this.log(`[V5-PERSIST] Verified: read-back ${verify.length} bytes`);
|
|
6423
|
+
}
|
|
6330
6424
|
} else {
|
|
6425
|
+
this.log(`[V5-PERSIST] No pending V5 tokens to save (total tokens: ${this.tokens.size}), clearing KV`);
|
|
6331
6426
|
await this.deps.storage.set(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS, "");
|
|
6332
6427
|
}
|
|
6333
6428
|
}
|
|
@@ -6337,16 +6432,47 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6337
6432
|
*/
|
|
6338
6433
|
async loadPendingV5Tokens() {
|
|
6339
6434
|
const data = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS);
|
|
6435
|
+
this.log(`[V5-PERSIST] loadPendingV5Tokens: KV data = ${data ? `${data.length} bytes` : "null/empty"}`);
|
|
6340
6436
|
if (!data) return;
|
|
6341
6437
|
try {
|
|
6342
6438
|
const pendingTokens = JSON.parse(data);
|
|
6439
|
+
this.log(`[V5-PERSIST] Parsed ${pendingTokens.length} pending V5 token(s): ${pendingTokens.map((t) => t.id.slice(0, 16)).join(", ")}`);
|
|
6343
6440
|
for (const token of pendingTokens) {
|
|
6344
6441
|
if (!this.tokens.has(token.id)) {
|
|
6345
6442
|
this.tokens.set(token.id, token);
|
|
6443
|
+
this.log(`[V5-PERSIST] Restored token ${token.id.slice(0, 16)} (status=${token.status})`);
|
|
6444
|
+
} else {
|
|
6445
|
+
this.log(`[V5-PERSIST] Token ${token.id.slice(0, 16)} already in map, skipping`);
|
|
6346
6446
|
}
|
|
6347
6447
|
}
|
|
6348
|
-
|
|
6349
|
-
|
|
6448
|
+
} catch (err) {
|
|
6449
|
+
console.error("[Payments][V5-PERSIST] Failed to parse pending V5 tokens:", err);
|
|
6450
|
+
}
|
|
6451
|
+
}
|
|
6452
|
+
/**
|
|
6453
|
+
* Persist the set of processed splitGroupIds to KV storage.
|
|
6454
|
+
* This ensures Nostr re-deliveries are ignored across page reloads,
|
|
6455
|
+
* even when the confirmed token's in-memory ID differs from v5split_{id}.
|
|
6456
|
+
*/
|
|
6457
|
+
async saveProcessedSplitGroupIds() {
|
|
6458
|
+
const ids = Array.from(this.processedSplitGroupIds);
|
|
6459
|
+
if (ids.length > 0) {
|
|
6460
|
+
await this.deps.storage.set(
|
|
6461
|
+
STORAGE_KEYS_ADDRESS.PROCESSED_SPLIT_GROUP_IDS,
|
|
6462
|
+
JSON.stringify(ids)
|
|
6463
|
+
);
|
|
6464
|
+
}
|
|
6465
|
+
}
|
|
6466
|
+
/**
|
|
6467
|
+
* Load processed splitGroupIds from KV storage.
|
|
6468
|
+
*/
|
|
6469
|
+
async loadProcessedSplitGroupIds() {
|
|
6470
|
+
const data = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PROCESSED_SPLIT_GROUP_IDS);
|
|
6471
|
+
if (!data) return;
|
|
6472
|
+
try {
|
|
6473
|
+
const ids = JSON.parse(data);
|
|
6474
|
+
for (const id of ids) {
|
|
6475
|
+
this.processedSplitGroupIds.add(id);
|
|
6350
6476
|
}
|
|
6351
6477
|
} catch {
|
|
6352
6478
|
}
|
|
@@ -7001,7 +7127,32 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7001
7127
|
try {
|
|
7002
7128
|
const result = await provider.sync(localData);
|
|
7003
7129
|
if (result.success && result.merged) {
|
|
7130
|
+
const savedTokens = new Map(this.tokens);
|
|
7004
7131
|
this.loadFromStorageData(result.merged);
|
|
7132
|
+
let restoredCount = 0;
|
|
7133
|
+
for (const [tokenId, token] of savedTokens) {
|
|
7134
|
+
if (this.tokens.has(tokenId)) continue;
|
|
7135
|
+
const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);
|
|
7136
|
+
const stateHash = extractStateHashFromSdkData(token.sdkData);
|
|
7137
|
+
if (sdkTokenId && stateHash && this.isStateTombstoned(sdkTokenId, stateHash)) {
|
|
7138
|
+
continue;
|
|
7139
|
+
}
|
|
7140
|
+
if (sdkTokenId) {
|
|
7141
|
+
let hasEquivalent = false;
|
|
7142
|
+
for (const existing of this.tokens.values()) {
|
|
7143
|
+
if (extractTokenIdFromSdkData(existing.sdkData) === sdkTokenId) {
|
|
7144
|
+
hasEquivalent = true;
|
|
7145
|
+
break;
|
|
7146
|
+
}
|
|
7147
|
+
}
|
|
7148
|
+
if (hasEquivalent) continue;
|
|
7149
|
+
}
|
|
7150
|
+
this.tokens.set(tokenId, token);
|
|
7151
|
+
restoredCount++;
|
|
7152
|
+
}
|
|
7153
|
+
if (restoredCount > 0) {
|
|
7154
|
+
console.log(`[Payments] Sync: restored ${restoredCount} token(s) lost by loadFromStorageData`);
|
|
7155
|
+
}
|
|
7005
7156
|
if (this.nametags.length === 0 && savedNametags.length > 0) {
|
|
7006
7157
|
this.nametags = savedNametags;
|
|
7007
7158
|
}
|
|
@@ -7335,8 +7486,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7335
7486
|
return;
|
|
7336
7487
|
}
|
|
7337
7488
|
this.tokens.set(token.id, token);
|
|
7489
|
+
console.log(`[Payments][DEBUG] NOSTR-FIRST: saving token id=${token.id.slice(0, 16)} status=${token.status} sdkData.length=${token.sdkData?.length}`);
|
|
7338
7490
|
await this.save();
|
|
7339
|
-
|
|
7491
|
+
console.log(`[Payments][DEBUG] NOSTR-FIRST: save() completed, tokens.size=${this.tokens.size}`);
|
|
7340
7492
|
const senderInfo = await this.resolveSenderInfo(transfer.senderTransportPubkey);
|
|
7341
7493
|
const incomingTransfer = {
|
|
7342
7494
|
id: transfer.id,
|
|
@@ -7493,8 +7645,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7493
7645
|
}
|
|
7494
7646
|
}
|
|
7495
7647
|
async handleIncomingTransfer(transfer) {
|
|
7648
|
+
if (!this.loaded && this.loadedPromise) {
|
|
7649
|
+
await this.loadedPromise;
|
|
7650
|
+
}
|
|
7496
7651
|
try {
|
|
7497
7652
|
const payload = transfer.payload;
|
|
7653
|
+
console.log("[Payments][DEBUG] handleIncomingTransfer: keys=", Object.keys(payload).join(","));
|
|
7498
7654
|
let instantBundle = null;
|
|
7499
7655
|
if (isInstantSplitBundle(payload)) {
|
|
7500
7656
|
instantBundle = payload;
|
|
@@ -7526,7 +7682,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7526
7682
|
return;
|
|
7527
7683
|
}
|
|
7528
7684
|
if (payload.sourceToken && payload.commitmentData && !payload.transferTx) {
|
|
7529
|
-
|
|
7685
|
+
console.log("[Payments][DEBUG] >>> NOSTR-FIRST commitment-only transfer detected");
|
|
7530
7686
|
await this.handleCommitmentOnlyTransfer(transfer, payload);
|
|
7531
7687
|
return;
|
|
7532
7688
|
}
|
|
@@ -7689,17 +7845,24 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7689
7845
|
// ===========================================================================
|
|
7690
7846
|
async save() {
|
|
7691
7847
|
const providers = this.getTokenStorageProviders();
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
return
|
|
7695
|
-
}
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7848
|
+
const tokenStats = Array.from(this.tokens.values()).map((t) => {
|
|
7849
|
+
const txf = tokenToTxf(t);
|
|
7850
|
+
return `${t.id.slice(0, 12)}(${t.status},txf=${!!txf})`;
|
|
7851
|
+
});
|
|
7852
|
+
console.log(`[Payments][DEBUG] save(): providers=${providers.size}, tokens=[${tokenStats.join(", ")}]`);
|
|
7853
|
+
if (providers.size > 0) {
|
|
7854
|
+
const data = await this.createStorageData();
|
|
7855
|
+
const dataKeys = Object.keys(data).filter((k) => k.startsWith("token-"));
|
|
7856
|
+
console.log(`[Payments][DEBUG] save(): TXF keys=${dataKeys.length} (${dataKeys.join(", ")})`);
|
|
7857
|
+
for (const [id, provider] of providers) {
|
|
7858
|
+
try {
|
|
7859
|
+
await provider.save(data);
|
|
7860
|
+
} catch (err) {
|
|
7861
|
+
console.error(`[Payments] Failed to save to provider ${id}:`, err);
|
|
7862
|
+
}
|
|
7702
7863
|
}
|
|
7864
|
+
} else {
|
|
7865
|
+
console.log("[Payments][DEBUG] save(): No token storage providers - TXF not persisted");
|
|
7703
7866
|
}
|
|
7704
7867
|
await this.savePendingV5Tokens();
|
|
7705
7868
|
}
|
|
@@ -7735,6 +7898,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7735
7898
|
}
|
|
7736
7899
|
loadFromStorageData(data) {
|
|
7737
7900
|
const parsed = parseTxfStorageData(data);
|
|
7901
|
+
console.log(`[Payments][DEBUG] loadFromStorageData: parsed ${parsed.tokens.length} tokens, ${parsed.tombstones.length} tombstones, errors=[${parsed.validationErrors.join("; ")}]`);
|
|
7738
7902
|
this.tombstones = parsed.tombstones;
|
|
7739
7903
|
this.tokens.clear();
|
|
7740
7904
|
for (const token of parsed.tokens) {
|