From 14b6d7f81877765f3a38586c10c07784622e3ddd Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Wed, 16 Oct 2024 19:46:33 +0200 Subject: [PATCH 1/9] blink receive --- fragments/wallet.js | 8 +++ lib/validate.js | 28 +++++++-- .../20241016171557_blinkreceive/migration.sql | 26 ++++++++ prisma/schema.prisma | 12 ++++ wallets/blink/client.js | 59 +++++------------- wallets/blink/common.js | 62 +++++++++++++++++++ wallets/blink/index.js | 45 +++++++++++--- wallets/blink/server.js | 62 +++++++++++++++++++ wallets/server.js | 3 +- 9 files changed, 245 insertions(+), 60 deletions(-) create mode 100644 prisma/migrations/20241016171557_blinkreceive/migration.sql create mode 100644 wallets/blink/common.js create mode 100644 wallets/blink/server.js diff --git a/fragments/wallet.js b/fragments/wallet.js index 89feb7cd..49e4d3f3 100644 --- a/fragments/wallet.js +++ b/fragments/wallet.js @@ -141,6 +141,10 @@ export const WALLET = gql` url secondaryPassword } + ... on WalletBlink { + apiKeyRecv + currencyRecv + } } } } @@ -181,6 +185,10 @@ export const WALLET_BY_TYPE = gql` url secondaryPassword } + ... on WalletBlink { + apiKeyRecv + currencyRecv + } } } } diff --git a/lib/validate.js b/lib/validate.js index 00215d1a..570ec796 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -748,14 +748,32 @@ export const nwcSchema = object().shape({ }) }, ['nwcUrl', 'nwcUrlRecv']) -export const blinkSchema = object({ +export const blinkSchema = object().shape({ apiKey: string() - .required('required') - .matches(/^blink_[A-Za-z0-9]+$/, { message: 'must match pattern blink_A-Za-z0-9' }), + .matches(/^blink_[A-Za-z0-9]+$/, { message: 'must match pattern blink_A-Za-z0-9' }) + .when(['apiKeyRecv'], ([apiKeyRecv], schema) => { + if (!apiKeyRecv) return schema.required('required if apiKeyRecv not set') + return schema.test({ + test: apiKey => apiKey !== apiKeyRecv, + message: 'apiKey cannot be the same as apiKeyRecv' + }) + }), + apiKeyRecv: string() + .matches(/^blink_[A-Za-z0-9]+$/, { message: 'must match pattern blink_A-Za-z0-9' }) + .when(['apiKey'], ([apiKey], schema) => { + if (!apiKey) return schema.required('required if apiKey not set') + return schema.test({ + test: apiKeyRecv => apiKeyRecv !== apiKey, + message: 'apiKeyRecv cannot be the same as apiKey' + }) + }), currency: string() .transform(value => value ? value.toUpperCase() : 'BTC') - .oneOf(['USD', 'BTC'], 'must be BTC or USD') -}) + .oneOf(['USD', 'BTC'], 'must be BTC or USD'), + currencyRecv: string() + .transform(value => value ? value.toUpperCase() : 'BTC') + .oneOf(['BTC'], 'must be BTC') +}, ['apiKey', 'apiKeyRecv']) export const lncSchema = object({ pairingPhrase: string() diff --git a/prisma/migrations/20241016171557_blinkreceive/migration.sql b/prisma/migrations/20241016171557_blinkreceive/migration.sql new file mode 100644 index 00000000..04f940c6 --- /dev/null +++ b/prisma/migrations/20241016171557_blinkreceive/migration.sql @@ -0,0 +1,26 @@ +-- AlterEnum +ALTER TYPE "WalletType" ADD VALUE 'BLINK'; + +-- CreateTable +CREATE TABLE "WalletBlink" ( + "id" SERIAL NOT NULL, + "walletId" INTEGER NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "apiKeyRecv" TEXT NOT NULL, + "currencyRecv" TEXT, + + CONSTRAINT "WalletBlink_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "WalletBlink_walletId_key" ON "WalletBlink"("walletId"); + +-- AddForeignKey +ALTER TABLE "WalletBlink" ADD CONSTRAINT "WalletBlink_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "Wallet"("id") ON DELETE CASCADE ON UPDATE CASCADE; + + +-- Update wallet json +CREATE TRIGGER wallet_blink_as_jsonb +AFTER INSERT OR UPDATE ON "WalletBlink" +FOR EACH ROW EXECUTE PROCEDURE wallet_wallet_type_as_jsonb(); \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7a629e44..9bc1088e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -178,6 +178,7 @@ enum WalletType { LNBITS NWC PHOENIXD + BLINK } model Wallet { @@ -204,6 +205,7 @@ model Wallet { walletLNbits WalletLNbits? walletNWC WalletNWC? walletPhoenixd WalletPhoenixd? + walletBlink WalletBlink? withdrawals Withdrawl[] InvoiceForward InvoiceForward[] @@ -272,6 +274,16 @@ model WalletNWC { nwcUrlRecv String } +model WalletBlink { + id Int @id @default(autoincrement()) + walletId Int @unique + wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade) + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") + apiKeyRecv String + currencyRecv String? +} + model WalletPhoenixd { id Int @id @default(autoincrement()) walletId Int @unique diff --git a/wallets/blink/client.js b/wallets/blink/client.js index d3aa2751..c24c186f 100644 --- a/wallets/blink/client.js +++ b/wallets/blink/client.js @@ -1,10 +1,23 @@ -import { galoyBlinkUrl } from 'wallets/blink' +import { getScopes, SCOPE_READ, SCOPE_RECEIVE, SCOPE_WRITE, getWallet, request } from 'wallets/blink/common' export * from 'wallets/blink' export async function testSendPayment ({ apiKey, currency }, { logger }) { - currency = currency ? currency.toUpperCase() : 'BTC' logger.info('trying to fetch ' + currency + ' wallet') + const strict = false + const scopes = await getScopes(apiKey) + if (!scopes.includes(SCOPE_READ)) { + throw new Error('missing READ scope') + } + if (!scopes.includes(SCOPE_WRITE)) { + throw new Error('missing WRITE scope') + } + if (strict && scopes.includes(SCOPE_RECEIVE)) { + throw new Error('RECEIVE scope must not be present') + } + + currency = currency ? currency.toUpperCase() : 'BTC' await getWallet(apiKey, currency) + logger.ok(currency + ' wallet found') } @@ -143,45 +156,3 @@ async function getTxInfo (authToken, wallet, invoice) { error: '' } } - -async function getWallet (authToken, currency) { - const out = await request(authToken, ` - query me { - me { - defaultAccount { - wallets { - id - walletCurrency - } - } - } - } - `, {}) - const wallets = out.data.me.defaultAccount.wallets - for (const wallet of wallets) { - if (wallet.walletCurrency === currency) { - return wallet - } - } - throw new Error(`wallet ${currency} not found`) -} - -async function request (authToken, query, variables = {}) { - const options = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-API-KEY': authToken - }, - body: JSON.stringify({ query, variables }) - } - const res = await fetch(galoyBlinkUrl, options) - if (res.status >= 400 && res.status <= 599) { - if (res.status === 401) { - throw new Error('unauthorized') - } else { - throw new Error('API responded with HTTP ' + res.status) - } - } - return res.json() -} diff --git a/wallets/blink/common.js b/wallets/blink/common.js new file mode 100644 index 00000000..acb11aec --- /dev/null +++ b/wallets/blink/common.js @@ -0,0 +1,62 @@ +export const galoyBlinkUrl = 'https://api.blink.sv/graphql' +export const galoyBlinkDashboardUrl = 'https://dashboard.blink.sv/' + +export const SCOPE_READ = 'READ' +export const SCOPE_WRITE = 'WRITE' +export const SCOPE_RECEIVE = 'RECEIVE' + +export async function getWallet (authToken, currency) { + const out = await request(authToken, ` + query me { + me { + defaultAccount { + wallets { + id + walletCurrency + } + } + } + } + `, {}) + const wallets = out.data.me.defaultAccount.wallets + for (const wallet of wallets) { + if (wallet.walletCurrency === currency) { + return wallet + } + } + throw new Error(`wallet ${currency} not found`) +} + +export async function request (authToken, query, variables = {}) { + const options = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-API-KEY': authToken + }, + body: JSON.stringify({ query, variables }) + } + const res = await fetch(galoyBlinkUrl, options) + if (res.status >= 400 && res.status <= 599) { + // consume res + res.text().catch(() => {}) + if (res.status === 401) { + throw new Error('unauthorized') + } else { + throw new Error('API responded with HTTP ' + res.status) + } + } + return res.json() +} + +export async function getScopes (authToken) { + const out = await request(authToken, ` + query scopes { + authorization { + scopes + } + } + `, {}) + const scopes = out?.data?.authorization?.scopes + return scopes || [] +} diff --git a/wallets/blink/index.js b/wallets/blink/index.js index 6cbc3ff8..b8e45e35 100644 --- a/wallets/blink/index.js +++ b/wallets/blink/index.js @@ -1,17 +1,20 @@ import { blinkSchema } from '@/lib/validate' - -export const galoyBlinkUrl = 'https://api.blink.sv/graphql' -export const galoyBlinkDashboardUrl = 'https://dashboard.blink.sv/' +import { galoyBlinkDashboardUrl } from 'wallets/blink/common' export const name = 'blink' +export const walletType = 'BLINK' +export const walletField = 'walletBlink' +export const fieldValidation = blinkSchema export const fields = [ { name: 'apiKey', label: 'api key', type: 'password', - help: `you can get an API key from [Blink Dashboard](${galoyBlinkDashboardUrl})`, - placeholder: 'blink_...' + help: `you can get an API key from [Blink Dashboard](${galoyBlinkDashboardUrl}).\nPlease make sure to select ONLY the 'Read' and 'Write' scopes when generating this API key.`, + placeholder: 'blink_...', + optional: 'for sending', + clientOnly: true }, { name: 'currency', @@ -19,16 +22,38 @@ export const fields = [ type: 'text', help: 'the blink wallet to use (BTC or USD for stablesats)', placeholder: 'BTC', - optional: true, clear: true, - autoComplete: 'off' + autoComplete: 'off', + optional: 'for sending', + clientOnly: true + + }, + { + name: 'apiKeyRecv', + label: 'api key', + type: 'password', + help: `you can get an API key from [Blink Dashboard](${galoyBlinkDashboardUrl}).\nPlease make sure to select ONLY the 'Read' and 'Receive' scopes when generating this API key.`, + placeholder: 'blink_...', + optional: 'for receiving', + serverOnly: true + }, + { + name: 'currencyRecv', + label: 'wallet type', + type: 'text', + help: 'the blink wallet to use (BTC or USD for stablesats)', + value: 'BTC', + clear: true, + autoComplete: 'off', + optional: 'for receiving', + serverOnly: true, + editable: false + } ] export const card = { title: 'Blink', subtitle: 'use [Blink](https://blink.sv/) for payments', - badges: ['send only'] + badges: ['send & receive'] } - -export const fieldValidation = blinkSchema diff --git a/wallets/blink/server.js b/wallets/blink/server.js new file mode 100644 index 00000000..fbbb4401 --- /dev/null +++ b/wallets/blink/server.js @@ -0,0 +1,62 @@ +import { withTimeout } from '@/lib/time' +import { getScopes, SCOPE_READ, SCOPE_RECEIVE, SCOPE_WRITE, getWallet, request } from 'wallets/blink/common' +import { msatsToSats } from '@/lib/format' +export * from 'wallets/blink' + +export async function testCreateInvoice ({ apiKeyRecv, currencyRecv }) { + const strict = true + const scopes = await getScopes(apiKeyRecv) + if (!scopes.includes(SCOPE_READ)) { + throw new Error('missing READ scope') + } + if (strict && scopes.includes(SCOPE_WRITE)) { + throw new Error('WRITE scope must not be present') + } + if (!scopes.includes(SCOPE_RECEIVE)) { + throw new Error('missing RECEIVE scope') + } + + const timeout = 15_000 + currencyRecv = currencyRecv ? currencyRecv.toUpperCase() : 'BTC' + return await withTimeout(createInvoice({ msats: 1000, expiry: 1 }, { apiKeyRecv, currencyRecv }), timeout) +} + +export async function createInvoice ( + { msats, description, expiry }, + { apiKeyRecv, currencyRecv }) { + currencyRecv = currencyRecv ? currencyRecv.toUpperCase() : 'BTC' + + const wallet = await getWallet(apiKeyRecv, currencyRecv) + + if (currencyRecv !== 'BTC') { + throw new Error('unsupported currency ' + currencyRecv) + } + const mutation = ` + mutation LnInvoiceCreate($input: LnInvoiceCreateInput!) { + lnInvoiceCreate(input: $input) { + invoice { + paymentRequest + } + errors { + message + } + } + } + ` + + const out = await request(apiKeyRecv, mutation, { + input: { + amount: msatsToSats(msats), + expiresIn: Math.floor(expiry / 60) || 1, + memo: description, + walletId: wallet.id + } + }) + const res = out.data.lnInvoiceCreate + const errors = res.errors + if (errors && errors.length > 0) { + throw new Error('failed to pay invoice ' + errors.map(e => e.code + ' ' + e.message).join(', ')) + } + const invoice = res.invoice.paymentRequest + return invoice +} diff --git a/wallets/server.js b/wallets/server.js index 8f8e8f35..7cdc1c10 100644 --- a/wallets/server.js +++ b/wallets/server.js @@ -4,13 +4,14 @@ import * as lnAddr from 'wallets/lightning-address/server' import * as lnbits from 'wallets/lnbits/server' import * as nwc from 'wallets/nwc/server' import * as phoenixd from 'wallets/phoenixd/server' +import * as blink from 'wallets/blink/server' import { addWalletLog } from '@/api/resolvers/wallet' import walletDefs from 'wallets/server' import { parsePaymentRequest } from 'ln-service' import { toPositiveNumber } from '@/lib/validate' import { PAID_ACTION_TERMINAL_STATES } from '@/lib/constants' import { withTimeout } from '@/lib/time' -export default [lnd, cln, lnAddr, lnbits, nwc, phoenixd] +export default [lnd, cln, lnAddr, lnbits, nwc, phoenixd, blink] const MAX_PENDING_INVOICES_PER_WALLET = 25 From ce3ee703dfb4918fef8151c318895a921f42831f Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Wed, 16 Oct 2024 19:56:27 +0200 Subject: [PATCH 2/9] improve validation errors --- lib/validate.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/validate.js b/lib/validate.js index 570ec796..a6a48865 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -752,19 +752,19 @@ export const blinkSchema = object().shape({ apiKey: string() .matches(/^blink_[A-Za-z0-9]+$/, { message: 'must match pattern blink_A-Za-z0-9' }) .when(['apiKeyRecv'], ([apiKeyRecv], schema) => { - if (!apiKeyRecv) return schema.required('required if apiKeyRecv not set') + if (!apiKeyRecv) return schema.required('required if api key for receiving not set') return schema.test({ test: apiKey => apiKey !== apiKeyRecv, - message: 'apiKey cannot be the same as apiKeyRecv' + message: 'api key for sending cannot be the same as for receiving' }) }), apiKeyRecv: string() .matches(/^blink_[A-Za-z0-9]+$/, { message: 'must match pattern blink_A-Za-z0-9' }) .when(['apiKey'], ([apiKey], schema) => { - if (!apiKey) return schema.required('required if apiKey not set') + if (!apiKey) return schema.required('required if api key for sending not set') return schema.test({ test: apiKeyRecv => apiKeyRecv !== apiKey, - message: 'apiKeyRecv cannot be the same as apiKey' + message: 'api key for receiving cannot be the same as for sending' }) }), currency: string() From 1ef2af3f5d6cd9bf17221831f3e43189d7d19569 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Fri, 18 Oct 2024 17:18:19 +0200 Subject: [PATCH 3/9] fix error text --- wallets/blink/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallets/blink/server.js b/wallets/blink/server.js index fbbb4401..96c9967f 100644 --- a/wallets/blink/server.js +++ b/wallets/blink/server.js @@ -55,7 +55,7 @@ export async function createInvoice ( const res = out.data.lnInvoiceCreate const errors = res.errors if (errors && errors.length > 0) { - throw new Error('failed to pay invoice ' + errors.map(e => e.code + ' ' + e.message).join(', ')) + throw new Error('failed to create invoice ' + errors.map(e => e.code + ' ' + e.message).join(', ')) } const invoice = res.invoice.paymentRequest return invoice From fea990390a883b5a1fe38096d1172aa78e0fc80b Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Fri, 18 Oct 2024 17:20:24 +0200 Subject: [PATCH 4/9] remove strict toggle --- wallets/blink/client.js | 3 +-- wallets/blink/server.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/wallets/blink/client.js b/wallets/blink/client.js index c24c186f..0f2e6d01 100644 --- a/wallets/blink/client.js +++ b/wallets/blink/client.js @@ -3,7 +3,6 @@ export * from 'wallets/blink' export async function testSendPayment ({ apiKey, currency }, { logger }) { logger.info('trying to fetch ' + currency + ' wallet') - const strict = false const scopes = await getScopes(apiKey) if (!scopes.includes(SCOPE_READ)) { throw new Error('missing READ scope') @@ -11,7 +10,7 @@ export async function testSendPayment ({ apiKey, currency }, { logger }) { if (!scopes.includes(SCOPE_WRITE)) { throw new Error('missing WRITE scope') } - if (strict && scopes.includes(SCOPE_RECEIVE)) { + if (scopes.includes(SCOPE_RECEIVE)) { throw new Error('RECEIVE scope must not be present') } diff --git a/wallets/blink/server.js b/wallets/blink/server.js index 96c9967f..f21f9297 100644 --- a/wallets/blink/server.js +++ b/wallets/blink/server.js @@ -4,12 +4,11 @@ import { msatsToSats } from '@/lib/format' export * from 'wallets/blink' export async function testCreateInvoice ({ apiKeyRecv, currencyRecv }) { - const strict = true const scopes = await getScopes(apiKeyRecv) if (!scopes.includes(SCOPE_READ)) { throw new Error('missing READ scope') } - if (strict && scopes.includes(SCOPE_WRITE)) { + if (scopes.includes(SCOPE_WRITE)) { throw new Error('WRITE scope must not be present') } if (!scopes.includes(SCOPE_RECEIVE)) { From d45cf99dd4f7ba7834f298d96de45b5a819adc9d Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Fri, 18 Oct 2024 19:17:22 +0200 Subject: [PATCH 5/9] Add autowithdrawSchemaMembers --- lib/validate.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/validate.js b/lib/validate.js index a6a48865..60c6dfb0 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -772,7 +772,8 @@ export const blinkSchema = object().shape({ .oneOf(['USD', 'BTC'], 'must be BTC or USD'), currencyRecv: string() .transform(value => value ? value.toUpperCase() : 'BTC') - .oneOf(['BTC'], 'must be BTC') + .oneOf(['BTC'], 'must be BTC'), + ...autowithdrawSchemaMembers }, ['apiKey', 'apiKeyRecv']) export const lncSchema = object({ From 63c2a7f27086899b19e3b38b2ad45a93797bae81 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 22 Oct 2024 10:51:33 +0200 Subject: [PATCH 6/9] mark fields as non editable --- wallets/blink/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/wallets/blink/index.js b/wallets/blink/index.js index b8e45e35..bd76a4ae 100644 --- a/wallets/blink/index.js +++ b/wallets/blink/index.js @@ -14,7 +14,8 @@ export const fields = [ help: `you can get an API key from [Blink Dashboard](${galoyBlinkDashboardUrl}).\nPlease make sure to select ONLY the 'Read' and 'Write' scopes when generating this API key.`, placeholder: 'blink_...', optional: 'for sending', - clientOnly: true + clientOnly: true, + editable: false }, { name: 'currency', @@ -25,8 +26,8 @@ export const fields = [ clear: true, autoComplete: 'off', optional: 'for sending', - clientOnly: true - + clientOnly: true, + editable: false }, { name: 'apiKeyRecv', @@ -35,7 +36,8 @@ export const fields = [ help: `you can get an API key from [Blink Dashboard](${galoyBlinkDashboardUrl}).\nPlease make sure to select ONLY the 'Read' and 'Receive' scopes when generating this API key.`, placeholder: 'blink_...', optional: 'for receiving', - serverOnly: true + serverOnly: true, + editable: false }, { name: 'currencyRecv', From 751869fb634b741193d9bfdce10fe061d9a5d231 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 22 Oct 2024 10:52:58 +0200 Subject: [PATCH 7/9] fix text --- wallets/blink/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wallets/blink/index.js b/wallets/blink/index.js index bd76a4ae..bc4d0f88 100644 --- a/wallets/blink/index.js +++ b/wallets/blink/index.js @@ -21,7 +21,7 @@ export const fields = [ name: 'currency', label: 'wallet type', type: 'text', - help: 'the blink wallet to use (BTC or USD for stablesats)', + help: 'the blink wallet to use for sending (BTC or USD for stablesats)', placeholder: 'BTC', clear: true, autoComplete: 'off', @@ -43,7 +43,7 @@ export const fields = [ name: 'currencyRecv', label: 'wallet type', type: 'text', - help: 'the blink wallet to use (BTC or USD for stablesats)', + help: 'the blink wallet to use for receiving (only BTC available)', value: 'BTC', clear: true, autoComplete: 'off', From bc4b4196f47e7cf4c305b47c00cc4ed49ed81683 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 22 Oct 2024 10:54:05 +0200 Subject: [PATCH 8/9] make sending checks less strict --- wallets/blink/client.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/wallets/blink/client.js b/wallets/blink/client.js index 0f2e6d01..fd438a6b 100644 --- a/wallets/blink/client.js +++ b/wallets/blink/client.js @@ -1,4 +1,4 @@ -import { getScopes, SCOPE_READ, SCOPE_RECEIVE, SCOPE_WRITE, getWallet, request } from 'wallets/blink/common' +import { getScopes, SCOPE_READ, SCOPE_WRITE, getWallet, request } from 'wallets/blink/common' export * from 'wallets/blink' export async function testSendPayment ({ apiKey, currency }, { logger }) { @@ -10,9 +10,6 @@ export async function testSendPayment ({ apiKey, currency }, { logger }) { if (!scopes.includes(SCOPE_WRITE)) { throw new Error('missing WRITE scope') } - if (scopes.includes(SCOPE_RECEIVE)) { - throw new Error('RECEIVE scope must not be present') - } currency = currency ? currency.toUpperCase() : 'BTC' await getWallet(apiKey, currency) From 00ac89287b7b7bf30a2f39fbf123717e6eacbeaf Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Tue, 22 Oct 2024 10:57:14 +0200 Subject: [PATCH 9/9] Fix readonly (https://github.com/stackernews/stacker.news/pull/1479#pullrequestreview-2383552377) --- pages/settings/wallets/[wallet].js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/settings/wallets/[wallet].js b/pages/settings/wallets/[wallet].js index 0343893f..0672a376 100644 --- a/pages/settings/wallets/[wallet].js +++ b/pages/settings/wallets/[wallet].js @@ -110,7 +110,7 @@ function WalletFields ({ wallet: { config, fields, isConfigured } }) { ...props, name, initialValue: config?.[name], - readOnly: isClient && isConfigured && editable === false && !!config?.[name], + readOnly: isClient && editable === false && ((isConfigured && !!config?.[name]) || !!props.value), groupClassName: props.hidden ? 'd-none' : undefined, label: label ? (