Skip to content

Commit 02ebdd1

Browse files
committed
test: add lnurl msat pay/withdraw cases
1 parent 3819694 commit 02ebdd1

1 file changed

Lines changed: 100 additions & 1 deletion

File tree

test/specs/lnurl.e2e.ts

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,36 @@ function waitForEvent(lnurlServer: any, name: string): Promise<void> {
5454
});
5555
}
5656

57+
function spendingBalanceLabelSats(satsInteger: number): string {
58+
return satsInteger.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
59+
}
60+
61+
/** Balance in msats after pay (subtract) or withdraw (add) from a prior msat total. */
62+
function applyLnurlMsatDelta(balanceMsats: bigint, deltaMsats: number, direction: 'pay' | 'withdraw'): bigint {
63+
const d = BigInt(deltaMsats);
64+
return direction === 'pay' ? balanceMsats - d : balanceMsats + d;
65+
}
66+
67+
async function expectMoneyTextRoundedSats(
68+
parentTestId: 'ReviewAmount-primary' | 'WithdrawAmount-primary',
69+
msats: number,
70+
) {
71+
const money = await elementByIdWithin(parentTestId, 'MoneyText');
72+
const raw = await money.getText();
73+
const digits = raw.replace(/[^\d]/g, '');
74+
const displayed = Number(digits);
75+
if (Number.isNaN(displayed)) {
76+
throw new Error(`MoneyText is not numeric: raw=${JSON.stringify(raw)} msats=${msats}`);
77+
}
78+
const floorSats = Math.floor(msats / 1000);
79+
const ceilSats = Math.ceil(msats / 1000);
80+
if (displayed !== floorSats && displayed !== ceilSats) {
81+
throw new Error(
82+
`Unexpected MoneyText: raw=${JSON.stringify(raw)} displayed=${displayed} expected=${floorSats}|${ceilSats} msats=${msats}`,
83+
);
84+
}
85+
}
86+
5787
describe('@lnurl - LNURL', () => {
5888
let electrum: Awaited<ReturnType<typeof initElectrum>> | undefined;
5989
let lnurlServer: any;
@@ -98,7 +128,7 @@ describe('@lnurl - LNURL', () => {
98128
});
99129

100130
ciIt(
101-
'@lnurl_1 - Can process lnurl-channel, lnurl-pay, lnurl-withdraw, and lnurl-auth',
131+
'@lnurl_1 - Can process lnurl-channel, lnurl-pay, lnurl-withdraw, lnurl-auth, and msat-precision pay/withdraw',
102132
async () => {
103133
await receiveOnchainFunds({ sats: 1000 });
104134

@@ -309,6 +339,75 @@ describe('@lnurl - LNURL', () => {
309339
await swipeFullScreen('down');
310340
await swipeFullScreen('down');
311341

342+
// Fixed min==max LNURL amounts in msats (LND invoice uses value_msat). Each pair pays then withdraws the same amount so balance returns to 19 713 sats.
343+
// 222538 — remainder 538 msats (regression: payment must not truncate msats).
344+
// 222222 — remainder 222 msats (< 500).
345+
// 500500 — remainder 500 msats exactly.
346+
let balanceMsats = 19713000n;
347+
348+
async function msatPayWithdraw(label: string, msats: number) {
349+
const afterPay = applyLnurlMsatDelta(balanceMsats, msats, 'pay');
350+
const afterWithdraw = applyLnurlMsatDelta(afterPay, msats, 'withdraw');
351+
352+
const payReq = await lnurlServer.generateNewUrl('payRequest', {
353+
minSendable: msats,
354+
maxSendable: msats,
355+
metadata: `[["text/plain","lnurl-msat-${label}"]]`,
356+
commentAllowed: 0,
357+
});
358+
console.log(`payRequest msat ${label}`, payReq);
359+
360+
await enterAddressViaScanPrompt(payReq.encoded, { acceptCameraPermission: false });
361+
await sleep(2000);
362+
await elementById('ReviewAmount-primary').waitForDisplayed({ timeout: 5000 });
363+
await elementById('CommentInput').waitForDisplayed({ reverse: true });
364+
await expectMoneyTextRoundedSats('ReviewAmount-primary', msats);
365+
await dragOnElement('GRAB', 'right', 0.95);
366+
await elementById('SendSuccess').waitForDisplayed();
367+
await tap('Close');
368+
balanceMsats = afterPay;
369+
await expectTextWithin(
370+
'ActivitySpending',
371+
spendingBalanceLabelSats(Number(balanceMsats / 1000n)),
372+
);
373+
await elementById('ActivityShort-0').waitForDisplayed();
374+
await expectTextWithin('ActivityShort-0', '-');
375+
await expectTextWithin('ActivityShort-0', 'Sent');
376+
await sleep(1000);
377+
await swipeFullScreen('down');
378+
await swipeFullScreen('down');
379+
380+
const wReq = await lnurlServer.generateNewUrl('withdrawRequest', {
381+
minWithdrawable: msats,
382+
maxWithdrawable: msats,
383+
defaultDescription: `lnurl-withdraw-msat-${label}`,
384+
});
385+
console.log(`withdrawRequest msat ${label}`, wReq);
386+
387+
await enterAddressViaScanPrompt(wReq.encoded, { acceptCameraPermission: false });
388+
await sleep(2000);
389+
await elementById('WithdrawAmount-primary').waitForDisplayed({ timeout: 5000 });
390+
await expectMoneyTextRoundedSats('WithdrawAmount-primary', msats);
391+
await tap('WithdrawConfirmButton');
392+
await acknowledgeReceivedPayment();
393+
balanceMsats = afterWithdraw;
394+
await expectTextWithin(
395+
'ActivitySpending',
396+
spendingBalanceLabelSats(Number(balanceMsats / 1000n)),
397+
);
398+
await elementById('ActivityShort-0').waitForDisplayed();
399+
await expectTextWithin('ActivityShort-0', '+');
400+
await expectTextWithin('ActivityShort-0', `lnurl-withdraw-msat-${label}`);
401+
await expectTextWithin('ActivityShort-0', 'Received');
402+
await sleep(1000);
403+
await swipeFullScreen('down');
404+
await swipeFullScreen('down');
405+
}
406+
407+
await msatPayWithdraw('222538', 222_538);
408+
await msatPayWithdraw('222222', 222_222);
409+
await msatPayWithdraw('500500', 500_500);
410+
312411
// lnurl-auth
313412
const loginRequest1 = await lnurlServer.generateNewUrl('login');
314413
console.log('loginRequest1', loginRequest1);

0 commit comments

Comments
 (0)