diff options
-rw-r--r-- | packages/taler-wallet-core/src/db.ts | 5 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/pay-peer-pull-credit.ts | 182 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/pay-peer-push-credit.ts | 213 |
3 files changed, 370 insertions, 30 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 1a0cd4135..2cee19f8e 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -1944,6 +1944,7 @@ export enum PeerPullPaymentCreditStatus { PendingReady = 0x0100_0001, PendingMergeKycRequired = 0x0100_0002, PendingWithdrawing = 0x0100_0003, + PendingBalanceKycRequired = 0x0100_0004, AbortingDeletePurse = 0x0103_0000, @@ -1951,6 +1952,7 @@ export enum PeerPullPaymentCreditStatus { SuspendedReady = 0x0110_0001, SuspendedMergeKycRequired = 0x0110_0002, SuspendedWithdrawing = 0x0110_0003, + SuspendedBalanceKycRequired = 0x0110_0004, SuspendedAbortingDeletePurse = 0x0113_0000, @@ -2024,9 +2026,12 @@ export enum PeerPushCreditStatus { */ PendingWithdrawing = 0x0100_0002, + PendingBalanceKycRequired = 0x0100_0003, + SuspendedMerge = 0x0110_0000, SuspendedMergeKycRequired = 0x0110_0001, SuspendedWithdrawing = 0x0110_0002, + SuspendedBalanceKycRequired = 0x0110_0003, DialogProposed = 0x0101_0000, diff --git a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts index 8bda9e1e6..ac50f2e0e 100644 --- a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts +++ b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts @@ -62,6 +62,8 @@ import { TaskRunResult, TombstoneTag, TransactionContext, + TransitionResult, + TransitionResultType, constructTaskIdentifier, requireExchangeTosAcceptedOrThrow, runWithClientCancellation, @@ -73,6 +75,7 @@ import { PeerPullPaymentCreditStatus, WalletDbAllStoresReadOnlyTransaction, WalletDbReadWriteTransaction, + WalletDbStoresArr, WithdrawalGroupRecord, WithdrawalGroupStatus, WithdrawalRecordType, @@ -80,12 +83,19 @@ import { timestampPreciseFromDb, timestampPreciseToDb, } from "./db.js"; -import { fetchFreshExchange, getScopeForAllExchanges } from "./exchanges.js"; +import { + checkIncomingAmountLegalUnderKycBalanceThreshold, + fetchFreshExchange, + getScopeForAllExchanges, + handleStartExchangeWalletKyc, + waitIncomingAmountLegalUnderKycBalanceThreshold, +} from "./exchanges.js"; import { codecForExchangePurseStatus, getMergeReserveInfo, } from "./pay-peer-common.js"; import { + TransitionInfo, constructTransactionIdentifier, isUnsuccessfulTransaction, notifyTransition, @@ -117,6 +127,91 @@ export class PeerPullCreditTransactionContext implements TransactionContext { }); } + /** + * Transition a peer-pull-credit transaction. + * Extra object stores may be accessed during the transition. + */ + async transition<StoreNameArray extends WalletDbStoresArr = []>( + opts: { extraStores?: StoreNameArray; transactionLabel?: string }, + f: ( + rec: PeerPullCreditRecord | undefined, + tx: WalletDbReadWriteTransaction< + [ + "peerPullCredit", + "transactionsMeta", + "operationRetries", + "exchanges", + "exchangeDetails", + ...StoreNameArray, + ] + >, + ) => Promise<TransitionResult<PeerPullCreditRecord>>, + ): Promise<TransitionInfo | undefined> { + const baseStores = [ + "peerPullCredit" as const, + "transactionsMeta" as const, + "operationRetries" as const, + "exchanges" as const, + "exchangeDetails" as const, + ]; + const stores = opts.extraStores + ? [...baseStores, ...opts.extraStores] + : baseStores; + + let errorThrown: Error | undefined; + const transitionInfo = await this.wex.db.runReadWriteTx( + { storeNames: stores }, + async (tx) => { + const rec = await tx.peerPullCredit.get(this.pursePub); + let oldTxState: TransactionState; + if (rec) { + oldTxState = computePeerPullCreditTransactionState(rec); + } else { + oldTxState = { + major: TransactionMajorState.None, + }; + } + let res: TransitionResult<PeerPullCreditRecord> | undefined; + try { + res = await f(rec, tx); + } catch (error) { + if (error instanceof Error) { + errorThrown = error; + } + return undefined; + } + + switch (res.type) { + case TransitionResultType.Transition: { + await tx.peerPullCredit.put(res.rec); + await this.updateTransactionMeta(tx); + const newTxState = computePeerPullCreditTransactionState(res.rec); + return { + oldTxState, + newTxState, + }; + } + case TransitionResultType.Delete: + await tx.peerPullCredit.delete(this.pursePub); + await this.updateTransactionMeta(tx); + return { + oldTxState, + newTxState: { + major: TransactionMajorState.None, + }, + }; + default: + return undefined; + } + }, + ); + if (errorThrown) { + throw errorThrown; + } + notifyTransition(this.wex, this.transactionId, transitionInfo); + return transitionInfo; + } + async updateTransactionMeta( tx: WalletDbReadWriteTransaction<["peerPullCredit", "transactionsMeta"]>, ): Promise<void> { @@ -317,7 +412,11 @@ export class PeerPullCreditTransactionContext implements TransactionContext { newStatus = PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse; break; + case PeerPullPaymentCreditStatus.PendingBalanceKycRequired: + newStatus = PeerPullPaymentCreditStatus.SuspendedBalanceKycRequired; + break; case PeerPullPaymentCreditStatus.Done: + case PeerPullPaymentCreditStatus.SuspendedBalanceKycRequired: case PeerPullPaymentCreditStatus.SuspendedCreatePurse: case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired: case PeerPullPaymentCreditStatus.SuspendedReady: @@ -374,6 +473,8 @@ export class PeerPullCreditTransactionContext implements TransactionContext { case PeerPullPaymentCreditStatus.Aborted: case PeerPullPaymentCreditStatus.Failed: case PeerPullPaymentCreditStatus.Expired: + case PeerPullPaymentCreditStatus.PendingBalanceKycRequired: + case PeerPullPaymentCreditStatus.SuspendedBalanceKycRequired: break; case PeerPullPaymentCreditStatus.AbortingDeletePurse: case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: @@ -418,6 +519,7 @@ export class PeerPullCreditTransactionContext implements TransactionContext { case PeerPullPaymentCreditStatus.PendingMergeKycRequired: case PeerPullPaymentCreditStatus.PendingWithdrawing: case PeerPullPaymentCreditStatus.PendingReady: + case PeerPullPaymentCreditStatus.PendingBalanceKycRequired: case PeerPullPaymentCreditStatus.AbortingDeletePurse: case PeerPullPaymentCreditStatus.Done: case PeerPullPaymentCreditStatus.Failed: @@ -439,6 +541,9 @@ export class PeerPullCreditTransactionContext implements TransactionContext { case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: newStatus = PeerPullPaymentCreditStatus.AbortingDeletePurse; break; + case PeerPullPaymentCreditStatus.SuspendedBalanceKycRequired: + newStatus = PeerPullPaymentCreditStatus.PendingBalanceKycRequired; + break; default: assertUnreachable(pullCreditRec.status); } @@ -474,6 +579,8 @@ export class PeerPullCreditTransactionContext implements TransactionContext { } let newStatus: PeerPullPaymentCreditStatus | undefined = undefined; switch (pullCreditRec.status) { + case PeerPullPaymentCreditStatus.PendingBalanceKycRequired: + case PeerPullPaymentCreditStatus.SuspendedBalanceKycRequired: case PeerPullPaymentCreditStatus.PendingCreatePurse: case PeerPullPaymentCreditStatus.PendingMergeKycRequired: newStatus = PeerPullPaymentCreditStatus.AbortingDeletePurse; @@ -814,6 +921,33 @@ async function handlePeerPullCreditCreatePurse( wex: WalletExecutionContext, pullIni: PeerPullCreditRecord, ): Promise<TaskRunResult> { + const ctx = new PeerPullCreditTransactionContext(wex, pullIni.pursePub); + + const kycCheckRes = await checkIncomingAmountLegalUnderKycBalanceThreshold( + wex, + pullIni.exchangeBaseUrl, + pullIni.estimatedAmountEffective, + ); + + if (kycCheckRes.result === "violation") { + // Do this before we transition so that the exchange is already in the right state. + await handleStartExchangeWalletKyc(wex, { + amount: kycCheckRes.nextThreshold, + exchangeBaseUrl: pullIni.exchangeBaseUrl, + }); + await ctx.transition({}, async (rec) => { + if (!rec) { + return TransitionResult.stay(); + } + if (rec.status !== PeerPullPaymentCreditStatus.PendingCreatePurse) { + return TransitionResult.stay(); + } + rec.status = PeerPullPaymentCreditStatus.PendingBalanceKycRequired; + return TransitionResult.transition(rec); + }); + return TaskRunResult.progress(); + } + const purseFee = Amounts.stringify(Amounts.zeroOfAmount(pullIni.amount)); const pursePub = pullIni.pursePub; const mergeReserve = await wex.db.runReadOnlyTx( @@ -910,8 +1044,6 @@ async function handlePeerPullCreditCreatePurse( logger.info(`reserve merge response: ${j2s(resp)}`); - const ctx = new PeerPullCreditTransactionContext(wex, pullIni.pursePub); - const transitionInfo = await wex.db.runReadWriteTx( { storeNames: ["peerPullCredit", "transactionsMeta"] }, async (tx) => { @@ -956,6 +1088,8 @@ export async function processPeerPullCredit( logger.trace(`processing ${retryTag}, status=${pullIni.status}`); + const ctx = new PeerPullCreditTransactionContext(wex, pullIni.pursePub); + switch (pullIni.status) { case PeerPullPaymentCreditStatus.Done: { return TaskRunResult.finished(); @@ -979,9 +1113,12 @@ export async function processPeerPullCredit( return await processPeerPullCreditAbortingDeletePurse(wex, pullIni); case PeerPullPaymentCreditStatus.PendingWithdrawing: return handlePeerPullCreditWithdrawing(wex, pullIni); + case PeerPullPaymentCreditStatus.PendingBalanceKycRequired: + return processPeerPullCreditBalanceKyc(ctx, pullIni); case PeerPullPaymentCreditStatus.Aborted: case PeerPullPaymentCreditStatus.Failed: case PeerPullPaymentCreditStatus.Expired: + case PeerPullPaymentCreditStatus.SuspendedBalanceKycRequired: case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: case PeerPullPaymentCreditStatus.SuspendedCreatePurse: case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired: @@ -995,6 +1132,31 @@ export async function processPeerPullCredit( return TaskRunResult.finished(); } +async function processPeerPullCreditBalanceKyc( + ctx: PeerPullCreditTransactionContext, + peerInc: PeerPullCreditRecord, +): Promise<TaskRunResult> { + const exchangeBaseUrl = peerInc.exchangeBaseUrl; + const amount = peerInc.estimatedAmountEffective; + + await waitIncomingAmountLegalUnderKycBalanceThreshold( + ctx.wex, + exchangeBaseUrl, + amount, + ); + await ctx.transition({}, async (rec) => { + if (!rec) { + return TransitionResult.stay(); + } + if (rec.status !== PeerPullPaymentCreditStatus.PendingBalanceKycRequired) { + return TransitionResult.stay(); + } + rec.status = PeerPullPaymentCreditStatus.PendingCreatePurse; + return TransitionResult.transition(rec); + }); + return TaskRunResult.progress(); +} + async function processPeerPullCreditKycRequired( wex: WalletExecutionContext, peerIni: PeerPullCreditRecord, @@ -1375,6 +1537,16 @@ export function computePeerPullCreditTransactionState( major: TransactionMajorState.Aborting, minor: TransactionMinorState.DeletePurse, }; + case PeerPullPaymentCreditStatus.PendingBalanceKycRequired: + return { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.BalanceKycRequired, + }; + case PeerPullPaymentCreditStatus.SuspendedBalanceKycRequired: + return { + major: TransactionMajorState.Suspended, + minor: TransactionMinorState.BalanceKycRequired, + }; } } @@ -1430,5 +1602,9 @@ export function computePeerPullCreditTransactionActions( return [TransactionAction.Delete]; case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse: return [TransactionAction.Resume, TransactionAction.Fail]; + case PeerPullPaymentCreditStatus.PendingBalanceKycRequired: + return [TransactionAction.Suspend, TransactionAction.Abort]; + case PeerPullPaymentCreditStatus.SuspendedBalanceKycRequired: + return [TransactionAction.Resume, TransactionAction.Abort]; } } diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/pay-peer-push-credit.ts index b7cead3fa..6f2b15d20 100644 --- a/packages/taler-wallet-core/src/pay-peer-push-credit.ts +++ b/packages/taler-wallet-core/src/pay-peer-push-credit.ts @@ -59,6 +59,8 @@ import { TaskRunResult, TombstoneTag, TransactionContext, + TransitionResult, + TransitionResultType, constructTaskIdentifier, requireExchangeTosAcceptedOrThrow, } from "./common.js"; @@ -69,13 +71,20 @@ import { PeerPushPaymentIncomingRecord, WalletDbAllStoresReadOnlyTransaction, WalletDbReadWriteTransaction, + WalletDbStoresArr, WithdrawalGroupRecord, WithdrawalGroupStatus, WithdrawalRecordType, timestampPreciseFromDb, timestampPreciseToDb, } from "./db.js"; -import { fetchFreshExchange, getScopeForAllExchanges } from "./exchanges.js"; +import { + checkIncomingAmountLegalUnderKycBalanceThreshold, + fetchFreshExchange, + getScopeForAllExchanges, + handleStartExchangeWalletKyc, + waitIncomingAmountLegalUnderKycBalanceThreshold, +} from "./exchanges.js"; import { codecForExchangePurseStatus, getMergeReserveInfo, @@ -116,6 +125,91 @@ export class PeerPushCreditTransactionContext implements TransactionContext { }); } + /** + * Transition a peer-push-credit transaction. + * Extra object stores may be accessed during the transition. + */ + async transition<StoreNameArray extends WalletDbStoresArr = []>( + opts: { extraStores?: StoreNameArray; transactionLabel?: string }, + f: ( + rec: PeerPushPaymentIncomingRecord | undefined, + tx: WalletDbReadWriteTransaction< + [ + "peerPushCredit", + "transactionsMeta", + "operationRetries", + "exchanges", + "exchangeDetails", + ...StoreNameArray, + ] + >, + ) => Promise<TransitionResult<PeerPushPaymentIncomingRecord>>, + ): Promise<TransitionInfo | undefined> { + const baseStores = [ + "peerPushCredit" as const, + "transactionsMeta" as const, + "operationRetries" as const, + "exchanges" as const, + "exchangeDetails" as const, + ]; + const stores = opts.extraStores + ? [...baseStores, ...opts.extraStores] + : baseStores; + + let errorThrown: Error | undefined; + const transitionInfo = await this.wex.db.runReadWriteTx( + { storeNames: stores }, + async (tx) => { + const rec = await tx.peerPushCredit.get(this.peerPushCreditId); + let oldTxState: TransactionState; + if (rec) { + oldTxState = computePeerPushCreditTransactionState(rec); + } else { + oldTxState = { + major: TransactionMajorState.None, + }; + } + let res: TransitionResult<PeerPushPaymentIncomingRecord> | undefined; + try { + res = await f(rec, tx); + } catch (error) { + if (error instanceof Error) { + errorThrown = error; + } + return undefined; + } + + switch (res.type) { + case TransitionResultType.Transition: { + await tx.peerPushCredit.put(res.rec); + await this.updateTransactionMeta(tx); + const newTxState = computePeerPushCreditTransactionState(res.rec); + return { + oldTxState, + newTxState, + }; + } + case TransitionResultType.Delete: + await tx.peerPushCredit.delete(this.peerPushCreditId); + await this.updateTransactionMeta(tx); + return { + oldTxState, + newTxState: { + major: TransactionMajorState.None, + }, + }; + default: + return undefined; + } + }, + ); + if (errorThrown) { + throw errorThrown; + } + notifyTransition(this.wex, this.transactionId, transitionInfo); + return transitionInfo; + } + async updateTransactionMeta( tx: WalletDbReadWriteTransaction<["peerPushCredit", "transactionsMeta"]>, ): Promise<void> { @@ -284,6 +378,12 @@ export class PeerPushCreditTransactionContext implements TransactionContext { case PeerPushCreditStatus.SuspendedMerge: case PeerPushCreditStatus.SuspendedMergeKycRequired: case PeerPushCreditStatus.SuspendedWithdrawing: + case PeerPushCreditStatus.SuspendedBalanceKycRequired: + case PeerPushCreditStatus.Failed: + case PeerPushCreditStatus.Aborted: + break; + case PeerPushCreditStatus.PendingBalanceKycRequired: + newStatus = PeerPushCreditStatus.SuspendedBalanceKycRequired; break; case PeerPushCreditStatus.PendingMergeKycRequired: newStatus = PeerPushCreditStatus.SuspendedMergeKycRequired; @@ -295,10 +395,6 @@ export class PeerPushCreditTransactionContext implements TransactionContext { // FIXME: Suspend internal withdrawal transaction! newStatus = PeerPushCreditStatus.SuspendedWithdrawing; break; - case PeerPushCreditStatus.Aborted: - break; - case PeerPushCreditStatus.Failed: - break; default: assertUnreachable(pushCreditRec.status); } @@ -334,29 +430,21 @@ export class PeerPushCreditTransactionContext implements TransactionContext { } let newStatus: PeerPushCreditStatus | undefined = undefined; switch (pushCreditRec.status) { - case PeerPushCreditStatus.DialogProposed: - newStatus = PeerPushCreditStatus.Aborted; - break; + case PeerPushCreditStatus.Failed: + case PeerPushCreditStatus.Aborted: case PeerPushCreditStatus.Done: break; case PeerPushCreditStatus.SuspendedMerge: + case PeerPushCreditStatus.DialogProposed: case PeerPushCreditStatus.SuspendedMergeKycRequired: case PeerPushCreditStatus.SuspendedWithdrawing: - newStatus = PeerPushCreditStatus.Aborted; - break; + case PeerPushCreditStatus.PendingBalanceKycRequired: + case PeerPushCreditStatus.SuspendedBalanceKycRequired: + case PeerPushCreditStatus.PendingWithdrawing: case PeerPushCreditStatus.PendingMergeKycRequired: - newStatus = PeerPushCreditStatus.Aborted; - break; case PeerPushCreditStatus.PendingMerge: newStatus = PeerPushCreditStatus.Aborted; break; - case PeerPushCreditStatus.PendingWithdrawing: - newStatus = PeerPushCreditStatus.Aborted; - break; - case PeerPushCreditStatus.Aborted: - break; - case PeerPushCreditStatus.Failed: - break; default: assertUnreachable(pushCreditRec.status); } @@ -397,6 +485,10 @@ export class PeerPushCreditTransactionContext implements TransactionContext { case PeerPushCreditStatus.PendingMergeKycRequired: case PeerPushCreditStatus.PendingMerge: case PeerPushCreditStatus.PendingWithdrawing: + case PeerPushCreditStatus.Aborted: + case PeerPushCreditStatus.Failed: + case PeerPushCreditStatus.PendingBalanceKycRequired: + break; case PeerPushCreditStatus.SuspendedMerge: newStatus = PeerPushCreditStatus.PendingMerge; break; @@ -407,9 +499,8 @@ export class PeerPushCreditTransactionContext implements TransactionContext { // FIXME: resume underlying "internal-withdrawal" transaction. newStatus = PeerPushCreditStatus.PendingWithdrawing; break; - case PeerPushCreditStatus.Aborted: - break; - case PeerPushCreditStatus.Failed: + case PeerPushCreditStatus.SuspendedBalanceKycRequired: + newStatus = PeerPushCreditStatus.PendingBalanceKycRequired; break; default: assertUnreachable(pushCreditRec.status); @@ -458,6 +549,8 @@ export class PeerPushCreditTransactionContext implements TransactionContext { case PeerPushCreditStatus.SuspendedMerge: case PeerPushCreditStatus.SuspendedMergeKycRequired: case PeerPushCreditStatus.SuspendedWithdrawing: + case PeerPushCreditStatus.PendingBalanceKycRequired: + case PeerPushCreditStatus.SuspendedBalanceKycRequired: newStatus = PeerPushCreditStatus.Failed; break; default: @@ -813,6 +906,31 @@ async function handlePendingMerge( const { peerPushCreditId } = peerInc; const ctx = new PeerPushCreditTransactionContext(wex, peerPushCreditId); + const kycCheckRes = await checkIncomingAmountLegalUnderKycBalanceThreshold( + wex, + peerInc.exchangeBaseUrl, + peerInc.estimatedAmountEffective, + ); + + if (kycCheckRes.result === "violation") { + // Do this before we transition so that the exchange is already in the right state. + await handleStartExchangeWalletKyc(wex, { + amount: kycCheckRes.nextThreshold, + exchangeBaseUrl: peerInc.exchangeBaseUrl, + }); + await ctx.transition({}, async (rec) => { + if (!rec) { + return TransitionResult.stay(); + } + if (rec.status !== PeerPushCreditStatus.PendingMerge) { + return TransitionResult.stay(); + } + rec.status = PeerPushCreditStatus.PendingBalanceKycRequired; + return TransitionResult.transition(rec); + }); + return TaskRunResult.progress(); + } + const amount = Amounts.parseOrThrow(contractTerms.amount); // FIXME: What if this changes? Should be part of the p2p record @@ -1057,18 +1175,45 @@ export async function processPeerPushCredit( peerInc.kycInfo, ); } - - case PeerPushCreditStatus.PendingMerge: + case PeerPushCreditStatus.PendingMerge: { return handlePendingMerge(wex, peerInc, contractTerms); - - case PeerPushCreditStatus.PendingWithdrawing: + } + case PeerPushCreditStatus.PendingWithdrawing: { return handlePendingWithdrawing(wex, peerInc); - + } + case PeerPushCreditStatus.PendingBalanceKycRequired: { + return await processPeerPushCreditBalanceKyc(ctx, peerInc); + } default: return TaskRunResult.finished(); } } +async function processPeerPushCreditBalanceKyc( + ctx: PeerPushCreditTransactionContext, + peerInc: PeerPushPaymentIncomingRecord, +): Promise<TaskRunResult> { + const exchangeBaseUrl = peerInc.exchangeBaseUrl; + const amount = peerInc.estimatedAmountEffective; + + await waitIncomingAmountLegalUnderKycBalanceThreshold( + ctx.wex, + exchangeBaseUrl, + amount, + ); + await ctx.transition({}, async (rec) => { + if (!rec) { + return TransitionResult.stay(); + } + if (rec.status !== PeerPushCreditStatus.PendingBalanceKycRequired) { + return TransitionResult.stay(); + } + rec.status = PeerPushCreditStatus.PendingMerge; + return TransitionResult.transition(rec); + }); + return TaskRunResult.progress(); +} + export async function confirmPeerPushCredit( wex: WalletExecutionContext, req: ConfirmPeerPushCreditRequest, @@ -1170,6 +1315,16 @@ export function computePeerPushCreditTransactionState( return { major: TransactionMajorState.Failed, }; + case PeerPushCreditStatus.PendingBalanceKycRequired: + return { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.BalanceKycRequired, + }; + case PeerPushCreditStatus.SuspendedBalanceKycRequired: + return { + major: TransactionMajorState.Suspended, + minor: TransactionMinorState.BalanceKycRequired, + }; default: assertUnreachable(pushCreditRecord.status); } @@ -1207,6 +1362,10 @@ export function computePeerPushCreditTransactionActions( return [TransactionAction.Resume, TransactionAction.Abort]; case PeerPushCreditStatus.SuspendedWithdrawing: return [TransactionAction.Resume, TransactionAction.Fail]; + case PeerPushCreditStatus.PendingBalanceKycRequired: + return [TransactionAction.Suspend, TransactionAction.Abort]; + case PeerPushCreditStatus.SuspendedBalanceKycRequired: + return [TransactionAction.Resume, TransactionAction.Abort]; case PeerPushCreditStatus.Aborted: return [TransactionAction.Delete]; case PeerPushCreditStatus.Failed: |