diff --git a/api/resolvers/wallet.js b/api/resolvers/wallet.js
index 4b21ac40..268b4ad9 100644
--- a/api/resolvers/wallet.js
+++ b/api/resolvers/wallet.js
@@ -24,6 +24,7 @@ import { lnAddrOptions } from '@/lib/lnurl'
import { GqlAuthenticationError, GqlAuthorizationError, GqlInputError } from '@/lib/error'
import { getNodeSockets, getOurPubkey } from '../lnd'
import validateWallet from '@/wallets/validate'
+import { canReceive } from '@/wallets/common'
function injectResolvers (resolvers) {
console.group('injected GraphQL resolvers:')
@@ -43,9 +44,10 @@ function injectResolvers (resolvers) {
field: walletDef.walletField,
type: walletDef.walletType
},
- testCreateInvoice: walletDef.testCreateInvoice && validateLightning
- ? (data) => walletDef.testCreateInvoice(data, { me, models })
- : null
+ testCreateInvoice:
+ walletDef.testCreateInvoice && validateLightning && canReceive({ def: walletDef, config: data })
+ ? (data) => walletDef.testCreateInvoice(data, { me, models })
+ : null
}, {
settings,
data,
diff --git a/components/banners.js b/components/banners.js
index a36f5b54..72387e7c 100644
--- a/components/banners.js
+++ b/components/banners.js
@@ -7,6 +7,7 @@ import { WELCOME_BANNER_MUTATION } from '@/fragments/users'
import { useToast } from '@/components/toast'
import { BALANCE_LIMIT_MSATS } from '@/lib/constants'
import { msatsToSats, numWithUnits } from '@/lib/format'
+import Link from 'next/link'
export function WelcomeBanner ({ Banner }) {
const { me } = useMe()
@@ -132,7 +133,7 @@ export function WalletSecurityBanner ({ isActive }) {
Listen up, pardner! Put a limit on yer spendin' wallet or hook up a wallet that's only for Stacker News. It'll keep them varmints from cleanin' out yer whole goldmine if they rustle up yer wallet.
- Your spending wallet's credentials are never sent to our servers in plain text. To sync across devices, enable device sync in your settings.
+ Your spending wallet's credentials are never sent to our servers in plain text. To sync across devices, enable device sync in your settings.
)
diff --git a/pages/settings/passphrase/index.js b/pages/settings/passphrase/index.js
index 612d53ca..d52e3a72 100644
--- a/pages/settings/passphrase/index.js
+++ b/pages/settings/passphrase/index.js
@@ -17,7 +17,8 @@ export const getServerSideProps = getGetServerSideProps({ authRequired: true })
export default function DeviceSync ({ ssrData }) {
const { me } = useMe()
const { onVaultKeySet, beforeDisconnectVault } = useWallets()
- const { key, setVaultKey, clearVault, disconnectVault } = useVaultConfigurator({ onVaultKeySet, beforeDisconnectVault })
+ const { key, setVaultKey, clearVault, disconnectVault } =
+ useVaultConfigurator({ onVaultKeySet, beforeDisconnectVault })
const [passphrase, setPassphrase] = useState()
const setSeedPassphrase = useCallback(async (passphrase) => {
diff --git a/pages/settings/wallets/[wallet].js b/pages/settings/wallets/[wallet].js
index 3b934c7f..f1c1a026 100644
--- a/pages/settings/wallets/[wallet].js
+++ b/pages/settings/wallets/[wallet].js
@@ -52,7 +52,8 @@ export default function WalletSettings () {
const validate = useCallback(async (data) => {
try {
- await validateWallet(wallet.def, data, { yupOptions: { abortEarly: false }, topLevel: false })
+ await validateWallet(wallet.def, data,
+ { yupOptions: { abortEarly: false }, topLevel: false, skipGenerated: true })
} catch (error) {
if (error instanceof ValidationError) {
return error.inner.reduce((acc, error) => {
@@ -81,7 +82,7 @@ export default function WalletSettings () {
values.enabled = true
}
- await save(values, true)
+ await save(values, values.enabled)
toaster.success('saved settings')
router.push('/settings/wallets')
@@ -137,7 +138,10 @@ function ReceiveSettings ({ walletDef }) {
function WalletFields ({ wallet }) {
return wallet.def.fields
- .map(({ name, label = '', type, help, optional, editable, requiredWithout, validate, clientOnly, serverOnly, ...props }, i) => {
+ .map(({
+ name, label = '', type, help, optional, editable, requiredWithout,
+ validate, clientOnly, serverOnly, generated, ...props
+ }, i) => {
const rawProps = {
...props,
name,
diff --git a/wallets/common.js b/wallets/common.js
index 7bcef430..43c89ac3 100644
--- a/wallets/common.js
+++ b/wallets/common.js
@@ -62,14 +62,14 @@ export function isClientField (f) {
function checkFields ({ fields, config }) {
// a wallet is configured if all of its required fields are set
let val = fields.every(f => {
- if (f.optional && !f.requiredWithout) return true
+ if ((f.optional || f.generated) && !f.requiredWithout) return true
return !!config?.[f.name]
})
// however, a wallet is not configured if all fields are optional and none are set
// since that usually means that one of them is required
if (val && fields.length > 0) {
- val = !(fields.every(f => f.optional) && fields.every(f => !config?.[f.name]))
+ val = !(fields.every(f => f.optional || f.generated) && fields.every(f => !config?.[f.name]))
}
return val
@@ -81,12 +81,12 @@ export function isConfigured ({ def, config }) {
function isSendConfigured ({ def, config }) {
const fields = def.fields.filter(isClientField)
- return checkFields({ fields, config })
+ return (fields.length > 0 || def.isAvailable?.()) && checkFields({ fields, config })
}
function isReceiveConfigured ({ def, config }) {
const fields = def.fields.filter(isServerField)
- return checkFields({ fields, config })
+ return fields.length > 0 && checkFields({ fields, config })
}
export function canSend ({ def, config }) {
diff --git a/wallets/config.js b/wallets/config.js
index 16014040..25185a8c 100644
--- a/wallets/config.js
+++ b/wallets/config.js
@@ -37,7 +37,7 @@ export function useWalletConfigurator (wallet) {
let serverConfig = serverWithShared
if (canSend({ def: wallet.def, config: clientConfig })) {
- let transformedConfig = await validateWallet(wallet.def, clientWithShared)
+ let transformedConfig = await validateWallet(wallet.def, clientWithShared, { skipGenerated: true })
if (transformedConfig) {
clientConfig = Object.assign(clientConfig, transformedConfig)
}
@@ -46,6 +46,8 @@ export function useWalletConfigurator (wallet) {
if (transformedConfig) {
clientConfig = Object.assign(clientConfig, transformedConfig)
}
+ // validate again to ensure generated fields are valid
+ await validateWallet(wallet.def, clientConfig)
}
} else if (canReceive({ def: wallet.def, config: serverConfig })) {
const transformedConfig = await validateWallet(wallet.def, serverConfig)
@@ -74,6 +76,7 @@ export function useWalletConfigurator (wallet) {
// if vault is active, encrypt and send to server regardless of wallet type
if (isActive) {
await _saveToServer(serverConfig, clientConfig, validateLightning)
+ await _detachFromLocal()
} else {
if (canSend({ def: wallet.def, config: clientConfig })) {
await _saveToLocal(clientConfig)
diff --git a/wallets/lnc/index.js b/wallets/lnc/index.js
index 51d41ad9..03a795bd 100644
--- a/wallets/lnc/index.js
+++ b/wallets/lnc/index.js
@@ -36,23 +36,26 @@ export const fields = [
{
name: 'localKey',
type: 'text',
- optional: true,
hidden: true,
- clientOnly: true
+ clientOnly: true,
+ generated: true,
+ validate: string()
},
{
name: 'remoteKey',
type: 'text',
- optional: true,
hidden: true,
- clientOnly: true
+ clientOnly: true,
+ generated: true,
+ validate: string()
},
{
name: 'serverHost',
type: 'text',
- optional: true,
hidden: true,
- clientOnly: true
+ clientOnly: true,
+ generated: true,
+ validate: string()
}
]
diff --git a/wallets/validate.js b/wallets/validate.js
index 8131189c..7d7734d7 100644
--- a/wallets/validate.js
+++ b/wallets/validate.js
@@ -12,8 +12,8 @@ import * as Yup from '@/lib/yup'
import { canReceive } from './common'
export default async function validateWallet (walletDef, data,
- { yupOptions = { abortEarly: true }, topLevel = true, serverSide = false } = {}) {
- let schema = composeWalletSchema(walletDef, serverSide)
+ { yupOptions = { abortEarly: true }, topLevel = true, serverSide = false, skipGenerated = false } = {}) {
+ let schema = composeWalletSchema(walletDef, serverSide, skipGenerated)
if (canReceive({ def: walletDef, config: data })) {
schema = schema.concat(autowithdrawSchemaMembers)
@@ -58,12 +58,16 @@ function createFieldSchema (name, validate) {
}
}
-function composeWalletSchema (walletDef, serverSide) {
+function composeWalletSchema (walletDef, serverSide, skipGenerated) {
const { fields } = walletDef
const vaultEntrySchemas = { required: [], optional: [] }
const schemaShape = fields.reduce((acc, field) => {
- const { name, validate, optional, clientOnly, requiredWithout } = field
+ const { name, validate, optional, generated, clientOnly, requiredWithout } = field
+
+ if (generated && skipGenerated) {
+ return acc
+ }
if (clientOnly && serverSide) {
// For server-side validation, accumulate clientOnly fields as vaultEntries