Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 67 additions & 5 deletions src/services/anchor-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,41 @@ import { Errors } from "../errors";
export type AnchorSessionStatus =
| "incomplete"
| "pending_user_transfer_start"
| "pending_user"
| "pending_transaction_info_update"
| "pending_receiver"
| "pending_sender"
| "pending_stellar"
| "pending_trust"
| "pending_anchor"
| "completed"
| "error"
| "refunded";
| "refunded"
| "expired"
| "no_market"
| "too_small"
| "too_large";

export type AnchorTransitionSource = "user" | "webhook" | "poll";

/** The full set of statuses Mergepay tracks for an anchor session. */
export const ANCHOR_SESSION_STATUSES: readonly AnchorSessionStatus[] = [
"incomplete",
"pending_user_transfer_start",
"pending_user",
"pending_transaction_info_update",
"pending_receiver",
"pending_sender",
"pending_stellar",
"pending_trust",
"pending_anchor",
"completed",
"error",
"refunded",
"expired",
"no_market",
"too_small",
"too_large",
];

/**
Expand All @@ -48,16 +68,58 @@ export const ANCHOR_SESSION_STATUSES: readonly AnchorSessionStatus[] = [
* but never regresses back to a pending state.
*/
const ALLOWED_TRANSITIONS: Record<AnchorSessionStatus, readonly AnchorSessionStatus[]> = {
incomplete: ["pending_user_transfer_start", "pending_anchor", "error"],
pending_user_transfer_start: ["pending_anchor", "error", "refunded", "completed"],
pending_anchor: ["completed", "error", "refunded"],
incomplete: [
"pending_user_transfer_start", "pending_user", "pending_transaction_info_update",
"pending_receiver", "pending_sender", "pending_stellar", "pending_trust", "pending_anchor",
"error", "expired", "no_market", "too_small", "too_large",
],
pending_user_transfer_start: [
"pending_user", "pending_transaction_info_update", "pending_receiver", "pending_sender",
"pending_stellar", "pending_trust", "pending_anchor", "error", "refunded", "completed",
"expired", "no_market", "too_small", "too_large",
],
pending_user: [
"pending_transaction_info_update", "pending_receiver", "pending_sender", "pending_stellar",
"pending_trust", "pending_anchor", "error", "refunded", "completed", "expired", "no_market",
"too_small", "too_large",
],
pending_transaction_info_update: [
"pending_user", "pending_receiver", "pending_sender", "pending_stellar", "pending_trust",
"pending_anchor", "error", "refunded", "completed", "expired", "no_market", "too_small", "too_large",
],
pending_receiver: [
"pending_user", "pending_transaction_info_update", "pending_sender", "pending_stellar", "pending_trust",
"pending_anchor", "error", "refunded", "completed", "expired", "no_market", "too_small", "too_large",
],
pending_sender: [
"pending_user", "pending_transaction_info_update", "pending_receiver", "pending_stellar", "pending_trust",
"pending_anchor", "error", "refunded", "completed", "expired", "no_market", "too_small", "too_large",
],
pending_stellar: [
"pending_user", "pending_transaction_info_update", "pending_receiver", "pending_sender", "pending_trust",
"pending_anchor", "error", "refunded", "completed", "expired", "no_market", "too_small", "too_large",
],
pending_trust: [
"pending_user", "pending_transaction_info_update", "pending_receiver", "pending_sender", "pending_stellar",
"pending_anchor", "error", "refunded", "completed", "expired", "no_market", "too_small", "too_large",
],
pending_anchor: [
"pending_user", "pending_transaction_info_update", "pending_receiver", "pending_sender", "pending_stellar",
"pending_trust", "error", "refunded", "completed", "expired", "no_market", "too_small", "too_large",
],
completed: [],
error: ["refunded"],
refunded: [],
expired: [],
no_market: [],
too_small: [],
too_large: [],
};

export function isTerminalAnchorStatus(status: string): boolean {
return status === "completed" || status === "refunded";
return [
"completed", "error", "refunded", "expired", "no_market", "too_small", "too_large",
].includes(status);
}

export function canTransitionAnchorStatus(
Expand Down
17 changes: 15 additions & 2 deletions src/worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ async function scheduleRetry(params: {
}): Promise<void> {
const { job, attempt, category, reason, delayMs, ctx } = params;
const nextAttemptAt = new Date(Date.now() + delayMs);
const leaseExpiresAt = new Date(nextAttemptAt.getTime() + config.WORKER_LEASE_TIMEOUT_MS);

await prisma.settlement.update({
where: { id: job.id },
Expand All @@ -205,7 +206,7 @@ async function scheduleRetry(params: {
nextAttemptAt,
errorCategory: category,
failureReason: reason,
leaseExpiresAt: leaseDeadline(),
leaseExpiresAt,
},
});

Expand Down Expand Up @@ -907,7 +908,19 @@ export async function reconcileAnchors(): Promise<void> {

const sessions = await prisma.anchorSession.findMany({
where: {
status: { in: ["incomplete", "pending_user_transfer_start", "pending_anchor"] },
status: {
in: [
"incomplete",
"pending_user_transfer_start",
"pending_user",
"pending_transaction_info_update",
"pending_receiver",
"pending_sender",
"pending_stellar",
"pending_trust",
"pending_anchor",
],
},
externalTransactionId: { not: null },
AND: [
{ OR: [{ nextAttemptAt: null }, { nextAttemptAt: { lte: now } }] },
Expand Down
Loading