summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/deposits.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-04-05 17:38:34 +0200
committerFlorian Dold <florian@dold.me>2023-04-05 17:38:41 +0200
commit2823b1cdf4b6e9a6c4dbdb752c7c7259775e18f5 (patch)
tree9d6fe1e025f89cea0e044f8bba53f67e7d04a77f /packages/taler-wallet-core/src/operations/deposits.ts
parent8eee38d55936c194637ecead302dde69ec0b9897 (diff)
downloadwallet-core-2823b1cdf4b6e9a6c4dbdb752c7c7259775e18f5.tar.gz
wallet-core-2823b1cdf4b6e9a6c4dbdb752c7c7259775e18f5.tar.bz2
wallet-core-2823b1cdf4b6e9a6c4dbdb752c7c7259775e18f5.zip
wallet-core: towards DD37 for deposits
Diffstat (limited to 'packages/taler-wallet-core/src/operations/deposits.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts294
1 files changed, 99 insertions, 195 deletions
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts
index 47efbb213..660fb8816 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -33,7 +33,6 @@ import {
durationFromSpec,
encodeCrock,
ExchangeDepositRequest,
- GetFeeForDepositRequest,
getRandomBytes,
hashTruncate32,
hashWire,
@@ -41,7 +40,6 @@ import {
j2s,
Logger,
MerchantContractTerms,
- NotificationType,
parsePaytoUri,
PayCoinSelection,
PrepareDepositRequest,
@@ -50,18 +48,16 @@ import {
stringToBytes,
TalerErrorCode,
TalerProtocolTimestamp,
- TrackDepositGroupRequest,
- TrackDepositGroupResponse,
TrackTransaction,
- TrackTransactionWired,
+ TransactionState,
+ TransactionStateInfo,
+ TransactionSubstate,
TransactionType,
URL,
WireFee,
} from "@gnu-taler/taler-util";
import {
- DenominationRecord,
DepositGroupRecord,
- ExchangeDetailsRecord,
OperationStatus,
TransactionStatus,
} from "../db.js";
@@ -77,7 +73,6 @@ import {
generateDepositPermissions,
getTotalPaymentCost,
} from "./pay-merchant.js";
-import { getTotalRefreshCost } from "./refresh.js";
import { selectPayCoinsNew } from "../util/coinSelection.js";
/**
@@ -85,7 +80,81 @@ import { selectPayCoinsNew } from "../util/coinSelection.js";
*/
const logger = new Logger("deposits.ts");
-export async function checkDepositKycStatus(
+export async function computeDepositTransactionStatus(
+ ws: InternalWalletState,
+ dg: DepositGroupRecord,
+): Promise<TransactionStateInfo> {
+ switch (dg.operationStatus) {
+ case OperationStatus.Finished: {
+ return {
+ txState: TransactionState.Done,
+ txSubstate: TransactionSubstate.None,
+ };
+ }
+ case OperationStatus.Pending: {
+ const numTotal = dg.payCoinSelection.coinPubs.length;
+ let numDeposited = 0;
+ let numKycRequired = 0;
+ let numWired = 0;
+ for (let i = 0; i < numTotal; i++) {
+ if (dg.depositedPerCoin[i]) {
+ numDeposited++;
+ }
+ switch (dg.transactionPerCoin[i]) {
+ case TransactionStatus.KycRequired:
+ numKycRequired++;
+ break;
+ case TransactionStatus.Wired:
+ numWired++;
+ break;
+ }
+ }
+
+ if (numKycRequired > 0) {
+ return {
+ txState: TransactionState.Pending,
+ txSubstate: TransactionSubstate.DepositKycRequired,
+ };
+ }
+
+ if (numDeposited == numTotal) {
+ return {
+ txState: TransactionState.Pending,
+ txSubstate: TransactionSubstate.DepositPendingTrack,
+ };
+ }
+
+ return {
+ txState: TransactionState.Pending,
+ txSubstate: TransactionSubstate.DepositPendingInitial,
+ };
+ }
+ default:
+ throw Error("unexpected deposit group state");
+ }
+}
+
+export async function suspendDepositGroup(
+ ws: InternalWalletState,
+ depositGroupId: string,
+): Promise<void> {
+ throw Error("not implemented");
+}
+
+export async function abortDepositGroup(
+ ws: InternalWalletState,
+ depositGroupId: string,
+): Promise<void> {
+ throw Error("not implemented");
+}
+
+/**
+ * Check KYC status with the exchange, throw an appropriate exception when KYC is required.
+ *
+ * FIXME: Why does this throw an exception when KYC is required?
+ * Should we not return some proper result record here?
+ */
+async function checkDepositKycStatus(
ws: InternalWalletState,
exchangeUrl: string,
kycInfo: KycPendingInfo,
@@ -119,7 +188,7 @@ export async function checkDepositKycStatus(
}
/**
- * @see {processDepositGroup}
+ * Process a deposit group that is not in its final state yet.
*/
export async function processDepositGroup(
ws: InternalWalletState,
@@ -181,7 +250,9 @@ export async function processDepositGroup(
options.cancellationToken?.throwIfCancelled();
const url = new URL(`coins/${perm.coin_pub}/deposit`, perm.exchange_url);
logger.info(`depositing to ${url}`);
- const httpResp = await ws.http.postJson(url.href, requestBody, {
+ const httpResp = await ws.http.fetch(url.href, {
+ method: "POST",
+ body: requestBody,
cancellationToken: options.cancellationToken,
});
await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess());
@@ -198,7 +269,6 @@ export async function processDepositGroup(
}
| undefined;
- let signature: string | undefined;
if (depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired) {
const track = await trackDepositPermission(ws, depositGroup, perm);
@@ -347,39 +417,6 @@ async function getExchangeWireFee(
return fee;
}
-export async function trackDepositGroup(
- ws: InternalWalletState,
- req: TrackDepositGroupRequest,
-): Promise<TrackDepositGroupResponse> {
- const responses: TrackTransaction[] = [];
- const depositGroup = await ws.db
- .mktx((x) => [x.depositGroups])
- .runReadOnly(async (tx) => {
- return tx.depositGroups.get(req.depositGroupId);
- });
- if (!depositGroup) {
- throw Error("deposit group not found");
- }
- const contractData = extractContractData(
- depositGroup.contractTermsRaw,
- depositGroup.contractTermsHash,
- "",
- );
-
- const depositPermissions = await generateDepositPermissions(
- ws,
- depositGroup.payCoinSelection,
- contractData,
- );
-
- for (const dp of depositPermissions) {
- const track = await trackDepositPermission(ws, depositGroup, dp);
- responses.push(track);
- }
-
- return { responses };
-}
-
async function trackDepositPermission(
ws: InternalWalletState,
depositGroup: DepositGroupRecord,
@@ -423,66 +460,10 @@ async function trackDepositPermission(
}
}
-export async function getFeeForDeposit(
- ws: InternalWalletState,
- req: GetFeeForDepositRequest,
-): Promise<DepositGroupFees> {
- 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) => [x.exchanges, 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 || amount.currency !== details.currency) {
- continue;
- }
- exchangeInfos.push({
- master_pub: details.masterPublicKey,
- url: e.baseUrl,
- });
- }
- });
-
- const payCoinSel = await selectPayCoinsNew(ws, {
- auditors: [],
- exchanges: Object.values(exchangeInfos).map((v) => ({
- exchangeBaseUrl: v.url,
- exchangePub: v.master_pub,
- })),
- wireMethod: p.targetType,
- contractTermsAmount: Amounts.parseOrThrow(req.amount),
- depositFeeLimit: Amounts.parseOrThrow(req.amount),
- wireFeeAmortization: 1,
- wireFeeLimit: Amounts.parseOrThrow(req.amount),
- prevPayCoins: [],
- });
-
- if (payCoinSel.type !== "success") {
- throw TalerError.fromDetail(
- TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE,
- {
- insufficientBalanceDetails: payCoinSel.insufficientBalanceDetails,
- },
- );
- }
-
- return await getTotalFeesForDepositAmount(
- ws,
- p.targetType,
- amount,
- payCoinSel.coinSel,
- );
-}
-
+/**
+ * FIXME: This should be renamed to checkDepositGroup,
+ * as it doesn't prepare anything
+ */
export async function prepareDepositGroup(
ws: InternalWalletState,
req: PrepareDepositRequest,
@@ -569,7 +550,7 @@ export async function prepareDepositGroup(
const totalDepositCost = await getTotalPaymentCost(ws, payCoinSel.coinSel);
- const effectiveDepositAmount = await getEffectiveDepositAmount(
+ const effectiveDepositAmount = await getCounterpartyEffectiveDepositAmount(
ws,
p.targetType,
payCoinSel.coinSel,
@@ -674,11 +655,12 @@ export async function createDepositGroup(
const depositGroupId = encodeCrock(getRandomBytes(32));
- const effectiveDepositAmount = await getEffectiveDepositAmount(
- ws,
- p.targetType,
- payCoinSel.coinSel,
- );
+ const countarpartyEffectiveDepositAmount =
+ await getCounterpartyEffectiveDepositAmount(
+ ws,
+ p.targetType,
+ payCoinSel.coinSel,
+ );
const depositGroup: DepositGroupRecord = {
contractTermsHash,
@@ -697,7 +679,9 @@ export async function createDepositGroup(
merchantPriv: merchantPair.priv,
merchantPub: merchantPair.pub,
totalPayCost: Amounts.stringify(totalDepositCost),
- effectiveDepositAmount: Amounts.stringify(effectiveDepositAmount),
+ effectiveDepositAmount: Amounts.stringify(
+ countarpartyEffectiveDepositAmount,
+ ),
wire: {
payto_uri: req.depositPaytoUri,
salt: wireSalt,
@@ -733,10 +717,10 @@ export async function createDepositGroup(
}
/**
- * Get the amount that will be deposited on the merchant's bank
- * account, not considering aggregation.
+ * Get the amount that will be deposited on the users bank
+ * account after depositing, not considering aggregation.
*/
-export async function getEffectiveDepositAmount(
+export async function getCounterpartyEffectiveDepositAmount(
ws: InternalWalletState,
wireType: string,
pcs: PayCoinSelection,
@@ -790,83 +774,3 @@ export async function getEffectiveDepositAmount(
});
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,
- ),
- };
-}