summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-12-23 15:17:36 -0300
committerSebastian <sebasjm@gmail.com>2021-12-23 15:17:36 -0300
commit2e71117f59e0ae6106930e705ae6a54a9839281b (patch)
treea39856486a2801f56c65de245c871ce596f8ab16 /packages
parentb8200de6f6c5ab9be3ff9f556c8acda013e574c3 (diff)
downloadwallet-core-2e71117f59e0ae6106930e705ae6a54a9839281b.tar.gz
wallet-core-2e71117f59e0ae6106930e705ae6a54a9839281b.tar.bz2
wallet-core-2e71117f59e0ae6106930e705ae6a54a9839281b.zip
deposit from wallet webex: wip
Diffstat (limited to 'packages')
-rw-r--r--packages/taler-util/src/walletTypes.ts28
-rw-r--r--packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts4
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts250
-rw-r--r--packages/taler-wallet-core/src/operations/pay.ts63
-rw-r--r--packages/taler-wallet-core/src/wallet-api-types.ts5
-rw-r--r--packages/taler-wallet-core/src/wallet.ts39
-rw-r--r--packages/taler-wallet-webextension/src/NavigationBar.tsx1
-rw-r--r--packages/taler-wallet-webextension/src/components/BalanceTable.tsx18
-rw-r--r--packages/taler-wallet-webextension/src/components/styled/index.tsx4
-rw-r--r--packages/taler-wallet-webextension/src/popup/BalancePage.tsx24
-rw-r--r--packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx68
-rw-r--r--packages/taler-wallet-webextension/src/popupEntryPoint.tsx4
-rw-r--r--packages/taler-wallet-webextension/src/wallet/BalancePage.tsx42
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage.stories.tsx52
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage.tsx234
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx10
-rw-r--r--packages/taler-wallet-webextension/src/walletEntryPoint.tsx8
-rw-r--r--packages/taler-wallet-webextension/src/wxApi.ts23
18 files changed, 758 insertions, 119 deletions
diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts
index ced30e4db..4158dde9e 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -54,6 +54,7 @@ import {
} from "./talerTypes.js";
import { OrderShortInfo, codecForOrderShortInfo } from "./transactionsTypes.js";
import { BackupRecovery } from "./backupTypes.js";
+import { PaytoUri } from "./payto.js";
/**
* Response for the create reserve request to the wallet.
@@ -525,6 +526,10 @@ export interface ExchangesListRespose {
exchanges: ExchangeListItem[];
}
+export interface KnownBankAccounts {
+ accounts: PaytoUri[];
+}
+
export interface ExchangeTos {
acceptedVersion?: string;
currentVersion?: string;
@@ -737,12 +742,19 @@ export const codecForApplyRefundRequest = (): Codec<ApplyRefundRequest> =>
export interface GetWithdrawalDetailsForUriRequest {
talerWithdrawUri: string;
}
-
export const codecForGetWithdrawalDetailsForUri = (): Codec<GetWithdrawalDetailsForUriRequest> =>
buildCodecForObject<GetWithdrawalDetailsForUriRequest>()
.property("talerWithdrawUri", codecForString())
.build("GetWithdrawalDetailsForUriRequest");
+export interface ListKnownBankAccountsRequest {
+ currency?: string;
+}
+export const codecForListKnownBankAccounts = (): Codec<ListKnownBankAccountsRequest> =>
+ buildCodecForObject<ListKnownBankAccountsRequest>()
+ .property("currency", codecOptional(codecForString()))
+ .build("ListKnownBankAccountsRequest");
+
export interface GetExchangeWithdrawalInfo {
exchangeBaseUrl: string;
amount: AmountJson;
@@ -965,11 +977,23 @@ export const codecForAbortPayWithRefundRequest = (): Codec<AbortPayWithRefundReq
.property("proposalId", codecForString())
.build("AbortPayWithRefundRequest");
+export interface GetFeeForDepositRequest {
+ depositPaytoUri: string;
+ amount: AmountString;
+}
+
export interface CreateDepositGroupRequest {
depositPaytoUri: string;
- amount: string;
+ amount: AmountString;
}
+
+export const codecForGetFeeForDeposit = (): Codec<GetFeeForDepositRequest> =>
+ buildCodecForObject<GetFeeForDepositRequest>()
+ .property("amount", codecForAmountString())
+ .property("depositPaytoUri", codecForString())
+ .build("GetFeeForDepositRequest");
+
export const codecForCreateDepositGroupRequest = (): Codec<CreateDepositGroupRequest> =>
buildCodecForObject<CreateDepositGroupRequest>()
.property("amount", codecForAmountString())
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index 04bc2d9bc..b5987582a 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -162,7 +162,7 @@ async function myEddsaSign(
export class CryptoImplementation {
static enableTracing = false;
- constructor(private primitiveWorker?: PrimitiveWorker) {}
+ constructor(private primitiveWorker?: PrimitiveWorker) { }
/**
* Create a pre-coin of the given denomination to be withdrawn from then given
@@ -369,7 +369,7 @@ export class CryptoImplementation {
sig: string,
masterPub: string,
): boolean {
- if (versionCurrent === 10) {
+ if (versionCurrent === 10 || versionCurrent === 11) {
const paytoHash = hash(stringToBytes(paytoUri + "\0"));
const p = buildSigPS(TalerSignaturePurpose.MASTER_WIRE_DETAILS)
.put(paytoHash)
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts
index f90172a45..6d28c23e5 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -15,6 +15,7 @@
*/
import {
+ AmountJson,
Amounts,
buildCodecForObject,
canonicalJson,
@@ -28,6 +29,7 @@ import {
decodeCrock,
DenomKeyType,
durationFromSpec,
+ GetFeeForDepositRequest,
getTimestampNow,
Logger,
NotificationType,
@@ -35,6 +37,7 @@ import {
TalerErrorDetails,
Timestamp,
timestampAddDuration,
+ timestampIsBetween,
timestampTruncateToSecond,
TrackDepositGroupRequest,
TrackDepositGroupResponse,
@@ -49,7 +52,7 @@ import {
} from "@gnu-taler/taler-util";
import { DepositGroupRecord } from "../db.js";
import { guardOperationException } from "../errors.js";
-import { selectPayCoins } from "../util/coinSelection.js";
+import { PayCoinSelection, selectPayCoins } from "../util/coinSelection.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries.js";
import { getExchangeDetails } from "./exchanges.js";
@@ -58,11 +61,11 @@ import {
extractContractData,
generateDepositPermissions,
getCandidatePayCoins,
- getEffectiveDepositAmount,
getTotalPaymentCost,
hashWire,
hashWireLegacy,
} from "./pay.js";
+import { getTotalRefreshCost } from "./refresh.js";
/**
* Logger.
@@ -342,6 +345,100 @@ export async function trackDepositGroup(
};
}
+export async function getFeeForDeposit(
+ ws: InternalWalletState,
+ req: GetFeeForDepositRequest,
+): Promise<DepositFee> {
+ const p = parsePaytoUri(req.depositPaytoUri);
+ if (!p) {
+ throw Error("invalid payto URI");
+ }
+
+ const amount = Amounts.parseOrThrow(req.amount);
+
+ const exchangeInfos: { url: string; master_pub: string }[] = [];
+
+ await ws.db
+ .mktx((x) => ({
+ exchanges: x.exchanges,
+ exchangeDetails: x.exchangeDetails,
+ }))
+ .runReadOnly(async (tx) => {
+ const allExchanges = await tx.exchanges.iter().toArray();
+ for (const e of allExchanges) {
+ const details = await getExchangeDetails(tx, e.baseUrl);
+ if (!details) {
+ continue;
+ }
+ exchangeInfos.push({
+ master_pub: details.masterPublicKey,
+ url: e.baseUrl,
+ });
+ }
+ });
+
+ const timestamp = getTimestampNow();
+ const timestampRound = timestampTruncateToSecond(timestamp);
+ // const noncePair = await ws.cryptoApi.createEddsaKeypair();
+ // const merchantPair = await ws.cryptoApi.createEddsaKeypair();
+ // const wireSalt = encodeCrock(getRandomBytes(16));
+ // const wireHash = hashWire(req.depositPaytoUri, wireSalt);
+ // const wireHashLegacy = hashWireLegacy(req.depositPaytoUri, wireSalt);
+ const contractTerms: ContractTerms = {
+ auditors: [],
+ exchanges: exchangeInfos,
+ amount: req.amount,
+ max_fee: Amounts.stringify(amount),
+ max_wire_fee: Amounts.stringify(amount),
+ wire_method: p.targetType,
+ timestamp: timestampRound,
+ merchant_base_url: "",
+ summary: "",
+ nonce: "",
+ wire_transfer_deadline: timestampRound,
+ order_id: "",
+ h_wire: "",
+ pay_deadline: timestampAddDuration(
+ timestampRound,
+ durationFromSpec({ hours: 1 }),
+ ),
+ merchant: {
+ name: "",
+ },
+ merchant_pub: "",
+ refund_deadline: { t_ms: 0 },
+ };
+
+ const contractData = extractContractData(
+ contractTerms,
+ "",
+ "",
+ );
+
+ const candidates = await getCandidatePayCoins(ws, contractData);
+
+ const payCoinSel = selectPayCoins({
+ candidates,
+ contractTermsAmount: contractData.amount,
+ depositFeeLimit: contractData.maxDepositFee,
+ wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
+ wireFeeLimit: contractData.maxWireFee,
+ prevPayCoins: [],
+ });
+
+ if (!payCoinSel) {
+ throw Error("insufficient funds");
+ }
+
+ return await getTotalFeeForDepositAmount(
+ ws,
+ p.targetType,
+ amount,
+ payCoinSel,
+ );
+
+}
+
export async function createDepositGroup(
ws: InternalWalletState,
req: CreateDepositGroupRequest,
@@ -495,3 +592,152 @@ export async function createDepositGroup(
return { depositGroupId };
}
+
+/**
+ * Get the amount that will be deposited on the merchant's bank
+ * account, not considering aggregation.
+ */
+export async function getEffectiveDepositAmount(
+ ws: InternalWalletState,
+ wireType: string,
+ pcs: PayCoinSelection,
+): Promise<AmountJson> {
+ const amt: AmountJson[] = [];
+ const fees: AmountJson[] = [];
+ const exchangeSet: Set<string> = new Set();
+
+ await ws.db
+ .mktx((x) => ({
+ coins: x.coins,
+ denominations: x.denominations,
+ exchanges: x.exchanges,
+ exchangeDetails: x.exchangeDetails,
+ }))
+ .runReadOnly(async (tx) => {
+ for (let i = 0; i < pcs.coinPubs.length; i++) {
+ const coin = await tx.coins.get(pcs.coinPubs[i]);
+ if (!coin) {
+ throw Error("can't calculate deposit amount, coin not found");
+ }
+ const denom = await tx.denominations.get([
+ coin.exchangeBaseUrl,
+ coin.denomPubHash,
+ ]);
+ if (!denom) {
+ throw Error("can't find denomination to calculate deposit amount");
+ }
+ amt.push(pcs.coinContributions[i]);
+ fees.push(denom.feeDeposit);
+ exchangeSet.add(coin.exchangeBaseUrl);
+ }
+
+ for (const exchangeUrl of exchangeSet.values()) {
+ const exchangeDetails = await getExchangeDetails(tx, exchangeUrl);
+ if (!exchangeDetails) {
+ continue;
+ }
+
+ // FIXME/NOTE: the line below _likely_ throws exception
+ // about "find method not found on undefined" when the wireType
+ // is not supported by the Exchange.
+ const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => {
+ return timestampIsBetween(
+ getTimestampNow(),
+ x.startStamp,
+ x.endStamp,
+ );
+ })?.wireFee;
+ if (fee) {
+ fees.push(fee);
+ }
+ }
+ });
+ return Amounts.sub(Amounts.sum(amt).amount, Amounts.sum(fees).amount).amount;
+}
+
+export interface DepositFee {
+ coin: AmountJson;
+ wire: AmountJson;
+ refresh: AmountJson;
+}
+
+/**
+ * Get the fee amount that will be charged when trying to deposit the
+ * specified amount using the selected coins and the wire method.
+ */
+export async function getTotalFeeForDepositAmount(
+ ws: InternalWalletState,
+ wireType: string,
+ total: AmountJson,
+ pcs: PayCoinSelection,
+): Promise<DepositFee> {
+ const wireFee: AmountJson[] = [];
+ const coinFee: AmountJson[] = [];
+ const refreshFee: AmountJson[] = [];
+ const exchangeSet: Set<string> = new Set();
+
+ // let acc: AmountJson = Amounts.getZero(total.currency);
+
+ await ws.db
+ .mktx((x) => ({
+ coins: x.coins,
+ denominations: x.denominations,
+ exchanges: x.exchanges,
+ exchangeDetails: x.exchangeDetails,
+ }))
+ .runReadOnly(async (tx) => {
+ for (let i = 0; i < pcs.coinPubs.length; i++) {
+ const coin = await tx.coins.get(pcs.coinPubs[i]);
+ if (!coin) {
+ throw Error("can't calculate deposit amount, coin not found");
+ }
+ const denom = await tx.denominations.get([
+ coin.exchangeBaseUrl,
+ coin.denomPubHash,
+ ]);
+ if (!denom) {
+ throw Error("can't find denomination to calculate deposit amount");
+ }
+ // const cc = pcs.coinContributions[i]
+ // acc = Amounts.add(acc, cc).amount
+ coinFee.push(denom.feeDeposit);
+ exchangeSet.add(coin.exchangeBaseUrl);
+
+ const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
+ .iter(coin.exchangeBaseUrl)
+ .filter((x) =>
+ Amounts.isSameCurrency(x.value, pcs.coinContributions[i]),
+ );
+ const amountLeft = Amounts.sub(denom.value, pcs.coinContributions[i])
+ .amount;
+ const refreshCost = getTotalRefreshCost(allDenoms, denom, amountLeft);
+ refreshFee.push(refreshCost);
+ }
+
+ for (const exchangeUrl of exchangeSet.values()) {
+ const exchangeDetails = await getExchangeDetails(tx, exchangeUrl);
+ if (!exchangeDetails) {
+ continue;
+ }
+ // FIXME/NOTE: the line below _likely_ throws exception
+ // about "find method not found on undefined" when the wireType
+ // is not supported by the Exchange.
+ const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => {
+ return timestampIsBetween(
+ getTimestampNow(),
+ x.startStamp,
+ x.endStamp,
+ );
+ })?.wireFee;
+ if (fee) {
+ wireFee.push(fee);
+ }
+ }
+ });
+
+ return {
+ coin: coinFee.length === 0 ? Amounts.getZero(total.currency) : Amounts.sum(coinFee).amount,
+ wire: wireFee.length === 0 ? Amounts.getZero(total.currency) : Amounts.sum(wireFee).amount,
+ refresh: refreshFee.length === 0 ? Amounts.getZero(total.currency) : Amounts.sum(refreshFee).amount
+ };
+}
diff --git a/packages/taler-wallet-core/src/operations/pay.ts b/packages/taler-wallet-core/src/operations/pay.ts
index 63ccc6531..89930120d 100644
--- a/packages/taler-wallet-core/src/operations/pay.ts
+++ b/packages/taler-wallet-core/src/operations/pay.ts
@@ -177,66 +177,6 @@ export async function getTotalPaymentCost(
});
}
-/**
- * Get the amount that will be deposited on the merchant's bank
- * account, not considering aggregation.
- */
-export async function getEffectiveDepositAmount(
- ws: InternalWalletState,
- wireType: string,
- pcs: PayCoinSelection,
-): Promise<AmountJson> {
- const amt: AmountJson[] = [];
- const fees: AmountJson[] = [];
- const exchangeSet: Set<string> = new Set();
-
- await ws.db
- .mktx((x) => ({
- coins: x.coins,
- denominations: x.denominations,
- exchanges: x.exchanges,
- exchangeDetails: x.exchangeDetails,
- }))
- .runReadOnly(async (tx) => {
- for (let i = 0; i < pcs.coinPubs.length; i++) {
- const coin = await tx.coins.get(pcs.coinPubs[i]);
- if (!coin) {
- throw Error("can't calculate deposit amount, coin not found");
- }
- const denom = await tx.denominations.get([
- coin.exchangeBaseUrl,
- coin.denomPubHash,
- ]);
- if (!denom) {
- throw Error("can't find denomination to calculate deposit amount");
- }
- amt.push(pcs.coinContributions[i]);
- fees.push(denom.feeDeposit);
- exchangeSet.add(coin.exchangeBaseUrl);
- }
- for (const exchangeUrl of exchangeSet.values()) {
- const exchangeDetails = await getExchangeDetails(tx, exchangeUrl);
- if (!exchangeDetails) {
- continue;
- }
- // FIXME/NOTE: the line below _likely_ throws exception
- // about "find method not found on undefined" when the wireType
- // is not supported by the Exchange.
- const fee = exchangeDetails.wireInfo.feesForType[wireType].find((x) => {
- return timestampIsBetween(
- getTimestampNow(),
- x.startStamp,
- x.endStamp,
- );
- })?.wireFee;
- if (fee) {
- fees.push(fee);
- }
- }
- });
- return Amounts.sub(Amounts.sum(amt).amount, Amounts.sum(fees).amount).amount;
-}
-
function isSpendableCoin(coin: CoinRecord, denom: DenominationRecord): boolean {
if (coin.suspended) {
return false;
@@ -585,8 +525,7 @@ async function incrementPurchasePayRetry(
pr.payRetryInfo.retryCounter++;
updateRetryInfoTimeout(pr.payRetryInfo);
logger.trace(
- `retrying pay in ${
- getDurationRemaining(pr.payRetryInfo.nextRetry).d_ms
+ `retrying pay in ${getDurationRemaining(pr.payRetryInfo.nextRetry).d_ms
} ms`,
);
pr.lastPayError = err;
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts
index 445c0539a..0555b0ced 100644
--- a/packages/taler-wallet-core/src/wallet-api-types.ts
+++ b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -83,6 +83,7 @@ export enum WalletApiOperation {
AddExchange = "addExchange",
GetTransactions = "getTransactions",
ListExchanges = "listExchanges",
+ ListKnownBankAccounts = "listKnownBankAccounts",
GetWithdrawalDetailsForUri = "getWithdrawalDetailsForUri",
GetWithdrawalDetailsForAmount = "getWithdrawalDetailsForAmount",
AcceptManualWithdrawal = "acceptManualWithdrawal",
@@ -279,11 +280,11 @@ export type WalletOperations = {
export type RequestType<
Op extends WalletApiOperation & keyof WalletOperations
-> = WalletOperations[Op] extends { request: infer T } ? T : never;
+ > = WalletOperations[Op] extends { request: infer T } ? T : never;
export type ResponseType<
Op extends WalletApiOperation & keyof WalletOperations
-> = WalletOperations[Op] extends { response: infer T } ? T : never;
+ > = WalletOperations[Op] extends { response: infer T } ? T : never;
export interface WalletCoreApiClient {
call<Op extends WalletApiOperation & keyof WalletOperations>(
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index ed0046c59..2f94d5e82 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -41,6 +41,10 @@ import {
codecForWithdrawFakebankRequest,
URL,
parsePaytoUri,
+ KnownBankAccounts,
+ PaytoUri,
+ codecForGetFeeForDeposit,
+ codecForListKnownBankAccounts,
} from "@gnu-taler/taler-util";
import {
addBackupProvider,
@@ -58,6 +62,7 @@ import { exportBackup } from "./operations/backup/export.js";
import { getBalances } from "./operations/balance.js";
import {
createDepositGroup,
+ getFeeForDeposit,
processDepositGroup,
trackDepositGroup,
} from "./operations/deposits.js";
@@ -495,6 +500,30 @@ async function getExchangeTos(
};
}
+async function listKnownBankAccounts(
+ ws: InternalWalletState,
+ currency?: string,
+): Promise<KnownBankAccounts> {
+ const accounts: PaytoUri[] = []
+ await ws.db
+ .mktx((x) => ({
+ reserves: x.reserves,
+ }))
+ .runReadOnly(async (tx) => {
+ const reservesRecords = await tx.reserves.iter().toArray()
+ for (const r of reservesRecords) {
+ if (currency && currency !== r.currency) {
+ continue
+ }
+ const payto = r.senderWire ? parsePaytoUri(r.senderWire) : undefined
+ if (payto) {
+ accounts.push(payto)
+ }
+ }
+ })
+ return { accounts }
+}
+
async function getExchanges(
ws: InternalWalletState,
): Promise<ExchangesListRespose> {
@@ -728,6 +757,10 @@ async function dispatchRequestInternal(
case "listExchanges": {
return await getExchanges(ws);
}
+ case "listKnownBankAccounts": {
+ const req = codecForListKnownBankAccounts().decode(payload);
+ return await listKnownBankAccounts(ws, req.currency);
+ }
case "getWithdrawalDetailsForUri": {
const req = codecForGetWithdrawalDetailsForUri().decode(payload);
return await getWithdrawalDetailsForUri(ws, req.talerWithdrawUri);
@@ -881,6 +914,10 @@ async function dispatchRequestInternal(
const resp = await getBackupInfo(ws);
return resp;
}
+ case "getFeeForDeposit": {
+ const req = codecForGetFeeForDeposit().decode(payload);
+ return await getFeeForDeposit(ws, req);
+ }
case "createDepositGroup": {
const req = codecForCreateDepositGroupRequest().decode(payload);
return await createDepositGroup(ws, req);
@@ -1004,7 +1041,7 @@ export async function handleCoreApiRequest(
try {
logger.error("Caught unexpected exception:");
logger.error(e.stack);
- } catch (e) {}
+ } catch (e) { }
return {
type: "error",
operation,
diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index 8dc73efdb..e7108679c 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -34,6 +34,7 @@ export enum Pages {
welcome = "/welcome",
balance = "/balance",
manual_withdraw = "/manual-withdraw",
+ deposit = "/deposit/:currency",
settings = "/settings",
dev = "/dev",
cta = "/cta",
diff --git a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
index e1c19cc23..cf396e129 100644
--- a/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
+++ b/packages/taler-wallet-webextension/src/components/BalanceTable.tsx
@@ -16,9 +16,18 @@
import { amountFractionalBase, Amounts, Balance } from "@gnu-taler/taler-util";
import { h, VNode } from "preact";
-import { TableWithRoundRows as TableWithRoundedRows } from "./styled/index";
+import {
+ ButtonPrimary,
+ TableWithRoundRows as TableWithRoundedRows,
+} from "./styled/index";
-export function BalanceTable({ balances }: { balances: Balance[] }): VNode {
+export function BalanceTable({
+ balances,
+ goToWalletDeposit,
+}: {
+ balances: Balance[];
+ goToWalletDeposit: (currency: string) => void;
+}): VNode {
const currencyFormatter = new Intl.NumberFormat("en-US");
return (
<TableWithRoundedRows>
@@ -40,6 +49,11 @@ export function BalanceTable({ balances }: { balances: Balance[] }): VNode {
>
{v}
</td>
+ <td>
+ <ButtonPrimary onClick={() => goToWalletDeposit(av.currency)}>
+ Deposit
+ </ButtonPrimary>
+ </td>
</tr>
);
})}
diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx
index a5c9f2837..216a1fabc 100644
--- a/packages/taler-wallet-webextension/src/components/styled/index.tsx
+++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx
@@ -716,6 +716,10 @@ export const InputWithLabel = styled.div<{ invalid?: boolean }>`
}
`;
+export const ErrorText = styled.div`
+ color: red;
+`;
+
export const ErrorBox = styled.div`
border: 2px solid #f5c6cb;
border-radius: 0.25em;
diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
index 33164783d..40499b87c 100644
--- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
@@ -21,18 +21,21 @@ import { ButtonPrimary, ErrorBox } from "../components/styled/index";
import { HookResponse, useAsyncAsHook } from "../hooks/useAsyncAsHook";
import { PageLink } from "../renderHtml";
import * as wxApi from "../wxApi";
-
+interface Props {
+ goToWalletDeposit: (currency: string) => void;
+ goToWalletManualWithdraw: () => void;
+}
export function BalancePage({
goToWalletManualWithdraw,
-}: {
- goToWalletManualWithdraw: () => void;
-}): VNode {
+ goToWalletDeposit,
+}: Props): VNode {
const state = useAsyncAsHook(wxApi.getBalance);
return (
<BalanceView
balance={state}
Linker={PageLink}
goToWalletManualWithdraw={goToWalletManualWithdraw}
+ goToWalletDeposit={goToWalletDeposit}
/>
);
}
@@ -40,12 +43,14 @@ export interface BalanceViewProps {
balance: HookResponse<BalancesResponse>;
Linker: typeof PageLink;
goToWalletManualWithdraw: () => void;
+ goToWalletDeposit: (currency: string) => void;
}
export function BalanceView({
balance,
Linker,
goToWalletManualWithdraw,
+ goToWalletDeposit,
}: BalanceViewProps): VNode {
if (!balance) {
return <div>Loading...</div>;
@@ -71,7 +76,8 @@ export function BalanceView({
<Linker pageName="/welcome">help</Linker> getting started?
</i18n.Translate>
</p>
- <footer style={{ justifyContent: "space-around" }}>
+ <footer style={{ justifyContent: "space-between" }}>
+ <div />
<ButtonPrimary onClick={goToWalletManualWithdraw}>
Withdraw
</ButtonPrimary>
@@ -83,9 +89,13 @@ export function BalanceView({
return (
<Fragment>
<section>
- <BalanceTable balances={balance.response.balances} />
+ <BalanceTable
+ balances={balance.response.balances}
+ goToWalletDeposit={goToWalletDeposit}
+ />
</section>
- <footer style={{ justifyContent: "space-around" }}>
+ <footer style={{ justifyContent: "space-between" }}>
+ <div />
<ButtonPrimary onClick={goToWalletManualWithdraw}>
Withdraw
</ButtonPrimary>
diff --git a/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
index b32555248..b689004cc 100644
--- a/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/DeveloperPage.tsx
@@ -43,14 +43,17 @@ export function DeveloperPage(): VNode {
? []
: operationsResponse.response.pendingOperations;
- return <View status={status}
- timedOut={timedOut}
- operations={operations}
- onDownloadDatabase={async () => {
- const db = await wxApi.exportDB()
- return JSON.stringify(db)
- }}
- />;
+ return (
+ <View
+ status={status}
+ timedOut={timedOut}
+ operations={operations}
+ onDownloadDatabase={async () => {
+ const db = await wxApi.exportDB();
+ return JSON.stringify(db);
+ }}
+ />
+ );
}
export interface Props {
@@ -64,14 +67,21 @@ function hashObjectId(o: any): string {
return JSON.stringify(o);
}
-export function View({ status, timedOut, operations, onDownloadDatabase }: Props): VNode {
- const [downloadedDatabase, setDownloadedDatabase] = useState<{time: Date; content: string}|undefined>(undefined)
+export function View({
+ status,
+ timedOut,
+ operations,
+ onDownloadDatabase,
+}: Props): VNode {
+ const [downloadedDatabase, setDownloadedDatabase] = useState<
+ { time: Date; content: string } | undefined
+ >(undefined);
async function onExportDatabase(): Promise<void> {
- const content = await onDownloadDatabase()
+ const content = await onDownloadDatabase();
setDownloadedDatabase({
time: new Date(),
- content
- })
+ content,
+ });
}
return (
<div>
@@ -83,9 +93,27 @@ export function View({ status, timedOut, operations, onDownloadDatabase }: Props
<button onClick={confirmReset}>reset</button>
<br />
<button onClick={onExportDatabase}>export database</button>
- {downloadedDatabase && <div>
- Database exported at <Time timestamp={{t_ms: downloadedDatabase.time.getTime()}} format="yyyy/MM/dd HH:mm:ss" /> <a href={`data:text/plain;charset=utf-8;base64,${btoa(downloadedDatabase.content)}`} download={`taler-wallet-database-${format(downloadedDatabase.time, 'yyyy/MM/dd_HH:mm')}.json`}>click here</a> to download
- </div>}
+ {downloadedDatabase && (
+ <div>
+ Database exported at
+ <Time
+ timestamp={{ t_ms: downloadedDatabase.time.getTime() }}
+ format="yyyy/MM/dd HH:mm:ss"
+ />
+ <a
+ href={`data:text/plain;charset=utf-8;base64,${toBase64(
+ downloadedDatabase.content,
+ )}`}
+ download={`taler-wallet-database-${format(
+ downloadedDatabase.time,
+ "yyyy/MM/dd_HH:mm",
+ )}.json`}
+ >
+ click here
+ </a>
+ to download
+ </div>
+ )}
<br />
<Diagnostics diagnostics={status} timedOut={timedOut} />
{operations && operations.length > 0 && (
@@ -109,6 +137,14 @@ export function View({ status, timedOut, operations, onDownloadDatabase }: Props
);
}
+function toBase64(str: string): string {
+ return btoa(
+ encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
+ return String.fromCharCode(parseInt(p1, 16));
+ }),
+ );
+}
+
export function reload(): void {
try {
// eslint-disable-next-line no-undef
diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
index 568913753..ac1872fb1 100644
--- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
+++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
@@ -84,6 +84,9 @@ function Application() {
goToWalletManualWithdraw={() =>
goToWalletPage(Pages.manual_withdraw)
}
+ goToWalletDeposit={(currency: string) =>
+ goToWalletPage(Pages.deposit.replace(":currency", currency))
+ }
/>
<Route path={Pages.settings} component={SettingsPage} />
<Route
@@ -107,6 +110,7 @@ function Application() {
/>
<Route path={Pages.history} component={HistoryPage} />
+
<Route
path={Pages.backup}
component={BackupPage}
diff --git a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
index 0a8910646..52edbbe51 100644
--- a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx
@@ -24,7 +24,9 @@ import * as wxApi from "../wxApi";
export function BalancePage({
goToWalletManualWithdraw,
+ goToWalletDeposit,
}: {
+ goToWalletDeposit: (currency: string) => void;
goToWalletManualWithdraw: () => void;
}): VNode {
const state = useAsyncAsHook(wxApi.getBalance);
@@ -33,6 +35,7 @@ export function BalancePage({
balance={state}
Linker={PageLink}
goToWalletManualWithdraw={goToWalletManualWithdraw}
+ goToWalletDeposit={goToWalletDeposit}
/>
);
}
@@ -41,12 +44,14 @@ export interface BalanceViewProps {
balance: HookResponse<BalancesResponse>;
Linker: typeof PageLink;
goToWalletManualWithdraw: () => void;
+ goToWalletDeposit: (currency: string) => void;
}
export function BalanceView({
balance,
Linker,
goToWalletManualWithdraw,
+ goToWalletDeposit,
}: BalanceViewProps): VNode {
if (!balance) {
return <div>Loading...</div>;
@@ -65,28 +70,35 @@ export function BalanceView({
}
if (balance.response.balances.length === 0) {
return (
- <p>
- <Centered style={{ marginTop: 100 }}>
- <i18n.Translate>
- You have no balance to show. Need some{" "}
- <Linker pageName="/welcome">help</Linker> getting started?
- </i18n.Translate>
- <div>
- <ButtonPrimary onClick={goToWalletManualWithdraw}>
- Withdraw
- </ButtonPrimary>
- </div>
- </Centered>
- </p>
+ <Fragment>
+ <p>
+ <Centered style={{ marginTop: 100 }}>
+ <i18n.Translate>
+ You have no balance to show. Need some{" "}
+ <Linker pageName="/welcome">help</Linker> getting started?
+ </i18n.Translate>
+ </Centered>
+ </p>
+ <footer style={{ justifyContent: "space-between" }}>
+ <div />
+ <ButtonPrimary onClick={goToWalletManualWithdraw}>
+ Withdraw
+ </ButtonPrimary>
+ </footer>
+ </Fragment>
);
}
return (
<Fragment>
<section>
- <BalanceTable balances={balance.response.balances} />
+ <BalanceTable
+ balances={balance.response.balances}
+ goToWalletDeposit={goToWalletDeposit}
+ />
</section>
- <footer style={{ justifyContent: "space-around" }}>
+ <footer style={{ justifyContent: "space-between" }}>
+ <div />
<ButtonPrimary onClick={goToWalletManualWithdraw}>
Withdraw
</ButtonPrimary>
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage.stories.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage.stories.tsx
new file mode 100644
index 000000000..346b85d4f
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage.stories.tsx
@@ -0,0 +1,52 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { AmountJson, Amounts, parsePaytoUri } from "@gnu-taler/taler-util";
+import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits";
+import { createExample } from "../test-utils";
+import { View as TestedComponent } from "./DepositPage";
+
+export default {
+ title: "wallet/deposit",
+ component: TestedComponent,
+ argTypes: {},
+};
+
+async function alwaysReturnFeeToOne(): Promise<DepositFee> {
+ const fee = {
+ currency: "EUR",
+ value: 1,
+ fraction: 0,
+ };
+ return { coin: fee, refresh: fee, wire: fee };
+}
+
+export const WithEmptyAccountList = createExample(TestedComponent, {
+ knownBankAccounts: [],
+ balance: Amounts.parseOrThrow("USD:10"),
+ onCalculateFee: alwaysReturnFeeToOne,
+});
+
+export const WithSomeBankAccounts = createExample(TestedComponent, {
+ knownBankAccounts: [parsePaytoUri("payto://iban/ES8877998399652238")!],
+ balance: Amounts.parseOrThrow("EUR:10"),
+ onCalculateFee: alwaysReturnFeeToOne,
+});
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx
new file mode 100644
index 000000000..d4759c537
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage.tsx
@@ -0,0 +1,234 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+
+import {
+ AmountJson,
+ Amounts,
+ AmountString,
+ PaytoUri,
+} from "@gnu-taler/taler-util";
+import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits";
+import { Fragment, h, VNode } from "preact";
+import { useEffect, useState } from "preact/hooks";
+import { Part } from "../components/Part";
+import { SelectList } from "../components/SelectList";
+import {
+ ButtonPrimary,
+ ErrorText,
+ Input,
+ InputWithLabel,
+} from "../components/styled";
+import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
+import * as wxApi from "../wxApi";
+
+interface Props {
+ currency: string;
+}
+export function DepositPage({ currency }: Props): VNode {
+ const [success, setSuccess] = useState(false);
+
+ const state = useAsyncAsHook(async () => {
+ const balance = await wxApi.getBalance();
+ const bs = balance.balances.filter((b) => b.available.startsWith(currency));
+ const currencyBalance =
+ bs.length === 0
+ ? Amounts.getZero(currency)
+ : Amounts.parseOrThrow(bs[0].available);
+ const knownAccounts = await wxApi.listKnownBankAccounts(currency);
+ return { accounts: knownAccounts.accounts, currencyBalance };
+ });
+
+ const accounts =
+ state === undefined ? [] : state.hasError ? [] : state.response.accounts;
+
+ const currencyBalance =
+ state === undefined
+ ? Amounts.getZero(currency)
+ : state.hasError
+ ? Amounts.getZero(currency)
+ : state.response.currencyBalance;
+
+ async function doSend(account: string, amount: AmountString): Promise<void> {
+ await wxApi.createDepositGroup(account, amount);
+ setSuccess(true);
+ }
+
+ async function getFeeForAmount(
+ account: string,
+ amount: AmountString,
+ ): Promise<DepositFee> {
+ return await wxApi.getFeeForDeposit(account, amount);
+ }
+
+ if (accounts.length === 0) return <div>loading..</div>;
+ if (success) return <div>deposit created</div>;
+ return (
+ <View
+ knownBankAccounts={accounts}
+ balance={currencyBalance}
+ onSend={doSend}
+ onCalculateFee={getFeeForAmount}
+ />
+ );
+}
+
+interface ViewProps {
+ knownBankAccounts: Array<PaytoUri>;
+ balance: AmountJson;
+ onSend: (account: string, amount: AmountString) => Promise<void>;
+ onCalculateFee: (
+ account: string,
+ amount: AmountString,
+ ) => Promise<DepositFee>;
+}
+
+export function View({
+ knownBankAccounts,
+ balance,
+ onSend,
+ onCalculateFee,
+}: ViewProps): VNode {
+ const accountMap = createLabelsForBankAccount(knownBankAccounts);
+ const [accountIdx, setAccountIdx] = useState(0);
+ const [amount, setAmount] = useState<number | undefined>(undefined);
+ const [fee, setFee] = useState<DepositFee | undefined>(undefined);
+ const currency = balance.currency;
+ const amountStr: AmountString = `${currency}:${amount}`;
+
+ const account = knownBankAccounts[accountIdx];
+ const accountURI = `payto://${account.targetType}/${account.targetPath}`;
+ useEffect(() => {
+ if (amount === undefined) return;
+ onCalculateFee(accountURI, amountStr).then((result) => {
+ setFee(result);
+ });
+ }, [amount]);
+
+ if (!balance) {
+ return <div>no balance</div>;
+ }
+ if (!knownBankAccounts || !knownBankAccounts.length) {
+ return <div>there is no known bank account to send money to</div>;
+ }
+ const parsedAmount =
+ amount === undefined ? undefined : Amounts.parse(amountStr);
+ const isDirty = amount !== 0;
+ const error = !isDirty
+ ? undefined
+ : !parsedAmount
+ ? "Invalid amount"
+ : Amounts.cmp(balance, parsedAmount) === -1
+ ? `To much, your current balance is ${balance.value}`
+ : undefined;
+
+ return (
+ <Fragment>
+ <h2>Send {currency} to your account</h2>
+ <section>
+ <Input>
+ <SelectList
+ label="Bank account IBAN number"
+ list={accountMap}
+ name="account"
+ value={String(accountIdx)}
+ onChange={(s) => setAccountIdx(parseInt(s, 10))}
+ />
+ </Input>
+ <InputWithLabel invalid={!!error}>
+ <label>Amount to send</label>
+ <div>
+ <span>{currency}</span>
+ <input
+ type="number"
+ value={amount}
+ onInput={(e) => {
+ const num = parseFloat(e.currentTarget.value);
+ console.log(num);
+ if (!Number.isNaN(num)) {
+ setAmount(num);
+ } else {
+ setAmount(undefined);
+ setFee(undefined);
+ }
+ }}
+ />
+ </div>
+ {error && <ErrorText>{error}</ErrorText>}
+ </InputWithLabel>
+ {!error && fee && (
+ <div style={{ textAlign: "center" }}>
+ <Part
+ title="Exchange fee"
+ text={Amounts.stringify(Amounts.sum([fee.wire, fee.coin]).amount)}
+ kind="negative"
+ />
+ <Part
+ title="Change cost"
+ text={Amounts.stringify(fee.refresh)}
+ kind="negative"
+ />
+ {parsedAmount && (
+ <Part
+ title="Total received"
+ text={Amounts.stringify(
+ Amounts.sub(
+ parsedAmount,
+ Amounts.sum([fee.wire, fee.coin]).amount,
+ ).amount,
+ )}
+ kind="positive"
+ />
+ )}
+ </div>
+ )}
+ </section>
+ <footer>
+ <div />
+ <ButtonPrimary
+ disabled={!parsedAmount}
+ onClick={() => onSend(accountURI, amountStr)}
+ >
+ Send
+ </ButtonPrimary>
+ </footer>
+ </Fragment>
+ );
+}
+
+function createLabelsForBankAccount(knownBankAccounts: Array<PaytoUri>): {
+ [label: number]: string;
+} {
+ if (!knownBankAccounts) return {};
+ return knownBankAccounts.reduce((prev, cur, i) => {
+ let label = cur.targetPath;
+ if (cur.isKnown) {
+ switch (cur.targetType) {
+ case "x-taler-bank": {
+ label = cur.account;
+ break;
+ }
+ case "iban": {
+ label = cur.iban;
+ break;
+ }
+ }
+ }
+ return {
+ ...prev,
+ [i]: label,
+ };
+ }, {} as { [label: number]: string });
+}
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 22947d0c4..8172e02a2 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -369,8 +369,8 @@ export function TransactionView({
if (transaction.type === TransactionType.Deposit) {
const fee = Amounts.sub(
- Amounts.parseOrThrow(transaction.amountRaw),
Amounts.parseOrThrow(transaction.amountEffective),
+ Amounts.parseOrThrow(transaction.amountRaw),
).amount;
return (
<TransactionTemplate>
@@ -379,15 +379,15 @@ export function TransactionView({
<br />
<Part
big
- title="Total deposit"
+ title="Total send"
text={amountToString(transaction.amountEffective)}
- kind="negative"
+ kind="neutral"
/>
<Part
big
- title="Purchase amount"
+ title="Deposit amount"
text={amountToString(transaction.amountRaw)}
- kind="neutral"
+ kind="positive"
/>
<Part big title="Fee" text={amountToString(fee)} kind="negative" />
</TransactionTemplate>
diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
index 714e3fe5a..a38add3ca 100644
--- a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
+++ b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
@@ -45,6 +45,7 @@ import { WalletBox } from "./components/styled";
import { ProviderDetailPage } from "./wallet/ProviderDetailPage";
import { ProviderAddPage } from "./wallet/ProviderAddPage";
import { ExchangeAddPage } from "./wallet/ExchangeAddPage";
+import { DepositPage } from "./wallet/DepositPage";
function main(): void {
try {
@@ -105,6 +106,9 @@ function Application(): VNode {
path={Pages.balance}
component={withLogoAndNavBar(BalancePage)}
goToWalletManualWithdraw={() => route(Pages.manual_withdraw)}
+ goToWalletDeposit={(currency: string) =>
+ route(Pages.deposit.replace(":currency", currency))
+ }
/>
<Route
path={Pages.settings}
@@ -146,6 +150,10 @@ function Application(): VNode {
/>
<Route
+ path={Pages.deposit}
+ component={withLogoAndNavBar(DepositPage)}
+ />
+ <Route
path={Pages.reset_required}
component={() => <div>no yet implemented</div>}
/>
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts
index 4d8b932d3..64a506c13 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -24,10 +24,11 @@
import {
AcceptExchangeTosRequest,
AcceptManualWithdrawalResult, AcceptTipRequest, AcceptWithdrawalResponse,
- AddExchangeRequest, ApplyRefundResponse, BalancesResponse, ConfirmPayResult,
- CoreApiResponse, DeleteTransactionRequest, ExchangesListRespose,
+ AddExchangeRequest, AmountJson, AmountString, ApplyRefundResponse, BalancesResponse, ConfirmPayResult,
+ CoreApiResponse, CreateDepositGroupRequest, CreateDepositGroupResponse, DeleteTransactionRequest, ExchangesListRespose,
GetExchangeTosResult, GetExchangeWithdrawalInfo,
- GetWithdrawalDetailsForUriRequest, NotificationType, PreparePayResult, PrepareTipRequest,
+ GetFeeForDepositRequest,
+ GetWithdrawalDetailsForUriRequest, KnownBankAccounts, NotificationType, PreparePayResult, PrepareTipRequest,
PrepareTipResult, RetryTransactionRequest,
SetWalletDeviceIdRequest, TransactionsResponse, WalletDiagnostics, WithdrawUriInfoResponse
} from "@gnu-taler/taler-util";
@@ -36,6 +37,7 @@ import {
PendingOperationsResponse,
RemoveBackupProviderRequest
} from "@gnu-taler/taler-wallet-core";
+import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits";
import { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core/src/operations/withdraw";
import { MessageFromBackend } from "./wxBackend.js";
@@ -119,6 +121,18 @@ export function resetDb(): Promise<void> {
return callBackend("reset-db", {});
}
+export function getFeeForDeposit(depositPaytoUri: string, amount: AmountString): Promise<DepositFee> {
+ return callBackend("getFeeForDeposit", {
+ depositPaytoUri, amount
+ } as GetFeeForDepositRequest);
+}
+
+export function createDepositGroup(depositPaytoUri: string, amount: AmountString): Promise<CreateDepositGroupResponse> {
+ return callBackend("createDepositGroup", {
+ depositPaytoUri, amount
+ } as CreateDepositGroupRequest);
+}
+
/**
* Get balances for all currencies/exchanges.
*/
@@ -170,6 +184,9 @@ export function listKnownCurrencies(): Promise<ListOfKnownCurrencies> {
export function listExchanges(): Promise<ExchangesListRespose> {
return callBackend("listExchanges", {});
}
+export function listKnownBankAccounts(currency?: string): Promise<KnownBankAccounts> {
+ return callBackend("listKnownBankAccounts", { currency });
+}
/**
* Get information about the current state of wallet backups.