aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/taler-wallet-core/src/db.ts5
-rw-r--r--packages/taler-wallet-core/src/pay-peer-pull-credit.ts182
-rw-r--r--packages/taler-wallet-core/src/pay-peer-push-credit.ts213
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: