gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (2a4dbc67e -> e0e82cdf0)


From: gnunet
Subject: [taler-wallet-core] branch master updated (2a4dbc67e -> e0e82cdf0)
Date: Fri, 08 Mar 2024 16:19:27 +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 2a4dbc67e remove dead code
     new b3ae213bc remove http resp from operation
     new e0e82cdf0 wip #8489

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:
 .../src/pages/AntiMoneyLaunderingForm.tsx          |   5 -
 .../auditor-backoffice-ui/src/hooks/testing.tsx    |   8 +-
 packages/demobank-ui/src/Routing.tsx               |   9 +-
 packages/demobank-ui/src/context/config.ts         | 201 ++++++++++-----------
 packages/demobank-ui/src/hooks/account.ts          |  12 +-
 packages/demobank-ui/src/hooks/regional.ts         |  34 ++--
 packages/demobank-ui/src/pages/LoginForm.tsx       |   5 +-
 .../demobank-ui/src/pages/OperationState/state.ts  |  10 +-
 .../src/pages/PaytoWireTransferForm.tsx            |   2 +-
 packages/demobank-ui/src/pages/QrCodeSection.tsx   |   2 +-
 .../demobank-ui/src/pages/RegistrationPage.tsx     |   2 +-
 .../demobank-ui/src/pages/SolveChallengePage.tsx   |   2 +-
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |   2 +-
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |   2 +-
 .../src/pages/WithdrawalOperationPage.tsx          |   4 +-
 .../src/pages/account/ShowAccountDetails.tsx       |   2 +-
 .../src/pages/account/UpdateAccountPassword.tsx    |   2 +-
 .../src/pages/admin/CreateNewAccount.tsx           |   2 +-
 .../demobank-ui/src/pages/admin/DownloadStats.tsx  |   2 +-
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  |   2 +-
 .../src/pages/regional/ConversionConfig.tsx        |   6 +-
 .../src/pages/regional/CreateCashout.tsx           |   2 +-
 .../merchant-backoffice-ui/src/hooks/testing.tsx   |   8 +-
 .../taler-harness/src/integrationtests/test-otp.ts |   7 +-
 .../src/integrationtests/test-payment-template.ts  |   2 +-
 packages/taler-util/src/MerchantApiClient.ts       |   6 +-
 packages/taler-util/src/errors.ts                  |   5 +
 .../taler-util/src/http-client/authentication.ts   |   4 +-
 .../taler-util/src/http-client/bank-conversion.ts  |  21 ++-
 packages/taler-util/src/http-client/bank-core.ts   | 124 +++++++------
 .../taler-util/src/http-client/bank-integration.ts |   8 +-
 .../taler-util/src/http-client/bank-revenue.ts     |   4 +-
 packages/taler-util/src/http-client/bank-wire.ts   |  14 +-
 packages/taler-util/src/http-client/exchange.ts    |  14 +-
 packages/taler-util/src/http-client/utils.ts       |   8 +
 packages/taler-util/src/index.ts                   |   1 +
 packages/taler-util/src/operation.ts               |  39 ++--
 37 files changed, 293 insertions(+), 290 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx 
b/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx
index bab672192..c42b1e7af 100644
--- a/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx
+++ b/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx
@@ -121,7 +121,6 @@ export function parseJustification(
     const info = codecForSimpleFormMetadata().decode(justification);
     if (!info.id) {
       return {
-        httpResp: undefined as any,
         type: "fail",
         case: "id-not-found",
         detail: {} as any,
@@ -129,7 +128,6 @@ export function parseJustification(
     }
     if (!info.version) {
       return {
-        httpResp: undefined as any,
         type: "fail",
         case: "version-not-found",
         detail: {} as any,
@@ -140,14 +138,12 @@ export function parseJustification(
     });
     if (!found) {
       return {
-        httpResp: undefined as any,
         type: "fail",
         case: "form-not-found",
         detail: {} as any,
       };
     }
     return {
-      httpResp: undefined as any,
       type: "ok",
       body: {
         justification,
@@ -156,7 +152,6 @@ export function parseJustification(
     };
   } catch (e) {
     return {
-      httpResp: undefined as any,
       type: "fail",
       case: "not-json",
       detail: {} as any,
diff --git a/packages/auditor-backoffice-ui/src/hooks/testing.tsx 
b/packages/auditor-backoffice-ui/src/hooks/testing.tsx
index 3ea22475b..20b9c8a35 100644
--- a/packages/auditor-backoffice-ui/src/hooks/testing.tsx
+++ b/packages/auditor-backoffice-ui/src/hooks/testing.tsx
@@ -27,7 +27,7 @@ import { ApiContextProvider } from 
"@gnu-taler/web-util/browser";
 import { BackendContextProvider } from "../context/backend.js";
 import { InstanceContextProvider } from "../context/instance.js";
 import { HttpResponseOk, RequestOptions } from "@gnu-taler/web-util/browser";
-import { TalerBankIntegrationHttpClient, TalerCoreBankHttpClient } from 
"@gnu-taler/taler-util";
+import { TalerBankIntegrationHttpClient, TalerCoreBankHttpClient, 
TalerRevenueHttpClient, TalerWireGatewayHttpClient } from 
"@gnu-taler/taler-util";
 
 export class ApiMockEnvironment extends MockEnvironment {
   constructor(debug = false) {
@@ -144,9 +144,9 @@ export class ApiMockEnvironment extends MockEnvironment {
 
       }
       const bankCore = new TalerCoreBankHttpClient("http://localhost";, 
mockHttpClient)
-      const bankIntegration = bankCore.getIntegrationAPI()
-      const bankRevenue = bankCore.getRevenueAPI("a")
-      const bankWire = bankCore.getWireGatewayAPI("b")
+      const bankIntegration = new 
TalerBankIntegrationHttpClient(bankCore.getIntegrationAPI(), mockHttpClient)
+      const bankRevenue = new 
TalerRevenueHttpClient(bankCore.getRevenueAPI("a"), "a", mockHttpClient)
+      const bankWire = new 
TalerWireGatewayHttpClient(bankCore.getWireGatewayAPI("b"), "b", mockHttpClient)
 
       return (
         <BackendContextProvider defaultUrl="http://backend";>
diff --git a/packages/demobank-ui/src/Routing.tsx 
b/packages/demobank-ui/src/Routing.tsx
index c85d74e17..f9b388ed1 100644
--- a/packages/demobank-ui/src/Routing.tsx
+++ b/packages/demobank-ui/src/Routing.tsx
@@ -94,7 +94,7 @@ function PublicRounting({
   const { i18n } = useTranslationContext();
   const location = useCurrentLocation(publicPages);
   const { navigateTo } = useNavigationContext();
-  const { api } = useBankCoreApiContext();
+  const { bank, authenticator } = useBankCoreApiContext();
   const [notification, notify, handleError] = useLocalNotification();
 
   useEffect(() => {
@@ -109,8 +109,7 @@ function PublicRounting({
 
   async function doAutomaticLogin(username: string, password: string) {
     await handleError(async () => {
-      const resp = await api
-        .getAuthenticationAPI(username)
+      const resp = await authenticator(username)
         .createAccessToken(password, {
           scope: "readwrite",
           duration: { d_us: "forever" },
@@ -570,11 +569,11 @@ function PrivateRouting({
         routeMyAccountDetails={privatePages.myAccountDetails}
         routeMyAccountPassword={privatePages.myAccountPassword}
         routeConversionConfig={privatePages.conversionConfig}
-        routeCancel={privatePages.home} 
+        routeCancel={privatePages.home}
         onUpdateSuccess={() => {
           navigateTo(privatePages.home.url({}))
         }}
-        />;
+      />;
     }
     case "homeWireTransfer": {
       return (
diff --git a/packages/demobank-ui/src/context/config.ts 
b/packages/demobank-ui/src/context/config.ts
index 72c176679..39d12be86 100644
--- a/packages/demobank-ui/src/context/config.ts
+++ b/packages/demobank-ui/src/context/config.ts
@@ -15,20 +15,16 @@
  */
 
 import {
-  AccessToken,
-  AmountJson,
-  HttpStatusCode,
+  assertUnreachable,
   LibtoolVersion,
-  OperationFail,
-  OperationOk,
-  TalerBankConversionApi,
+  TalerBankConversionCacheEviction,
   TalerBankConversionHttpClient,
   TalerCorebankApi,
+  TalerAuthenticationHttpClient,
+  TalerCoreBankCacheEviction,
   TalerCoreBankHttpClient,
-  TalerError,
-  UserAndToken,
+  TalerError
 } from "@gnu-taler/taler-util";
-import { HttpRequestLibrary } from "@gnu-taler/taler-util/http";
 import {
   BrowserHttpLib,
   ErrorLoading,
@@ -61,7 +57,9 @@ import {
 export type Type = {
   url: URL;
   config: TalerCorebankApi.Config;
-  api: TalerCoreBankHttpClient;
+  bank: TalerCoreBankHttpClient;
+  conversion: TalerBankConversionHttpClient;
+  authenticator: (user: string) => TalerAuthenticationHttpClient;
   hints: VersionHint[];
 };
 
@@ -95,13 +93,14 @@ export const BankCoreApiProvider = ({
 }): VNode => {
   const [checked, setChecked] = useState<ConfigResult>();
   const { i18n } = useTranslationContext();
-  const url = new URL(baseUrl);
-  const api = new CacheAwareTalerCoreBankHttpClient(url.href, new 
BrowserHttpLib());
+
+  const { bankClient, conversionClient, authClient } = buildApiClient(new 
URL(baseUrl))
+
   useEffect(() => {
-    api
+    bankClient
       .getConfig()
       .then((resp) => {
-        if (api.isCompatible(resp.body.version)) {
+        if (bankClient.isCompatible(resp.body.version)) {
           setChecked({ type: "ok", config: resp.body, hints: [] });
         } else {
           // this API supports version 3.0.3
@@ -116,7 +115,7 @@ export const BankCoreApiProvider = ({
             setChecked({
               type: "incompatible",
               result: resp.body,
-              supported: api.PROTOCOL_VERSION,
+              supported: bankClient.PROTOCOL_VERSION,
             });
           }
         }
@@ -146,9 +145,11 @@ export const BankCoreApiProvider = ({
     });
   }
   const value: Type = {
-    url,
+    url: new URL(bankClient.baseUrl),
     config: checked.config,
-    api: api,
+    bank: bankClient,
+    conversion: conversionClient,
+    authenticator: authClient,
     hints: checked.hints,
   };
   return h(Context.Provider, {
@@ -158,100 +159,84 @@ export const BankCoreApiProvider = ({
 };
 
 /**
- * 
+ * build http client with cache breaker due to SWR
+ * @param url 
+ * @returns 
  */
-class CacheAwareTalerBankConversionHttpClient extends 
TalerBankConversionHttpClient {
-  constructor(baseUrl: string, httpClient?: HttpRequestLibrary) {
-    super(baseUrl, httpClient);
-  }
-  async updateConversionRate(auth: AccessToken, body: 
TalerBankConversionApi.ConversionRate) {
-    const resp = await super.updateConversionRate(auth, body);
-    if (resp.type === "ok") {
-      await revalidateConversionInfo();
-    }
-    return resp
-  }
-}
+function buildApiClient(url: URL) {
+  const httpLib = new BrowserHttpLib();
 
-class CacheAwareTalerCoreBankHttpClient extends TalerCoreBankHttpClient {
-  constructor(baseUrl: string, httpClient?: HttpRequestLibrary) {
-    super(baseUrl, httpClient);
-  }
-  async deleteAccount(auth: UserAndToken, cid?: string | undefined) {
-    const resp = await super.deleteAccount(auth, cid);
-    if (resp.type === "ok") {
-      await revalidatePublicAccounts();
-      await revalidateBusinessAccounts();
-    }
-    return resp;
-  }
-  async createAccount(
-    auth: AccessToken,
-    body: TalerCorebankApi.RegisterAccountRequest,
-  ) {
-    const resp = await super.createAccount(auth, body);
-    if (resp.type === "ok") {
-      // admin balance change on new account
-      await revalidateAccountDetails();
-      await revalidateTransactions();
-      await revalidatePublicAccounts();
-      await revalidateBusinessAccounts();
-    }
-    return resp;
-  }
-  async updateAccount(
-    auth: UserAndToken,
-    body: TalerCorebankApi.AccountReconfiguration,
-    cid?: string | undefined,
-  ) {
-    const resp = await super.updateAccount(auth, body, cid);
-    if (resp.type === "ok") {
-      await revalidateAccountDetails();
-    }
-    return resp;
-  }
-  async createTransaction(
-    auth: UserAndToken,
-    body: TalerCorebankApi.CreateTransactionRequest,
-    cid?: string | undefined,
-  ) {
-    const resp = await super.createTransaction(auth, body, cid);
-    if (resp.type === "ok") {
-      await revalidateAccountDetails();
-      await revalidateTransactions();
-    }
-    return resp;
-  }
-  async confirmWithdrawalById(
-    auth: UserAndToken,
-    wid: string,
-    cid?: string | undefined,
-  ) {
-    const resp = await super.confirmWithdrawalById(auth, wid, cid);
-    if (resp.type === "ok") {
-      await revalidateAccountDetails();
-      await revalidateTransactions();
+  const bankClient = new TalerCoreBankHttpClient(url.href, httpLib, {
+    async notifySuccess(op) {
+      switch (op) {
+        case TalerCoreBankCacheEviction.DELELE_ACCOUNT: {
+          await Promise.all([
+            revalidatePublicAccounts(),
+            revalidateBusinessAccounts(),
+          ]);
+          return
+        }
+        case TalerCoreBankCacheEviction.CREATE_ACCOUNT: {
+          // admin balance change on new account
+          await Promise.all([
+            revalidateAccountDetails(),
+            revalidateTransactions(),
+            revalidatePublicAccounts(),
+            revalidateBusinessAccounts(),
+          ])
+          return;
+        }
+        case TalerCoreBankCacheEviction.UPDATE_ACCOUNT: {
+          await Promise.all([
+            revalidateAccountDetails(),
+          ])
+          return;
+        }
+        case TalerCoreBankCacheEviction.CREATE_TRANSACTION: {
+          await Promise.all([
+            revalidateAccountDetails(),
+            revalidateTransactions(),
+          ])
+          return;
+        }
+        case TalerCoreBankCacheEviction.CONFIRM_WITHDRAWAL: {
+          await Promise.all([
+            revalidateAccountDetails(),
+            revalidateTransactions(),
+          ])
+          return;
+        }
+        case TalerCoreBankCacheEviction.CREATE_CASHOUT: {
+          await Promise.all([
+            revalidateAccountDetails(),
+            revalidateCashouts(),
+            revalidateTransactions(),
+          ])
+          return;
+        }
+        case TalerCoreBankCacheEviction.UPDATE_PASSWORD:
+        case TalerCoreBankCacheEviction.ABORT_WITHDRAWAL:
+        case TalerCoreBankCacheEviction.CREATE_WITHDRAWAL:
+          return;
+        default:
+          assertUnreachable(op)
+      }
     }
-    return resp;
-  }
-  async createCashout(
-    auth: UserAndToken,
-    body: TalerCorebankApi.CashoutRequest,
-    cid?: string | undefined,
-  ) {
-    const resp = await super.createCashout(auth, body, cid);
-    if (resp.type === "ok") {
-      await revalidateAccountDetails();
-      await revalidateCashouts();
-      await revalidateTransactions();
+  });
+  const conversionClient = new 
TalerBankConversionHttpClient(bankClient.getConversionInfoAPI(), httpLib, {
+    async notifySuccess(op) {
+      switch (op) {
+        case TalerBankConversionCacheEviction.UPDATE_RATE: {
+          await revalidateConversionInfo();
+          return
+        }
+        default:
+          assertUnreachable(op)
+      }
     }
-    return resp;
-  }
-
-  getConversionInfoAPI(): TalerBankConversionHttpClient {
-    const api = super.getConversionInfoAPI();
-    return new CacheAwareTalerBankConversionHttpClient(api.baseUrl, 
this.httpLib)
-  }
+  });
+  const authClient = (user: string) => new 
TalerAuthenticationHttpClient(bankClient.getAuthenticationAPI(user), user, 
httpLib);
+  return { bankClient, conversionClient, authClient }
 }
 
 export const BankCoreApiProviderTesting = ({
@@ -267,7 +252,7 @@ export const BankCoreApiProviderTesting = ({
     url: new URL(url),
     config: state,
     // @ts-expect-error this API is not being used, not really needed
-    api: undefined,
+    bank: undefined,
     hints: [],
   };
 
diff --git a/packages/demobank-ui/src/hooks/account.ts 
b/packages/demobank-ui/src/hooks/account.ts
index 61a11b1a5..aa0745253 100644
--- a/packages/demobank-ui/src/hooks/account.ts
+++ b/packages/demobank-ui/src/hooks/account.ts
@@ -44,7 +44,7 @@ export function revalidateAccountDetails() {
 
 export function useAccountDetails(account: string) {
   const { state: credentials } = useSessionState();
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   async function fetcher([username, token]: [string, AccessToken]) {
     return await api.getAccount({ username, token });
@@ -66,7 +66,7 @@ export function revalidateWithdrawalDetails() {
 }
 
 export function useWithdrawalDetails(wid: string) {
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
   const [latestStatus, setLatestStatus] = 
useState<WithdrawalOperationStatus>();
 
   async function fetcher([wid, old_state]: [
@@ -117,7 +117,7 @@ export function useTransactionDetails(account: string, tid: 
number) {
   const { state: credentials } = useSessionState();
   const token =
     credentials.status !== "loggedIn" ? undefined : credentials.token;
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   async function fetcher([username, token, txid]: [
     string,
@@ -147,7 +147,7 @@ export function useTransactionDetails(account: string, tid: 
number) {
   return undefined;
 }
 
-export function revalidatePublicAccounts() {
+export async function revalidatePublicAccounts() {
   return mutate(
     (key) => Array.isArray(key) && key[key.length - 1] === 
"getPublicAccounts", undefined, { revalidate: true }
   );
@@ -158,7 +158,7 @@ export function usePublicAccounts(
 ) {
   const [offset, setOffset] = useState<number | undefined>(initial);
 
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   async function fetcher([account, txid]: [
     string | undefined,
@@ -233,7 +233,7 @@ export function useTransactions(account: string, initial?: 
number) {
     credentials.status !== "loggedIn" ? undefined : credentials.token;
 
   const [offset, setOffset] = useState<number | undefined>(initial);
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   async function fetcher([username, token, txid]: [
     string,
diff --git a/packages/demobank-ui/src/hooks/regional.ts 
b/packages/demobank-ui/src/hooks/regional.ts
index a9ebb30a2..bf948d293 100644
--- a/packages/demobank-ui/src/hooks/regional.ts
+++ b/packages/demobank-ui/src/hooks/regional.ts
@@ -60,10 +60,10 @@ export function revalidateConversionInfo() {
   );
 }
 export function useConversionInfo() {
-  const { api, config } = useBankCoreApiContext();
+  const { conversion, config } = useBankCoreApiContext();
 
   async function fetcher() {
-    return await api.getConversionInfoAPI().getConfig();
+    return await conversion.getConfig();
   }
   const { data, error } = useSWR<
     TalerBankConversionResultByMethod<"getConfig">,
@@ -86,10 +86,10 @@ export function useConversionInfo() {
 }
 
 export function useCashinEstimator(): ConversionEstimators {
-  const { api } = useBankCoreApiContext();
+  const { conversion } = useBankCoreApiContext();
   return {
     estimateByCredit: async (fiatAmount, fee) => {
-      const resp = await api.getConversionInfoAPI().getCashinRate({
+      const resp = await conversion.getCashinRate({
         credit: fiatAmount,
       });
       if (resp.type === "fail") {
@@ -114,7 +114,7 @@ export function useCashinEstimator(): ConversionEstimators {
       };
     },
     estimateByDebit: async (regionalAmount, fee) => {
-      const resp = await api.getConversionInfoAPI().getCashinRate({
+      const resp = await conversion.getCashinRate({
         debit: regionalAmount,
       });
       if (resp.type === "fail") {
@@ -142,10 +142,10 @@ export function useCashinEstimator(): 
ConversionEstimators {
 }
 
 export function useCashoutEstimator(): ConversionEstimators {
-  const { api } = useBankCoreApiContext();
+  const { bank, conversion } = useBankCoreApiContext();
   return {
     estimateByCredit: async (fiatAmount, fee) => {
-      const resp = await api.getConversionInfoAPI().getCashoutRate({
+      const resp = await conversion.getCashoutRate({
         credit: fiatAmount,
       });
       if (resp.type === "fail") {
@@ -170,7 +170,7 @@ export function useCashoutEstimator(): ConversionEstimators 
{
       };
     },
     estimateByDebit: async (regionalAmount, fee) => {
-      const resp = await api.getConversionInfoAPI().getCashoutRate({
+      const resp = await conversion.getCashoutRate({
         debit: regionalAmount,
       });
       if (resp.type === "fail") {
@@ -204,14 +204,14 @@ export function useEstimator(): ConversionEstimators {
   return useCashoutEstimator()
 }
 
-export function revalidateBusinessAccounts() {
+export async function revalidateBusinessAccounts() {
   return mutate((key) => Array.isArray(key) && key[key.length - 1] === 
"getAccounts", undefined, { revalidate: true });
 }
 export function useBusinessAccounts() {
   const { state: credentials } = useSessionState();
   const token =
     credentials.status !== "loggedIn" ? undefined : credentials.token;
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   const [offset, setOffset] = useState<number | undefined>();
 
@@ -281,7 +281,7 @@ export function revalidateOnePendingCashouts() {
 }
 export function useOnePendingCashouts(account: string) {
   const { state: credentials } = useSessionState();
-  const { api, config } = useBankCoreApiContext();
+  const { bank: api, config } = useBankCoreApiContext();
   const token =
     credentials.status !== "loggedIn" ? undefined : credentials.token;
 
@@ -291,7 +291,7 @@ export function useOnePendingCashouts(account: string) {
       return list;
     }
     const pendingCashout = list.body.cashouts.length > 0 ? 
list.body.cashouts[0] : undefined;
-    if (!pendingCashout) return opFixedSuccess(list.httpResp, undefined);
+    if (!pendingCashout) return opFixedSuccess(undefined);
     const cashoutInfo = await api.getCashoutById(
       { username, token },
       pendingCashout.cashout_id,
@@ -299,7 +299,7 @@ export function useOnePendingCashouts(account: string) {
     if (cashoutInfo.type !== "ok") {
       return cashoutInfo;
     }
-    return opFixedSuccess(list.httpResp, {
+    return opFixedSuccess({
       ...cashoutInfo.body,
       id: pendingCashout.cashout_id,
     });
@@ -338,7 +338,7 @@ export function revalidateCashouts() {
 }
 export function useCashouts(account: string) {
   const { state: credentials } = useSessionState();
-  const { api, config } = useBankCoreApiContext();
+  const { bank: api, config } = useBankCoreApiContext();
   const token =
     credentials.status !== "loggedIn" ? undefined : credentials.token;
 
@@ -357,7 +357,7 @@ export function useCashouts(account: string) {
       }),
     );
     const cashouts = all.filter(notUndefined);
-    return { type: "ok" as const, body: { cashouts }, httpResp: list.httpResp 
};
+    return { type: "ok" as const, body: { cashouts }};
   }
   const { data, error } = useSWR<
     | OperationOk<{ cashouts: CashoutWithId[] }>
@@ -392,7 +392,7 @@ export function revalidateCashoutDetails() {
 export function useCashoutDetails(cashoutId: number | undefined) {
   const { state: credentials } = useSessionState();
   const creds = credentials.status !== "loggedIn" ? undefined : credentials;
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   async function fetcher([username, token, id]: [string, AccessToken, number]) 
{
     return api.getCashoutById({ username, token }, id);
@@ -443,7 +443,7 @@ export function useLastMonitorInfo(
   previousMoment: number,
   timeframe: TalerCorebankApi.MonitorTimeframeParam,
 ) {
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
   const { state: credentials } = useSessionState();
   const token =
     credentials.status !== "loggedIn" ? undefined : credentials.token;
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx 
b/packages/demobank-ui/src/pages/LoginForm.tsx
index e62759415..bd20e79c8 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -53,7 +53,7 @@ export function LoginForm({
   );
   const [password, setPassword] = useState<string | undefined>();
   const { i18n } = useTranslationContext();
-  const { api } = useBankCoreApiContext();
+  const { authenticator } = useBankCoreApiContext();
   const [notification, withErrorHandler] = useLocalNotificationHandler();
   const { config } = useBankCoreApiContext();
 
@@ -77,8 +77,7 @@ export function LoginForm({
   }
 
   const loginHandler = !username || !password ? undefined : withErrorHandler(
-    async () => api
-      .getAuthenticationAPI(username)
+    async () => authenticator(username)
       .createAccessToken(password, {
         // scope: "readwrite" as "write", // FIX: different than merchant
         scope: "readwrite",
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts 
b/packages/demobank-ui/src/pages/OperationState/state.ts
index 693179d40..5baf2d51c 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -45,7 +45,7 @@ export function useComponentState({
   const [bankState, updateBankState] = useBankState();
   const { state: credentials } = useSessionState();
   const creds = credentials.status !== "loggedIn" ? undefined : credentials;
-  const { api } = useBankCoreApiContext();
+  const { bank } = useBankCoreApiContext();
 
   const [failure, setFailure] = useState<
     TalerCoreBankErrorsByMethod<"createWithdrawal"> | undefined
@@ -56,7 +56,7 @@ export function useComponentState({
     // FIXME: if amount is not enough use balance
     const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`);
     if (!creds) return;
-    const resp = await api.createWithdrawal(creds, {
+    const resp = await bank.createWithdrawal(creds, {
       amount: Amounts.stringify(parsedAmount),
     });
     if (resp.type === "fail") {
@@ -91,7 +91,7 @@ export function useComponentState({
 
   async function doAbort() {
     if (!creds) return;
-    const resp = await api.abortWithdrawalById(creds, wid);
+    const resp = await bank.abortWithdrawalById(creds, wid);
     if (resp.type === "ok") {
       // updateBankState("currentWithdrawalOperationId", undefined)
       onAbort();
@@ -104,7 +104,7 @@ export function useComponentState({
     TalerCoreBankErrorsByMethod<"confirmWithdrawalById"> | undefined
   > {
     if (!creds) return;
-    const resp = await api.confirmWithdrawalById(creds, wid);
+    const resp = await bank.confirmWithdrawalById(creds, wid);
     if (resp.type === "ok") {
       mutate(() => true); //clean withdrawal state
     } else {
@@ -113,7 +113,7 @@ export function useComponentState({
   }
 
   const uri = stringifyWithdrawUri({
-    bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl,
+    bankIntegrationApiBaseUrl: bank.getIntegrationAPI(),
     withdrawalOperationId,
   });
   const parsedUri = parseWithdrawUri(uri);
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx 
b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index f746094ce..791a3b440 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -81,7 +81,7 @@ export function PaytoWireTransferForm({
 }: Props): VNode {
   const [isRawPayto, setIsRawPayto] = useState(false);
   const { state: credentials } = useSessionState();
-  const { api, config, url } = useBankCoreApiContext();
+  const { bank: api, config, url } = useBankCoreApiContext();
 
   const sendingToFixedAccount = withAccount !== undefined;
 
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx 
b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index d4f5a5455..da11e631d 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -51,7 +51,7 @@ export function QrCodeSection({
 
   const [notification, handleError] = useLocalNotificationHandler();
 
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   const onAbortHandler = handleError(
     async () => {
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx 
b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index 18b4c470b..e9f7e602f 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -80,7 +80,7 @@ function RegistrationForm({
   const [notification, _, handleError] = useLocalNotification();
   const settings = useSettingsContext();
 
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
   // const { register } = useTestingAPI();
   const { i18n } = useTranslationContext();
 
diff --git a/packages/demobank-ui/src/pages/SolveChallengePage.tsx 
b/packages/demobank-ui/src/pages/SolveChallengePage.tsx
index 7e117f535..b2e053b3c 100644
--- a/packages/demobank-ui/src/pages/SolveChallengePage.tsx
+++ b/packages/demobank-ui/src/pages/SolveChallengePage.tsx
@@ -57,7 +57,7 @@ export function SolveChallengePage({
   onChallengeCompleted: () => void;
   routeClose: RouteDefinition;
 }): VNode {
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
   const { i18n } = useTranslationContext();
   const [bankState, updateBankState] = useBankState();
   const [code, setCode] = useState<string | undefined>(undefined);
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx 
b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index caf205f31..001d90fa1 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -63,7 +63,7 @@ function OldWithdrawalForm({
   // const { navigateTo } = useNavigationContext();
 
   const [bankState, updateBankState] = useBankState();
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   const { state: credentials } = useSessionState();
   const creds = credentials.status !== "loggedIn" ? undefined : credentials;
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx 
b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index 4efc82017..5925719c3 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -74,7 +74,7 @@ export function WithdrawalConfirmationQuestion({
 
   const [notification, notify, handleError] = useLocalNotification();
 
-  const { config, api } = useBankCoreApiContext();
+  const { config, bank: api } = useBankCoreApiContext();
 
   async function doTransfer() {
     await handleError(async () => {
diff --git a/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx 
b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
index 7075c34c6..973a23011 100644
--- a/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalOperationPage.tsx
@@ -36,9 +36,9 @@ export function WithdrawalOperationPage({
   routeClose: RouteDefinition;
   routeWithdrawalDetails: RouteDefinition<{ wopid: string }>;
 }): VNode {
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
   const uri = stringifyWithdrawUri({
-    bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl,
+    bankIntegrationApiBaseUrl: api.getIntegrationAPI(),
     withdrawalOperationId: operationId,
   });
   const parsedUri = parseWithdrawUri(uri);
diff --git a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx 
b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
index 8ab3998ad..62c8df7f8 100644
--- a/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
+++ b/packages/demobank-ui/src/pages/account/ShowAccountDetails.tsx
@@ -67,7 +67,7 @@ export function ShowAccountDetails({
   const { i18n } = useTranslationContext();
   const { state: credentials } = useSessionState();
   const creds = credentials.status !== "loggedIn" ? undefined : credentials;
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
   const accountIsTheCurrentUser =
     credentials.status === "loggedIn"
       ? credentials.username === account
diff --git a/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx 
b/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx
index b9a334088..e21ac2464 100644
--- a/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx
+++ b/packages/demobank-ui/src/pages/account/UpdateAccountPassword.tsx
@@ -65,7 +65,7 @@ export function UpdateAccountPassword({
   const { state: credentials } = useSessionState();
   const token =
     credentials.status !== "loggedIn" ? undefined : credentials.token;
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   const [current, setCurrent] = useState<string | undefined>();
   const [password, setPassword] = useState<string | undefined>();
diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx 
b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
index 3ae2b636c..38119735e 100644
--- a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
@@ -45,7 +45,7 @@ export function CreateNewAccount({
   const { state: credentials } = useSessionState();
   const token =
     credentials.status !== "loggedIn" ? undefined : credentials.token;
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   const [submitAccount, setSubmitAccount] = useState<
     TalerCorebankApi.RegisterAccountRequest | undefined
diff --git a/packages/demobank-ui/src/pages/admin/DownloadStats.tsx 
b/packages/demobank-ui/src/pages/admin/DownloadStats.tsx
index 66ef73d19..fba366676 100644
--- a/packages/demobank-ui/src/pages/admin/DownloadStats.tsx
+++ b/packages/demobank-ui/src/pages/admin/DownloadStats.tsx
@@ -59,7 +59,7 @@ export function DownloadStats({ routeCancel }: Props): VNode {
     credentials.status !== "loggedIn" || !credentials.isUserAdministrator
       ? undefined
       : credentials;
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
 
   const [options, setOptions] = useState<Options>({
     compareWithPrevious: true,
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx 
b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
index 6039db326..61def9a95 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -64,7 +64,7 @@ export function RemoveAccount({
 
   const { state } = useSessionState();
   const token = state.status !== "loggedIn" ? undefined : state.token;
-  const { api } = useBankCoreApiContext();
+  const { bank: api } = useBankCoreApiContext();
   const [notification, notify, handleError] = useLocalNotification();
   const [, updateBankState] = useBankState();
 
diff --git a/packages/demobank-ui/src/pages/regional/ConversionConfig.tsx 
b/packages/demobank-ui/src/pages/regional/ConversionConfig.tsx
index 63423353b..8845ec9a0 100644
--- a/packages/demobank-ui/src/pages/regional/ConversionConfig.tsx
+++ b/packages/demobank-ui/src/pages/regional/ConversionConfig.tsx
@@ -88,7 +88,7 @@ function useComponentState({
   return () => {
     const { i18n } = useTranslationContext();
 
-    const { api, config } = useBankCoreApiContext();
+    const { bank, conversion, config } = useBankCoreApiContext();
 
     const [notification, notify, handleError] = useLocalNotification();
 
@@ -153,9 +153,7 @@ function useComponentState({
       if (!creds) return
       await handleError(async () => {
         if (status.status === "fail") return;
-        const resp = await api
-          .getConversionInfoAPI()
-          .updateConversionRate(creds.token, status.result.conv)
+        const resp = await conversion.updateConversionRate(creds.token, 
status.result.conv)
         if (resp.type === "ok") {
           setSection("detail")
         } else {
diff --git a/packages/demobank-ui/src/pages/regional/CreateCashout.tsx 
b/packages/demobank-ui/src/pages/regional/CreateCashout.tsx
index a5b8f774a..2f15d16b4 100644
--- a/packages/demobank-ui/src/pages/regional/CreateCashout.tsx
+++ b/packages/demobank-ui/src/pages/regional/CreateCashout.tsx
@@ -86,7 +86,7 @@ export function CreateCashout({
   const creds = credentials.status !== "loggedIn" ? undefined : credentials;
   const [, updateBankState] = useBankState();
 
-  const { api, config, hints } = useBankCoreApiContext();
+  const { bank: api, config, hints } = useBankCoreApiContext();
   const [form, setForm] = useState<Partial<FormType>>({ isDebit: true });
   const [notification, notify, handleError] = useLocalNotification();
   const info = useConversionInfo();
diff --git a/packages/merchant-backoffice-ui/src/hooks/testing.tsx 
b/packages/merchant-backoffice-ui/src/hooks/testing.tsx
index 3ea22475b..20b9c8a35 100644
--- a/packages/merchant-backoffice-ui/src/hooks/testing.tsx
+++ b/packages/merchant-backoffice-ui/src/hooks/testing.tsx
@@ -27,7 +27,7 @@ import { ApiContextProvider } from 
"@gnu-taler/web-util/browser";
 import { BackendContextProvider } from "../context/backend.js";
 import { InstanceContextProvider } from "../context/instance.js";
 import { HttpResponseOk, RequestOptions } from "@gnu-taler/web-util/browser";
-import { TalerBankIntegrationHttpClient, TalerCoreBankHttpClient } from 
"@gnu-taler/taler-util";
+import { TalerBankIntegrationHttpClient, TalerCoreBankHttpClient, 
TalerRevenueHttpClient, TalerWireGatewayHttpClient } from 
"@gnu-taler/taler-util";
 
 export class ApiMockEnvironment extends MockEnvironment {
   constructor(debug = false) {
@@ -144,9 +144,9 @@ export class ApiMockEnvironment extends MockEnvironment {
 
       }
       const bankCore = new TalerCoreBankHttpClient("http://localhost";, 
mockHttpClient)
-      const bankIntegration = bankCore.getIntegrationAPI()
-      const bankRevenue = bankCore.getRevenueAPI("a")
-      const bankWire = bankCore.getWireGatewayAPI("b")
+      const bankIntegration = new 
TalerBankIntegrationHttpClient(bankCore.getIntegrationAPI(), mockHttpClient)
+      const bankRevenue = new 
TalerRevenueHttpClient(bankCore.getRevenueAPI("a"), "a", mockHttpClient)
+      const bankWire = new 
TalerWireGatewayHttpClient(bankCore.getWireGatewayAPI("b"), "b", mockHttpClient)
 
       return (
         <BackendContextProvider defaultUrl="http://backend";>
diff --git a/packages/taler-harness/src/integrationtests/test-otp.ts 
b/packages/taler-harness/src/integrationtests/test-otp.ts
index 3636b1f47..d0aeba095 100644
--- a/packages/taler-harness/src/integrationtests/test-otp.ts
+++ b/packages/taler-harness/src/integrationtests/test-otp.ts
@@ -50,7 +50,7 @@ export async function runOtpTest(t: GlobalTestState) {
     otp_device_id: "mydevice",
     otp_key: randomRfc3548Base32Key(),
   });
-  narrowOpSuccessOrThrow(createOtpRes);
+  narrowOpSuccessOrThrow("createOtpDevice", createOtpRes);
 
   const createTemplateRes = await merchantClient.createTemplate({
     template_description: "my template",
@@ -65,10 +65,11 @@ export async function runOtpTest(t: GlobalTestState) {
       ),
     },
   });
-  narrowOpSuccessOrThrow(createTemplateRes);
+  narrowOpSuccessOrThrow("createTemplate", createTemplateRes);
 
   const getTemplateResp = await merchantClient.getTemplate("tpl1");
-  narrowOpSuccessOrThrow(getTemplateResp);
+  narrowOpSuccessOrThrow("getTemplate", getTemplateResp);
+  
   console.log(`template: ${j2s(getTemplateResp.body)}`);
 
   const wres = await withdrawViaBankV2(t, {
diff --git 
a/packages/taler-harness/src/integrationtests/test-payment-template.ts 
b/packages/taler-harness/src/integrationtests/test-payment-template.ts
index c9f1caacd..c3ab5ffc8 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-template.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-template.ts
@@ -55,7 +55,7 @@ export async function runPaymentTemplateTest(t: 
GlobalTestState) {
       summary: "hello, I'm a summary",
     },
   });
-  narrowOpSuccessOrThrow(createTemplateRes);
+  narrowOpSuccessOrThrow("createTemplate", createTemplateRes);
 
   // Withdraw digital cash into the wallet.
 
diff --git a/packages/taler-util/src/MerchantApiClient.ts 
b/packages/taler-util/src/MerchantApiClient.ts
index 8afacfa46..a71887940 100644
--- a/packages/taler-util/src/MerchantApiClient.ts
+++ b/packages/taler-util/src/MerchantApiClient.ts
@@ -44,7 +44,7 @@ import {
   ResultByMethod,
   opEmptySuccess,
   opKnownHttpFailure,
-  opSuccess,
+  opSuccessFromHttp,
   opUnknownFailure,
 } from "./operation.js";
 import { AmountString } from "./taler-types.js";
@@ -318,7 +318,7 @@ export class MerchantApiClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForAny());
+        return opSuccessFromHttp(resp, codecForAny());
       case HttpStatusCode.NotFound:
         return opKnownHttpFailure(resp.status, resp);
       default:
@@ -341,7 +341,7 @@ export class MerchantApiClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForMerchantConfig());
+        return opSuccessFromHttp(resp, codecForMerchantConfig());
       default:
         return opUnknownFailure(resp, await resp.text());
     }
diff --git a/packages/taler-util/src/errors.ts 
b/packages/taler-util/src/errors.ts
index 3ada34d63..c5110bda8 100644
--- a/packages/taler-util/src/errors.ts
+++ b/packages/taler-util/src/errors.ts
@@ -108,6 +108,11 @@ export interface DetailsMap {
      */
     contentType?: string;
   };
+  [TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR]: {
+    operation: string;
+    error: string;
+    detail: TalerErrorDetail | undefined;
+  }
   [TalerErrorCode.WALLET_EXCHANGE_COIN_SIGNATURE_INVALID]: empty;
   [TalerErrorCode.WALLET_WITHDRAWAL_GROUP_INCOMPLETE]: {
     numErrors: number;
diff --git a/packages/taler-util/src/http-client/authentication.ts 
b/packages/taler-util/src/http-client/authentication.ts
index b17ede458..e8ef6a274 100644
--- a/packages/taler-util/src/http-client/authentication.ts
+++ b/packages/taler-util/src/http-client/authentication.ts
@@ -27,7 +27,7 @@ import { LibtoolVersion } from "../libtool-version.js";
 import {
   opEmptySuccess,
   opKnownHttpFailure,
-  opSuccess,
+  opSuccessFromHttp,
   opUnknownFailure,
 } from "../operation.js";
 import {
@@ -74,7 +74,7 @@ export class TalerAuthenticationHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForTokenSuccessResponse());
+        return opSuccessFromHttp(resp, codecForTokenSuccessResponse());
       //FIXME: missing in docs
       case HttpStatusCode.Unauthorized:
         return opKnownHttpFailure(resp.status, resp);
diff --git a/packages/taler-util/src/http-client/bank-conversion.ts 
b/packages/taler-util/src/http-client/bank-conversion.ts
index 5242a5fc6..ea247ccfc 100644
--- a/packages/taler-util/src/http-client/bank-conversion.ts
+++ b/packages/taler-util/src/http-client/bank-conversion.ts
@@ -27,7 +27,7 @@ import {
   ResultByMethod,
   opEmptySuccess,
   opKnownHttpFailure,
-  opSuccess,
+  opSuccessFromHttp,
   opUnknownFailure,
 } from "../operation.js";
 import { TalerErrorCode } from "../taler-error-codes.js";
@@ -39,7 +39,7 @@ import {
   codecForCashoutConversionResponse,
   codecForConversionBankConfig,
 } from "./types.js";
-import { makeBearerTokenAuthHeader } from "./utils.js";
+import { CacheEvictor, makeBearerTokenAuthHeader, nullEvictor } from 
"./utils.js";
 
 export type TalerBankConversionResultByMethod<
   prop extends keyof TalerBankConversionHttpClient,
@@ -48,6 +48,10 @@ export type TalerBankConversionErrorsByMethod<
   prop extends keyof TalerBankConversionHttpClient,
 > = FailCasesByMethod<TalerBankConversionHttpClient, prop>;
 
+export enum TalerBankConversionCacheEviction {
+  UPDATE_RATE,
+}
+
 /**
  * The API is used by the wallets.
  */
@@ -55,12 +59,15 @@ export class TalerBankConversionHttpClient {
   public readonly PROTOCOL_VERSION = "0:0:0";
 
   httpLib: HttpRequestLibrary;
+  cacheEvictor: CacheEvictor<TalerBankConversionCacheEviction>;
 
   constructor(
     readonly baseUrl: string,
     httpClient?: HttpRequestLibrary,
+    cacheEvictor?: CacheEvictor<TalerBankConversionCacheEviction>,
   ) {
     this.httpLib = httpClient ?? createPlatformHttpLib();
+    this.cacheEvictor = cacheEvictor ?? nullEvictor;
   }
 
   isCompatible(version: string): boolean {
@@ -79,7 +86,7 @@ export class TalerBankConversionHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForConversionBankConfig());
+        return opSuccessFromHttp(resp, codecForConversionBankConfig());
       case HttpStatusCode.NotImplemented:
         return opKnownHttpFailure(resp.status, resp);
       default:
@@ -107,7 +114,7 @@ export class TalerBankConversionHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForCashinConversionResponse());
+        return opSuccessFromHttp(resp, codecForCashinConversionResponse());
       case HttpStatusCode.BadRequest: {
         const body = await resp.json();
         const details = codecForTalerErrorDetail().decode(body);
@@ -154,7 +161,7 @@ export class TalerBankConversionHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForCashoutConversionResponse());
+        return opSuccessFromHttp(resp, codecForCashoutConversionResponse());
       case HttpStatusCode.BadRequest: {
         const body = await resp.json();
         const details = codecForTalerErrorDetail().decode(body);
@@ -195,8 +202,10 @@ export class TalerBankConversionHttpClient {
       body,
     });
     switch (resp.status) {
-      case HttpStatusCode.NoContent:
+      case HttpStatusCode.NoContent: {
+        
this.cacheEvictor.notifySuccess(TalerBankConversionCacheEviction.UPDATE_RATE);
         return opEmptySuccess(resp);
+      }
       case HttpStatusCode.Unauthorized:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.NotImplemented:
diff --git a/packages/taler-util/src/http-client/bank-core.ts 
b/packages/taler-util/src/http-client/bank-core.ts
index 90ba88b6a..4131edb11 100644
--- a/packages/taler-util/src/http-client/bank-core.ts
+++ b/packages/taler-util/src/http-client/bank-core.ts
@@ -35,14 +35,9 @@ import {
   ResultByMethod,
   opEmptySuccess,
   opFixedSuccess,
-  opSuccess,
+  opSuccessFromHttp,
   opUnknownFailure,
 } from "../operation.js";
-import { TalerAuthenticationHttpClient } from "./authentication.js";
-import { TalerBankConversionHttpClient } from "./bank-conversion.js";
-import { TalerBankIntegrationHttpClient } from "./bank-integration.js";
-import { TalerRevenueHttpClient } from "./bank-revenue.js";
-import { TalerWireGatewayHttpClient } from "./bank-wire.js";
 import {
   AccessToken,
   PaginationParams,
@@ -66,9 +61,11 @@ import {
   codecForWithdrawalPublicInfo,
 } from "./types.js";
 import {
+  CacheEvictor,
   addLongPollingParam,
   addPaginationParams,
   makeBearerTokenAuthHeader,
+  nullEvictor,
 } from "./utils.js";
 
 export type TalerCoreBankResultByMethod<
@@ -78,25 +75,38 @@ export type TalerCoreBankErrorsByMethod<
   prop extends keyof TalerCoreBankHttpClient,
 > = FailCasesByMethod<TalerCoreBankHttpClient, prop>;
 
-/**
- * Protocol version spoken with the core bank.
- *
- * Endpoint must be ordered in the same way that in the docs
- * Response code (http and taler) must have the same order that in the docs
- * That way is easier to see changes
- *
- * Uses libtool's current:revision:age versioning.
- */
-export class TalerCoreBankHttpClient {
+export enum TalerCoreBankCacheEviction {
+  DELELE_ACCOUNT,
+  CREATE_ACCOUNT,
+  UPDATE_ACCOUNT,
+  UPDATE_PASSWORD,
+  CREATE_TRANSACTION,
+  CONFIRM_WITHDRAWAL,
+  ABORT_WITHDRAWAL,
+  CREATE_WITHDRAWAL,
+  CREATE_CASHOUT,
+}
+  /**
+   * Protocol version spoken with the core bank.
+   *
+   * Endpoint must be ordered in the same way that in the docs
+   * Response code (http and taler) must have the same order that in the docs
+   * That way is easier to see changes
+   *
+   * Uses libtool's current:revision:age versioning.
+   */
+  export class TalerCoreBankHttpClient {
   public readonly PROTOCOL_VERSION = "4:0:0";
 
   httpLib: HttpRequestLibrary;
-
+  cacheEvictor: CacheEvictor<TalerCoreBankCacheEviction>;
   constructor(
     readonly baseUrl: string,
     httpClient?: HttpRequestLibrary,
+    cacheEvictor?: CacheEvictor<TalerCoreBankCacheEviction>,
   ) {
     this.httpLib = httpClient ?? createPlatformHttpLib();
+    this.cacheEvictor = cacheEvictor ?? nullEvictor;
   }
 
   isCompatible(version: string): boolean {
@@ -115,7 +125,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForCoreBankConfig());
+        return opSuccessFromHttp(resp, codecForCoreBankConfig());
       default:
         return opUnknownFailure(resp, await resp.text());
     }
@@ -142,8 +152,10 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForRegisterAccountResponse());
+      case HttpStatusCode.Ok: {
+        await 
this.cacheEvictor.notifySuccess(TalerCoreBankCacheEviction.CREATE_ACCOUNT)
+        return opSuccessFromHttp(resp, codecForRegisterAccountResponse());
+      }
       case HttpStatusCode.BadRequest:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.Unauthorized:
@@ -339,11 +351,11 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForPublicAccountsResponse());
+        return opSuccessFromHttp(resp, codecForPublicAccountsResponse());
       case HttpStatusCode.NoContent:
-        return opFixedSuccess(resp, { public_accounts: [] });
+        return opFixedSuccess({ public_accounts: [] });
       case HttpStatusCode.NotFound:
-        return opFixedSuccess(resp, { public_accounts: [] });
+        return opFixedSuccess({ public_accounts: [] });
       default:
         return opUnknownFailure(resp, await resp.text());
     }
@@ -371,9 +383,9 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForListBankAccountsResponse());
+        return opSuccessFromHttp(resp, codecForListBankAccountsResponse());
       case HttpStatusCode.NoContent:
-        return opFixedSuccess(resp, { accounts: [] });
+        return opFixedSuccess({ accounts: [] });
       case HttpStatusCode.Unauthorized:
         return opKnownHttpFailure(resp.status, resp);
       default:
@@ -395,7 +407,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForAccountData());
+        return opSuccessFromHttp(resp, codecForAccountData());
       case HttpStatusCode.Unauthorized:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.NotFound:
@@ -428,9 +440,9 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForBankAccountTransactionsResponse());
+        return opSuccessFromHttp(resp, 
codecForBankAccountTransactionsResponse());
       case HttpStatusCode.NoContent:
-        return opFixedSuccess(resp, { transactions: [] });
+        return opFixedSuccess({ transactions: [] });
       case HttpStatusCode.Unauthorized:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.NotFound:
@@ -457,7 +469,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForBankAccountTransactionInfo());
+        return opSuccessFromHttp(resp, codecForBankAccountTransactionInfo());
       case HttpStatusCode.NotFound:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.Unauthorized:
@@ -487,7 +499,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForCreateTransactionResponse());
+        return opSuccessFromHttp(resp, codecForCreateTransactionResponse());
       case HttpStatusCode.Accepted:
         return opKnownAlternativeFailure(
           resp,
@@ -541,7 +553,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForBankAccountCreateWithdrawalResponse());
+        return opSuccessFromHttp(resp, 
codecForBankAccountCreateWithdrawalResponse());
       case HttpStatusCode.NotFound:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.Conflict:
@@ -656,7 +668,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForWithdrawalPublicInfo());
+        return opSuccessFromHttp(resp, codecForWithdrawalPublicInfo());
       //FIXME: missing in docs
       case HttpStatusCode.BadRequest:
         return opKnownHttpFailure(resp.status, resp);
@@ -691,7 +703,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForCashoutPending());
+        return opSuccessFromHttp(resp, codecForCashoutPending());
       case HttpStatusCode.Accepted:
         return opKnownAlternativeFailure(
           resp,
@@ -750,7 +762,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForCashoutStatusResponse());
+        return opSuccessFromHttp(resp, codecForCashoutStatusResponse());
       case HttpStatusCode.NotFound:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.NotImplemented:
@@ -775,9 +787,9 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForCashouts());
+        return opSuccessFromHttp(resp, codecForCashouts());
       case HttpStatusCode.NoContent:
-        return opFixedSuccess(resp, { cashouts: [] });
+        return opFixedSuccess({ cashouts: [] });
       case HttpStatusCode.NotImplemented:
         return opKnownHttpFailure(resp.status, resp);
       default:
@@ -800,9 +812,9 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForGlobalCashouts());
+        return opSuccessFromHttp(resp, codecForGlobalCashouts());
       case HttpStatusCode.NoContent:
-        return opFixedSuccess(resp, { cashouts: [] });
+        return opFixedSuccess({ cashouts: [] });
       case HttpStatusCode.NotImplemented:
         return opKnownHttpFailure(resp.status, resp);
       default:
@@ -831,7 +843,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForTanTransmission());
+        return opSuccessFromHttp(resp, codecForTanTransmission());
       case HttpStatusCode.Unauthorized:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.NotFound:
@@ -930,7 +942,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForMonitorResponse());
+        return opSuccessFromHttp(resp, codecForMonitorResponse());
       case HttpStatusCode.BadRequest:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.Unauthorized:
@@ -948,47 +960,47 @@ export class TalerCoreBankHttpClient {
    * https://docs.taler.net/core/api-corebank.html#taler-bank-integration-api
    *
    */
-  getIntegrationAPI(): TalerBankIntegrationHttpClient {
-    const url = new URL(`taler-integration/`, this.baseUrl);
-    return new TalerBankIntegrationHttpClient(url.href, this.httpLib);
+  getIntegrationAPI(): string {
+    return new URL(`taler-integration/`, this.baseUrl).href;
+    // return new TalerBankIntegrationHttpClient(url.href, this.httpLib);
   }
 
   /**
    * https://docs.taler.net/core/api-corebank.html#taler-bank-integration-api
    *
    */
-  getWireGatewayAPI(username: string): TalerWireGatewayHttpClient {
-    const url = new URL(
+  getWireGatewayAPI(username: string): string {
+    return new URL(
       `accounts/${username}/taler-wire-gateway/`,
       this.baseUrl,
-    );
-    return new TalerWireGatewayHttpClient(url.href, username, this.httpLib);
+    ).href;
+    // return new TalerWireGatewayHttpClient(url.href, username, this.httpLib);
   }
 
   /**
    * https://docs.taler.net/core/api-corebank.html#taler-bank-integration-api
    *
    */
-  getRevenueAPI(username: string): TalerRevenueHttpClient {
-    const url = new URL(`accounts/${username}/taler-revenue/`, this.baseUrl);
-    return new TalerRevenueHttpClient(url.href, username, this.httpLib);
+  getRevenueAPI(username: string): string {
+    return new URL(`accounts/${username}/taler-revenue/`, this.baseUrl).href;
+    // return new TalerRevenueHttpClient(url.href, username, this.httpLib);
   }
 
   /**
    * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token
    *
    */
-  getAuthenticationAPI(username: string): TalerAuthenticationHttpClient {
-    const url = new URL(`accounts/${username}/`, this.baseUrl);
-    return new TalerAuthenticationHttpClient(url.href, username, this.httpLib);
+  getAuthenticationAPI(username: string): string {
+    return new URL(`accounts/${username}/`, this.baseUrl).href;
+    // return new TalerAuthenticationHttpClient(url.href, username, 
this.httpLib);
   }
 
   /**
    * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token
    *
    */
-  getConversionInfoAPI(): TalerBankConversionHttpClient {
-    const url = new URL(`conversion-info/`, this.baseUrl);
-    return new TalerBankConversionHttpClient(url.href, this.httpLib);
+  getConversionInfoAPI(): string {
+    return new URL(`conversion-info/`, this.baseUrl).href;
+    // TalerBankConversionHttpClient
   }
 }
diff --git a/packages/taler-util/src/http-client/bank-integration.ts 
b/packages/taler-util/src/http-client/bank-integration.ts
index 938249daf..a224c1f09 100644
--- a/packages/taler-util/src/http-client/bank-integration.ts
+++ b/packages/taler-util/src/http-client/bank-integration.ts
@@ -24,7 +24,7 @@ import {
   opEmptySuccess,
   opKnownHttpFailure,
   opKnownTalerFailure,
-  opSuccess,
+  opSuccessFromHttp,
   opUnknownFailure,
 } from "../operation.js";
 import { TalerErrorCode } from "../taler-error-codes.js";
@@ -77,7 +77,7 @@ export class TalerBankIntegrationHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForIntegrationBankConfig());
+        return opSuccessFromHttp(resp, codecForIntegrationBankConfig());
       default:
         return opUnknownFailure(resp, await resp.text());
     }
@@ -106,7 +106,7 @@ export class TalerBankIntegrationHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForBankWithdrawalOperationStatus());
+        return opSuccessFromHttp(resp, 
codecForBankWithdrawalOperationStatus());
       case HttpStatusCode.NotFound:
         return opKnownHttpFailure(resp.status, resp);
       default:
@@ -129,7 +129,7 @@ export class TalerBankIntegrationHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForBankWithdrawalOperationPostResponse());
+        return opSuccessFromHttp(resp, 
codecForBankWithdrawalOperationPostResponse());
       case HttpStatusCode.NotFound:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.Conflict: {
diff --git a/packages/taler-util/src/http-client/bank-revenue.ts 
b/packages/taler-util/src/http-client/bank-revenue.ts
index fb90f9c1c..3b6b3c258 100644
--- a/packages/taler-util/src/http-client/bank-revenue.ts
+++ b/packages/taler-util/src/http-client/bank-revenue.ts
@@ -21,7 +21,7 @@ import {
   FailCasesByMethod,
   ResultByMethod,
   opKnownHttpFailure,
-  opSuccess,
+  opSuccessFromHttp,
   opUnknownFailure,
 } from "../operation.js";
 import {
@@ -90,7 +90,7 @@ export class TalerRevenueHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForMerchantIncomingHistory());
+        return opSuccessFromHttp(resp, codecForMerchantIncomingHistory());
       case HttpStatusCode.BadRequest:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.Unauthorized:
diff --git a/packages/taler-util/src/http-client/bank-wire.ts 
b/packages/taler-util/src/http-client/bank-wire.ts
index ba68d26ef..54211fef7 100644
--- a/packages/taler-util/src/http-client/bank-wire.ts
+++ b/packages/taler-util/src/http-client/bank-wire.ts
@@ -22,7 +22,7 @@ import {
   ResultByMethod,
   opFixedSuccess,
   opKnownHttpFailure,
-  opSuccess,
+  opSuccessFromHttp,
   opUnknownFailure,
 } from "../operation.js";
 import {
@@ -96,7 +96,7 @@ export class TalerWireGatewayHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForTransferResponse());
+        return opSuccessFromHttp(resp, codecForTransferResponse());
       //FIXME: show more details in docs
       case HttpStatusCode.BadRequest:
         return opKnownHttpFailure(resp.status, resp);
@@ -131,10 +131,10 @@ export class TalerWireGatewayHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForIncomingHistory());
+        return opSuccessFromHttp(resp, codecForIncomingHistory());
       //FIXME: account should not be returned or make it optional
       case HttpStatusCode.NoContent:
-        return opFixedSuccess(resp, {
+        return opFixedSuccess({
           incoming_transactions: [],
           credit_account: undefined,
         });
@@ -170,10 +170,10 @@ export class TalerWireGatewayHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForOutgoingHistory());
+        return opSuccessFromHttp(resp, codecForOutgoingHistory());
       //FIXME: account should not be returned or make it optional
       case HttpStatusCode.NoContent:
-        return opFixedSuccess(resp, {
+        return opFixedSuccess({
           outgoing_transactions: [],
           debit_account: undefined,
         });
@@ -208,7 +208,7 @@ export class TalerWireGatewayHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForAddIncomingResponse());
+        return opSuccessFromHttp(resp, codecForAddIncomingResponse());
       //FIXME: show more details in docs
       case HttpStatusCode.BadRequest:
         return opKnownHttpFailure(resp.status, resp);
diff --git a/packages/taler-util/src/http-client/exchange.ts 
b/packages/taler-util/src/http-client/exchange.ts
index 003410ddb..c61ba1f8d 100644
--- a/packages/taler-util/src/http-client/exchange.ts
+++ b/packages/taler-util/src/http-client/exchange.ts
@@ -9,7 +9,7 @@ import {
   opEmptySuccess,
   opFixedSuccess,
   opKnownHttpFailure,
-  opSuccess,
+  opSuccessFromHttp,
   opUnknownFailure,
 } from "../operation.js";
 import {
@@ -70,7 +70,7 @@ export class TalerExchangeHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForExchangeConfig());
+        return opSuccessFromHttp(resp, codecForExchangeConfig());
       default:
         return opUnknownFailure(resp, await resp.text());
     }
@@ -87,7 +87,7 @@ export class TalerExchangeHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForExchangeKeys());
+        return opSuccessFromHttp(resp, codecForExchangeKeys());
       default:
         return opUnknownFailure(resp, await resp.text());
     }
@@ -123,9 +123,9 @@ export class TalerExchangeHttpClient {
 
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForAmlRecords());
+        return opSuccessFromHttp(resp, codecForAmlRecords());
       case HttpStatusCode.NoContent:
-        return opFixedSuccess(resp, { records: [] });
+        return opFixedSuccess({ records: [] });
       //this should be unauthorized
       case HttpStatusCode.Forbidden:
         return opKnownHttpFailure(resp.status, resp);
@@ -156,9 +156,9 @@ export class TalerExchangeHttpClient {
 
     switch (resp.status) {
       case HttpStatusCode.Ok:
-        return opSuccess(resp, codecForAmlDecisionDetails());
+        return opSuccessFromHttp(resp, codecForAmlDecisionDetails());
       case HttpStatusCode.NoContent:
-        return opFixedSuccess(resp, { aml_history: [], kyc_attributes: [] });
+        return opFixedSuccess({ aml_history: [], kyc_attributes: [] });
       //this should be unauthorized
       case HttpStatusCode.Forbidden:
         return opKnownHttpFailure(resp.status, resp);
diff --git a/packages/taler-util/src/http-client/utils.ts 
b/packages/taler-util/src/http-client/utils.ts
index 7abedae63..2b8920b66 100644
--- a/packages/taler-util/src/http-client/utils.ts
+++ b/packages/taler-util/src/http-client/utils.ts
@@ -65,3 +65,11 @@ export function addLongPollingParam(url: URL, param?: 
LongPollParams) {
     url.searchParams.set("long_poll_ms", String(param.timeoutMs));
   }
 }
+
+export interface CacheEvictor<T> {
+  notifySuccess: (op: T) => Promise<void>;
+}
+
+export const nullEvictor: CacheEvictor<unknown> = {
+  notifySuccess: () => Promise.resolve()
+}
diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts
index 28909facb..74ef9e8e6 100644
--- a/packages/taler-util/src/index.ts
+++ b/packages/taler-util/src/index.ts
@@ -19,6 +19,7 @@ export * from "./errors.js";
 export { fnutil } from "./fnutils.js";
 export * from "./helpers.js";
 export * from "./http-client/bank-conversion.js";
+export * from "./http-client/authentication.js";
 export * from "./http-client/bank-core.js";
 export * from "./http-client/bank-integration.js";
 export * from "./http-client/bank-revenue.js";
diff --git a/packages/taler-util/src/operation.ts 
b/packages/taler-util/src/operation.ts
index 02cf70196..ecf4a020a 100644
--- a/packages/taler-util/src/operation.ts
+++ b/packages/taler-util/src/operation.ts
@@ -57,8 +57,6 @@ export function isOperationFail<T, E>(
 export interface OperationOk<BodyT> {
   type: "ok";
 
-  httpResp: HttpResponse;
-
   /**
    * Parsed response body.
    */
@@ -71,8 +69,6 @@ export interface OperationOk<BodyT> {
 export interface OperationFail<T> {
   type: "fail";
 
-  httpResp: HttpResponse;
-
   /**
    * Error case (either HTTP status code or TalerErrorCode)
    */
@@ -87,8 +83,6 @@ export interface OperationFail<T> {
 export interface OperationAlternative<T, B> {
   type: "fail";
 
-  httpResp: HttpResponse;
-
   case: T;
   body: B;
 }
@@ -102,24 +96,24 @@ export interface OperationFailWithBody<B> {
   body: B[OperationFailWithBody<B>["case"]];
 }
 
-export async function opSuccess<T>(
+export async function opSuccessFromHttp<T>(
   resp: HttpResponse,
   codec: Codec<T>,
 ): Promise<OperationOk<T>> {
   const body = await readSuccessResponseJsonOrThrow(resp, codec);
-  return { type: "ok" as const, body, httpResp: resp };
+  return { type: "ok" as const, body };
 }
 
 /**
  * Success case, but instead of the body we're returning a fixed response
  * to the client.
  */
-export function opFixedSuccess<T>(resp: HttpResponse, body: T): OperationOk<T> 
{
-  return { type: "ok" as const, body, httpResp: resp };
+export function opFixedSuccess<T>(body: T): OperationOk<T> {
+  return { type: "ok" as const, body };
 }
 
 export function opEmptySuccess(resp: HttpResponse): OperationOk<void> {
-  return { type: "ok" as const, body: void 0, httpResp: resp };
+  return { type: "ok" as const, body: void 0 };
 }
 
 export async function opKnownFailureWithBody<B>(
@@ -135,7 +129,7 @@ export async function opKnownAlternativeFailure<T extends 
HttpStatusCode, B>(
   codec: Codec<B>,
 ): Promise<OperationAlternative<T, B>> {
   const body = await readSuccessResponseJsonOrThrow(resp, codec);
-  return { type: "fail", case: s, body, httpResp: resp };
+  return { type: "fail", case: s, body };
 }
 
 export async function opKnownHttpFailure<T extends HttpStatusCode>(
@@ -143,7 +137,7 @@ export async function opKnownHttpFailure<T extends 
HttpStatusCode>(
   resp: HttpResponse,
 ): Promise<OperationFail<T>> {
   const detail = await readTalerErrorResponse(resp);
-  return { type: "fail", case: s, detail, httpResp: resp };
+  return { type: "fail", case: s, detail };
 }
 
 export async function opKnownTalerFailure<T extends TalerErrorCode>(
@@ -151,7 +145,7 @@ export async function opKnownTalerFailure<T extends 
TalerErrorCode>(
   resp: HttpResponse,
 ): Promise<OperationFail<T>> {
   const detail = await readTalerErrorResponse(resp);
-  return { type: "fail", case: s, detail, httpResp: resp };
+  return { type: "fail", case: s, detail };
 }
 
 export function opUnknownFailure(resp: HttpResponse, text: string): never {
@@ -171,24 +165,21 @@ export function opUnknownFailure(resp: HttpResponse, 
text: string): never {
  * Convenience function to throw an error if the operation is not a success.
  */
 export function narrowOpSuccessOrThrow<Body, ErrorEnum>(
+  opName: string,
   opRes: OperationResult<Body, ErrorEnum>,
 ): asserts opRes is OperationOk<Body> {
-  const httpResponse = opRes.httpResp;
   if (opRes.type !== "ok") {
     throw TalerError.fromDetail(
-      TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
+      TalerErrorCode.GENERIC_CLIENT_INTERNAL_ERROR,
       {
-        requestUrl: httpResponse.requestUrl,
-        requestMethod: httpResponse.requestMethod,
-        httpStatusCode: httpResponse.status,
-        errorResponse:
+        operation: opName,
+        error: String(opRes.case),
+        detail:
           "detail" in opRes
             ? opRes.detail
-            : "body" in opRes
-              ? opRes.body
-              : undefined,
+            : undefined,
       },
-      `Unexpected HTTP status ${httpResponse.status} in response`,
+      `Operation ${opName} failed: ${String(opRes.case)}`,
     );
   }
 }

-- 
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]