From ee405e7170a285dacf55ec5f6ea2908caad257b2 Mon Sep 17 00:00:00 2001 From: holybasil Date: Thu, 25 Jul 2024 15:10:34 +0800 Subject: [PATCH] fix: detailed tx error message --- package.json | 2 +- src/pages/canvas/mint/flow/MintStep.tsx | 7 ++- src/services/canvasService.ts | 12 ++-- src/utils/index.ts | 1 + src/utils/txError.ts | 82 +++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 src/utils/txError.ts diff --git a/package.json b/package.json index 83051824f..28a14c68b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scroll.io", - "version": "5.1.0", + "version": "5.1.1", "private": false, "license": "MIT", "scripts": { diff --git a/src/pages/canvas/mint/flow/MintStep.tsx b/src/pages/canvas/mint/flow/MintStep.tsx index 47bd8a6c7..219791748 100644 --- a/src/pages/canvas/mint/flow/MintStep.tsx +++ b/src/pages/canvas/mint/flow/MintStep.tsx @@ -15,7 +15,7 @@ import useCheckViewport from "@/hooks/useCheckViewport" import useSnackbar from "@/hooks/useSnackbar" // import { testAsyncFunc } from "@/services/canvasService" import useCanvasStore from "@/stores/canvasStore" -import { isUserRejected, requireEnv, sentryDebug, trimErrorMessage } from "@/utils" +import { isUserRejected, recognizeError, requireEnv, sentryDebug, trimErrorMessage } from "@/utils" import InsufficientDialog from "./InsufficientDialog" import StepWrapper from "./StepWrapper" @@ -99,8 +99,9 @@ const MintStep = props => { } } catch (error) { if (!isUserRejected(error)) { - alertWarning(trimErrorMessage(error.message)) - sentryDebug(`mint canvas:${walletCurrentAddress}-${error.message}`) + const message = recognizeError(error) + alertWarning(trimErrorMessage(message)) + sentryDebug(`mint canvas:${walletCurrentAddress}-${message}`) } } finally { changeIsProfileMinting(false) diff --git a/src/services/canvasService.ts b/src/services/canvasService.ts index 57b9b5b49..7dd971616 100644 --- a/src/services/canvasService.ts +++ b/src/services/canvasService.ts @@ -6,7 +6,7 @@ import AttestProxyABI from "@/assets/abis/CanvasAttestProxy.json" import BadgeABI from "@/assets/abis/CanvasBadge.json" import ProfileABI from "@/assets/abis/CanvasProfile.json" import ProfileRegistryABI from "@/assets/abis/CanvasProfileRegistry.json" -import { checkDelegatedAttestation, decodeBadgePayload, isUserRejected, requireEnv, sentryDebug, trimErrorMessage } from "@/utils" +import { checkDelegatedAttestation, decodeBadgePayload, isUserRejected, recognizeError, requireEnv, sentryDebug, trimErrorMessage } from "@/utils" const EAS_GRAPHQL_URL = requireEnv("REACT_APP_EAS_GRAPHQL_URL") const BADGE_SCHEMA = requireEnv("REACT_APP_BADGE_SCHEMA") @@ -373,8 +373,9 @@ const mintBadge = async (provider, walletCurrentAddress, badge) => { if (isUserRejected(error)) { return false } else { - sentryDebug(`mint badge:${walletCurrentAddress}-${badge.badgeContract}-${error.message}`) - throw new Error(trimErrorMessage(error.message)) + const message = recognizeError(error) + sentryDebug(`mint badge:${walletCurrentAddress}-${badge.badgeContract}-${message}`) + throw new Error(trimErrorMessage(message)) } } } @@ -397,8 +398,9 @@ const upgradeBadge = async (provider, badge) => { if (isUserRejected(error)) { return false } else { - sentryDebug(`upgrade badge:${badge.id}-${error.message}`) - throw new Error(trimErrorMessage(error.message)) + const message = recognizeError(error) + sentryDebug(`upgrade badge:${badge.id}-${message}`) + throw new Error(trimErrorMessage(message)) } } } diff --git a/src/utils/index.ts b/src/utils/index.ts index 17b85cd9b..65fa62266 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -6,3 +6,4 @@ export * from "./nft" export * from "./dom" export * from "./blog" export * from "./canvas" +export * from "./txError" diff --git a/src/utils/txError.ts b/src/utils/txError.ts new file mode 100644 index 000000000..650d8c2f8 --- /dev/null +++ b/src/utils/txError.ts @@ -0,0 +1,82 @@ +import { Interface } from "ethers" + +const abi = [ + "error Unauthorized()", + "error CannotUpgrade(bytes32 uid)", + + // attestation errors + "error BadgeNotAllowed(address badge)", + "error BadgeNotFound(address badge)", + "error ExpirationDisabled()", + "error MissingPayload()", + "error ResolverPaymentsDisabled()", + "error RevocationDisabled()", + "error SingletonBadge()", + "error UnknownSchema()", + + // query errors + "error AttestationBadgeMismatch(bytes32 uid)", + "error AttestationExpired(bytes32 uid)", + "error AttestationNotFound(bytes32 uid)", + "error AttestationOwnerMismatch(bytes32 uid)", + "error AttestationRevoked(bytes32 uid)", + "error AttestationSchemaMismatch(bytes32 uid)", + + // profile errors + "error BadgeCountReached()", + "error LengthMismatch()", + "error TokenNotOwnedByUser(address token, uint256 tokenId)", + + // profile registry errors + "error CallerIsNotUserProfile()", + "error DuplicatedUsername()", + "error ExpiredSignature()", + "error ImplementationNotContract()", + "error InvalidReferrer()", + "error InvalidSignature()", + "error InvalidUsername()", + "error MsgValueMismatchWithMintFee()", + "error ProfileAlreadyMinted()", + + "error AccessDenied()", + + // EAS error + "error DeadlineExpired()", + "error InvalidEAS()", + "error InvalidLength()", + "error NotFound()", +] + +const IDENTIFIED_ERROR_MAP = { + AccessDenied: "Access Denied. Please contact the badge issuer for assistance", + SingletonBadge: "You have minted this badge before. Please wait for EAS to sync the data", + ExpiredSignature: "Invitation code signature has expired. Please refresh the page and try again", + InvalidSignature: "Invalid signature. Please contact the badge issuer for assistance", + DeadlineExpired: "The signature has expired. Please try again", + ProfileAlreadyMinted: "You have minted Canvas before. Please refresh the page to sync the latest data.", +} + +export const decodeErrorData = errSelector => { + const contract = new Interface(abi) + const parsedError = contract.parseError(errSelector) + return parsedError?.name +} + +export const recognizeError = error => { + if (error.code === "INSUFFICIENT_FUNDS") { + return "Transaction failed due to insufficient funds. Please ensure your account has enough balance." + } + if (error.code === "CALL_EXCEPTION") { + const unrecognized = "Transaction failed due to an unknown error. Please try again later." + // execution reverted + if (error.data) { + const type = decodeErrorData(error.data) + return type ? `${IDENTIFIED_ERROR_MAP[type] ? IDENTIFIED_ERROR_MAP[type] : "Execution reverted due to " + type}` : unrecognized + } + return unrecognized + } + if (error.code === "UNKNOWN_ERROR" && error.message.startsWith("could not coalesce error")) { + return error.error?.message || error.error?.data?.error?.message || "The PRC is busy, please try again later." + } + return error.message +}