aboutsummaryrefslogtreecommitdiffstats
path: root/src/wallet.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-06-25 16:01:39 -0700
committerAdam Soltys <[email protected]>2026-06-25 16:01:39 -0700
commit124fee3c15556d62d6efd8714fd4501e95273ebd (patch)
treee8302cf0a2e913f11fc8a9bb883d3b99dbc427d2 /src/wallet.js
parent5ed8b673bb0d6981a834d3b4d677c6678f0bd1dd (diff)
downloadcoinosv3-124fee3c15556d62d6efd8714fd4501e95273ebd.tar.gz
coinosv3-124fee3c15556d62d6efd8714fd4501e95273ebd.zip
Gift claim: detect already-claimed (even unconfirmed) and block the race
Opening a gift link now checks whether its funding coin is already spent — via the Esplora outspend endpoint, which reports a spend even by an unconfirmed claim. The Claim button is gated until that check passes; if the coin is spent, show an 'Already claimed' screen with a link to the claim tx instead of a claimable amount. doClaim also re-checks on broadcast failure (the race window), and refuses to run while unchecked/taken — so a second opener can't attempt a doomed double-spend. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src/wallet.js')
-rw-r--r--src/wallet.js16
1 files changed, 16 insertions, 0 deletions
diff --git a/src/wallet.js b/src/wallet.js
index ccd043a..e21710c 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -1748,6 +1748,22 @@ function _sumInputs(tx) {
function _giftChange(tx) {
return tx.outputsLength > 0 ? tx.getOutput(0).amount : 0n;
}
+// The gift's funding outpoints (the coins its presigned inputs spend). If any is
+// already spent — even by an unconfirmed claim — the gift has been taken and
+// re-claiming would be a doomed double-spend. Used to gate the claim screen.
+export function giftOutpoints(code) {
+ try {
+ const tx = btc.Transaction.fromPSBT(base64urlnopad.decode(code));
+ const out = [];
+ for (let i = 0; i < tx.inputsLength; i++) {
+ const inp = tx.getInput(i);
+ out.push({ txid: hex.encode(inp.txid), vout: inp.index });
+ }
+ return out;
+ } catch {
+ return [];
+ }
+}
export function previewGift(code) {
try {
const tx = btc.Transaction.fromPSBT(base64urlnopad.decode(code));