aboutsummaryrefslogtreecommitdiffstats
path: root/src/ark/manager.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-14 15:24:17 -0700
committerAdam Soltys <[email protected]>2026-08-14 15:24:17 -0700
commit4bd22c0c45ae35388b2a24e673bedcde5df77961 (patch)
tree74eed71c3b477dd14be848f5f4d9d72814a04f47 /src/ark/manager.js
parent7a7b2c61fe8394844fa306debb84917454ade7b0 (diff)
downloadcoinosv3-4bd22c0c45ae35388b2a24e673bedcde5df77961.tar.gz
coinosv3-4bd22c0c45ae35388b2a24e673bedcde5df77961.zip
A mailbox backlog claimed at sign-in reads as the old payments they are, not a wall of seconds-ago windfalls; a gift send knows itself in history — status shown, link recopyable, revoke at hand
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
Diffstat (limited to 'src/ark/manager.js')
-rw-r--r--src/ark/manager.js28
1 files changed, 27 insertions, 1 deletions
diff --git a/src/ark/manager.js b/src/ark/manager.js
index b2c2837..143242a 100644
--- a/src/ark/manager.js
+++ b/src/ark/manager.js
@@ -150,6 +150,28 @@ export class ArkManager {
_movement(m) {
this.state.movements.push({ id: `${Date.now()}-${this.state.movements.length}`, ts: Date.now(), ...m });
}
+
+ // When did a mailbox vtxo come into being? Its expiry counts down from the
+ // server's vtxo lifetime, so (lifetime - blocksLeft) blocks have passed
+ // since the sender created it. At ~10 minutes a block that dates a backlog
+ // claimed at first sign-in as the old payments they are — instead of a wall
+ // of "seconds ago" receives and a bogus celebration.
+ _estimateReceiveTs(v, tip) {
+ const life = (this.info && this.info.vtxoExpiryDelta) || 0;
+ if (!life || !tip || !v.expiryHeight) return Date.now();
+ const age = life - (v.expiryHeight - tip);
+ if (age <= 0) return Date.now();
+ return Date.now() - age * 600_000;
+ }
+
+ async _tipMemo() {
+ if (this._tipAt && Date.now() - this._tipAt < 60_000) return this._tipH;
+ try {
+ this._tipH = await this.chain.tipHeight();
+ this._tipAt = Date.now();
+ } catch { /* keep whatever we had */ }
+ return this._tipH || 0;
+ }
_vtxo(id) { return this.state.vtxos.find((v) => v.id === id); }
_addVtxo(decoded, bytes, keyIndex, state = 'spendable') {
if (this._vtxo(decoded.id)) return false;
@@ -264,6 +286,7 @@ export class ArkManager {
}
if (m.kind !== 'arkoor') return changed;
const ourKeys = [hex.encode(this._key(0).pubkey)];
+ const tip = await this._tipMemo();
for (const v of m.vtxos) {
if (this._vtxo(v.id)) continue;
try {
@@ -280,7 +303,10 @@ export class ArkManager {
// same message concurrently (validation awaits in between), and only the
// copy that actually added the vtxo may record the receive movement.
if (!this._addVtxo(v, v._raw.bytes, 0)) continue;
- this._movement({ type: 'receive', amountSat: v.amountSat, status: 'complete', vtxoId: v.id });
+ this._movement({
+ type: 'receive', amountSat: v.amountSat, status: 'complete', vtxoId: v.id,
+ ts: this._estimateReceiveTs(v, tip),
+ });
changed = true;
}
return changed;