-re-add missing fields, fix types

This commit is contained in:
Florian Dold 2023-04-06 12:47:34 +02:00
parent 3cf6d15eae
commit 43ae414a55
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
10 changed files with 168 additions and 29 deletions

View File

@ -2097,7 +2097,7 @@ export interface WalletClientArgs {
export class WalletClient {
remoteWallet: RemoteWallet | undefined = undefined;
waiter: WalletNotificationWaiter = makeNotificationWaiter();
private waiter: WalletNotificationWaiter = makeNotificationWaiter();
constructor(private args: WalletClientArgs) {}

View File

@ -99,6 +99,8 @@ export interface EnvOptions {
/**
* Run a test case with a simple TESTKUDOS Taler environment, consisting
* of one exchange, one bank and one merchant.
*
* @deprecated use {@link createSimpleTestkudosEnvironmentV2} instead
*/
export async function createSimpleTestkudosEnvironment(
t: GlobalTestState,
@ -505,6 +507,11 @@ export interface WithdrawViaBankResult {
withdrawalFinishedCond: Promise<WithdrawalGroupFinishedNotification>;
}
/**
* Withdraw via a bank with the testing API enabled.
* Uses the new notification-based mechanism to wait for the
* operation to finish.
*/
export async function withdrawViaBankV2(
t: GlobalTestState,
p: {
@ -550,6 +557,8 @@ export async function withdrawViaBankV2(
/**
* Withdraw balance.
*
* @deprecated use {@link withdrawViaBankV2 instead}
*/
export async function withdrawViaBank(
t: GlobalTestState,

View File

@ -17,11 +17,12 @@
/**
* Imports.
*/
import { NotificationType, TransactionState } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, getPayto } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironment,
withdrawViaBank,
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
} from "../harness/helpers.js";
/**
@ -30,16 +31,27 @@ import {
export async function runDepositTest(t: GlobalTestState) {
// Set up test environment
const { wallet, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t);
const { walletClient, bank, exchange } =
await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
const withdrawalResult = await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
await wallet.runUntilDone();
await withdrawalResult.withdrawalFinishedCond;
const { depositGroupId } = await wallet.client.call(
const depositDone = await walletClient.waitForNotificationCond(
(n) =>
n.type == NotificationType.TransactionStateTransition &&
n.newTxState == TransactionState.Done,
);
const depositGroupResult = await walletClient.client.call(
WalletApiOperation.CreateDepositGroup,
{
amount: "TESTKUDOS:10",
@ -47,9 +59,7 @@ export async function runDepositTest(t: GlobalTestState) {
},
);
await wallet.runUntilDone();
const transactions = await wallet.client.call(
const transactions = await walletClient.client.call(
WalletApiOperation.GetTransactions,
{},
);

View File

@ -22,6 +22,7 @@
/**
* Imports.
*/
import { TransactionState, TransactionSubstate } from "./transactions-types.js";
import { TalerErrorDetail } from "./wallet-types.js";
export enum NotificationType {
@ -67,6 +68,16 @@ export enum NotificationType {
WithdrawalGroupReserveReady = "withdrawal-group-reserve-ready",
PeerPullCreditReady = "peer-pull-credit-ready",
DepositOperationError = "deposit-operation-error",
TransactionStateTransition = "transaction-state-transition",
}
export interface TransactionStateTransitionNotification {
type: NotificationType.TransactionStateTransition;
transactionId: string;
oldTxState: TransactionState;
oldTxSubstate: TransactionSubstate;
newTxState: TransactionState;
newTxSubstate: TransactionSubstate;
}
export interface ProposalAcceptedNotification {
@ -327,4 +338,5 @@ export type WalletNotification =
| KycRequestedNotification
| WithdrawalGroupBankConfirmed
| WithdrawalGroupReserveReadyNotification
| PeerPullCreditReadyNotification;
| PeerPullCreditReadyNotification
| TransactionStateTransitionNotification;

View File

@ -1722,6 +1722,7 @@ export const codecForPrepareDepositRequest = (): Codec<PrepareDepositRequest> =>
export interface PrepareDepositResponse {
totalDepositCost: AmountString;
effectiveDepositAmount: AmountString;
fees: DepositGroupFees;
}
export const codecForCreateDepositGroupRequest =

View File

@ -57,12 +57,13 @@ import {
WireFee,
} from "@gnu-taler/taler-util";
import {
DenominationRecord,
DepositGroupRecord,
OperationStatus,
TransactionStatus,
} from "../db.js";
import { TalerError } from "@gnu-taler/taler-util";
import { KycPendingInfo, KycUserType } from "../index.js";
import { getTotalRefreshCost, KycPendingInfo, KycUserType } from "../index.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { OperationAttemptResult } from "../util/retries.js";
@ -556,9 +557,17 @@ export async function prepareDepositGroup(
payCoinSel.coinSel,
);
const fees = await getTotalFeesForDepositAmount(
ws,
p.targetType,
amount,
payCoinSel.coinSel,
);
return {
totalDepositCost: Amounts.stringify(totalDepositCost),
effectiveDepositAmount: Amounts.stringify(effectiveDepositAmount),
fees,
};
}
@ -774,3 +783,83 @@ export async function getCounterpartyEffectiveDepositAmount(
});
return Amounts.sub(Amounts.sum(amt).amount, Amounts.sum(fees).amount).amount;
}
/**
* 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 getTotalFeesForDepositAmount(
ws: InternalWalletState,
wireType: string,
total: AmountJson,
pcs: PayCoinSelection,
): Promise<DepositGroupFees> {
const wireFee: AmountJson[] = [];
const coinFee: AmountJson[] = [];
const refreshFee: AmountJson[] = [];
const exchangeSet: Set<string> = new Set();
await ws.db
.mktx((x) => [x.coins, x.denominations, x.exchanges, 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 ws.getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
coin.denomPubHash,
);
if (!denom) {
throw Error("can't find denomination to calculate deposit amount");
}
coinFee.push(Amounts.parseOrThrow(denom.feeDeposit));
exchangeSet.add(coin.exchangeBaseUrl);
const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
.iter(coin.exchangeBaseUrl)
.filter((x) =>
Amounts.isSameCurrency(
DenominationRecord.getValue(x),
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;
}
const fee = exchangeDetails.wireInfo.feesForType[wireType]?.find(
(x) => {
return AbsoluteTime.isBetween(
AbsoluteTime.now(),
AbsoluteTime.fromTimestamp(x.startStamp),
AbsoluteTime.fromTimestamp(x.endStamp),
);
},
)?.wireFee;
if (fee) {
wireFee.push(Amounts.parseOrThrow(fee));
}
}
});
return {
coin: Amounts.stringify(Amounts.sumOrZero(total.currency, coinFee).amount),
wire: Amounts.stringify(Amounts.sumOrZero(total.currency, wireFee).amount),
refresh: Amounts.stringify(
Amounts.sumOrZero(total.currency, refreshFee).amount,
),
};
}

View File

@ -74,6 +74,11 @@ describe("Deposit CTA states", () => {
{
effectiveDepositAmount: "EUR:1",
totalDepositCost: "EUR:1.2",
fees: {
coin: "EUR:0",
refresh: "EUR:0.2",
wire: "EUR:0",
},
},
);

View File

@ -151,7 +151,7 @@ export function useComponentState({
// eslint-disable-next-line react-hooks/rules-of-hooks
const hook = useAsyncAsHook(async () => {
const fee = await api.wallet.call(WalletApiOperation.GetFeeForDeposit, {
const fee = await api.wallet.call(WalletApiOperation.PrepareDeposit, {
amount: amountStr,
depositPaytoUri,
});
@ -181,7 +181,7 @@ export function useComponentState({
const totalFee =
fee !== undefined
? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount
? Amounts.sum([fee.fees.wire, fee.fees.coin, fee.fees.refresh]).amount
: Amounts.zeroOfCurrency(currency);
const totalToDeposit =

View File

@ -23,6 +23,7 @@ import {
Amounts,
DepositGroupFees,
parsePaytoUri,
PrepareDepositResponse,
ScopeType,
stringifyPaytoUri,
} from "@gnu-taler/taler-util";
@ -36,16 +37,24 @@ import { useComponentState } from "./state.js";
const currency = "EUR";
const amount = `${currency}:0`;
const withoutFee = (): DepositGroupFees => ({
coin: Amounts.stringify(`${currency}:0`),
wire: Amounts.stringify(`${currency}:0`),
refresh: Amounts.stringify(`${currency}:0`),
const withoutFee = (): PrepareDepositResponse => ({
effectiveDepositAmount: `${currency}:5`,
totalDepositCost: `${currency}:5`,
fees: {
coin: Amounts.stringify(`${currency}:0`),
wire: Amounts.stringify(`${currency}:0`),
refresh: Amounts.stringify(`${currency}:0`),
},
});
const withSomeFee = (): DepositGroupFees => ({
coin: Amounts.stringify(`${currency}:1`),
wire: Amounts.stringify(`${currency}:1`),
refresh: Amounts.stringify(`${currency}:1`),
const withSomeFee = (): PrepareDepositResponse => ({
effectiveDepositAmount: `${currency}:5`,
totalDepositCost: `${currency}:5`,
fees: {
coin: Amounts.stringify(`${currency}:1`),
wire: Amounts.stringify(`${currency}:1`),
refresh: Amounts.stringify(`${currency}:1`),
},
});
describe("DepositPage states", () => {
@ -182,7 +191,7 @@ describe("DepositPage states", () => {
},
);
handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit,
WalletApiOperation.PrepareDeposit,
undefined,
withoutFee(),
);
@ -241,13 +250,13 @@ describe("DepositPage states", () => {
},
);
handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit,
WalletApiOperation.PrepareDeposit,
undefined,
withoutFee(),
);
handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit,
WalletApiOperation.PrepareDeposit,
undefined,
withoutFee(),
);
@ -330,17 +339,17 @@ describe("DepositPage states", () => {
},
);
handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit,
WalletApiOperation.PrepareDeposit,
undefined,
withoutFee(),
);
handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit,
WalletApiOperation.PrepareDeposit,
undefined,
withSomeFee(),
);
handler.addWalletCallResponse(
WalletApiOperation.GetFeeForDeposit,
WalletApiOperation.PrepareDeposit,
undefined,
withSomeFee(),
);

View File

@ -34,6 +34,8 @@ import {
TransactionPeerPushDebit,
TransactionRefresh,
TransactionRefund,
TransactionState,
TransactionSubstate,
TransactionTip,
TransactionType,
TransactionWithdrawal,
@ -68,6 +70,8 @@ const commonTransaction = {
transactionId: "txn:deposit:12",
frozen: undefined as any as boolean, //deprecated
type: TransactionType.Deposit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.None,
} as TransactionCommon;
import merchantIcon from "../../static-dev/merchant-icon.jpeg";