Skip to content

Conversation

@wss-mz-web3
Copy link

@wss-mz-web3 wss-mz-web3 commented Dec 1, 2025

[中文版模板 / Chinese template]

💡 Background and solution

ICP 生态的主流钱包 Plug 尚未在示例或文档中提供明确链接,用户在使用 Web3 连接组件时容易找不到插件来源。

本 PR 增加了 Plug Wallet 的官方链接,方便开发者和用户快速获取 ICP 浏览器钱包,提高文档的可用性。

🔗 Related issue link

N/A

Summary by CodeRabbit

发布说明

  • 新功能

    • 新增 Internet Computer (ICP) 区块链集成支持(含链元数据与代币信息)
    • 新增 Plug 与 Infinity 钱包适配及内置钱包工厂
    • 新增 ICP 专用 Web3 配置提供器与统一连接/断开管理
    • 新增包发布配置以支持独立发布
  • 文档

    • 新增 ICP 集成文档(中/英文)与基础示例
  • 其他

    • 添加项目路径别名与版本变更记录 (changeset)

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Dec 1, 2025

🦋 Changeset detected

Latest commit: acf7a64

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@ant-design/web3-icp Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Dec 1, 2025

Someone is attempting to deploy a commit to the pro-components Team on Vercel.

A member of the Team first needs to authorize it.

@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Dec 1, 2025
@github-actions
Copy link

github-actions bot commented Dec 1, 2025

Prepare preview

@coderabbitai
Copy link

coderabbitai bot commented Dec 1, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

新增 @ant-design/web3-icp 包,包含 ICP 链与资产定义、Plug/Infinity 钱包实现与工厂、ICP 专用 Web3 配置提供器、类型声明、文档示例及构建配置和导出入口。

Changes

内聚体 / 文件(s) 变更摘要
包配置
packages/icp/package.json, packages/icp/tsconfig.json
新建包配置与 Typescript 项目配置,设置构建/发布、workspace 依赖与导出入口
包入口
packages/icp/src/index.ts
导出 icp-provider 与 wallets 的类型、工厂与内置实现
钱包类型与工厂
packages/icp/src/wallets/types.ts, packages/icp/src/wallets/factory.ts
新增 IcpWallet 接口、IcpWalletType 与 WalletFactory(IcpWalletFactory)实现及相关类型
钱包实现
packages/icp/src/wallets/plug.tsx, packages/icp/src/wallets/infinity.tsx, packages/icp/src/wallets/built-in.ts
新增 Plug 与 Infinity 钱包适配层(检测、连接、断开、getPrincipal)及内置 PlugWallet 工厂导出
ICP 提供器
packages/icp/src/icp-provider/config-provider.tsx, packages/icp/src/icp-provider/index.tsx
新增 AntDesignWeb3ConfigProvider 与 IcpWeb3ConfigProvider 组件,管理 principal、chain、wallets、balance 与连接流程
资产与钱包元数据
packages/assets/src/icp/chains.tsx, packages/assets/src/icp/index.ts, packages/assets/src/wallets/plug.tsx, packages/assets/src/index.ts, packages/assets/src/wallets/index.ts
添加 ICP 链定义与本币信息;新增 Plug 钱包元数据并将 icp 导出至 assets 总入口
公共类型与路径
packages/common/src/types.ts, tsconfig.base.json
新增 IcpChainIds 枚举,并在 tsconfig.base.json 中加入 "@ant-design/web3-icp" 路径别名
示例与文档
packages/web3/src/icp/index.md, packages/web3/src/icp/index.zh-CN.md, packages/web3/src/icp/demos/basic.tsx
新增中英文文档页与 basic 示例组件,展示 IcpWeb3ConfigProvider 与 PlugWallet 集成用法
变更集
.changeset/tidy-flies-accept.md
新增 changeset,记录 @ant-design/web3-icp 发布说明

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User as 用户
    participant UI as ConnectButton
    participant IcpProvider as IcpWeb3ConfigProvider
    participant Factory as IcpWalletFactory
    participant Wallet as 钱包 (Plug/Infinity)
    participant Ext as 浏览器扩展

    User->>UI: 点击连接
    UI->>IcpProvider: 调用 connect()
    IcpProvider->>Factory: 选择/创建可用钱包实例
    Factory->>Wallet: create()
    Wallet->>Wallet: 检查 installed
    alt 已安装
        IcpProvider->>Wallet: 调用 connect()
        Wallet->>Ext: requestConnect()
        Ext-->>Wallet: 用户授权
        Wallet->>Ext: getPrincipal()
        Ext-->>Wallet: 返回 principal
        Wallet-->>IcpProvider: 返回 principal
        IcpProvider->>IcpProvider: 更新 principal/account/balance
        IcpProvider-->>UI: 连接成功
    else 未安装
        Wallet-->>IcpProvider: 返回未安装
        IcpProvider-->>UI: 提示或回退逻辑
    end
Loading

Estimated code review effort

🎯 4 (复杂) | ⏱️ ~60 分钟

  • 重点检查:
    • packages/icp/src/icp-provider/config-provider.tsxindex.tsx:异步连接流程、connectPromise 管理、chainList 合并与 onCurrentChainChange 逻辑
    • packages/icp/src/wallets/*:与外部扩展交互的错误处理、isInstalled/isConnected 的健壮性
    • packages/icp/src/wallets/factory.ts:元数据覆盖逻辑与兼容性
    • 导出与 tsconfig 路径别名:确保类型与包导出的一致性

诗歌

🐰 我是兔子来报春,
Plug 与 Infinity 拉手行,
主体登场链已明,
Web3 配置铺新庭,
小胡萝卜庆功能兴 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题虽然与变更相关(涉及ICP链和Plug钱包),但用中文表述且表述方式较为简洁,缺乏对主要变更内容的全面说明。本PR不仅添加了Plug钱包,还添加了Infinity钱包、ICP链集成、Web3配置提供者等多项重要内容,而标题仅强调了Plug钱包链接。
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @wss-mz-web3, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求通过引入一个新的 @ant-design/web3-icp 包,显著扩展了 Ant Design Web3 组件库对 Internet Computer (ICP) 链的支持。它提供了一个统一的上下文提供者和钱包适配器,使用户能够轻松地将 ICP 钱包(如 Plug 和 Infinity)集成到他们的去中心化应用中,并利用现有的 Ant Design Web3 UI 组件,从而提升了用户体验和开发效率。

Highlights

  • 新增 ICP 链适配包: 引入了新的 @ant-design/web3-icp 包,为 Internet Computer (ICP) 链提供基础适配能力。
  • ICP 钱包连接上下文: 新增 IcpWeb3ConfigProvider 组件和 useIcpWallet Hook,用于管理 ICP 钱包的连接状态和提供上下文。
  • 支持 Plug 和 Infinity 钱包: 内置支持 Plug 和 Infinity 两种 ICP 钱包的连接,并提供相应的适配器函数。
  • 更新文档和示例: 更新了 Ant Design Web3 的文档,增加了 ICP 链的介绍、使用场景和连接钱包的演示示例。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@dosubot dosubot bot added the feature Independent new features label Dec 1, 2025
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 新增了对 ICP 链和 Plug 钱包的支持,整体实现良好。代码结构清晰,并提供了相应的演示和文档。我提出了一些关于性能优化、代码复用和 React Hooks 最佳实践的建议,希望能帮助提升代码质量和可维护性。请查看具体的评论。

Comment on lines 78 to 115
const handleConnect = async () => {
try {
if (!installed) {
Modal.info({
title: `未检测到 ${walletName} 钱包`,
content: (
<>
<p>当前浏览器环境未检测到 {walletName} 钱包扩展。</p>
<p>请先安装该钱包并完成初始化,再刷新页面重试。</p>
</>
),
okText: '我知道了',
});
return;
}
await connect();
} catch (error: any) {
messageApi.error(error?.message ?? `连接 ${walletName} 钱包失败,请检查插件是否安装。`);
}
};

const handleDisconnect = async () => {
try {
await disconnect();
} catch (error: any) {
messageApi.error(error?.message ?? `断开 ${walletName} 钱包失败,请重试。`);
}
};

useEffect(() => {
if (autoConnect) {
void (async () => {
await handleConnect();
onAutoConnectConsumed();
})();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [autoConnect, walletType]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

useEffect 钩子禁用 exhaustive-deps 规则,这可能掩盖了潜在的 bug,即 handleConnectonAutoConnectConsumed 未包含在依赖项数组中。handleConnect 函数在每次渲染时都会重新创建,并且依赖于 installedconnectwalletName。为了解决这个问题,应该使用 useCallback 包装 handleConnecthandleDisconnect,并将它们包含在 useEffect 的依赖项数组中。这可以确保 effect 仅在其真正的依赖项发生变化时才运行,并遵循 React hooks 的最佳实践。

  const handleConnect = React.useCallback(async () => {
    try {
      if (!installed) {
        Modal.info({
          title: `未检测到 ${walletName} 钱包`,
          content: (
            <>
              <p>当前浏览器环境未检测到 ${walletName} 钱包扩展。</p>
              <p>请先安装该钱包并完成初始化,再刷新页面重试。</p>
            </>
          ),
          okText: '我知道了',
        });
        return;
      }
      await connect();
    } catch (error: any) {
      messageApi.error(error?.message ?? `连接 ${walletName} 钱包失败,请检查插件是否安装。`);
    }
  }, [installed, connect, walletName, messageApi]);

  const handleDisconnect = React.useCallback(async () => {
    try {
      await disconnect();
    } catch (error: any) {
      messageApi.error(error?.message ?? `断开 ${walletName} 钱包失败,请重试。`);
    }
  }, [disconnect, walletName, messageApi]);

  useEffect(() => {
    if (autoConnect) {
      void (async () => {
        await handleConnect();
        onAutoConnectConsumed();
      })();
    }
  }, [autoConnect, handleConnect, onAutoConnectConsumed]);

Comment on lines 49 to 72
const connect = async () => {
setConnecting(true);
try {
await wallet.connect();
const p = await wallet.getPrincipal();
setPrincipal(p);
} finally {
setConnecting(false);
}
};

const disconnect = async () => {
await wallet.disconnect();
setPrincipal(null);
};

const value: IcpContextValue = {
wallet,
principal,
connecting,
connect,
disconnect,
installed: wallet.installed,
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

connectdisconnect 函数以及传递给 IcpContext.Providervalue 对象在每次渲染时都会重新创建。这可能导致消费该上下文的组件发生不必要的重新渲染。为了优化性能,建议使用 useCallback 包装 connectdisconnect 函数,并使用 useMemo 包装 value 对象。

  const connect = React.useCallback(async () => {
    setConnecting(true);
    try {
      await wallet.connect();
      const p = await wallet.getPrincipal();
      setPrincipal(p);
    } finally {
      setConnecting(false);
    }
  }, [wallet]);

  const disconnect = React.useCallback(async () => {
    await wallet.disconnect();
    setPrincipal(null);
  }, [wallet]);

  const value = React.useMemo<IcpContextValue>(() => ({
    wallet,
    principal,
    connecting,
    connect,
    disconnect,
    installed: wallet.installed,
  }), [wallet, principal, connecting, connect, disconnect]);

Comment on lines +23 to +73
export function createPlugWallet(): IcpWallet {
const getInstalled = () => isPlugInstalled();

return {
id: 'plug',
name: 'Plug',
icon: null,

get installed() {
return getInstalled();
},

async connect() {
const plug = getPlug();
if (!plug) {
// 不抛异常,由上层根据 installed 提示安装钱包
return;
}
await plug.requestConnect();
},

async disconnect() {
const plug = getPlug();
if (!plug) {
return;
}
await plug.disconnect();
},

async isConnected() {
const plug = getPlug();
if (!plug) {
return false;
}
return plug.isConnected();
},

async getPrincipal() {
const plug = getPlug();
if (!plug) {
return null;
}
try {
const principal = await plug.getPrincipal();
return principal.toText();
} catch {
return null;
}
},
};
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

createPlugWallet 的实现与 packages/icp/src/wallets/infinity.tsx 中的 createInfinityWallet 非常相似。这种代码重复可以通过创建一个通用的钱包工厂函数来减少,从而提高可维护性,并使将来添加更多钱包变得更容易。

例如,您可以在一个共享文件中创建此函数:

function createIcpWalletImplementation(
  id: 'plug' | 'infinity',
  name: string,
  getWalletObject: () => any | undefined
): IcpWallet {
  const isInstalled = () => !!getWalletObject();

  return {
    id,
    name,
    icon: null,
    get installed() {
      return isInstalled();
    },
    async connect() {
      const wallet = getWalletObject();
      if (!wallet) return;
      await wallet.requestConnect();
    },
    async disconnect() {
      const wallet = getWalletObject();
      if (!wallet) return;
      await wallet.disconnect();
    },
    async isConnected() {
      const wallet = getWalletObject();
      if (!wallet) return false;
      return wallet.isConnected();
    },
    async getPrincipal() {
      const wallet = getWalletObject();
      if (!wallet) return null;
      try {
        const principal = await wallet.getPrincipal();
        return principal.toText();
      } catch {
        return null;
      }
    },
  };
}

// 在 plug.tsx 中
export function createPlugWallet(): IcpWallet {
  return createIcpWalletImplementation('plug', 'Plug', getPlug);
}

// 在 infinity.tsx 中
export function createInfinityWallet(): IcpWallet {
  return createIcpWalletImplementation('infinity', 'Infinity', getInfinity);
}

);

const handleWalletSelected = async (wallet: Wallet) => {
const nextType = (wallet.key as WalletType) ?? 'plug';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

在此文件中,walletList 是从只包含 'plug' 键的 walletMetaMap 创建的。因此,wallet.key 的值将始终是 'plug'。这意味着 ?? 'plug' 这部分是多余的,可以移除以简化代码。

    const nextType = wallet.key as WalletType;

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (12)
packages/web3/src/icp/index.zh-CN.md (1)

1-31: 中文文档结构清晰,说明与当前实现匹配

整体结构(介绍 / 何时使用 / 代码演示)和其它链文档风格一致,也明确说明当前仅内置 Plug 钱包、未来可扩展更多 ICP 钱包,和当前对外 API(IcpWeb3ConfigProvider + ConnectButton + 基础 demo)是对齐的。后续如果将 Infinity 或其他 ICP 钱包真正接入对外能力,记得同步在这里补充一句支持列表,避免和代码实现脱节。

packages/icp/src/index.ts (1)

1-3: 对外导出保持最小 API,确认是否刻意不暴露 Infinity 钱包

当前仅从根入口导出了:

  • ./icp-provider/context(Provider / hooks)
  • ./wallets/plug(Plug 钱包工厂 & 工具)
  • ./wallets/types(类型)

这在“只对外宣称支持 Plug”这一阶段是合理的最小 API 设计。由于代码中已经存在 wallets/infinity 实现和 IcpWalletType = 'plug' | 'infinity',建议团队内部确认一下:

  • 如果短期内不打算对外开放 Infinity 工具函数,保持现在的导出集合即可;
  • 如果希望让高级用户自行接入 Infinity,可在后续 PR 中补充一行 export * from './wallets/infinity'; 并同步文档。

当前实现本身没有问题,只是 API 取舍需要对齐一下对外支持范围。

packages/web3/src/icp/index.md (1)

1-28: 英文文档与中文版基本对齐,描述清晰

英文版同样聚焦于 “ICP 链 + Plug 钱包最小接入”,并展示通过 IcpWeb3ConfigProvider + ConnectButton 接入的方式,和中文文档、代码实现是一致的。后续如果正式开放 Infinity 或其它 ICP 钱包支持,记得同时更新中英文两份文档中的“currently ships with Plug wallet support”/“目前内置支持 Plug 钱包”等表述,以免文档与实际能力不一致。

packages/icp/package.json (1)

1-63: ICP 包 manifest 基本与其它链包保持一致

  • main/module/typingsexports 均指向 dist 下同一路径,看起来是沿用现有链包的约定;
  • dependencies 仅依赖 @ant-design/web3-common(workspace),devDependencies 中声明了 fathertypescript,以及常规的 filessideEffectspublishConfig 配置,整体规范。

建议在合并前做两点确认:

  1. Father 配置的构建输出目录与这里的 dist/libdist/esm 保持一致,避免发布后入口指向不存在的文件;
  2. 如果后续正式支持 Infinity 并对外宣传,可以顺带在 keywords 中补充相关关键词(目前只含有 plug)。

除此之外,这个 manifest 本身没有明显问题。

packages/icp/src/wallets/plug.tsx (2)

23-42: 建议为 connect 失败场景提供反馈机制

当前 connect() 在钱包未安装时静默返回,虽然注释说明了由上层处理,但如果 plug.requestConnect() 抛出异常,错误会向上传播而未被捕获。建议添加 try-catch 以保持一致的错误处理策略。

     async connect() {
       const plug = getPlug();
       if (!plug) {
         // 不抛异常,由上层根据 installed 提示安装钱包
         return;
       }
-      await plug.requestConnect();
+      try {
+        await plug.requestConnect();
+      } catch (error) {
+        console.error('Plug wallet connect failed:', error);
+        throw error;
+      }
     },

26-33: 考虑添加钱包图标

icon 当前设置为 null。为了提升用户体验,建议添加 Plug 钱包的官方图标,这样在钱包选择列表中会有更好的视觉效果。

packages/icp/src/icp-provider/context.tsx (3)

49-58: connect 函数缺少错误处理

connect 函数调用 wallet.connect()wallet.getPrincipal() 时没有 catch 错误,异常会直接向上抛出。虽然上层 demo 有 try-catch,但作为通用 Provider,建议在此处也添加错误处理或至少记录日志。

   const connect = async () => {
     setConnecting(true);
     try {
       await wallet.connect();
       const p = await wallet.getPrincipal();
       setPrincipal(p);
+    } catch (error) {
+      console.error('ICP wallet connect failed:', error);
+      throw error;
     } finally {
       setConnecting(false);
     }
   };

49-63: 建议使用 useCallback 优化 connect/disconnect

connectdisconnect 函数在每次渲染时都会重新创建,可能导致依赖这些函数的子组件不必要地重新渲染。

-  const connect = async () => {
+  const connect = useCallback(async () => {
     setConnecting(true);
     try {
       await wallet.connect();
       const p = await wallet.getPrincipal();
       setPrincipal(p);
     } finally {
       setConnecting(false);
     }
-  };
+  }, [wallet]);

-  const disconnect = async () => {
+  const disconnect = useCallback(async () => {
     await wallet.disconnect();
     setPrincipal(null);
-  };
+  }, [wallet]);

65-74: Context value 对象建议 memoize

每次渲染都会创建新的 value 对象,可能导致消费该 Context 的组件不必要地重新渲染。

-  const value: IcpContextValue = {
-    wallet,
-    principal,
-    connecting,
-    connect,
-    disconnect,
-    installed: wallet.installed,
-  };
+  const value: IcpContextValue = useMemo(
+    () => ({
+      wallet,
+      principal,
+      connecting,
+      connect,
+      disconnect,
+      installed: wallet.installed,
+    }),
+    [wallet, principal, connecting, connect, disconnect]
+  );
packages/web3/src/icp/demos/basic.tsx (3)

18-26: checkInstalled 类型定义可以简化

isPlugInstalled() 是同步函数返回 boolean,但 checkInstalled 被定义为 Promise<boolean>。虽然 async () => isPlugInstalled() 可以工作,但类型定义可以更精确。

 type WalletMeta = {
   name: string;
   remark: string;
   group: string;
   color: string;
-  checkInstalled?: () => Promise<boolean>;
+  checkInstalled?: () => boolean | Promise<boolean>;
 };

46-59: 重复调用 checkInstalled 可以优化

hasExtensionInstalledhasWalletReady 使用了几乎相同的逻辑且都调用 meta.checkInstalled?.()。可以考虑复用同一个函数引用。

 const walletList: Wallet[] = (Object.keys(walletMetaMap) as WalletType[]).map((type) => {
   const meta = walletMetaMap[type];
+  const checkReady = meta.checkInstalled
+    ? async () => !!(await meta.checkInstalled?.())
+    : undefined;
   return {
     key: type,
     name: meta.name,
     remark: meta.remark,
     icon: <WalletIcon color={meta.color} label={meta.name.slice(0, 1)} />,
     group: meta.group,
-    hasExtensionInstalled: meta.checkInstalled
-      ? async () => !!(await meta.checkInstalled?.())
-      : undefined,
-    hasWalletReady: meta.checkInstalled ? async () => !!(await meta.checkInstalled?.()) : undefined,
+    hasExtensionInstalled: checkReady,
+    hasWalletReady: checkReady,
   };
 });

158-180: handleWalletSelected 中的安装检查与 IcpConnectButton 重复

钱包安装状态在 handleWalletSelectedIcpConnectButton.handleConnect 中都进行了检查。考虑到用户体验,在选择钱包时检查是合理的,但建议统一检查逻辑的来源(使用 context 中的 installed 状态)。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a2a0dc and 8d7eaf3.

📒 Files selected for processing (11)
  • packages/icp/package.json (1 hunks)
  • packages/icp/src/icp-provider/context.tsx (1 hunks)
  • packages/icp/src/index.ts (1 hunks)
  • packages/icp/src/wallets/infinity.tsx (1 hunks)
  • packages/icp/src/wallets/plug.tsx (1 hunks)
  • packages/icp/src/wallets/types.ts (1 hunks)
  • packages/icp/tsconfig.json (1 hunks)
  • packages/web3/src/icp/demos/basic.tsx (1 hunks)
  • packages/web3/src/icp/index.md (1 hunks)
  • packages/web3/src/icp/index.zh-CN.md (1 hunks)
  • tsconfig.base.json (1 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: This is a multi-package React project built on Ant Design component library, designed to provide Web3 projects with wallet connection capabilities and DApp UI components
📚 Learning: 2025-11-25T09:58:18.233Z
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: Applies to packages/web3/*/index.{md,zh-CN.md} : Documentation files should be available as `packages/web3/*/index.md` and `packages/web3/*/index.zh-CN.md` for component documentation and `packages/web3/ethereum` for wagmi package documentation

Applied to files:

  • packages/web3/src/icp/index.md
  • packages/web3/src/icp/index.zh-CN.md
  • packages/icp/package.json
📚 Learning: 2025-11-25T09:58:18.233Z
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: This is a multi-package React project built on Ant Design component library, designed to provide Web3 projects with wallet connection capabilities and DApp UI components

Applied to files:

  • packages/web3/src/icp/index.md
  • packages/web3/src/icp/index.zh-CN.md
  • packages/icp/src/wallets/plug.tsx
  • packages/icp/src/icp-provider/context.tsx
  • packages/icp/src/wallets/infinity.tsx
  • packages/icp/package.json
  • packages/web3/src/icp/demos/basic.tsx
  • packages/icp/src/wallets/types.ts
  • tsconfig.base.json
📚 Learning: 2025-11-25T09:58:18.233Z
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: UI components are located under `packages/web3`, with other packages containing adapters for different blockchains or foundational packages

Applied to files:

  • packages/icp/package.json
  • tsconfig.base.json
📚 Learning: 2025-11-25T09:58:18.233Z
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: Applies to packages/web3/*/demos/** : Demo components should be located in `packages/web3/*/demos` directories

Applied to files:

  • packages/web3/src/icp/demos/basic.tsx
  • tsconfig.base.json
📚 Learning: 2025-11-25T09:58:18.233Z
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: Applies to **/style/index.{ts,tsx} : Styling implementation should be based on `ant-design/cssinjs` and utilize Ant Design's Design Tokens for custom styling support

Applied to files:

  • tsconfig.base.json
🧬 Code graph analysis (3)
packages/icp/src/wallets/plug.tsx (1)
packages/icp/src/wallets/types.ts (1)
  • IcpWallet (3-12)
packages/icp/src/wallets/infinity.tsx (1)
packages/icp/src/wallets/types.ts (1)
  • IcpWallet (3-12)
packages/web3/src/icp/demos/basic.tsx (2)
packages/icp/src/wallets/plug.tsx (4)
  • isPlugInstalled (21-21)
  • installed (31-33)
  • connect (35-42)
  • disconnect (44-50)
packages/icp/src/icp-provider/context.tsx (2)
  • useIcpWallet (25-33)
  • IcpWeb3ConfigProvider (35-75)
🔇 Additional comments (6)
packages/icp/tsconfig.json (1)

1-4: ICP 子包 tsconfig 配置整体合理

继承根目录 tsconfig.base.json,并仅包含 srcglobal.d.ts,和多包结构的常见做法一致,后续如果有测试文件夹再按需补充即可。可以在本包目录跑一遍构建/类型检查,确认不会有路径或类型解析问题。

tsconfig.base.json (1)

37-38: 新增路径别名与包目录结构一致

"@ant-design/web3-icp": ["./packages/icp/src/"] 与新包目录匹配,也保持了和其他链包一致的 alias 约定,便于在 docs/demos 中直接从别名导入。建议在根目录跑一遍 TS 构建或现有 lint 流程,确认别名解析正常(例如在 demo 中用该 alias 导入时无报错)。

packages/icp/src/wallets/types.ts (1)

1-14: IcpWallet 类型设计合理,注意与其它链钱包接口保持一致

IcpWallet 抽象了常见的钱包能力(安装状态、连接/断开、连接状态查询、获取 principal),并通过 IcpWalletType 预留了 plug | infinity 两种类型,整体设计清晰、易于扩展。建议在合并前简单对比一下其它链(如 EVM、Ton 等)对应的钱包接口,确保字段命名与行为语义尽量统一,方便上层做统一处理。

packages/icp/src/wallets/plug.tsx (2)

18-21: SSR 安全检查实现正确

getPlug 函数正确处理了服务端渲染场景,避免了 window 未定义的问题。


60-71: getPrincipal 错误处理良好

使用 try-catch 包裹异步调用并返回 null 作为降级方案,这是合理的防御性编程。

packages/web3/src/icp/demos/basic.tsx (1)

144-203: Demo 组件整体结构清晰

Demo 正确展示了 ICP 钱包连接流程,包括钱包选择、安装检测、连接/断开等功能。使用 IcpWeb3ConfigProvider 包裹并传递 walletType 的模式符合预期设计。

Comment on lines 42 to 47
const wallet = useMemo<IcpWallet>(() => {
if (walletType === 'infinity') {
return createInfinityWallet();
}
return createPlugWallet();
}, [walletType]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

钱包类型切换时需要重置状态

walletType 改变时,wallet 会通过 useMemo 重新创建,但 principal 状态会保留之前钱包的值。这可能导致 UI 显示错误的连接状态。

+  // 当钱包类型改变时重置状态
+  useEffect(() => {
+    setPrincipal(null);
+  }, [walletType]);
+
   const wallet = useMemo<IcpWallet>(() => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const wallet = useMemo<IcpWallet>(() => {
if (walletType === 'infinity') {
return createInfinityWallet();
}
return createPlugWallet();
}, [walletType]);
// 当钱包类型改变时重置状态
useEffect(() => {
setPrincipal(null);
}, [walletType]);
const wallet = useMemo<IcpWallet>(() => {
if (walletType === 'infinity') {
return createInfinityWallet();
}
return createPlugWallet();
}, [walletType]);
🤖 Prompt for AI Agents
In packages/icp/src/icp-provider/context.tsx around lines 42 to 47, the useMemo
recreates the wallet when walletType changes but the principal state remains
from the previous wallet causing stale connection UI; update the provider so
that whenever walletType (or the derived wallet) changes you reset/clear the
principal state (and any related connection flags) and re-run any wallet
initialization/connection logic — e.g., add an effect that watches walletType or
wallet and sets principal to null/undefined (and performs any necessary
cleanup/disconnect of the old wallet) before creating/connecting the new wallet.

Comment on lines +5 to +75
// NOTE: 这里假设 Infinity 钱包通过 window.ic.infinityWallet 注入,
// 具体字段请根据官方文档调整。
declare global {
interface Window {
ic?: {
infinityWallet?: {
isConnected: () => Promise<boolean>;
requestConnect: (opts?: unknown) => Promise<{ principalId: string }>;
disconnect: () => Promise<void>;
getPrincipal: () => Promise<{ toText: () => string }>;
};
};
}
}

const getInfinity = () =>
typeof window === 'undefined' || typeof window.ic === 'undefined'
? undefined
: window.ic.infinityWallet;

export const isInfinityInstalled = () => !!getInfinity();

export function createInfinityWallet(): IcpWallet {
const getInstalled = () => isInfinityInstalled();

return {
id: 'infinity',
name: 'Infinity',
icon: null as React.ReactNode | null,

get installed() {
return getInstalled();
},

async connect() {
const wallet = getInfinity();
if (!wallet) {
return;
}
await wallet.requestConnect();
},

async disconnect() {
const wallet = getInfinity();
if (!wallet) {
return;
}
await wallet.disconnect();
},

async isConnected() {
const wallet = getInfinity();
if (!wallet) {
return false;
}
return wallet.isConnected();
},

async getPrincipal() {
const wallet = getInfinity();
if (!wallet) {
return null;
}
try {
const principal = await wallet.getPrincipal();
return principal.toText();
} catch {
return null;
}
},
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find references to Infinity wallet in the codebase
rg -i "infinity" --type ts --type tsx --type js --type json -B2 -A2

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

# Check the structure of the wallets directory to understand patterns
git ls-files packages/icp/src/wallets/

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

# Look for tests related to Infinity or wallet implementations
fd -e test.ts -e test.tsx -e spec.ts -e spec.tsx | head -20

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

# Check if Infinity is exported from index files
rg "infinity" packages/icp/src/ --type ts --type tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🌐 Web query:

Infinity wallet ICP API documentation official

💡 Result:

I couldn't find an official Infinity Wallet "ICP API" reference. Useful official links I found instead:

  • Infinity Wallet developer docs (DApp/connect, add-a-dapp, chains/tokens). These are the primary Infinity Wallet docs site. [1]
  • Infinity Wallet GitHub (repo / notices). [2]
  • Infinity Wallet "DApp Connect" integration guide (developer-facing page). [3]

If you want an ICP-specific wallet API for Internet Computer (ICP) dapps, the common ICP wallets with developer docs are Plug and Stoic (and the Internet Computer docs for ledger/canisters). If you'd like, I can: (A) search for a specific Infinity Wallet ICP page again, (B) fetch Plug/Stoic ICP wallet developer docs, or (C contact Infinity Wallet support pages for confirmation. Which do you prefer?

Sources:
[1] https://docs.infinitywallet.io/
[2] https://github.com/infinitywallet/infinitywallet
[3] https://docs.infinitywallet.io/developers-and-projects/dapp-connect-integration


🌐 Web query:

Infinity wallet window.ic API specification

💡 Result:

Do you mean the Internet Computer "IC provider" API (window.ic) used by wallets like Plug, or Infinity Wallet (infinitywallet.io) for EVM/mobile? I can't find an official "Infinity Wallet window.ic" API specification — Infinity Wallet's repo/docs are deprecated and their site doesn't publish a window.ic spec. [1][2]

If you mean the IC provider API (what dapps use at window.ic.*), refer to Wallets such as Plug which inject window.ic.plug and publish developer docs showing the API methods (requestConnect, agent, requestTransfer, etc.). See Plug developer docs for the API shape and examples. [3][4]

Sources:

  • Infinity Wallet (repo / support / deprecation notice). [1]
  • Infinity Wallet feature/docs (no window.ic spec). [2]
  • Plug Wallet developer docs (example and window.ic.plug API). [3]
  • Plug usage examples / forum references to window.ic.plug methods. [4]

Tell me which you meant (Infinity Wallet for IC dapps vs Infinity Wallet desktop/mobile for EVM), and I’ll fetch the exact API spec or relevant docs.


🌐 Web query:

Infinity Wallet Internet Computer ICP support

💡 Result:

Short answer: No — Infinity Wallet does not list Internet Computer (ICP) as a supported chain.

Evidence:

  • Infinity Wallet supported-chains list (no ICP / Internet Computer entry). [1]
  • DFINITY / Internet Computer documentation lists common ICP wallets (NNS dapp, Plug, Stoic, etc.) — these are the wallets to use for ICP. [2][3]

If you want, I can show how to move ICP into a recommended wallet (Plug/Stoic/NNS) or check again for a recent Infinity Wallet update.


🌐 Web query:

"Infinity Wallet" "Internet Computer" integration

💡 Result:

Short answer: There are two different projects called "Infinity Wallet." One is a multi‑chain desktop wallet (Infinity Wallet.io) that does not list Internet Computer / ICP among its supported chains in its docs. The other is an Internet Computer ecosystem wallet (used with InfinitySwap, NFT marketplaces on ICP, and integrations like Internet Identity / ICRC‑1) and is referenced by IC community sites and tooling (e.g., Artemis adapters). So whether "Infinity Wallet" integrates with Internet Computer depends on which project you mean. Sources: [1][2][3][4].

Sources

  • Infinity Wallet (desktop) docs — supported chains and DApp integration (no ICP listed). [1]
  • Infinity Wallet (desktop) features / Web3 browser docs. [2]
  • IC ecosystem / InfinitySwap / Bitfinity blog referencing Infinity Wallet as an ICP wallet and NFT marketplace integrations. [3]
  • Artemis React (ICP wallet adapters) listing Infinity Wallet among Internet Computer wallets. [4]

🌐 Web query:

Artemis React Infinity Wallet ICP adapter API

💡 Result:

Short answer
Artemis is a set of JavaScript/React packages (artemis-web3-adapter + artemis-react) that act as a universal Internet Computer (ICP) wallet adapter/bridge — it exposes a React-friendly API to connect, disconnect, auto‑connect, sign and send calls via multiple ICP wallets (Plug, Stoic, Internet Identity, Infinity Wallet, NFID, etc.). The Infinity Wallet is one of the built‑in connectors supported by Artemis. [1][2][3]

Quick facts / how to get started

  • Install the packages: npm i @amerej/artemis-web3-adapter @amerej/artemis-react. [1][2]
  • Create an Artemis adapter instance with your IC host/whitelist and use the adapter’s connect/disconnect/sign/send methods (artemis-web3-adapter provides the core API; artemis-react exposes React hooks/components). Example from the adapter README: const artemis = new Artemis({ whitelist: [...], host: 'https://icp0.io' }); then use the adapter or react utilities to connect. [2][1]
  • Supported wallets include Infinity Wallet (plus Plug, Stoic, Internet Identity, NFID, MetaMask/MSQ for Safe ICP flows). Use Artemis’ built‑in connector for Infinity Wallet to prompt the user and obtain an identity/agent for canister calls. [1][2]
  • For Infinity Wallet-specific DApp integration details (if you need to wire the Infinity connector or follow their DApp Connect flow) consult Infinity Wallet’s DApp Connect docs. [3]

Where to read more / docs

  • artemis-react (React utilities / hooks): npm package page / repo. [1]
  • artemis-web3-adapter (core adapter, BatchTransact, examples): npm package page / README. [2]
  • Infinity Wallet DApp Connect developer docs (integration notes for connecting DApps to Infinity Wallet). [3]
    If you want, I can: show a minimal React code example wiring Artemis to Infinity Wallet, or fetch the current Artemis API reference/readme and paste the exact method signatures. Which would you prefer?

🌐 Web query:

InfinitySwap Infinity Wallet window.ic API integration

💡 Result:

Quick summary from docs/search:

  • Infinity Wallet provides a DApp integration flow and developer docs for adding a DApp / connecting wallets. See their developer page (Add a DApp) for requirements and process. [1]
  • Infinity Wallet has developer docs (custom token, wallet features) and a support/knowledge base you can use for implementation details. [2][3]
  • Third‑party wallet connectors (e.g., web3-onboard) already include an Infinity Wallet module you can use to integrate the wallet in a web dApp. That is often the easiest route instead of custom provider plumbing. [4]
  • Vezgo and other aggregator APIs list Infinity Wallet as a supported provider if you prefer an API connector rather than direct wallet injection. [5]

I could not find any authoritative documentation that describes a provider object named exactly "window.ic" for Infinity Wallet. Do you mean:

  • the Infinity Wallet web3 provider (injected window.ethereum-like object), or
  • the Internet Computer (IC) API window.ic, or
  • a specific InfinitySwap integration?

Tell me which one you mean and whether you want a code example (web3-onboard, injected provider, or using an API connector); I’ll fetch exact code and steps.

Sources:
[1] Infinity Wallet — Add a DApp / developer page.
[2] Infinity Wallet docs — custom token / support.
[3] Infinity Wallet knowledge base.
[4] Web3-Onboard Infinity Wallet module docs.
[5] Vezgo — Infinity Wallet API support.


🌐 Web query:

Infinity Wallet ICP window.ic provider API specification official documentation

💡 Result:

Short answer: I couldn't find an official "window.ic provider" API specification published by Infinity Wallet. The commonly used Internet Computer "window.ic" provider API is documented by Plug (the IC Provider API) and many dapps use that or the ICPay/ICPay SDK; Infinity Wallet appears to support ICP in various ways but does not publish its own window.ic provider spec. See:

  • Plug (IC Provider API / window.ic.plug) developer docs — describes the injected window.ic API used by ICP wallets. [1]
  • Infinity Wallet connector (npm/github) — Infinity's connector exposes web3 APIs for EVM (window.ethereum.isInfinityWallet) but there is no equivalent official window.ic provider spec in their public docs/repo. [2][3]

If you want, I can:

  • fetch and summarize the Plug window.ic API (methods like requestConnect, createActor, requestTransfer, etc.) with examples.
  • keep looking for any newer Infinity Wallet developer docs if you want me to continue searching.

Official Infinity Wallet ICP API specification does not exist; merge this PR only after direct coordination with the Infinity team

This implementation assumes a window.ic.infinityWallet API shape (isConnected, requestConnect, disconnect, getPrincipal returning specific structures) without any official documentation or published specification. Web searches confirm that Infinity Wallet does not currently publish a public window.ic provider specification—while Infinity Wallet may support ICP through various integrations (noted in Artemis React adapters), the exact API surface used here is undocumented.

Required before merging:

  • Contact the Infinity Wallet team directly to obtain or confirm the official window.ic.infinityWallet API specification (method names, parameter types, return value structures).
  • Update the declare global interface to match the official spec.
  • Add a clear comment in the file stating the spec version this code targets and when it was last verified.
  • If the team cannot provide an official spec or this integration is not yet officially supported, either defer this PR or mark Infinity support as experimental with a prominent warning.

Without this verification step, the code risks runtime failures when users with Infinity Wallet encounter method signature mismatches or structural differences from the assumed API.

🤖 Prompt for AI Agents
In packages/icp/src/wallets/infinity.tsx around lines 5 to 75, the code assumes
an undocumented window.ic.infinityWallet API; before merging you must verify the
real API with the Infinity Wallet team, update the TypeScript declare global
types to match the official spec, and add an inline comment stating the spec
version and last-verified date; if the team cannot provide a spec, mark this
integration as experimental (prominent comment and runtime opt-in/feature flag)
and add defensive runtime guards and error handling for unexpected shapes to
avoid crashes.

Comment on lines +1 to +16
import type React from 'react';

import type { IcpWallet } from './types';

declare global {
interface Window {
ic?: {
plug?: {
isConnected: () => Promise<boolean>;
requestConnect: (opts?: unknown) => Promise<{ principalId: string }>;
disconnect: () => Promise<void>;
getPrincipal: () => Promise<{ toText: () => string }>;
};
};
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Plug wallet ICP requestConnect API return type documentation

💡 Result:

The Plug API method requestConnect returns a Promise — i.e., an async call that resolves to an object containing the connected account's publicKey (and throws if the user declines). [1]

Sources:

  • Plug docs — "requestConnect" (returns Promise and returns an object containing the publicKey). [1]

Fix incorrect requestConnect return type in Plug wallet interface declaration

The requestConnect method is declared to return Promise<{ principalId: string }>, but according to the official Plug wallet API documentation, it returns Promise<PublicKey> containing a publicKey field, not principalId. Update the global interface declaration to match the actual Plug API:

requestConnect: (opts?: unknown) => Promise<{ publicKey: string }>;
🤖 Prompt for AI Agents
In packages/icp/src/wallets/plug.tsx around lines 1 to 16, the global
Window.ic.plug.requestConnect return type is incorrect (currently Promise<{
principalId: string }>) — update its type to match the Plug API by changing the
return shape to Promise<{ publicKey: string }>, keeping opts?: unknown and other
method signatures unchanged so TypeScript reflects the actual Plug wallet
response.

Comment on lines 107 to 115
useEffect(() => {
if (autoConnect) {
void (async () => {
await handleConnect();
onAutoConnectConsumed();
})();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [autoConnect, walletType]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

useEffect 依赖项处理存在潜在问题

当前使用 eslint-disable-next-line react-hooks/exhaustive-deps 跳过了依赖检查。handleConnect 依赖于 installedconnectmessageApiwalletName,这些值在 walletType 变化时可能已经过期。

建议将 handleConnect 提取为 useCallback 并正确声明依赖,或者在 effect 内部直接调用 connect 逻辑。

+  const handleConnectRef = useRef(handleConnect);
+  handleConnectRef.current = handleConnect;
+
   useEffect(() => {
     if (autoConnect) {
       void (async () => {
-        await handleConnect();
+        await handleConnectRef.current();
         onAutoConnectConsumed();
       })();
     }
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [autoConnect, walletType]);
+  }, [autoConnect, walletType, onAutoConnectConsumed]);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/web3/src/icp/demos/basic.tsx around lines 107 to 115, the useEffect
currently disables exhaustive-deps and calls handleConnect which closes over
installed, connect, messageApi and walletName — these values can become stale
when walletType changes. Fix by extracting handleConnect into a useCallback that
lists its true dependencies (installed, connect, messageApi, walletName, etc.)
and then reference that callback in the effect dependency array (or
alternatively move the connect logic inline inside the effect and include the
same dependencies), and remove the eslint-disable comment so the effect
dependencies are correct.

@github-actions github-actions bot added the major label Dec 1, 2025
@github-actions
Copy link

github-actions bot commented Dec 1, 2025

🚨 @wss-mz-web3, 本次 PR 的更改包含 major 变更,请确认是否符合预期。


🚨 @wss-mz-web3, this PR contains major changes, please confirm if it is as expected.

+'@ant-design/web3-icp': major

@wss-mz-web3
Copy link
Author

🚨 @wss-mz-web3, 本次 PR 的更改包含 major 变更,请确认是否符合预期。

🚨 @wss-mz-web3, this PR contains major changes, please confirm if it is as expected.

+'@ant-design/web3-icp': major

确认


## 介绍

`@ant-design/web3-icp` 提供了对 ICP 链的基础适配能力,目前内置支持 **Plug** 钱包,后续可以按需扩展更多 ICP 钱包。
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`@ant-design/web3-icp` 提供了对 ICP 链的基础适配能力,目前内置支持 **Plug** 钱包,后续可以按需扩展更多 ICP 钱包。
`@ant-design/web3-icp` 提供了对 [ICP ](https://dashboard.internetcomputer.org/)的基础适配能力,目前内置支持 **Plug** 钱包,后续可以按需扩展更多 ICP 钱包。

};

return (
<IcpWeb3ConfigProvider walletType={walletType}>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plug 是一个钱包名吧?你应该是支持一个 wallets 的数组,方便以后扩展。类似 https://github.com/ant-design/ant-design-web3/blob/main/packages/solana/src/wallets/built-in.ts

plug 钱包的信息也可以维护在 https://github.com/ant-design/ant-design-web3/tree/main/packages/assets/src/wallets 里面。

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢建议,相关修改已完成,请再 review 一下。


return (
<IcpWeb3ConfigProvider walletType={walletType}>
<IcpConnectButton
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

实现的方式不对,按照 antd web3 的设计,你应该是通过 https://web3.ant.design/components/web3-config-provider-cn 提供链接所需要的 context,然后引入 Connect 组件来使用,可以参考一下其它链的实现。

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感谢建议,修改已完成,请再 review 一下

@socket-security
Copy link

socket-security bot commented Dec 9, 2025

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
packages/icp/src/icp-provider/config-provider.tsx (2)

8-12: ConnectAsync 接口的 resolve 类型定义可以更精确

resolve 参数定义为 account?: Account,但 Promise 的泛型是 Promise<Account>,这意味着 resolve 时应该总是传入一个 Account。建议将类型改为非可选参数以保持一致性。

 interface ConnectAsync {
   promise: Promise<Account>;
-  resolve: (account?: Account) => void;
+  resolve: (account: Account) => void;
   reject: (reason: any) => void;
 }

76-95: chainList 中的 filter 检查可简化

当前 .filter((item) => item !== null) 是冗余的,因为 map 回调始终返回 item(合并后的对象或原始对象),不会返回 null

     return availableChains
       .map((item) => {
         const c = chainAssets?.find((asset) => {
           return asset.id === item.id;
         }) as Chain;

         if (c?.id) {
           return {
             ...item,
             ...c,
             id: c.id,
             name: c.name,
             icon: c.icon,
           };
         }
         return item;
-      })
-      .filter((item) => item !== null) as (Chain & IcpChain)[];
+      }) as (Chain & IcpChain)[];
packages/icp/src/wallets/built-in.ts (1)

1-10: 缺少 /* v8 ignore stop */ 结束注释。

文件开头使用了 /* v8 ignore start */ 来忽略测试覆盖率,但缺少对应的结束注释。如果是有意忽略整个文件的覆盖率,建议添加注释说明原因。

 export const PlugWallet = (metadata?: Partial<WalletMetadata>): IcpWalletFactory =>
   WalletFactory(createPlugWallet, { ...metadata_Plug, ...metadata });
+/* v8 ignore stop */
packages/icp/src/icp-provider/index.tsx (1)

27-45: 钱包实例重复创建,可能导致状态不一致。

availableWalletswallet 两个 useMemo 都调用 factory.create() 创建新的钱包实例。如果 create() 返回的不是同一个对象引用,或者钱包状态有副作用,可能导致不一致的行为。

建议先创建所有钱包实例,然后在两处复用:

+ const walletInstances = useMemo(() => {
+   return walletFactories.map((factory) => factory.create());
+ }, [walletFactories]);
+
  const availableWallets = useMemo(() => {
-   return walletFactories.map((factory) => {
-     const wallet = factory.create();
+   return walletInstances.map((wallet) => {
      return {
        name: wallet.name,
        remark: wallet.name,
        icon: wallet.icon,
        hasExtensionInstalled: async () => wallet.installed,
        hasWalletReady: async () => wallet.installed,
      };
    });
- }, [walletFactories]);
+ }, [walletInstances]);

  const wallet = useMemo(() => {
    // 优先选择已安装的钱包,如果没有已安装的,则选择第一个
-   const walletInstances = walletFactories.map((factory) => factory.create());
    const installedWallet = walletInstances.find((w) => w.installed);
    return installedWallet ?? walletInstances[0];
- }, [walletFactories]);
+ }, [walletInstances]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40f8f10 and 63140d3.

📒 Files selected for processing (12)
  • packages/assets/src/icp/chains.tsx (1 hunks)
  • packages/assets/src/icp/index.ts (1 hunks)
  • packages/assets/src/index.ts (1 hunks)
  • packages/assets/src/wallets/index.ts (1 hunks)
  • packages/assets/src/wallets/plug.tsx (1 hunks)
  • packages/common/src/types.ts (1 hunks)
  • packages/icp/src/icp-provider/config-provider.tsx (1 hunks)
  • packages/icp/src/icp-provider/index.tsx (1 hunks)
  • packages/icp/src/index.ts (1 hunks)
  • packages/icp/src/wallets/built-in.ts (1 hunks)
  • packages/icp/src/wallets/factory.ts (1 hunks)
  • packages/web3/src/icp/demos/basic.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/web3/src/icp/demos/basic.tsx
  • packages/icp/src/index.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: This is a multi-package React project built on Ant Design component library, designed to provide Web3 projects with wallet connection capabilities and DApp UI components
📚 Learning: 2025-11-25T09:58:18.233Z
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: This is a multi-package React project built on Ant Design component library, designed to provide Web3 projects with wallet connection capabilities and DApp UI components

Applied to files:

  • packages/icp/src/icp-provider/config-provider.tsx
  • packages/icp/src/icp-provider/index.tsx
📚 Learning: 2025-11-25T09:58:18.233Z
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: UI components are located under `packages/web3`, with other packages containing adapters for different blockchains or foundational packages

Applied to files:

  • packages/assets/src/index.ts
🧬 Code graph analysis (5)
packages/icp/src/icp-provider/config-provider.tsx (3)
packages/common/src/types.ts (4)
  • Account (8-14)
  • Locale (341-349)
  • Chain (81-93)
  • Wallet (141-149)
packages/assets/src/icp/chains.tsx (1)
  • IcpChain (4-6)
packages/icp/src/wallets/types.ts (1)
  • IcpWallet (3-12)
packages/icp/src/wallets/built-in.ts (4)
packages/common/src/types.ts (1)
  • WalletMetadata (186-255)
packages/icp/src/wallets/factory.ts (2)
  • IcpWalletFactory (5-7)
  • WalletFactory (14-25)
packages/icp/src/wallets/plug.tsx (1)
  • createPlugWallet (23-73)
packages/assets/src/wallets/plug.tsx (1)
  • metadata_Plug (4-20)
packages/assets/src/wallets/plug.tsx (2)
packages/common/src/types.ts (1)
  • WalletMetadata (186-255)
packages/icons/src/components/chrome-circle-colorful.tsx (1)
  • ChromeCircleColorful (13-25)
packages/assets/src/icp/chains.tsx (2)
packages/common/src/types.ts (1)
  • Chain (81-93)
packages/common/src/utils/createGetBrowserLink.ts (1)
  • createGetBrowserLink (1-9)
packages/icp/src/wallets/factory.ts (2)
packages/icp/src/wallets/types.ts (1)
  • IcpWallet (3-12)
packages/common/src/types.ts (1)
  • WalletMetadata (186-255)
🔇 Additional comments (9)
packages/icp/src/icp-provider/config-provider.tsx (3)

51-61: 账户状态同步逻辑正确

基于 principal 正确更新账户状态,处理了连接和断开两种情况。


63-74: 连接状态处理逻辑合理

正确处理了异步连接流程,在连接进行中时避免过早 resolve,并在完成后清理 ref。


125-145: 连接回调实现正确

Promise 模式正确实现了异步连接流程,错误处理妥当。_walletoptions 参数未使用是因为 ICP 钱包连接不需要这些参数,但保留它们以符合 Web3ConfigProvider 的接口要求。

packages/assets/src/index.ts (1)

4-4: 导出 ICP 模块符合现有模式

新增的 ICP 模块导出与现有的 walletschains/ethereumtokens 导出保持一致,正确扩展了 assets 包的公共 API。

packages/assets/src/wallets/index.ts (1)

18-18: Plug 钱包导出符合现有模式

新增的 Plug 钱包导出与其他钱包(MetaMask、Phantom 等)的导出方式保持一致。

packages/common/src/types.ts (1)

47-49: IcpChainIds 枚举定义正确

新增的 ICP 链 ID 枚举遵循现有的 SolanaChainIdsSuiChainIds 模式,结构清晰。

packages/assets/src/icp/index.ts (1)

1-1: ICP 资产模块入口文件结构正确

使用标准的 barrel export 模式聚合 ICP 相关的链定义,符合项目约定。

packages/icp/src/wallets/factory.ts (1)

1-25: LGTM! 工厂模式实现简洁清晰。

类型定义完整,WalletFactory 正确地将基础钱包与元数据合并,使用空值合并运算符处理图标回退逻辑。

packages/assets/src/wallets/plug.tsx (1)

4-20: Code structure looks good. Verify the Chrome extension link is accessible.

The wallet metadata is properly formatted with all required fields. The icon: null is acceptable if the actual icon is provided elsewhere in the implementation. Consider verifying that the Chrome Web Store URL (https://chromewebstore.google.com/detail/plug/cfbfdhimifdmdehjmkdobpcjfefblkjm) is correct and publicly accessible.

Comment on lines +12 to +15
browser: {
icon: <IcpColorful />,
getBrowserLink: createGetBrowserLink('https://dashboard.internetcomputer.org'),
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

ICP Dashboard 浏览器链接格式可能不正确。

createGetBrowserLink 工具函数生成的 URL 格式为 /address/{address}/tx/{hash},但 ICP Dashboard 使用的是 /account/{principal}/transaction/{txId} 格式。这会导致生成的浏览器链接无法正确跳转。

建议自定义 getBrowserLink 函数以匹配 ICP Dashboard 的 URL 结构:

   browser: {
     icon: <IcpColorful />,
-    getBrowserLink: createGetBrowserLink('https://dashboard.internetcomputer.org'),
+    getBrowserLink: (address: string, type: string) => {
+      if (type === 'address') {
+        return `https://dashboard.internetcomputer.org/account/${address}`;
+      }
+      if (type === 'transaction') {
+        return `https://dashboard.internetcomputer.org/transaction/${address}`;
+      }
+      throw new Error(`getBrowserLink unsupported type ${type}`);
+    },
   },

Committable suggestion skipped: line range outside the PR's diff.


const connectAsyncRef = useRef<ConnectAsync>();
const [account, setAccount] = useState<Account>();
const [balanceData, setBalanceData] = useState<bigint>();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

balanceData 状态未被更新,余额功能不完整

balanceData 状态被定义但 setBalanceData 从未被调用,导致传递给 Web3ConfigProviderbalance.value 始终为 undefined。如果 balance 功能是预期的,需要添加获取和更新余额的逻辑。

是否需要我帮助实现余额获取逻辑?或者如果这是后续迭代的功能,建议添加 TODO 注释说明。

🤖 Prompt for AI Agents
In packages/icp/src/icp-provider/config-provider.tsx around line 49, the
balanceData state is defined but never updated; add logic to fetch the account
balance and call setBalanceData so Web3ConfigProvider receives a real value.
Specifically, add a useEffect that runs when the active wallet/account or
provider changes, call the appropriate ICP actor/provider method to get the
balance, convert the returned value to bigint if needed, and invoke
setBalanceData with that result; handle errors and loading cases and clean up
any subscriptions. If balance is intentionally out of scope for now, add a TODO
comment at this location stating that balance fetching will be implemented in a
follow-up and leave balanceData undefined until then.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
packages/icp/src/icp-provider/config-provider.tsx (1)

48-49: balanceData 从未更新,balance 打开时实际不会有余额数据

balanceData 只在 Line 49 初始化,从未调用 setBalanceData,但在 balancetrue 时会被直接传入 Web3ConfigProviderbalance.value,结果始终是 undefined,余额能力名义存在但没有实际数据。

建议:

  • 如果本 PR 就要提供余额能力:在 principal / currentChainData 变化时拉取 ICP 余额并更新 balanceData
  • 如果余额不在本次范围内:暂时移除 balance 相关 props 或在这里加清晰的 TODO 注释,避免外部以为已经支持余额展示。

Also applies to: 108-117

🧹 Nitpick comments (3)
packages/icp/src/icp-provider/config-provider.tsx (1)

32-45: wallet/_wallet 未被使用,当前实现等同于“固定单钱包”,多钱包场景需要明确

在该组件中:

  • props 里解构了 wallet,但后续完全未使用;
  • connect 签名为 connect={async (_wallet, options) => {...}},实现中忽略了 _walletoptions

这意味着:

  • 现在无论 UI 选择哪个钱包,底层都走同一套 onConnect/onDisconnect 流程;
  • 如果未来上层 IcpWeb3ConfigProvider 支持多个钱包工厂,UI 展示的“可选钱包”与实际连接的钱包会不对齐。

建议:

  • 如果 ICP 目前只支持单一钱包(例如 Plug),可以在 props / 文档中明确“仅单钱包”,并移除未使用的 wallet/_wallet 参数,简化接口;
  • 如果预期支持多钱包,则应利用 _wallet(或者传入的 id)在内部选择对应的钱包实例,或者在这里留下 TODO 注释,避免后续使用者误解。

Also applies to: 121-148

packages/icp/src/icp-provider/index.tsx (2)

27-45: 重复调用 factory.create() 生成多份实例,可考虑统一实例化以减少混淆

目前每个 IcpWalletFactory 会在两个 useMemo 中各调用一次 create()

  • availableWallets:用于生成展示信息;
  • wallet:再次调用 create() 并基于 installed 选出一个实例。

结果是:

  • 同一个工厂会产生两套不同的 IcpWallet 实例;
  • 虽然现在只在 connect/disconnect 中使用 wallet,不会出大问题,但后续如果需要基于同一个实例上的状态(例如缓存连接、事件监听)就会比较混乱。

推荐简单重构为共用实例数组,例如:

const walletInstances = useMemo(
  () => walletFactories.map((factory) => factory.create()),
  [walletFactories],
);

const availableWallets = useMemo(
  () =>
    walletInstances.map((wallet) => ({
      name: wallet.name,
      remark: wallet.name,
      icon: wallet.icon,
      hasExtensionInstalled: async () => wallet.installed,
      hasWalletReady: async () => wallet.installed,
    })),
  [walletInstances],
);

const wallet = useMemo(() => {
  const installedWallet = walletInstances.find((w) => w.installed);
  return installedWallet ?? walletInstances[0];
}, [walletInstances]);

可读性和行为都会更清晰一些。


23-25: currentChain 不会随 chains Prop 更新,动态切换链列表时可能出现不同步

currentChain 初始值来源于 chains[0]

const [currentChain, setCurrentChain] = useState<IcpChain | undefined>(chains[0]);

但后续如果调用方在运行期改变了 chains(例如从 [Mainnet] 切成 [Testnet]),这里没有任何 useEffect 去同步 currentChain,可能会出现:

  • chains 已经换成新的列表;
  • currentChain 仍指向旧对象或旧 id,导致传给下层的链上下文不一致。

如果预期 chains 在整个生命周期内基本不变可以接受当前实现;否则建议在 chains 变化时进行一次校正,例如:

  • 若当前 currentChain 不在新的 chains 中,则回退到 chains[0]undefined

Also applies to: 75-75

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63140d3 and acf7a64.

📒 Files selected for processing (2)
  • packages/icp/src/icp-provider/config-provider.tsx (1 hunks)
  • packages/icp/src/icp-provider/index.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: This is a multi-package React project built on Ant Design component library, designed to provide Web3 projects with wallet connection capabilities and DApp UI components
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: UI components are located under `packages/web3`, with other packages containing adapters for different blockchains or foundational packages
📚 Learning: 2025-11-25T09:58:18.233Z
Learnt from: CR
Repo: ant-design/ant-design-web3 PR: 0
File: .cursor/rules/basic.mdc:0-0
Timestamp: 2025-11-25T09:58:18.233Z
Learning: This is a multi-package React project built on Ant Design component library, designed to provide Web3 projects with wallet connection capabilities and DApp UI components

Applied to files:

  • packages/icp/src/icp-provider/config-provider.tsx
  • packages/icp/src/icp-provider/index.tsx
🧬 Code graph analysis (2)
packages/icp/src/icp-provider/config-provider.tsx (3)
packages/common/src/types.ts (4)
  • Account (8-14)
  • Locale (341-349)
  • Chain (81-93)
  • Wallet (141-149)
packages/assets/src/icp/chains.tsx (1)
  • IcpChain (4-6)
packages/icp/src/wallets/types.ts (1)
  • IcpWallet (3-12)
packages/icp/src/icp-provider/index.tsx (5)
packages/common/src/types.ts (1)
  • Locale (341-349)
packages/assets/src/icp/chains.tsx (2)
  • IcpChain (4-6)
  • Icp (8-17)
packages/icp/src/wallets/factory.ts (1)
  • IcpWalletFactory (5-7)
packages/icp/src/wallets/built-in.ts (1)
  • PlugWallet (9-10)
packages/icp/src/icp-provider/config-provider.tsx (1)
  • AntDesignWeb3ConfigProvider (29-156)
🔇 Additional comments (1)
packages/icp/src/icp-provider/index.tsx (1)

47-60: Verify the promise resolution behavior of AntDesignWeb3ConfigProvider when getPrincipal() returns null

The review identifies a potential issue where getPrincipal() can return null (per IcpWallet type), but the current connect implementation doesn't handle this case. The concern is that if null is returned, setPrincipal(null) executes normally while the downstream AntDesignWeb3ConfigProvider promise may not resolve or reject properly, leaving callers hanging.

The suggested fix—throwing an error when getPrincipal() returns null—is logically sound and aligns with patterns in ICP development where null principal should be treated as a connection failure. However, this verification requires inspecting the actual promise handling logic in AntDesignWeb3ConfigProvider to confirm the hanging scenario described.

Comment on lines +47 to +75
const connectAsyncRef = useRef<ConnectAsync>();
const [account, setAccount] = useState<Account>();
const [balanceData, setBalanceData] = useState<bigint>();

// get account address
useEffect(() => {
if (!principal) {
setAccount(undefined);
return;
}

setAccount({
address: principal,
});
}, [principal]);

// connect/disconnect wallet
useEffect(() => {
if (connecting && connectAsyncRef.current) {
// connecting in progress
return;
}

if (principal && connectAsyncRef.current) {
connectAsyncRef.current.resolve({ address: principal });
connectAsyncRef.current = undefined;
}
}, [principal, connecting]);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find packages/icp/src -name "config-provider.tsx" -o -name "index.tsx" | head -20

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

fd -e tsx "config-provider" packages/icp/src/

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

fd -e tsx "index" packages/icp/src/icp-provider/

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

ls -la packages/icp/src/icp-provider/

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

wc -l packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

cat -n packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '120,160p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '40,80p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

cat -n packages/icp/src/icp-provider/index.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

rg -n "wallet\." packages/icp/src/icp-provider/index.tsx -A 3

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

rg -n "interface.*Wallet|type.*Wallet" packages/icp/src/ -A 5

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

rg -n "getPrincipal" packages/icp/src/ -B 2 -A 5

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '1,50p' packages/icp/src/icp-provider/index.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '50,100p' packages/icp/src/icp-provider/index.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

rg -n "type IcpWeb3Config|interface IcpWeb3Config" packages/icp/src/ -A 10

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

find packages/icp/src -name "*.ts" -o -name "*.tsx" | xargs rg -l "interface.*Wallet"

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '100,145p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '90,130p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

grep -n "onConnect" packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '80,110p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '1,50p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

grep -n "connectAsyncRef" packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '40,50p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

rg -n "connectAsyncRef" packages/icp/src/icp-provider/config-provider.tsx -B 2 -A 2

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

rg -n "type ConnectAsync|interface ConnectAsync" packages/icp/src/

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

rg -n "ConnectAsync" packages/icp/src/ -B 1 -A 3

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '125,150p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '1,30p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

fd -e ts -e tsx packages/icp/src/ | xargs grep -l "ConnectAsync"

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

rg "ConnectAsync" packages/icp/src/icp-provider/ -B 5 -A 5

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '127,145p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '1,80p' packages/icp/src/icp-provider/index.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

wc -l packages/icp/src/icp-provider/index.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

grep -n "principal" packages/icp/src/icp-provider/config-provider.tsx | head -20

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '50,75p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '45,90p' packages/icp/src/icp-provider/index.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

grep -n "wallet.connect" packages/icp/src/icp-provider/index.tsx -B 5 -A 5

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '50,95p' packages/icp/src/icp-provider/index.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

rg -n "timeout|setTimeout|catch|error" packages/icp/src/icp-provider/index.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '1,120p' packages/icp/src/icp-provider/index.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '125,150p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '55,75p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


🏁 Script executed:

sed -n '125,155p' packages/icp/src/icp-provider/config-provider.tsx

Repository: ant-design/ant-design-web3

Length of output: 196


Fix Promise hanging when wallet returns null principal without throwing

The connect() function creates a Promise that stores its resolve/reject in connectAsyncRef and waits for resolution from the useEffect([principal, connecting]). However, this effect only calls resolve when both principal && connectAsyncRef.current are truthy.

If wallet.getPrincipal() in the parent component's onConnect() returns null without throwing an error, the subsequent setPrincipal(null) causes the effect condition to fail—the Promise hangs indefinitely with no error feedback.

Add validation in onConnect():

const principal = await wallet.getPrincipal();
if (!principal) {
  throw new Error('Failed to get principal from wallet');
}
setPrincipal(principal);

This ensures the .catch() block in the connect() function is triggered, allowing reject to be called on the stored Promise so the caller receives proper error feedback instead of an indefinite hang.

🤖 Prompt for AI Agents
In packages/icp/src/icp-provider/config-provider.tsx around lines 47 to 75, the
connect() Promise can hang when the wallet returns a null principal because the
useEffect only resolves when principal is truthy; modify the onConnect() logic
where wallet.getPrincipal() is called to validate the result and throw an Error
if it is null/undefined (e.g., if (!principal) throw new Error('Failed to get
principal from wallet')), so the connect() caller's .catch() runs and the stored
Promise can be rejected instead of left hanging.

Comment on lines +40 to +45
const wallet = useMemo(() => {
// 优先选择已安装的钱包,如果没有已安装的,则选择第一个
const walletInstances = walletFactories.map((factory) => factory.create());
const installedWallet = walletInstances.find((w) => w.installed);
return installedWallet ?? walletInstances[0];
}, [walletFactories]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

walletFactories 允许为空时,disconnect 会在运行时抛异常

wallet 的计算逻辑:

const walletInstances = walletFactories.map((factory) => factory.create());
const installedWallet = walletInstances.find((w) => w.installed);
return installedWallet ?? walletInstances[0];

walletFactories 为空数组时,walletundefined。目前:

  • connect 已经在 Line 47–50 做了 if (!wallet) { return; } 防御;
  • disconnect 中直接执行 await wallet.disconnect();,会在 walletundefined 时抛出运行时错误。

建议加上与 connect 一致的防御逻辑,例如:

 const disconnect = async () => {
-  await wallet.disconnect();
-  setPrincipal(null);
+  if (!wallet) {
+    setPrincipal(null);
+    return;
+  }
+  await wallet.disconnect();
+  setPrincipal(null);
 };

这样即使调用方传入 wallets={[]} 也不会导致崩溃。

Also applies to: 62-65

🤖 Prompt for AI Agents
packages/icp/src/icp-provider/index.tsx around lines 40-45 (and also apply same
fix at lines 62-65): the computed wallet can be undefined when walletFactories
is an empty array, so add the same defensive null-check used in connect to
disconnect; before calling await wallet.disconnect(); add if (!wallet) { return;
} (or guard early) so disconnect no longer throws when wallet is undefined—apply
the identical guard wherever wallet is used without a prior null check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Independent new features major size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants