From 25b2d317f3d528f0cb1d81caddd274b7881e483e Mon Sep 17 00:00:00 2001 From: tompro Date: Tue, 16 Jan 2024 18:26:24 +0100 Subject: [PATCH 01/10] page navigation and withdraw - route scan/paste navigation to either /send or /receive - similar to /send handling use onMount and global state to communicate and trigger lnUrl handling - experimental trigger lnurlWithdrawal --- src/routes/Receive.tsx | 36 ++++++++++++++++++++++++ src/routes/Scanner.tsx | 34 +++++++++++++++++++--- src/routes/Send.tsx | 62 ++++++++++++++++++++++------------------- src/state/megaStore.tsx | 9 +++++- 4 files changed, 108 insertions(+), 33 deletions(-) diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index 6a347fb3..4fff6ea1 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -1,6 +1,7 @@ /* @refresh reload */ import { + LnUrlParams, MutinyBip21RawMaterials, MutinyInvoice } from "@mutinywallet/mutiny-wasm"; @@ -12,6 +13,7 @@ import { createSignal, Match, onCleanup, + onMount, Show, Switch } from "solid-js"; @@ -328,6 +330,40 @@ export function Receive() { } } + // If we got here from an LNUrl withdrawal request + onMount(() => { + if (state.scan_result && state.scan_result.lnurl && state.lnUrlParams) { + handleLnUrlWithdrawal(state.scan_result.lnurl, state.lnUrlParams); + actions.setScanResult(undefined); + actions.setLnUrlParams(undefined); + } + }); + + async function handleLnUrlWithdrawal( + lnurl: string, + lnUrlParams: LnUrlParams + ) { + console.log("handleLnUrlWithdrawal", lnurl, lnUrlParams); + setError(""); + setFlavor("lightning"); + setLoading(true); + setAmount(lnUrlParams.max); + setReceiveState("show"); + if (lnUrlParams.min === lnUrlParams.max) { + const success = await state.mutiny_wallet?.lnurl_withdraw( + lnurl, + lnUrlParams.max / 1000n + ); + if (success) { + setReceiveState("paid"); + setLoading(false); + await vibrateSuccess(); + } else { + setError("lnurl_withdraw failed"); + } + } + } + function selectFlavor(flavor: string) { setFlavor(flavor as ReceiveFlavor); if (rememberChoice()) { diff --git a/src/routes/Scanner.tsx b/src/routes/Scanner.tsx index 72383e45..c027e132 100644 --- a/src/routes/Scanner.tsx +++ b/src/routes/Scanner.tsx @@ -1,15 +1,17 @@ import { Clipboard } from "@capacitor/clipboard"; import { Capacitor } from "@capacitor/core"; +import { LnUrlParams } from "@mutinywallet/mutiny-wasm"; import { useNavigate } from "@solidjs/router"; import { createEffect, createSignal } from "solid-js"; import { Button, Scanner as Reader, showToast } from "~/components"; import { useI18n } from "~/i18n/context"; import { useMegaStore } from "~/state/megaStore"; +import { eify } from "~/utils"; export function Scanner() { const i18n = useI18n(); - const [_state, actions] = useMegaStore(); + const [state, actions] = useMegaStore(); const [scanResult, setScanResult] = createSignal(); const navigate = useNavigate(); @@ -40,7 +42,19 @@ export function Scanner() { } } - // When we have a nice result we can head over to the send screen + function handleLnUrl( + lnUrl: string, + success: (result: LnUrlParams) => void + ) { + state.mutiny_wallet + ?.decode_lnurl(lnUrl) + .then((lnurlParams) => { + success(lnurlParams); + }) + .catch((e) => showToast(eify(e))); + } + + // When we have a nice result we can head over to the next screen createEffect(() => { if (scanResult()) { actions.handleIncomingString( @@ -49,8 +63,20 @@ export function Scanner() { showToast(error); }, (result) => { - actions.setScanResult(result); - navigate("/send"); + if (result.lnurl && !result.is_lnurl_auth) { + handleLnUrl(result.lnurl, (lnurlParams) => { + actions.setScanResult(result); + actions.setLnUrlParams(lnurlParams); + if (lnurlParams.tag === "withdrawRequest") { + navigate("/receive"); + } else { + navigate("/send"); + } + }); + } else { + actions.setScanResult(result); + navigate("/send"); + } } ); } diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx index 6e856f8d..5fe4d7dc 100644 --- a/src/routes/Send.tsx +++ b/src/routes/Send.tsx @@ -1,4 +1,4 @@ -import { MutinyInvoice, TagItem } from "@mutinywallet/mutiny-wasm"; +import { LnUrlParams, MutinyInvoice, TagItem } from "@mutinywallet/mutiny-wasm"; import { useNavigate, useSearchParams } from "@solidjs/router"; import { createEffect, @@ -361,7 +361,10 @@ export function Send() { const [parsingDestination, setParsingDestination] = createSignal(false); - function handleDestination(source: ParsedParams | undefined) { + function handleDestination( + source: ParsedParams | undefined, + lnUrlParams: LnUrlParams | undefined + ) { if (!source) return; setParsingDestination(true); setOriginalScan(source.original); @@ -380,7 +383,10 @@ export function Send() { ); } else if (source.lnurl) { console.log("processing lnurl"); - processLnurl(source as ParsedParams & { lnurl: string }); + processLnurl( + source as ParsedParams & { lnurl: string }, + lnUrlParams + ); } else { setAmountSats(source.amount_sats || 0n); if (source.amount_sats) setIsAmtEditable(false); @@ -424,31 +430,30 @@ export function Send() { } // A ParsedParams with an lnurl in it - function processLnurl(source: ParsedParams & { lnurl: string }) { - state.mutiny_wallet - ?.decode_lnurl(source.lnurl) - .then((lnurlParams) => { - if (lnurlParams.tag === "payRequest") { - if (lnurlParams.min == lnurlParams.max) { - setAmountSats(lnurlParams.min / 1000n); - setIsAmtEditable(false); - } else { - setAmountSats(source.amount_sats || 0n); - } + function processLnurl( + source: ParsedParams & { lnurl: string }, + lnurlParams?: LnUrlParams + ) { + console.log("processing lnurl", source.lnurl, lnurlParams); + if (lnurlParams) { + if (lnurlParams.min == lnurlParams.max) { + setAmountSats(lnurlParams.min / 1000n); + setIsAmtEditable(false); + } else { + setAmountSats(source.amount_sats || 0n); + } - if (source.lightning_address) { - setLnAddress(source.lightning_address); - setIsHodlInvoice( - source.lightning_address - .toLowerCase() - .includes("zeuspay.com") - ); - } - setLnurlp(source.lnurl); - setSource("lightning"); - } - }) - .catch((e) => showToast(eify(e))); + if (source.lightning_address) { + setLnAddress(source.lightning_address); + setIsHodlInvoice( + source.lightning_address + .toLowerCase() + .includes("zeuspay.com") + ); + } + setLnurlp(source.lnurl); + setSource("lightning"); + } } createEffect(() => { @@ -472,8 +477,9 @@ export function Send() { // If we got here from a scan or search onMount(() => { if (state.scan_result) { - handleDestination(state.scan_result); + handleDestination(state.scan_result, state.lnUrlParams); actions.setScanResult(undefined); + actions.setLnUrlParams(undefined); } }); diff --git a/src/state/megaStore.tsx b/src/state/megaStore.tsx index e4e8b9e7..fb540f50 100644 --- a/src/state/megaStore.tsx +++ b/src/state/megaStore.tsx @@ -2,6 +2,7 @@ // Inspired by https://github.com/solidjs/solid-realworld/blob/main/src/store/index.js import { + LnUrlParams, MutinyBalance, MutinyWallet, TagItem @@ -73,11 +74,13 @@ type MegaStore = [ testflightPromptDismissed: boolean; should_zap_hodl: boolean; federations?: MutinyFederationIdentity[]; + lnUrlParams?: LnUrlParams; }, { setup(password?: string): Promise; deleteMutinyWallet(): Promise; setScanResult(scan_result: ParsedParams | undefined): void; + setLnUrlParams(lnUrlParams: LnUrlParams | undefined): void; sync(): Promise; setHasBackedUp(): void; listTags(): Promise; @@ -141,7 +144,8 @@ export const Provider: ParentComponent = (props) => { should_zap_hodl: localStorage.getItem("should_zap_hodl") === "true", testflightPromptDismissed: localStorage.getItem("testflightPromptDismissed") === "true", - federations: undefined as MutinyFederationIdentity[] | undefined + federations: undefined as MutinyFederationIdentity[] | undefined, + lnUrlParams: undefined as LnUrlParams | undefined }); const actions = { @@ -327,6 +331,9 @@ export const Provider: ParentComponent = (props) => { return []; } }, + setLnUrlParams(lnUrlParams: LnUrlParams | undefined) { + setState({ lnUrlParams }); + }, async saveFiat(fiat: Currency) { localStorage.setItem("fiat_currency", JSON.stringify(fiat)); const price = await actions.fetchPrice(fiat); From b2fb43648220c8c77f126f8bfd843a62606df264 Mon Sep 17 00:00:00 2001 From: tompro Date: Thu, 18 Jan 2024 10:44:19 +0100 Subject: [PATCH 02/10] Variable amount - allow non fixed amt lnurl withdrawal - fix paste ln url in send --- src/routes/Receive.tsx | 94 +++++++++++++++++++++++++++++++---------- src/routes/Scanner.tsx | 6 ++- src/routes/Send.tsx | 54 ++++++++++++----------- src/state/megaStore.tsx | 15 ++++--- 4 files changed, 117 insertions(+), 52 deletions(-) diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index 4fff6ea1..cda8bb25 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -48,7 +48,7 @@ import { VStack } from "~/components"; import { useI18n } from "~/i18n/context"; -import { useMegaStore } from "~/state/megaStore"; +import { LnUrlData, useMegaStore } from "~/state/megaStore"; import { eify, objectToSearchParams, vibrateSuccess } from "~/utils"; type OnChainTx = { @@ -137,6 +137,9 @@ export function Receive() { const [detailsOpen, setDetailsOpen] = createSignal(false); const [detailsKind, setDetailsKind] = createSignal(); const [detailsId, setDetailsId] = createSignal(""); + const [lnUrlData, setLnUrlData] = createSignal(); + const [fixedAmount, setFixedAmount] = createSignal(false); + const [lnUrlExecuted, setLnUrlExecuted] = createSignal(false); const RECEIVE_FLAVORS = [ { @@ -179,6 +182,7 @@ export function Receive() { setPaymentInvoice(undefined); setError(""); setFlavor(state.preferredInvoiceType); + setLnUrlData(undefined); } function openDetailsModal() { @@ -274,8 +278,12 @@ export function Receive() { async function onSubmit(e: Event) { e.preventDefault(); - - await getQr(); + const lnUrl = lnUrlData(); + if (!lnUrl) { + await getQr(); + } else { + await handleLnUrlWithdrawalSubmit(lnUrl); + } } async function getQr() { @@ -332,38 +340,80 @@ export function Receive() { // If we got here from an LNUrl withdrawal request onMount(() => { - if (state.scan_result && state.scan_result.lnurl && state.lnUrlParams) { - handleLnUrlWithdrawal(state.scan_result.lnurl, state.lnUrlParams); + if (state.lnUrlData) { + handleLnUrlWithdrawal(state.lnUrlData); actions.setScanResult(undefined); - actions.setLnUrlParams(undefined); + actions.setLnUrlData(undefined); } }); - async function handleLnUrlWithdrawal( - lnurl: string, - lnUrlParams: LnUrlParams - ) { - console.log("handleLnUrlWithdrawal", lnurl, lnUrlParams); + function handleLnUrlWithdrawal(lnUrlData: LnUrlData) { + console.log("handleLnUrlWithdrawal", lnUrlData.lnurl, lnUrlData.params); + setLnUrlData(lnUrlData); setError(""); setFlavor("lightning"); - setLoading(true); - setAmount(lnUrlParams.max); - setReceiveState("show"); + const lnUrlParams = lnUrlData.params; + if (lnUrlParams.min === lnUrlParams.max) { - const success = await state.mutiny_wallet?.lnurl_withdraw( - lnurl, - lnUrlParams.max / 1000n + setAmount(mSatsToSats(lnUrlParams.max)); + } else { + setAmount(mSatsToSats(lnUrlParams.min)); + } + + setReceiveState("edit"); + } + + async function handleLnUrlWithdrawalSubmit(lnUrlData: LnUrlData) { + if (lnUrlData) { + const lnurl = lnUrlData.lnurl; + const lnUrlParams = lnUrlData.params; + const amt = amount(); + const amtError = validateAmount( + amt, + mSatsToSats(lnUrlParams.min), + mSatsToSats(lnUrlParams.max) ); - if (success) { - setReceiveState("paid"); + if (amtError) { + showToast(new Error(amtError)); + return; + } + setLoading(true); + setReceiveState("show"); + try { + const success = await state.mutiny_wallet?.lnurl_withdraw( + lnurl, + amount() + ); + if (!success) { + setError("lnurl_withdraw failed"); + } else { + setReceiveState("paid"); + } + } catch (e) { + console.error("lnurl_withdraw failed", e); + showToast(eify(e)); + } finally { + setLnUrlExecuted(true); setLoading(false); - await vibrateSuccess(); - } else { - setError("lnurl_withdraw failed"); } } } + function validateAmount( + amount: bigint, + min: bigint, + max: bigint + ): string | undefined { + console.log("validateAmount", amount, min, max); + if (amount === 0n) return "amount is zero"; + if (amount < min) return "amount smaller min"; + if (amount > max * 1000n) return "amount greater max"; + } + + function mSatsToSats(mSats: bigint) { + return mSats / 1000n; + } + function selectFlavor(flavor: string) { setFlavor(flavor as ReceiveFlavor); if (rememberChoice()) { diff --git a/src/routes/Scanner.tsx b/src/routes/Scanner.tsx index c027e132..16cff574 100644 --- a/src/routes/Scanner.tsx +++ b/src/routes/Scanner.tsx @@ -64,9 +64,13 @@ export function Scanner() { }, (result) => { if (result.lnurl && !result.is_lnurl_auth) { + const lnurl = result.lnurl; handleLnUrl(result.lnurl, (lnurlParams) => { actions.setScanResult(result); - actions.setLnUrlParams(lnurlParams); + actions.setLnUrlData({ + lnurl: lnurl, + params: lnurlParams + }); if (lnurlParams.tag === "withdrawRequest") { navigate("/receive"); } else { diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx index 5fe4d7dc..b597f70a 100644 --- a/src/routes/Send.tsx +++ b/src/routes/Send.tsx @@ -47,7 +47,7 @@ import { } from "~/components"; import { useI18n } from "~/i18n/context"; import { ParsedParams } from "~/logic/waila"; -import { useMegaStore } from "~/state/megaStore"; +import { LnUrlData, useMegaStore } from "~/state/megaStore"; import { eify, vibrateSuccess } from "~/utils"; export type SendSource = "lightning" | "onchain"; @@ -363,7 +363,7 @@ export function Send() { function handleDestination( source: ParsedParams | undefined, - lnUrlParams: LnUrlParams | undefined + lnUrlParams: LnUrlData | undefined ) { if (!source) return; setParsingDestination(true); @@ -432,28 +432,34 @@ export function Send() { // A ParsedParams with an lnurl in it function processLnurl( source: ParsedParams & { lnurl: string }, - lnurlParams?: LnUrlParams + lnurlData?: LnUrlData ) { - console.log("processing lnurl", source.lnurl, lnurlParams); - if (lnurlParams) { - if (lnurlParams.min == lnurlParams.max) { - setAmountSats(lnurlParams.min / 1000n); - setIsAmtEditable(false); - } else { - setAmountSats(source.amount_sats || 0n); - } + const promise = lnurlData + ? Promise.resolve(lnurlData.params) + : state.mutiny_wallet?.decode_lnurl(source.lnurl); + promise + ?.then((lnurlParams) => { + if (lnurlParams.tag === "payRequest") { + if (lnurlParams.min == lnurlParams.max) { + setAmountSats(lnurlParams.min / 1000n); + setIsAmtEditable(false); + } else { + setAmountSats(source.amount_sats || 0n); + } - if (source.lightning_address) { - setLnAddress(source.lightning_address); - setIsHodlInvoice( - source.lightning_address - .toLowerCase() - .includes("zeuspay.com") - ); - } - setLnurlp(source.lnurl); - setSource("lightning"); - } + if (source.lightning_address) { + setLnAddress(source.lightning_address); + setIsHodlInvoice( + source.lightning_address + .toLowerCase() + .includes("zeuspay.com") + ); + } + setLnurlp(source.lnurl); + setSource("lightning"); + } + }) + .catch((e) => showToast(eify(e))); } createEffect(() => { @@ -477,9 +483,9 @@ export function Send() { // If we got here from a scan or search onMount(() => { if (state.scan_result) { - handleDestination(state.scan_result, state.lnUrlParams); + handleDestination(state.scan_result, state.lnUrlData); actions.setScanResult(undefined); - actions.setLnUrlParams(undefined); + actions.setLnUrlData(undefined); } }); diff --git a/src/state/megaStore.tsx b/src/state/megaStore.tsx index fb540f50..03a6b400 100644 --- a/src/state/megaStore.tsx +++ b/src/state/megaStore.tsx @@ -74,13 +74,13 @@ type MegaStore = [ testflightPromptDismissed: boolean; should_zap_hodl: boolean; federations?: MutinyFederationIdentity[]; - lnUrlParams?: LnUrlParams; + lnUrlData?: LnUrlData; }, { setup(password?: string): Promise; deleteMutinyWallet(): Promise; setScanResult(scan_result: ParsedParams | undefined): void; - setLnUrlParams(lnUrlParams: LnUrlParams | undefined): void; + setLnUrlData(lnUrlData: LnUrlData | undefined): void; sync(): Promise; setHasBackedUp(): void; listTags(): Promise; @@ -105,6 +105,11 @@ type MegaStore = [ } ]; +export interface LnUrlData { + lnurl: string; + params: LnUrlParams; +} + export const Provider: ParentComponent = (props) => { const [searchParams] = useSearchParams(); const navigate = useNavigate(); @@ -145,7 +150,7 @@ export const Provider: ParentComponent = (props) => { testflightPromptDismissed: localStorage.getItem("testflightPromptDismissed") === "true", federations: undefined as MutinyFederationIdentity[] | undefined, - lnUrlParams: undefined as LnUrlParams | undefined + lnUrlData: undefined as LnUrlData | undefined }); const actions = { @@ -331,8 +336,8 @@ export const Provider: ParentComponent = (props) => { return []; } }, - setLnUrlParams(lnUrlParams: LnUrlParams | undefined) { - setState({ lnUrlParams }); + setLnUrlData(lnUrlData: LnUrlData | undefined) { + setState({ lnUrlData }); }, async saveFiat(fiat: Currency) { localStorage.setItem("fiat_currency", JSON.stringify(fiat)); From 7ab8ab89af4aa31eb8b66285c40a3d3bc20bdfc4 Mon Sep 17 00:00:00 2001 From: tompro Date: Fri, 19 Jan 2024 14:28:04 +0100 Subject: [PATCH 03/10] rudimentary receive flow did not add new ui elements but just show the available info with current elements --- src/routes/Receive.tsx | 133 ++++++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 54 deletions(-) diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index cda8bb25..cd5c2dc6 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -279,11 +279,10 @@ export function Receive() { async function onSubmit(e: Event) { e.preventDefault(); const lnUrl = lnUrlData(); - if (!lnUrl) { - await getQr(); - } else { - await handleLnUrlWithdrawalSubmit(lnUrl); + if (lnUrl) { + return await handleLnUrlWithdrawal(lnUrl); } + await getQr(); } async function getQr() { @@ -341,13 +340,13 @@ export function Receive() { // If we got here from an LNUrl withdrawal request onMount(() => { if (state.lnUrlData) { - handleLnUrlWithdrawal(state.lnUrlData); + initLnUrlWithdrawal(state.lnUrlData); actions.setScanResult(undefined); actions.setLnUrlData(undefined); } }); - function handleLnUrlWithdrawal(lnUrlData: LnUrlData) { + function initLnUrlWithdrawal(lnUrlData: LnUrlData) { console.log("handleLnUrlWithdrawal", lnUrlData.lnurl, lnUrlData.params); setLnUrlData(lnUrlData); setError(""); @@ -356,46 +355,48 @@ export function Receive() { if (lnUrlParams.min === lnUrlParams.max) { setAmount(mSatsToSats(lnUrlParams.max)); + setFixedAmount(true); } else { setAmount(mSatsToSats(lnUrlParams.min)); + setFixedAmount(false); } setReceiveState("edit"); } - async function handleLnUrlWithdrawalSubmit(lnUrlData: LnUrlData) { - if (lnUrlData) { - const lnurl = lnUrlData.lnurl; - const lnUrlParams = lnUrlData.params; - const amt = amount(); - const amtError = validateAmount( - amt, - mSatsToSats(lnUrlParams.min), - mSatsToSats(lnUrlParams.max) + async function handleLnUrlWithdrawal(lnUrlData: LnUrlData) { + const lnurl = lnUrlData.lnurl; + const lnUrlParams = lnUrlData.params; + const amt = amount(); + const amtError = validateAmount( + amt, + mSatsToSats(lnUrlParams.min), + mSatsToSats(lnUrlParams.max) + ); + if (amtError) { + showToast(new Error(amtError)); + return; + } + setLoading(true); + setReceiveState("show"); + try { + const success = await state.mutiny_wallet?.lnurl_withdraw( + lnurl, + amount() ); - if (amtError) { - showToast(new Error(amtError)); - return; - } - setLoading(true); - setReceiveState("show"); - try { - const success = await state.mutiny_wallet?.lnurl_withdraw( - lnurl, - amount() - ); - if (!success) { - setError("lnurl_withdraw failed"); - } else { - setReceiveState("paid"); - } - } catch (e) { - console.error("lnurl_withdraw failed", e); - showToast(eify(e)); - } finally { - setLnUrlExecuted(true); - setLoading(false); + if (!success) { + console.error("lnurl_withdraw failed result was false"); + setError("lnurl_withdraw failed"); + } else { + console.log("lnurl_withdraw success"); + setReceiveState("paid"); } + } catch (e) { + console.error("lnurl_withdraw failed", e); + showToast(eify(e)); + } finally { + setLnUrlExecuted(true); + setLoading(false); } } @@ -422,6 +423,18 @@ export function Receive() { setMethodChooserOpen(false); } + function satsReceived() { + if (receiveState() === "paid" && paidState() === "lightning_paid") { + return paymentInvoice()?.amount_sats; + } + + if (lnUrlExecuted()) { + return amount(); + } + + return paymentTx()?.received; + } + const [paidState, { refetch }] = createResource(bip21Raw, checkIfPaid); createEffect(() => { @@ -455,13 +468,20 @@ export function Receive() { {i18n.t("receive.receive_bitcoin")} - + +

EDIT {amount().toString()}

- + @@ -502,6 +527,9 @@ export function Receive() { amountSats={amount() ? amount().toString() : "0"} kind={flavor()} /> +

+ LNUrl withdrawal in progress... +

{i18n.t("receive.keep_mutiny_open")}

@@ -536,8 +564,9 @@ export function Receive() {
+

PAID {amount().toString()}

{ if (!open) clearAll(); }} @@ -557,30 +586,21 @@ export function Receive() {

{receiveState() === "paid" && - paidState() === "lightning_paid" + (paidState() === "lightning_paid" || + lnUrlData()) ? i18n.t("receive.payment_received") : i18n.t("receive.payment_initiated")}

@@ -595,7 +615,12 @@ export function Receive() { {/*TODO: Confirmation time estimate still not possible needs to be implemented in mutiny-node first*/} - +

Date: Fri, 19 Jan 2024 14:35:19 +0100 Subject: [PATCH 04/10] remove debug stuff --- src/i18n/en/translations.ts | 3 ++- src/routes/Receive.tsx | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/i18n/en/translations.ts b/src/i18n/en/translations.ts index 3eacc0cd..b5201c82 100644 --- a/src/i18n/en/translations.ts +++ b/src/i18n/en/translations.ts @@ -113,7 +113,8 @@ export default { gift: "Lightning Gift" }, remember_choice: "Remember my choice next time", - what_for: "What's this for?" + what_for: "What's this for?", + lnurl_withdrawal_in_progress: "LNUrl withdrawal in progress" }, send: { search: { diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index cd5c2dc6..9102df5f 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -385,10 +385,8 @@ export function Receive() { amount() ); if (!success) { - console.error("lnurl_withdraw failed result was false"); setError("lnurl_withdraw failed"); } else { - console.log("lnurl_withdraw success"); setReceiveState("paid"); } } catch (e) { @@ -474,7 +472,6 @@ export function Receive() { receiveState() === "edit" } > -

EDIT {amount().toString()}

- LNUrl withdrawal in progress... + {i18n.t("receive.lnurl_withdrawal_in_progress")} ...

{i18n.t("receive.keep_mutiny_open")} @@ -564,7 +561,6 @@ export function Receive() { -

PAID {amount().toString()}

{ From 912529b0163da631c0c103855604c0080060b6dd Mon Sep 17 00:00:00 2001 From: tompro Date: Fri, 19 Jan 2024 14:54:22 +0100 Subject: [PATCH 05/10] amount message --- src/i18n/en/translations.ts | 4 +++- src/routes/Receive.tsx | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/i18n/en/translations.ts b/src/i18n/en/translations.ts index b5201c82..6aa683d2 100644 --- a/src/i18n/en/translations.ts +++ b/src/i18n/en/translations.ts @@ -114,7 +114,9 @@ export default { }, remember_choice: "Remember my choice next time", what_for: "What's this for?", - lnurl_withdrawal_in_progress: "LNUrl withdrawal in progress" + lnurl_withdrawal_in_progress: "LNUrl withdrawal in progress", + lnurl_amount_message: + "Enter LNUrl withdrawal amount between min: {{min}} and max: {{max}}" }, send: { search: { diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index 9102df5f..ca2a4349 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -413,6 +413,13 @@ export function Receive() { return mSats / 1000n; } + function lnUrlAmountText(lnUrlData: LnUrlData) { + return i18n.t("receive.lnurl_amount_message", { + min: mSatsToSats(lnUrlData.params.min).toLocaleString(), + max: mSatsToSats(lnUrlData.params.max).toLocaleString() + }); + } + function selectFlavor(flavor: string) { setFlavor(flavor as ReceiveFlavor); if (rememberChoice()) { @@ -473,6 +480,11 @@ export function Receive() { } >
+ + +

{lnUrlAmountText(lnUrlData()!)}

+
+
Date: Fri, 19 Jan 2024 15:06:18 +0100 Subject: [PATCH 06/10] cleanup --- src/routes/Receive.tsx | 1 - src/routes/Send.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index ca2a4349..abd452ad 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -1,7 +1,6 @@ /* @refresh reload */ import { - LnUrlParams, MutinyBip21RawMaterials, MutinyInvoice } from "@mutinywallet/mutiny-wasm"; diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx index b597f70a..b9dfe57f 100644 --- a/src/routes/Send.tsx +++ b/src/routes/Send.tsx @@ -1,4 +1,4 @@ -import { LnUrlParams, MutinyInvoice, TagItem } from "@mutinywallet/mutiny-wasm"; +import { MutinyInvoice, TagItem } from "@mutinywallet/mutiny-wasm"; import { useNavigate, useSearchParams } from "@solidjs/router"; import { createEffect, From a22c17dc36960137bad7b4b0ffb65f118f1cbb21 Mon Sep 17 00:00:00 2001 From: tompro Date: Fri, 19 Jan 2024 15:21:06 +0100 Subject: [PATCH 07/10] remove validate debug --- src/routes/Receive.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index abd452ad..72a54efb 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -402,10 +402,9 @@ export function Receive() { min: bigint, max: bigint ): string | undefined { - console.log("validateAmount", amount, min, max); if (amount === 0n) return "amount is zero"; if (amount < min) return "amount smaller min"; - if (amount > max * 1000n) return "amount greater max"; + if (amount > max) return "amount greater max"; } function mSatsToSats(mSats: bigint) { From cb90d1f40c23006d3e9b88efa1c84a3dc1d80a55 Mon Sep 17 00:00:00 2001 From: tompro Date: Mon, 22 Jan 2024 13:43:21 +0100 Subject: [PATCH 08/10] resolve fixed amount issue --- src/routes/Receive.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index 72a54efb..a8556978 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -136,10 +136,15 @@ export function Receive() { const [detailsOpen, setDetailsOpen] = createSignal(false); const [detailsKind, setDetailsKind] = createSignal(); const [detailsId, setDetailsId] = createSignal(""); + const [lnUrlData, setLnUrlData] = createSignal(); const [fixedAmount, setFixedAmount] = createSignal(false); const [lnUrlExecuted, setLnUrlExecuted] = createSignal(false); + if (state.lnUrlData) { + initLnUrlWithdrawal(state.lnUrlData); + } + const RECEIVE_FLAVORS = [ { value: "unified", @@ -182,6 +187,8 @@ export function Receive() { setError(""); setFlavor(state.preferredInvoiceType); setLnUrlData(undefined); + setLnUrlExecuted(false); + setFixedAmount(false); } function openDetailsModal() { @@ -336,17 +343,10 @@ export function Receive() { } } - // If we got here from an LNUrl withdrawal request - onMount(() => { - if (state.lnUrlData) { - initLnUrlWithdrawal(state.lnUrlData); - actions.setScanResult(undefined); - actions.setLnUrlData(undefined); - } - }); - function initLnUrlWithdrawal(lnUrlData: LnUrlData) { console.log("handleLnUrlWithdrawal", lnUrlData.lnurl, lnUrlData.params); + actions.setScanResult(undefined); + actions.setLnUrlData(undefined); setLnUrlData(lnUrlData); setError(""); setFlavor("lightning"); From e7d73f98794a5aef97a97606f7ade908b4568987 Mon Sep 17 00:00:00 2001 From: tompro Date: Mon, 22 Jan 2024 14:05:17 +0100 Subject: [PATCH 09/10] only show LNURL processing when LNUrl --- src/routes/Receive.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/routes/Receive.tsx b/src/routes/Receive.tsx index a8556978..aa389dc8 100644 --- a/src/routes/Receive.tsx +++ b/src/routes/Receive.tsx @@ -12,7 +12,6 @@ import { createSignal, Match, onCleanup, - onMount, Show, Switch } from "solid-js"; @@ -534,9 +533,13 @@ export function Receive() { amountSats={amount() ? amount().toString() : "0"} kind={flavor()} /> -

- {i18n.t("receive.lnurl_withdrawal_in_progress")} ... -

+ +

+ {i18n.t("receive.lnurl_withdrawal_in_progress")}{" "} + ... +

+
+

{i18n.t("receive.keep_mutiny_open")}

From 1636470bb1bf1280910b43c61619fb6090114ba2 Mon Sep 17 00:00:00 2001 From: tompro Date: Fri, 26 Jan 2024 10:12:32 +0100 Subject: [PATCH 10/10] rebase --- src/routes/Send.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx index b9dfe57f..48bc947a 100644 --- a/src/routes/Send.tsx +++ b/src/routes/Send.tsx @@ -52,9 +52,6 @@ import { eify, vibrateSuccess } from "~/utils"; export type SendSource = "lightning" | "onchain"; -// const TEST_DEST = "bitcoin:tb1pdh43en28jmhnsrhxkusja46aufdlae5qnfrhucw5jvefw9flce3sdxfcwe?amount=0.00001&label=heyo&lightning=lntbs10u1pjrwrdedq8dpjhjmcnp4qd60w268ve0jencwzhz048ruprkxefhj0va2uspgj4q42azdg89uupp5gngy2pqte5q5uvnwcxwl2t8fsdlla5s6xl8aar4xcsvxeus2w2pqsp5n5jp3pz3vpu92p3uswttxmw79a5lc566herwh3f2amwz2sp6f9tq9qyysgqcqpcxqrpwugv5m534ww5ukcf6sdw2m75f2ntjfh3gzeqay649256yvtecgnhjyugf74zakaf56sdh66ec9fqep2kvu6xv09gcwkv36rrkm38ylqsgpw3yfjl" -// const TEST_DEST_ADDRESS = "tb1pdh43en28jmhnsrhxkusja46aufdlae5qnfrhucw5jvefw9flce3sdxfcwe" - // TODO: better success / fail type type SentDetails = { amount?: bigint;