Separate client and server imports by files
* wallets now consist of an index.js, a client.js and a server.js file * client.js is imported on the client and contains the client portion * server.js is imported on the server and contains the server porition * both reexport index.js so everything in index.js can be shared by client and server * every wallet contains a client.js file since they are all imported on the client to show the cards * client.js of every wallet is reexported as an array in wallets/client.js * server.js of every wallet is reexported as an array in wallets/server.js FIXME: for some reason, worker does not properly import the default export of wallets/server.js
This commit is contained in:
parent
259ebef971
commit
5b2e835722
|
@ -1,32 +1,30 @@
|
|||
import { getIdentity, createHodlInvoice, createInvoice, decodePaymentRequest, payViaPaymentRequest, cancelHodlInvoice, getInvoice as getInvoiceFromLnd, getNode, authenticatedLndGrpc, deletePayment, getPayment } from 'ln-service'
|
||||
import { createHodlInvoice, createInvoice, decodePaymentRequest, payViaPaymentRequest, cancelHodlInvoice, getInvoice as getInvoiceFromLnd, getNode, deletePayment, getPayment, getIdentity } from 'ln-service'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import crypto, { timingSafeEqual } from 'crypto'
|
||||
import serialize from './serial'
|
||||
import { decodeCursor, LIMIT, nextCursorEncoded } from '@/lib/cursor'
|
||||
import { SELECT, itemQueryWithMeta } from './item'
|
||||
import { msatsToSats, msatsToSatsDecimal } from '@/lib/format'
|
||||
import { amountSchema, ssValidate, withdrawlSchema } from '@/lib/validate'
|
||||
import { amountSchema, ssValidate, withdrawlSchema, lnAddrSchema } from '@/lib/validate'
|
||||
import { ANON_BALANCE_LIMIT_MSATS, ANON_INV_PENDING_LIMIT, USER_ID, BALANCE_LIMIT_MSATS, INVOICE_RETENTION_DAYS, INV_PENDING_LIMIT, USER_IDS_BALANCE_NO_LIMIT } from '@/lib/constants'
|
||||
import { datePivot } from '@/lib/time'
|
||||
import assertGofacYourself from './ofac'
|
||||
import assertApiKeyNotPermitted from './apiKey'
|
||||
import { createInvoice as clnCreateInvoice } from '@/lib/cln'
|
||||
import { bolt11Tags } from '@/lib/bolt11'
|
||||
import { checkInvoice } from 'worker/wallet'
|
||||
import * as lnd from 'wallets/lnd'
|
||||
import * as lnAddr from 'wallets/lightning-address'
|
||||
import * as cln from 'wallets/cln'
|
||||
import { fetchLnAddrInvoice, generateResolverName } from '@/lib/wallet'
|
||||
|
||||
export const SERVER_WALLET_DEFS = [lnd, lnAddr, cln]
|
||||
import walletDefs from 'wallets/server'
|
||||
import { generateResolverName } from '@/lib/wallet'
|
||||
import { lnAddrOptions } from '@/lib/lnurl'
|
||||
|
||||
function injectResolvers (resolvers) {
|
||||
console.group('injected GraphQL resolvers:')
|
||||
for (
|
||||
const w of SERVER_WALLET_DEFS) {
|
||||
// FIXME: this throws
|
||||
// TypeError: import_server.default is not iterable
|
||||
const w of walletDefs) {
|
||||
const {
|
||||
schema,
|
||||
server: { walletType, walletField, testConnect }
|
||||
walletType, walletField, testConnect
|
||||
// app and worker import file differently
|
||||
} = w.default || w
|
||||
const resolverName = generateResolverName(walletField)
|
||||
|
@ -40,10 +38,7 @@ function injectResolvers (resolvers) {
|
|||
data,
|
||||
{
|
||||
me,
|
||||
models,
|
||||
addWalletLog,
|
||||
lnService: { authenticatedLndGrpc, createInvoice },
|
||||
cln: { createInvoice: clnCreateInvoice }
|
||||
models
|
||||
}
|
||||
)
|
||||
}, { settings, data }, { me, models })
|
||||
|
@ -726,10 +721,69 @@ export async function sendToLnAddr (parent, { addr, amount, maxFee, comment, ...
|
|||
{
|
||||
me,
|
||||
models,
|
||||
lnd,
|
||||
lnService: { decodePaymentRequest, getIdentity }
|
||||
lnd
|
||||
})
|
||||
|
||||
// take pr and createWithdrawl
|
||||
return await createWithdrawal(parent, { invoice: res.pr, maxFee }, { me, models, lnd, headers })
|
||||
}
|
||||
|
||||
export async function fetchLnAddrInvoice (
|
||||
{ addr, amount, maxFee, comment, ...payer },
|
||||
{
|
||||
me, models, lnd, autoWithdraw = false
|
||||
}) {
|
||||
const options = await lnAddrOptions(addr)
|
||||
await ssValidate(lnAddrSchema, { addr, amount, maxFee, comment, ...payer }, options)
|
||||
|
||||
if (payer) {
|
||||
payer = {
|
||||
...payer,
|
||||
identifier: payer.identifier ? `${me.name}@stacker.news` : undefined
|
||||
}
|
||||
payer = Object.fromEntries(
|
||||
Object.entries(payer).filter(([, value]) => !!value)
|
||||
)
|
||||
}
|
||||
|
||||
const milliamount = 1000 * amount
|
||||
const callback = new URL(options.callback)
|
||||
callback.searchParams.append('amount', milliamount)
|
||||
|
||||
if (comment?.length) {
|
||||
callback.searchParams.append('comment', comment)
|
||||
}
|
||||
|
||||
let stringifiedPayerData = ''
|
||||
if (payer && Object.entries(payer).length) {
|
||||
stringifiedPayerData = JSON.stringify(payer)
|
||||
callback.searchParams.append('payerdata', stringifiedPayerData)
|
||||
}
|
||||
|
||||
// call callback with amount and conditionally comment
|
||||
const res = await (await fetch(callback.toString())).json()
|
||||
if (res.status === 'ERROR') {
|
||||
throw new Error(res.reason)
|
||||
}
|
||||
|
||||
// decode invoice
|
||||
try {
|
||||
const decoded = await decodePaymentRequest({ lnd, request: res.pr })
|
||||
const ourPubkey = (await getIdentity({ lnd })).public_key
|
||||
if (autoWithdraw && decoded.destination === ourPubkey && process.env.NODE_ENV === 'production') {
|
||||
// unset lnaddr so we don't trigger another withdrawal with same destination
|
||||
await models.wallet.deleteMany({
|
||||
where: { userId: me.id, type: 'LIGHTNING_ADDRESS' }
|
||||
})
|
||||
throw new Error('automated withdrawals to other stackers are not allowed')
|
||||
}
|
||||
if (!decoded.mtokens || BigInt(decoded.mtokens) !== BigInt(milliamount)) {
|
||||
throw new Error('invoice has incorrect amount')
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
throw e
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { gql } from 'graphql-tag'
|
||||
import { SERVER_WALLET_DEFS } from '@/api/resolvers/wallet'
|
||||
import { generateResolverName } from '@/lib/wallet'
|
||||
|
||||
import walletDefs from 'wallets/server'
|
||||
|
||||
function injectTypeDefs (typeDefs) {
|
||||
console.group('injected GraphQL type defs:')
|
||||
const injected = SERVER_WALLET_DEFS.map(
|
||||
const injected = walletDefs.map(
|
||||
(w) => {
|
||||
let args = 'id: ID, '
|
||||
args += w.fields.map(f => {
|
||||
|
@ -15,7 +16,7 @@ function injectTypeDefs (typeDefs) {
|
|||
return arg
|
||||
}).join(', ')
|
||||
args += ', settings: AutowithdrawSettings!'
|
||||
const resolverName = generateResolverName(w.server.walletField)
|
||||
const resolverName = generateResolverName(w.walletField)
|
||||
const typeDef = `${resolverName}(${args}): Boolean`
|
||||
console.log(typeDef)
|
||||
return typeDef
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Button } from 'react-bootstrap'
|
|||
import { useToast } from './toast'
|
||||
import { useShowModal } from './modal'
|
||||
import { WALLET_LOGS } from '@/fragments/wallet'
|
||||
import { getServerWallet } from 'wallets'
|
||||
import { getWalletByType } from 'wallets'
|
||||
import { gql, useMutation, useQuery } from '@apollo/client'
|
||||
import { useMe } from './me'
|
||||
|
||||
|
@ -128,7 +128,7 @@ export const WalletLoggerProvider = ({ children }) => {
|
|||
.map(({ createdAt, wallet: walletType, ...log }) => {
|
||||
return {
|
||||
ts: +new Date(createdAt),
|
||||
wallet: tag(getServerWallet(walletType)),
|
||||
wallet: tag(getWalletByType(walletType)),
|
||||
...log
|
||||
}
|
||||
})
|
||||
|
@ -146,7 +146,7 @@ export const WalletLoggerProvider = ({ children }) => {
|
|||
{
|
||||
onCompleted: (_, { variables: { wallet: walletType } }) => {
|
||||
setLogs((logs) => {
|
||||
return logs.filter(l => walletType ? l.wallet !== getServerWallet(walletType).name : false)
|
||||
return logs.filter(l => walletType ? l.wallet !== getWalletByType(walletType).name : false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,66 +1,3 @@
|
|||
import { lnAddrOptions } from './lnurl'
|
||||
import { lnAddrSchema, ssValidate } from './validate'
|
||||
|
||||
export async function fetchLnAddrInvoice ({ addr, amount, maxFee, comment, ...payer },
|
||||
{
|
||||
me, models, lnd, autoWithdraw = false,
|
||||
lnService: { decodePaymentRequest, getIdentity }
|
||||
}) {
|
||||
const options = await lnAddrOptions(addr)
|
||||
await ssValidate(lnAddrSchema, { addr, amount, maxFee, comment, ...payer }, options)
|
||||
|
||||
if (payer) {
|
||||
payer = {
|
||||
...payer,
|
||||
identifier: payer.identifier ? `${me.name}@stacker.news` : undefined
|
||||
}
|
||||
payer = Object.fromEntries(
|
||||
Object.entries(payer).filter(([, value]) => !!value)
|
||||
)
|
||||
}
|
||||
|
||||
const milliamount = 1000 * amount
|
||||
const callback = new URL(options.callback)
|
||||
callback.searchParams.append('amount', milliamount)
|
||||
|
||||
if (comment?.length) {
|
||||
callback.searchParams.append('comment', comment)
|
||||
}
|
||||
|
||||
let stringifiedPayerData = ''
|
||||
if (payer && Object.entries(payer).length) {
|
||||
stringifiedPayerData = JSON.stringify(payer)
|
||||
callback.searchParams.append('payerdata', stringifiedPayerData)
|
||||
}
|
||||
|
||||
// call callback with amount and conditionally comment
|
||||
const res = await (await fetch(callback.toString())).json()
|
||||
if (res.status === 'ERROR') {
|
||||
throw new Error(res.reason)
|
||||
}
|
||||
|
||||
// decode invoice
|
||||
try {
|
||||
const decoded = await decodePaymentRequest({ lnd, request: res.pr })
|
||||
const ourPubkey = (await getIdentity({ lnd })).public_key
|
||||
if (autoWithdraw && decoded.destination === ourPubkey && process.env.NODE_ENV === 'production') {
|
||||
// unset lnaddr so we don't trigger another withdrawal with same destination
|
||||
await models.wallet.deleteMany({
|
||||
where: { userId: me.id, type: 'LIGHTNING_ADDRESS' }
|
||||
})
|
||||
throw new Error('automated withdrawals to other stackers are not allowed')
|
||||
}
|
||||
if (!decoded.mtokens || BigInt(decoded.mtokens) !== BigInt(milliamount)) {
|
||||
throw new Error('invoice has incorrect amount')
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
throw e
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
export function generateResolverName (walletField) {
|
||||
const capitalized = walletField[0].toUpperCase() + walletField.slice(1)
|
||||
return `upsertWallet${capitalized}`
|
||||
|
|
|
@ -232,7 +232,10 @@ module.exports = withPlausibleProxy()({
|
|||
})
|
||||
}
|
||||
|
||||
// const ignorePlugin = new webpack.IgnorePlugin({ resourceRegExp: /server\.js$/ })
|
||||
|
||||
config.plugins.push(workboxPlugin)
|
||||
// config.plugins.push(ignorePlugin)
|
||||
}
|
||||
|
||||
config.module.rules.push(
|
||||
|
|
|
@ -1,147 +0,0 @@
|
|||
import { lnbitsSchema } from '@/lib/validate'
|
||||
|
||||
// ~~~
|
||||
// AFTER YOU HAVE FILLED OUT THIS TEMPLATE, IMPORT THIS FILE IN components/wallet/index.js
|
||||
// AND ADD IT TO THE `WALLET_DEFS` array.
|
||||
// DO THE SAME IN api/resolvers/wallet.js WITH THE `SERVER_WALLET_DEFS` ARRAY.
|
||||
// (these arrays are separate to avoid backend imports in frontend)
|
||||
// ~~~
|
||||
|
||||
// This name is used to identify this wallet and thus must be unique.
|
||||
// It is used with the useWallet hook to select this wallet, see components/wallet/index.js.
|
||||
// [required]
|
||||
export const name = 'lnbits-as-an-example'
|
||||
|
||||
// The form to configure this wallet is generated from these fields,
|
||||
// see the component <WalletFields /> in pages/settings/wallets/[wallet].js.
|
||||
//
|
||||
//
|
||||
// If not handled otherwise in <WalletFields />, field properties are simply
|
||||
// passed into <ClientInput /> or <PasswordInput /> as props (component depends on 'type').
|
||||
//
|
||||
// For example, the following fields will generate a config in this shape (depending on user inputs):
|
||||
// {
|
||||
// url: 'https://demo.lnbits.com/',
|
||||
// adminKey: 'a47acd6feba4489e9e99b256b4ae9049'
|
||||
// }
|
||||
// [required]
|
||||
export const fields = [
|
||||
{
|
||||
name: 'url',
|
||||
label: 'lnbits url',
|
||||
// 'type' can be 'text' or 'password'
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'adminKey',
|
||||
label: 'admin key',
|
||||
type: 'password'
|
||||
// see other wallets for more complex fields
|
||||
}
|
||||
]
|
||||
|
||||
// Used to display information about this wallet to the user in the wallet list or during configuration,
|
||||
// see components/wallet-card.js and pages/settings/wallets/[wallet].js.
|
||||
// [required]
|
||||
export const card = {
|
||||
title: 'LNbits',
|
||||
// subtitle supports markdown
|
||||
subtitle: 'use [LNbits](https://lnbits.com/) for payments',
|
||||
badges: ['send only', 'non-custodialish']
|
||||
}
|
||||
|
||||
// The validation schema that will be used on the client and server during save
|
||||
// [required]
|
||||
export const schema = lnbitsSchema
|
||||
|
||||
// This optional function will be called during save to abort the save if the configuration is invalid.
|
||||
// It receives the config and context as arguments.
|
||||
// It must throw an error if validation fails.
|
||||
// [optional]
|
||||
export async function validate (config, context) {
|
||||
// what the config object will contain is determined by the fields array above
|
||||
// const { url, adminKey } = config
|
||||
|
||||
// the context includes the logger and other useful stuff, see save method in components/wallet/index.js
|
||||
const { logger } = context
|
||||
|
||||
// validate should log useful, wallet-specific information for the user
|
||||
logger.info('running some wallet-specific validation')
|
||||
|
||||
// ...
|
||||
// throw error if validation failed
|
||||
}
|
||||
|
||||
// If this wallet supports payments, you need to implement this function:
|
||||
//
|
||||
// sendPayment: (bolt11, config, context) => Promise<{ preimage: string }>
|
||||
//
|
||||
// [required for payments]
|
||||
export async function sendPayment (bolt11, config, context) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// If this wallet supports receiving, you need to implement this object.
|
||||
// [required for receiving]
|
||||
export const server = {
|
||||
// This must match a WalletType enum value in the database
|
||||
// since it will be used to fetch this wallet using the WALLET_BY_TYPE GraphQL query,
|
||||
// see `useServerConfig` in components/wallet/index.js.
|
||||
// [required]
|
||||
walletType: 'LNBITS',
|
||||
|
||||
// This used must match a column of the 'Wallet' table
|
||||
// since it will be used to save the wallet configuration.
|
||||
// [required]
|
||||
walletField: 'walletLNbits',
|
||||
|
||||
// Similar to validate above, this function should throw an error if the connection test fails.
|
||||
// It is called on save on the server before writing the configuration to the database.
|
||||
// As the name suggests, a good idea is to try to connect to the wallet and create an invoice in this function.
|
||||
// [required]
|
||||
testConnect: async (
|
||||
// Wallet configuration as entered by the user
|
||||
config,
|
||||
// Context object with useful stuff, see `injectResolvers` in pages/api/resolvers/wallet.js.
|
||||
{
|
||||
me,
|
||||
models,
|
||||
addWalletLog,
|
||||
lnService: { authenticatedLndGrpc, createInvoice },
|
||||
cln: { createInvoice: clnCreateInvoice }
|
||||
}
|
||||
) => {
|
||||
|
||||
// ...
|
||||
// throw error if validation failed
|
||||
// (logging errors is handled by calling context but you can add custom logging on success here)
|
||||
},
|
||||
|
||||
// This function is called during autowithdrawals.
|
||||
// It should return a bolt11 payment request.
|
||||
//
|
||||
// createInvoice: ({ amount, maxFee }, config, context) => Promise<bolt11>
|
||||
//
|
||||
createInvoice: async (
|
||||
{ amount, maxFee },
|
||||
{ socket, rune, cert },
|
||||
// Context object with useful stuff, see `autowithdraw` function in worker/autowithdraw.js.
|
||||
{
|
||||
me,
|
||||
models,
|
||||
// SN LND node instance
|
||||
lnd,
|
||||
lnService: {
|
||||
authenticatedLndGrpc,
|
||||
createInvoice: lndCreateInvoice,
|
||||
getIdentity,
|
||||
decodePaymentRequest
|
||||
},
|
||||
cln: {
|
||||
createInvoice: clnCreateInvoice
|
||||
}
|
||||
}
|
||||
) => {
|
||||
// ... create invoice and return bolt11 that the SN node will pay
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import * as nwc from 'wallets/nwc/client'
|
||||
import * as lnbits from 'wallets/lnbits/client'
|
||||
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'
|
||||
|
||||
export default [nwc, lnbits, lnc, lnAddr, cln, lnd]
|
|
@ -0,0 +1 @@
|
|||
export * from 'wallets/cln'
|
|
@ -1,5 +1,4 @@
|
|||
import { CLNAutowithdrawSchema } from '@/lib/validate'
|
||||
import { server } from 'wallets/cln/server'
|
||||
|
||||
export const name = 'cln'
|
||||
|
||||
|
@ -41,5 +40,3 @@ export const card = {
|
|||
}
|
||||
|
||||
export const schema = CLNAutowithdrawSchema
|
||||
|
||||
export { server }
|
||||
|
|
|
@ -1,39 +1,44 @@
|
|||
import { ensureB64 } from '@/lib/format'
|
||||
import { createInvoice as clnCreateInvoice } from '@/lib/cln'
|
||||
import { addWalletLog } from '@/api/resolvers/wallet'
|
||||
|
||||
export const server = {
|
||||
walletType: 'CLN',
|
||||
walletField: 'walletCLN',
|
||||
testConnect: async (
|
||||
{ socket, rune, cert },
|
||||
{ me, models, addWalletLog, cln: { createInvoice } }
|
||||
) => {
|
||||
cert = ensureB64(cert)
|
||||
const inv = await createInvoice({
|
||||
socket,
|
||||
rune,
|
||||
cert,
|
||||
description: 'SN connection test',
|
||||
msats: 'any',
|
||||
expiry: 0
|
||||
})
|
||||
await addWalletLog({ wallet: { type: 'CLN' }, level: 'SUCCESS', message: 'connected to CLN' }, { me, models })
|
||||
return inv
|
||||
},
|
||||
createInvoice: async (
|
||||
{ amount },
|
||||
{ socket, rune, cert },
|
||||
{ me, models, lnd, cln: { createInvoice } }
|
||||
) => {
|
||||
cert = ensureB64(cert)
|
||||
export * from 'wallets/cln'
|
||||
|
||||
const inv = await createInvoice({
|
||||
socket,
|
||||
rune,
|
||||
cert,
|
||||
description: me.hideInvoiceDesc ? undefined : 'autowithdraw to CLN from SN',
|
||||
msats: amount + 'sat',
|
||||
expiry: 360
|
||||
})
|
||||
return inv.bolt11
|
||||
}
|
||||
export const walletType = 'CLN'
|
||||
|
||||
export const walletField = 'walletCLN'
|
||||
|
||||
export const testConnect = async (
|
||||
{ socket, rune, cert },
|
||||
{ me, models }
|
||||
) => {
|
||||
cert = ensureB64(cert)
|
||||
const inv = await clnCreateInvoice({
|
||||
socket,
|
||||
rune,
|
||||
cert,
|
||||
description: 'SN connection test',
|
||||
msats: 'any',
|
||||
expiry: 0
|
||||
})
|
||||
await addWalletLog({ wallet: { type: 'CLN' }, level: 'SUCCESS', message: 'connected to CLN' }, { me, models })
|
||||
return inv
|
||||
}
|
||||
|
||||
export const createInvoice = async (
|
||||
{ amount },
|
||||
{ socket, rune, cert },
|
||||
{ me, models, lnd }
|
||||
) => {
|
||||
cert = ensureB64(cert)
|
||||
|
||||
const inv = await clnCreateInvoice({
|
||||
socket,
|
||||
rune,
|
||||
cert,
|
||||
description: me.hideInvoiceDesc ? undefined : 'autowithdraw to CLN from SN',
|
||||
msats: amount + 'sat',
|
||||
expiry: 360
|
||||
})
|
||||
return inv.bolt11
|
||||
}
|
||||
|
|
|
@ -5,12 +5,7 @@ import { useWalletLogger } from '@/components/wallet-logger'
|
|||
import { SSR } from '@/lib/constants'
|
||||
import { bolt11Tags } from '@/lib/bolt11'
|
||||
|
||||
import * as lnbits from 'wallets/lnbits'
|
||||
import * as nwc from 'wallets/nwc'
|
||||
import * as lnc from 'wallets/lnc'
|
||||
import * as lnd from 'wallets/lnd'
|
||||
import * as lnAddr from 'wallets/lightning-address'
|
||||
import * as cln from 'wallets/cln'
|
||||
import walletDefs from 'wallets/client'
|
||||
import { gql, useApolloClient, useQuery } from '@apollo/client'
|
||||
import { REMOVE_WALLET, WALLET_BY_TYPE } from '@/fragments/wallet'
|
||||
import { autowithdrawInitial } from '@/components/autowithdraw-shared'
|
||||
|
@ -18,9 +13,6 @@ import { useShowModal } from '@/components/modal'
|
|||
import { useToast } from '../components/toast'
|
||||
import { generateResolverName } from '@/lib/wallet'
|
||||
|
||||
// wallet definitions
|
||||
export const WALLET_DEFS = [lnbits, nwc, lnc, lnd, lnAddr, cln]
|
||||
|
||||
export const Status = {
|
||||
Initialized: 'Initialized',
|
||||
Enabled: 'Enabled',
|
||||
|
@ -249,15 +241,15 @@ function generateMutation (wallet) {
|
|||
}
|
||||
|
||||
export function getWalletByName (name) {
|
||||
return WALLET_DEFS.find(def => def.name === name)
|
||||
return walletDefs.find(def => def.name === name)
|
||||
}
|
||||
|
||||
export function getServerWallet (type) {
|
||||
return WALLET_DEFS.find(def => def.server?.walletType === type)
|
||||
export function getWalletByType (type) {
|
||||
return walletDefs.find(def => def.server?.walletType === type)
|
||||
}
|
||||
|
||||
export function getEnabledWallet (me) {
|
||||
return WALLET_DEFS
|
||||
return walletDefs
|
||||
.filter(def => !!def.sendPayment)
|
||||
.map(def => {
|
||||
// populate definition with properties from useWallet that are required for sorting
|
||||
|
@ -290,7 +282,7 @@ export function walletPrioritySort (w1, w2) {
|
|||
}
|
||||
|
||||
export function useWallets () {
|
||||
const wallets = WALLET_DEFS.map(def => useWallet(def.name))
|
||||
const wallets = walletDefs.map(def => useWallet(def.name))
|
||||
|
||||
const resetClient = useCallback(async (wallet) => {
|
||||
for (const w of wallets) {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from 'wallets/lightning-address'
|
|
@ -1,5 +1,4 @@
|
|||
import { lnAddrAutowithdrawSchema } from '@/lib/validate'
|
||||
import { server } from 'wallets/lightning-address/server'
|
||||
|
||||
export const name = 'lightning-address'
|
||||
export const shortName = 'lnAddr'
|
||||
|
@ -20,5 +19,3 @@ export const card = {
|
|||
}
|
||||
|
||||
export const schema = lnAddrAutowithdrawSchema
|
||||
|
||||
export { server }
|
||||
|
|
|
@ -1,29 +1,32 @@
|
|||
import { fetchLnAddrInvoice } from '@/api/resolvers/wallet'
|
||||
import { lnAddrOptions } from '@/lib/lnurl'
|
||||
import { fetchLnAddrInvoice } from '@/lib/wallet'
|
||||
|
||||
export const server = {
|
||||
walletType: 'LIGHTNING_ADDRESS',
|
||||
walletField: 'walletLightningAddress',
|
||||
testConnect: async (
|
||||
{ address },
|
||||
{ me, models, addWalletLog }
|
||||
) => {
|
||||
const options = await lnAddrOptions(address)
|
||||
await addWalletLog({ wallet: { type: 'LIGHTNING_ADDRESS' }, level: 'SUCCESS', message: 'fetched payment details' }, { me, models })
|
||||
return options
|
||||
},
|
||||
createInvoice: async (
|
||||
{ amount, maxFee },
|
||||
{ address },
|
||||
{ me, models, lnd, lnService }
|
||||
) => {
|
||||
const res = await fetchLnAddrInvoice({ addr: address, amount, maxFee }, {
|
||||
me,
|
||||
models,
|
||||
lnd,
|
||||
lnService,
|
||||
autoWithdraw: true
|
||||
})
|
||||
return res.pr
|
||||
}
|
||||
export * from 'wallets/lightning-address'
|
||||
|
||||
export const walletType = 'LIGHTNING_ADDRESS'
|
||||
|
||||
export const walletField = 'walletLightningAddress'
|
||||
|
||||
export const testConnect = async (
|
||||
{ address },
|
||||
{ me, models, addWalletLog }
|
||||
) => {
|
||||
const options = await lnAddrOptions(address)
|
||||
await addWalletLog({ wallet: { type: 'LIGHTNING_ADDRESS' }, level: 'SUCCESS', message: 'fetched payment details' }, { me, models })
|
||||
return options
|
||||
}
|
||||
|
||||
export const createInvoice = async (
|
||||
{ amount, maxFee },
|
||||
{ address },
|
||||
{ me, models, lnd, lnService }
|
||||
) => {
|
||||
const res = await fetchLnAddrInvoice({ addr: address, amount, maxFee }, {
|
||||
me,
|
||||
models,
|
||||
lnd,
|
||||
lnService,
|
||||
autoWithdraw: true
|
||||
})
|
||||
return res.pr
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
export * from 'wallets/lnbits'
|
||||
|
||||
export async function validate ({ url, adminKey }, { logger }) {
|
||||
logger.info('trying to fetch wallet')
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { lnbitsSchema } from '@/lib/validate'
|
||||
import { sendPayment, validate } from 'wallets/lnbits/client'
|
||||
|
||||
export const name = 'lnbits'
|
||||
|
||||
|
@ -23,5 +22,3 @@ export const card = {
|
|||
}
|
||||
|
||||
export const schema = lnbitsSchema
|
||||
|
||||
export { sendPayment, validate }
|
||||
|
|
|
@ -6,6 +6,8 @@ import LNC from '@lightninglabs/lnc-web'
|
|||
import { Mutex } from 'async-mutex'
|
||||
import { Status } from 'wallets'
|
||||
|
||||
export * from 'wallets/lnc'
|
||||
|
||||
const XXX_DEFAULT_PASSWORD = 'password'
|
||||
|
||||
export async function validate ({ pairingPhrase, password }, { me, logger }) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { lncSchema } from '@/lib/validate'
|
||||
import { sendPayment, validate } from 'wallets/lnc/client'
|
||||
|
||||
export const name = 'lnc'
|
||||
|
||||
|
@ -26,5 +25,3 @@ export const card = {
|
|||
}
|
||||
|
||||
export const schema = lncSchema
|
||||
|
||||
export { sendPayment, validate }
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from 'wallets/lnd'
|
|
@ -1,5 +1,4 @@
|
|||
import { LNDAutowithdrawSchema } from '@/lib/validate'
|
||||
import { server } from 'wallets/lnd/server'
|
||||
|
||||
export const name = 'lnd'
|
||||
|
||||
|
@ -42,5 +41,3 @@ export const card = {
|
|||
}
|
||||
|
||||
export const schema = LNDAutowithdrawSchema
|
||||
|
||||
export { server }
|
||||
|
|
|
@ -1,58 +1,63 @@
|
|||
import { ensureB64 } from '@/lib/format'
|
||||
import { datePivot } from '@/lib/time'
|
||||
import { authenticatedLndGrpc, createInvoice as lndCreateInvoice } from 'ln-service'
|
||||
import { addWalletLog } from '@/api/resolvers/wallet'
|
||||
|
||||
export const server = {
|
||||
walletType: 'LND',
|
||||
walletField: 'walletLND',
|
||||
testConnect: async (
|
||||
{ cert, macaroon, socket },
|
||||
{ me, models, addWalletLog, lnService: { authenticatedLndGrpc, createInvoice } }
|
||||
) => {
|
||||
try {
|
||||
cert = ensureB64(cert)
|
||||
macaroon = ensureB64(macaroon)
|
||||
export * from 'wallets/lnd'
|
||||
|
||||
const { lnd } = await authenticatedLndGrpc({
|
||||
cert,
|
||||
macaroon,
|
||||
socket
|
||||
})
|
||||
export const walletType = 'LND'
|
||||
|
||||
const inv = await createInvoice({
|
||||
description: 'SN connection test',
|
||||
lnd,
|
||||
tokens: 0,
|
||||
expires_at: new Date()
|
||||
})
|
||||
export const walletField = 'walletLND'
|
||||
|
||||
// we wrap both calls in one try/catch since connection attempts happen on RPC calls
|
||||
await addWalletLog({ wallet: { type: 'LND' }, level: 'SUCCESS', message: 'connected to LND' }, { me, models })
|
||||
export const testConnect = async (
|
||||
{ cert, macaroon, socket },
|
||||
{ me, models }
|
||||
) => {
|
||||
try {
|
||||
cert = ensureB64(cert)
|
||||
macaroon = ensureB64(macaroon)
|
||||
|
||||
return inv
|
||||
} catch (err) {
|
||||
// LND errors are in this shape: [code, type, { err: { code, details, metadata } }]
|
||||
const details = err[2]?.err?.details || err.message || err.toString?.()
|
||||
throw new Error(details)
|
||||
}
|
||||
},
|
||||
createInvoice: async (
|
||||
{ amount },
|
||||
{ cert, macaroon, socket },
|
||||
{ me, lnService: { authenticatedLndGrpc, createInvoice } }
|
||||
) => {
|
||||
const { lnd } = await authenticatedLndGrpc({
|
||||
cert,
|
||||
macaroon,
|
||||
socket
|
||||
})
|
||||
|
||||
const invoice = await createInvoice({
|
||||
description: me.hideInvoiceDesc ? undefined : 'autowithdraw to LND from SN',
|
||||
const inv = await lndCreateInvoice({
|
||||
description: 'SN connection test',
|
||||
lnd,
|
||||
tokens: amount,
|
||||
expires_at: datePivot(new Date(), { seconds: 360 })
|
||||
tokens: 0,
|
||||
expires_at: new Date()
|
||||
})
|
||||
|
||||
return invoice.request
|
||||
// we wrap both calls in one try/catch since connection attempts happen on RPC calls
|
||||
await addWalletLog({ wallet: { type: 'LND' }, level: 'SUCCESS', message: 'connected to LND' }, { me, models })
|
||||
|
||||
return inv
|
||||
} catch (err) {
|
||||
// LND errors are in this shape: [code, type, { err: { code, details, metadata } }]
|
||||
const details = err[2]?.err?.details || err.message || err.toString?.()
|
||||
throw new Error(details)
|
||||
}
|
||||
}
|
||||
|
||||
export const createInvoice = async (
|
||||
{ amount },
|
||||
{ cert, macaroon, socket },
|
||||
{ me }
|
||||
) => {
|
||||
const { lnd } = await authenticatedLndGrpc({
|
||||
cert,
|
||||
macaroon,
|
||||
socket
|
||||
})
|
||||
|
||||
const invoice = await lndCreateInvoice({
|
||||
description: me.hideInvoiceDesc ? undefined : 'autowithdraw to LND from SN',
|
||||
lnd,
|
||||
tokens: amount,
|
||||
expires_at: datePivot(new Date(), { seconds: 360 })
|
||||
})
|
||||
|
||||
return invoice.request
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { parseNwcUrl } from '@/lib/url'
|
||||
import { Relay, finalizeEvent, nip04 } from 'nostr-tools'
|
||||
|
||||
export * from 'wallets/nwc'
|
||||
|
||||
export async function validate ({ nwcUrl }, { logger }) {
|
||||
const { relayUrl, walletPubkey } = parseNwcUrl(nwcUrl)
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { nwcSchema } from '@/lib/validate'
|
||||
import { sendPayment, validate } from 'wallets/nwc/client'
|
||||
|
||||
export const name = 'nwc'
|
||||
|
||||
|
@ -18,5 +17,3 @@ export const card = {
|
|||
}
|
||||
|
||||
export const schema = nwcSchema
|
||||
|
||||
export { sendPayment, validate }
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import * as lnd from 'wallets/lnd/server'
|
||||
import * as cln from 'wallets/cln/server'
|
||||
import * as lnAddr from 'wallets/lightning-address/server'
|
||||
|
||||
// worker and app import modules differently
|
||||
// const resolveImport = i => i.default || i
|
||||
|
||||
export default [lnd, cln, lnAddr]
|
|
@ -1,7 +1,6 @@
|
|||
import { authenticatedLndGrpc, createInvoice as lndCreateInvoice, getIdentity, decodePaymentRequest } from 'ln-service'
|
||||
import { msatsToSats, satsToMsats } from '@/lib/format'
|
||||
import { createWithdrawal, addWalletLog, SERVER_WALLET_DEFS } from '@/api/resolvers/wallet'
|
||||
import { createInvoice as clnCreateInvoice } from '@/lib/cln'
|
||||
import { createWithdrawal, addWalletLog } from '@/api/resolvers/wallet'
|
||||
import walletDefs from 'wallets/server'
|
||||
|
||||
export async function autoWithdraw ({ data: { id }, models, lnd }) {
|
||||
const user = await models.user.findUnique({ where: { id } })
|
||||
|
@ -48,9 +47,9 @@ export async function autoWithdraw ({ data: { id }, models, lnd }) {
|
|||
})
|
||||
|
||||
for (const wallet of wallets) {
|
||||
const w = SERVER_WALLET_DEFS.find(({ default: w }) => w.server.walletType === wallet.type)
|
||||
const w = walletDefs.find(({ default: w }) => w.walletType === wallet.type)
|
||||
try {
|
||||
const { server: { walletType, walletField, createInvoice } } = w.default
|
||||
const { walletType, walletField, createInvoice } = w.default
|
||||
return await autowithdraw(
|
||||
{ walletType, walletField, createInvoice },
|
||||
{ amount, maxFee },
|
||||
|
@ -58,6 +57,9 @@ export async function autoWithdraw ({ data: { id }, models, lnd }) {
|
|||
)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
// TODO: I think this is a bug, `walletCreateInvoice` in `autowithdraw` should parse the error
|
||||
|
||||
// LND errors are in this shape: [code, type, { err: { code, details, metadata } }]
|
||||
const details = error[2]?.err?.details || error.message || error.toString?.()
|
||||
await addWalletLog({
|
||||
|
@ -99,16 +101,7 @@ async function autowithdraw (
|
|||
{
|
||||
me,
|
||||
models,
|
||||
lnd,
|
||||
lnService: {
|
||||
authenticatedLndGrpc,
|
||||
createInvoice: lndCreateInvoice,
|
||||
getIdentity,
|
||||
decodePaymentRequest
|
||||
},
|
||||
cln: {
|
||||
createInvoice: clnCreateInvoice
|
||||
}
|
||||
lnd
|
||||
})
|
||||
|
||||
return await createWithdrawal(null, { invoice: bolt11, maxFee }, { me, models, lnd, walletId: wallet.id })
|
||||
|
|
Loading…
Reference in New Issue