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 <keyan.kousha+huumn@gmail.com>
This commit is contained in:
		
							parent
							
								
									c20a954cfc
								
							
						
					
					
						commit
						d3ca87a78b
					
				@ -10,7 +10,7 @@ export default function WalletButtonBar ({
 | 
			
		||||
  return (
 | 
			
		||||
    <div className={`mt-3 ${className}`}>
 | 
			
		||||
      <div className='d-flex justify-content-between'>
 | 
			
		||||
        {wallet.isConfigured &&
 | 
			
		||||
        {wallet.hasConfig && wallet.isConfigured &&
 | 
			
		||||
          <Button onClick={onDelete} variant='grey-medium'>{deleteText}</Button>}
 | 
			
		||||
        {children}
 | 
			
		||||
        <div className='d-flex align-items-center ms-auto'>
 | 
			
		||||
 | 
			
		||||
@ -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 (
 | 
			
		||||
    <CenterLayout>
 | 
			
		||||
      <h2 className='pb-2'>{wallet.card.title}</h2>
 | 
			
		||||
      <h6 className='text-muted text-center pb-3'><Text>{wallet.card.subtitle}</Text></h6>
 | 
			
		||||
      {!wallet.walletType && <WalletSecurityBanner />}
 | 
			
		||||
      {!wallet.walletType && wallet.hasConfig > 0 && <WalletSecurityBanner />}
 | 
			
		||||
      <Form
 | 
			
		||||
        initial={initial}
 | 
			
		||||
        {...validateProps}
 | 
			
		||||
@ -74,7 +85,7 @@ export default function WalletSettings () {
 | 
			
		||||
          ? <AutowithdrawSettings wallet={wallet} />
 | 
			
		||||
          : (
 | 
			
		||||
            <ClientCheckbox
 | 
			
		||||
              disabled={!wallet.isConfigured}
 | 
			
		||||
              disabled={!available}
 | 
			
		||||
              initialValue={wallet.status === Status.Enabled}
 | 
			
		||||
              label='enabled'
 | 
			
		||||
              name='enabled'
 | 
			
		||||
 | 
			
		||||
@ -64,6 +64,10 @@ Since `name` will also be used in [wallet logs](https://stacker.news/wallet/logs
 | 
			
		||||
 | 
			
		||||
Wallet fields define what this wallet requires for configuration and thus are used to construct the forms like the one you can see at [/settings/wallets/lnbits](https://stacker.news/settings/walletslnbits).
 | 
			
		||||
 | 
			
		||||
- `available?: boolean`
 | 
			
		||||
 | 
			
		||||
This property can be used to override the default behavior of the `enabled` checkbox in the wallet configuration form. By default, it will be clickable when a wallet is configured. However, if a wallet does not have any configuration, this checkbox will always be disabled. You can set `available` to an expression that will determine when a wallet can be enabled.
 | 
			
		||||
 | 
			
		||||
- `card: WalletCard`
 | 
			
		||||
 | 
			
		||||
Wallet cards are the components you can see at [/settings/wallets](https://stacker.news/settings/wallets). This property customizes this card for this wallet.
 | 
			
		||||
 | 
			
		||||
@ -4,5 +4,6 @@ import * as lnc from 'wallets/lnc/client'
 | 
			
		||||
import * as lnAddr from 'wallets/lightning-address/client'
 | 
			
		||||
import * as cln from 'wallets/cln/client'
 | 
			
		||||
import * as lnd from 'wallets/lnd/client'
 | 
			
		||||
import * as webln from 'wallets/webln/client'
 | 
			
		||||
 | 
			
		||||
export default [nwc, lnbits, lnc, lnAddr, cln, lnd]
 | 
			
		||||
export default [nwc, lnbits, lnc, lnAddr, cln, lnd, webln]
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ export function useWallet (name) {
 | 
			
		||||
  const { logger, deleteLogs } = useWalletLogger(wallet)
 | 
			
		||||
 | 
			
		||||
  const [config, saveConfig, clearConfig] = useConfig(wallet)
 | 
			
		||||
  const hasConfig = wallet?.fields.length > 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]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								wallets/webln/ATTACH.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								wallets/webln/ATTACH.md
									
									
									
									
									
										Normal file
									
								
							@ -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)
 | 
			
		||||
							
								
								
									
										21
									
								
								wallets/webln/client.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								wallets/webln/client.js
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								wallets/webln/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								wallets/webln/index.js
									
									
									
									
									
										Normal file
									
								
							@ -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']
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user