/** * 合约错误处理工具 */ const USER_REJECTION_PATTERNS = [ 'User rejected', 'User denied', 'user rejected', 'rejected the request', ] /** 判断是否为用户拒绝交易 */ export function isUserRejection(err: unknown): boolean { const msg = getErrorMessage(err) return USER_REJECTION_PATTERNS.some(pattern => msg.includes(pattern)) } /** 解析合约 revert 原因 */ export function parseContractError(err: unknown, fallback = 'Transaction failed'): string { if (!err) return fallback const msg = getErrorMessage(err) const match = msg.match(/reverted with reason string '(.+?)'/) || msg.match(/execution reverted: (.+?)(?:\n|$)/) || msg.match(/reverted: (.+?)(?:\n|$)/) if (match) return match[1] const short = getShortMessage(err) if (short) return short return fallback } /** 统一 catch 块处理 */ export function handleContractCatchError( err: unknown, fallback: string, setError: (msg: string) => void, setStatus: (status: string) => void, ): void { if (isUserRejection(err)) { setError('Transaction cancelled') setStatus('idle') } else { setError(parseContractError(err, fallback)) setStatus('error') } } /** 校验金额是否为合法正数 */ export function isValidAmount(amount: string): boolean { const n = Number(amount) return !isNaN(n) && isFinite(n) && n > 0 } /** 安全解析浮点数,NaN/Infinity → 0 */ export function safeParseFloat(value: string | number): number { const n = typeof value === 'string' ? parseFloat(value) : value if (isNaN(n) || !isFinite(n)) return 0 return n } function getErrorMessage(err: unknown): string { if (err instanceof Error) return err.message if (typeof err === 'object' && err !== null) { const e = err as Record if (typeof e.message === 'string') return e.message if (typeof e.shortMessage === 'string') return e.shortMessage } return String(err) } function getShortMessage(err: unknown): string | undefined { if (typeof err === 'object' && err !== null) { const e = err as Record if (typeof e.shortMessage === 'string') return e.shortMessage } return undefined }