calculate using server
This commit is contained in:
parent
8701ae100e
commit
f947c8e549
8
packages/demobank-ui/src/declaration.d.ts
vendored
8
packages/demobank-ui/src/declaration.d.ts
vendored
@ -329,6 +329,14 @@ namespace SandboxBackend {
|
||||
// where to send cashouts.
|
||||
cashout_address: string;
|
||||
}
|
||||
interface CashoutEstimate {
|
||||
// Amount that the user will get deducted from their regional
|
||||
// bank account, according to the 'amount_credit' value.
|
||||
amount_debit: Amount;
|
||||
// Amount that the user will receive in their fiat
|
||||
// bank account, according to 'amount_debit'.
|
||||
amount_credit: Amount;
|
||||
}
|
||||
interface CashoutRequest {
|
||||
// Optional subject to associate to the
|
||||
// cashout operation. This data will appear
|
||||
|
@ -32,6 +32,7 @@ import {
|
||||
|
||||
// FIX default import https://github.com/microsoft/TypeScript/issues/49189
|
||||
import _useSWR, { SWRHook } from "swr";
|
||||
import { AmountJson, Amounts } from "@gnu-taler/taler-util";
|
||||
const useSWR = _useSWR as unknown as SWRHook;
|
||||
|
||||
export function useAdminAccountAPI(): AdminAccountAPI {
|
||||
@ -215,6 +216,23 @@ export interface CircuitAccountAPI {
|
||||
async function getBusinessStatus(
|
||||
request: ReturnType<typeof useApiContext>["request"],
|
||||
basicAuth: { username: string; password: string },
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const url = getInitialBackendBaseURL();
|
||||
const result = await request<SandboxBackend.Circuit.CircuitAccountData>(
|
||||
url,
|
||||
`circuit-api/accounts/${basicAuth.username}`,
|
||||
{ basicAuth },
|
||||
);
|
||||
return result.ok;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getEstimationByCredit(
|
||||
request: ReturnType<typeof useApiContext>["request"],
|
||||
basicAuth: { username: string; password: string },
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const url = getInitialBackendBaseURL();
|
||||
@ -227,6 +245,93 @@ async function getBusinessStatus(
|
||||
}
|
||||
}
|
||||
|
||||
export type TransferCalculation = {
|
||||
debit: AmountJson;
|
||||
credit: AmountJson;
|
||||
beforeFee: AmountJson;
|
||||
};
|
||||
type EstimatorFunction = (
|
||||
amount: AmountJson,
|
||||
sellFee: AmountJson,
|
||||
sellRate: number,
|
||||
) => Promise<TransferCalculation>;
|
||||
|
||||
type CashoutEstimators = {
|
||||
estimateByCredit: EstimatorFunction;
|
||||
estimateByDebit: EstimatorFunction;
|
||||
};
|
||||
|
||||
export function useEstimator(): CashoutEstimators {
|
||||
const { state } = useBackendContext();
|
||||
const { request } = useApiContext();
|
||||
const basicAuth =
|
||||
state.status === "loggedOut"
|
||||
? undefined
|
||||
: { username: state.username, password: state.password };
|
||||
return {
|
||||
estimateByCredit: async (amount, fee, rate) => {
|
||||
const zeroBalance = Amounts.zeroOfCurrency(fee.currency);
|
||||
const zeroFiat = Amounts.zeroOfCurrency(fee.currency);
|
||||
const zeroCalc = {
|
||||
debit: zeroBalance,
|
||||
credit: zeroFiat,
|
||||
beforeFee: zeroBalance,
|
||||
};
|
||||
const url = getInitialBackendBaseURL();
|
||||
const result = await request<SandboxBackend.Circuit.CashoutEstimate>(
|
||||
url,
|
||||
`circuit-api/cashouts/estimates`,
|
||||
{
|
||||
basicAuth,
|
||||
params: {
|
||||
amount_credit: Amounts.stringify(amount),
|
||||
},
|
||||
},
|
||||
);
|
||||
// const credit = Amounts.parseOrThrow(result.data.data.amount_credit);
|
||||
const credit = amount;
|
||||
const _credit = { ...credit, currency: fee.currency };
|
||||
const beforeFee = Amounts.sub(_credit, fee).amount;
|
||||
|
||||
const debit = Amounts.parseOrThrow(result.data.amount_debit);
|
||||
return {
|
||||
debit,
|
||||
beforeFee,
|
||||
credit,
|
||||
};
|
||||
},
|
||||
estimateByDebit: async (amount, fee, rate) => {
|
||||
const zeroBalance = Amounts.zeroOfCurrency(fee.currency);
|
||||
const zeroFiat = Amounts.zeroOfCurrency(fee.currency);
|
||||
const zeroCalc = {
|
||||
debit: zeroBalance,
|
||||
credit: zeroFiat,
|
||||
beforeFee: zeroBalance,
|
||||
};
|
||||
const url = getInitialBackendBaseURL();
|
||||
const result = await request<SandboxBackend.Circuit.CashoutEstimate>(
|
||||
url,
|
||||
`circuit-api/cashouts/estimates`,
|
||||
{
|
||||
basicAuth,
|
||||
params: {
|
||||
amount_debit: Amounts.stringify(amount),
|
||||
},
|
||||
},
|
||||
);
|
||||
const credit = Amounts.parseOrThrow(result.data.amount_credit);
|
||||
const _credit = { ...credit, currency: fee.currency };
|
||||
const debit = amount;
|
||||
const beforeFee = Amounts.sub(_credit, fee).amount;
|
||||
return {
|
||||
debit,
|
||||
beforeFee,
|
||||
credit,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function useBusinessAccountFlag(): boolean | undefined {
|
||||
const [isBusiness, setIsBusiness] = useState<boolean | undefined>();
|
||||
const { state } = useBackendContext();
|
||||
|
@ -34,6 +34,7 @@ import { useAccountDetails } from "../hooks/access.js";
|
||||
import {
|
||||
useCashoutDetails,
|
||||
useCircuitAccountAPI,
|
||||
useEstimator,
|
||||
useRatiosAndFeeConfig,
|
||||
} from "../hooks/circuit.js";
|
||||
import {
|
||||
@ -230,7 +231,10 @@ function CreateCashout({
|
||||
const ratiosResult = useRatiosAndFeeConfig();
|
||||
const result = useAccountDetails(account);
|
||||
const [error, saveError] = useState<ErrorMessage | undefined>();
|
||||
|
||||
const {
|
||||
estimateByCredit: calculateFromCredit,
|
||||
estimateByDebit: calculateFromDebit,
|
||||
} = useEstimator();
|
||||
const [form, setForm] = useState<Partial<FormType>>({ isDebit: true });
|
||||
|
||||
const { createCashout } = useCircuitAccountAPI();
|
||||
@ -256,21 +260,45 @@ function CreateCashout({
|
||||
|
||||
if (!sellRate || sellRate < 0) return <div>error rate</div>;
|
||||
|
||||
const amount = Amounts.parse(`${balance.currency}:${form.amount}`);
|
||||
const amount = Amounts.parseOrThrow(
|
||||
`${!form.isDebit ? fiatCurrency : balance.currency}:${
|
||||
!form.amount ? "0" : form.amount
|
||||
}`,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!amount) {
|
||||
setCalc(zeroCalc);
|
||||
if (form.isDebit) {
|
||||
calculateFromDebit(amount, sellFee, sellRate)
|
||||
.then((r) => {
|
||||
setCalc(r);
|
||||
saveError(undefined);
|
||||
})
|
||||
.catch((error) => {
|
||||
saveError(
|
||||
error instanceof RequestError
|
||||
? buildRequestErrorMessage(i18n, error.cause)
|
||||
: {
|
||||
title: i18n.str`Could not estimate the cashout`,
|
||||
description: error.message,
|
||||
},
|
||||
);
|
||||
});
|
||||
} else {
|
||||
if (form.isDebit) {
|
||||
calculateFromDebit(amount, sellFee, sellRate).then((r) => {
|
||||
calculateFromCredit(amount, sellFee, sellRate)
|
||||
.then((r) => {
|
||||
setCalc(r);
|
||||
saveError(undefined);
|
||||
})
|
||||
.catch((error) => {
|
||||
saveError(
|
||||
error instanceof RequestError
|
||||
? buildRequestErrorMessage(i18n, error.cause)
|
||||
: {
|
||||
title: i18n.str`Could not estimate the cashout`,
|
||||
description: error.message,
|
||||
},
|
||||
);
|
||||
});
|
||||
} else {
|
||||
calculateFromCredit(amount, sellFee, sellRate).then((r) => {
|
||||
setCalc(r);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [form.amount, form.isDebit]);
|
||||
|
||||
@ -326,14 +354,10 @@ function CreateCashout({
|
||||
type="text"
|
||||
readonly
|
||||
class="currency-indicator"
|
||||
size={
|
||||
!form.isDebit ? fiatCurrency.length : balance.currency.length
|
||||
}
|
||||
maxLength={
|
||||
!form.isDebit ? fiatCurrency.length : balance.currency.length
|
||||
}
|
||||
size={amount?.currency.length ?? 0}
|
||||
maxLength={amount?.currency.length ?? 0}
|
||||
tabIndex={-1}
|
||||
value={!form.isDebit ? fiatCurrency : balance.currency}
|
||||
value={amount?.currency ?? ""}
|
||||
/>
|
||||
|
||||
<input
|
||||
@ -588,9 +612,7 @@ function CreateCashout({
|
||||
if (errors) return;
|
||||
try {
|
||||
const res = await createCashout({
|
||||
amount_credit: `${fiatCurrency}:${Amounts.stringifyValue(
|
||||
calc.credit,
|
||||
)}`,
|
||||
amount_credit: Amounts.stringify(calc.credit),
|
||||
amount_debit: Amounts.stringify(calc.debit),
|
||||
subject: form.subject,
|
||||
tan_channel: form.channel,
|
||||
@ -842,39 +864,6 @@ function truncate(a: AmountJson): AmountJson {
|
||||
return Amounts.parseOrThrow(truncated);
|
||||
}
|
||||
|
||||
type TransferCalculation = {
|
||||
debit: AmountJson;
|
||||
credit: AmountJson;
|
||||
beforeFee: AmountJson;
|
||||
};
|
||||
|
||||
async function calculateFromDebit(
|
||||
amount: AmountJson,
|
||||
sellFee: AmountJson,
|
||||
sellRate: number,
|
||||
): Promise<TransferCalculation> {
|
||||
const debit = amount;
|
||||
|
||||
const beforeFee = truncate(Amounts.divide(debit, 1 / sellRate));
|
||||
|
||||
const credit = Amounts.sub(beforeFee, sellFee).amount;
|
||||
return { debit, credit, beforeFee };
|
||||
}
|
||||
|
||||
async function calculateFromCredit(
|
||||
amount: AmountJson,
|
||||
sellFee: AmountJson,
|
||||
sellRate: number,
|
||||
): Promise<TransferCalculation> {
|
||||
const credit = amount;
|
||||
|
||||
const beforeFee = Amounts.add(credit, sellFee).amount;
|
||||
|
||||
const debit = truncate(Amounts.divide(beforeFee, sellRate));
|
||||
|
||||
return { debit, credit, beforeFee };
|
||||
}
|
||||
|
||||
export function assertUnreachable(x: never): never {
|
||||
throw new Error("Didn't expect to get here");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user