From 2f45c78741639b0c3f30af0b159b9a08dfea0975 Mon Sep 17 00:00:00 2001 From: Laan Tungir Date: Thu, 25 Jun 2026 14:05:33 -0400 Subject: [PATCH] Lightweight 17375-only republish: skip 7375 rewrite, no deletions needed Kind 17375 is a replaceable event (NIP-60, d-tag=''), so publishing a new one automatically supersedes the old one on relays. The republish now only: 1. ensureDirectNutzapP2pk() - generate privkey if missing 2. Build NIP-60 tag-array payload 3. NIP-44 encrypt + sign + publish single 17375 event That's 1 signing round-trip instead of 100+. Reduced timeout from 120s back to 30s. Bumped WORKER_REVISION to nip60-lightweight-republish-1. --- www/js/init-ndk.mjs | 4 +-- www/js/version.json | 6 ++-- www/ndk-worker.js | 69 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/www/js/init-ndk.mjs b/www/js/init-ndk.mjs index 5d2f58c..93b5350 100644 --- a/www/js/init-ndk.mjs +++ b/www/js/init-ndk.mjs @@ -184,7 +184,7 @@ export async function initNDKPage(options = {}) { // All pages in www/ directory, so worker path is always the same. // Include explicit worker revision token so updated SharedWorker code is guaranteed to restart. - const WORKER_REVISION = 'nip60-privkey-ensure-1'; + const WORKER_REVISION = 'nip60-lightweight-republish-1'; const workerPath = `./ndk-worker.js?v=${encodeURIComponent(VERSION)}&wr=${encodeURIComponent(WORKER_REVISION)}`; // Initialize NDK SharedWorker (shared across all tabs/pages) @@ -1426,7 +1426,7 @@ export function walletShutdown() { } export function walletRepublish() { - return sendWorkerRequest('walletRepublish', {}, 120000, 'walletRepublish timeout'); + return sendWorkerRequest('walletRepublish', {}, 30000, 'walletRepublish timeout'); } export function walletPublishMintList(relays = [], receiveMints = []) { diff --git a/www/js/version.json b/www/js/version.json index 341a41a..4ada06f 100644 --- a/www/js/version.json +++ b/www/js/version.json @@ -1,5 +1,5 @@ { - "VERSION": "v0.7.23", - "VERSION_NUMBER": "0.7.23", - "BUILD_DATE": "2026-06-25T18:01:01.940Z" + "VERSION": "v0.7.24", + "VERSION_NUMBER": "0.7.24", + "BUILD_DATE": "2026-06-25T18:05:33.223Z" } diff --git a/www/ndk-worker.js b/www/ndk-worker.js index 991c5db..f9b0c9d 100644 --- a/www/ndk-worker.js +++ b/www/ndk-worker.js @@ -4313,6 +4313,16 @@ async function handleWalletCheckProofs(requestId, port) { } } +/** + * Lightweight republish of ONLY the kind 17375 wallet event. + * + * Kind 17375 is a replaceable event (NIP-60, d-tag = ""), so publishing a + * new one automatically supersedes the old one on relays — no NIP-09 + * deletions needed. This skips the heavy publishDirectProofs() which + * rewrites all 7375 token events too (100+ signing round-trips). + * + * Just: ensure privkey → build tag-array payload → encrypt → sign → publish. + */ async function handleWalletRepublish(requestId, port) { try { if (!ndk || !currentPubkey) { @@ -4320,7 +4330,63 @@ async function handleWalletRepublish(requestId, port) { return; } await ensureDirectWalletLoaded(); - await publishDirectProofs({ traceId: 'walletRepublish' }); + + // Ensure a nutzap privkey exists (auto-generate if missing) + try { + ensureDirectNutzapP2pk(); + } catch (err) { + console.warn('[Worker] handleWalletRepublish: ensureDirectNutzapP2pk failed:', err?.message || err); + } + + const NDKEvent = getNDKEventCtor(); + if (!NDKEvent) { + port.postMessage({ type: 'response', requestId, error: 'NDKEvent constructor not available' }); + return; + } + + // Build NIP-60 tag-array payload: [["mint",url],["privkey",hex]] + const mintTags = getDirectWalletMints().map((mintUrl) => ['mint', mintUrl]); + const walletPayloadObj = [ + ...getDirectWalletMints().map((url) => ['mint', url]), + ...(directNutzapPrivateKeyHex ? [['privkey', directNutzapPrivateKeyHex]] : []) + ]; + const walletPayload = JSON.stringify(walletPayloadObj); + + // NIP-44 encrypt the payload + const encryptedWalletResp = await messageSigner.requestFromPage('nip44Encrypt', { + recipientPubkey: currentPubkey, + plaintext: walletPayload + }); + const encryptedWallet = typeof encryptedWalletResp === 'string' ? encryptedWalletResp : encryptedWalletResp?.ciphertext; + + if (!encryptedWallet) { + port.postMessage({ type: 'response', requestId, error: 'Failed to encrypt wallet payload' }); + return; + } + + // Build and publish the new 17375 event (replaceable — supersedes old one) + const walletEvent = new NDKEvent(ndk, { + kind: 17375, + tags: mintTags, + content: encryptedWallet, + created_at: Math.floor(Date.now() / 1000) + }); + + await signEventWithMessageSigner(walletEvent); + const relaySet = await walletEvent.publish(); + + if (relaySet && relaySet.size > 0) { + for (const relay of relaySet) { + trackRelayWrite(relay.url); + } + } + + console.log('[Worker] handleWalletRepublish: 17375 published', { + relayCount: relaySet ? relaySet.size : 0, + mintCount: mintTags.length, + hasPrivkey: Boolean(directNutzapPrivateKeyHex) + }); + const payload = getWalletBalancePayload(); port.postMessage({ type: 'response', @@ -4328,6 +4394,7 @@ async function handleWalletRepublish(requestId, port) { data: { success: true, mints: getDirectWalletMints(), + relayCount: relaySet ? relaySet.size : 0, ...payload } });