Generate validation schema for LND
This commit is contained in:
parent
3933a4f460
commit
587bfa34be
|
@ -13,18 +13,18 @@ import assertApiKeyNotPermitted from './apiKey'
|
||||||
import { bolt11Tags } from '@/lib/bolt11'
|
import { bolt11Tags } from '@/lib/bolt11'
|
||||||
import { checkInvoice } from 'worker/wallet'
|
import { checkInvoice } from 'worker/wallet'
|
||||||
import walletDefs from 'wallets/server'
|
import walletDefs from 'wallets/server'
|
||||||
import { generateResolverName } from '@/lib/wallet'
|
import { generateResolverName, generateSchema } from '@/lib/wallet'
|
||||||
import { lnAddrOptions } from '@/lib/lnurl'
|
import { lnAddrOptions } from '@/lib/lnurl'
|
||||||
|
|
||||||
function injectResolvers (resolvers) {
|
function injectResolvers (resolvers) {
|
||||||
console.group('injected GraphQL resolvers:')
|
console.group('injected GraphQL resolvers:')
|
||||||
for (const w of walletDefs) {
|
for (const w of walletDefs) {
|
||||||
const { schema, walletType, walletField, testConnect } = w
|
const { walletType, walletField, testConnect } = w
|
||||||
const resolverName = generateResolverName(walletField)
|
const resolverName = generateResolverName(walletField)
|
||||||
console.log(resolverName)
|
console.log(resolverName)
|
||||||
resolvers.Mutation[resolverName] = async (parent, { settings, ...data }, { me, models }) => {
|
resolvers.Mutation[resolverName] = async (parent, { settings, ...data }, { me, models }) => {
|
||||||
return await upsertWallet({
|
return await upsertWallet({
|
||||||
schema,
|
schema: generateSchema(w),
|
||||||
wallet: { field: walletField, type: walletType },
|
wallet: { field: walletField, type: walletType },
|
||||||
testConnect: (data) =>
|
testConnect: (data) =>
|
||||||
testConnect(data, { me, models })
|
testConnect(data, { me, models })
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { NOSTR_MAX_RELAY_NUM, NOSTR_PUBKEY_BECH32, NOSTR_PUBKEY_HEX } from './no
|
||||||
import { msatsToSats, numWithUnits, abbrNum, ensureB64, B64_URL_REGEX } from './format'
|
import { msatsToSats, numWithUnits, abbrNum, ensureB64, B64_URL_REGEX } from './format'
|
||||||
import * as usersFragments from '@/fragments/users'
|
import * as usersFragments from '@/fragments/users'
|
||||||
import * as subsFragments from '@/fragments/subs'
|
import * as subsFragments from '@/fragments/subs'
|
||||||
import { isInvoicableMacaroon, isInvoiceMacaroon } from './macaroon'
|
|
||||||
import { datePivot } from './time'
|
import { datePivot } from './time'
|
||||||
import { decodeRune } from '@/lib/cln'
|
import { decodeRune } from '@/lib/cln'
|
||||||
import bip39Words from './bip39-words'
|
import bip39Words from './bip39-words'
|
||||||
|
@ -156,7 +155,7 @@ const lightningAddressValidator = process.env.NODE_ENV === 'development'
|
||||||
'address is no good')
|
'address is no good')
|
||||||
: string().email('address is no good')
|
: string().email('address is no good')
|
||||||
|
|
||||||
const hexOrBase64Validator = string().test({
|
export const hexOrBase64Validator = string().test({
|
||||||
name: 'hex-or-base64',
|
name: 'hex-or-base64',
|
||||||
message: 'invalid encoding',
|
message: 'invalid encoding',
|
||||||
test: (val) => {
|
test: (val) => {
|
||||||
|
@ -311,20 +310,7 @@ export function lnAddrAutowithdrawSchema ({ me } = {}) {
|
||||||
test: addr => !addr.endsWith('@stacker.news'),
|
test: addr => !addr.endsWith('@stacker.news'),
|
||||||
message: 'automated withdrawals must be external'
|
message: 'automated withdrawals must be external'
|
||||||
}),
|
}),
|
||||||
...autowithdrawSchemaMembers({ me })
|
...autowithdrawSchemaMembers
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LNDAutowithdrawSchema ({ me } = {}) {
|
|
||||||
return object({
|
|
||||||
socket: string().socket().required('required'),
|
|
||||||
macaroon: hexOrBase64Validator.required('required').test({
|
|
||||||
name: 'macaroon',
|
|
||||||
test: v => isInvoiceMacaroon(v) || isInvoicableMacaroon(v),
|
|
||||||
message: 'not an invoice macaroon or an invoicable macaroon'
|
|
||||||
}),
|
|
||||||
cert: hexOrBase64Validator,
|
|
||||||
...autowithdrawSchemaMembers({ me })
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,16 +336,14 @@ export function CLNAutowithdrawSchema ({ me } = {}) {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
cert: hexOrBase64Validator,
|
cert: hexOrBase64Validator,
|
||||||
...autowithdrawSchemaMembers({ me })
|
...autowithdrawSchemaMembers
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function autowithdrawSchemaMembers ({ me } = {}) {
|
export const autowithdrawSchemaMembers = {
|
||||||
return {
|
enabled: boolean(),
|
||||||
enabled: boolean(),
|
autoWithdrawThreshold: intValidator.required('required').min(0, 'must be at least 0').max(msatsToSats(BALANCE_LIMIT_MSATS), `must be at most ${abbrNum(msatsToSats(BALANCE_LIMIT_MSATS))}`),
|
||||||
autoWithdrawThreshold: intValidator.required('required').min(0, 'must be at least 0').max(msatsToSats(BALANCE_LIMIT_MSATS), `must be at most ${abbrNum(msatsToSats(BALANCE_LIMIT_MSATS))}`),
|
autoWithdrawMaxFeePercent: floatValidator.required('required').min(0, 'must be at least 0').max(50, 'must not exceed 50')
|
||||||
autoWithdrawMaxFeePercent: floatValidator.required('required').min(0, 'must be at least 0').max(50, 'must not exceed 50')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bountySchema (args) {
|
export function bountySchema (args) {
|
||||||
|
|
105
lib/wallet.js
105
lib/wallet.js
|
@ -1,4 +1,109 @@
|
||||||
|
import { array, object, string } from 'yup'
|
||||||
|
import { autowithdrawSchemaMembers, hexOrBase64Validator } from '@/lib/validate'
|
||||||
|
import { TOR_REGEXP } from '@/lib/url'
|
||||||
|
|
||||||
export function generateResolverName (walletField) {
|
export function generateResolverName (walletField) {
|
||||||
const capitalized = walletField[0].toUpperCase() + walletField.slice(1)
|
const capitalized = walletField[0].toUpperCase() + walletField.slice(1)
|
||||||
return `upsertWallet${capitalized}`
|
return `upsertWallet${capitalized}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateSchema (wallet) {
|
||||||
|
if (wallet.schema) return wallet.schema
|
||||||
|
|
||||||
|
const fieldValidator = (field) => {
|
||||||
|
if (!field.validate) {
|
||||||
|
// default validation
|
||||||
|
let validator = string()
|
||||||
|
if (!field.optional) validator = validator.required('required')
|
||||||
|
return validator
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex validation
|
||||||
|
if (field.validate.schema) return field.validate.schema
|
||||||
|
|
||||||
|
const { type: validationType, words, min, max } = field.validate
|
||||||
|
|
||||||
|
let validator
|
||||||
|
|
||||||
|
const stringTypes = ['url', 'string']
|
||||||
|
|
||||||
|
if (stringTypes.includes(validationType)) {
|
||||||
|
validator = string()
|
||||||
|
|
||||||
|
if (field.validate.length) validator = validator.length(field.validate.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validationType === 'url') {
|
||||||
|
validator = process.env.NODE_ENV === 'development'
|
||||||
|
? validator
|
||||||
|
.or([string().matches(/^(http:\/\/)?localhost:\d+$/), string().url()], 'invalid url')
|
||||||
|
: validator
|
||||||
|
.url()
|
||||||
|
.test(async (url, context) => {
|
||||||
|
if (field.validate.torAllowed && TOR_REGEXP.test(url)) {
|
||||||
|
// allow HTTP and HTTPS over Tor
|
||||||
|
if (!/^https?:\/\//.test(url)) {
|
||||||
|
return context.createError({ message: 'http or https required' })
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// force HTTPS over clearnet
|
||||||
|
await string().https().validate(url)
|
||||||
|
} catch (err) {
|
||||||
|
return context.createError({ message: err.message })
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (words) {
|
||||||
|
validator = array()
|
||||||
|
.transform(function (value, originalValue) {
|
||||||
|
if (this.isType(value) && value !== null) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return originalValue ? originalValue.trim().split(/[\s]+/) : []
|
||||||
|
})
|
||||||
|
.test(async (values, context) => {
|
||||||
|
for (const v of values) {
|
||||||
|
try {
|
||||||
|
await string().oneOf(words).validate(v)
|
||||||
|
} catch {
|
||||||
|
return context.createError({ message: `'${v}' is not a valid ${field.label} word` })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validationType === 'socket') validator = string().socket()
|
||||||
|
|
||||||
|
if (validationType === 'hexOrBase64') validator = hexOrBase64Validator
|
||||||
|
|
||||||
|
if (min !== undefined) validator = validator.min(min)
|
||||||
|
if (max !== undefined) validator = validator.max(max)
|
||||||
|
|
||||||
|
if (!field.optional) validator = validator.required('required')
|
||||||
|
|
||||||
|
if (field.validate.test) {
|
||||||
|
validator = validator.test({
|
||||||
|
name: field.name,
|
||||||
|
test: field.validate.test,
|
||||||
|
message: field.validate.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return validator
|
||||||
|
}
|
||||||
|
|
||||||
|
return object({
|
||||||
|
...wallet.fields.reduce((acc, field) => {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[field.name]: fieldValidator(field)
|
||||||
|
}
|
||||||
|
}, {}),
|
||||||
|
...(wallet.walletType ? autowithdrawSchemaMembers : {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -10,10 +10,7 @@ import Info from '@/components/info'
|
||||||
import Text from '@/components/text'
|
import Text from '@/components/text'
|
||||||
import { AutowithdrawSettings } from '@/components/autowithdraw-shared'
|
import { AutowithdrawSettings } from '@/components/autowithdraw-shared'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
import { array, object, string } from 'yup'
|
import { generateSchema } from '@/lib/wallet'
|
||||||
import { autowithdrawSchemaMembers } from '@/lib/validate'
|
|
||||||
import { useMe } from '@/components/me'
|
|
||||||
import { TOR_REGEXP } from '@/lib/url'
|
|
||||||
|
|
||||||
const WalletButtonBar = dynamic(() => import('@/components/wallet-buttonbar.js'), { ssr: false })
|
const WalletButtonBar = dynamic(() => import('@/components/wallet-buttonbar.js'), { ssr: false })
|
||||||
|
|
||||||
|
@ -24,7 +21,6 @@ export default function WalletSettings () {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { wallet: name } = router.query
|
const { wallet: name } = router.query
|
||||||
const wallet = useWallet(name)
|
const wallet = useWallet(name)
|
||||||
const me = useMe()
|
|
||||||
|
|
||||||
const initial = wallet.fields.reduce((acc, field) => {
|
const initial = wallet.fields.reduce((acc, field) => {
|
||||||
// We still need to run over all wallet fields via reduce
|
// We still need to run over all wallet fields via reduce
|
||||||
|
@ -38,7 +34,7 @@ export default function WalletSettings () {
|
||||||
}
|
}
|
||||||
}, wallet.config)
|
}, wallet.config)
|
||||||
|
|
||||||
const schema = generateSchema(wallet, { me })
|
const schema = generateSchema(wallet)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CenterLayout>
|
<CenterLayout>
|
||||||
|
@ -137,95 +133,3 @@ function WalletFields ({ wallet: { config, fields } }) {
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateSchema (wallet, { me }) {
|
|
||||||
if (wallet.schema) return wallet.schema
|
|
||||||
|
|
||||||
const fieldValidator = (field) => {
|
|
||||||
if (!field.validate) {
|
|
||||||
// default validation
|
|
||||||
let validator = string()
|
|
||||||
if (!field.optional) validator = validator.required('required')
|
|
||||||
return validator
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field.validate.schema) {
|
|
||||||
// complex validation
|
|
||||||
return field.validate.schema
|
|
||||||
}
|
|
||||||
|
|
||||||
const { type: validationType, words, min, max } = field.validate
|
|
||||||
|
|
||||||
let validator
|
|
||||||
|
|
||||||
const stringTypes = ['url', 'string']
|
|
||||||
|
|
||||||
if (stringTypes.includes(validationType)) {
|
|
||||||
validator = string()
|
|
||||||
|
|
||||||
if (field.validate.length) {
|
|
||||||
validator = validator.length(field.validate.length)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (validationType === 'url') {
|
|
||||||
validator = process.env.NODE_ENV === 'development'
|
|
||||||
? validator
|
|
||||||
.or([string().matches(/^(http:\/\/)?localhost:\d+$/), string().url()], 'invalid url')
|
|
||||||
: validator
|
|
||||||
.url()
|
|
||||||
.test(async (url, context) => {
|
|
||||||
if (field.validate.torAllowed && TOR_REGEXP.test(url)) {
|
|
||||||
// allow HTTP and HTTPS over Tor
|
|
||||||
if (!/^https?:\/\//.test(url)) {
|
|
||||||
return context.createError({ message: 'http or https required' })
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// force HTTPS over clearnet
|
|
||||||
await string().https().validate(url)
|
|
||||||
} catch (err) {
|
|
||||||
return context.createError({ message: err.message })
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (words) {
|
|
||||||
validator = array()
|
|
||||||
.transform(function (value, originalValue) {
|
|
||||||
if (this.isType(value) && value !== null) {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return originalValue ? originalValue.trim().split(/[\s]+/) : []
|
|
||||||
})
|
|
||||||
.test(async (values, context) => {
|
|
||||||
for (const v of values) {
|
|
||||||
try {
|
|
||||||
await string().oneOf(words).validate(v)
|
|
||||||
} catch {
|
|
||||||
return context.createError({ message: `'${v}' is not a valid ${field.label} word` })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (min !== undefined) validator = validator.min(min)
|
|
||||||
if (max !== undefined) validator = validator.max(max)
|
|
||||||
|
|
||||||
if (!field.optional) validator = validator.required('required')
|
|
||||||
|
|
||||||
return validator
|
|
||||||
}
|
|
||||||
|
|
||||||
return object(
|
|
||||||
wallet.fields.reduce((acc, field) => {
|
|
||||||
return {
|
|
||||||
...acc,
|
|
||||||
[field.name]: fieldValidator(field)
|
|
||||||
}
|
|
||||||
}, wallet.walletType ? autowithdrawSchemaMembers({ me }) : {})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { LNDAutowithdrawSchema } from '@/lib/validate'
|
import { isInvoicableMacaroon, isInvoiceMacaroon } from '@/lib/macaroon'
|
||||||
|
|
||||||
export const name = 'lnd'
|
export const name = 'lnd'
|
||||||
|
|
||||||
|
@ -9,7 +9,10 @@ export const fields = [
|
||||||
type: 'text',
|
type: 'text',
|
||||||
placeholder: '55.5.555.55:10001',
|
placeholder: '55.5.555.55:10001',
|
||||||
hint: 'tor or clearnet',
|
hint: 'tor or clearnet',
|
||||||
clear: true
|
clear: true,
|
||||||
|
validate: {
|
||||||
|
type: 'socket'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'macaroon',
|
name: 'macaroon',
|
||||||
|
@ -21,7 +24,12 @@ export const fields = [
|
||||||
type: 'text',
|
type: 'text',
|
||||||
placeholder: 'AgEDbG5kAlgDChCn7YgfWX7uTkQQgXZ2uahNEgEwGhYKB2FkZHJlc3MSBHJlYWQSBXdyaXRlGhcKCGludm9pY2VzEgRyZWFkEgV3cml0ZRoPCgdvbmNoYWluEgRyZWFkAAAGIJkMBrrDV0npU90JV0TGNJPrqUD8m2QYoTDjolaL6eBs',
|
placeholder: 'AgEDbG5kAlgDChCn7YgfWX7uTkQQgXZ2uahNEgEwGhYKB2FkZHJlc3MSBHJlYWQSBXdyaXRlGhcKCGludm9pY2VzEgRyZWFkEgV3cml0ZRoPCgdvbmNoYWluEgRyZWFkAAAGIJkMBrrDV0npU90JV0TGNJPrqUD8m2QYoTDjolaL6eBs',
|
||||||
hint: 'hex or base64 encoded',
|
hint: 'hex or base64 encoded',
|
||||||
clear: true
|
clear: true,
|
||||||
|
validate: {
|
||||||
|
type: 'hexOrBase64',
|
||||||
|
test: v => isInvoiceMacaroon(v) || isInvoicableMacaroon(v),
|
||||||
|
message: 'not an invoice macaroon or an invoicable macaroon'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'cert',
|
name: 'cert',
|
||||||
|
@ -30,7 +38,10 @@ export const fields = [
|
||||||
placeholder: 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNNVENDQWRpZ0F3SUJBZ0lRSHBFdFdrcGJwZHV4RVF2eVBPc3NWVEFLQmdncWhrak9QUVFEQWpBdk1SOHcKSFFZRFZRUUtFeFpzYm1RZ1lYVjBiMmRsYm1WeVlYUmxaQ0JqWlhKME1Rd3dDZ1lEVlFRREV3TmliMkl3SGhjTgpNalF3TVRBM01qQXhORE0wV2hjTk1qVXdNekF6TWpBeE5ETTBXakF2TVI4d0hRWURWUVFLRXhac2JtUWdZWFYwCmIyZGxibVZ5WVhSbFpDQmpaWEowTVF3d0NnWURWUVFERXdOaWIySXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak8KUFFNQkJ3TkNBQVJUS3NMVk5oZnhqb1FLVDlkVVdDbzUzSmQwTnBuL1BtYi9LUE02M1JxbU52dFYvdFk4NjJJZwpSbE41cmNHRnBEajhUeFc2OVhIK0pTcHpjWDdlN3N0Um80SFZNSUhTTUE0R0ExVWREd0VCL3dRRUF3SUNwREFUCkJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREFUQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01CMEdBMVVkRGdRV0JCVDAKMnh3V25GeHRUNzI0MWxwZlNoNm9FWi9UMWpCN0JnTlZIUkVFZERCeWdnTmliMktDQ1d4dlkyRnNhRzl6ZElJRApZbTlpZ2d4d2IyeGhjaTF1TVMxaWIyS0NGR2h2YzNRdVpHOWphMlZ5TG1sdWRHVnlibUZzZ2dSMWJtbDRnZ3AxCmJtbDRjR0ZqYTJWMGdnZGlkV1pqYjI1dWh3Ui9BQUFCaHhBQUFBQUFBQUFBQUFBQUFBQUFBQUFCaHdTc0VnQUQKTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUEwUTlkRXdoNXpPRnpwL3hYeHNpemh5SkxNVG5yazU1VWx1NHJPRwo4WW52QWlBVGt4U3p3Y3hZZnFscGx0UlNIbmd0NUJFcDBzcXlHL05nenBzb2pmMGNqQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K',
|
placeholder: 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNNVENDQWRpZ0F3SUJBZ0lRSHBFdFdrcGJwZHV4RVF2eVBPc3NWVEFLQmdncWhrak9QUVFEQWpBdk1SOHcKSFFZRFZRUUtFeFpzYm1RZ1lYVjBiMmRsYm1WeVlYUmxaQ0JqWlhKME1Rd3dDZ1lEVlFRREV3TmliMkl3SGhjTgpNalF3TVRBM01qQXhORE0wV2hjTk1qVXdNekF6TWpBeE5ETTBXakF2TVI4d0hRWURWUVFLRXhac2JtUWdZWFYwCmIyZGxibVZ5WVhSbFpDQmpaWEowTVF3d0NnWURWUVFERXdOaWIySXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak8KUFFNQkJ3TkNBQVJUS3NMVk5oZnhqb1FLVDlkVVdDbzUzSmQwTnBuL1BtYi9LUE02M1JxbU52dFYvdFk4NjJJZwpSbE41cmNHRnBEajhUeFc2OVhIK0pTcHpjWDdlN3N0Um80SFZNSUhTTUE0R0ExVWREd0VCL3dRRUF3SUNwREFUCkJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREFUQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01CMEdBMVVkRGdRV0JCVDAKMnh3V25GeHRUNzI0MWxwZlNoNm9FWi9UMWpCN0JnTlZIUkVFZERCeWdnTmliMktDQ1d4dlkyRnNhRzl6ZElJRApZbTlpZ2d4d2IyeGhjaTF1TVMxaWIyS0NGR2h2YzNRdVpHOWphMlZ5TG1sdWRHVnlibUZzZ2dSMWJtbDRnZ3AxCmJtbDRjR0ZqYTJWMGdnZGlkV1pqYjI1dWh3Ui9BQUFCaHhBQUFBQUFBQUFBQUFBQUFBQUFBQUFCaHdTc0VnQUQKTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUEwUTlkRXdoNXpPRnpwL3hYeHNpemh5SkxNVG5yazU1VWx1NHJPRwo4WW52QWlBVGt4U3p3Y3hZZnFscGx0UlNIbmd0NUJFcDBzcXlHL05nenBzb2pmMGNqQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K',
|
||||||
optional: 'optional if from [CA](https://en.wikipedia.org/wiki/Certificate_authority) (e.g. voltage)',
|
optional: 'optional if from [CA](https://en.wikipedia.org/wiki/Certificate_authority) (e.g. voltage)',
|
||||||
hint: 'hex or base64 encoded',
|
hint: 'hex or base64 encoded',
|
||||||
clear: true
|
clear: true,
|
||||||
|
validate: {
|
||||||
|
type: 'hexOrBase64'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -40,8 +51,6 @@ export const card = {
|
||||||
badges: ['receive only', 'non-custodial']
|
badges: ['receive only', 'non-custodial']
|
||||||
}
|
}
|
||||||
|
|
||||||
export const schema = LNDAutowithdrawSchema
|
|
||||||
|
|
||||||
export const walletType = 'LND'
|
export const walletType = 'LND'
|
||||||
|
|
||||||
export const walletField = 'walletLND'
|
export const walletField = 'walletLND'
|
||||||
|
|
Loading…
Reference in New Issue