Skip to content

Commit

Permalink
chore: Allow cancelling txs that already errored
Browse files Browse the repository at this point in the history
  • Loading branch information
arcoraven committed Apr 10, 2024
1 parent b703dd3 commit 8d37d3b
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/server/routes/transaction/cancel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const responseBodySchema = Type.Object({

responseBodySchema.example = {
result: {
qeueuId: "a20ed4ce-301d-4251-a7af-86bd88f6c015",
queueId: "a20ed4ce-301d-4251-a7af-86bd88f6c015",
status: "success",
},
};
Expand Down
126 changes: 80 additions & 46 deletions src/server/utils/transaction.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { TransactionResponse } from "@ethersproject/abstract-provider";
import { getDefaultGasOverrides } from "@thirdweb-dev/sdk";
import { StatusCodes } from "http-status-codes";
import { getTxById } from "../../db/transactions/getTxById";
Expand All @@ -25,10 +24,6 @@ export const cancelTransactionAndUpdate = async ({
};
}

let message = "";
let error = null;
let transferTransactionResult: TransactionResponse | null = null;

if (txData.signerAddress && txData.accountAddress) {
switch (txData.status) {
case TransactionStatus.Errored:
Expand Down Expand Up @@ -62,25 +57,44 @@ export const cancelTransactionAndUpdate = async ({
status: TransactionStatus.Cancelled,
},
});
message = "Transaction cancelled on-database successfully.";
break;
return {
message: "Transaction cancelled on-database successfully.",
};
}
} else {
switch (txData.status) {
case TransactionStatus.Errored:
error = createCustomError(
case TransactionStatus.Errored: {
if (!txData.chainId || !txData.fromAddress) {
throw new Error("Invalid transaction state to cancel.");
}
if (txData.nonce) {
const { transactionHash, error } = await sendNullTransaction({
chainId: parseInt(txData.chainId),
walletAddress: txData.fromAddress,
nonce: txData.nonce,
});
if (error) {
return { message: error };
}

return {
message: "Transaction cancelled successfully.",
transactionHash,
};
}

throw createCustomError(
`Transaction has already errored: ${txData.errorMessage}`,
StatusCodes.BAD_REQUEST,
"TransactionErrored",
);
break;
}
case TransactionStatus.Cancelled:
error = createCustomError(
throw createCustomError(
"Transaction is already cancelled.",
StatusCodes.BAD_REQUEST,
"TransactionAlreadyCancelled",
);
break;
case TransactionStatus.Queued:
await updateTx({
queueId,
Expand All @@ -89,40 +103,28 @@ export const cancelTransactionAndUpdate = async ({
status: TransactionStatus.Cancelled,
},
});
message = "Transaction cancelled successfully.";
break;
return {
message: "Transaction cancelled successfully.",
};
case TransactionStatus.Mined:
error = createCustomError(
throw createCustomError(
"Transaction already mined.",
StatusCodes.BAD_REQUEST,
"TransactionAlreadyMined",
);
break;
case TransactionStatus.Sent: {
const sdk = await getSdk({
chainId: parseInt(txData.chainId!),
walletAddress: txData.fromAddress!,
});

const txReceipt = await sdk
.getProvider()
.getTransactionReceipt(txData.transactionHash!);
if (txReceipt) {
message = "Transaction already mined.";
break;
if (!txData.chainId || !txData.fromAddress || !txData.nonce) {
throw new Error("Invalid transaction state to cancel.");
}

const gasOverrides = await getDefaultGasOverrides(sdk.getProvider());
transferTransactionResult = await sdk.wallet.sendRawTransaction({
to: txData.fromAddress!,
from: txData.fromAddress!,
data: "0x",
value: "0x00",
nonce: txData.nonce!,
...multiplyGasOverrides(gasOverrides, 2),
const { transactionHash, error } = await sendNullTransaction({
chainId: parseInt(txData.chainId),
walletAddress: txData.fromAddress,
nonce: txData.nonce,
});

message = "Transaction cancelled successfully.";
if (error) {
return { message: error };
}

await updateTx({
queueId,
Expand All @@ -131,19 +133,51 @@ export const cancelTransactionAndUpdate = async ({
status: TransactionStatus.Cancelled,
},
});
break;
return {
message: "Transaction cancelled successfully.",
transactionHash,
};
}
default:
break;
}
}

if (error) {
throw error;
throw new Error("Unhandled cancellation state.");
};

const sendNullTransaction = async (args: {
chainId: number;
walletAddress: string;
nonce: number;
transactionHash?: string;
}): Promise<{
transactionHash?: string;
error?: string;
}> => {
const { chainId, walletAddress, nonce, transactionHash } = args;

const sdk = await getSdk({ chainId, walletAddress });
const provider = sdk.getProvider();

// Skip if the tx is already mined.
if (transactionHash) {
const txReceipt = await provider.getTransactionReceipt(transactionHash);
if (txReceipt) {
return { error: "Transaction already mined." };
}
}

return {
message,
transactionHash: transferTransactionResult?.hash,
};
try {
const gasOverrides = await getDefaultGasOverrides(provider);
const { hash } = await sdk.wallet.sendRawTransaction({
to: walletAddress,
from: walletAddress,
data: "0x",
value: "0",
nonce,
...multiplyGasOverrides(gasOverrides, 2),
});
return { transactionHash: hash };
} catch (e: any) {
return { error: e.toString() };
}
};

0 comments on commit 8d37d3b

Please sign in to comment.