gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (5eec408d9 -> 305c513c2)


From: gnunet
Subject: [taler-wallet-core] branch master updated (5eec408d9 -> 305c513c2)
Date: Wed, 22 Nov 2023 19:20:42 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a change to branch master
in repository wallet-core.

    from 5eec408d9 wallet-core: check debit account restrictions for deposit
     new 33c0267b3 settings and preferences, getting conversion info
     new 305c513c2 more cashout ui

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 packages/demobank-ui/dev.mjs                       |   4 +-
 packages/demobank-ui/src/Routing.tsx               |  10 +-
 packages/demobank-ui/src/bank-ui-settings.js       |  19 --
 .../demobank-ui/src/components/Cashouts/index.ts   |  13 +-
 .../demobank-ui/src/components/Cashouts/views.tsx  |  21 ++-
 .../src/components/Transactions/views.tsx          |   6 +-
 packages/demobank-ui/src/components/app.tsx        |  52 +++---
 .../src/context/settings.ts}                       |  36 ++--
 packages/demobank-ui/src/hooks/backend.ts          |   1 -
 packages/demobank-ui/src/hooks/circuit.ts          |  77 ++++----
 .../src/hooks/{settings.ts => preferences.ts}      |  30 ++--
 packages/demobank-ui/src/index.html                |   2 -
 .../demobank-ui/src/pages/AccountPage/views.tsx    |   4 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       |  34 ++--
 packages/demobank-ui/src/pages/LoginForm.tsx       |  13 +-
 .../demobank-ui/src/pages/OperationState/state.ts  |   4 +-
 .../demobank-ui/src/pages/OperationState/views.tsx |   6 +-
 packages/demobank-ui/src/pages/PaymentOptions.tsx  |   4 +-
 .../src/pages/PaytoWireTransferForm.tsx            |   8 +-
 .../demobank-ui/src/pages/RegistrationPage.tsx     |  17 +-
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |   6 +-
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |   8 +-
 .../src/pages/WithdrawalOperationPage.tsx          |   4 +-
 .../src/pages/account/CashoutListForAccount.tsx    |   4 +-
 .../src/pages/account/ShowAccountDetails.tsx       |   5 +-
 .../demobank-ui/src/pages/admin/AccountForm.tsx    |  14 +-
 .../demobank-ui/src/pages/admin/AccountList.tsx    |   2 +-
 packages/demobank-ui/src/pages/admin/AdminHome.tsx |   7 +-
 .../src/pages/business/CreateCashout.tsx           | 198 +++++++++++++++++----
 .../src/pages/business/ShowCashoutDetails.tsx      |  12 +-
 packages/demobank-ui/src/pages/rnd.ts              |   2 -
 packages/demobank-ui/src/settings.json             |  12 ++
 packages/demobank-ui/src/settings.ts               |  33 +++-
 packages/taler-util/src/http-client/bank-core.ts   |   8 +-
 packages/taler-util/src/http-client/types.ts       |  99 ++++-------
 .../src/cta/Withdraw/test.ts                       |   2 +
 packages/web-util/src/components/Header.tsx        |   7 +-
 37 files changed, 458 insertions(+), 326 deletions(-)
 delete mode 100644 packages/demobank-ui/src/bank-ui-settings.js
 copy packages/{taler-wallet-webextension/src/context/backend.ts => 
demobank-ui/src/context/settings.ts} (69%)
 rename packages/demobank-ui/src/hooks/{settings.ts => preferences.ts} (78%)
 create mode 100644 packages/demobank-ui/src/settings.json

diff --git a/packages/demobank-ui/dev.mjs b/packages/demobank-ui/dev.mjs
index 8b04155f4..c5ea318e7 100755
--- a/packages/demobank-ui/dev.mjs
+++ b/packages/demobank-ui/dev.mjs
@@ -18,13 +18,13 @@
 import { serve } from "@gnu-taler/web-util/node";
 import { initializeDev } from "@gnu-taler/web-util/build";
 
-const devEntryPoints = ["src/stories.tsx", "src/index.tsx", 
"src/bank-ui-settings.js"];
+const devEntryPoints = ["src/stories.tsx", "src/index.tsx"];
 
 const build = initializeDev({
   type: "development",
   source: {
     js: devEntryPoints,
-    assets: [{ base: "src", files: ["src/index.html"] }],
+    assets: [{ base: "src", files: ["src/index.html", "src/settings.json"] }],
   },
   destination: "./dist/dev",
   public: "/app",
diff --git a/packages/demobank-ui/src/Routing.tsx 
b/packages/demobank-ui/src/Routing.tsx
index d797a837d..733d55a0f 100644
--- a/packages/demobank-ui/src/Routing.tsx
+++ b/packages/demobank-ui/src/Routing.tsx
@@ -19,6 +19,7 @@ import { createHashHistory } from "history";
 import { Fragment, VNode, h } from "preact";
 import { Route, Router, route } from "preact-router";
 import { useEffect } from "preact/hooks";
+
 import { useBackendState } from "./hooks/backend.js";
 import { BankFrame } from "./pages/BankFrame.js";
 import { WithdrawalOperationPage } from "./pages/WithdrawalOperationPage.js";
@@ -27,7 +28,6 @@ import { PublicHistoriesPage } from 
"./pages/PublicHistoriesPage.js";
 import { RegistrationPage } from "./pages/RegistrationPage.js";
 import { AdminHome } from "./pages/admin/AdminHome.js";
 import { CreateCashout } from "./pages/business/CreateCashout.js";
-import { bankUiSettings } from "./settings.js";
 import { ShowAccountDetails } from "./pages/account/ShowAccountDetails.js";
 import { UpdateAccountPassword } from 
"./pages/account/UpdateAccountPassword.js";
 import { RemoveAccount } from "./pages/admin/RemoveAccount.js";
@@ -36,10 +36,14 @@ import { CashoutListForAccount } from 
"./pages/account/CashoutListForAccount.js"
 import { ShowCashoutDetails } from "./pages/business/ShowCashoutDetails.js";
 import { WireTransfer } from "./pages/WireTransfer.js";
 import { AccountPage } from "./pages/AccountPage/index.js";
+import { useSettingsContext } from "./context/settings.js";
+import { useBankCoreApiContext } from "./context/config.js";
 
 export function Routing(): VNode {
   const history = createHashHistory();
   const backend = useBackendState();
+  const settings = useSettingsContext();
+  const {config} = useBankCoreApiContext();
   const { i18n } = useTranslationContext();
 
   if (backend.state.status === "loggedOut") {
@@ -50,7 +54,7 @@ export function Routing(): VNode {
           component={() => (
             <Fragment>
               <div class="sm:mx-auto sm:w-full sm:max-w-sm">
-                <h2 class="text-center text-2xl font-bold leading-9 
tracking-tight text-gray-900">{i18n.str`Welcome to 
${bankUiSettings.bankName}!`}</h2>
+                <h2 class="text-center text-2xl font-bold leading-9 
tracking-tight text-gray-900">{i18n.str`Welcome to ${settings.bankName}!`}</h2>
               </div>
 
               <LoginForm
@@ -76,7 +80,7 @@ export function Routing(): VNode {
             />
           )}
         />
-        {bankUiSettings.allowRegistrations &&
+        {config.allow_registrations &&
           <Route
             path="/register"
             component={() => (
diff --git a/packages/demobank-ui/src/bank-ui-settings.js 
b/packages/demobank-ui/src/bank-ui-settings.js
deleted file mode 100644
index 397fa28c0..000000000
--- a/packages/demobank-ui/src/bank-ui-settings.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// Values for development environment
-
-/**
- * Global settings for the bank UI.
- */
-globalThis.talerBankSettings = {
-  backendBaseURL: "http://bank.taler.test:1180/";,
-  allowRegistrations: true,
-  showDemoNav: true,
-  simplePasswordForRandomAccounts: true,
-  allowRandomAccountCreation: true,
-  bankName: "Taler DEVELOPMENT Bank",
-  // Names and links for other demo sites to show in the navbar
-  demoSites: [
-    ["Exchange", "https://Exchnage.taler.test/";],
-    ["Bank", "https://bank-ui.taler.test/";],
-    ["Merchant", "https://merchant.taler.test/";],
-  ],
-};
diff --git a/packages/demobank-ui/src/components/Cashouts/index.ts 
b/packages/demobank-ui/src/components/Cashouts/index.ts
index 6cbb1247d..ca58de98f 100644
--- a/packages/demobank-ui/src/components/Cashouts/index.ts
+++ b/packages/demobank-ui/src/components/Cashouts/index.ts
@@ -14,17 +14,18 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpError, utils } from "@gnu-taler/web-util/browser";
+import { ErrorLoading, HttpError, utils } from "@gnu-taler/web-util/browser";
 import { Loading } from "@gnu-taler/web-util/browser";
 // import { compose, StateViewMap } from "../../utils/index.js";
 // import { wxApi } from "../../wxApi.js";
 import { AbsoluteTime, AmountJson, TalerCoreBankErrorsByMethod, 
TalerCorebankApi, TalerError } from "@gnu-taler/taler-util";
 import { useComponentState } from "./state.js";
 import { FailedView, LoadingUriView, ReadyView } from "./views.js";
+import { h } from "preact";
 
 export interface Props {
   account: string;
-  onSelected: (id: string) => void;
+  onSelected: (id: number) => void;
 }
 
 export type State = State.Loading | State.Failed | State.LoadingUriError | 
State.Ready;
@@ -51,8 +52,8 @@ export namespace State {
   export interface Ready extends BaseInfo {
     status: "ready";
     error: undefined;
-    cashouts: (TalerCorebankApi.CashoutStatusResponse & { id: string })[];
-    onSelected: (id: string) => void;
+    cashouts: (TalerCorebankApi.CashoutStatusResponse & { id: number })[];
+    onSelected: (id: number) => void;
   }
 }
 
@@ -66,7 +67,9 @@ export interface Transaction {
 
 const viewMapping: utils.StateViewMap<State> = {
   loading: Loading,
-  "loading-error": LoadingUriView,
+  "loading-error": ({error}) => {
+    return h(ErrorLoading, {error, showDetail:true});
+  },
   "failed": FailedView,
   ready: ReadyView,
 };
diff --git a/packages/demobank-ui/src/components/Cashouts/views.tsx 
b/packages/demobank-ui/src/components/Cashouts/views.tsx
index 76a3a90df..651a7a034 100644
--- a/packages/demobank-ui/src/components/Cashouts/views.tsx
+++ b/packages/demobank-ui/src/components/Cashouts/views.tsx
@@ -14,13 +14,13 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Fragment, h, VNode } from "preact";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { State } from "./index.js";
+import { Amounts, TalerError, assertUnreachable } from "@gnu-taler/taler-util";
+import { Attention, ErrorLoading, Loading, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { format } from "date-fns";
-import { Amounts, assertUnreachable } from "@gnu-taler/taler-util";
+import { Fragment, h, VNode } from "preact";
 import { RenderAmount } from "../../pages/PaytoWireTransferForm.js";
-import { Attention } from "@gnu-taler/web-util/browser";
+import { State } from "./index.js";
+import { useConversionInfo } from "../../hooks/circuit.js";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   const { i18n } = useTranslationContext();
@@ -52,6 +52,13 @@ export function FailedView({ error }: State.Failed) {
 
 export function ReadyView({ cashouts, onSelected }: State.Ready): VNode {
   const { i18n } = useTranslationContext();
+  const resp = useConversionInfo();
+  if (!resp) {
+    return <Loading />
+  }
+  if (resp instanceof TalerError) {
+    return <ErrorLoading error={resp} />
+  }
   if (!cashouts.length) return <div />
   const txByDate = cashouts.reduce((prev, cur) => {
     const d = cur.creation_time.t_s === "never"
@@ -122,8 +129,8 @@ export function ReadyView({ cashouts, onSelected }: 
State.Ready): VNode {
                       </dl> */}
                     </td>
                     <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500">{confirmationTime}</td>
-                    <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-red-600"><RenderAmount value={Amounts.parseOrThrow(item.amount_debit)} 
/></td>
-                    <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-green-600"><RenderAmount value={Amounts.parseOrThrow(item.amount_credit)} 
/></td>
+                    <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-red-600"><RenderAmount value={Amounts.parseOrThrow(item.amount_debit)}  
spec={resp.body.regional_currency_specification} /></td>
+                    <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-green-600"><RenderAmount value={Amounts.parseOrThrow(item.amount_credit)} 
spec={resp.body.fiat_currency_specification} /></td>
 
                     <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500">{item.status}</td>
                     <td class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500 break-all min-w-md">
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx 
b/packages/demobank-ui/src/components/Transactions/views.tsx
index 72dd43415..1613cb06a 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -19,6 +19,7 @@ import { format } from "date-fns";
 import { Fragment, h, VNode } from "preact";
 import { doAutoFocus, RenderAmount } from 
"../../pages/PaytoWireTransferForm.js";
 import { State } from "./index.js";
+import { useBankCoreApiContext } from "../../context/config.js";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   const { i18n } = useTranslationContext();
@@ -32,6 +33,7 @@ export function LoadingUriView({ error }: 
State.LoadingUriError): VNode {
 
 export function ReadyView({ transactions, onNext, onPrev }: State.Ready): 
VNode {
   const { i18n } = useTranslationContext();
+  const {config} = useBankCoreApiContext();
   if (!transactions.length) return <div />
   const txByDate = transactions.reduce((prev, cur) => {
     const d = cur.when.t_ms === "never"
@@ -78,7 +80,7 @@ export function ReadyView({ transactions, onNext, onPrev }: 
State.Ready): VNode
                         <dd class="mt-1 truncate text-gray-700">
                           {item.negative ? i18n.str`sent` : 
i18n.str`received`} {item.amount ? (
                             <span data-negative={item.negative ? "true" : 
"false"} class="data-[negative=false]:text-green-600 
data-[negative=true]:text-red-600">
-                              <RenderAmount value={item.amount} />
+                              <RenderAmount value={item.amount} 
spec={config.currency_specification}/>
                             </span>
                           ) : (
                             <span style={{ color: "grey" 
}}>&lt;{i18n.str`invalid value`}&gt;</span>
@@ -99,7 +101,7 @@ export function ReadyView({ transactions, onNext, onPrev }: 
State.Ready): VNode
                     </td>
                     <td data-negative={item.negative ? "true" : "false"}
                       class="hidden sm:table-cell px-3 py-3.5 text-sm 
text-gray-500 ">
-                      {item.amount ? (<RenderAmount value={item.amount} 
negative={item.negative} withColor />
+                      {item.amount ? (<RenderAmount value={item.amount} 
negative={item.negative} withColor spec={config.currency_specification}/>
                       ) : (
                         <span style={{ color: "grey" }}>&lt;{i18n.str`invalid 
value`}&gt;</span>
                       )}
diff --git a/packages/demobank-ui/src/components/app.tsx 
b/packages/demobank-ui/src/components/app.tsx
index c787fa713..27898caeb 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -19,35 +19,45 @@ import {
   getGlobalLogLevel,
   setGlobalLogLevelFromString
 } from "@gnu-taler/taler-util";
-import { TranslationProvider } from "@gnu-taler/web-util/browser";
+import { Loading, TranslationProvider } from "@gnu-taler/web-util/browser";
 import { Fragment, FunctionalComponent, h } from "preact";
 import { SWRConfig } from "swr";
 import { BackendStateProvider } from "../context/backend.js";
 import { BankCoreApiProvider } from "../context/config.js";
 import { strings } from "../i18n/strings.js";
-import { bankUiSettings } from "../settings.js";
+import { BankUiSettings, fetchSettings } from "../settings.js";
 import { Routing } from "../Routing.js";
 import { BankFrame } from "../pages/BankFrame.js";
+import { useEffect, useState } from "preact/hooks";
+import { SettingsProvider } from "../context/settings.js";
 const WITH_LOCAL_STORAGE_CACHE = false;
 
 const App: FunctionalComponent = () => {
-  const baseUrl = getInitialBackendBaseURL();
+  const [settings, setSettings] = useState<BankUiSettings>()
+  useEffect(() => {
+    fetchSettings(setSettings)
+  }, [])
+  if (!settings) return <Loading />;
+  
+  const baseUrl = getInitialBackendBaseURL(settings.backendBaseURL);
   return (
-    <TranslationProvider source={strings}>
-      <BackendStateProvider>
-        <BankCoreApiProvider baseUrl={baseUrl} frameOnError={BankFrame}>
-          <SWRConfig
-            value={{
-              provider: WITH_LOCAL_STORAGE_CACHE
-                ? localStorageProvider
-                : undefined,
-            }}
-          >
-            <Routing />
-          </SWRConfig>
-        </BankCoreApiProvider>
-      </BackendStateProvider>
-    </TranslationProvider >
+    <SettingsProvider value={settings}>
+      <TranslationProvider source={strings}>
+        <BackendStateProvider>
+          <BankCoreApiProvider baseUrl={baseUrl} frameOnError={BankFrame}>
+            <SWRConfig
+              value={{
+                provider: WITH_LOCAL_STORAGE_CACHE
+                  ? localStorageProvider
+                  : undefined,
+              }}
+            >
+              <Routing />
+            </SWRConfig>
+          </BankCoreApiProvider>
+        </BackendStateProvider>
+      </TranslationProvider >
+    </SettingsProvider>
   );
 };
 
@@ -66,7 +76,7 @@ function localStorageProvider(): Map<unknown, unknown> {
 
 export default App;
 
-function getInitialBackendBaseURL(): string {
+function getInitialBackendBaseURL(backendFromSettings: string | undefined): 
string {
   const overrideUrl =
     typeof localStorage !== "undefined"
       ? localStorage.getItem("bank-base-url")
@@ -75,13 +85,13 @@ function getInitialBackendBaseURL(): string {
 
   if (!overrideUrl) {
     //normal path
-    if (!bankUiSettings.backendBaseURL) {
+    if (!backendFromSettings) {
       console.error(
         "ERROR: backendBaseURL was overridden by a setting file and missing. 
Setting value to 'window.origin'",
       );
       result = window.origin
     } else {
-      result = bankUiSettings.backendBaseURL;
+      result = backendFromSettings;
     }
   } else {
     // testing/development path
diff --git a/packages/taler-wallet-webextension/src/context/backend.ts 
b/packages/demobank-ui/src/context/settings.ts
similarity index 69%
copy from packages/taler-wallet-webextension/src/context/backend.ts
copy to packages/demobank-ui/src/context/settings.ts
index 280fb266d..a14c14d15 100644
--- a/packages/taler-wallet-webextension/src/context/backend.ts
+++ b/packages/demobank-ui/src/context/settings.ts
@@ -14,39 +14,31 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { ComponentChildren, createContext, h, VNode } from "preact";
+import { useContext } from "preact/hooks";
+import { BankUiSettings } from "../settings.js";
+
 /**
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { ComponentChildren, createContext, h, VNode } from "preact";
-import { useContext } from "preact/hooks";
-import { wxApi, WxApiType } from "../wxApi.js";
-
-type Type = WxApiType;
-
-const initial = wxApi;
+export type Type = BankUiSettings;
 
+const initial: BankUiSettings = {};
 const Context = createContext<Type>(initial);
 
-type Props = Partial<Type> & {
-  children: ComponentChildren;
-};
+export const useSettingsContext = (): Type => useContext(Context);
 
-export const BackendProvider = ({
-  wallet,
-  background,
-  listener,
+export const SettingsProvider = ({
   children,
-}: Props): VNode => {
+  value,
+}: {
+  value: BankUiSettings,
+  children: ComponentChildren;
+}): VNode => {
   return h(Context.Provider, {
-    value: {
-      wallet: wallet ?? initial.wallet,
-      background: background ?? initial.background,
-      listener: listener ?? initial.listener,
-    },
+    value,
     children,
   });
 };
-
-export const useBackendContext = (): Type => useContext(Context);
diff --git a/packages/demobank-ui/src/hooks/backend.ts 
b/packages/demobank-ui/src/hooks/backend.ts
index 93d647e73..863b47bf3 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -29,7 +29,6 @@ import {
   useLocalStorage
 } from "@gnu-taler/web-util/browser";
 import { useSWRConfig } from "swr";
-import { bankUiSettings } from "../settings.js";
 
 /**
  * Has the information to reach and
diff --git a/packages/demobank-ui/src/hooks/circuit.ts 
b/packages/demobank-ui/src/hooks/circuit.ts
index d0d180a53..01c62c409 100644
--- a/packages/demobank-ui/src/hooks/circuit.ts
+++ b/packages/demobank-ui/src/hooks/circuit.ts
@@ -34,9 +34,7 @@ export type TransferCalculation = {
 };
 type EstimatorFunction = (
   amount: AmountJson,
-  currency: string,
-  sellFee: AmountJson,
-  sellRate: number,
+  fee: AmountJson,
 ) => Promise<TransferCalculation>;
 
 type CashoutEstimators = {
@@ -73,45 +71,40 @@ export function useEstimator(): CashoutEstimators {
   const { state } = useBackendState();
   const { api } = useBankCoreApiContext();
   return {
-    estimateByCredit: async (fiatAmount, regionalCurrency, fee, rate) => {
-      // const resp = await api.getConversionInfoAPI().getCashoutRate({
-      //   credit: amount
-      // });
-      // if (resp.type === "fail") {
-      //   // can't happen
-      //   // not-supported: it should not be able to call this function
-      //   // wrong-calculation: we are using just one parameter
-      //   throw TalerError.fromDetail(resp.detail.code, {}, resp.detail.hint)
-      // }
-      const credit = fiatAmount;
+    estimateByCredit: async (fiatAmount, fee) => {
+      const resp = await api.getConversionInfoAPI().getCashoutRate({
+        credit: fiatAmount
+      });
+      if (resp.type === "fail") {
+        // can't happen
+        // not-supported: it should not be able to call this function
+        // wrong-calculation: we are using just one parameter
+        throw TalerError.fromDetail(resp.detail.code, {}, resp.detail.hint)
+      }
+      const credit = Amounts.parseOrThrow(resp.body.amount_credit);
+      const debit = Amounts.parseOrThrow(resp.body.amount_debit);
       const beforeFee = Amounts.sub(credit, fee).amount;
 
-      // const debit = Amounts.parseOrThrow(resp.body.amount_debit);
-      //FIXME: remove this when endpoint works
-      const debit = Amounts.add(
-        Amounts.zeroOfCurrency(regionalCurrency),
-        beforeFee
-      ).amount;
-
       return {
         debit,
         beforeFee,
         credit,
       };
     },
-    estimateByDebit: async (regionalAmount, fiatCurrency, fee, rate) => {
-      // const resp = await api.getConversionInfoAPI().getCashoutRate({ debit: 
amount });
-      // if (resp.type === "fail") {
-      //   // can't happen
-      //   // not-supported: it should not be able to call this function
-      //   // wrong-calculation: we are using just one parameter
-      //   throw TalerError.fromDetail(resp.detail.code, {}, resp.detail.hint)
-      // }
-      // const credit = Amounts.parseOrThrow(resp.body.amount_credit);
-      const debit = regionalAmount;
-      const _credit = Amounts.parseOrThrow(regionalAmount);
-      const beforeFee = { ..._credit, currency: fiatCurrency };
-      const credit = Amounts.sub(beforeFee, fee).amount;
+    estimateByDebit: async (regionalAmount, fee) => {
+      const resp = await api.getConversionInfoAPI().getCashoutRate({
+        debit: regionalAmount
+      });
+      if (resp.type === "fail") {
+        // can't happen
+        // not-supported: it should not be able to call this function
+        // wrong-calculation: we are using just one parameter
+        throw TalerError.fromDetail(resp.detail.code, {}, resp.detail.hint)
+      }
+      const credit = Amounts.parseOrThrow(resp.body.amount_credit);
+      const debit = Amounts.parseOrThrow(resp.body.amount_debit);
+      const beforeFee = Amounts.add(credit, fee).amount;
+
       return {
         debit,
         beforeFee,
@@ -177,7 +170,7 @@ export function useBusinessAccounts() {
   return undefined;
 }
 
-type CashoutWithId = TalerCorebankApi.CashoutStatusResponse & { id: string }
+type CashoutWithId = TalerCorebankApi.CashoutStatusResponse & { id: number }
 function notUndefined(c: CashoutWithId | undefined): c is CashoutWithId {
   return c !== undefined
 }
@@ -189,11 +182,15 @@ export function useCashouts(account: string) {
   async function fetcher([username, token]: [string, AccessToken]) {
     const list = await api.getAccountCashouts({ username, token })
     if (list.type !== "ok") {
+      console.error(list)
       return list;
     }
     const all: Array<CashoutWithId | undefined> = await 
Promise.all(list.body.cashouts.map(c => {
       return api.getCashoutById({ username, token }, c.cashout_id).then(r => {
-        if (r.type === "fail") return undefined
+        if (r.type === "fail") {
+          console.error("failed", r)
+          return undefined
+        }
         return { ...r.body, id: c.cashout_id }
       })
     }))
@@ -203,7 +200,7 @@ export function useCashouts(account: string) {
   }
 
   const { data, error } = useSWR<OperationOk<{ cashouts: CashoutWithId[] }> | 
TalerCoreBankErrorsByMethod<"getAccountCashouts">, TalerHttpError>(
-    !config.allow_conversion ? false : [account, token, "getAccountCashouts"], 
fetcher, {
+    !config.allow_conversion ? undefined : [account, token, 
"getAccountCashouts"], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -220,17 +217,17 @@ export function useCashouts(account: string) {
   return undefined;
 }
 
-export function useCashoutDetails(cashoutId: string) {
+export function useCashoutDetails(cashoutId: number | undefined) {
   const { state: credentials } = useBackendState();
   const creds = credentials.status !== "loggedIn" ? undefined : credentials
   const { api } = useBankCoreApiContext();
 
-  async function fetcher([username, token, id]: [string, AccessToken, string]) 
{
+  async function fetcher([username, token, id]: [string, AccessToken, number]) 
{
     return api.getCashoutById({ username, token }, id)
   }
 
   const { data, error } = 
useSWR<TalerCoreBankResultByMethod<"getCashoutById">, TalerHttpError>(
-    [creds?.username, creds?.token, cashoutId, "getCashoutById"], fetcher, {
+    cashoutId === undefined ? undefined : [creds?.username, creds?.token, 
cashoutId, "getCashoutById"], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
diff --git a/packages/demobank-ui/src/hooks/settings.ts 
b/packages/demobank-ui/src/hooks/preferences.ts
similarity index 78%
rename from packages/demobank-ui/src/hooks/settings.ts
rename to packages/demobank-ui/src/hooks/preferences.ts
index bd48ca680..a1525ac80 100644
--- a/packages/demobank-ui/src/hooks/settings.ts
+++ b/packages/demobank-ui/src/hooks/preferences.ts
@@ -25,7 +25,7 @@ import {
 } from "@gnu-taler/taler-util";
 import { buildStorageKey, useLocalStorage, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 
-interface Settings {
+interface Preferences {
   currentWithdrawalOperationId: string | undefined;
   showWithdrawalSuccess: boolean;
   showDemoDescription: boolean;
@@ -36,11 +36,11 @@ interface Settings {
 
 }
 
-export function getAllBooleanSettings(): Array<keyof Settings> {
+export function getAllBooleanPreferences(): Array<keyof Preferences> {
   return ["fastWithdrawal", "showDebugInfo", "showDemoDescription", 
"showInstallWallet", "showWithdrawalSuccess"]
 }
 
-export function getLabelForSetting(k: keyof Settings, i18n: ReturnType<typeof 
useTranslationContext>["i18n"]): TranslatedString {
+export function getLabelForPreferences(k: keyof Preferences, i18n: 
ReturnType<typeof useTranslationContext>["i18n"]): TranslatedString {
   switch (k) {
     case "currentWithdrawalOperationId": return i18n.str`Current withdrawal 
operation`
     case "maxWithdrawalAmount": return i18n.str`Max withdrawal amount`
@@ -52,8 +52,8 @@ export function getLabelForSetting(k: keyof Settings, i18n: 
ReturnType<typeof us
   }
 }
 
-export const codecForSettings = (): Codec<Settings> =>
-  buildCodecForObject<Settings>()
+export const codecForPreferences = (): Codec<Preferences> =>
+  buildCodecForObject<Preferences>()
     .property("currentWithdrawalOperationId", codecOptional(codecForString()))
     .property("showWithdrawalSuccess", (codecForBoolean()))
     .property("showDemoDescription", (codecForBoolean()))
@@ -63,7 +63,7 @@ export const codecForSettings = (): Codec<Settings> =>
     .property("maxWithdrawalAmount", codecForNumber())
     .build("Settings");
 
-const defaultSettings: Settings = {
+const defaultPreferences: Preferences = {
   currentWithdrawalOperationId: undefined,
   showWithdrawalSuccess: true,
   showDemoDescription: true,
@@ -73,21 +73,21 @@ const defaultSettings: Settings = {
   showDebugInfo: false,
 };
 
-const BANK_SETTINGS_KEY = buildStorageKey(
-  "bank-settings",
-  codecForSettings(),
+const BANK_PREFERENCES_KEY = buildStorageKey(
+  "bank-preferences",
+  codecForPreferences(),
 );
 
-export function useSettings(): [
-  Readonly<Settings>,
-  <T extends keyof Settings>(key: T, value: Settings[T]) => void,
+export function usePreferences(): [
+  Readonly<Preferences>,
+  <T extends keyof Preferences>(key: T, value: Preferences[T]) => void,
 ] {
   const { value, update } = useLocalStorage(
-    BANK_SETTINGS_KEY,
-    defaultSettings,
+    BANK_PREFERENCES_KEY,
+    defaultPreferences,
   );
 
-  function updateField<T extends keyof Settings>(k: T, v: Settings[T]) {
+  function updateField<T extends keyof Preferences>(k: T, v: Preferences[T]) {
     const newValue = { ...value, [k]: v };
     update(newValue);
   }
diff --git a/packages/demobank-ui/src/index.html 
b/packages/demobank-ui/src/index.html
index f702f30ea..3cc7f7fd2 100644
--- a/packages/demobank-ui/src/index.html
+++ b/packages/demobank-ui/src/index.html
@@ -29,8 +29,6 @@
     
href="data:;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///////////////////////////////////////////////////////////////////////////////////////////////////7//v38//78/P/+/fz//vz7///+/v/+/f3//vz7///+/v/+/fz//v38///////////////////////+/v3///7+/////////////////////////////////////////////////////////v3//v79///////+/v3///////r28v/ct5//06SG/9Gffv/Xqo7/7N/V/9e2nf/bsJb/6uDW/9Sskf/euKH/+/j2///////+/v3//////+3azv+/eE3/2rWd/9Kkhv/Vr5T/48i2/8J+VP/Qn
 [...]
   <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon" />
   <title>Bank</title>
-  <!-- Optional customization script.  -->
-  <script src="bank-ui-settings.js"></script>
   <!-- Entry point for the bank SPA. -->
   <script type="module" src="index.js"></script>
   <link rel="stylesheet" href="index.css" />
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx 
b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index 0f5236192..cfee684fa 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -18,7 +18,7 @@ import { useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { Attention } from "@gnu-taler/web-util/browser";
 import { Transactions } from "../../components/Transactions/index.js";
-import { useSettings } from "../../hooks/settings.js";
+import { usePreferences } from "../../hooks/preferences.js";
 import { PaymentOptions } from "../PaymentOptions.js";
 import { State } from "./index.js";
 
@@ -32,7 +32,7 @@ const IS_PUBLIC_ACCOUNT_ENABLED = false
 
 function ShowDemoInfo(): VNode {
   const { i18n } = useTranslationContext();
-  const [settings, updateSettings] = useSettings();
+  const [settings, updateSettings] = usePreferences();
   if (!settings.showDemoDescription) return <Fragment />
   return <Attention title={i18n.str`This is a demo bank`} onClose={() => {
     updateSettings("showDemoDescription", false);
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx 
b/packages/demobank-ui/src/pages/BankFrame.tsx
index f0baae3a3..34c39e9d3 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -20,9 +20,10 @@ import { ComponentChildren, Fragment, VNode, h } from 
"preact";
 import { useEffect, useErrorBoundary } from "preact/hooks";
 import { useAccountDetails } from "../hooks/access.js";
 import { useBackendState } from "../hooks/backend.js";
-import { getAllBooleanSettings, getLabelForSetting, useSettings } from 
"../hooks/settings.js";
-import { bankUiSettings } from "../settings.js";
+import { getAllBooleanPreferences, getLabelForPreferences, usePreferences } 
from "../hooks/preferences.js";
 import { RenderAmount } from "./PaytoWireTransferForm.js";
+import { useSettingsContext } from "../context/settings.js";
+import { useBankCoreApiContext } from "../context/config.js";
 
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : 
undefined;
 const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
@@ -37,15 +38,16 @@ export function BankFrame({
 }): VNode {
   const { i18n } = useTranslationContext();
   const backend = useBackendState();
-  const [settings, updateSettings] = useSettings();
+  const settings = useSettingsContext();
+  const [preferences, updatePreferences] = usePreferences();
 
   const [error, resetError] = useErrorBoundary();
 
   useEffect(() => {
     if (error) {
       const desc = (error instanceof Error ? error.stack : String(error)) as 
TranslatedString
+      console.log(error)
       if (error instanceof Error) {
-        console.log(error)
         notifyException(i18n.str`Internal error, please report.`, error)
       } else {
         notifyError(i18n.str`Internal error, please report.`, String(error) as 
TranslatedString)
@@ -59,12 +61,12 @@ export function BankFrame({
     <div class="bg-indigo-600 pb-32">
       <Header
         title="Bank"
-        iconLinkURL={bankUiSettings.iconLinkURL ?? "#"}
+        iconLinkURL={settings.iconLinkURL ?? "#"}
         onLogout={backend.state.status !== "loggedIn" ? undefined : () => {
           backend.logOut()
-          updateSettings("currentWithdrawalOperationId", undefined);
+          updatePreferences("currentWithdrawalOperationId", undefined);
         }}
-        sites={bankUiSettings.demoSites ?? []}
+        sites={settings.demoSites ?? new Array<Array<string>>(new 
Array<string>())}
         supportedLangs={["en", "es", "de"]}
       >
         <li>
@@ -72,18 +74,18 @@ export function BankFrame({
             <i18n.Translate>Preferences</i18n.Translate>
           </div>
           <ul role="list" class="space-y-1">
-            {getAllBooleanSettings().map(set => {
-              const isOn: boolean = !!settings[set]
+            {getAllBooleanPreferences().map(set => {
+              const isOn: boolean = !!preferences[set]
               return <li class="mt-2 pl-2">
                 <div class="flex items-center justify-between">
                   <span class="flex flex-grow flex-col">
                     <span class="text-sm text-black font-medium leading-6 " 
id="availability-label">
-                      {getLabelForSetting(set, i18n)}
+                      {getLabelForPreferences(set, i18n)}
                     </span>
                   </span>
                   <button type="button" data-enabled={isOn} 
class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 
w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent 
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 
focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" 
aria-labelledby="availability-label" aria-describedby="availability-description"
 
-                    onClick={() => { updateSettings(set, !isOn); }}>
+                    onClick={() => { updatePreferences(set, !isOn); }}>
                     <span aria-hidden="true" data-enabled={isOn} 
class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none 
inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition 
duration-200 ease-in-out"></span>
                   </button>
                 </div>
@@ -117,9 +119,9 @@ export function BankFrame({
 
     <Footer
       testingUrl={
-        (typeof localStorage !== "undefined") && 
localStorage.getItem("bank-base-url") ? 
-        localStorage.getItem("bank-base-url") ?? undefined : 
-        undefined}
+        (typeof localStorage !== "undefined") && 
localStorage.getItem("bank-base-url") ?
+          localStorage.getItem("bank-base-url") ?? undefined :
+          undefined}
       GIT_HASH={GIT_HASH}
       VERSION={VERSION}
     />
@@ -130,7 +132,7 @@ export function BankFrame({
 }
 
 function MaybeShowDebugInfo({ info }: { info: any }): VNode {
-  const [settings] = useSettings()
+  const [settings] = usePreferences()
   if (settings.showDebugInfo) {
     return <pre class="whitespace-break-spaces ">
       {info}
@@ -171,6 +173,7 @@ function WelcomeAccount({ account: accountName }: { 
account: string }): VNode {
 
 function AccountBalance({ account }: { account: string }): VNode {
   const result = useAccountDetails(account);
+  const { config } = useBankCoreApiContext();
   if (!result) {
     return <Loading />
   }
@@ -182,5 +185,6 @@ function AccountBalance({ account }: { account: string }): 
VNode {
   return <RenderAmount
     value={Amounts.parseOrThrow(result.body.balance.amount)}
     negative={result.body.balance.credit_debit_indicator === "debit"}
+    spec={config.currency_specification}
   />
 }
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx 
b/packages/demobank-ui/src/pages/LoginForm.tsx
index 018416390..02ec75dbf 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -15,18 +15,14 @@
  */
 
 import { TranslatedString } from "@gnu-taler/taler-util";
-import { Notification, useLocalNotification, useTranslationContext } from 
"@gnu-taler/web-util/browser";
+import { LocalNotificationBanner, ShowInputErrorLabel, useLocalNotification, 
useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
-import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
-import { bankUiSettings } from "../settings.js";
-import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
-import { assertUnreachable } from "./WithdrawalOperationPage.js";
+import { undefinedIfEmpty } from "../utils.js";
 import { doAutoFocus } from "./PaytoWireTransferForm.js";
-import { Attention } from "@gnu-taler/web-util/browser";
-import { LocalNotificationBanner } from "@gnu-taler/web-util/browser";
+import { assertUnreachable } from "./WithdrawalOperationPage.js";
 
 
 /**
@@ -40,6 +36,7 @@ export function LoginForm({ reason, onRegister }: { reason?: 
"not-found" | "forb
   const { i18n } = useTranslationContext();
   const { api } = useBankCoreApiContext();
   const [notification, notify, handleError] = useLocalNotification()
+  const {config} = useBankCoreApiContext();
 
   /** 
    * Register form may be shown in the initialization step.
@@ -208,7 +205,7 @@ export function LoginForm({ reason, onRegister }: { 
reason?: "not-found" | "forb
           </div>}
         </form>
 
-        {bankUiSettings.allowRegistrations && onRegister &&
+        {config.allow_registrations && onRegister &&
           <p class="mt-10 text-center text-sm text-gray-500 border-t">
             <button type="submit"
               class="flex mt-4 rounded-md bg-blue-600 px-3 py-1.5 text-sm 
font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 
focus-visible:outline-blue-600"
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts 
b/packages/demobank-ui/src/pages/OperationState/state.ts
index 2d33ff78b..30f7419f0 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -21,12 +21,12 @@ import { mutate } from "swr";
 import { useBankCoreApiContext } from "../../context/config.js";
 import { useWithdrawalDetails } from "../../hooks/access.js";
 import { useBackendState } from "../../hooks/backend.js";
-import { useSettings } from "../../hooks/settings.js";
+import { usePreferences } from "../../hooks/preferences.js";
 import { assertUnreachable } from "../WithdrawalOperationPage.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState({ currency, onClose }: Props): 
utils.RecursiveState<State> {
-  const [settings, updateSettings] = useSettings()
+  const [settings, updateSettings] = usePreferences()
   const { state: credentials } = useBackendState()
   const creds = credentials.status !== "loggedIn" ? undefined : credentials
   const { api } = useBankCoreApiContext()
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx 
b/packages/demobank-ui/src/pages/OperationState/views.tsx
index e7db566ea..a06147039 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -20,7 +20,7 @@ import { Fragment, VNode, h } from "preact";
 import { useEffect, useMemo, useState } from "preact/hooks";
 import { QR } from "../../components/QR.js";
 import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
-import { useSettings } from "../../hooks/settings.js";
+import { usePreferences } from "../../hooks/preferences.js";
 import { undefinedIfEmpty } from "../../utils.js";
 import { State } from "./index.js";
 import { LocalNotificationBanner } from "@gnu-taler/web-util/browser";
@@ -46,7 +46,7 @@ export function InvalidReserveView({ reserve, onClose }: 
State.InvalidReserve) {
 
 export function NeedConfirmationView({ error, onAbort: doAbort, onConfirm: 
doConfirm, busy }: State.NeedConfirmation) {
   const { i18n } = useTranslationContext()
-  const [settings] = useSettings()
+  const [settings] = usePreferences()
   const [notification, notify, errorHandler] = useLocalNotification()
 
   const captchaNumbers = useMemo(() => {
@@ -309,7 +309,7 @@ export function AbortedView({ error, onClose }: 
State.Aborted) {
 
 export function ConfirmedView({ error, onClose }: State.Confirmed) {
   const { i18n } = useTranslationContext();
-  const [settings, updateSettings] = useSettings()
+  const [settings, updateSettings] = usePreferences()
   return (
     <Fragment>
 
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx 
b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 2e756b86d..76d20867e 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -20,7 +20,7 @@ import { h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { PaytoWireTransferForm, doAutoFocus } from 
"./PaytoWireTransferForm.js";
 import { WalletWithdrawForm } from "./WalletWithdrawForm.js";
-import { useSettings } from "../hooks/settings.js";
+import { usePreferences } from "../hooks/preferences.js";
 
 /**
  * Let the user choose a payment option,
@@ -28,7 +28,7 @@ import { useSettings } from "../hooks/settings.js";
  */
 export function PaymentOptions({ limit, goToConfirmOperation }: { limit: 
AmountJson, goToConfirmOperation: (id: string) => void }): VNode {
   const { i18n } = useTranslationContext();
-  const [settings] = useSettings();
+  const [settings] = usePreferences();
 
   const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | 
undefined>();
 
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx 
b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index a6282c947..e035c7fed 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -18,6 +18,7 @@ import {
   AmountJson,
   AmountString,
   Amounts,
+  CurrencySpecification,
   Logger,
   PaytoString,
   TranslatedString,
@@ -470,13 +471,12 @@ export function InputAmount(
   );
 }
 
-export function RenderAmount({ value, negative, withColor }: { value: 
AmountJson, negative?: boolean, withColor?: boolean }): VNode {
-  const { config } = useBankCoreApiContext()
+export function RenderAmount({ value, spec, negative, withColor }: { spec: 
CurrencySpecification; value: AmountJson, negative?: boolean, withColor?: 
boolean }): VNode {
   const neg = !!negative //convert to true or false
   const str = Amounts.stringifyValue(value)
   const sep_pos = str.indexOf(FRAC_SEPARATOR)
-  if (sep_pos !== -1 && str.length - sep_pos - 1 > 
config.currency_specification.num_fractional_normal_digits) {
-    const limit = sep_pos + 
config.currency_specification.num_fractional_normal_digits + 1
+  if (sep_pos !== -1 && str.length - sep_pos - 1 > 
spec.num_fractional_normal_digits) {
+    const limit = sep_pos + spec.num_fractional_normal_digits + 1
     const normal = str.substring(0, limit)
     const small = str.substring(limit)
     return <span data-negative={withColor ? neg : undefined} 
class="whitespace-nowrap data-[negative=false]:text-green-600 
data-[negative=true]:text-red-600">
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx 
b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index fdaa28bbb..9c3b21097 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -15,18 +15,18 @@
  */
 import { AccessToken, Logger, TranslatedString } from "@gnu-taler/taler-util";
 import {
+  LocalNotificationBanner,
+  ShowInputErrorLabel,
   useLocalNotification,
   useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
-import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
-import { bankUiSettings } from "../settings.js";
-import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
+import { undefinedIfEmpty } from "../utils.js";
 import { getRandomPassword, getRandomUsername } from "./rnd.js";
-import { LocalNotificationBanner } from "@gnu-taler/web-util/browser";
+import { useSettingsContext } from "../context/settings.js";
 
 const logger = new Logger("RegistrationPage");
 
@@ -38,7 +38,8 @@ export function RegistrationPage({
   onCancel: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
-  if (!bankUiSettings.allowRegistrations) {
+  const {config} = useBankCoreApiContext();
+  if (!config.allow_registrations) {
     return (
       <p>{i18n.str`Currently, the bank is not accepting new 
registrations!`}</p>
     );
@@ -62,6 +63,7 @@ function RegistrationForm({ onComplete, onCancel }: { 
onComplete: () => void, on
   const [email, setEmail] = useState<string | undefined>();
   const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
   const [notification, notify, handleError] = useLocalNotification()
+  const settings = useSettingsContext();
 
   const { api } = useBankCoreApiContext()
   // const { register } = useTestingAPI();
@@ -177,7 +179,8 @@ function RegistrationForm({ onComplete, onCancel }: { 
onComplete: () => void, on
 
   async function doRandomRegistration(tries: number = 3) {
     const user = getRandomUsername();
-    const pass = getRandomPassword();
+     
+    const pass = settings.simplePasswordForRandomAccounts ? "123" : 
getRandomPassword();
     const username = `_${user.first}-${user.second}_`
     await doRegistrationAndLogin(name, username, pass)
     onComplete();
@@ -389,7 +392,7 @@ function RegistrationForm({ onComplete, onCancel }: { 
onComplete: () => void, on
 
           </form>
 
-          {bankUiSettings.allowRandomAccountCreation &&
+          {settings.allowRandomAccountCreation &&
             <p class="mt-10 text-center text-sm text-gray-500 border-t">
               <button type="submit"
                 class="flex mt-4 w-full justify-center rounded-md bg-green-600 
px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm 
hover:bg-green-500 focus-visible:outline focus-visible:outline-2 
focus-visible:outline-offset-2 focus-visible:outline-green-600"
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx 
b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index a9a661c25..5eef95f1e 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -32,7 +32,7 @@ import { useState } from "preact/hooks";
 import { Attention } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
 import { useBackendState } from "../hooks/backend.js";
-import { useSettings } from "../hooks/settings.js";
+import { usePreferences } from "../hooks/preferences.js";
 import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
 import { OperationState } from "./OperationState/index.js";
 import { InputAmount, doAutoFocus } from "./PaytoWireTransferForm.js";
@@ -50,7 +50,7 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, 
onCancel, focus }: {
   onCancel: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
-  const [settings, updateSettings] = useSettings()
+  const [settings, updateSettings] = usePreferences()
 
   const { state: credentials } = useBackendState();
   const creds = credentials.status !== "loggedIn" ? undefined : credentials
@@ -240,7 +240,7 @@ export function WalletWithdrawForm({
   onCancel: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
-  const [settings, updateSettings] = useSettings()
+  const [settings, updateSettings] = usePreferences()
 
   return (<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 
bg-gray-100 my-4 px-4 pb-4 rounded-lg">
     <div class="px-4 sm:px-0">
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx 
b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index 7fec76d2f..bfb118c6c 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -33,7 +33,7 @@ import { useMemo, useState } from "preact/hooks";
 import { mutate } from "swr";
 import { ShowInputErrorLabel } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
-import { useSettings } from "../hooks/settings.js";
+import { usePreferences } from "../hooks/preferences.js";
 import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js";
 import { RenderAmount } from "./PaytoWireTransferForm.js";
 import { assertUnreachable } from "./WithdrawalOperationPage.js";
@@ -60,7 +60,7 @@ export function WithdrawalConfirmationQuestion({
   withdrawUri,
 }: Props): VNode {
   const { i18n } = useTranslationContext();
-  const [settings, updateSettings] = useSettings()
+  const [settings, updateSettings] = usePreferences()
 
   const captchaNumbers = useMemo(() => {
     return {
@@ -70,7 +70,7 @@ export function WithdrawalConfirmationQuestion({
   }, []);
   const [notification, notify, handleError] = useLocalNotification()
 
-  const { api } = useBankCoreApiContext()
+  const { config, api } = useBankCoreApiContext()
   const [captchaAnswer, setCaptchaAnswer] = useState<string | undefined>();
   const answer = parseInt(captchaAnswer ?? "", 10);
   const [busy, setBusy] = useState<Record<string, undefined>>()
@@ -289,7 +289,7 @@ export function WithdrawalConfirmationQuestion({
                   <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 
sm:px-0">
                     <dt class="text-sm font-medium leading-6 
text-gray-900">Amount</dt>
                     <dd class="mt-1 text-sm leading-6 text-gray-700 
sm:col-span-2 sm:mt-0">
-                      <RenderAmount value={details.amount} />
+                      <RenderAmount value={details.amount} 
spec={config.currency_specification} />
                     </dd>
                   </div>
                 </dl>
diff --git a/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx 
b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
index 5ed57a0f7..7060b7a98 100644
--- a/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
@@ -25,7 +25,7 @@ import {
 import { Fragment, VNode, h } from "preact";
 import { Attention } from "@gnu-taler/web-util/browser";
 import { useBankCoreApiContext } from "../context/config.js";
-import { useSettings } from "../hooks/settings.js";
+import { usePreferences } from "../hooks/preferences.js";
 import { WithdrawalQRCode } from "./WithdrawalQRCode.js";
 
 const logger = new Logger("AccountPage");
@@ -44,7 +44,7 @@ export function WithdrawalOperationPage({
   });
   const parsedUri = parseWithdrawUri(uri);
   const { i18n } = useTranslationContext();
-  const [settings, updateSettings] = useSettings();
+  const [settings, updateSettings] = usePreferences();
 
   if (!parsedUri) {
     return <Attention type="danger" title={i18n.str`The Withdrawal URI is not 
valid`}>
diff --git a/packages/demobank-ui/src/pages/account/CashoutListForAccount.tsx 
b/packages/demobank-ui/src/pages/account/CashoutListForAccount.tsx
index 293b821e2..f2972ed65 100644
--- a/packages/demobank-ui/src/pages/account/CashoutListForAccount.tsx
+++ b/packages/demobank-ui/src/pages/account/CashoutListForAccount.tsx
@@ -9,7 +9,7 @@ import { CreateCashout } from "../business/CreateCashout.js";
 interface Props {
   account: string,
   onClose: () => void,
-  onSelected: (cid: string) => void
+  onSelected: (cid: number) => void
 }
 
 export function CashoutListForAccount({ account, onSelected, onClose }: 
Props): VNode {
@@ -29,7 +29,7 @@ export function CashoutListForAccount({ account, onSelected, 
onClose }: Props):
       </h1>
     }
 
-    <CreateCashout onCancel={() => { }} onComplete={() => { }} 
account={account} />
+    <CreateCashout focus onCancel={onClose} onComplete={() => { }} 
account={account} />
 
     <Cashouts
       account={account}
diff --git a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx 
b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
index 7a4fbddf5..ab5ceb8d5 100644
--- a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
+++ b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
@@ -52,7 +52,10 @@ export function ShowAccountDetails({
   async function doUpdate() {
     if (!update || !submitAccount || !creds) return;
     await handleError(async () => {
-      const resp = await api.updateAccount(creds, {
+      const resp = await api.updateAccount({
+        token: creds.token,
+        username: account,
+      }, {
         cashout_address: submitAccount.cashout_payto_uri,
         challenge_contact_data: undefinedIfEmpty({
           email: submitAccount.contact_data?.email,
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx 
b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
index 5d8e3797a..b38d40012 100644
--- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -61,12 +61,12 @@ export function AccountForm({
               : validateIBAN(parsed.iban, i18n)) as PaytoString,
       contact_data: undefinedIfEmpty({
         email: !newForm.contact_data?.email
-          ? i18n.str`required`
+          ? undefined
           : !EMAIL_REGEX.test(newForm.contact_data.email)
             ? i18n.str`it should be an email`
             : undefined,
         phone: !newForm.contact_data?.phone
-          ? i18n.str`required`
+          ? undefined
           : !newForm.contact_data.phone.startsWith("+")
             ? i18n.str`should start with +`
             : !REGEX_JUST_NUMBERS_REGEX.test(newForm.contact_data.phone)
@@ -152,7 +152,7 @@ export function AccountForm({
                 name="name"
                 data-error={!!errors?.name && form.name !== undefined}
                 id="name"
-                disabled={purpose !== "create"}
+                disabled={purpose === "show"}
                 value={form.name ?? ""}
                 onChange={(e) => {
                   form.name = e.currentTarget.value;
@@ -189,7 +189,7 @@ export function AccountForm({
                 name="email"
                 id="email"
                 data-error={!!errors?.contact_data?.email && 
form.contact_data?.email !== undefined}
-                disabled={purpose !== "create"}
+                disabled={purpose === "show"}
                 value={form.contact_data?.email ?? ""}
                 onChange={(e) => {
                   if (form.contact_data) {
@@ -273,7 +273,11 @@ export function AccountForm({
               <i18n.Translate>account number where the money is going to be 
sent when doing cashouts</i18n.Translate>
             </p>
           </div>
-
+          <div class="sm:col-span-5">
+            <pre>
+              {JSON.stringify(errors, undefined, 2)}
+            </pre>
+          </div>
         </div>
       </div>
       {children}
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx 
b/packages/demobank-ui/src/pages/admin/AccountList.tsx
index 8c018120d..7d6cfaf7d 100644
--- a/packages/demobank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx
@@ -105,7 +105,7 @@ export function AccountList({ onRemoveAccount, 
onShowAccountDetails, onUpdateAcc
                           i18n.str`unknown`
                         ) : (
                           <span class="amount">
-                            <RenderAmount value={balance} 
negative={balanceIsDebit} />
+                            <RenderAmount value={balance} 
negative={balanceIsDebit} spec={config.currency_specification} />
                           </span>
                         )}
                       </td>
diff --git a/packages/demobank-ui/src/pages/admin/AdminHome.tsx 
b/packages/demobank-ui/src/pages/admin/AdminHome.tsx
index 795a2c6d0..e9fa1dc47 100644
--- a/packages/demobank-ui/src/pages/admin/AdminHome.tsx
+++ b/packages/demobank-ui/src/pages/admin/AdminHome.tsx
@@ -7,6 +7,7 @@ import { useLastMonitorInfo } from "../../hooks/circuit.js";
 import { RenderAmount } from "../PaytoWireTransferForm.js";
 import { WireTransfer } from "../WireTransfer.js";
 import { AccountList } from "./AccountList.js";
+import { useBankCoreApiContext } from "../../context/config.js";
 
 /**
  * Query account information and show QR code if there is pending withdrawal
@@ -42,7 +43,6 @@ function Metrics(): VNode {
   const [metricType, setMetricType] = 
useState<TalerCorebankApi.MonitorTimeframeParam>(TalerCorebankApi.MonitorTimeframeParam.day);
 
   const resp = useLastMonitorInfo(new Date(), metricType);
-  console.log(resp)
   if (!resp) return <Fragment />;
   if (resp instanceof TalerError) {
     return <ErrorLoading error={resp} />
@@ -162,6 +162,7 @@ function Metrics(): VNode {
 
 function MetricValue({ current, previous }: { current: AmountString | 
undefined, previous: AmountString | undefined }): VNode {
   const { i18n } = useTranslationContext()
+  const {config} = useBankCoreApiContext();
   const cmp = current && previous ? Amounts.cmp(current, previous) : 0;
   const currAmount = !current ? undefined : 
Number.parseFloat(Amounts.stringifyValue(current))
   const prevAmount = !previous ? undefined : 
Number.parseFloat(Amounts.stringifyValue(previous))
@@ -173,11 +174,11 @@ function MetricValue({ current, previous }: { current: 
AmountString | undefined,
   const rateStr = `${(Math.abs(rate) * 100).toFixed(2)}%`
   return <dd class="mt-1 flex justify-between md:block lg:flex">
     <div class="flex justify-start  items-baseline text-2xl font-semibold 
text-indigo-600">
-      {!current ? "-" : <RenderAmount value={Amounts.parseOrThrow(current)} />}
+      {!current ? "-" : <RenderAmount value={Amounts.parseOrThrow(current)} 
spec={config.currency_specification} />}
     </div>
     <div class="flex justify-end items-baseline text-2xl font-semibold 
text-indigo-600">
       <small class="ml-2 text-sm font-medium text-gray-500">
-        <i18n.Translate>from</i18n.Translate> {!previous ? "-" : <RenderAmount 
value={Amounts.parseOrThrow(previous)} />}
+        <i18n.Translate>from</i18n.Translate> {!previous ? "-" : <RenderAmount 
value={Amounts.parseOrThrow(previous)}  spec={config.currency_specification}/>}
       </small>
     </div>
 
diff --git a/packages/demobank-ui/src/pages/business/CreateCashout.tsx 
b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
index 1838dbda3..2f77f3960 100644
--- a/packages/demobank-ui/src/pages/business/CreateCashout.tsx
+++ b/packages/demobank-ui/src/pages/business/CreateCashout.tsx
@@ -26,6 +26,7 @@ import {
   Loading,
   LocalNotificationBanner,
   ShowInputErrorLabel,
+  notifyInfo,
   useLocalNotification,
   useTranslationContext
 } from "@gnu-taler/web-util/browser";
@@ -46,12 +47,13 @@ import {
 import { LoginForm } from "../LoginForm.js";
 import { InputAmount, RenderAmount, doAutoFocus } from 
"../PaytoWireTransferForm.js";
 import { assertUnreachable } from "../WithdrawalOperationPage.js";
+import { getRandomPassword, getRandomUsername } from "../rnd.js";
 
 interface Props {
   account: string;
   focus?: boolean,
   onComplete: (id: string) => void;
-  onCancel: () => void;
+  onCancel?: () => void;
 }
 
 type FormType = {
@@ -77,6 +79,9 @@ export function CreateCashout({
     estimateByCredit: calculateFromCredit,
     estimateByDebit: calculateFromDebit,
   } = useEstimator();
+  const { state: credentials } = useBackendState();
+  const creds = credentials.status !== "loggedIn" ? undefined : credentials
+
   const { api, config } = useBankCoreApiContext()
   const [form, setForm] = useState<Partial<FormType>>({ isDebit: true, amount: 
"2" });
   const [notification, notify, handleError] = useLocalNotification()
@@ -108,35 +113,28 @@ export function CreateCashout({
     return <ErrorLoading error={info} />
   }
 
+  const conversionInfo = info.body.conversion_info
+  if (!conversionInfo) {
+    return <div>conversion enabled but server replied without 
conversion_info</div>
+  }
+
   const account = {
     balance: Amounts.parseOrThrow(resultAccount.body.balance.amount),
     balanceIsDebit: resultAccount.body.balance.credit_debit_indicator == 
"debit",
     debitThreshold: Amounts.parseOrThrow(resultAccount.body.debit_threshold)
   }
 
-  const { fiat_currency, regional_currency, cashout_ratio, cashout_fee } = 
info.body
+  const { fiat_currency, regional_currency, fiat_currency_specification, 
regional_currency_specification } = info.body
   const regionalZero = Amounts.zeroOfCurrency(regional_currency);
   const fiatZero = Amounts.zeroOfCurrency(fiat_currency);
   const limit = account.balanceIsDebit
     ? Amounts.sub(account.debitThreshold, account.balance).amount
     : Amounts.add(account.balance, account.debitThreshold).amount;
 
-  const zeroCalc = { debit: regionalZero, credit: fiatZero, beforeFee: 
regionalZero };
+  const zeroCalc = { debit: regionalZero, credit: fiatZero, beforeFee: 
fiatZero };
   const [calc, setCalc] = useState(zeroCalc);
-
-  const sellRate = Number.parseFloat(cashout_ratio);
-  const sellFee = !cashout_fee
-    ? fiatZero
-    : Amounts.parseOrThrow(cashout_fee);
-
-  if (sellRate === undefined || sellRate < 0) return <div>error rate d
-    <pre>
-      {JSON.stringify(info.body, undefined, 2)}
-    </pre>
-  </div>;
-
-  const safeSellRate = sellRate
-
+  const sellFee = Amounts.parseOrThrow(conversionInfo.cashout_fee);
+  const sellRate = conversionInfo.cashout_ratio
   /**
    * can be in regional currency or fiat currency
    * depending on the isDebit flag
@@ -150,8 +148,8 @@ export function CreateCashout({
       await handleError(async () => {
         if (Amounts.isNonZero(inputAmount)) {
           const resp = await (form.isDebit ?
-            calculateFromDebit(inputAmount, fiat_currency, sellFee, 
safeSellRate) :
-            calculateFromCredit(inputAmount, regional_currency, sellFee, 
safeSellRate));
+            calculateFromDebit(inputAmount, sellFee) :
+            calculateFromCredit(inputAmount, sellFee));
           setCalc(resp)
         }
       })
@@ -165,6 +163,7 @@ export function CreateCashout({
     setForm(newForm);
   }
   const errors = undefinedIfEmpty<ErrorFrom<typeof form>>({
+    subject: !form.subject ? i18n.str`required` : undefined,
     amount: !form.amount
       ? i18n.str`required`
       : !inputAmount
@@ -180,6 +179,70 @@ export function CreateCashout({
   });
   const trimmedAmountStr = form.amount?.trim();
 
+  async function createCashout() {
+    const request_uid = encodeCrock(getRandomBytes(32))
+    await handleError(async () => {
+      if (!creds || !form.subject || !form.channel) return;
+
+      const resp = await api.createCashout(creds, {
+        request_uid,
+        amount_credit: Amounts.stringify(calc.credit),
+        amount_debit: Amounts.stringify(calc.debit),
+        subject: form.subject,
+        tan_channel: form.channel,
+      })
+      if (resp.type === "ok") {
+        notifyInfo(i18n.str`Cashout created`)
+      } else {
+        switch (resp.case) {
+          case "account-not-found": return notify({
+            type: "error",
+            title: i18n.str`Account not found`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          });
+          case "request-already-used": return notify({
+            type: "error",
+            title: i18n.str`Duplicated request detected, check if the 
operation succeded or try again.`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          });
+          case "incorrect-exchange-rate": return notify({
+            type: "error",
+            title: i18n.str`The exchange rate was incorrectly applied`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          });
+          case "no-contact-info": return notify({
+            type: "error",
+            title: i18n.str`Missing contact info before to create the cashout`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          });
+          case "no-enough-balance": return notify({
+            type: "error",
+            title: i18n.str`The account does not have sufficient funds`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          });
+          case "cashout-not-supported": return notify({
+            type: "error",
+            title: i18n.str`Cashouts are not supported`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          });
+          case "tan-failed": return notify({
+            type: "error",
+            title: i18n.str`Sending the confirmation code failed.`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          });
+        }
+        assertUnreachable(resp)
+      }
+    })
+  }
+
   return (
     <div>
       <LocalNotificationBanner notification={notification} />
@@ -198,18 +261,18 @@ export function CreateCashout({
 
             <div class="flex items-center justify-between border-t-2 afu pt-4">
               <dt class="flex items-center text-sm text-gray-600">
-                <span><i18n.Translate>Current balance</i18n.Translate></span>
+                <span><i18n.Translate>Balance</i18n.Translate></span>
               </dt>
               <dd class="text-sm text-gray-900">
-                <RenderAmount value={account.balance} />
+                <RenderAmount value={account.balance} 
spec={regional_currency_specification} />
               </dd>
             </div>
             <div class="flex items-center justify-between border-t-2 afu pt-4">
               <dt class="flex items-center text-sm text-gray-600">
-                <span><i18n.Translate>Cashout fee</i18n.Translate></span>
+                <span><i18n.Translate>Fee</i18n.Translate></span>
               </dt>
               <dd class="text-sm text-gray-900">
-                <RenderAmount value={sellFee} />
+                <RenderAmount value={sellFee} 
spec={fiat_currency_specification} />
               </dd>
             </div>
           </dl>
@@ -232,7 +295,7 @@ export function CreateCashout({
                   class="block text-sm font-medium leading-6 text-gray-900"
                   for="subject"
                 >
-                  {i18n.str`Subject`}
+                  {i18n.str`Transfer subject`}
                 </label>
                 <div class="mt-2">
                   <input
@@ -259,14 +322,24 @@ export function CreateCashout({
 
               {/* amount */}
               <div class="sm:col-span-5">
-                <label
-                  class="block text-sm font-medium leading-6 text-gray-900"
-                  for="amount"
-                >
-                  {form.isDebit
-                    ? i18n.str`Amount to send`
-                    : i18n.str`Amount to receive`}
-                </label>
+                <div class="flex justify-between">
+                  <label
+                    class="block text-sm font-medium leading-6 text-gray-900"
+                    for="amount"
+                  >
+                    {form.isDebit
+                      ? i18n.str`Amount to send`
+                      : i18n.str`Amount to receive`}
+                  </label>
+                  <button type="button" data-enabled={form.isDebit} 
class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 
w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent 
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 
focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" 
aria-labelledby="availability-label" aria-describedby="availability-description"
+                    onClick={() => {
+                      form.isDebit = !form.isDebit
+                      updateForm(structuredClone(form))
+                    }}>
+                    <span aria-hidden="true" data-enabled={form.isDebit} 
class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none 
inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition 
duration-200 ease-in-out"></span>
+                  </button>
+
+                </div>
                 <div class="mt-2">
                   <InputAmount
                     name="amount"
@@ -293,7 +366,7 @@ export function CreateCashout({
                     <div class="justify-between items-center flex ">
                       <dt class="text-sm text-gray-600"><i18n.Translate>Total 
cost</i18n.Translate></dt>
                       <dd class="text-sm text-gray-900">
-                        <RenderAmount value={calc.debit} negative withColor />
+                        <RenderAmount value={calc.debit} negative withColor 
spec={regional_currency_specification} />
                       </dd>
                     </div>
 
@@ -308,7 +381,7 @@ export function CreateCashout({
                 </a> */}
                       </dt>
                       <dd class="text-sm text-gray-900">
-                        <RenderAmount value={balanceAfter} />
+                        <RenderAmount value={balanceAfter} 
spec={regional_currency_specification} />
                       </dd>
                     </div>
                     {Amounts.isZero(sellFee) || Amounts.isZero(calc.beforeFee) 
? undefined : (
@@ -322,14 +395,14 @@ export function CreateCashout({
                 </a> */}
                         </dt>
                         <dd class="text-sm text-gray-900">
-                          <RenderAmount value={calc.beforeFee} />
+                          <RenderAmount value={calc.beforeFee} 
spec={fiat_currency_specification} />
                         </dd>
                       </div>
                     )}
                     <div class="flex justify-between items-center border-t-2 
afu pt-4">
                       <dt class="text-lg text-gray-900 
font-medium"><i18n.Translate>Total cashout transfer</i18n.Translate></dt>
                       <dd class="text-lg text-gray-900 font-medium">
-                        <RenderAmount value={calc.credit} withColor />
+                        <RenderAmount value={calc.credit} withColor 
spec={fiat_currency_specification} />
                       </dd>
                     </div>
                   </dl>
@@ -337,6 +410,55 @@ export function CreateCashout({
               )}
 
               {/* channel */}
+              <div class="sm:col-span-5">
+              <label
+                  class="block text-sm font-medium leading-6 text-gray-900"
+                  for="channel"
+                >
+                  {i18n.str`Confirmation the operation using`}
+                </label>
+
+                <div class="mt-2 max-w-xl text-sm text-gray-500">
+                  <div class="px-4 mt-4 grid grid-cols-1 gap-y-6">
+
+                    <label onClick={()=>{
+                      form.channel = TanChannel.EMAIL
+                      updateForm(structuredClone(form))
+                    }} data-selected={form.channel === TanChannel.EMAIL} 
class="relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm 
focus:outline-none border-gray-300 data-[selected=true]:ring-2 
data-[selected=true]:ring-indigo-600">
+                      <input type="radio" name="channel" value="Newsletter" 
class="sr-only" />
+                      <span class="flex flex-1">
+                        <span class="flex flex-col">
+                          <span id="project-type-0-label" class="block text-sm 
font-medium text-gray-900 ">
+                            <i18n.Translate>Email</i18n.Translate>
+                          </span>
+                        </span>
+                      </span>
+                      <svg data-selected={form.channel === TanChannel.EMAIL} 
class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden" viewBox="0 0 20 
20" fill="currentColor" aria-hidden="true">
+                        <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 
000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 
10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+                      </svg>
+                    </label>
+
+                    <label onClick={()=>{
+                      form.channel = TanChannel.SMS
+                      updateForm(structuredClone(form))
+                    }} data-selected={form.channel === TanChannel.SMS} 
class="relative flex cursor-pointer rounded-lg border bg-white  p-4 shadow-sm 
focus:outline-none border-gray-300 data-[selected=true]:ring-2 
data-[selected=true]:ring-indigo-600">
+                      <input type="radio" name="channel" value="Existing 
Customers" class="sr-only"  />
+                      <span class="flex flex-1">
+                        <span class="flex flex-col">
+                          <span id="project-type-1-label" class="block text-sm 
font-medium text-gray-900">
+                            <i18n.Translate>SMS</i18n.Translate>
+                          </span>
+                        </span>
+                      </span>
+                      <svg data-selected={form.channel === TanChannel.SMS} 
class="h-5 w-5 text-indigo-600 data-[selected=false]:hidden" viewBox="0 0 20 
20" fill="currentColor" aria-hidden="true">
+                        <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 
000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 
10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd" />
+                      </svg>
+                    </label>
+
+                  </div>
+                </div>
+
+              </div>
             </div>
           </div>
 
@@ -354,10 +476,10 @@ export function CreateCashout({
               disabled={!!errors}
               onClick={(e) => {
                 e.preventDefault()
-                // doChangePassword()
+                createCashout()
               }}
             >
-              <i18n.Translate>Change</i18n.Translate>
+              <i18n.Translate>Cashout</i18n.Translate>
             </button>
           </div>
         </form>
diff --git a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx 
b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
index ddfc18a0c..52ff713e2 100644
--- a/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
+++ b/packages/demobank-ui/src/pages/business/ShowCashoutDetails.tsx
@@ -53,7 +53,9 @@ export function ShowCashoutDetails({
   const { state } = useBackendState();
   const creds = state.status !== "loggedIn" ? undefined : state
   const { api } = useBankCoreApiContext()
-  const result = useCashoutDetails(id);
+  const cid = Number.parseInt(id, 10)
+
+  const result = useCashoutDetails(Number.isNaN(cid) ? undefined : cid);
   const [code, setCode] = useState<string | undefined>(undefined);
   const [notification, notify, handleError] = useLocalNotification()
 
@@ -72,6 +74,10 @@ export function ShowCashoutDetails({
       default: assertUnreachable(result)
     }
   }
+  if (Number.isNaN(cid)) {
+    //TODO: better error message
+    return <div>cashout id should be a number</div>
+  }
   const errors = undefinedIfEmpty({
     code: !code ? i18n.str`required` : undefined,
   });
@@ -165,7 +171,7 @@ export function ShowCashoutDetails({
                 e.preventDefault();
                 if (!creds) return;
                 await handleError(async () => {
-                  const resp = await api.abortCashoutById(creds, id);
+                  const resp = await api.abortCashoutById(creds, cid);
                   if (resp.type === "ok") {
                     onCancel();
                   } else {
@@ -207,7 +213,7 @@ export function ShowCashoutDetails({
                 e.preventDefault();
                 if (!creds || !code) return;
                 await handleError(async () => {
-                  const resp = await api.confirmCashoutById(creds, id, {
+                  const resp = await api.confirmCashoutById(creds, cid, {
                     tan: code,
                   });
                   if (resp.type === "ok") {
diff --git a/packages/demobank-ui/src/pages/rnd.ts 
b/packages/demobank-ui/src/pages/rnd.ts
index 32c3a934f..46111425e 100644
--- a/packages/demobank-ui/src/pages/rnd.ts
+++ b/packages/demobank-ui/src/pages/rnd.ts
@@ -1,5 +1,4 @@
 import { createEddsaKeyPair, encodeCrock, getRandomBytes } from 
"@gnu-taler/taler-util"
-import { bankUiSettings } from "../settings.js"
 
 
 const noun = [
@@ -2890,6 +2889,5 @@ export function getRandomUsername(): { first: string, 
second: string } {
 }
 
 export function getRandomPassword(): string {
-  if (bankUiSettings.simplePasswordForRandomAccounts) return "123"
   return encodeCrock(getRandomBytes(16))
 }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/settings.json 
b/packages/demobank-ui/src/settings.json
new file mode 100644
index 000000000..8d5b149b5
--- /dev/null
+++ b/packages/demobank-ui/src/settings.json
@@ -0,0 +1,12 @@
+{
+  "backendBaseURL": "http://bank.taler.test:1180/";,
+  "showDemoNav": true,
+  "simplePasswordForRandomAccounts": true,
+  "allowRandomAccountCreation": true,
+  "bankName": "Taler DEVELOPMENT Bank",
+  "demoSites": [
+    ["Exchange", "https://Exchnage.taler.test/";],
+    ["Bank", "https://bank-ui.taler.test/";],
+    ["Merchant", "https://merchant.taler.test/";]
+  ]
+}
diff --git a/packages/demobank-ui/src/settings.ts 
b/packages/demobank-ui/src/settings.ts
index f17d1d511..a9c63857b 100644
--- a/packages/demobank-ui/src/settings.ts
+++ b/packages/demobank-ui/src/settings.ts
@@ -14,15 +14,16 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { Codec, buildCodecForObject, codecForBoolean, codecForList, 
codecForString, codecOptional } from "@gnu-taler/taler-util";
+
 export interface BankUiSettings {
   backendBaseURL?: string;
-  allowRegistrations?: boolean;
   iconLinkURL?: string;
   showDemoNav?: boolean;
   simplePasswordForRandomAccounts?: boolean;
   allowRandomAccountCreation?: boolean;
   bankName?: string;
-  demoSites?: [string, string][];
+  demoSites?: Array<Array<string>>;
 }
 
 /**
@@ -31,7 +32,6 @@ export interface BankUiSettings {
 const defaultSettings: BankUiSettings = {
   backendBaseURL: "https://bank.demo.taler.net/demobanks/default/";,
   iconLinkURL: "https://demo.taler.net/";,
-  allowRegistrations: true,
   bankName: "Taler Bank",
   showDemoNav: true,
   simplePasswordForRandomAccounts: true,
@@ -45,7 +45,26 @@ const defaultSettings: BankUiSettings = {
   ],
 };
 
-export const bankUiSettings: BankUiSettings =
-  "talerBankSettings" in globalThis
-    ? (globalThis as any).talerBankSettings
-    : defaultSettings;
+const codecForBankUISettings = (): Codec<BankUiSettings> =>
+  buildCodecForObject<BankUiSettings>()
+    .property("allowRandomAccountCreation", codecOptional(codecForBoolean()))
+    .property("backendBaseURL", codecOptional(codecForString()))
+    .property("bankName", codecOptional(codecForString()))
+    .property("demoSites", 
codecOptional(codecForList(codecForList(codecForString()))))
+    .property("iconLinkURL", codecOptional(codecForString()))
+    .property("showDemoNav", codecOptional(codecForBoolean()))
+    .property("simplePasswordForRandomAccounts", 
codecOptional(codecForBoolean()))
+    .build("BankUiSettings");
+
+export function fetchSettings(listener: (s: BankUiSettings) => void): void {
+  fetch("./settings.json")
+    .then(resp => resp.json())
+    .then(json => codecForBankUISettings().decode(json))
+    .then(listener)
+    .catch(e => {
+      console.log("failed to fetch settings", e)
+      listener(defaultSettings)
+    })
+}
+
+
diff --git a/packages/taler-util/src/http-client/bank-core.ts 
b/packages/taler-util/src/http-client/bank-core.ts
index d7bf6be29..273fb97c6 100644
--- a/packages/taler-util/src/http-client/bank-core.ts
+++ b/packages/taler-util/src/http-client/bank-core.ts
@@ -442,7 +442,7 @@ export class TalerCoreBankHttpClient {
       body,
     });
     switch (resp.status) {
-      case HttpStatusCode.Accepted: return opSuccess(resp, 
codecForCashoutPending())
+      case HttpStatusCode.Ok: return opSuccess(resp, codecForCashoutPending())
       case HttpStatusCode.NotFound: return opKnownFailure("account-not-found", 
resp)
       case HttpStatusCode.Conflict: {
         const body = await resp.json()
@@ -465,7 +465,7 @@ export class TalerCoreBankHttpClient {
    * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-cashouts-$CASHOUT_ID-abort
    * 
    */
-  async abortCashoutById(auth: UserAndToken, cid: string) {
+  async abortCashoutById(auth: UserAndToken, cid: number) {
     const url = new URL(`accounts/${auth.username}/cashouts/${cid}/abort`, 
this.baseUrl);
     const resp = await this.httpLib.fetch(url.href, {
       method: "POST",
@@ -487,7 +487,7 @@ export class TalerCoreBankHttpClient {
    * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-cashouts-$CASHOUT_ID-confirm
    * 
    */
-  async confirmCashoutById(auth: UserAndToken, cid: string, body: 
TalerCorebankApi.CashoutConfirmRequest) {
+  async confirmCashoutById(auth: UserAndToken, cid: number, body: 
TalerCorebankApi.CashoutConfirmRequest) {
     const url = new URL(`accounts/${auth.username}/cashouts/${cid}/confirm`, 
this.baseUrl);
     const resp = await this.httpLib.fetch(url.href, {
       method: "POST",
@@ -522,7 +522,7 @@ export class TalerCoreBankHttpClient {
    * 
https://docs.taler.net/core/api-corebank.html#get--accounts-$USERNAME-cashouts-$CASHOUT_ID
    * 
    */
-  async getCashoutById(auth: UserAndToken, cid: string) {
+  async getCashoutById(auth: UserAndToken, cid: number) {
     const url = new URL(`accounts/${auth.username}/cashouts/${cid}`, 
this.baseUrl);
     const resp = await this.httpLib.fetch(url.href, {
       method: "GET",
diff --git a/packages/taler-util/src/http-client/types.ts 
b/packages/taler-util/src/http-client/types.ts
index 3ef0ff76c..4c8a146a6 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -404,7 +404,7 @@ export const codecForBankAccountGetWithdrawalResponse =
 export const codecForCashoutPending =
   (): Codec<TalerCorebankApi.CashoutPending> =>
     buildCodecForObject<TalerCorebankApi.CashoutPending>()
-      .property("cashout_id", codecForString())
+      .property("cashout_id", codecForNumber())
       .build("TalerCorebankApi.CashoutPending");
 
 export const codecForCashoutConversionResponse =
@@ -428,7 +428,7 @@ export const codecForCashouts = (): 
Codec<TalerCorebankApi.Cashouts> =>
 
 export const codecForCashoutInfo = (): Codec<TalerCorebankApi.CashoutInfo> =>
   buildCodecForObject<TalerCorebankApi.CashoutInfo>()
-    .property("cashout_id", codecForString())
+    .property("cashout_id", codecForNumber())
     .property(
       "status",
       codecForEither(
@@ -448,7 +448,7 @@ export const codecForGlobalCashouts =
 export const codecForGlobalCashoutInfo =
   (): Codec<TalerCorebankApi.GlobalCashoutInfo> =>
     buildCodecForObject<TalerCorebankApi.GlobalCashoutInfo>()
-      .property("cashout_id", codecForString())
+      .property("cashout_id", codecForNumber())
       .property("username", codecForString())
       .property(
         "status",
@@ -465,7 +465,7 @@ export const codecForCashoutStatusResponse =
     buildCodecForObject<TalerCorebankApi.CashoutStatusResponse>()
       .property("amount_credit", codecForAmountString())
       .property("amount_debit", codecForAmountString())
-      .property("confirmation_time", codecForTimestamp)
+      .property("confirmation_time", codecOptional(codecForTimestamp))
       .property("creation_time", codecForTimestamp)
       // .property("credit_payto_uri", codecForPaytoString())
       .property(
@@ -754,29 +754,21 @@ export const codecForConversionInfo =
     buildCodecForObject<TalerBankConversionApi.ConversionInfo>()
       .property("cashin_fee", codecForAmountString())
       .property("cashin_min_amount", codecForAmountString())
-      .property("cashin_ratio", codecForString())
-      // .property("cashin_ratio", codecForDecimalNumber())
-      .property(
-        "cashin_rounding_mode",
-        codecForEither(
-          codecForConstString("zero"),
-          codecForConstString("up"),
-          codecForConstString("nearest"),
-        ),
-      )
+      .property("cashin_ratio", codecForDecimalNumber())
+      .property("cashin_rounding_mode", codecForEither(
+        codecForConstString("zero"),
+        codecForConstString("up"),
+        codecForConstString("nearest")
+      ))
       .property("cashin_tiny_amount", codecForAmountString())
       .property("cashout_fee", codecForAmountString())
       .property("cashout_min_amount", codecForAmountString())
-      .property("cashout_ratio", codecForString())
-      // .property("cashout_ratio", codecForDecimalNumber())
-      .property(
-        "cashout_rounding_mode",
-        codecForEither(
-          codecForConstString("zero"),
-          codecForConstString("up"),
-          codecForConstString("nearest"),
-        ),
-      )
+      .property("cashout_ratio", codecForDecimalNumber())
+      .property("cashout_rounding_mode", codecForEither(
+        codecForConstString("zero"),
+        codecForConstString("up"),
+        codecForConstString("nearest")
+      ))
       .property("cashout_tiny_amount", codecForAmountString())
       .build("ConversionBankConfig.ConversionInfo");
 
@@ -792,34 +784,10 @@ export const codecForConversionBankConfig =
       )
       .property("fiat_currency", codecForString())
       .property("fiat_currency_specification", 
codecForCurrencySpecificiation())
-      // .property("conversion_info", codecOptional(codecForConversionInfo()))
-      ////////////////////////// remove this
-      .property("cashin_fee", codecForAmountString())
-      .property("cashin_min_amount", codecForAmountString())
-      .property("cashin_ratio", codecForString())
-      .property(
-        "cashin_rounding_mode",
-        codecForEither(
-          codecForConstString("zero"),
-          codecForConstString("up"),
-          codecForConstString("nearest"),
-        ),
-      )
-      .property("cashin_tiny_amount", codecForAmountString())
-      .property("cashout_fee", codecForAmountString())
-      .property("cashout_min_amount", codecForAmountString())
-      .property("cashout_ratio", codecForString())
-      .property(
-        "cashout_rounding_mode",
-        codecForEither(
-          codecForConstString("zero"),
-          codecForConstString("up"),
-          codecForConstString("nearest"),
-        ),
-      )
-      .property("cashout_tiny_amount", codecForAmountString())
-      //////////////////////////
-      .build("ConversionBankConfig.IntegrationConfig");
+
+      .property("conversion_info", codecOptional(codecForConversionInfo()))
+      .build("ConversionBankConfig.IntegrationConfig")
+
 // export const codecFor =
 //   (): Codec<TalerWireGatewayApi.PublicAccountsResponse> =>
 //     buildCodecForObject<TalerWireGatewayApi.PublicAccountsResponse>()
@@ -833,7 +801,7 @@ type EddsaSignature = string;
 type BlindedRsaSignature = string;
 type Base32 = string;
 
-type DecimalNumber = number;
+type DecimalNumber = string;
 type RsaSignature = string;
 // The type of a coin's blinded envelope depends on the cipher that is used
 // for signing with a denomination key.
@@ -859,11 +827,10 @@ interface CSCoinEnvelope {
 // a 256-bit nonce, converted to Crockford Base32.
 type DenominationBlindingKeyP = string;
 
-const codecForURL = codecForString;
-const codecForLibtoolVersion = codecForString;
-const codecForCurrencyName = codecForString;
-const codecForEddsaSignature = codecForString;
-const codecForDecimalNumber = codecForNumber;
+const codecForURL = codecForString
+const codecForLibtoolVersion = codecForString
+const codecForCurrencyName = codecForString
+const codecForDecimalNumber = codecForString
 
 enum TanChannel {
   SMS = "sms",
@@ -1065,12 +1032,10 @@ export namespace TalerRevenueApi {
 export namespace TalerBankConversionApi {
   export interface ConversionInfo {
     // Exchange rate to buy regional currency from fiat
-    // cashin_ratio: DecimalNumber;
-    cashin_ratio: string;
+    cashin_ratio: DecimalNumber;
 
     // Exchange rate to sell regional currency for fiat
-    // cashout_ratio: DecimalNumber;
-    cashout_ratio: string;
+    cashout_ratio: DecimalNumber;
 
     // Fee to subtract after applying the cashin ratio.
     cashin_fee: AmountString;
@@ -1097,7 +1062,7 @@ export namespace TalerBankConversionApi {
     cashout_rounding_mode: "zero" | "up" | "nearest";
   }
 
-  export interface IntegrationConfig extends ConversionInfo {
+  export interface IntegrationConfig {
     // libtool-style representation of the Bank protocol version, see
     // 
https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
     // The format is "current:revision:age".
@@ -1120,7 +1085,7 @@ export namespace TalerBankConversionApi {
 
     // Extra conversion rate information.
     // Only present if server opts in to report the static conversion rate.
-    // conversion_info?: ConversionInfo
+    conversion_info?: ConversionInfo
   }
 
   export interface CashinConversionResponse {
@@ -1497,7 +1462,7 @@ export namespace TalerCorebankApi {
   export interface CashoutPending {
     // ID identifying the operation being created
     // and now waiting for the TAN confirmation.
-    cashout_id: string;
+    cashout_id: number;
   }
 
   export interface CashoutConfirmRequest {
@@ -1511,7 +1476,7 @@ export namespace TalerCorebankApi {
   }
 
   export interface CashoutInfo {
-    cashout_id: string;
+    cashout_id: number;
     status: "pending" | "aborted" | "confirmed";
   }
   export interface GlobalCashouts {
@@ -1519,7 +1484,7 @@ export namespace TalerCorebankApi {
     cashouts: GlobalCashoutInfo[];
   }
   export interface GlobalCashoutInfo {
-    cashout_id: string;
+    cashout_id: number;
     username: string;
     status: "pending" | "aborted" | "confirmed";
   }
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
index e5eaa3c14..108f5d005 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
@@ -159,6 +159,7 @@ describe("Withdraw CTA states", () => {
         amountEffective: "ARS:2" as AmountString,
         paytoUris: ["payto://"],
         tosAccepted: true,
+        withdrawalAccountList: [],
         ageRestrictionOptions: [],
         numCoins: 42,
       },
@@ -223,6 +224,7 @@ describe("Withdraw CTA states", () => {
         amountEffective: "ARS:2" as AmountString,
         paytoUris: ["payto://"],
         tosAccepted: false,
+        withdrawalAccountList: [],
         ageRestrictionOptions: [],
         numCoins: 42,
       },
diff --git a/packages/web-util/src/components/Header.tsx 
b/packages/web-util/src/components/Header.tsx
index bde0688dc..0ffc57417 100644
--- a/packages/web-util/src/components/Header.tsx
+++ b/packages/web-util/src/components/Header.tsx
@@ -3,7 +3,8 @@ import { LangSelector, useTranslationContext } from 
"../index.browser.js";
 import { ComponentChildren, Fragment, VNode, h } from "preact";
 import logo from "../assets/logo-2021.svg";
 
-export function Header({ title, iconLinkURL, sites, supportedLangs, onLogout, 
children }: { title: string, iconLinkURL: string, children?: ComponentChildren, 
onLogout: (() => void) | undefined, sites: [string, string][], supportedLangs: 
string[] }): VNode {
+export function Header({ title, iconLinkURL, sites, supportedLangs, onLogout, 
children }: 
+  { title: string, iconLinkURL: string, children?: ComponentChildren, 
onLogout: (() => void) | undefined, sites: Array<Array<string>>, 
supportedLangs: string[] }): VNode {
   const { i18n } = useTranslationContext();
   const [open, setOpen] = useState(false)
   return <Fragment>
@@ -28,7 +29,9 @@ export function Header({ title, iconLinkURL, sites, 
supportedLangs, onLogout, ch
           {sites.length !== 0 &&
             <div class="flex flex-1 space-x-4">
               {/* <!-- Current: "bg-indigo-700 text-white", Default: 
"text-white hover:bg-indigo-500 hover:bg-opacity-75" --> */}
-              {sites.map(([name, url]) => {
+              {sites.map((site) => {
+                if (site.length !== 2) return;
+                const [name, url] = site
                 return <a href={url} class="text-white hover:bg-indigo-500 
hover:bg-opacity-75 rounded-md py-2 px-3 text-sm font-medium">{name}</a>
               })}
             </div>

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]