diff options
| author | Adam Soltys <[email protected]> | 2026-09-06 17:03:09 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-06 17:03:09 -0700 |
| commit | dcfabe9165e7b0f48d9d3f645f762b84e6bc48b8 (patch) | |
| tree | a235ad01426ec1f60a106dd64762f4f1a726859c /src/features | |
| parent | 5119fcab671646454c10563c7b79741728d33a03 (diff) | |
| download | coinosv3-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/features')
| -rw-r--r-- | src/features/gifts.js | 20 |
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); |
