MidnightDApp错误处理Skill midnight-dapp:error-handling

Midnight DApp错误处理技能专注于为基于零知识证明的区块链去中心化应用提供完整的错误管理解决方案。该技能涵盖错误分类、用户友好消息转换、恢复策略实现、React错误边界集成等技术要点,帮助开发者优雅处理证明生成失败、交易拒绝、网络断开等各类异常情况,提升DApp的稳定性和用户体验。关键词:Midnight DApp、错误处理、零知识证明、区块链错误分类、用户友好消息、React错误边界、交易错误恢复、网络错误处理、ZK证明失败、DApp开发。

DApp开发 0 次安装 3 次浏览 更新于 2/26/2026

name: midnight-dapp:error-handling description: 在Midnight DApp中实现错误处理时使用,包括向用户显示错误、处理证明生成失败、管理交易拒绝或处理网络断开连接。

错误处理

在Midnight DApp中优雅地处理错误,提供用户友好的消息、正确的错误分类和恢复策略。

何时使用

  • 以有帮助的方式向用户显示错误
  • 为React组件实现错误边界
  • 处理证明生成失败
  • 管理交易拒绝和合约错误
  • 处理网络断开连接(索引器、证明服务器)
  • 在DApp中创建一致的错误消息

关键概念

错误分类

Midnight DApp会遇到以下几类错误:

类别 来源 示例
证明错误 ZK证明生成 超时、约束违反、见证失败
交易错误 合约执行 余额不足、状态冲突、拒绝
网络错误 基础设施 索引器宕机、证明服务器不可用、WebSocket断开连接
钱包错误 用户交互 未安装、拒绝、错误网络

用户友好消息

技术错误应转换为可操作的消息:

// 技术错误
"电路约束在门1042处失败:输入 > 最大值"

// 用户友好消息
"金额超过允许的最大值。请输入较小的金额。"

恢复模式

不同的错误需要不同的恢复策略:

  • 可重试:超时、临时网络问题 - 提供重试按钮
  • 需要用户操作:错误网络、余额不足 - 引导用户
  • 致命错误:合约错误、无效状态 - 显示支持联系方式

参考资料

文档 描述
proof-errors.md ZK证明失败和诊断
transaction-errors.md 合约执行错误
network-errors.md 连接和超时处理
user-messaging.md 错误显示的UX指南

示例

示例 描述
error-boundary/ Midnight的React错误边界
error-toast/ Toast通知组件
typed-errors/ 自定义错误类和目录

快速开始

1. 创建类型化错误

import { MidnightError, ErrorCode } from './MidnightErrors';

// 在代码中抛出类型化错误
throw new MidnightError(
  ErrorCode.PROOF_TIMEOUT,
  '证明生成超时',
  { timeoutMs: 60000, operation: 'transfer' }
);

2. 分类未知错误

import { classifyError } from './errorUtils';

try {
  await contract.callTx.transfer(recipient, amount, witnesses);
} catch (error) {
  const classified = classifyError(error);
  // { code, message, userMessage, suggestion, retryable, category }
}

3. 显示给用户

import { useErrorToast } from './useErrorToast';

function TransferButton() {
  const { showError } = useErrorToast();

  const handleTransfer = async () => {
    try {
      await performTransfer();
    } catch (error) {
      showError(error); // 显示用户友好的toast
    }
  };
}

4. 包装组件

import { MidnightErrorBoundary } from './MidnightErrorBoundary';

function App() {
  return (
    <MidnightErrorBoundary
      onReset={() => window.location.reload()}
      fallback={<ErrorPage />}
    >
      <MyDApp />
    </MidnightErrorBoundary>
  );
}

常见模式

错误分类工具

interface ClassifiedError {
  code: string;
  message: string;
  userMessage: string;
  suggestion: string;
  retryable: boolean;
  category: 'proof' | 'transaction' | 'network' | 'wallet' | 'unknown';
}

function classifyError(error: unknown): ClassifiedError {
  if (error instanceof MidnightError) {
    return {
      code: error.code,
      message: error.message,
      userMessage: ERROR_CATALOG[error.code].userMessage,
      suggestion: ERROR_CATALOG[error.code].suggestion,
      retryable: ERROR_CATALOG[error.code].retryable,
      category: ERROR_CATALOG[error.code].category,
    };
  }

  // 通过错误消息模式分类
  const message = error instanceof Error ? error.message : String(error);
  return classifyByMessage(message);
}

带退避的重试

async function withRetry<T>(
  operation: () => Promise<T>,
  options: {
    maxRetries: number;
    baseDelayMs: number;
    shouldRetry: (error: unknown) => boolean;
  }
): Promise<T> {
  let lastError: unknown;

  for (let attempt = 1; attempt <= options.maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      lastError = error;

      if (!options.shouldRetry(error) || attempt === options.maxRetries) {
        throw error;
      }

      const delay = options.baseDelayMs * Math.pow(2, attempt - 1);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw lastError;
}

错误上下文提供者

const ErrorContext = createContext<ErrorContextValue | null>(null);

function ErrorProvider({ children }: { children: ReactNode }) {
  const [errors, setErrors] = useState<ClassifiedError[]>([]);

  const addError = useCallback((error: unknown) => {
    const classified = classifyError(error);
    setErrors(prev => [...prev, classified]);

    // 自动消除非关键错误
    if (classified.category !== 'wallet') {
      setTimeout(() => dismissError(classified.code), 5000);
    }
  }, []);

  const dismissError = useCallback((code: string) => {
    setErrors(prev => prev.filter(e => e.code !== code));
  }, []);

  return (
    <ErrorContext.Provider value={{ errors, addError, dismissError }}>
      {children}
    </ErrorContext.Provider>
  );
}

错误处理最佳实践

  1. 永远不要静默吞掉错误 - 始终记录或显示
  2. 提供可操作的消息 - 告诉用户可以做什么
  3. 记录技术细节 - 保留完整的错误信息用于调试
  4. 使用错误边界 - 防止UI中的级联故障
  5. 适当分类 - 不同的错误需要不同的处理
  6. 测试错误路径 - 验证错误UI正常工作
  7. 考虑可访问性 - 错误消息应与屏幕阅读器兼容

相关技能

  • wallet-integration - 钱包特定的错误处理
  • proof-handling - 证明生成错误模式
  • transaction-flows - 交易错误恢复
  • state-management - 状态读取错误处理

相关命令

  • /dapp-check - 验证错误处理模式
  • /dapp-debug errors - 诊断错误处理问题