-withdrawal notifications
This commit is contained in:
parent
c4f5c83b8e
commit
16d30adf0d
@ -1680,21 +1680,15 @@ export const codecForResumeTransaction = (): Codec<ResumeTransactionRequest> =>
|
||||
|
||||
export interface AbortTransactionRequest {
|
||||
transactionId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the payment immediately into an aborted state.
|
||||
* The UI should warn the user that this might lead
|
||||
* to money being lost.
|
||||
*
|
||||
* Defaults to false.
|
||||
*/
|
||||
forceImmediateAbort?: boolean;
|
||||
export interface CancelAbortingTransactionRequest {
|
||||
transactionId: string;
|
||||
}
|
||||
|
||||
export const codecForAbortTransaction = (): Codec<AbortTransactionRequest> =>
|
||||
buildCodecForObject<AbortTransactionRequest>()
|
||||
.property("transactionId", codecForString())
|
||||
.property("forceImmediateAbort", codecOptional(codecForBoolean()))
|
||||
.build("AbortTransactionRequest");
|
||||
|
||||
export interface DepositGroupFees {
|
||||
|
@ -443,6 +443,21 @@ transactionsCli
|
||||
});
|
||||
});
|
||||
|
||||
transactionsCli
|
||||
.subcommand("cancelAbortingTransaction", "suspend", {
|
||||
help: "Cancel the attempt of properly aborting a transaction.",
|
||||
})
|
||||
.requiredArgument("transactionId", clk.STRING, {
|
||||
help: "Identifier of the transaction to cancel aborting.",
|
||||
})
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
await wallet.client.call(WalletApiOperation.CancelAbortingTransaction, {
|
||||
transactionId: args.cancelAbortingTransaction.transactionId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
transactionsCli
|
||||
.subcommand("resumeTransaction", "resume", {
|
||||
help: "Resume a transaction.",
|
||||
@ -484,14 +499,10 @@ transactionsCli
|
||||
.requiredArgument("transactionId", clk.STRING, {
|
||||
help: "Identifier of the transaction to delete",
|
||||
})
|
||||
.flag("force", ["--force"], {
|
||||
help: "Force aborting the transaction. Might lose money.",
|
||||
})
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
await wallet.client.call(WalletApiOperation.AbortTransaction, {
|
||||
transactionId: args.abortTransaction.transactionId,
|
||||
forceImmediateAbort: args.abortTransaction.force,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -26,6 +26,7 @@ import {
|
||||
ExtendedStatus,
|
||||
j2s,
|
||||
Logger,
|
||||
NotificationType,
|
||||
OrderShortInfo,
|
||||
PaymentStatus,
|
||||
PeerContractTerms,
|
||||
@ -38,6 +39,7 @@ import {
|
||||
TransactionMajorState,
|
||||
TransactionsRequest,
|
||||
TransactionsResponse,
|
||||
TransactionState,
|
||||
TransactionType,
|
||||
WithdrawalType,
|
||||
} from "@gnu-taler/taler-util";
|
||||
@ -94,6 +96,7 @@ import { processPeerPullCredit } from "./pay-peer.js";
|
||||
import { processRefreshGroup } from "./refresh.js";
|
||||
import { computeTipTransactionStatus, processTip } from "./tip.js";
|
||||
import {
|
||||
abortWithdrawalTransaction,
|
||||
augmentPaytoUrisForWithdrawal,
|
||||
computeWithdrawalTransactionStatus,
|
||||
processWithdrawalGroup,
|
||||
@ -1854,24 +1857,55 @@ export async function deleteTransaction(
|
||||
export async function abortTransaction(
|
||||
ws: InternalWalletState,
|
||||
transactionId: string,
|
||||
forceImmediateAbort?: boolean,
|
||||
): Promise<void> {
|
||||
const { type, args: rest } = parseId("txn", transactionId);
|
||||
const txId = parseTransactionIdentifier(transactionId);
|
||||
if (!txId) {
|
||||
throw Error("invalid transaction identifier");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
switch (txId.tag) {
|
||||
case TransactionType.Payment: {
|
||||
const proposalId = rest[0];
|
||||
await abortPay(ws, proposalId, forceImmediateAbort);
|
||||
await abortPay(ws, txId.proposalId);
|
||||
break;
|
||||
}
|
||||
case TransactionType.PeerPushDebit: {
|
||||
case TransactionType.Withdrawal: {
|
||||
await abortWithdrawalTransaction(ws, txId.withdrawalGroupId);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const unknownTxType: any = type;
|
||||
const unknownTxType: any = txId.tag;
|
||||
throw Error(
|
||||
`can't abort a '${unknownTxType}' transaction: not yet implemented`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface TransitionInfo {
|
||||
oldTxState: TransactionState;
|
||||
newTxState: TransactionState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify of a state transition if necessary.
|
||||
*/
|
||||
export function notifyTransition(
|
||||
ws: InternalWalletState,
|
||||
transactionId: string,
|
||||
ti: TransitionInfo | undefined,
|
||||
): void {
|
||||
if (
|
||||
ti &&
|
||||
!(
|
||||
ti.oldTxState.major === ti.newTxState.major &&
|
||||
ti.oldTxState.minor === ti.newTxState.minor
|
||||
)
|
||||
) {
|
||||
ws.notify({
|
||||
type: NotificationType.TransactionStateTransition,
|
||||
oldTxState: ti.oldTxState,
|
||||
newTxState: ti.newTxState,
|
||||
transactionId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +132,7 @@ import {
|
||||
import { PendingTaskType, isWithdrawableDenom } from "../index.js";
|
||||
import {
|
||||
constructTransactionIdentifier,
|
||||
notifyTransition,
|
||||
stopLongpolling,
|
||||
} from "./transactions.js";
|
||||
|
||||
@ -149,7 +150,7 @@ export async function suspendWithdrawalTransaction(
|
||||
withdrawalGroupId,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const stateUpdate = await ws.db
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.withdrawalGroups])
|
||||
.runReadWrite(async (tx) => {
|
||||
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||
@ -198,24 +199,18 @@ export async function suspendWithdrawalTransaction(
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (stateUpdate) {
|
||||
ws.notify({
|
||||
type: NotificationType.TransactionStateTransition,
|
||||
transactionId: constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
}),
|
||||
oldTxState: stateUpdate.oldTxState,
|
||||
newTxState: stateUpdate.newTxState,
|
||||
});
|
||||
}
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function resumeWithdrawalTransaction(
|
||||
ws: InternalWalletState,
|
||||
withdrawalGroupId: string,
|
||||
) {
|
||||
const stateUpdate = await ws.db
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.withdrawalGroups])
|
||||
.runReadWrite(async (tx) => {
|
||||
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||
@ -264,17 +259,11 @@ export async function resumeWithdrawalTransaction(
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (stateUpdate) {
|
||||
ws.notify({
|
||||
type: NotificationType.TransactionStateTransition,
|
||||
transactionId: constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
}),
|
||||
oldTxState: stateUpdate.oldTxState,
|
||||
newTxState: stateUpdate.newTxState,
|
||||
});
|
||||
}
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
export async function abortWithdrawalTransaction(
|
||||
@ -285,8 +274,12 @@ export async function abortWithdrawalTransaction(
|
||||
tag: PendingTaskType.Withdraw,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const stateUpdate = await ws.db
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.withdrawalGroups])
|
||||
.runReadWrite(async (tx) => {
|
||||
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||
@ -339,18 +332,7 @@ export async function abortWithdrawalTransaction(
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (stateUpdate) {
|
||||
ws.notify({
|
||||
type: NotificationType.TransactionStateTransition,
|
||||
transactionId: constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
}),
|
||||
oldTxState: stateUpdate.oldTxState,
|
||||
newTxState: stateUpdate.newTxState,
|
||||
});
|
||||
}
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
}
|
||||
|
||||
// Called "cancel" in the spec right now,
|
||||
@ -363,6 +345,10 @@ export async function cancelAbortingWithdrawalTransaction(
|
||||
tag: PendingTaskType.Withdraw,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
stopLongpolling(ws, taskId);
|
||||
const stateUpdate = await ws.db
|
||||
.mktx((x) => [x.withdrawalGroups])
|
||||
@ -392,21 +378,9 @@ export async function cancelAbortingWithdrawalTransaction(
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
if (stateUpdate) {
|
||||
ws.notify({
|
||||
type: NotificationType.TransactionStateTransition,
|
||||
transactionId: constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
}),
|
||||
oldTxState: stateUpdate.oldTxState,
|
||||
newTxState: stateUpdate.newTxState,
|
||||
});
|
||||
}
|
||||
notifyTransition(ws, transactionId, stateUpdate);
|
||||
}
|
||||
|
||||
|
||||
export function computeWithdrawalTransactionStatus(
|
||||
wgRecord: WithdrawalGroupRecord,
|
||||
): TransactionState {
|
||||
@ -1140,6 +1114,10 @@ async function queryReserve(
|
||||
withdrawalGroupId: string,
|
||||
cancellationToken: CancellationToken,
|
||||
): Promise<{ ready: boolean }> {
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
const withdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, {
|
||||
withdrawalGroupId,
|
||||
});
|
||||
@ -1190,25 +1168,31 @@ async function queryReserve(
|
||||
|
||||
logger.trace(`got reserve status ${j2s(result.response)}`);
|
||||
|
||||
await ws.db
|
||||
const transitionResult = await ws.db
|
||||
.mktx((x) => [x.withdrawalGroups])
|
||||
.runReadWrite(async (tx) => {
|
||||
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||
if (!wg) {
|
||||
logger.warn(`withdrawal group ${withdrawalGroupId} not found`);
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
const txStateOld = computeWithdrawalTransactionStatus(wg);
|
||||
wg.status = WithdrawalGroupStatus.Ready;
|
||||
const txStateNew = computeWithdrawalTransactionStatus(wg);
|
||||
wg.reserveBalanceAmount = Amounts.stringify(result.response.balance);
|
||||
await tx.withdrawalGroups.put(wg);
|
||||
return {
|
||||
oldTxState: txStateOld,
|
||||
newTxState: txStateNew,
|
||||
};
|
||||
});
|
||||
|
||||
notifyTransition(ws, transactionId, transitionResult);
|
||||
|
||||
// FIXME: This notification is deprecated with DD37
|
||||
ws.notify({
|
||||
type: NotificationType.WithdrawalGroupReserveReady,
|
||||
transactionId: makeTransactionId(
|
||||
TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
),
|
||||
transactionId,
|
||||
});
|
||||
|
||||
return { ready: true };
|
||||
@ -1252,6 +1236,10 @@ export async function processWithdrawalGroup(
|
||||
}
|
||||
|
||||
const retryTag = TaskIdentifiers.forWithdrawal(withdrawalGroup);
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
|
||||
// We're already running!
|
||||
if (ws.activeLongpoll[retryTag]) {
|
||||
@ -1322,17 +1310,24 @@ export async function processWithdrawalGroup(
|
||||
|
||||
if (withdrawalGroup.denomsSel.selectedDenoms.length === 0) {
|
||||
logger.warn("Finishing empty withdrawal group (no denoms)");
|
||||
await ws.db
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.withdrawalGroups])
|
||||
.runReadWrite(async (tx) => {
|
||||
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||
if (!wg) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
const txStatusOld = computeWithdrawalTransactionStatus(wg);
|
||||
wg.status = WithdrawalGroupStatus.Finished;
|
||||
wg.timestampFinish = TalerProtocolTimestamp.now();
|
||||
const txStatusNew = computeWithdrawalTransactionStatus(wg);
|
||||
await tx.withdrawalGroups.put(wg);
|
||||
return {
|
||||
oldTxState: txStatusOld,
|
||||
newTxState: txStatusNew,
|
||||
};
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
return {
|
||||
type: OperationAttemptResultType.Finished,
|
||||
result: undefined,
|
||||
@ -1421,6 +1416,7 @@ export async function processWithdrawalGroup(
|
||||
errorsPerCoin[x.coinIdx] = x.lastError;
|
||||
}
|
||||
});
|
||||
const oldTxState = computeWithdrawalTransactionStatus(wg);
|
||||
logger.info(`now withdrawn ${numFinished} of ${numTotalCoins} coins`);
|
||||
if (wg.timestampFinish === undefined && numFinished === numTotalCoins) {
|
||||
finishedForFirstTime = true;
|
||||
@ -1428,10 +1424,15 @@ export async function processWithdrawalGroup(
|
||||
wg.status = WithdrawalGroupStatus.Finished;
|
||||
}
|
||||
|
||||
const newTxState = computeWithdrawalTransactionStatus(wg);
|
||||
await tx.withdrawalGroups.put(wg);
|
||||
|
||||
return {
|
||||
kycInfo: wg.kycPending,
|
||||
transitionInfo: {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@ -1439,6 +1440,8 @@ export async function processWithdrawalGroup(
|
||||
throw Error("withdrawal group does not exist anymore");
|
||||
}
|
||||
|
||||
notifyTransition(ws, transactionId, res.transitionInfo);
|
||||
|
||||
const { kycInfo } = res;
|
||||
|
||||
if (numKycRequired > 0) {
|
||||
@ -1478,6 +1481,7 @@ export async function processWithdrawalGroup(
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME: Deprecated with DD37
|
||||
if (finishedForFirstTime) {
|
||||
ws.notify({
|
||||
type: NotificationType.WithdrawGroupFinished,
|
||||
@ -1838,6 +1842,10 @@ async function registerReserveWithBank(
|
||||
.runReadOnly(async (tx) => {
|
||||
return await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
switch (withdrawalGroup?.status) {
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
@ -1860,19 +1868,21 @@ async function registerReserveWithBank(
|
||||
selected_exchange: bankInfo.exchangePaytoUri,
|
||||
};
|
||||
logger.info(`registering reserve with bank: ${j2s(reqBody)}`);
|
||||
const httpResp = await ws.http.postJson(bankStatusUrl, reqBody, {
|
||||
const httpResp = await ws.http.fetch(bankStatusUrl, {
|
||||
method: "POST",
|
||||
body: reqBody,
|
||||
timeout: getReserveRequestTimeout(withdrawalGroup),
|
||||
});
|
||||
await readSuccessResponseJsonOrThrow(
|
||||
httpResp,
|
||||
codecForBankWithdrawalOperationPostResponse(),
|
||||
);
|
||||
await ws.db
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.withdrawalGroups])
|
||||
.runReadWrite(async (tx) => {
|
||||
const r = await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||
if (!r) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
switch (r.status) {
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
@ -1887,9 +1897,18 @@ async function registerReserveWithBank(
|
||||
r.wgInfo.bankInfo.timestampReserveInfoPosted = AbsoluteTime.toTimestamp(
|
||||
AbsoluteTime.now(),
|
||||
);
|
||||
const oldTxState = computeWithdrawalTransactionStatus(r);
|
||||
r.status = WithdrawalGroupStatus.WaitConfirmBank;
|
||||
const newTxState = computeWithdrawalTransactionStatus(r);
|
||||
await tx.withdrawalGroups.put(r);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
};
|
||||
});
|
||||
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
// FIXME: This notification is deprecated with DD37
|
||||
ws.notify({ type: NotificationType.ReserveRegisteredWithBank });
|
||||
}
|
||||
|
||||
@ -1904,6 +1923,10 @@ async function processReserveBankStatus(
|
||||
const withdrawalGroup = await getWithdrawalGroupRecordTx(ws.db, {
|
||||
withdrawalGroupId,
|
||||
});
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
switch (withdrawalGroup?.status) {
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
case WithdrawalGroupStatus.RegisteringBank:
|
||||
@ -1938,7 +1961,7 @@ async function processReserveBankStatus(
|
||||
|
||||
if (status.aborted) {
|
||||
logger.info("bank aborted the withdrawal");
|
||||
await ws.db
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.withdrawalGroups])
|
||||
.runReadWrite(async (tx) => {
|
||||
const r = await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||
@ -1956,10 +1979,17 @@ async function processReserveBankStatus(
|
||||
throw Error("invariant failed");
|
||||
}
|
||||
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
const oldTxState = computeWithdrawalTransactionStatus(r);
|
||||
r.wgInfo.bankInfo.timestampBankConfirmed = now;
|
||||
r.status = WithdrawalGroupStatus.BankAborted;
|
||||
const newTxState = computeWithdrawalTransactionStatus(r);
|
||||
await tx.withdrawalGroups.put(r);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
}
|
||||
});
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
return {
|
||||
status: BankStatusResultCode.Aborted,
|
||||
};
|
||||
@ -1977,12 +2007,12 @@ async function processReserveBankStatus(
|
||||
return await processReserveBankStatus(ws, withdrawalGroupId);
|
||||
}
|
||||
|
||||
await ws.db
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [x.withdrawalGroups])
|
||||
.runReadWrite(async (tx) => {
|
||||
const r = await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||
if (!r) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
// Re-check reserve status within transaction
|
||||
switch (r.status) {
|
||||
@ -1990,16 +2020,18 @@ async function processReserveBankStatus(
|
||||
case WithdrawalGroupStatus.WaitConfirmBank:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
if (r.wgInfo.withdrawalType !== WithdrawalRecordType.BankIntegrated) {
|
||||
throw Error("invariant failed");
|
||||
}
|
||||
const oldTxState = computeWithdrawalTransactionStatus(r);
|
||||
if (status.transfer_done) {
|
||||
logger.info("withdrawal: transfer confirmed by bank.");
|
||||
const now = AbsoluteTime.toTimestamp(AbsoluteTime.now());
|
||||
r.wgInfo.bankInfo.timestampBankConfirmed = now;
|
||||
r.status = WithdrawalGroupStatus.QueryingStatus;
|
||||
// FIXME: Notification is deprecated with DD37.
|
||||
ws.notify({
|
||||
type: NotificationType.WithdrawalGroupBankConfirmed,
|
||||
transactionId: makeTransactionId(
|
||||
@ -2012,9 +2044,16 @@ async function processReserveBankStatus(
|
||||
r.wgInfo.bankInfo.confirmUrl = status.confirm_transfer_url;
|
||||
r.senderWire = status.sender_wire;
|
||||
}
|
||||
const newTxState = computeWithdrawalTransactionStatus(r);
|
||||
await tx.withdrawalGroups.put(r);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
}
|
||||
});
|
||||
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
|
||||
if (status.transfer_done) {
|
||||
return {
|
||||
status: BankStatusResultCode.Done,
|
||||
@ -2071,6 +2110,11 @@ export async function internalCreateWithdrawalGroup(
|
||||
withdrawalGroupId = encodeCrock(getRandomBytes(32));
|
||||
}
|
||||
|
||||
const transactionId = constructTransactionIdentifier({
|
||||
tag: TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
|
||||
await updateWithdrawalDenoms(ws, canonExchange);
|
||||
const denoms = await getCandidateWithdrawalDenoms(ws, canonExchange);
|
||||
|
||||
@ -2122,7 +2166,7 @@ export async function internalCreateWithdrawalGroup(
|
||||
exchangeInfo.exchange,
|
||||
);
|
||||
|
||||
await ws.db
|
||||
const transitionInfo = await ws.db
|
||||
.mktx((x) => [
|
||||
x.withdrawalGroups,
|
||||
x.reserves,
|
||||
@ -2151,8 +2195,19 @@ export async function internalCreateWithdrawalGroup(
|
||||
uids: [encodeCrock(getRandomBytes(32))],
|
||||
});
|
||||
}
|
||||
|
||||
const oldTxState = {
|
||||
major: TransactionMajorState.None,
|
||||
}
|
||||
const newTxState = computeWithdrawalTransactionStatus(withdrawalGroup);
|
||||
return {
|
||||
oldTxState,
|
||||
newTxState,
|
||||
}
|
||||
});
|
||||
|
||||
notifyTransition(ws, transactionId, transitionInfo);
|
||||
|
||||
return withdrawalGroup;
|
||||
}
|
||||
|
||||
@ -2225,6 +2280,10 @@ export async function acceptWithdrawalFromUri(
|
||||
});
|
||||
|
||||
const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
|
||||
const transactionId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.Withdraw,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
|
||||
// We do this here, as the reserve should be registered before we return,
|
||||
// so that we can redirect the user to the bank's status page.
|
||||
@ -2249,10 +2308,7 @@ export async function acceptWithdrawalFromUri(
|
||||
return {
|
||||
reservePub: withdrawalGroup.reservePub,
|
||||
confirmTransferUrl: withdrawInfo.confirmTransferUrl,
|
||||
transactionId: makeTransactionId(
|
||||
TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
),
|
||||
transactionId,
|
||||
};
|
||||
}
|
||||
|
||||
@ -2285,6 +2341,10 @@ export async function createManualWithdrawal(
|
||||
});
|
||||
|
||||
const withdrawalGroupId = withdrawalGroup.withdrawalGroupId;
|
||||
const transactionId = constructTaskIdentifier({
|
||||
tag: PendingTaskType.Withdraw,
|
||||
withdrawalGroupId,
|
||||
});
|
||||
|
||||
const exchangePaytoUris = await ws.db
|
||||
.mktx((x) => [
|
||||
@ -2313,9 +2373,6 @@ export async function createManualWithdrawal(
|
||||
return {
|
||||
reservePub: withdrawalGroup.reservePub,
|
||||
exchangePaytoUris: exchangePaytoUris,
|
||||
transactionId: makeTransactionId(
|
||||
TransactionType.Withdrawal,
|
||||
withdrawalGroupId,
|
||||
),
|
||||
transactionId,
|
||||
};
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import {
|
||||
ApplyRefundResponse,
|
||||
BackupRecovery,
|
||||
BalancesResponse,
|
||||
CancelAbortingTransactionRequest,
|
||||
CheckPeerPullCreditRequest,
|
||||
CheckPeerPullCreditResponse,
|
||||
CheckPeerPushDebitRequest,
|
||||
@ -156,6 +157,7 @@ export enum WalletApiOperation {
|
||||
GetExchangeDetailedInfo = "getExchangeDetailedInfo",
|
||||
RetryPendingNow = "retryPendingNow",
|
||||
AbortTransaction = "abortTransaction",
|
||||
CancelAbortingTransaction = "cancelAbortingTransaction",
|
||||
SuspendTransaction = "suspendTransaction",
|
||||
ResumeTransaction = "resumeTransaction",
|
||||
ConfirmPay = "confirmPay",
|
||||
@ -327,6 +329,17 @@ export type AbortTransactionOp = {
|
||||
response: EmptyObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel aborting a transaction
|
||||
*
|
||||
* For payment transactions, it puts the payment into an "aborting" state.
|
||||
*/
|
||||
export type CancelAbortingTransactionOp = {
|
||||
op: WalletApiOperation.CancelAbortingTransaction;
|
||||
request: CancelAbortingTransactionRequest;
|
||||
response: EmptyObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* Suspend a transaction
|
||||
*/
|
||||
@ -922,6 +935,7 @@ export type WalletOperations = {
|
||||
[WalletApiOperation.WithdrawTestkudos]: WithdrawTestkudosOp;
|
||||
[WalletApiOperation.ConfirmPay]: ConfirmPayOp;
|
||||
[WalletApiOperation.AbortTransaction]: AbortTransactionOp;
|
||||
[WalletApiOperation.CancelAbortingTransaction]: CancelAbortingTransactionOp;
|
||||
[WalletApiOperation.SuspendTransaction]: SuspendTransactionOp;
|
||||
[WalletApiOperation.ResumeTransaction]: ResumeTransactionOp;
|
||||
[WalletApiOperation.GetBalances]: GetBalancesOp;
|
||||
|
@ -1221,7 +1221,7 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
|
||||
}
|
||||
case WalletApiOperation.AbortTransaction: {
|
||||
const req = codecForAbortTransaction().decode(payload);
|
||||
await abortTransaction(ws, req.transactionId, req.forceImmediateAbort);
|
||||
await abortTransaction(ws, req.transactionId);
|
||||
return {};
|
||||
}
|
||||
case WalletApiOperation.SuspendTransaction: {
|
||||
|
Loading…
Reference in New Issue
Block a user