diff options
author | Florian Dold <florian@dold.me> | 2024-02-27 17:39:58 +0100 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2024-02-27 17:40:03 +0100 |
commit | 523280b3862b528512ff93c651bc0d9ed632fbf6 (patch) | |
tree | b99f866db59b572685c8c7215136270e22210ca2 /packages/taler-wallet-core/src/withdraw.ts | |
parent | 3a889c177dd35a114d2c95efd296274cd185ce52 (diff) | |
download | wallet-core-523280b3862b528512ff93c651bc0d9ed632fbf6.tar.xz |
wallet-core: thread through wallet execution context
Diffstat (limited to 'packages/taler-wallet-core/src/withdraw.ts')
-rw-r--r-- | packages/taler-wallet-core/src/withdraw.ts | 414 |
1 files changed, 194 insertions, 220 deletions
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts index 541525c6b..44f1ee4f9 100644 --- a/packages/taler-wallet-core/src/withdraw.ts +++ b/packages/taler-wallet-core/src/withdraw.ts @@ -146,7 +146,11 @@ import { WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION, } from "./versions.js"; -import { getDenomInfo, type InternalWalletState } from "./wallet.js"; +import { + WalletExecutionContext, + getDenomInfo, + type InternalWalletState, +} from "./wallet.js"; /** * Logger for this file. @@ -158,7 +162,7 @@ export class WithdrawTransactionContext implements TransactionContext { readonly taskId: TaskIdStr; constructor( - public ws: InternalWalletState, + public wex: WalletExecutionContext, public withdrawalGroupId: string, ) { this.transactionId = constructTransactionIdentifier({ @@ -172,7 +176,7 @@ export class WithdrawTransactionContext implements TransactionContext { } async deleteTransaction(): Promise<void> { - const { ws, withdrawalGroupId } = this; + const { wex: ws, withdrawalGroupId } = this; await ws.db.runReadWriteTx( ["withdrawalGroups", "tombstones"], async (tx) => { @@ -190,8 +194,8 @@ export class WithdrawTransactionContext implements TransactionContext { } async suspendTransaction(): Promise<void> { - const { ws, withdrawalGroupId, transactionId, taskId } = this; - const transitionInfo = await ws.db.runReadWriteTx( + const { wex, withdrawalGroupId, transactionId, taskId } = this; + const transitionInfo = await wex.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -240,12 +244,12 @@ export class WithdrawTransactionContext implements TransactionContext { return undefined; }, ); - ws.taskScheduler.stopShepherdTask(taskId); - notifyTransition(ws, transactionId, transitionInfo); + wex.taskScheduler.stopShepherdTask(taskId); + notifyTransition(wex, transactionId, transitionInfo); } async abortTransaction(): Promise<void> { - const { ws, withdrawalGroupId, transactionId, taskId } = this; + const { wex: ws, withdrawalGroupId, transactionId, taskId } = this; const transitionInfo = await ws.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { @@ -307,7 +311,12 @@ export class WithdrawTransactionContext implements TransactionContext { } async resumeTransaction(): Promise<void> { - const { ws, withdrawalGroupId, transactionId, taskId: retryTag } = this; + const { + wex: ws, + withdrawalGroupId, + transactionId, + taskId: retryTag, + } = this; const transitionInfo = await ws.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { @@ -362,7 +371,12 @@ export class WithdrawTransactionContext implements TransactionContext { } async failTransaction(): Promise<void> { - const { ws, withdrawalGroupId, transactionId, taskId: retryTag } = this; + const { + wex: ws, + withdrawalGroupId, + transactionId, + taskId: retryTag, + } = this; const stateUpdate = await ws.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { @@ -618,17 +632,17 @@ export async function getBankWithdrawalInfo( * Return denominations that can potentially used for a withdrawal. */ async function getCandidateWithdrawalDenoms( - ws: InternalWalletState, + wex: WalletExecutionContext, exchangeBaseUrl: string, currency: string, ): Promise<DenominationRecord[]> { - return await ws.db.runReadOnlyTx(["denominations"], async (tx) => { - return getCandidateWithdrawalDenomsTx(ws, tx, exchangeBaseUrl, currency); + return await wex.db.runReadOnlyTx(["denominations"], async (tx) => { + return getCandidateWithdrawalDenomsTx(wex, tx, exchangeBaseUrl, currency); }); } export async function getCandidateWithdrawalDenomsTx( - ws: InternalWalletState, + wex: WalletExecutionContext, tx: WalletDbReadOnlyTransaction<["denominations"]>, exchangeBaseUrl: string, currency: string, @@ -638,7 +652,9 @@ export async function getCandidateWithdrawalDenomsTx( await tx.denominations.indexes.byExchangeBaseUrl.getAll(exchangeBaseUrl); return allDenoms .filter((d) => d.currency === currency) - .filter((d) => isWithdrawableDenom(d, ws.config.testing.denomselAllowLate)); + .filter((d) => + isWithdrawableDenom(d, wex.ws.config.testing.denomselAllowLate), + ); } /** @@ -649,11 +665,11 @@ export async function getCandidateWithdrawalDenomsTx( * the exchange requests per reserve. */ async function processPlanchetGenerate( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroup: WithdrawalGroupRecord, coinIdx: number, ): Promise<void> { - let planchet = await ws.db.runReadOnlyTx(["planchets"], async (tx) => { + let planchet = await wex.db.runReadOnlyTx(["planchets"], async (tx) => { return tx.planchets.indexes.byGroupAndIndex.get([ withdrawalGroup.withdrawalGroupId, coinIdx, @@ -677,11 +693,11 @@ async function processPlanchetGenerate( } const denomPubHash = maybeDenomPubHash; - const denom = await ws.db.runReadOnlyTx(["denominations"], async (tx) => { - return getDenomInfo(ws, tx, withdrawalGroup.exchangeBaseUrl, denomPubHash); + const denom = await wex.db.runReadOnlyTx(["denominations"], async (tx) => { + return getDenomInfo(wex, tx, withdrawalGroup.exchangeBaseUrl, denomPubHash); }); checkDbInvariant(!!denom); - const r = await ws.cryptoApi.createPlanchet({ + const r = await wex.cryptoApi.createPlanchet({ denomPub: denom.denomPub, feeWithdraw: Amounts.parseOrThrow(denom.feeWithdraw), reservePriv: withdrawalGroup.reservePriv, @@ -705,7 +721,7 @@ async function processPlanchetGenerate( ageCommitmentProof: r.ageCommitmentProof, lastError: undefined, }; - await ws.db.runReadWriteTx(["planchets"], async (tx) => { + await wex.db.runReadWriteTx(["planchets"], async (tx) => { const p = await tx.planchets.indexes.byGroupAndIndex.get([ withdrawalGroup.withdrawalGroupId, coinIdx, @@ -743,15 +759,15 @@ enum ExchangeAmlStatus { * Emit a notification for the (self-)transition. */ async function transitionKycUrlUpdate( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroupId: string, kycUrl: string, ): Promise<void> { let notificationKycUrl: string | undefined = undefined; - const ctx = new WithdrawTransactionContext(ws, withdrawalGroupId); + const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId); const transactionId = ctx.transactionId; - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { const wg2 = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -777,7 +793,7 @@ async function transitionKycUrlUpdate( ); if (transitionInfo) { // Always notify, even on self-transition, as the KYC URL might have changed. - ws.notify({ + wex.ws.notify({ type: NotificationType.TransactionStateTransition, oldTxState: transitionInfo.oldTxState, newTxState: transitionInfo.newTxState, @@ -785,16 +801,15 @@ async function transitionKycUrlUpdate( experimentalUserData: notificationKycUrl, }); } - ws.taskScheduler.startShepherdTask(ctx.taskId); + wex.taskScheduler.startShepherdTask(ctx.taskId); } async function handleKycRequired( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroup: WithdrawalGroupRecord, resp: HttpResponse, startIdx: number, requestCoinIdxs: number[], - cancellationToken: CancellationToken, ): Promise<void> { logger.info("withdrawal requires KYC"); const respJson = await resp.json(); @@ -816,9 +831,9 @@ async function handleKycRequired( exchangeUrl, ); logger.info(`kyc url ${url.href}`); - const kycStatusRes = await ws.http.fetch(url.href, { + const kycStatusRes = await wex.http.fetch(url.href, { method: "GET", - cancellationToken, + cancellationToken: wex.cancellationToken, }); let kycUrl: string; let amlStatus: ExchangeAmlStatus | undefined; @@ -846,7 +861,7 @@ async function handleKycRequired( let notificationKycUrl: string | undefined = undefined; - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["planchets", "withdrawalGroups"], async (tx) => { for (let i = startIdx; i < requestCoinIdxs.length; i++) { @@ -897,7 +912,7 @@ async function handleKycRequired( } }, ); - notifyTransition(ws, transactionId, transitionInfo, notificationKycUrl); + notifyTransition(wex, transactionId, transitionInfo, notificationKycUrl); } /** @@ -906,10 +921,9 @@ async function handleKycRequired( * The verification of the response is done asynchronously to enable parallelism. */ async function processPlanchetExchangeBatchRequest( - ws: InternalWalletState, + wex: WalletExecutionContext, wgContext: WithdrawalGroupContext, args: WithdrawalRequestBatchArgs, - cancellationToken: CancellationToken, ): Promise<WithdrawalBatchResult> { const withdrawalGroup: WithdrawalGroupRecord = wgContext.wgRecord; logger.info( @@ -920,7 +934,7 @@ async function processPlanchetExchangeBatchRequest( // Indices of coins that are included in the batch request const requestCoinIdxs: number[] = []; - await ws.db.runReadOnlyTx(["planchets", "denominations"], async (tx) => { + await wex.db.runReadOnlyTx(["planchets", "denominations"], async (tx) => { for ( let coinIdx = args.coinStartIndex; coinIdx < args.coinStartIndex + args.batchSize && @@ -939,7 +953,7 @@ async function processPlanchetExchangeBatchRequest( continue; } const denom = await getDenomInfo( - ws, + wex, tx, withdrawalGroup.exchangeBaseUrl, planchet.denomPubHash, @@ -972,7 +986,7 @@ async function processPlanchetExchangeBatchRequest( const errDetail = getErrorDetailFromException(e); logger.trace("withdrawal request failed", e); logger.trace(String(e)); - await ws.db.runReadWriteTx(["planchets"], async (tx) => { + await wex.db.runReadWriteTx(["planchets"], async (tx) => { let planchet = await tx.planchets.indexes.byGroupAndIndex.get([ withdrawalGroup.withdrawalGroupId, coinIdx, @@ -993,21 +1007,14 @@ async function processPlanchetExchangeBatchRequest( ).href; try { - const resp = await ws.http.fetch(reqUrl, { + const resp = await wex.http.fetch(reqUrl, { method: "POST", body: batchReq, - cancellationToken, + cancellationToken: wex.cancellationToken, timeout: Duration.fromSpec({ seconds: 40 }), }); if (resp.status === HttpStatusCode.UnavailableForLegalReasons) { - await handleKycRequired( - ws, - withdrawalGroup, - resp, - 0, - requestCoinIdxs, - cancellationToken, - ); + await handleKycRequired(wex, withdrawalGroup, resp, 0, requestCoinIdxs); return { batchResp: { ev_sigs: [] }, coinIdxs: [], @@ -1031,14 +1038,14 @@ async function processPlanchetExchangeBatchRequest( } async function processPlanchetVerifyAndStoreCoin( - ws: InternalWalletState, + wex: WalletExecutionContext, wgContext: WithdrawalGroupContext, coinIdx: number, resp: ExchangeWithdrawResponse, ): Promise<void> { const withdrawalGroup = wgContext.wgRecord; logger.trace(`checking and storing planchet idx=${coinIdx}`); - const d = await ws.db.runReadOnlyTx( + const d = await wex.db.runReadOnlyTx( ["planchets", "denominations"], async (tx) => { let planchet = await tx.planchets.indexes.byGroupAndIndex.get([ @@ -1053,7 +1060,7 @@ async function processPlanchetVerifyAndStoreCoin( return; } const denomInfo = await getDenomInfo( - ws, + wex, tx, withdrawalGroup.exchangeBaseUrl, planchet.denomPubHash, @@ -1090,20 +1097,20 @@ async function processPlanchetVerifyAndStoreCoin( throw Error("unsupported cipher"); } - const denomSigRsa = await ws.cryptoApi.rsaUnblind({ + const denomSigRsa = await wex.cryptoApi.rsaUnblind({ bk: planchet.blindingKey, blindedSig: evSig.blinded_rsa_signature, pk: planchetDenomPub.rsa_public_key, }); - const isValid = await ws.cryptoApi.rsaVerify({ + const isValid = await wex.cryptoApi.rsaVerify({ hm: planchet.coinPub, pk: planchetDenomPub.rsa_public_key, sig: denomSigRsa.sig, }); if (!isValid) { - await ws.db.runReadWriteTx(["planchets"], async (tx) => { + await wex.db.runReadWriteTx(["planchets"], async (tx) => { let planchet = await tx.planchets.indexes.byGroupAndIndex.get([ withdrawalGroup.withdrawalGroupId, coinIdx, @@ -1156,7 +1163,7 @@ async function processPlanchetVerifyAndStoreCoin( wgContext.planchetsFinished.add(planchet.coinPub); - await ws.db.runReadWriteTx( + await wex.db.runReadWriteTx( ["planchets", "coins", "coinAvailability", "denominations"], async (tx) => { const p = await tx.planchets.get(planchetCoinPub); @@ -1166,7 +1173,7 @@ async function processPlanchetVerifyAndStoreCoin( p.planchetStatus = PlanchetStatus.WithdrawalDone; p.lastError = undefined; await tx.planchets.put(p); - await makeCoinAvailable(ws, tx, coin); + await makeCoinAvailable(wex, tx, coin); }, ); } @@ -1176,13 +1183,13 @@ async function processPlanchetVerifyAndStoreCoin( * are validated, and the result of validation is stored in the database. */ async function updateWithdrawalDenoms( - ws: InternalWalletState, + wex: WalletExecutionContext, exchangeBaseUrl: string, ): Promise<void> { logger.trace( `updating denominations used for withdrawal for ${exchangeBaseUrl}`, ); - const exchangeDetails = await ws.db.runReadOnlyTx( + const exchangeDetails = await wex.db.runReadOnlyTx( ["exchanges", "exchangeDetails"], async (tx) => { return getExchangeWireDetailsInTx(tx, exchangeBaseUrl); @@ -1196,7 +1203,7 @@ async function updateWithdrawalDenoms( // is checked and the result is stored in the database. logger.trace("getting candidate denominations"); const denominations = await getCandidateWithdrawalDenoms( - ws, + wex, exchangeBaseUrl, exchangeDetails.currency, ); @@ -1222,10 +1229,10 @@ async function updateWithdrawalDenoms( }) signature of ${denom.denomPubHash}`, ); let valid = false; - if (ws.config.testing.insecureTrustExchange) { + if (wex.ws.config.testing.insecureTrustExchange) { valid = true; } else { - const res = await ws.cryptoApi.isValidDenom({ + const res = await wex.cryptoApi.isValidDenom({ denom, masterPub: exchangeDetails.masterPublicKey, }); @@ -1246,7 +1253,7 @@ async function updateWithdrawalDenoms( } if (updatedDenominations.length > 0) { logger.trace("writing denomination batch to db"); - await ws.db.runReadWriteTx(["denominations"], async (tx) => { + await wex.db.runReadWriteTx(["denominations"], async (tx) => { for (let i = 0; i < updatedDenominations.length; i++) { const denom = updatedDenominations[i]; await tx.denominations.put(denom); @@ -1266,15 +1273,14 @@ async function updateWithdrawalDenoms( * create a new withdrawal group for the remaining amount. */ async function processQueryReserve( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroupId: string, - cancellationToken: CancellationToken, ): Promise<TaskRunResult> { const transactionId = constructTransactionIdentifier({ tag: TransactionType.Withdrawal, withdrawalGroupId, }); - const withdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, { + const withdrawalGroup = await getWithdrawalGroupRecordTx(wex.db, { withdrawalGroupId, }); checkDbInvariant(!!withdrawalGroup); @@ -1291,9 +1297,9 @@ async function processQueryReserve( logger.trace(`querying reserve status via ${reserveUrl.href}`); - const resp = await ws.http.fetch(reserveUrl.href, { + const resp = await wex.http.fetch(reserveUrl.href, { timeout: getReserveRequestTimeout(withdrawalGroup), - cancellationToken, + cancellationToken: wex.cancellationToken, }); logger.trace(`reserve status code: HTTP ${resp.status}`); @@ -1316,7 +1322,7 @@ async function processQueryReserve( logger.trace(`got reserve status ${j2s(result.response)}`); - const transitionResult = await ws.db.runReadWriteTx( + const transitionResult = await wex.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -1336,7 +1342,7 @@ async function processQueryReserve( }, ); - notifyTransition(ws, transactionId, transitionResult); + notifyTransition(wex, transactionId, transitionResult); if (transitionResult) { return TaskRunResult.progress(); @@ -1361,9 +1367,8 @@ interface WithdrawalGroupContext { } async function processWithdrawalGroupAbortingBank( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroup: WithdrawalGroupRecord, - cancellationToken: CancellationToken, ): Promise<TaskRunResult> { const { withdrawalGroupId } = withdrawalGroup; const transactionId = constructTransactionIdentifier({ @@ -1377,14 +1382,14 @@ async function processWithdrawalGroupAbortingBank( } const abortUrl = getBankAbortUrl(wgInfo.bankInfo.talerWithdrawUri); logger.info(`aborting withdrawal at ${abortUrl}`); - const abortResp = await ws.http.fetch(abortUrl, { + const abortResp = await wex.http.fetch(abortUrl, { method: "POST", body: {}, - cancellationToken, + cancellationToken: wex.cancellationToken, }); logger.info(`abort response status: ${abortResp.status}`); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -1402,7 +1407,7 @@ async function processWithdrawalGroupAbortingBank( }; }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); return TaskRunResult.finished(); } @@ -1411,14 +1416,14 @@ async function processWithdrawalGroupAbortingBank( * satisfied. */ async function transitionKycSatisfied( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroup: WithdrawalGroupRecord, ): Promise<void> { const transactionId = constructTransactionIdentifier({ tag: TransactionType.Withdrawal, withdrawalGroupId: withdrawalGroup.withdrawalGroupId, }); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { const wg2 = await tx.withdrawalGroups.get( @@ -1445,13 +1450,12 @@ async function transitionKycSatisfied( } }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); } async function processWithdrawalGroupPendingKyc( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroup: WithdrawalGroupRecord, - cancellationToken: CancellationToken, ): Promise<TaskRunResult> { const userType = "individual"; const kycInfo = withdrawalGroup.kycPending; @@ -1468,9 +1472,9 @@ async function processWithdrawalGroupPendingKyc( const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; logger.info(`long-polling for withdrawal KYC status via ${url.href}`); - const kycStatusRes = await ws.http.fetch(url.href, { + const kycStatusRes = await wex.http.fetch(url.href, { method: "GET", - cancellationToken, + cancellationToken: wex.cancellationToken, }); logger.info(`kyc long-polling response status: HTTP ${kycStatusRes.status}`); if ( @@ -1479,13 +1483,13 @@ async function processWithdrawalGroupPendingKyc( // remove after the exchange is fixed or clarified kycStatusRes.status === HttpStatusCode.NoContent ) { - await transitionKycSatisfied(ws, withdrawalGroup); + await transitionKycSatisfied(wex, withdrawalGroup); } else if (kycStatusRes.status === HttpStatusCode.Accepted) { const kycStatus = await kycStatusRes.json(); logger.info(`kyc status: ${j2s(kycStatus)}`); const kycUrl = kycStatus.kyc_url; if (typeof kycUrl === "string") { - await transitionKycUrlUpdate(ws, withdrawalGroupId, kycUrl); + await transitionKycUrlUpdate(wex, withdrawalGroupId, kycUrl); } } else if ( kycStatusRes.status === HttpStatusCode.UnavailableForLegalReasons @@ -1499,9 +1503,8 @@ async function processWithdrawalGroupPendingKyc( } async function processWithdrawalGroupPendingReady( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroup: WithdrawalGroupRecord, - cancellationToken: CancellationToken, ): Promise<TaskRunResult> { const { withdrawalGroupId } = withdrawalGroup; const transactionId = constructTransactionIdentifier({ @@ -1509,11 +1512,11 @@ async function processWithdrawalGroupPendingReady( withdrawalGroupId, }); - await fetchFreshExchange(ws, withdrawalGroup.exchangeBaseUrl); + await fetchFreshExchange(wex, withdrawalGroup.exchangeBaseUrl); if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) { logger.warn("Finishing empty withdrawal group (no denoms)"); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -1531,7 +1534,7 @@ async function processWithdrawalGroupPendingReady( }; }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); return TaskRunResult.finished(); } @@ -1545,7 +1548,7 @@ async function processWithdrawalGroupPendingReady( wgRecord: withdrawalGroup, }; - await ws.db.runReadOnlyTx(["planchets"], async (tx) => { + await wex.db.runReadOnlyTx(["planchets"], async (tx) => { const planchets = await tx.planchets.indexes.byGroup.getAll(withdrawalGroupId); for (const p of planchets) { @@ -1558,21 +1561,16 @@ async function processWithdrawalGroupPendingReady( // We sequentially generate planchets, so that // large withdrawal groups don't make the wallet unresponsive. for (let i = 0; i < numTotalCoins; i++) { - await processPlanchetGenerate(ws, withdrawalGroup, i); + await processPlanchetGenerate(wex, withdrawalGroup, i); } const maxBatchSize = 100; for (let i = 0; i < numTotalCoins; i += maxBatchSize) { - const resp = await processPlanchetExchangeBatchRequest( - ws, - wgContext, - { - batchSize: maxBatchSize, - coinStartIndex: i, - }, - cancellationToken, - ); + const resp = await processPlanchetExchangeBatchRequest(wex, wgContext, { + batchSize: maxBatchSize, + coinStartIndex: i, + }); let work: Promise<void>[] = []; work = []; for (let j = 0; j < resp.coinIdxs.length; j++) { @@ -1582,7 +1580,7 @@ async function processWithdrawalGroupPendingReady( } work.push( processPlanchetVerifyAndStoreCoin( - ws, + wex, wgContext, resp.coinIdxs[j], resp.batchResp.ev_sigs[j], @@ -1597,7 +1595,7 @@ async function processWithdrawalGroupPendingReady( let numPlanchetErrors = 0; const maxReportedErrors = 5; - const res = await ws.db.runReadWriteTx( + const res = await wex.db.runReadWriteTx( ["coins", "coinAvailability", "withdrawalGroups", "planchets"], async (tx) => { const wg = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -1623,7 +1621,7 @@ async function processWithdrawalGroupPendingReady( if (wg.timestampFinish === undefined && numFinished === numTotalCoins) { wg.timestampFinish = timestampPreciseToDb(TalerPreciseTimestamp.now()); wg.status = WithdrawalGroupStatus.Done; - await makeCoinsVisible(ws, tx, transactionId); + await makeCoinsVisible(wex, tx, transactionId); } const newTxState = computeWithdrawalTransactionStatus(wg); @@ -1643,8 +1641,8 @@ async function processWithdrawalGroupPendingReady( throw Error("withdrawal group does not exist anymore"); } - notifyTransition(ws, transactionId, res.transitionInfo); - ws.notify({ + notifyTransition(wex, transactionId, res.transitionInfo); + wex.ws.notify({ type: NotificationType.BalanceChange, hintTransactionId: transactionId, }); @@ -1666,12 +1664,11 @@ async function processWithdrawalGroupPendingReady( } export async function processWithdrawalGroup( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroupId: string, - cancellationToken: CancellationToken, ): Promise<TaskRunResult> { logger.trace("processing withdrawal group", withdrawalGroupId); - const withdrawalGroup = await ws.db.runReadOnlyTx( + const withdrawalGroup = await wex.db.runReadOnlyTx( ["withdrawalGroups"], async (tx) => { return tx.withdrawalGroups.get(withdrawalGroupId); @@ -1684,41 +1681,21 @@ export async function processWithdrawalGroup( switch (withdrawalGroup.status) { case WithdrawalGroupStatus.PendingRegisteringBank: - return await processBankRegisterReserve( - ws, - withdrawalGroupId, - cancellationToken, - ); + return await processBankRegisterReserve(wex, withdrawalGroupId); case WithdrawalGroupStatus.PendingQueryingStatus: - return processQueryReserve(ws, withdrawalGroupId, cancellationToken); + return processQueryReserve(wex, withdrawalGroupId); case WithdrawalGroupStatus.PendingWaitConfirmBank: - return await processReserveBankStatus( - ws, - withdrawalGroupId, - cancellationToken, - ); + return await processReserveBankStatus(wex, withdrawalGroupId); case WithdrawalGroupStatus.PendingAml: // FIXME: Handle this case, withdrawal doesn't support AML yet. return TaskRunResult.backoff(); case WithdrawalGroupStatus.PendingKyc: - return processWithdrawalGroupPendingKyc( - ws, - withdrawalGroup, - cancellationToken, - ); + return processWithdrawalGroupPendingKyc(wex, withdrawalGroup); case WithdrawalGroupStatus.PendingReady: // Continue with the actual withdrawal! - return await processWithdrawalGroupPendingReady( - ws, - withdrawalGroup, - cancellationToken, - ); + return await processWithdrawalGroupPendingReady(wex, withdrawalGroup); case WithdrawalGroupStatus.AbortingBank: - return await processWithdrawalGroupAbortingBank( - ws, - withdrawalGroup, - cancellationToken, - ); + return await processWithdrawalGroupAbortingBank(wex, withdrawalGroup); case WithdrawalGroupStatus.AbortedBank: case WithdrawalGroupStatus.AbortedExchange: case WithdrawalGroupStatus.FailedAbortingBank: @@ -1743,13 +1720,13 @@ const AGE_MASK_GROUPS = "8:10:12:14:16:18" .map((n) => parseInt(n, 10)); export async function getExchangeWithdrawalInfo( - ws: InternalWalletState, + wex: WalletExecutionContext, exchangeBaseUrl: string, instructedAmount: AmountJson, ageRestricted: number | undefined, ): Promise<ExchangeWithdrawalDetails> { logger.trace("updating exchange"); - const exchange = await fetchFreshExchange(ws, exchangeBaseUrl); + const exchange = await fetchFreshExchange(wex, exchangeBaseUrl); if (exchange.currency != instructedAmount.currency) { // Specifying the amount in the conversion input currency is not yet supported. @@ -1760,7 +1737,7 @@ export async function getExchangeWithdrawalInfo( } const withdrawalAccountsList = await fetchWithdrawalAccountInfo( - ws, + wex, { exchange, instructedAmount, @@ -1769,11 +1746,11 @@ export async function getExchangeWithdrawalInfo( ); logger.trace("updating withdrawal denoms"); - await updateWithdrawalDenoms(ws, exchangeBaseUrl); + await updateWithdrawalDenoms(wex, exchangeBaseUrl); logger.trace("getting candidate denoms"); const denoms = await getCandidateWithdrawalDenoms( - ws, + wex, exchangeBaseUrl, instructedAmount.currency, ); @@ -1781,7 +1758,7 @@ export async function getExchangeWithdrawalInfo( const selectedDenoms = selectWithdrawalDenominations( instructedAmount, denoms, - ws.config.testing.denomselAllowLate, + wex.ws.config.testing.denomselAllowLate, ); logger.trace("selection done"); @@ -1806,11 +1783,11 @@ export async function getExchangeWithdrawalInfo( let earliestDepositExpiration: TalerProtocolTimestamp | undefined; - await ws.db.runReadOnlyTx(["denominations"], async (tx) => { + await wex.db.runReadOnlyTx(["denominations"], async (tx) => { for (let i = 0; i < selectedDenoms.selectedDenoms.length; i++) { const ds = selectedDenoms.selectedDenoms[i]; const denom = await getDenomInfo( - ws, + wex, tx, exchangeBaseUrl, ds.denomPubHash, @@ -1837,7 +1814,7 @@ export async function getExchangeWithdrawalInfo( checkLogicInvariant(!!earliestDepositExpiration); const possibleDenoms = await getCandidateWithdrawalDenoms( - ws, + wex, exchangeBaseUrl, instructedAmount.currency, ); @@ -1915,18 +1892,18 @@ const ongoingChecks: WithdrawalOperationMemoryMap = {}; * to the wallet's list of known exchanges. */ export async function getWithdrawalDetailsForUri( - ws: InternalWalletState, + wex: WalletExecutionContext, talerWithdrawUri: string, opts: GetWithdrawalDetailsForUriOpts = {}, ): Promise<WithdrawUriInfoResponse> { logger.trace(`getting withdrawal details for URI ${talerWithdrawUri}`); - const info = await getBankWithdrawalInfo(ws.http, talerWithdrawUri); + const info = await getBankWithdrawalInfo(wex.http, talerWithdrawUri); logger.trace(`got bank info`); if (info.suggestedExchange) { try { // If the exchange entry doesn't exist yet, // it'll be created as an ephemeral entry. - await fetchFreshExchange(ws, info.suggestedExchange); + await fetchFreshExchange(wex, info.suggestedExchange); } catch (e) { // We still continued if it failed, as other exchanges might be available. // We don't want to fail if the bank-suggested exchange is broken/offline. @@ -1938,7 +1915,7 @@ export async function getWithdrawalDetailsForUri( const currency = Amounts.currencyOf(info.amount); - const listExchangesResp = await listExchanges(ws); + const listExchangesResp = await listExchanges(wex); const possibleExchanges = listExchangesResp.exchanges.filter((x) => { return ( x.currency === currency && @@ -1957,7 +1934,7 @@ export async function getWithdrawalDetailsForUri( ongoingChecks[talerWithdrawUri] = true; const bankApi = new TalerBankIntegrationHttpClient( info.apiBaseUrl, - ws.http, + wex.http, ); console.log( `waiting operation (${info.operationId}) to change from pending`, @@ -2076,11 +2053,10 @@ export function getBankAbortUrl(talerWithdrawUri: string): string { } async function registerReserveWithBank( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroupId: string, - cancellationToken: CancellationToken, ): Promise<void> { - const withdrawalGroup = await ws.db.runReadOnlyTx( + const withdrawalGroup = await wex.db.runReadOnlyTx( ["withdrawalGroups"], async (tx) => { return await tx.withdrawalGroups.get(withdrawalGroupId); @@ -2112,17 +2088,17 @@ async function registerReserveWithBank( selected_exchange: bankInfo.exchangePaytoUri, }; logger.info(`registering reserve with bank: ${j2s(reqBody)}`); - const httpResp = await ws.http.fetch(bankStatusUrl, { + const httpResp = await wex.http.fetch(bankStatusUrl, { method: "POST", body: reqBody, timeout: getReserveRequestTimeout(withdrawalGroup), - cancellationToken, + cancellationToken: wex.cancellationToken, }); const status = await readSuccessResponseJsonOrThrow( httpResp, codeForBankWithdrawalOperationPostResponse(), ); - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { const r = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -2154,14 +2130,14 @@ async function registerReserveWithBank( }, ); - notifyTransition(ws, transactionId, transitionInfo); + notifyTransition(wex, transactionId, transitionInfo); } async function transitionBankAborted( ctx: WithdrawTransactionContext, ): Promise<TaskRunResult> { logger.info("bank aborted the withdrawal"); - const transitionInfo = await ctx.ws.db.runReadWriteTx( + const transitionInfo = await ctx.wex.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { const r = await tx.withdrawalGroups.get(ctx.withdrawalGroupId); @@ -2190,17 +2166,16 @@ async function transitionBankAborted( }; }, ); - notifyTransition(ctx.ws, ctx.transactionId, transitionInfo); + notifyTransition(ctx.wex, ctx.transactionId, transitionInfo); return TaskRunResult.finished(); } async function processBankRegisterReserve( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroupId: string, - cancellationToken: CancellationToken, ): Promise<TaskRunResult> { - const ctx = new WithdrawTransactionContext(ws, withdrawalGroupId); - const withdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, { + const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId); + const withdrawalGroup = await getWithdrawalGroupRecordTx(wex.db, { withdrawalGroupId, }); if (!withdrawalGroup) { @@ -2226,9 +2201,9 @@ async function processBankRegisterReserve( uriResult.bankIntegrationApiBaseUrl, ); - const statusResp = await ws.http.fetch(url.href, { + const statusResp = await wex.http.fetch(url.href, { timeout: getReserveRequestTimeout(withdrawalGroup), - cancellationToken, + cancellationToken: wex.cancellationToken, }); const status = await readSuccessResponseJsonOrThrow( @@ -2242,16 +2217,15 @@ async function processBankRegisterReserve( // FIXME: Put confirm transfer URL in the DB! - await registerReserveWithBank(ws, withdrawalGroupId, cancellationToken); + await registerReserveWithBank(wex, withdrawalGroupId); return TaskRunResult.progress(); } async function processReserveBankStatus( - ws: InternalWalletState, + wex: WalletExecutionContext, withdrawalGroupId: string, - cancellationToken: CancellationToken, ): Promise<TaskRunResult> { - const withdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, { + const withdrawalGroup = await getWithdrawalGroupRecordTx(wex.db, { withdrawalGroupId, }); @@ -2259,7 +2233,7 @@ async function processReserveBankStatus( return TaskRunResult.finished(); } - const ctx = new WithdrawTransactionContext(ws, withdrawalGroupId); + const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId); if ( withdrawalGroup.wgInfo.withdrawalType != WithdrawalRecordType.BankIntegrated @@ -2282,9 +2256,9 @@ async function processReserveBankStatus( bankStatusUrl.searchParams.set("long_poll_ms", "30000"); logger.info(`long-polling for withdrawal operation at ${bankStatusUrl.href}`); - const statusResp = await ws.http.fetch(bankStatusUrl.href, { + const statusResp = await wex.http.fetch(bankStatusUrl.href, { timeout: getReserveRequestTimeout(withdrawalGroup), - cancellationToken, + cancellationToken: wex.cancellationToken, }); logger.info( `long-polling for withdrawal operation returned status ${statusResp.status}`, @@ -2307,7 +2281,7 @@ async function processReserveBankStatus( return TaskRunResult.longpollReturnedPending(); } - const transitionInfo = await ws.db.runReadWriteTx( + const transitionInfo = await wex.db.runReadWriteTx( ["withdrawalGroups"], async (tx) => { const r = await tx.withdrawalGroups.get(withdrawalGroupId); @@ -2341,7 +2315,7 @@ async function processReserveBankStatus( }, ); - notifyTransition(ws, ctx.transactionId, transitionInfo); + notifyTransition(wex, ctx.transactionId, transitionInfo); if (transitionInfo) { return TaskRunResult.progress(); @@ -2360,7 +2334,7 @@ export interface PrepareCreateWithdrawalGroupResult { } export async function internalPrepareCreateWithdrawalGroup( - ws: InternalWalletState, + wex: WalletExecutionContext, args: { reserveStatus: WithdrawalGroupStatus; amount: AmountJson; @@ -2373,7 +2347,7 @@ export async function internalPrepareCreateWithdrawalGroup( }, ): Promise<PrepareCreateWithdrawalGroupResult> { const reserveKeyPair = - args.reserveKeyPair ?? (await ws.cryptoApi.createEddsaKeypair({})); + args.reserveKeyPair ?? (await wex.cryptoApi.createEddsaKeypair({})); const now = AbsoluteTime.toPreciseTimestamp(AbsoluteTime.now()); const secretSeed = encodeCrock(getRandomBytes(32)); const canonExchange = canonicalizeBaseUrl(args.exchangeBaseUrl); @@ -2385,7 +2359,7 @@ export async function internalPrepareCreateWithdrawalGroup( if (args.forcedWithdrawalGroupId) { withdrawalGroupId = args.forcedWithdrawalGroupId; const wgId = withdrawalGroupId; - const existingWg = await ws.db.runReadOnlyTx( + const existingWg = await wex.db.runReadOnlyTx( ["withdrawalGroups"], async (tx) => { return tx.withdrawalGroups.get(wgId); @@ -2403,9 +2377,9 @@ export async function internalPrepareCreateWithdrawalGroup( withdrawalGroupId = encodeCrock(getRandomBytes(32)); } - await updateWithdrawalDenoms(ws, canonExchange); + await updateWithdrawalDenoms(wex, canonExchange); const denoms = await getCandidateWithdrawalDenoms( - ws, + wex, canonExchange, currency, ); @@ -2418,13 +2392,13 @@ export async function internalPrepareCreateWithdrawalGroup( amount, denoms, args.forcedDenomSel, - ws.config.testing.denomselAllowLate, + wex.ws.config.testing.denomselAllowLate, ); } else { initialDenomSel = selectWithdrawalDenominations( amount, denoms, - ws.config.testing.denomselAllowLate, + wex.ws.config.testing.denomselAllowLate, ); } @@ -2447,7 +2421,7 @@ export async function internalPrepareCreateWithdrawalGroup( wgInfo: args.wgInfo, }; - await fetchFreshExchange(ws, canonExchange); + await fetchFreshExchange(wex, canonExchange); const transactionId = constructTransactionIdentifier({ tag: TransactionType.Withdrawal, withdrawalGroupId: withdrawalGroup.withdrawalGroupId, @@ -2476,7 +2450,7 @@ export interface PerformCreateWithdrawalGroupResult { } export async function internalPerformCreateWithdrawalGroup( - ws: InternalWalletState, + wex: WalletExecutionContext, tx: WalletDbReadWriteTransaction< ["withdrawalGroups", "reserves", "exchanges"] >, @@ -2523,17 +2497,17 @@ export async function internalPerformCreateWithdrawalGroup( }; const exchangeUsedRes = await markExchangeUsed( - ws, + wex, tx, prep.withdrawalGroup.exchangeBaseUrl, ); const ctx = new WithdrawTransactionContext( - ws, + wex, withdrawalGroup.withdrawalGroupId, ); - ws.taskScheduler.startShepherdTask(ctx.taskId); + wex.taskScheduler.startShepherdTask(ctx.taskId); return { withdrawalGroup, @@ -2551,7 +2525,7 @@ export async function internalPerformCreateWithdrawalGroup( * of the other arguments is done in that case. */ export async function internalCreateWithdrawalGroup( - ws: InternalWalletState, + wex: WalletExecutionContext, args: { reserveStatus: WithdrawalGroupStatus; amount: AmountJson; @@ -2563,21 +2537,21 @@ export async function internalCreateWithdrawalGroup( wgInfo: WgInfo; }, ): Promise<WithdrawalGroupRecord> { - const prep = await internalPrepareCreateWithdrawalGroup(ws, args); + const prep = await internalPrepareCreateWithdrawalGroup(wex, args); const transactionId = constructTransactionIdentifier({ tag: TransactionType.Withdrawal, withdrawalGroupId: prep.withdrawalGroup.withdrawalGroupId, }); - const res = await ws.db.runReadWriteTx( + const res = await wex.db.runReadWriteTx( ["withdrawalGroups", "reserves", "exchanges", "exchangeDetails"], async (tx) => { - return await internalPerformCreateWithdrawalGroup(ws, tx, prep); + return await internalPerformCreateWithdrawalGroup(wex, tx, prep); }, ); if (res.exchangeNotif) { - ws.notify(res.exchangeNotif); + wex.ws.notify(res.exchangeNotif); } - notifyTransition(ws, transactionId, res.transitionInfo); + notifyTransition(wex, transactionId, res.transitionInfo); return res.withdrawalGroup; } @@ -2590,7 +2564,7 @@ export async function internalCreateWithdrawalGroup( * with the bank. */ export async function acceptWithdrawalFromUri( - ws: InternalWalletState, + wex: WalletExecutionContext, req: { talerWithdrawUri: string; selectedExchange: string; @@ -2602,7 +2576,7 @@ export async function acceptWithdrawalFromUri( logger.info( `accepting withdrawal via ${req.talerWithdrawUri}, canonicalized selected exchange ${selectedExchange}`, ); - const existingWithdrawalGroup = await ws.db.runReadOnlyTx( + const existingWithdrawalGroup = await wex.db.runReadOnlyTx( ["withdrawalGroups"], async (tx) => { return await tx.withdrawalGroups.indexes.byTalerWithdrawUri.get( @@ -2629,21 +2603,21 @@ export async function acceptWithdrawalFromUri( }; } - await fetchFreshExchange(ws, selectedExchange); + await fetchFreshExchange(wex, selectedExchange); const withdrawInfo = await getBankWithdrawalInfo( - ws.http, + wex.http, req.talerWithdrawUri, ); const exchangePaytoUri = await getExchangePaytoUri( - ws, + wex, selectedExchange, withdrawInfo.wireTypes, ); - const exchange = await fetchFreshExchange(ws, selectedExchange); + const exchange = await fetchFreshExchange(wex, selectedExchange); const withdrawalAccountList = await fetchWithdrawalAccountInfo( - ws, + wex, { exchange, instructedAmount: withdrawInfo.amount, @@ -2651,7 +2625,7 @@ export async function acceptWithdrawalFromUri( CancellationToken.CONTINUE, ); - const withdrawalGroup = await internalCreateWithdrawalGroup(ws, { + const withdrawalGroup = await internalCreateWithdrawalGroup(wex, { amount: withdrawInfo.amount, exchangeBaseUrl: req.selectedExchange, wgInfo: { @@ -2672,16 +2646,16 @@ export async function acceptWithdrawalFromUri( const withdrawalGroupId = withdrawalGroup.withdrawalGroupId; - const ctx = new WithdrawTransactionContext(ws, withdrawalGroupId); + const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId); - ws.notify({ + wex.ws.notify({ type: NotificationType.BalanceChange, hintTransactionId: ctx.transactionId, }); - await waitWithdrawalRegistered(ws, ctx); + await waitWithdrawalRegistered(wex, ctx); - ws.taskScheduler.startShepherdTask(ctx.taskId); + wex.taskScheduler.startShepherdTask(ctx.taskId); return { reservePub: withdrawalGroup.reservePub, @@ -2691,12 +2665,12 @@ export async function acceptWithdrawalFromUri( } async function internalWaitWithdrawalRegistered( - ws: InternalWalletState, + wex: WalletExecutionContext, ctx: WithdrawTransactionContext, withdrawalNotifFlag: AsyncFlag, ): Promise<void> { while (true) { - const { withdrawalRec, retryRec } = await ws.db.runReadOnlyTx( + const { withdrawalRec, retryRec } = await wex.db.runReadOnlyTx( ["withdrawalGroups", "operationRetries"], async (tx) => { return { @@ -2742,7 +2716,7 @@ async function internalWaitWithdrawalRegistered( } async function waitWithdrawalRegistered( - ws: InternalWalletState, + wex: WalletExecutionContext, ctx: WithdrawTransactionContext, ): Promise<void> { // FIXME: We should use Symbol.dispose magic here for cleanup! @@ -2750,7 +2724,7 @@ async function waitWithdrawalRegistered( const withdrawalNotifFlag = new AsyncFlag(); // Raise exchangeNotifFlag whenever we get a notification // about our exchange. - const cancelNotif = ws.addNotificationListener((notif) => { + const cancelNotif = wex.ws.addNotificationListener((notif) => { if ( notif.type === NotificationType.TransactionStateTransition && notif.transactionId === ctx.transactionId @@ -2762,7 +2736,7 @@ async function waitWithdrawalRegistered( try { const res = await internalWaitWithdrawalRegistered( - ws, + wex, ctx, withdrawalNotifFlag, ); @@ -2774,7 +2748,7 @@ async function waitWithdrawalRegistered( } async function fetchAccount( - ws: InternalWalletState, + wex: WalletExecutionContext, instructedAmount: AmountJson, acct: ExchangeWireAccount, reservePub: string | undefined, @@ -2789,7 +2763,7 @@ async function fetchAccount( "amount_credit", Amounts.stringify(instructedAmount), ); - const httpResp = await ws.http.fetch(reqUrl.href, { + const httpResp = await wex.http.fetch(reqUrl.href, { cancellationToken, }); const respOrErr = await readSuccessResponseJsonOrErrorCode( @@ -2807,7 +2781,7 @@ async function fetchAccount( paytoUri = acct.payto_uri; transferAmount = resp.amount_debit; const configUrl = new URL("config", acct.conversion_url); - const configResp = await ws.http.fetch(configUrl.href, { + const configResp = await wex.http.fetch(configUrl.href, { cancellationToken, }); const configRespOrError = await readSuccessResponseJsonOrErrorCode( @@ -2854,7 +2828,7 @@ async function fetchAccount( * currency and require conversion. */ async function fetchWithdrawalAccountInfo( - ws: InternalWalletState, + wex: WalletExecutionContext, req: { exchange: ReadyExchangeSummary; instructedAmount: AmountJson; @@ -2866,7 +2840,7 @@ async function fetchWithdrawalAccountInfo( const withdrawalAccounts: WithdrawalExchangeAccountDetails[] = []; for (let acct of exchange.wireInfo.accounts) { const acctInfo = await fetchAccount( - ws, + wex, req.instructedAmount, acct, req.reservePub, @@ -2886,7 +2860,7 @@ async function fetchWithdrawalAccountInfo( * Asynchronously starts the withdrawal. */ export async function createManualWithdrawal( - ws: InternalWalletState, + wex: WalletExecutionContext, req: { exchangeBaseUrl: string; amount: AmountLike; @@ -2896,19 +2870,19 @@ export async function createManualWithdrawal( ): Promise<AcceptManualWithdrawalResult> { const { exchangeBaseUrl } = req; const amount = Amounts.parseOrThrow(req.amount); - const exchange = await fetchFreshExchange(ws, exchangeBaseUrl); + const exchange = await fetchFreshExchange(wex, exchangeBaseUrl); if (exchange.currency != amount.currency) { throw Error( "manual withdrawal with conversion from foreign currency is not yet supported", ); } - const reserveKeyPair: EddsaKeypair = await ws.cryptoApi.createEddsaKeypair( + const reserveKeyPair: EddsaKeypair = await wex.cryptoApi.createEddsaKeypair( {}, ); const withdrawalAccountsList = await fetchWithdrawalAccountInfo( - ws, + wex, { exchange, instructedAmount: amount, @@ -2917,7 +2891,7 @@ export async function createManualWithdrawal( CancellationToken.CONTINUE, ); - const withdrawalGroup = await internalCreateWithdrawalGroup(ws, { + const withdrawalGroup = await internalCreateWithdrawalGroup(wex, { amount: Amounts.jsonifyAmount(req.amount), wgInfo: { withdrawalType: WithdrawalRecordType.BankManual, @@ -2931,23 +2905,23 @@ export async function createManualWithdrawal( }); const ctx = new WithdrawTransactionContext( - ws, + wex, withdrawalGroup.withdrawalGroupId, ); - const exchangePaytoUris = await ws.db.runReadOnlyTx( + const exchangePaytoUris = await wex.db.runReadOnlyTx( ["withdrawalGroups", "exchanges", "exchangeDetails"], async (tx) => { return await getFundingPaytoUris(tx, withdrawalGroup.withdrawalGroupId); }, ); - ws.notify({ + wex.ws.notify({ type: NotificationType.BalanceChange, hintTransactionId: ctx.transactionId, }); - ws.taskScheduler.startShepherdTask(ctx.taskId); + wex.taskScheduler.startShepherdTask(ctx.taskId); return { reservePub: withdrawalGroup.reservePub, |