aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-28 21:08:33 -0700
committerAdam Soltys <[email protected]>2026-08-28 21:08:33 -0700
commit065d50b3ef553f540c6471c60abd804abed61b28 (patch)
tree6f0661549ad23464d8c95b171c1da9e87d90668c /src/features
parentf13acc1bf4ffa3d0e2733dc53041bbc61659a5af (diff)
downloadcoinosv3-065d50b3ef553f540c6471c60abd804abed61b28.tar.gz
coinosv3-065d50b3ef553f540c6471c60abd804abed61b28.zip
One payment, one line: the settle path is idempotent (the mailbox push and the status poll raced past each other's step checks — a toque got two history entries), movements carry their payment hash, and the sync merge folds same-preimage lightning records into the earliest telling
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
Diffstat (limited to 'src/features')
-rw-r--r--src/features/ark.js14
1 files changed, 13 insertions, 1 deletions
diff --git a/src/features/ark.js b/src/features/ark.js
index 3e614c4..15b7687 100644
--- a/src/features/ark.js
+++ b/src/features/ark.js
@@ -175,7 +175,19 @@ export function mergeArkStates(a, b) {
return [...x, ...y.filter((i) => !ids.has(i.id))];
};
out.actions = unionById(a.actions, b.actions);
- out.movements = unionById(a.movements, b.movements).sort((m, n) => (m.ts || 0) - (n.ts || 0));
+ // Two devices (or the push and the poll, pre-guard) can each record the
+ // SAME lightning payment under different movement ids — same preimage is
+ // the same payment, keep the earliest telling of it.
+ const seenPay = new Set();
+ out.movements = unionById(a.movements, b.movements)
+ .sort((m, n) => (m.ts || 0) - (n.ts || 0))
+ .filter((m) => {
+ if (!m.type || !m.type.startsWith('ln-') || m.status !== 'complete') return true;
+ const key = m.type + ':' + (m.paymentHash || m.preimage || m.invoice || m.id);
+ if (seenPay.has(key)) return false;
+ seenPay.add(key);
+ return true;
+ });
const gifts = new Map((a.gifts || []).map((g) => [g.id, { ...g }]));
for (const rg of b.gifts || []) {
const lg = gifts.get(rg.id);