aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/nwc.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/features/nwc.js')
-rw-r--r--src/features/nwc.js38
1 files changed, 30 insertions, 8 deletions
diff --git a/src/features/nwc.js b/src/features/nwc.js
index 9c71a7f..a2730f5 100644
--- a/src/features/nwc.js
+++ b/src/features/nwc.js
@@ -30,7 +30,7 @@ import { t } from '../i18n.js';
import { dlog } from '../debug.js';
import { qrSvg } from '../qr.js';
import { getNetwork } from '../api.js';
-import { loadBg, saveBg, clearBg } from '../nwc-bg.js';
+import { loadBg, saveBg, clearBg, readBgLog } from '../nwc-bg.js';
import { encodeNoffer } from '../noffer.js';
import { maybeBolt11 } from '../ark/lightning.js';
import { withRequestLock } from '../nwc-lock.js';
@@ -56,7 +56,7 @@ const nowSec = () => Math.floor(Date.now() / 1000);
const errRes = (code, message) => ({ error: { code, message } });
export function nwcFeature(ctx) {
- const { h, ui, render, wallet, hook, fmtAmount, unitLabel, copyBtn, toast } = ctx;
+ const { h, ui, render, wallet, hook, fmtAmount, unitLabel, copyBtn, copy, toast } = ctx;
// Relay transport, injectable so the protocol can be driven in tests
// without a live relay.
const {
@@ -478,11 +478,21 @@ export function nwcFeature(ctx) {
try {
res = await hook('arkPayInvoice', invoice, { maxAmountSat: c.maxSat });
} catch (e) {
- // The ASP already holds a payment for this invoice: another device
- // of this wallet took the same request first. Its outcome is ours —
- // wait for it rather than report a failure over a zap that is
- // paying. The payer records the spend; this side only relays.
- if (!/already in progress/i.test(e.message || '')) throw e;
+ // A payment for this invoice already exists: ours, begun on an
+ // earlier request (the worker started it while closed, say), or
+ // another device's — the ASP refuses a second HTLC for one invoice.
+ // Its outcome is the answer, never an error whose text says "paid":
+ // a client once read "already paid or is being paid" as paid, hours
+ // before the money moved. The payer records the spend; this side
+ // only relays.
+ if (!/already in progress|already paid/i.test(e.message || '')) throw e;
+ const mine = await hook('arkLnLookup', { paymentHash: dec.paymentHash }).catch(() => null);
+ if (mine && mine.type === 'outgoing' && mine.state === 'settled' && mine.preimage) {
+ return { sibling: true, result: { preimage: mine.preimage, fees_paid: 0 } };
+ }
+ if (mine && mine.type === 'outgoing' && mine.state === 'pending') {
+ return errRes('OTHER', 'a payment for this invoice is still in progress; ask again in a moment');
+ }
const out = await hook('arkLnOutcome', dec.paymentHash, 60000);
if (!out || out.status !== 'success') throw e;
return { sibling: true, result: { preimage: out.preimage, fees_paid: 0 } };
@@ -1093,7 +1103,19 @@ export function nwcFeature(ctx) {
h('button', { class: 'btn-sm', onClick: () => {
const s = load(); s.paySentNotify = !on; save(s); writeBg(); render();
} }, on ? t('nwcOn') : t('nwcOff'))),
- h('div', { class: 'small faint' }, t('notifPaySentHint')))];
+ h('div', { class: 'small faint' }, t('notifPaySentHint'))),
+ // the worker's diary, for the report that says where a closed wallet
+ // got to (a phone stops it without a trace)
+ h('div', { class: 'card col', style: 'gap:10px' },
+ h('div', { class: 'row between' },
+ h('span', { class: 'small' }, t('nwcWorkerLog')),
+ h('button', { class: 'btn-sm', onClick: async () => {
+ const lines = await readBgLog();
+ if (!lines.length) { toast(t('nwcWorkerLogEmpty')); return; }
+ await copy(lines.join('\n'));
+ toast(t('copied'));
+ } }, t('copy'))),
+ h('div', { class: 'small faint' }, t('nwcWorkerLogHint')))];
},
};
}