@biconomy/abstractjs
Version:
SDK for Biconomy integration with support for account abstraction, smart accounts, ERC-4337.
115 lines • 6.27 kB
JavaScript
/**
* Final status types
*/
export const FINAL_STATUSES = ["FAILED", "MINED_SUCCESS", "MINED_FAIL"];
/**
* Calculate the overall transaction status based on individual userOp statuses.
*
* Status definitions:
* - PENDING: node waiting for conditions to be met
* - FAILED: off chain failure
* - MINING: tx broadcasted
* - MINED_SUCCESS: tx mined and userOp status: success
* - MINED_FAIL: tx mined and userOp status: fail
*
* Overall status calculation rules:
* 1. If any userOp has FAILED status, return "FAILED" (off-chain failure)
* 2. If any userOp has MINED_FAIL status, return "MINED_FAIL" (on-chain failure)
* 3. If any userOp has PENDING status, return "PENDING" (still waiting for conditions)
* 4. If any userOp has MINING status, return "MINING" (transaction in progress)
* 5. If all userOps have MINED_SUCCESS status, return "MINED_SUCCESS" (all successful)
* 6. Otherwise, return "PENDING" (default fallback)
*
* @param userOps - The user operations with their execution statuses
* @returns The calculated overall transaction status with finality information
*/
export const parseTransactionStatus = async (userOps, mode = "default") => {
// Handle empty userOps case
if (!userOps || userOps.length === 0) {
return {
status: "PENDING",
isFinalised: false,
message: ""
};
}
// Payment and cleanup userOps status are not considered for main status
const userOpsWithoutPaymentAndCleanup = userOps.filter((usop) => !usop.isCleanUpUserOp);
const statusMap = {
// If there is a cleanup user op failue ? Ignore it
hasFailedOps: userOpsWithoutPaymentAndCleanup.some((userOp) => userOp.executionStatus === "FAILED"),
// If there is a cleanup user op mining failue ? Ignore it
hasMinedFailOps: userOpsWithoutPaymentAndCleanup.some((userOp) => {
if (mode === "default") {
// If the block is not confirmed with sufficient block confirmations ? The userOps is still not considered as mined_fail until the confirmations is provided by node
return userOp.executionStatus === "MINED_FAIL" && userOp.isConfirmed;
}
return userOp.executionStatus === "MINED_FAIL";
}),
hasPendingOps: userOpsWithoutPaymentAndCleanup.some((userOp) => userOp.executionStatus === "PENDING"),
hasMiningOps: userOpsWithoutPaymentAndCleanup.some((userOp) => userOp.executionStatus === "MINING"),
// If there is a cleanup user op failue / mining failure ? Ignore it and mark the sprTx successful.
allMinedSuccess: userOpsWithoutPaymentAndCleanup.every((userOp) => {
if (mode === "default") {
// If the block is not confirmed with sufficient block confirmations ? The userOps is still not considered as mined_success until the confirmations is provided by node
return userOp.executionStatus === "MINED_SUCCESS" && userOp.isConfirmed;
}
return userOp.executionStatus === "MINED_SUCCESS";
}),
// Check if all userOps have a final state
allFinalised: userOpsWithoutPaymentAndCleanup.every((userOp) => {
if (mode === "default") {
// If the block is not confirmed with sufficient block confirmations ? The userOps is still not considered as finalized until the confirmations is provided by node
return (userOp.executionStatus === "FAILED" ||
(userOp.executionStatus === "MINED_FAIL" && userOp.isConfirmed) ||
(userOp.executionStatus === "MINED_SUCCESS" && userOp.isConfirmed));
}
return (userOp.executionStatus === "FAILED" ||
userOp.executionStatus === "MINED_FAIL" ||
userOp.executionStatus === "MINED_SUCCESS");
})
};
// Calculate status and finality
let status = "PENDING"; // Default status
let message = ""; // Default empty message
// Calculate overall status based on priority
if (statusMap.hasFailedOps) {
status = "FAILED";
// Find the first failed userOp to get error details
const failedUserOpIndex = userOpsWithoutPaymentAndCleanup.findIndex((userOp) => userOp.executionStatus === status);
const failedUserOp = userOpsWithoutPaymentAndCleanup[failedUserOpIndex];
message = `[${failedUserOpIndex}] ${failedUserOp?.executionError || "Transaction failed off-chain"}`;
}
else if (statusMap.hasMinedFailOps) {
status = "MINED_FAIL";
// Find the first mined-failed userOp to get error details
const minedFailUserOpIndex = userOpsWithoutPaymentAndCleanup.findIndex((userOp) => userOp.executionStatus === status);
const minedFailUserOp = userOpsWithoutPaymentAndCleanup[minedFailUserOpIndex];
message = `[${minedFailUserOpIndex}] ${minedFailUserOp?.executionError || "Transaction failed on-chain"}`;
}
else if (statusMap.hasMiningOps) {
status = "MINING";
const pendingUserOpIndex = userOpsWithoutPaymentAndCleanup.findIndex((userOp) => userOp.executionStatus === status);
message = `[${pendingUserOpIndex}] Transaction is mining, waiting for blockchain confirmation`;
}
else if (statusMap.hasPendingOps) {
status = "PENDING";
const pendingUserOpIndex = userOpsWithoutPaymentAndCleanup.findIndex((userOp) => userOp.executionStatus === status);
const pendingUserOp = userOpsWithoutPaymentAndCleanup[pendingUserOpIndex];
message = `[${pendingUserOpIndex}] ${pendingUserOp?.executionError || "Transaction is pending, waiting for conditions to be met"}`;
}
else if (statusMap.allMinedSuccess) {
status = "MINED_SUCCESS";
const minedSuccessUserOpIndex = userOpsWithoutPaymentAndCleanup.findIndex((userOp) => userOp.executionStatus === status);
message = `[${minedSuccessUserOpIndex}] Transaction executed successfully`;
}
const isFinalised = statusMap.allFinalised ||
statusMap.hasFailedOps ||
statusMap.hasMinedFailOps;
return {
status,
isFinalised,
message
};
};
export default parseTransactionStatus;
//# sourceMappingURL=parseTransactionStatus.js.map