From 4dfbb437f90adf4f7dc44e4cacc26d8567f761f6 Mon Sep 17 00:00:00 2001 From: Tyson Lupul Date: Thu, 5 Feb 2026 20:51:59 +0000 Subject: [PATCH] Wallet receive flow (#15) (#52) * Pin sharp via pnpm override, add wallet receive * Revert toast success styling * Route receive through wallet connect * Simplify receive invoice validation * Polish receive modal layout * Clarify NWC client config * Adjust wallet action layout on mobile --- package.json | 5 +- pnpm-lock.yaml | Bin 346469 -> 344635 bytes src/app/components/WalletPay.svelte | 20 +++- src/app/components/WalletReceive.svelte | 118 ++++++++++++++++++++++++ src/app/components/Zap.svelte | 3 +- src/app/core/commands.ts | 79 +++++++++++++++- src/lib/util.ts | 2 + src/routes/settings/wallet/+page.svelte | 39 +++++--- 8 files changed, 248 insertions(+), 18 deletions(-) create mode 100644 src/app/components/WalletReceive.svelte diff --git a/package.json b/package.json index 41494cda..8cd96080 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,9 @@ ], "onlyBuiltDependencies": [ "sharp" - ] + ], + "overrides": { + "sharp": "0.35.0-rc.0" + } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2014aaacabb76c144afdc968849ad82d48145397..94d8fcc3b1399c2af6e10fac8e6744a18c448fb9 100644 GIT binary patch delta 7085 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 delta 7986 zcmbtZd937ieb>zRZFaM}Nj7_s%^rD6LN^=dJ$r1A?b&MSoBQ;5Jcm6ql*8xPGh>g( z_MC0Rp;lDTKn{TmQXqj+u2v;bX;1=!ZD|pyI8s$@kf11tQYh#CQBgq!{dwl??#t$o zXy22b^nPRizSrk_edg&uI{xpE9DnEyPrmt^5Bl%@bmZ)3ZhiMx&-w59MC7FZpI?p~ ze)P}ayFMfT!-0qQ5#mmCoARIdNa%bs#$l#5!UQch&f566jv%N@N+MeeR#_v9eO>A!DZ>)itw6lT-m8dE~f5`A?pToV(3YMcLc3 zM9-M0TM{Aq5C3iGcV2w@?3==;eWeWDvwXJ@q9cbLbxWP7mgnwZ+q8d~0ZKYXu{WPY z5i?&dR9i%CYLwui5Jw0ZZ)BP(Tg#VJt&}KPomhYF4x|d4Zw;rOB=VV&M--T5l44r} z&6vq9*>$t(5b5d0O3CAr2;_;<(DAn9q2)Ql!9aCZnjU+pkNm{@LT3)HKGEDAVmr20 zaH2M>+M+L!Vl~fB>ZJlZC(+WPS<{kAQyVxjzNQx7N?oj$$(aVX$_iWIx}C1wo;1gu zGSAUUYBaNxiLoNGOcXBJiRSFl%Wpe+I1qJl^ZaWsMYp5mqa1c_EnFIF+OD2Aue4bm zth76@`hV!|;QnPia$f`Hqj1!9n%JmaNt(q}tJ80`V-pTd650BojC1K4Yn8Y*J5mj< zMmMEiB|oT&aHAkGggiDBqY}@F4Uz41>zS@QsY-gxA0eTmXZPwxY@_ENxcTPI&6RXp z^mPBppNC-Gn`;6lb8<3PsnB)N=sEhxLOVK@u(B1`k{303EU@W3sJL4dnO?edU6$(spqc(#g)rP4&Rbl?kooAGwq#5! zX<8mmHuKq9%bbzCQ(X)!F*9LwM>J8ZnsiEXMv>>qZp=aHG-}ZN%p3(@`%mtK&b|7&mmLs~`_2PXgP+y$8m}wV+z^dB{dd1Dyl)+n;3@y%hr*{0Uv<;i z8SG&G&M!k3njL0Dbd|W(diTv|Q^8@%}=b zo5XO4WOO*En`kPP9y1NMR>(2P+-u6wPTO35z7zU%*njAsLU1c8pp?wSyLB=xmu=PQ zXxVxXmV~k=R&#YWFX&Yxw`iFYz9`koN{+_dJW{ggj3-3-k-#TnM53f&daNHyN>)Ld zW^tVaf$$Gkp}Uvwe0?Z<(}6WAcVgTAPCj&RGn1F+g=&%`lJ(qR(o06keyr3@6fr7Q zaPw^hD<;iUt)>nMdLDN;oDrJ~p@o>(c3}II*R6H{&1@+Ah-wmSgc*F{2l(tf`ln zrj;pjz0TMyQKeQct2gI#yz00`VIcOCeD~6&<@e3dKkhrY1;AxkT~IFLej*<_ueiFy zi&HK;>{*=&Ib#S^v}Q!AKTKC)t>aFSj_PC>S3vCf!mUY}f$f%&bdndS5uTaNrejyB zv++*s=)?EkCJ#;9QC)Wn{IQ3Y>)&=Me1B6E@YuY=kVy$4hQ&e0l9@bKP7OO= zvDK)}GW94m$>931W(=oItS{unVkz6NC(~BC2Fqr=2*)|SW!5tDf;K_zo)K4jDSu>z z?m4Kas=cMoJk?VC-~1FLZJEu_3anUdEBQgO=A}3-UEn*OHbur7L&FZ=BZ+vekYC6q zGRaN3S}W_3Y;{z~3a*K@GK^Cx0F>59!fi*(nNk{?Bv}6n5xL8M0})cig-+wm|1|Tyfx^ z`!2}9a#M+^6o(DqE>G_+gy z1|UoADVmZ+TEgR{*`ySgwNlMU1R2==O6b!^)R`?>%9f{^_CWMhP*<)T3twoo+S<6p zOnL;1$27!dTV++LJEw#$irO^)sm^2z*Mw; zv{ScZGdy8vrd7cFC%zdv^WI+gCV%rm=!E}~diWkc^GrzEry1ICC%P_Zh!S_Tp-aqX z3wzp6_s12gkJU%nlGm=7sm7wKWq^U$3~;JJHn_BXP57PCNw>C0CZ~C6Tyh!Kl@GK*g452ro(zcqq6fJQ<%hwF)YO#MOQnS z-l9h0xyHOxG-D&3NE$HK?oP`oxqxWZY^Pt2RqMu#^~g@8WWrN8uh~daHm1YCu|Izp zy3;?n5x#4g{MXRQTaOu@K9;sLXJ~HOqTCm|>JI9MJ{G#ruFvyxyb+yXm1fJr;;btu zGqXh2ruj5H>G$vnty*+~O?INwB!}p!1Q(MMTqBKw+#!Ux*R&Cw)5#+3#4{7iGv+Ci zWrH^7|Kg&aI@Bs=d6*KSVn&t zdc%Q(jw8+uSKmRmaXLOR>NnSH`FC5l0UnO-cf6wUrT8iUK2N(x;IhmBH9 z6BZKLz%{*+9bqt|Hfj<|5;7?`Lb;n_32niJEqHj(to zBn{>UBOM(!9mJcp@K~iMi*2&vBEa4yQw=9JZn2FmX>1IXt+8heT>pv*ketg%PP;E@ zS&A7}#G#IX@=p_tr6EE`OQJa?mvI?-x}Y*Mdxn$;f);DiUfh}#ORc(%kDOS+%}6Db zkdrxmI6z8MfW*MX@aF)Eesv7E_;NG+bZEaQ89VqkP5GBuFki!|91|@^Q(e>3)v}x% z57c>^wWx|v<;SRtW{P<%McCt6H`9^gI?dU&cn(vNTBpb+nz}*p+-#hzG7732g}NX& z=0P#o+6{Br)#E=65 zPB|J;=oidh+hGxNRGros9gksM3?COnu9Pi!GDi{xs>F!JS-zX#6H8kNC(&EiUNwRe zF&OMn{^NCkADYoHS>mZ#RBlz9g>oXDE8x?yu8o9=XOt6BLmo8JQnuFVcWYc7t&H4i z$JQ+|&cpe6BO`)pGOn6EvBvGAoyeXHXQ!TzPG+CuRD@@v$i}FGu!RUyk(Dh;4sEMWCz@cCx<;H13 z!>aUR;%I`LPPci9!YkVHza9_&a9>0Q1@>_!e1V_iQm#8H^>8k!%*s-^KQsqJy+3gi zbXQ3jO>R(67OkYNS#YvC>k^VoVhY`mlR`8$6(?z}tIkqgS(f{{+?mX$bFN)wr~c1h z7rJBl?T?1vbuNVY4;RAc8>M-Sx-bB(H_8LQz* zsLI!g+KffjW_>cNf|;Gj;`KJ1B1)t-QYJ{6#VEIkO#E|qhR^tKdM*ToV#~wNgue~# z14UgHy;Z9;Tv;n`$?j}cdE7t3Otpd`vlR4{IL%yJFTBGs*~>VaA7*?i~%FF6y+jtSj7 z+B3PL6YZqaS}D!eRYB;&EI+q%@puA>P1?EYs7Tr~L(G@KhB5Q9y<~zKG9nsPr9`}| z>CrKh%8uA^Yc@&v-}+kkv0GNM+^Mlx41#X)?C%0FT8VK~$X2;JQ*p446$b-&rxZ`* zEW$(PacYn;VW!pan(3)lMf+}|&>}1rCY^CQUYw3SB1OdO$*ADMn$&VlWL}pAHoqKx zJ^bXM{b0VCfz287FW(=zx7D{`l^|4?*DNe6A}pG!W@UATQChE1CYe4jswiWUBRE59 zs4h%m6x~i`YSIKIOcaxQvq{>mmdchnbHoAS)W|`SNq~9P|7m37jKBCn=&ohsTj9I* z``>j!_ce)blD0&Y(o~t}`)q-%xr9nJ$O_x&8?lU8rW#FU+yDf_7|$qHeJ)!bUnCrr z?sq#0csh$0#%cj;N28J^b*J89oIy+eR0 zAerezZ)$cFohmmPq+J=P9W>VV$XGswXW)v#Ee0foO}HFR_X>`xd&NFd#LddI(1j;; zovX^_EK=sF6l0|Ya?W z)9!*z4{VQVt39r%TS}fOb8!ZZ#mB80oY3l2x-vv7cA+wg*rL z*_}?oeD!?*l5tNRNwS(qcBf2dmML0&ET3*;>4DYk!0};5mZGIagV)-5V~930q>`he z7Kti!8Y>W^%4DDlj?=Qup6yYSj+%{@EvED6mF^k;i`(IShxZ7?xBZ9Ch3{>Tiaiod zwLGfOjApgDj8}?GPbV||aVo3!2y)RJ6^SBckJ^KVK3C!ri?)&tQ)UZ#ebFiMg65X{ zE(%)%73sxrx}6Z>wZM_f$A26)p#zF4gKpq}0v2pIZ5$cK7kCS8p;}{XPu=#UhT?V+ zN%w^DXyj1tem3YjAr4$C*++t)4I918d$|7iX z5BN_V2RJ!9NbQxcsh<+9_%D)~}JXA*4PsbQ7Qn1h$E3q>yeMFiU> z{l`8Uc~^bK3f#yl?J@>iYtCT3D_=WPE#MolYF;~d5qlQ>M&u6v1N)&nmh#6U-#M@Z z6kp`fir!lSdhf^emGpl*1tGqBCv3rwI#TF}`;t9ys;#ZB;#Z+|Ir^6otrfQtK)2Kdnubn4c%m9s#54Btlm zBYzh;cl#b=cRlof)_<+ricJE;df@(B1NR5o2kN3gWMHQM_npvf{wG<;2nU_-3Fz$M zH3~0o#(?@!|2yx8&iGeOfaJNo3LTEze2vec{3A7J(|`Kw(9v6r^#&679NUgAPt~C( zp#y-O+$I7nEH4XCGyDSLB>D+(>sQ;*k$cvZ*>3;c?THsRQE(3tP=_o2!~acWW79vL zhmN0IKfXHMC1L`haUk??2YTnxqc>6)Di|-L?}s+;-@8Br4v21}!To!f2QcN6+vrN+ zM@;CAH@%1+c*}{6(+9ys0?1cj**3cTkq4cB!A^gq4{g3&5ML(yfDXIp#sOYe;sd>2 zw7P?c;r<;DLiZhBZ`1*}fv)~z&xG#u7mq;4{4f4X1oD6MT;yhd@Lc3}|399K99};9 zAf!UKUfYd#fYm_npF9kG>88^!wYdf#uRH)&%&+V_v<@qHD~Rggai|qOyk`a6CYGQ5 z5cG$~kMH#@$QK|a(CpXQ@QG#Wv(T@@x9?q~w(&rKPdyF&>evhS9gr}AcK3b``rw{z zO~`dH4mNjpZo=MlDff{L1qDGg4=?h?u*?J zzz_WT4?G5)d9&G949C7|K=4uQ+PfB5U-m9+>zmQto7cKZMu0XNRD#`Odu-b447{p> zBd-&O-hJZwFbbXnINOfyey|!mK(ztU0NJtD-`ms80_^~Vm;e3G(9WS?^Vs?Vw0RF8 z)vohgYv{&yU%m$BCHg!x2yK8xBB*sYZJfS)Ph#v|yZeq|0gHm#O0OK`b5BK%gFb^U z-}7DQnPXrq`Dxb-Np@aF7MAE;=^IOFZbJN3zhVg8?0SagzXm#QjhF0@}o1 zh=_}uTB9(#b_-P#@Zf)_B~z7v9f%htU7S!CnHKL5i9 zHjXZTabQC|aMNn&2j;!?1HCx8rSi=dOATXk{`5!JI*z8x(*|Ht>vFOczprE{U^Q*osEI7Y=ft$YbEeyg8hQ&W|b`QS2p#= z#Mt%xPB41~!@?4THXGivE`*?sv-_{Avx3r#zD8bxng3(^ZrPK&pp^Jmehh%}!TlR2 bd^r?3;s5d-pmqM 0) @@ -60,7 +66,7 @@ {#if invoice}
- {#if $session?.wallet?.type === "webln" && invoice.satoshi === 0} + {#if $session?.wallet?.type === "webln" && !hasAmount}

Uh oh! It looks like your current wallet doesn't support invoices without an amount. See if you can get a lightning invoice with a pre-set amount. @@ -101,7 +107,13 @@ Go back - + {#if invoice} + + {:else} + + {/if} + + diff --git a/src/app/components/Zap.svelte b/src/app/components/Zap.svelte index e842a4a2..62fd3e77 100644 --- a/src/app/components/Zap.svelte +++ b/src/app/components/Zap.svelte @@ -17,6 +17,7 @@ import ModalSubtitle from "@lib/components/ModalSubtitle.svelte" import ModalFooter from "@lib/components/ModalFooter.svelte" import EmojiButton from "@lib/components/EmojiButton.svelte" + import {errorMessage} from "@lib/util" import ProfileLink from "@app/components/ProfileLink.svelte" import {payInvoice} from "@app/core/commands" import {pushToast} from "@app/util/toast" @@ -85,7 +86,7 @@ } catch (e) { console.error(e) - const message = String(e).replace(/^.*Error: /, "") + const message = errorMessage(e) pushToast({ theme: "error", diff --git a/src/app/core/commands.ts b/src/app/core/commands.ts index a169d6a3..4fbb9c55 100644 --- a/src/app/core/commands.ts +++ b/src/app/core/commands.ts @@ -425,6 +425,22 @@ export const publishLeaveRequest = (params: LeaveRequestParams) => export const getWebLn = () => (window as any).webln +export const getNwcClient = () => { + const $session = session.get() + + if (!$session?.wallet || $session.wallet.type !== "nwc") { + throw new Error("No NWC wallet is connected") + } + + const {info} = $session.wallet + + if (info.nostrWalletConnectUrl) { + return new nwc.NWCClient({nostrWalletConnectUrl: info.nostrWalletConnectUrl}) + } + + return new nwc.NWCClient(info) +} + export const payInvoice = async (invoice: string, msats?: number) => { const $session = session.get() @@ -435,7 +451,7 @@ export const payInvoice = async (invoice: string, msats?: number) => { if ($session.wallet.type === "nwc") { const params: {invoice: string; amount?: number} = {invoice} if (msats) params.amount = msats - return new nwc.NWCClient($session.wallet.info).payInvoice(params) + return getNwcClient().payInvoice(params) } else if ($session.wallet.type === "webln") { if (msats) throw new Error("Unable to pay zero invoices with webln") return getWebLn() @@ -444,6 +460,67 @@ export const payInvoice = async (invoice: string, msats?: number) => { } } +export type CreateInvoiceParams = { + sats: number + description?: string +} + +export const createInvoice = async ({ + sats, + description = "Receive via lightning", +}: CreateInvoiceParams) => { + const $session = session.get() + + if (!$session?.wallet) { + throw new Error("No wallet is connected") + } + + const satAmount = Math.floor(sats) + + if (!Number.isFinite(satAmount) || satAmount <= 0) { + throw new Error("Invalid satoshi amount") + } + + if ($session.wallet.type === "nwc") { + const createdInvoice = await getNwcClient().makeInvoice({ + amount: satAmount * 1000, + description, + }) + + if (!createdInvoice.invoice) { + throw new Error("NWC wallet failed to return an invoice") + } + + return createdInvoice.invoice + } + + if ($session.wallet.type === "webln") { + const webLn = getWebLn() + + if (!webLn) { + throw new Error("WebLN not available") + } + + await webLn.enable() + + const response = await webLn.makeInvoice({ + amount: satAmount, + defaultMemo: description, + }) + + const paymentRequest = + typeof response === "string" ? response : response?.paymentRequest || response?.pr || "" + + if (!paymentRequest) { + throw new Error("Invalid payment request returned from WebLN") + } + + return paymentRequest + } + + throw new Error("Unsupported wallet type") +} + // File upload export const normalizeBlossomUrl = (url: string) => normalizeUrl(url.replace(/^ws/, "http")) diff --git a/src/lib/util.ts b/src/lib/util.ts index ab23de01..a1ae8d5b 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -17,6 +17,8 @@ export const daysBetween = (start: number, end: number) => [...range(start, end, export const ucFirst = (s: string) => s.slice(0, 1).toUpperCase() + s.slice(1) +export const errorMessage = (err: unknown) => String(err).replace(/^.*Error: /, "") + export const buildUrl = (base: string | URL, ...pathname: string[]) => { const url = new URL(base) diff --git a/src/routes/settings/wallet/+page.svelte b/src/routes/settings/wallet/+page.svelte index 4af6442a..fed15a08 100644 --- a/src/routes/settings/wallet/+page.svelte +++ b/src/routes/settings/wallet/+page.svelte @@ -3,10 +3,12 @@ import {LOCALE} from "@welshman/lib" import {displayRelayUrl, isNWCWallet, fromMsats} from "@welshman/util" import {session, pubkey, profilesByPubkey} from "@welshman/app" - import Bolt from "@assets/icons/bolt.svg?dataurl" + import DownloadMinimalistic from "@assets/icons/download-minimalistic.svg?dataurl" + import UploadMinimalistic from "@assets/icons/upload-minimalistic.svg?dataurl" import Icon from "@lib/components/Icon.svelte" import Button from "@lib/components/Button.svelte" import WalletPay from "@app/components/WalletPay.svelte" + import WalletReceive from "@app/components/WalletReceive.svelte" import WalletConnect from "@app/components/WalletConnect.svelte" import WalletDisconnect from "@app/components/WalletDisconnect.svelte" import WalletUpdateReceivingAddress from "@app/components/WalletUpdateReceivingAddress.svelte" @@ -31,6 +33,13 @@ ) const pay = () => pushModal(WalletPay) + const receive = () => { + if ($session?.wallet) { + pushModal(WalletReceive) + } else { + pushModal(WalletConnect) + } + }

@@ -94,10 +103,24 @@

{/if} - +
+ +
+ + +
+
{:else}

No wallet connected

{/if} @@ -122,10 +145,4 @@
{/if} -
- -