diff options
author | Florian Dold <florian@dold.me> | 2023-04-05 17:38:34 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2023-04-05 17:38:41 +0200 |
commit | 2823b1cdf4b6e9a6c4dbdb752c7c7259775e18f5 (patch) | |
tree | 9d6fe1e025f89cea0e044f8bba53f67e7d04a77f /packages/taler-wallet-core/src/operations/deposits.ts | |
parent | 8eee38d55936c194637ecead302dde69ec0b9897 (diff) | |
download | wallet-core-2823b1cdf4b6e9a6c4dbdb752c7c7259775e18f5.tar.xz |
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.ts | 294 |
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, - ), - }; -} |