diff options
| author | Adam Soltys <[email protected]> | 2026-08-03 14:05:21 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-08-03 14:05:21 -0700 |
| commit | 95c898f91d2f8ea1ef693624a542eb7fa8cadbb9 (patch) | |
| tree | 50b5ac19788f6001bb29a2f5710293d7691da59b | |
| parent | cdd34e88be8a58c1cc9fa3d0cfbbd02b23439629 (diff) | |
| download | coinosv3-95c898f91d2f8ea1ef693624a542eb7fa8cadbb9.tar.gz coinosv3-95c898f91d2f8ea1ef693624a542eb7fa8cadbb9.zip | |
A freshly imported wallet shows history, not a celebration
Importing a seed on a second device replayed "Payment received!" for a
payment that had arrived on the first one. The two-hour recency guard
couldn't catch it: a movement is stamped with the moment THIS device
discovered it, so on a fresh device the entire history looks like it
just landed — a week-old payment would have celebrated too.
A wallet whose ark state didn't exist on this device now treats its
first catch-up as history and acknowledges it, so only what turns up
afterwards is news. Reopening an existing wallet is unaffected, and a
payment that lands while you're watching still celebrates.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
| -rw-r--r-- | src/ark/manager.js | 16 |
1 files changed, 15 insertions, 1 deletions
diff --git a/src/ark/manager.js b/src/ark/manager.js index 5617cf1..01de06f 100644 --- a/src/ark/manager.js +++ b/src/ark/manager.js @@ -88,7 +88,14 @@ export class ArkManager { // ---- lifecycle ---- async init() { - this.state = this.storage.load() || EMPTY_STATE(); + const loaded = this.storage.load(); + this.state = loaded || EMPTY_STATE(); + // A wallet opening on a device for the first time (a fresh import, a new + // browser) is about to discover its whole history at once, and every + // movement it records gets stamped with the moment it was found — so + // without this, a week-old payment looks like it just arrived. Mark the + // first catch-up as history: only what turns up afterwards is news. + if (!loaded) this.state.baselinePending = true; // drop duplicate receive movements (same vtxo) left by the former // poll/stream race; the vtxo set itself was always deduped const seen = new Set(); @@ -211,10 +218,17 @@ export class ArkManager { // Read new mailbox messages, fully validate incoming vtxos, then push any // in-flight actions forward. async sync() { + const baselining = !!this.state.baselinePending; const mailbox = this._mailboxKey(); const { messages } = await readMailbox(this.arkUrl, mailbox, this.state.mailboxCheckpoint); let changed = false; for (const m of messages) changed = (await this._processMailboxMessage(m)) || changed; + // Everything the first catch-up found is history, not news. + if (baselining) { + this.state.receiveAckTs = Date.now(); + delete this.state.baselinePending; + changed = true; + } if (changed) this._save(); await this.resumePending(); } |
