aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.js19
-rw-r--r--src/swap.js6
-rw-r--r--src/wallet.js30
3 files changed, 42 insertions, 13 deletions
diff --git a/src/app.js b/src/app.js
index f43fe1c..008765e 100644
--- a/src/app.js
+++ b/src/app.js
@@ -2602,11 +2602,7 @@ function receiveTab() {
// Until receiveSeenIndex has been baselined (post-scan, in enterWallet) it
// stays null and we never celebrate. The recency guard additionally ensures an
// already-old payment never celebrates when a wallet is opened.
- // While the Lightning "Received!" card is up (a reverse-swap claim is also an
- // on-chain receive that advances the index), suppress this generic celebration
- // so the two don't stack — its Done dismisses both.
- const lnClaimShown = !!(ui.lnReceiveId && (() => { const r = swaps.list().find((s) => s.id === ui.lnReceiveId); return r && ['claimed', 'success'].includes(r.status); })());
- if (!lnClaimShown && ui.receiveSeenIndex != null && wallet.nextReceiveIndex > ui.receiveSeenIndex && hasRecentIncoming()) {
+ if (ui.receiveSeenIndex != null && wallet.nextReceiveIndex > ui.receiveSeenIndex && hasRecentIncoming()) {
// Mark it acknowledged as soon as it's shown — so a refresh or navigating
// away (without tapping) won't bring the celebration back. It stays visible
// this session (ui.receiveSeenIndex is unchanged) until the tap below.
@@ -2670,14 +2666,13 @@ function lnReceiveContent() {
swaps.reverseLimits().then((l) => { ui.swapLimits = l; ui._swapLimitsLoading = false; render(); }).catch(() => { ui._swapLimitsLoading = false; });
}
const lim = ui.swapLimits;
- const rec = ui.lnReceiveId ? swaps.list().find((s) => s.id === ui.lnReceiveId) : null;
+ let rec = ui.lnReceiveId ? swaps.list().find((s) => s.id === ui.lnReceiveId) : null;
if (rec && ['claimed', 'success'].includes(rec.status)) {
- return [
- h('div', { class: 'check-badge' }, '✓'),
- h('h3', { style: 'margin:0' }, t('lnReceived')),
- rec.received ? h('div', { class: 'amount-pos', style: 'font-size:18px' }, '+' + fmtAmount(rec.received) + ' ' + unitLabel()) : null,
- h('button', { class: 'btn-block', onClick: () => { ui.lnReceiveId = null; ui.swapReverseAmt = ''; ui.receiveSeenIndex = wallet.nextReceiveIndex; wallet.setReceiveAck(wallet.nextReceiveIndex); render(); } }, t('done')),
- ];
+ // Claimed — the coin is credited on-chain (swap.js → creditReceive), so the
+ // generic "Payment received!" celebration shows it like any receive. Just
+ // reset the Lightning flow back to its form.
+ ui.lnReceiveId = null;
+ rec = null;
}
if (rec) {
let svg = null;
diff --git a/src/swap.js b/src/swap.js
index 129712d..9afe7e7 100644
--- a/src/swap.js
+++ b/src/swap.js
@@ -318,10 +318,14 @@ export class SwapManager {
const preimage = sha256(node.privateKey);
const d = swapP2TR({ kind: 'reverse', preimageHash160: hash160(preimage), boltzPub33: hexToBytes(rec.refundPublicKey), halPub33: node.publicKey, lockTime: rec.timeoutBlockHeight, network: net });
const fee = this._spendFee();
- const hex = buildSwapSpend({ pay: d.pay, leaf: d.claimLeaf, halPriv: node.privateKey, preimage, lockupTxid: u.txid, lockupVout: u.vout, lockupValue: u.value, destAddress: w.freshReceive().address, fee, network: net });
+ const destAddress = w.freshReceive().address;
+ const hex = buildSwapSpend({ pay: d.pay, leaf: d.claimLeaf, halPriv: node.privateKey, preimage, lockupTxid: u.txid, lockupVout: u.vout, lockupValue: u.value, destAddress, fee, network: net });
const txid = await w.api.broadcast(hex);
rec.status = 'claimed'; rec.claimTxid = txid; rec.received = u.value - fee;
this._save(rec); this._stop(id);
+ // Credit the claimed coin (single output → destAddress) now, so the balance
+ // + generic "payment received" fire instantly instead of waiting for a scan.
+ if (w.creditReceive) w.creditReceive({ txid, vout: 0, value: rec.received, address: destAddress });
} catch (e) { rec.lastError = String(e?.message || e); this.onUpdate(rec); }
};
this.timers.set(id, setInterval(tick, 3000)); tick();
diff --git a/src/wallet.js b/src/wallet.js
index 596ca1b..1972f22 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -1922,6 +1922,36 @@ export class Wallet {
return true;
}
+ // Credit an on-chain receive we produced ourselves (a reverse-swap claim) the
+ // instant we broadcast it — so the balance + generic "payment received" fire
+ // without waiting for the next scan, exactly like the watcher does for inbound
+ // payments. `address` must be one of our receive (chain-0) addresses; returns
+ // false (caller relies on the scan) if it isn't known or the coin already exists.
+ creditReceive({ txid, vout, value, address }) {
+ let entry = this.receive.find((e) => e.address === address);
+ if (!entry) {
+ const info = this.addrMap.get(address);
+ if (info && info.chain === 0) entry = this.receive.find((e) => e.index === info.index);
+ }
+ if (!entry) return false;
+ if (this.utxos.find((u) => utxoId(u) === `${txid}:${vout}`)) return false;
+ this.utxos.push({ txid, vout, value, address, chain: 0, index: entry.index, confirmed: false });
+ entry.used = true;
+ entry.pending = (entry.pending || 0) + value;
+ if (!this.txs.find((t) => t.txid === txid)) {
+ this.txs.push({ txid, net: value, fee: 0, vsize: 0, confirmed: false, blockTime: 0, blockHeight: 0, firstSeen: Date.now() });
+ }
+ this.nextReceiveIndex = firstUnused(this.receive);
+ this.utxos.sort((a, b) => b.value - a.value);
+ this._sortTxs();
+ this._recomputeBalanceFromUtxos();
+ this.loaded = true;
+ this.saveCache();
+ this.retrack();
+ this.emit();
+ return true;
+ }
+
// Optimistically reflect a just-broadcast outgoing tx so the balance and
// history update instantly instead of waiting for the post-send rescan: drop
// the spent coins, credit our change as pending, and add a pending history