aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-07-04 08:12:38 -0700
committerAdam Soltys <[email protected]>2026-07-04 08:12:38 -0700
commit1a8373c36706303802fde5148b74ab1634f0048e (patch)
tree8438ba2b8037f1f0b5b3a8ec63adbb54a800c299 /src
parentcbbf90ed86c29f2c7c61b216be5e51946adaef52 (diff)
downloadcoinosv3-1a8373c36706303802fde5148b74ab1634f0048e.tar.gz
coinosv3-1a8373c36706303802fde5148b74ab1634f0048e.zip
Gift claim: name the real failure when the funding tx isn't visible
Claiming a gift whose funding tx is unknown to the claimer's data source (different network/chain, or not yet propagated) used to fail with the misleading "already been claimed". Now: - the claim screen probes the funding tx up front (esplora's status/ outspend endpoints don't 404 for unknown txids, so outspend alone can't detect this) and shows a network/data-source notice, and - a missing-inputs broadcast rejection maps to the same explanation instead of the generic already-claimed message. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
Diffstat (limited to 'src')
-rw-r--r--src/app.js15
-rw-r--r--src/i18n.js1
2 files changed, 15 insertions, 1 deletions
diff --git a/src/app.js b/src/app.js
index 486fa5b..cbac144 100644
--- a/src/app.js
+++ b/src/app.js
@@ -869,6 +869,7 @@ async function activateAccount(acc, opts = {}) {
ui.claimCode = opts.gift || null;
ui.claimError = '';
ui.claimTaken = null;
+ ui.claimNotVisible = false;
ui.claimChecking = !!opts.gift; // gate the Claim button until we've checked
// Restore the tab / open tx from the last session so a refresh keeps the
// user's place. A gift link always opens the claim screen instead.
@@ -2210,6 +2211,13 @@ async function checkGiftClaimed(code) {
try {
const res = await wallet.api.outspend(ops[0].txid, ops[0].vout);
if (res && res.spent) ui.claimTaken = { txid: res.txid || null };
+ // Unknown txids don't 404 on the status/outspend endpoints, so verify the
+ // funding tx is actually visible — if not, the claimer is likely on a
+ // different network/data source than the sender (or it hasn't propagated).
+ if (!ui.claimTaken) {
+ const tx = await wallet.api.getTx(ops[0].txid).catch(() => null);
+ ui.claimNotVisible = !tx;
+ }
} catch {
// Couldn't check (offline/transient) — leave it claimable; the broadcast in
// doClaim is the final guard (a double-spend is rejected by the network).
@@ -2251,7 +2259,11 @@ async function doClaim() {
if (res && res.spent) ui.claimTaken = { txid: res.txid || null };
} catch {}
}
- if (!ui.claimTaken) ui.claimError = t('claimFailed');
+ if (!ui.claimTaken) {
+ ui.claimError = /missing.?inputs|missingorspent|bad-txns-inputs/i.test(String(e && e.message))
+ ? t('claimNotVisibleBody') // funding tx unknown here: wrong chain/source
+ : t('claimFailed');
+ }
}
ui.busy = false;
render();
@@ -2443,6 +2455,7 @@ function claimScreen() {
return acc && !acc.provisional ? t('claimBodyExisting', { name: acc.label }) : t('claimBody'); })())
),
ui.claimError && h('div', { class: 'notice err' }, ui.claimError),
+ !ui.claimError && ui.claimNotVisible && h('div', { class: 'notice info' }, t('claimNotVisibleBody')),
ui.busy || ui.claimChecking
? h('button', { class: 'btn-primary btn-block', disabled: true }, h('span', { class: 'spinner' }))
: h('button', { class: 'btn-primary btn-block', onClick: doClaim },
diff --git a/src/i18n.js b/src/i18n.js
index 83af70c..ec4721e 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -298,6 +298,7 @@ const STR = {
claimBtn: 'Claim it',
claimFeeNote: '~{n} fee',
claimFailed: 'This gift has already been claimed or is no longer available.',
+ claimNotVisibleBody: 'The coins funding this gift aren\u2019t visible from this wallet\u2019s network/data source. Make sure this wallet uses the same network and data source as the sender, then try again.',
claimedTitle: 'Claimed!',
claimedBody: 'Your Bitcoin is on its way. Back up your recovery phrase to keep it safe.',
recoveryWarn: '⚠ Anyone who can see these words can steal your funds. Make sure nobody is watching before you reveal them.',