Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export function useHandleApprovalError(symbol: string | undefined): (error: unkn
} else {
captureError(error, ERROR_TYPES.ON_APPROVE)
approvalAnalytics('Error', symbol, extractErrorCode(err))
updateApproveProgressModalState({ error: error.message })
// Use shortMessage (viem) to avoid verbose error output with request arguments
const displayMessage = ('shortMessage' in error ? error.shortMessage : error.message) as string
updateApproveProgressModalState({ error: displayMessage })
}
},
[updateApproveProgressModalState, t, approvalAnalytics, symbol],
Expand Down
21 changes: 3 additions & 18 deletions apps/cowswap-frontend/src/modules/twap/utils/parseTwapError.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,7 @@
import { isRejectRequestProviderError } from '@cowprotocol/common-utils'
import { isRejectRequestProviderError, MAX_NESTED_ERROR_DEPTH } from '@cowprotocol/common-utils'

import { t } from '@lingui/core/macro'

const MAX_ERROR_DEPTH = 8

function isUserRejectionError(error: unknown, depth = 0): boolean {
if (depth > MAX_ERROR_DEPTH || error === null || error === undefined) {
return false
}
if (isRejectRequestProviderError(error)) {
return true
}
if (error instanceof Error && 'cause' in error && error.cause !== undefined) {
return isUserRejectionError(error.cause, depth + 1)
}
return false
}

function collectErrorMessageFromObject(error: object): string {
return 'message' in error ? String((error as { message: unknown }).message) : ''
}
Expand All @@ -25,7 +10,7 @@ function collectErrorMessageFromObject(error: object): string {
* Flattens `Error` + nested `cause` messages (viem / WalletConnect often nest "Request expired" here).
*/
function collectErrorMessages(error: unknown, depth = 0): string {
if (depth > MAX_ERROR_DEPTH) {
if (depth > MAX_NESTED_ERROR_DEPTH) {
return ''
}
if (error === null || error === undefined) {
Expand Down Expand Up @@ -67,7 +52,7 @@ function getWalletRequestExpiredMessage(flatMessage: string): string | undefined
export function getErrorMessage(error: unknown): string {
const DEFAULT_ERROR_MESSAGE = t`Something went wrong creating your order`

if (isUserRejectionError(error)) {
if (isRejectRequestProviderError(error)) {
return t`User rejected transaction`
}

Expand Down
50 changes: 34 additions & 16 deletions libs/common-utils/src/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface Market<T = string> {
quoteToken: T
}

export const MAX_NESTED_ERROR_DEPTH = 8
const PROVIDER_REJECT_REQUEST_CODES = [4001, -32000] // See https://eips.ethereum.org/EIPS/eip-1193
const PROVIDER_REJECT_REQUEST_ERROR_MESSAGES = [
'User denied message signature',
Expand Down Expand Up @@ -173,28 +174,45 @@ export function hashCode(text: string): number {
*/
// TODO: Add proper return type annotation
// TODO: Replace any with proper type definitions
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-explicit-any
export function isRejectRequestProviderError(error: any) {
if (error) {
// Check the error code is the user rejection as described in eip-1193
if (PROVIDER_REJECT_REQUEST_CODES.includes(error.code)) {
return true
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isRejectRequestProviderError(error: any, depth = 0): boolean {
if (!error || depth > MAX_NESTED_ERROR_DEPTH) {
return false
}

// Check for some specific messages returned by some wallets when rejecting requests
const message = getProviderErrorMessage(error)
if (
PROVIDER_REJECT_REQUEST_ERROR_MESSAGES.some(
(rejectMessage) => message && rejectMessage && message.toLowerCase().includes(rejectMessage.toLowerCase()),
)
) {
return true
}
if (isRejectionAtCurrentLevel(error)) {
return true
}

// Recurse into cause chain (viem/WalletConnect nest errors deeply)
const cause = error.cause
if (cause !== undefined && cause !== error) {
return isRejectRequestProviderError(cause, depth + 1)
}

return false
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isRejectionAtCurrentLevel(error: any): boolean {
// Check the error code is the user rejection as described in eip-1193
if (PROVIDER_REJECT_REQUEST_CODES.includes(error.code)) {
return true
}

// Check error message and viem's `details` property for wallet-specific rejection messages
const message = getProviderErrorMessage(error)
const details = typeof error.details === 'string' ? error.details : undefined

return matchesRejectionMessage(message) || matchesRejectionMessage(details)
}

function matchesRejectionMessage(message: string | undefined): boolean {
if (!message) return false
const lower = message.toLowerCase()
return PROVIDER_REJECT_REQUEST_ERROR_MESSAGES.some((rejectMessage) => lower.includes(rejectMessage.toLowerCase()))
}

/**
* Helper function that transforms a percentage into Basis Points (BPS)
* @param percent
Expand Down
Loading