From d3ca87a78bfe85b37fd1fe2f94b8304c2cc3f33b Mon Sep 17 00:00:00 2001 From: ekzyis Date: Tue, 23 Jul 2024 13:23:48 -0500 Subject: [PATCH] Add WebLN for sending payments (#1274) * Add WebLN for sending payments * attach docs for alby --------- Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com> Co-authored-by: keyan --- components/wallet-buttonbar.js | 2 +- pages/settings/wallets/[wallet].js | 15 +++++++++++++-- wallets/README.md | 4 ++++ wallets/client.js | 3 ++- wallets/index.js | 15 ++++++++++++++- wallets/webln/ATTACH.md | 10 ++++++++++ wallets/webln/client.js | 21 +++++++++++++++++++++ wallets/webln/index.js | 13 +++++++++++++ 8 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 wallets/webln/ATTACH.md create mode 100644 wallets/webln/client.js create mode 100644 wallets/webln/index.js diff --git a/components/wallet-buttonbar.js b/components/wallet-buttonbar.js index 2b807569..e07995f3 100644 --- a/components/wallet-buttonbar.js +++ b/components/wallet-buttonbar.js @@ -10,7 +10,7 @@ export default function WalletButtonBar ({ return (
- {wallet.isConfigured && + {wallet.hasConfig && wallet.isConfigured && } {children}
diff --git a/pages/settings/wallets/[wallet].js b/pages/settings/wallets/[wallet].js index 3b95edd8..41951359 100644 --- a/pages/settings/wallets/[wallet].js +++ b/pages/settings/wallets/[wallet].js @@ -10,6 +10,7 @@ import Info from '@/components/info' import Text from '@/components/text' import { AutowithdrawSettings } from '@/components/autowithdraw-shared' import dynamic from 'next/dynamic' +import { useEffect, useState } from 'react' const WalletButtonBar = dynamic(() => import('@/components/wallet-buttonbar.js'), { ssr: false }) @@ -21,6 +22,14 @@ export default function WalletSettings () { const { wallet: name } = router.query const wallet = useWallet(name) + const [mounted, setMounted] = useState(false) + useEffect(() => { + // mounted is required since available might depend + // on values that are only available on the client (and not during SSR) + // and thus we need to render the component again on the client + setMounted(true) + }, []) + const initial = wallet.fields.reduce((acc, field) => { // We still need to run over all wallet fields via reduce // even though we use wallet.config as the initial value @@ -38,11 +47,13 @@ export default function WalletSettings () { ? { validate: wallet.fieldValidation } : { schema: wallet.fieldValidation } + const available = mounted && wallet.available !== undefined ? wallet.available : wallet.isConfigured + return (

{wallet.card.title}

{wallet.card.subtitle}
- {!wallet.walletType && } + {!wallet.walletType && wallet.hasConfig > 0 && }
: ( 0 const _isConfigured = isConfigured({ ...wallet, config }) const status = config?.enabled ? Status.Enabled : Status.Initialized @@ -108,6 +109,7 @@ export function useWallet (name) { enable, disable, setPriority, + hasConfig, isConfigured: _isConfigured, status, enabled, @@ -135,6 +137,11 @@ function useConfig (wallet) { ...(hasServerConfig ? serverConfig : {}) } + if (wallet?.available !== undefined && config.enabled !== undefined) { + // wallet must be available to be enabled + config.enabled &&= wallet.available + } + const saveConfig = useCallback(async (config) => { if (hasLocalConfig) setLocalConfig(config) if (hasServerConfig) await setServerConfig(config) @@ -258,7 +265,13 @@ export function getEnabledWallet (me) { const priority = config?.priority return { ...def, config, priority } }) - .filter(({ config }) => config?.enabled) + .filter(({ available, config }) => { + if (available !== undefined && config?.enabled !== undefined) { + // wallet must be available to be enabled + config.enabled &&= available + } + return config?.enabled + }) .sort(walletPrioritySort)[0] } diff --git a/wallets/webln/ATTACH.md b/wallets/webln/ATTACH.md new file mode 100644 index 00000000..afb7b59d --- /dev/null +++ b/wallets/webln/ATTACH.md @@ -0,0 +1,10 @@ +Using webln will require installing the alby browser extension and connecting it to an alby hub connected to `stacker_lnd`. + +1. Install the [Alby browser extensions](https://chromewebstore.google.com/detail/alby-bitcoin-wallet-for-l/iokeahhehimjnekafflcihljlcjccdbe?pli=1) +2. Create an Alby account +3. Install the [Alby hub](https://guides.getalby.com/user-guide/v/alby-account-and-browser-extension/alby-hub/alby-hub-other-flavors/desktop) +4. Connect Alby Hub to our regest lnd: + - grpc host: `localhost:10010` + - hex admin.macaroon: `0201036c6e6402f801030a10b28622d3f1881964730f73e04e22b82a1201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e65726174651204726561640000062052ac4803c92801f06bda51762aa006f8e3055ff0a57561df6ae1a7b09ae988fd` + - hex tls cert: `2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d494943527a434341653267417749424167495163303676574942755039754b65514e484b62466c6c44414b42676771686b6a4f50515144416a41344d5238770a485159445651514b45785a73626d5167595856306232646c626d56795958526c5a43426a5a584a304d52557745775944565151444577773459324d344e44466b0a4d6a59324d7a67774868634e4d6a51774d7a41334d5463774d6a45355768634e4d6a55774e5441794d5463774d6a4535576a41344d523877485159445651514b0a45785a73626d5167595856306232646c626d56795958526c5a43426a5a584a304d52557745775944565151444577773459324d344e44466b4d6a59324d7a67770a5754415442676371686b6a4f5051494242676771686b6a4f50514d4242774e43414151542f6e77764d486156436664566165496776384d4b532b5348415339630a456c696637587161377173567650695737566e68344d445645426c4d357267306e6b614836563137734343337273652f4f71504c665659316f3448594d4948560a4d41344741315564447745422f775145417749437044415442674e56485355454444414b4267677242674546425163444154415042674e5648524d42416638450a425441444151482f4d42304741315564446751574242516d616d566e2f4b635271486f4e5239646b39433167324d2b6a5354422b42674e5648524545647a42310a6767773459324d344e44466b4d6a59324d7a694343577876593246736147397a6449494c633352685932746c636c3973626d534346476876633351755a47396a0a613256794c6d6c7564475679626d467367675231626d6c3467677031626d6c346347466a613256306767646964575a6a623235756877522f41414142687841410a414141414141414141414141414141414141414268775373477741474d416f4743437147534d343942414d43413067414d4555434946443237335742634d4b7a0a55506f4f4c3862777131354a587472534765504b7041654e3154626c5934513541694541764b74756b2b737378395751465a424569577843536a573567654b6b0a3648423754647873552b5a62664c673d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a` +5. Connect Alby Hub to the alby extension in (2) diff --git a/wallets/webln/client.js b/wallets/webln/client.js new file mode 100644 index 00000000..da99bacd --- /dev/null +++ b/wallets/webln/client.js @@ -0,0 +1,21 @@ +export * from 'wallets/webln' + +export const sendPayment = async (bolt11) => { + if (typeof window.webln === 'undefined') { + throw new Error('WebLN provider not found') + } + + // this will prompt the user to unlock the wallet if it's locked + await window.webln.enable() + + // this will prompt for payment if no budget is set + const response = await window.webln.sendPayment(bolt11) + if (!response) { + // sendPayment returns nothing if WebLN was enabled + // but browser extension that provides WebLN was then disabled + // without reloading the page + throw new Error('sendPayment returned no response') + } + + return response.preimage +} diff --git a/wallets/webln/index.js b/wallets/webln/index.js new file mode 100644 index 00000000..61990b06 --- /dev/null +++ b/wallets/webln/index.js @@ -0,0 +1,13 @@ +import { SSR } from '@/lib/constants' + +export const name = 'webln' + +export const fields = [] + +export const available = SSR ? false : typeof window.webln !== 'undefined' + +export const card = { + title: 'WebLN', + subtitle: 'use a [WebLN provider](https://www.webln.guide/ressources/webln-providers) for payments', + badges: ['send only'] +}