gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: scope info in tx details


From: gnunet
Subject: [taler-wallet-core] branch master updated: scope info in tx details
Date: Mon, 05 Aug 2024 15:44:19 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 50d183195 scope info in tx details
50d183195 is described below

commit 50d183195c94d0995aafd60f0fa3766f5f5ba256
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Aug 5 10:41:53 2024 -0300

    scope info in tx details
---
 .../src/types-taler-wallet-transactions.ts         |   5 +
 packages/taler-util/src/types-taler-wallet.ts      |  41 +++++
 packages/taler-wallet-core/src/deposits.ts         |  10 +-
 packages/taler-wallet-core/src/exchanges.ts        |  78 ++++++++++
 packages/taler-wallet-core/src/pay-merchant.ts     |  14 ++
 .../taler-wallet-core/src/pay-peer-pull-credit.ts  |   4 +-
 .../taler-wallet-core/src/pay-peer-pull-debit.ts   |   2 +
 .../taler-wallet-core/src/pay-peer-push-credit.ts  |   4 +-
 .../taler-wallet-core/src/pay-peer-push-debit.ts   |   2 +
 packages/taler-wallet-core/src/refresh.ts          |   2 +
 packages/taler-wallet-core/src/withdraw.ts         |  26 +++-
 .../src/NavigationBar.tsx                          |  12 +-
 .../src/components/BalanceTable.tsx                |   6 +-
 .../src/components/MultiActionButton.tsx           |  18 +--
 .../src/popup/Application.tsx                      |  51 ++++--
 .../src/popup/BalancePage.tsx                      |  13 +-
 .../src/wallet/Application.tsx                     | 172 ++++++++++++++-------
 .../src/wallet/DepositPage/index.ts                |   9 +-
 .../src/wallet/DepositPage/state.ts                |   9 +-
 .../src/wallet/DepositPage/test.ts                 |  12 +-
 .../src/wallet/History.stories.tsx                 |  79 ++++++++--
 .../src/wallet/History.tsx                         |  67 ++++----
 .../src/wallet/Transaction.stories.tsx             |   1 +
 .../src/wallet/Transaction.tsx                     |  24 +--
 24 files changed, 496 insertions(+), 165 deletions(-)

diff --git a/packages/taler-util/src/types-taler-wallet-transactions.ts 
b/packages/taler-util/src/types-taler-wallet-transactions.ts
index 6be6cc296..da3aaca2d 100644
--- a/packages/taler-util/src/types-taler-wallet-transactions.ts
+++ b/packages/taler-util/src/types-taler-wallet-transactions.ts
@@ -190,6 +190,11 @@ export interface TransactionCommon {
   // main timestamp of the transaction
   timestamp: TalerPreciseTimestamp;
 
+  /**
+   * Scope of this tx
+   */
+  scopes: ScopeInfo[];
+
   /**
    * Transaction state, as per DD37.
    */
diff --git a/packages/taler-util/src/types-taler-wallet.ts 
b/packages/taler-util/src/types-taler-wallet.ts
index 7a5c1317b..55a2b07d5 100644
--- a/packages/taler-util/src/types-taler-wallet.ts
+++ b/packages/taler-util/src/types-taler-wallet.ts
@@ -577,6 +577,47 @@ export type ScopeInfoAuditor = {
 
 export type ScopeInfo = ScopeInfoGlobal | ScopeInfoExchange | ScopeInfoAuditor;
 
+/**
+ * Shorter version of stringifyScopeInfo
+ *
+ * Format must be stable as it's used in the database.
+ */
+export function stringifyScopeInfoShort(si: ScopeInfo): string {
+  switch (si.type) {
+    case ScopeType.Global:
+      return `${si.currency}`;
+    case ScopeType.Exchange:
+      return `${si.currency}/${encodeURIComponent(si.url)}`;
+    case ScopeType.Auditor:
+      return `${si.currency}:${encodeURIComponent(si.url)}`;
+  }
+}
+export function parseScopeInfoShort(si: string): ScopeInfo | undefined {
+  const indexOfColon = si.indexOf(":");
+  const indexOfSlash = si.indexOf("/");
+  if (indexOfColon === -1 && indexOfColon === -1) {
+    return {
+      type: ScopeType.Global,
+      currency: si,
+    };
+  }
+  if (indexOfColon > 0) {
+    return {
+      type: ScopeType.Auditor,
+      currency: si.substring(0, indexOfColon),
+      url: decodeURIComponent(si.substring(indexOfColon + 1)),
+    };
+  }
+  if (indexOfSlash > 0) {
+    return {
+      type: ScopeType.Exchange,
+      currency: si.substring(0, indexOfSlash),
+      url: decodeURIComponent(si.substring(indexOfSlash + 1)),
+    };
+  }
+  return undefined;
+}
+
 /**
  * Encode scope info as a string.
  *
diff --git a/packages/taler-wallet-core/src/deposits.ts 
b/packages/taler-wallet-core/src/deposits.ts
index 37c73607a..2c931fc39 100644
--- a/packages/taler-wallet-core/src/deposits.ts
+++ b/packages/taler-wallet-core/src/deposits.ts
@@ -43,6 +43,7 @@ import {
   PrepareDepositRequest,
   PrepareDepositResponse,
   RefreshReason,
+  ScopeInfo,
   SelectedProspectiveCoin,
   TalerError,
   TalerErrorCode,
@@ -99,7 +100,13 @@ import {
   timestampProtocolFromDb,
   timestampProtocolToDb,
 } from "./db.js";
-import { getExchangeWireDetailsInTx, getExchangeWireFee } from 
"./exchanges.js";
+import {
+  getExchangeScopeInfo,
+  getExchangeScopeInfoOrUndefined,
+  getExchangeWireDetailsInTx,
+  getExchangeWireFee,
+  getScopeForAllExchanges,
+} from "./exchanges.js";
 import {
   extractContractData,
   generateDepositPermissions,
@@ -193,6 +200,7 @@ export class DepositTransactionContext implements 
TransactionContext {
     return {
       type: TransactionType.Deposit,
       txState,
+      scopes: await getScopeForAllExchanges(tx, !dg.infoPerExchange? []: 
Object.keys(dg.infoPerExchange)),
       txActions: computeDepositTransactionActions(dg),
       amountRaw: Amounts.stringify(dg.counterpartyEffectiveDepositAmount),
       amountEffective: isUnsuccessfulTransaction(txState)
diff --git a/packages/taler-wallet-core/src/exchanges.ts 
b/packages/taler-wallet-core/src/exchanges.ts
index 270cd6ded..5872d87bb 100644
--- a/packages/taler-wallet-core/src/exchanges.ts
+++ b/packages/taler-wallet-core/src/exchanges.ts
@@ -248,6 +248,83 @@ async function getExchangeRecordsInternal(
   return details;
 }
 
+export async function getScopeForAllCoins(
+  tx: WalletDbReadOnlyTransaction<
+    [
+      "exchanges",
+      "exchangeDetails",
+      "globalCurrencyExchanges",
+      "globalCurrencyAuditors",
+    ]
+  >,
+  exs: string[],
+): Promise<ScopeInfo[]> {
+  const queries = exs.map((exchange) => {
+    return getExchangeScopeInfoOrUndefined(tx, exchange);
+  });
+  const rs = await Promise.all(queries);
+  return rs.filter((d): d is ScopeInfo => d !== undefined);
+}
+
+export async function getScopeForAllExchanges(
+  tx: WalletDbReadOnlyTransaction<
+    [
+      "exchanges",
+      "exchangeDetails",
+      "globalCurrencyExchanges",
+      "globalCurrencyAuditors",
+    ]
+  >,
+  exs: string[],
+): Promise<ScopeInfo[]> {
+  const queries = exs.map((exchange) => {
+    return getExchangeScopeInfoOrUndefined(tx, exchange);
+  });
+  const rs = await Promise.all(queries);
+  return rs.filter((d): d is ScopeInfo => d !== undefined);
+}
+
+export async function getCoinScopeInfoOrUndefined(
+  tx: WalletDbReadOnlyTransaction<
+    [
+      "coins",
+      "exchanges",
+      "exchangeDetails",
+      "globalCurrencyExchanges",
+      "globalCurrencyAuditors",
+    ]
+  >,
+  coinPub: string,
+): Promise<ScopeInfo | undefined> {
+  const coin = await tx.coins.get(coinPub);
+  if (!coin) {
+    return undefined;
+  }
+  const det = await getExchangeRecordsInternal(tx, coin.exchangeBaseUrl);
+  if (!det) {
+    return undefined;
+  }
+  return internalGetExchangeScopeInfo(tx, det);
+}
+
+export async function getExchangeScopeInfoOrUndefined(
+  tx: WalletDbReadOnlyTransaction<
+    [
+      "exchanges",
+      "exchangeDetails",
+      "globalCurrencyExchanges",
+      "globalCurrencyAuditors",
+    ]
+  >,
+  exchangeBaseUrl: string,
+): Promise<ScopeInfo | undefined> {
+  const det = await getExchangeRecordsInternal(tx, exchangeBaseUrl);
+  if (!det) {
+    return undefined;
+  }
+  return internalGetExchangeScopeInfo(tx, det);
+}
+
 export async function getExchangeScopeInfo(
   tx: WalletDbReadOnlyTransaction<
     [
@@ -2154,6 +2231,7 @@ export class DenomLossTransactionContext implements 
TransactionContext {
     return {
       type: TransactionType.DenomLoss,
       txState,
+      scopes: await getScopeForAllExchanges(tx, [rec.exchangeBaseUrl]),
       txActions: [TransactionAction.Delete],
       amountRaw: Amounts.stringify(rec.amount),
       amountEffective: Amounts.stringify(rec.amount),
diff --git a/packages/taler-wallet-core/src/pay-merchant.ts 
b/packages/taler-wallet-core/src/pay-merchant.ts
index f74e47213..726abb1e7 100644
--- a/packages/taler-wallet-core/src/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/pay-merchant.ts
@@ -161,6 +161,7 @@ import {
   getDenomInfo,
   WalletExecutionContext,
 } from "./wallet.js";
+import { getScopeForAllExchanges } from "./exchanges.js";
 
 /**
  * Logger.
@@ -276,6 +277,12 @@ export class PayMerchantTransactionContext implements 
TransactionContext {
     return {
       type: TransactionType.Payment,
       txState,
+      scopes: await getScopeForAllExchanges(
+        tx,
+        !purchaseRec.payInfo.payCoinSelection
+          ? []
+          : purchaseRec.payInfo.payCoinSelection.coinPubs,
+      ),
       txActions: computePayMerchantTransactionActions(purchaseRec),
       amountRaw: Amounts.stringify(contractData.amount),
       amountEffective: isUnsuccessfulTransaction(txState)
@@ -617,10 +624,17 @@ export class RefundTransactionContext implements 
TransactionContext {
         summary_i18n: maybeContractData.summaryI18n,
       };
     }
+    const purchaseRecord = await tx.purchases.get(refundRecord.proposalId);
 
     const txState = computeRefundTransactionState(refundRecord);
     return {
       type: TransactionType.Refund,
+      scopes: await getScopeForAllExchanges(
+        tx,
+        !purchaseRecord || !purchaseRecord.payInfo?.payCoinSelection
+          ? []
+          : purchaseRecord.payInfo.payCoinSelection.coinPubs,
+      ),
       amountEffective: isUnsuccessfulTransaction(txState)
         ? Amounts.stringify(Amounts.zeroOfAmount(refundRecord.amountEffective))
         : refundRecord.amountEffective,
diff --git a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts 
b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
index de180ccd5..7071010b8 100644
--- a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
@@ -83,7 +83,7 @@ import {
   timestampPreciseFromDb,
   timestampPreciseToDb,
 } from "./db.js";
-import { fetchFreshExchange } from "./exchanges.js";
+import { fetchFreshExchange, getScopeForAllExchanges } from "./exchanges.js";
 import {
   codecForExchangePurseStatus,
   getMergeReserveInfo,
@@ -194,6 +194,7 @@ export class PeerPullCreditTransactionContext implements 
TransactionContext {
       return {
         type: TransactionType.PeerPullCredit,
         txState,
+        scopes: await getScopeForAllExchanges(tx, 
[pullCredit.exchangeBaseUrl]),
         txActions: computePeerPullCreditTransactionActions(pullCredit),
         amountEffective: isUnsuccessfulTransaction(txState)
           ? Amounts.stringify(Amounts.zeroOfAmount(wsr.instructedAmount))
@@ -228,6 +229,7 @@ export class PeerPullCreditTransactionContext implements 
TransactionContext {
     return {
       type: TransactionType.PeerPullCredit,
       txState,
+      scopes: await getScopeForAllExchanges(tx, [pullCredit.exchangeBaseUrl]),
       txActions: computePeerPullCreditTransactionActions(pullCredit),
       amountEffective: isUnsuccessfulTransaction(txState)
         ? Amounts.stringify(Amounts.zeroOfAmount(peerContractTerms.amount))
diff --git a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts 
b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts
index dca228547..09ecbeb94 100644
--- a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts
@@ -103,6 +103,7 @@ import {
   parseTransactionIdentifier,
 } from "./transactions.js";
 import { WalletExecutionContext } from "./wallet.js";
+import { getScopeForAllExchanges } from "./exchanges.js";
 
 const logger = new Logger("pay-peer-pull-debit.ts");
 
@@ -166,6 +167,7 @@ export class PeerPullDebitTransactionContext implements 
TransactionContext {
     return {
       type: TransactionType.PeerPullDebit,
       txState,
+      scopes: await getScopeForAllExchanges(tx, [pi.exchangeBaseUrl]),
       txActions: computePeerPullDebitTransactionActions(pi),
       amountEffective: isUnsuccessfulTransaction(txState)
         ? Amounts.stringify(Amounts.zeroOfAmount(pi.amount))
diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts 
b/packages/taler-wallet-core/src/pay-peer-push-credit.ts
index 5199dbde4..830e2bd1b 100644
--- a/packages/taler-wallet-core/src/pay-peer-push-credit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-push-credit.ts
@@ -79,7 +79,7 @@ import {
   timestampPreciseFromDb,
   timestampPreciseToDb,
 } from "./db.js";
-import { fetchFreshExchange } from "./exchanges.js";
+import { fetchFreshExchange, getScopeForAllExchanges } from "./exchanges.js";
 import {
   codecForExchangePurseStatus,
   getMergeReserveInfo,
@@ -183,6 +183,7 @@ export class PeerPushCreditTransactionContext implements 
TransactionContext {
       return {
         type: TransactionType.PeerPushCredit,
         txState,
+        scopes: await getScopeForAllExchanges(tx, [pushInc.exchangeBaseUrl]),
         txActions: computePeerPushCreditTransactionActions(pushInc),
         amountEffective: isUnsuccessfulTransaction(txState)
           ? Amounts.stringify(Amounts.zeroOfAmount(wg.instructedAmount))
@@ -207,6 +208,7 @@ export class PeerPushCreditTransactionContext implements 
TransactionContext {
     return {
       type: TransactionType.PeerPushCredit,
       txState,
+      scopes: await getScopeForAllExchanges(tx, [pushInc.exchangeBaseUrl]),
       txActions: computePeerPushCreditTransactionActions(pushInc),
       amountEffective: isUnsuccessfulTransaction(txState)
         ? Amounts.stringify(Amounts.zeroOfAmount(peerContractTerms.amount))
diff --git a/packages/taler-wallet-core/src/pay-peer-push-debit.ts 
b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
index b30ff334d..4485976b9 100644
--- a/packages/taler-wallet-core/src/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
@@ -93,6 +93,7 @@ import {
   notifyTransition,
 } from "./transactions.js";
 import { WalletExecutionContext } from "./wallet.js";
+import { getScopeForAllExchanges } from "./exchanges.js";
 
 const logger = new Logger("pay-peer-push-debit.ts");
 
@@ -167,6 +168,7 @@ export class PeerPushDebitTransactionContext implements 
TransactionContext {
     return {
       type: TransactionType.PeerPushDebit,
       txState,
+      scopes: await getScopeForAllExchanges(tx, 
[pushDebitRec.exchangeBaseUrl]),
       txActions: computePeerPushDebitTransactionActions(pushDebitRec),
       amountEffective: isUnsuccessfulTransaction(txState)
         ? Amounts.stringify(Amounts.zeroOfAmount(pushDebitRec.totalCost))
diff --git a/packages/taler-wallet-core/src/refresh.ts 
b/packages/taler-wallet-core/src/refresh.ts
index a657ec445..b8bd3c0b7 100644
--- a/packages/taler-wallet-core/src/refresh.ts
+++ b/packages/taler-wallet-core/src/refresh.ts
@@ -122,6 +122,7 @@ import {
   WalletExecutionContext,
 } from "./wallet.js";
 import { getCandidateWithdrawalDenomsTx } from "./withdraw.js";
+import { getScopeForAllExchanges } from "./exchanges.js";
 
 const logger = new Logger("refresh.ts");
 
@@ -186,6 +187,7 @@ export class RefreshTransactionContext implements 
TransactionContext {
     return {
       type: TransactionType.Refresh,
       txState,
+      scopes: await getScopeForAllExchanges(tx, 
!refreshGroupRecord.infoPerExchange? []: 
Object.keys(refreshGroupRecord.infoPerExchange)),
       txActions: computeRefreshTransactionActions(refreshGroupRecord),
       refreshReason: refreshGroupRecord.reason,
       amountEffective: isUnsuccessfulTransaction(txState)
diff --git a/packages/taler-wallet-core/src/withdraw.ts 
b/packages/taler-wallet-core/src/withdraw.ts
index 09348baf7..406772d5f 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -160,6 +160,7 @@ import {
   fetchFreshExchange,
   getExchangePaytoUri,
   getExchangeWireDetailsInTx,
+  getScopeForAllExchanges,
   listExchanges,
   lookupExchangeByUri,
   markExchangeUsed,
@@ -182,6 +183,7 @@ const logger = new Logger("withdraw.ts");
 
 function buildTransactionForBankIntegratedWithdraw(
   wg: WithdrawalGroupRecord,
+  scopes: ScopeInfo[],
   ort?: OperationRetryRecord,
 ): TransactionWithdrawal {
   if (wg.wgInfo.withdrawalType !== WithdrawalRecordType.BankIntegrated) {
@@ -202,6 +204,7 @@ function buildTransactionForBankIntegratedWithdraw(
   return {
     type: TransactionType.Withdrawal,
     txState,
+    scopes,
     txActions: computeWithdrawalTransactionActions(wg),
     exchangeBaseUrl: wg.exchangeBaseUrl,
     amountEffective:
@@ -237,6 +240,7 @@ function buildTransactionForBankIntegratedWithdraw(
 function buildTransactionForManualWithdraw(
   wg: WithdrawalGroupRecord,
   exchangeDetails: ExchangeWireDetails | undefined,
+  scopes: ScopeInfo[],
   ort?: OperationRetryRecord,
 ): TransactionWithdrawal {
   if (wg.wgInfo.withdrawalType !== WithdrawalRecordType.BankManual)
@@ -259,6 +263,7 @@ function buildTransactionForManualWithdraw(
   return {
     type: TransactionType.Withdrawal,
     txState,
+    scopes,
     txActions: computeWithdrawalTransactionActions(wg),
     amountEffective: isUnsuccessfulTransaction(txState)
       ? Amounts.stringify(Amounts.zeroOfAmount(wg.instructedAmount))
@@ -318,6 +323,18 @@ export class WithdrawTransactionContext implements 
TransactionContext {
     if (!withdrawalGroupRecord) {
       return undefined;
     }
+    const exchangeDetails =
+      withdrawalGroupRecord.exchangeBaseUrl === undefined
+        ? undefined
+        : await getExchangeWireDetailsInTx(
+            tx,
+            withdrawalGroupRecord.exchangeBaseUrl,
+          );
+    const scopes = await getScopeForAllExchanges(
+      tx,
+      !exchangeDetails ? [] : [exchangeDetails.exchangeBaseUrl],
+    );
+
     const ort = await tx.operationRetries.get(this.taskId);
     if (
       withdrawalGroupRecord.wgInfo.withdrawalType ===
@@ -325,16 +342,10 @@ export class WithdrawTransactionContext implements 
TransactionContext {
     ) {
       return buildTransactionForBankIntegratedWithdraw(
         withdrawalGroupRecord,
+        scopes,
         ort,
       );
     }
-    const exchangeDetails =
-      withdrawalGroupRecord.exchangeBaseUrl === undefined
-        ? undefined
-        : await getExchangeWireDetailsInTx(
-            tx,
-            withdrawalGroupRecord.exchangeBaseUrl,
-          );
     if (!exchangeDetails) {
       logger.warn(
         `transaction ${this.transactionId} is a manual withdrawal, but no 
exchange wire details found`,
@@ -343,6 +354,7 @@ export class WithdrawTransactionContext implements 
TransactionContext {
     return buildTransactionForManualWithdraw(
       withdrawalGroupRecord,
       exchangeDetails,
+      scopes,
       ort,
     );
   }
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx 
b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index fe348f7fb..d3a0ab37c 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -97,11 +97,11 @@ function pageDefinition<T extends object>(pattern: string): 
PageLocation<T> {
 export const Pages = {
   welcome: "/welcome",
   balance: "/balance",
-  balanceHistory: pageDefinition<{ currency?: string }>(
-    "/balance/history/:currency?",
+  balanceHistory: pageDefinition<{ scope?: string }>(
+    "/balance/history/:scope?",
   ),
-  searchHistory: pageDefinition<{ currency?: string }>(
-    "/search/history/:currency?",
+  searchHistory: pageDefinition<{ scope?: string }>(
+    "/search/history/:scope?",
   ),
   balanceDeposit: pageDefinition<{ amount: string }>(
     "/balance/deposit/:amount",
@@ -109,8 +109,8 @@ export const Pages = {
   balanceTransaction: pageDefinition<{ tid: string }>(
     "/balance/transaction/:tid",
   ),
-  sendCash: pageDefinition<{ amount?: string }>("/destination/send/:amount"),
-  receiveCash: pageDefinition<{ amount?: string 
}>("/destination/get/:amount?"),
+  sendCash: pageDefinition<{ scope?: string }>("/destination/send/:scope"),
+  receiveCash: pageDefinition<{ scope?: string }>("/destination/get/:scope?"),
   dev: "/dev",
 
   exchanges: "/exchanges",
diff --git a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx 
b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
index 6dd577b88..a6ccc10ca 100644
--- a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
+++ b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Amounts, ScopeType, WalletBalance } from "@gnu-taler/taler-util";
+import { Amounts, ScopeInfo, ScopeType, WalletBalance } from 
"@gnu-taler/taler-util";
 import { Fragment, VNode, h } from "preact";
 import {
   TableWithRoundRows as TableWithRoundedRows
@@ -25,7 +25,7 @@ export function BalanceTable({
   goToWalletHistory,
 }: {
   balances: WalletBalance[];
-  goToWalletHistory: (currency: string) => void;
+  goToWalletHistory: (currency: ScopeInfo) => void;
 }): VNode {
   return (
     <Fragment>
@@ -36,7 +36,7 @@ export function BalanceTable({
           return (
             <tr
               key={idx}
-              onClick={() => goToWalletHistory(av.currency)}
+              onClick={() => goToWalletHistory(entry.scopeInfo)}
               style={{ cursor: "pointer" }}
             >
               <td>{av.currency}</td>
diff --git 
a/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx 
b/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
index 7d3cf3f57..3f22e4849 100644
--- a/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
+++ b/packages/taler-wallet-webextension/src/components/MultiActionButton.tsx
@@ -20,10 +20,10 @@ import { Button } from "../mui/Button.js";
 import arrowDown from "../svg/chevron-down.inline.svg";
 import { ParagraphClickable } from "./styled/index.js";
 
-export interface Props {
-  label: (s: string) => TranslatedString;
-  actions: string[];
-  onClick: (s: string) => Promise<void>;
+export interface Props<T> {
+  label: (s: T) => TranslatedString;
+  actions: T[];
+  onClick: (s: T) => Promise<void>;
 }
 
 /**
@@ -37,19 +37,19 @@ export interface Props {
  *
  * @returns
  */
-export function MultiActionButton({
+export function MultiActionButton<T>({
   label,
   actions,
   onClick: doClick,
-}: Props): VNode {
-  const defaultAction = actions.length > 0 ? actions[0] : "";
+}: Props<T>): VNode {
+  const defaultAction = actions.length > 0 ? actions[0] : "" as T;
 
   const [opened, setOpened] = useState(false);
-  const [selected, setSelected] = useState<string>(defaultAction);
+  const [selected, setSelected] = useState<T>(defaultAction);
 
   const canChange = actions.length > 1;
   const options = canChange ? actions.filter((a) => a !== selected) : [];
-  function select(m: string): void {
+  function select(m: T): void {
     setSelected(m);
     setOpened(false);
   }
diff --git a/packages/taler-wallet-webextension/src/popup/Application.tsx 
b/packages/taler-wallet-webextension/src/popup/Application.tsx
index cbb9b50b2..8c732728e 100644
--- a/packages/taler-wallet-webextension/src/popup/Application.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Application.tsx
@@ -20,21 +20,25 @@
  * @author sebasjm
  */
 
+import {
+  ScopeInfo,
+  stringifyScopeInfoShort
+} from "@gnu-taler/taler-util";
 import {
   TranslationProvider,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
 import { createHashHistory } from "history";
-import { ComponentChildren, Fragment, h, VNode } from "preact";
-import { route, Route, Router } from "preact-router";
+import { ComponentChildren, Fragment, VNode, h } from "preact";
+import { Route, Router, route } from "preact-router";
 import { useEffect, useState } from "preact/hooks";
+import { Pages, PopupNavBar, PopupNavBarOptions } from "../NavigationBar.js";
 import PendingTransactions from "../components/PendingTransactions.js";
 import { PopupBox } from "../components/styled/index.js";
 import { AlertProvider } from "../context/alert.js";
 import { IoCProviderForRuntime } from "../context/iocContext.js";
 import { useTalerActionURL } from "../hooks/useTalerActionURL.js";
 import { strings } from "../i18n/strings.js";
-import { Pages, PopupNavBar, PopupNavBarOptions } from "../NavigationBar.js";
 import { platform } from "../platform/foreground.js";
 import { BackupPage } from "../wallet/BackupPage.js";
 import { ProviderDetailPage } from "../wallet/ProviderDetailPage.js";
@@ -68,7 +72,7 @@ function ApplicationView(): VNode {
   }
 
   function redirectToURL(str: string): void {
-    platform.openNewURLFromPopup(new URL(str))
+    platform.openNewURLFromPopup(new URL(str));
   }
 
   return (
@@ -76,14 +80,26 @@ function ApplicationView(): VNode {
       <Route
         path={Pages.balance}
         component={() => (
-          <PopupTemplate path="balance" goToTransaction={redirectToTxInfo} 
goToURL={redirectToURL}>
+          <PopupTemplate
+            path="balance"
+            goToTransaction={redirectToTxInfo}
+            goToURL={redirectToURL}
+          >
             <BalancePage
               goToWalletManualWithdraw={() => 
redirectTo(Pages.receiveCash({}))}
-              goToWalletDeposit={(currency: string) =>
-                redirectTo(Pages.sendCash({ amount: `${currency}:0` }))
+              goToWalletDeposit={(scope: ScopeInfo) =>
+                redirectTo(
+                  Pages.sendCash({
+                    scope: encodeURIComponent(stringifyScopeInfoShort(scope)),
+                  }),
+                )
               }
-              goToWalletHistory={(currency: string) =>
-                redirectTo(Pages.balanceHistory({ currency }))
+              goToWalletHistory={(scope: ScopeInfo) =>
+                redirectTo(
+                  Pages.balanceHistory({
+                    scope: encodeURIComponent(stringifyScopeInfoShort(scope)),
+                  }),
+                )
               }
             />
           </PopupTemplate>
@@ -112,7 +128,11 @@ function ApplicationView(): VNode {
       <Route
         path={Pages.backup}
         component={() => (
-          <PopupTemplate path="backup" goToTransaction={redirectToTxInfo} 
goToURL={redirectToURL}>
+          <PopupTemplate
+            path="backup"
+            goToTransaction={redirectToTxInfo}
+            goToURL={redirectToURL}
+          >
             <BackupPage
               onAddProvider={() => redirectTo(Pages.backupProviderAdd)}
             />
@@ -127,9 +147,9 @@ function ApplicationView(): VNode {
               onPayProvider={(uri: string) =>
                 redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`)
               }
-              onWithdraw={(amount: string) =>
-                redirectTo(Pages.receiveCash({ amount }))
-              }
+              onWithdraw={async (_amount: string) => {
+                // redirectTo(Pages.receiveCash({ amount }))
+              }}
               pid={pid}
               onBack={() => redirectTo(Pages.backup)}
             />
@@ -219,7 +239,10 @@ function PopupTemplate({
 }): VNode {
   return (
     <Fragment>
-      <PendingTransactions goToTransaction={goToTransaction} goToURL={goToURL} 
/>
+      <PendingTransactions
+        goToTransaction={goToTransaction}
+        goToURL={goToURL}
+      />
       <PopupNavBar path={path} />
       <PopupBox>
         <AlertProvider>{children}</AlertProvider>
diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx 
b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
index 73bd8e96d..a819d50e7 100644
--- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
@@ -17,6 +17,7 @@
 import {
   Amounts,
   NotificationType,
+  ScopeInfo,
   WalletBalance,
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
@@ -41,8 +42,8 @@ import { AddNewActionView } from 
"../wallet/AddNewActionView.js";
 import { NoBalanceHelp } from "./NoBalanceHelp.js";
 
 export interface Props {
-  goToWalletDeposit: (currency: string) => Promise<void>;
-  goToWalletHistory: (currency: string) => Promise<void>;
+  goToWalletDeposit: (currency: ScopeInfo) => Promise<void>;
+  goToWalletHistory: (currency: ScopeInfo) => Promise<void>;
   goToWalletManualWithdraw: () => Promise<void>;
 }
 
@@ -70,8 +71,8 @@ export namespace State {
     error: undefined;
     balances: WalletBalance[];
     addAction: ButtonHandler;
-    goToWalletDeposit: (currency: string) => Promise<void>;
-    goToWalletHistory: (currency: string) => Promise<void>;
+    goToWalletDeposit: (currency: ScopeInfo) => Promise<void>;
+    goToWalletHistory: (currency: ScopeInfo) => Promise<void>;
     goToWalletManualWithdraw: ButtonHandler;
   }
 }
@@ -156,7 +157,7 @@ export function BalanceView(state: State.Balances): VNode {
     .filter((b) => !Amounts.isZero(b.available))
     .map((b) => {
       b.flags
-      return b.available.split(":")[0]
+      return b.scopeInfo
     });
 
   if (state.balances.length === 0) {
@@ -184,7 +185,7 @@ export function BalanceView(state: State.Balances): VNode {
         </Button>
         {currencyWithNonZeroAmount.length > 0 && (
           <MultiActionButton
-            label={(s) => i18n.str`Send ${s}`}
+            label={(s) => i18n.str`Send ${s.currency}`}
             actions={currencyWithNonZeroAmount}
             onClick={(c) => state.goToWalletDeposit(c)}
           />
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx 
b/packages/taler-wallet-webextension/src/wallet/Application.tsx
index 893122c0f..4947eb7a9 100644
--- a/packages/taler-wallet-webextension/src/wallet/Application.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -22,10 +22,13 @@
 
 import {
   Amounts,
+  ScopeInfo,
   TalerUri,
   TalerUriAction,
   TranslatedString,
+  parseScopeInfoShort,
   parseTalerUri,
+  stringifyScopeInfoShort,
   stringifyTalerUri,
 } from "@gnu-taler/taler-util";
 import {
@@ -96,7 +99,7 @@ export function Application(): VNode {
     redirectTo(Pages.balanceTransaction({ tid }));
   }
   function redirectToURL(str: string): void {
-    window.location.href = new URL(str).href
+    window.location.href = new URL(str).href;
   }
 
   return (
@@ -115,7 +118,10 @@ export function Application(): VNode {
           <Route
             path={Pages.qr}
             component={() => (
-              <WalletTemplate goToTransaction={redirectToTxInfo} 
goToURL={redirectToURL}>
+              <WalletTemplate
+                goToTransaction={redirectToTxInfo}
+                goToURL={redirectToURL}
+              >
                 <QrReaderPage
                   onDetected={(talerActionUrl: TalerUri) => {
                     redirectTo(
@@ -132,7 +138,10 @@ export function Application(): VNode {
           <Route
             path={Pages.settings}
             component={() => (
-              <WalletTemplate goToTransaction={redirectToTxInfo} 
goToURL={redirectToURL}>
+              <WalletTemplate
+                goToTransaction={redirectToTxInfo}
+                goToURL={redirectToURL}
+              >
                 <SettingsPage />
               </WalletTemplate>
             )}
@@ -159,17 +168,33 @@ export function Application(): VNode {
 
           <Route
             path={Pages.balanceHistory.pattern}
-            component={({ currency }: { currency?: string }) => (
-              <WalletTemplate path="balance" 
goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+            component={({ scope }: { scope?: string }) => (
+              <WalletTemplate
+                path="balance"
+                goToTransaction={redirectToTxInfo}
+                goToURL={redirectToURL}
+              >
                 <HistoryPage
-                  currency={currency}
-                  goToWalletDeposit={(currency: string) =>
-                    redirectTo(Pages.sendCash({ amount: `${currency}:0` }))
+                  scope={
+                    !scope
+                      ? undefined
+                      : parseScopeInfoShort((scope))
                   }
-                  goToWalletManualWithdraw={(currency?: string) =>
+                  goToWalletDeposit={(scope: ScopeInfo) =>
+                    redirectTo(
+                      Pages.sendCash({
+                        scope: encodeURIComponent(
+                          stringifyScopeInfoShort(scope),
+                        ),
+                      }),
+                    )
+                  }
+                  goToWalletManualWithdraw={(scope?: ScopeInfo) =>
                     redirectTo(
                       Pages.receiveCash({
-                        amount: !currency ? undefined : `${currency}:0`,
+                        scope: !scope
+                          ? undefined
+                          : encodeURIComponent(stringifyScopeInfoShort(scope)),
                       }),
                     )
                   }
@@ -179,18 +204,34 @@ export function Application(): VNode {
           />
           <Route
             path={Pages.searchHistory.pattern}
-            component={({ currency }: { currency?: string }) => (
-              <WalletTemplate path="balance" 
goToTransaction={redirectToTxInfo} goToURL={redirectToURL}>
+            component={({ scope }: { scope?: string }) => (
+              <WalletTemplate
+                path="balance"
+                goToTransaction={redirectToTxInfo}
+                goToURL={redirectToURL}
+              >
                 <HistoryPage
-                  currency={currency}
+                  scope={
+                    !scope
+                      ? undefined
+                      : parseScopeInfoShort((scope))
+                  }
                   search
-                  goToWalletDeposit={(currency: string) =>
-                    redirectTo(Pages.sendCash({ amount: `${currency}:0` }))
+                  goToWalletDeposit={(scope: ScopeInfo) =>
+                    redirectTo(
+                      Pages.sendCash({
+                        scope: encodeURIComponent(
+                          stringifyScopeInfoShort(scope),
+                        ),
+                      }),
+                    )
                   }
-                  goToWalletManualWithdraw={(currency?: string) =>
+                  goToWalletManualWithdraw={(scope?: ScopeInfo) =>
                     redirectTo(
                       Pages.receiveCash({
-                        amount: !currency ? undefined : `${currency}:0`,
+                        scope: !scope
+                          ? undefined
+                          : encodeURIComponent(stringifyScopeInfoShort(scope)),
                       }),
                     )
                   }
@@ -239,8 +280,14 @@ export function Application(): VNode {
               <WalletTemplate path="balance" goToURL={redirectToURL}>
                 <TransactionPage
                   tid={tid}
-                  goToWalletHistory={(currency?: string) =>
-                    redirectTo(Pages.balanceHistory({ currency }))
+                  goToWalletHistory={(scope: ScopeInfo) =>
+                    redirectTo(
+                      Pages.balanceHistory({
+                        scope: encodeURIComponent(
+                          stringifyScopeInfoShort(scope),
+                        ),
+                      }),
+                    )
                   }
                 />
               </WalletTemplate>
@@ -249,15 +296,22 @@ export function Application(): VNode {
 
           <Route
             path={Pages.balanceDeposit.pattern}
-            component={({ amount }: { amount: string }) => (
+            component={({
+              scope,
+              amount,
+            }: {
+              scope: string;
+              amount?: string;
+            }) => (
               <WalletTemplate path="balance" goToURL={redirectToURL}>
                 <DepositPage
-                  amount={amount}
-                  onCancel={(currency: string) => {
-                    redirectTo(Pages.balanceHistory({ currency }));
+                  scope={parseScopeInfoShort((scope))}
+                  amount={!amount ? undefined : Amounts.parse(amount)}
+                  onCancel={(scope: ScopeInfo) => {
+                    redirectTo(Pages.balanceHistory({ scope: 
encodeURIComponent(stringifyScopeInfoShort(scope)) }));
                   }}
-                  onSuccess={(currency: string) => {
-                    redirectTo(Pages.balanceHistory({ currency }));
+                  onSuccess={(scope: ScopeInfo) => {
+                    redirectTo(Pages.balanceHistory({ scope: 
encodeURIComponent(stringifyScopeInfoShort(scope)) }));
                   }}
                 />
               </WalletTemplate>
@@ -267,7 +321,11 @@ export function Application(): VNode {
           <Route
             path={Pages.backup}
             component={() => (
-              <WalletTemplate path="backup" goToTransaction={redirectToTxInfo} 
goToURL={redirectToURL}>
+              <WalletTemplate
+                path="backup"
+                goToTransaction={redirectToTxInfo}
+                goToURL={redirectToURL}
+              >
                 <BackupPage
                   onAddProvider={() => redirectTo(Pages.backupProviderAdd)}
                 />
@@ -283,8 +341,8 @@ export function Application(): VNode {
                   onPayProvider={(uri: string) =>
                     redirectTo(`${Pages.ctaPay}?talerPayUri=${uri}`)
                   }
-                  onWithdraw={(amount: string) =>
-                    redirectTo(Pages.receiveCash({ amount }))
+                  onWithdraw={(_amount: string) =>
+                    redirectTo(Pages.receiveCash({ scope: "FIXME missing" }))
                   }
                   onBack={() => redirectTo(Pages.backup)}
                 />
@@ -314,7 +372,11 @@ export function Application(): VNode {
           <Route
             path={Pages.dev}
             component={() => (
-              <WalletTemplate path="dev" goToTransaction={redirectToTxInfo} 
goToURL={redirectToURL}>
+              <WalletTemplate
+                path="dev"
+                goToTransaction={redirectToTxInfo}
+                goToURL={redirectToURL}
+              >
                 <DeveloperPage />
               </WalletTemplate>
             )}
@@ -349,8 +411,8 @@ export function Application(): VNode {
               <CallToActionTemplate title={i18n.str`Digital cash payment`}>
                 <PaymentPage
                   talerPayUri={decodeURIComponent(talerUri)}
-                  goToWalletManualWithdraw={(amount?: string) =>
-                    redirectTo(Pages.receiveCash({ amount }))
+                  goToWalletManualWithdraw={(_amount?: string) =>
+                    redirectTo(Pages.receiveCash({ scope: "FIXME missing" }))
                   }
                   cancel={() => redirectTo(Pages.balance)}
                   onSuccess={(tid: string) =>
@@ -366,8 +428,8 @@ export function Application(): VNode {
               <CallToActionTemplate title={i18n.str`Digital cash payment`}>
                 <PaymentTemplatePage
                   talerTemplateUri={decodeURIComponent(talerUri)}
-                  goToWalletManualWithdraw={(amount?: string) =>
-                    redirectTo(Pages.receiveCash({ amount }))
+                  goToWalletManualWithdraw={(_amount?: string) =>
+                    redirectTo(Pages.receiveCash({ scope: "FIXME missing" }))
                   }
                   cancel={() => redirectTo(Pages.balance)}
                   onSuccess={(tid: string) =>
@@ -417,7 +479,9 @@ export function Application(): VNode {
               <CallToActionTemplate title={i18n.str`Digital cash withdrawal`}>
                 <WithdrawPageFromParams
                   onAmountChanged={async (newamount) => {
-                    const page = `${Pages.ctaWithdrawManual({ amount: 
newamount })}?talerUri=${encodeURIComponent(talerUri)}`;
+                    const page = `${Pages.ctaWithdrawManual({
+                      amount: newamount,
+                    })}?talerUri=${encodeURIComponent(talerUri)}`;
                     redirectTo(page);
                   }}
                   talerExchangeWithdrawUri={talerUri}
@@ -485,8 +549,8 @@ export function Application(): VNode {
               <CallToActionTemplate title={i18n.str`Digital cash invoice`}>
                 <InvoicePayPage
                   talerPayPullUri={decodeURIComponent(talerUri)}
-                  goToWalletManualWithdraw={(amount?: string) =>
-                    redirectTo(Pages.receiveCash({ amount }))
+                  goToWalletManualWithdraw={(_amount?: string) =>
+                    redirectTo(Pages.receiveCash({ scope: "FIXME missing" }))
                   }
                   onClose={() => redirectTo(Pages.balance)}
                   onSuccess={(tid: string) =>
@@ -537,23 +601,26 @@ export function Application(): VNode {
           <Route
             path={Pages.ctaAddExchange}
             component={({ talerUri }: { talerUri: string }) => {
-              const tUri = parseTalerUri(decodeURIComponent(talerUri))
-              const baseUrl = tUri?.type === TalerUriAction.AddExchange ? 
tUri.exchangeBaseUrl : undefined
+              const tUri = parseTalerUri(decodeURIComponent(talerUri));
+              const baseUrl =
+                tUri?.type === TalerUriAction.AddExchange
+                  ? tUri.exchangeBaseUrl
+                  : undefined;
               if (!baseUrl) {
-                redirectTo(Pages.balanceHistory({}))
-                return <div>
-                  invalid url {talerUri}
-                </div>
+                redirectTo(Pages.balanceHistory({}));
+                return <div>invalid url {talerUri}</div>;
               }
-              return <CallToActionTemplate title={i18n.str`Add exchange`}>
-                <ConfirmAddExchangeView
-                  url={baseUrl}
-                  status="confirm"
-                  error={undefined}
-                  onCancel={() => redirectTo(Pages.balanceHistory({}))}
-                  onConfirm={() => redirectTo(Pages.balanceHistory({}))}
-                />
-              </CallToActionTemplate>
+              return (
+                <CallToActionTemplate title={i18n.str`Add exchange`}>
+                  <ConfirmAddExchangeView
+                    url={baseUrl}
+                    status="confirm"
+                    error={undefined}
+                    onCancel={() => redirectTo(Pages.balanceHistory({}))}
+                    onConfirm={() => redirectTo(Pages.balanceHistory({}))}
+                  />
+                </CallToActionTemplate>
+              );
             }}
           />
           {/**
@@ -665,7 +732,8 @@ function WalletTemplate({
       <WalletNavBar path={path} />
       <PendingTransactions
         goToTransaction={goToTransaction}
-        goToURL={goToURL} />
+        goToURL={goToURL}
+      />
       <WalletBox>
         <AlertProvider>
           <CurrentAlerts />
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
index daba6aba4..11642861a 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AmountJson, PaytoUri } from "@gnu-taler/taler-util";
+import { AmountJson, PaytoUri, ScopeInfo } from "@gnu-taler/taler-util";
 import { ErrorAlertView } from "../../components/CurrentAlerts.js";
 import { Loading } from "../../components/Loading.js";
 import { ErrorAlert } from "../../context/alert.js";
@@ -34,9 +34,10 @@ import {
 } from "./views.js";
 
 export interface Props {
-  amount?: string;
-  onCancel: (currency: string) => void;
-  onSuccess: (currency: string) => void;
+  scope?:ScopeInfo;
+  amount?: AmountJson;
+  onCancel: (scope: ScopeInfo) => void;
+  onSuccess: (scope: ScopeInfo) => void;
 }
 
 export type State =
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index b674665cf..f45c9ddd5 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -32,14 +32,15 @@ import { RecursiveState } from "../../utils/index.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState({
-  amount: amountStr,
+  amount,
+  scope,
   onCancel,
   onSuccess,
 }: Props): RecursiveState<State> {
   const api = useBackendContext();
   const { i18n } = useTranslationContext();
   const { pushAlertOnError } = useAlertContext();
-  const parsed = amountStr === undefined ? undefined : 
Amounts.parse(amountStr);
+  const parsed = amount;
   const currency = parsed !== undefined ? parsed.currency : undefined;
 
   const hook = useAsyncAsHook(async () => {
@@ -214,7 +215,7 @@ export function useComponentState({
         amount: amountStr,
         depositPaytoUri,
       });
-      onSuccess(currency);
+      onSuccess(scope!);
     }
 
     return {
@@ -250,7 +251,7 @@ export function useComponentState({
       currentAccount,
       cancelHandler: {
         onClick: pushAlertOnError(async () => {
-          onCancel(currency);
+          onCancel(scope!);
         }),
       },
       depositHandler: {
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts 
b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
index 1144095e1..7f22ea0ed 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
@@ -24,6 +24,7 @@ import {
   Amounts,
   AmountString,
   parsePaytoUri,
+  ScopeInfo,
   ScopeType,
   stringifyPaytoUri
 } from "@gnu-taler/taler-util";
@@ -36,21 +37,26 @@ import { createWalletApiMock } from "../../test-utils.js";
 import { useComponentState } from "./state.js";
 
 const currency = "EUR";
-const amount = `${currency}:0`;
+const amount = Amounts.parseOrThrow(`${currency}:0`);
 const withoutFee = (value: number): AmountResponse => ({
   effectiveAmount: `${currency}:${value}` as AmountString,
   rawAmount: `${currency}:${value}` as AmountString,
 });
 
+const defaultScope: ScopeInfo = {
+  type: ScopeType.Global,
+  currency
+}
+
+
 const withSomeFee = (value: number, fee: number): AmountResponse => ({
   effectiveAmount: `${currency}:${value}` as AmountString,
   rawAmount: `${currency}:${value - fee}` as AmountString,
 });
-
 describe("DepositPage states", () => {
   it("should have status 'no-enough-balance' when balance is empty", async () 
=> {
     const { handler, TestingContext } = createWalletApiMock();
-    const props = { amount, onCancel: nullFunction, onSuccess: nullFunction };
+    const props = { scope: defaultScope, amount, onCancel: nullFunction, 
onSuccess: nullFunction };
 
     handler.addWalletCallResponse(WalletApiOperation.GetBalances, undefined, {
       balances: [
diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
index dda10d561..470ad0514 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
@@ -169,7 +169,12 @@ export const SomeBalanceWithNoTransactions = 
tests.createExample(
     transactionsByDate: {
       "11/11/11": [],
     },
-    balances: [
+    scope: {
+      currency: "Ásd",
+      type: ScopeType.Auditor,
+      url: "",
+    },
+      balances: [
       {
         available: "TESTKUDOS:10" as AmountString,
         flags: [],
@@ -184,7 +189,7 @@ export const SomeBalanceWithNoTransactions = 
tests.createExample(
         },
       },
     ],
-    balanceIndex: 0,
+    
   },
 );
 
@@ -192,6 +197,11 @@ export const OneSimpleTransaction = 
tests.createExample(TestedComponent, {
   transactionsByDate: {
     "11/11/11": [exampleData.withdraw],
   },
+  scope: {
+    currency: "Ásd",
+    type: ScopeType.Auditor,
+    url: "",
+  },
   balances: [
     {
       flags: [],
@@ -207,7 +217,7 @@ export const OneSimpleTransaction = 
tests.createExample(TestedComponent, {
       },
     },
   ],
-  balanceIndex: 0,
+  
 });
 
 export const TwoTransactionsAndZeroBalance = tests.createExample(
@@ -216,7 +226,12 @@ export const TwoTransactionsAndZeroBalance = 
tests.createExample(
     transactionsByDate: {
       "11/11/11": [exampleData.withdraw, exampleData.deposit],
     },
-    balances: [
+    scope: {
+      currency: "Ásd",
+      type: ScopeType.Auditor,
+      url: "",
+    },
+      balances: [
       {
         flags: [],
         available: "USD:0" as AmountString,
@@ -231,7 +246,7 @@ export const TwoTransactionsAndZeroBalance = 
tests.createExample(
         },
       },
     ],
-    balanceIndex: 0,
+    
   },
 );
 
@@ -246,6 +261,11 @@ export const OneTransactionPending = 
tests.createExample(TestedComponent, {
       },
     ],
   },
+  scope: {
+    currency: "Ásd",
+    type: ScopeType.Auditor,
+    url: "",
+  },
   balances: [
     {
       flags: [],
@@ -261,7 +281,7 @@ export const OneTransactionPending = 
tests.createExample(TestedComponent, {
       },
     },
   ],
-  balanceIndex: 0,
+  
 });
 
 export const SomeTransactions = tests.createExample(TestedComponent, {
@@ -283,6 +303,11 @@ export const SomeTransactions = 
tests.createExample(TestedComponent, {
       exampleData.deposit,
     ],
   },
+  scope: {
+    currency: "Ásd",
+    type: ScopeType.Auditor,
+    url: "",
+  },
   balances: [
     {
       flags: [],
@@ -298,7 +323,7 @@ export const SomeTransactions = 
tests.createExample(TestedComponent, {
       },
     },
   ],
-  balanceIndex: 0,
+  
 });
 
 export const SomeTransactionsInDifferentStates = tests.createExample(
@@ -379,7 +404,12 @@ export const SomeTransactionsInDifferentStates = 
tests.createExample(
         exampleData.deposit,
       ],
     },
-    balances: [
+    scope: {
+      currency: "Ásd",
+      type: ScopeType.Auditor,
+      url: "",
+    },
+      balances: [
       {
         flags: [],
         available: "USD:10" as AmountString,
@@ -394,7 +424,7 @@ export const SomeTransactionsInDifferentStates = 
tests.createExample(
         },
       },
     ],
-    balanceIndex: 0,
+    
   },
 );
 
@@ -412,7 +442,12 @@ export const SomeTransactionsWithTwoCurrencies = 
tests.createExample(
         exampleData.deposit,
       ],
     },
-    balances: [
+    scope: {
+      currency: "Ásd",
+      type: ScopeType.Auditor,
+      url: "",
+    },
+      balances: [
       {
         flags: [],
         available: "USD:0" as AmountString,
@@ -440,7 +475,7 @@ export const SomeTransactionsWithTwoCurrencies = 
tests.createExample(
         },
       },
     ],
-    balanceIndex: 0,
+    
   },
 );
 
@@ -448,6 +483,11 @@ export const FiveOfficialCurrencies = 
tests.createExample(TestedComponent, {
   transactionsByDate: {
     "11/11/11": [exampleData.withdraw],
   },
+  scope: {
+    currency: "Ásd",
+    type: ScopeType.Auditor,
+    url: "",
+  },
   balances: [
     {
       flags: [],
@@ -515,7 +555,7 @@ export const FiveOfficialCurrencies = 
tests.createExample(TestedComponent, {
       },
     },
   ],
-  balanceIndex: 0,
+  
 });
 
 export const FiveOfficialCurrenciesWithHighValue = tests.createExample(
@@ -524,7 +564,12 @@ export const FiveOfficialCurrenciesWithHighValue = 
tests.createExample(
     transactionsByDate: {
       "11/11/11": [exampleData.withdraw],
     },
-    balances: [
+    scope: {
+      currency: "Ásd",
+      type: ScopeType.Auditor,
+      url: "",
+    },
+      balances: [
       {
         flags: [],
         available: "USD:881001321230000" as AmountString,
@@ -591,7 +636,7 @@ export const FiveOfficialCurrenciesWithHighValue = 
tests.createExample(
         },
       },
     ],
-    balanceIndex: 0,
+    
   },
 );
 
@@ -604,6 +649,11 @@ export const PeerToPeer = 
tests.createExample(TestedComponent, {
       exampleData.push_debit,
     ],
   },
+  scope: {
+    currency: "Ásd",
+    type: ScopeType.Auditor,
+    url: "",
+  },
   balances: [
     {
       flags: [],
@@ -619,5 +669,4 @@ export const PeerToPeer = 
tests.createExample(TestedComponent, {
       },
     },
   ],
-  balanceIndex: 0,
 });
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx 
b/packages/taler-wallet-webextension/src/wallet/History.tsx
index f81e6db9f..59315d61f 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -18,9 +18,11 @@ import {
   AbsoluteTime,
   Amounts,
   NotificationType,
+  ScopeInfo,
   ScopeType,
   Transaction,
   WalletBalance,
+  stringifyScopeInfoShort,
 } from "@gnu-taler/taler-util";
 import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
@@ -49,35 +51,36 @@ import { TextField } from "../mui/TextField.js";
 import { TextFieldHandler } from "../mui/handlers.js";
 
 interface Props {
-  currency?: string;
+  scope?: ScopeInfo;
   search?: boolean;
-  goToWalletDeposit: (currency: string) => Promise<void>;
-  goToWalletManualWithdraw: (currency?: string) => Promise<void>;
+  goToWalletDeposit: (scope: ScopeInfo) => Promise<void>;
+  goToWalletManualWithdraw: (scope?: ScopeInfo) => Promise<void>;
 }
 export function HistoryPage({
-  currency: _c,
+  scope,
   search: showSearch,
   goToWalletManualWithdraw,
   goToWalletDeposit,
 }: Props): VNode {
   const { i18n } = useTranslationContext();
   const api = useBackendContext();
-  const [balanceIndex, setBalanceIndex] = useState<number>(0);
+  const [selectedScope, setSelectedScope] = useState(scope);
   const [search, setSearch] = useState<string>();
 
   const [settings] = useSettings();
   const state = useAsyncAsHook(async () => {
     const b = await api.wallet.call(WalletApiOperation.GetBalances, {});
-    const balance =
-      b.balances.length > 0 ? b.balances[balanceIndex] : undefined;
+    const balances = b.balances;
+    // const balance =
+    //   b.balances.length > 0 ? b.balances[balanceIndex] : undefined;
     const tx = await api.wallet.call(WalletApiOperation.GetTransactions, {
-      scopeInfo: showSearch ? undefined : balance?.scopeInfo,
+      scopeInfo: showSearch ? undefined : selectedScope,
       sort: "descending",
       includeRefreshes: settings.showRefeshTransactions,
       search,
     });
-    return { b, tx };
-  }, [balanceIndex, search]);
+    return { balances, transactions: tx.transactions };
+  }, [selectedScope, search]);
 
   useEffect(() => {
     return api.listener.onUpdateNotification(
@@ -103,7 +106,7 @@ export function HistoryPage({
     );
   }
 
-  if (!state.response.b.balances.length) {
+  if (!state.response.balances.length) {
     return (
       <NoBalanceHelp
         goToWalletManualWithdraw={{
@@ -113,7 +116,7 @@ export function HistoryPage({
     );
   }
 
-  const byDate = state.response.tx.transactions.reduce(
+  const txsByDate = state.response.transactions.reduce(
     (rv, x) => {
       const startDay =
         x.timestamp.t_s === "never"
@@ -141,41 +144,48 @@ export function HistoryPage({
             setSearch(d);
           }),
         }}
-        transactionsByDate={byDate}
+        transactionsByDate={txsByDate}
       />
     );
   }
 
   return (
     <HistoryView
-      balanceIndex={balanceIndex}
-      changeBalanceIndex={(b) => setBalanceIndex(b)}
-      balances={state.response.b.balances}
+      scope={selectedScope ?? state.response.balances[0].scopeInfo}
+      changeScope={(b) => setSelectedScope(b)}
+      balances={state.response.balances}
       goToWalletManualWithdraw={goToWalletManualWithdraw}
       goToWalletDeposit={goToWalletDeposit}
-      transactionsByDate={byDate}
+      transactionsByDate={txsByDate}
     />
   );
 }
 
 export function HistoryView({
   balances,
-  balanceIndex,
-  changeBalanceIndex,
+  scope,
+  changeScope,
   transactionsByDate,
   goToWalletManualWithdraw,
   goToWalletDeposit,
 }: {
-  balanceIndex: number;
-  changeBalanceIndex: (s: number) => void;
-  goToWalletDeposit: (currency: string) => Promise<void>;
-  goToWalletManualWithdraw: (currency?: string) => Promise<void>;
+  scope: ScopeInfo;
+  changeScope: (scope: ScopeInfo) => void;
+  goToWalletDeposit: (scope: ScopeInfo) => Promise<void>;
+  goToWalletManualWithdraw: (scope?: ScopeInfo) => Promise<void>;
   transactionsByDate: Record<string, Transaction[]>;
   balances: WalletBalance[];
 }): VNode {
   const { i18n } = useTranslationContext();
 
+  const scopeStr = stringifyScopeInfoShort(scope);
+  const balanceIndex = balances.findIndex(
+    (b) => stringifyScopeInfoShort(b.scopeInfo) === scopeStr,
+  );
   const balance = balances[balanceIndex];
+  if (!balance) {
+    return <div>unkown scope</div>;
+  }
 
   const available = balance
     ? Amounts.jsonifyAmount(balance.available)
@@ -200,9 +210,7 @@ export function HistoryView({
               tooltip="Transfer money to the wallet"
               startIcon={DownloadIcon}
               variant="contained"
-              onClick={() =>
-                goToWalletManualWithdraw(balance.scopeInfo.currency)
-              }
+              onClick={() => goToWalletManualWithdraw(balance.scopeInfo)}
             >
               <i18n.Translate>Receive</i18n.Translate>
             </Button>
@@ -212,7 +220,7 @@ export function HistoryView({
                 startIcon={UploadIcon}
                 variant="outlined"
                 color="primary"
-                onClick={() => goToWalletDeposit(balance.scopeInfo.currency)}
+                onClick={() => goToWalletDeposit(balance.scopeInfo)}
               >
                 <i18n.Translate>Send</i18n.Translate>
               </Button>
@@ -238,9 +246,8 @@ export function HistoryView({
                     }}
                     value={balanceIndex}
                     onChange={(e) => {
-                      changeBalanceIndex(
-                        Number.parseInt(e.currentTarget.value, 10),
-                      );
+                      const bIdx = Number.parseInt(e.currentTarget.value, 10);
+                      changeScope(balances[bIdx].scopeInfo);
                     }}
                   >
                     {balances.map((entry, index) => {
diff --git 
a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
index 5e096bf97..e45bfc1b1 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
@@ -61,6 +61,7 @@ export default {
 const commonTransaction: TransactionCommon = {
   error: undefined,
   amountRaw: "KUDOS:11" as AmountString,
+  scopes: [],
   amountEffective: "KUDOS:9.2" as AmountString,
   txState: {
     major: TransactionMajorState.Done,
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx 
b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 339ded173..3bd6a9fba 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -25,6 +25,8 @@ import {
   OrderShortInfo,
   parsePaytoUri,
   PaytoUri,
+  ScopeInfo,
+  ScopeType,
   stringifyPaytoUri,
   TalerErrorCode,
   TalerPreciseTimestamp,
@@ -78,7 +80,7 @@ import { assertUnreachable } from "../utils/index.js";
 
 interface Props {
   tid: string;
-  goToWalletHistory: (currency?: string) => Promise<void>;
+  goToWalletHistory: (scope: ScopeInfo) => Promise<void>;
 }
 
 export function TransactionPage({ tid, goToWalletHistory }: Props): VNode {
@@ -116,7 +118,11 @@ export function TransactionPage({ tid, goToWalletHistory 
}: Props): VNode {
     );
   }
 
-  const currency = Amounts.parse(state.response.amountRaw)?.currency;
+  const currency = Amounts.parse(state.response.amountEffective)!.currency;
+  const txScope = !state.response.scopes.length ? {
+    type: ScopeType.Global as const,
+    currency,
+  } : state.response.scopes[0]
 
   return (
     <TransactionView
@@ -125,44 +131,44 @@ export function TransactionPage({ tid, goToWalletHistory 
}: Props): VNode {
         await api.wallet.call(WalletApiOperation.FailTransaction, {
           transactionId,
         });
-        goToWalletHistory(currency);
+        // goToWalletHistory(txScope);
       }}
       onSuspend={async () => {
         await api.wallet.call(WalletApiOperation.SuspendTransaction, {
           transactionId,
         });
-        goToWalletHistory(currency);
+        // goToWalletHistory(txScope);
       }}
       onResume={async () => {
         await api.wallet.call(WalletApiOperation.ResumeTransaction, {
           transactionId,
         });
-        goToWalletHistory(currency);
+        // goToWalletHistory(txScope);
       }}
       onAbort={async () => {
         await api.wallet.call(WalletApiOperation.AbortTransaction, {
           transactionId,
         });
-        goToWalletHistory(currency);
+        // goToWalletHistory(txScope);
       }}
       onRetry={async () => {
         await api.wallet.call(WalletApiOperation.RetryTransaction, {
           transactionId,
         });
-        goToWalletHistory(currency);
+        // goToWalletHistory(txScope);
       }}
       onDelete={async () => {
         await api.wallet.call(WalletApiOperation.DeleteTransaction, {
           transactionId,
         });
-        goToWalletHistory(currency);
+        goToWalletHistory(txScope);
       }}
       onRefund={async (transactionId) => {
         await api.wallet.call(WalletApiOperation.StartRefundQuery, {
           transactionId,
         });
       }}
-      onBack={() => goToWalletHistory(currency)}
+      onBack={() => goToWalletHistory(txScope)}
     />
   );
 }

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