aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-06 17:03:09 -0700
committerAdam Soltys <[email protected]>2026-09-06 17:03:09 -0700
commitdcfabe9165e7b0f48d9d3f645f762b84e6bc48b8 (patch)
treea235ad01426ec1f60a106dd64762f4f1a726859c /src
parent5119fcab671646454c10563c7b79741728d33a03 (diff)
downloadcoinosv3-dcfabe9165e7b0f48d9d3f645f762b84e6bc48b8.tar.gz
coinosv3-dcfabe9165e7b0f48d9d3f645f762b84e6bc48b8.zip
On-chain gift claims can no longer strand funds at an address the claimer's backed-up seed doesn't control. A claim into a fresh wallet reportedly paid a DIFFERENT wallet's address than the seed shown for backup (5000-sat gift claimed in incognito → funds landed at an accumulator address the given seed couldn't derive), because the destination was wallet.receive[0] — which can be a stale address a previous account in the session left behind. Now the claim derives THIS wallet's own address fresh from the active seed (never receive[0]), and a hard guard refuses to broadcast unless that address is provably derivable from the wallet whose recovery phrase is about to be handed over — any future seed/wallet divergence becomes a safe error, not lost money
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
Diffstat (limited to 'src')
-rw-r--r--src/features/gifts.js20
1 files changed, 19 insertions, 1 deletions
diff --git a/src/features/gifts.js b/src/features/gifts.js
index 12dd735..2b8c6c7 100644
--- a/src/features/gifts.js
+++ b/src/features/gifts.js
@@ -427,7 +427,25 @@ export function giftsFeature(ctx) {
}
try {
const rate = (wallet.feeRates && wallet.feeRates.halfHourFee) || 5;
- const to = wallet.receive[0] ? wallet.receive[0].address : wallet.derive(0, 0).address;
+ // Claim to THIS wallet's own first address, derived fresh from the
+ // active seed — never wallet.receive[0], which can be a stale address
+ // left by a previous account in the session. (A gift once landed at an
+ // address the claimer's backed-up seed didn't control — 5000 sats
+ // stranded — because the claim paid a wallet the shown seed wasn't.)
+ const to = wallet.derive(0, 0).address;
+ // Hard guard: refuse to broadcast unless `to` is provably derivable
+ // from the wallet whose recovery phrase we're about to hand over. This
+ // turns any future seed/wallet divergence into a safe error instead of
+ // money sent to keys the claimer never gets.
+ const ownsTo = (() => {
+ try {
+ for (let i = 0; i < 5; i++) {
+ if (wallet.derive(0, i).address === to || wallet.derive(1, i).address === to) return true;
+ }
+ } catch {}
+ return false;
+ })();
+ if (!ownsTo) { ui.claimError = t('claimFailed'); ui.busy = false; render(); return; }
const claim = buildClaimTx(ui.claimCode, to, rate, wallet.netCfg.net);
await wallet.broadcast(claim.hex);
afterClaim(claim.amount);