关于 react-wallet-connector
此技能可生成可运行的 React 与 TypeScript 应用,通过 DApp Connector API 连接 Midnight 钱包扩展。它提供前端连接基础,包括钱包发现、授权、地址展示和可选的钱包选择。
主要功能
- 01Vite React TypeScript 应用脚手架
- 02发现注入的 Midnight 钱包
- 03通过 DApp Connector API 连接
- 04显示连接状态与未屏蔽地址
- 05可选的多钱包选择器
- 06网络选择指导
适用场景
- 01创建连接钱包的 React 应用
- 02让用户从已安装的钱包中选择
- 03请求钱包授权并连接网络
- 04展示钱包连接状态和地址信息
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ kali-decoder/ midnight-skills --skill react-wallet-connector安装后在 Claude Code 中输入/react-wallet-connector确认已加载。
详细介绍
React 钱包连接器技能
此技能可生成一个完整、可运行的 React 应用,通过 DApp Connector API 连接 Midnight 钱包扩展。涵盖所需的所有文件:Vite + React + TypeScript 脚手架、钱包选择、连接逻辑,以及 WalletCard UI 组件。
主要参考资料:
docs.midnight.network— React 钱包连接器指南(DApp Connector API)llms.txt— Midnight 完整文档索引@midnight-ntwrk/[email protected]— 最新稳定版连接器软件包(2026 年 2 月)
关键架构说明:
- 钱包会在
window.midnight上注入InitialAPI实例,每个实例都由一个 UUID 标识 — 切勿使用window.midnight.mnLace这类硬编码键 - 使用
Object.values(window.midnight)枚举钱包;存在多个钱包时,让用户选择 - 将
@midnight-ntwrk/dapp-connector-api作为副作用导入,以扩展全局window.midnight类型 - 仅在应用确实需要时才请求屏蔽地址;此模板使用
getUnshieldedAddress() - 网络 ID:
'undeployed'(本地)、'preview'、'preprod'、'mainnet'— 必须与钱包配置的网络一致 - 代码示例有意省略 CSS 样式;搭建脚手架后,可添加你偏好的样式方案
与其他技能的关系:
- 如需通过 1AM 钱包完整部署合约并调用电路 → 使用
1am-wallet/ - 如需无头 Node.js 钱包和测试 → 使用
example-hello-world/或midnight-js/ - 此技能仅作为前端钱包连接基础
1) 项目结构
my-wallet-app/
├── index.html
├── package.json
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.node.json
├── vite.config.ts
└── src/
├── main.tsx
├── App.tsx
├── WalletCard.tsx
├── types.ts
├── selectWallet.ts
└── vite-env.d.ts
2) 先决条件
- Node.js 18+ 和 npm
- 熟悉 React + TypeScript 基础知识
- 已安装 Midnight 钱包浏览器扩展(1AM、Lace 或任何兼容 DApp Connector 的钱包)
3) 搭建脚手架并安装
npm create vite@latest my-wallet-app -- --template react-ts
cd my-wallet-app
npm install @midnight-ntwrk/[email protected]
4) package.json
使用 Vite react-ts 模板的输出,并确保其中列出了 @midnight-ntwrk/dapp-connector-api:
{
"name": "my-wallet-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@midnight-ntwrk/dapp-connector-api": "4.0.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.0",
"typescript": "~5.7.0",
"vite": "^6.0.0"
}
}
除非用户另有说明,否则将
@midnight-ntwrk/dapp-connector-api固定为4.0.1。如果安装因ETARGET失败,请检查npm view @midnight-ntwrk/dapp-connector-api version。
5) vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});
6) tsconfig.json
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
7) tsconfig.app.json
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}
8) tsconfig.node.json
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}
9) src/vite-env.d.ts
/// <reference types="vite/client" />
App.tsx 中对 @midnight-ntwrk/dapp-connector-api 的副作用导入会扩展 window.midnight 类型 — 无需手动声明。
10) src/types.ts
export interface WalletCardProps {
isConnected: boolean;
walletAddress: string | null;
onConnect: () => void;
onDisconnect: () => void;
}
11) src/selectWallet.ts
钱包会在 window.midnight 下以 UUID 键注入。始终枚举钱包 — 切勿硬编码钱包名称。
import type { InitialAPI } from '@midnight-ntwrk/dapp-connector-api';
export const listWallets = (): InitialAPI[] => {
const injected = window.midnight;
return injected ? Object.values(injected) : [];
};
export const selectWallet = (): InitialAPI => {
const wallets = listWallets();
if (wallets.length === 0) {
throw new Error(
'No Midnight wallet found. Please install a Midnight wallet extension.',
);
}
return wallets[0];
};
如果有多个可用钱包,请使用 listWallets() 显示选择器,让用户选择。安全地显示每个钱包的名称和图标,以防止 XSS。
12) src/WalletCard.tsx
展示层 — 连接状态、地址显示、连接/断开连接按钮。不包含 CSS;可按需添加样式。
import React from 'react';
import type { WalletCardProps } from './types';
const WalletCard: React.FC<WalletCardProps> = ({
isConnected,
walletAddress,
onConnect,
onDisconnect,
}) => {
return (
<div>
<div>
<h2>Connection Status</h2>
<div>{isConnected ? 'Connected' : 'Disconnected'}</div>
</div>
<div>
{isConnected && walletAddress ? (
<>
<p>Wallet Address:</p>
<p title={walletAddress}>{walletAddress}</p>
</>
) : (
<p>Please connect your wallet to proceed.</p>
)}
</div>
<div>
{isConnected ? (
<button onClick={onDisconnect}>Disconnect Wallet</button>
) : (
<button onClick={onConnect}>Connect Wallet</button>
)}
</div>
</div>
);
};
export default WalletCard;
13) src/App.tsx
使用 DApp Connector API 的连接逻辑。
import React, { useState } from 'react';
import WalletCard from './WalletCard';
import '@midnight-ntwrk/dapp-connector-api';
import { selectWallet } from './selectWallet';
const App: React.FC = () => {
const [isConnected, setIsConnected] = useState<boolean>(false);
const [walletAddress, setWalletAddress] = useState<string | null>(null);
const handleConnect = async () => {
console.log('Connect button clicked');
let connected = false;
let address: string | null = null;
try {
const wallet = selectWallet();
// 'undeployed' for local dev; 'preprod' | 'preview' | 'mainnet' for live networks
const connectedApi = await wallet.connect('preprod');
const { unshieldedAddress } = await connectedApi.getUnshieldedAddress();
address = unshieldedAddress;
const serviceUriConfig = await connectedApi.getConfiguration();
console.log('Service URI Config:', serviceUriConfig);
const connectionStatus = await connectedApi.getConnectionStatus();
if (connectionStatus.status === 'connected') {
connected = true;
console.log('Connected to the wallet:', address);
}
} catch (error) {
console.log('An error occurred:', error);
}
setIsConnected(connected);
setWalletAddress(address);
};
const handleDisconnect = () => {
setWalletAddress(null);
setIsConnected(false);
};
return (
<div>
<header>
<h1>Midnight Wallet Connector</h1>
</header>
<main>
<WalletCard
isConnected={isConnected}
walletAddress={walletAddress}
onConnect={handleConnect}
onDisconnect={handleDisconnect}
/>
</main>
</div>
);
};
export default App;
连接流程分解
- 选择钱包 —
selectWallet()读取window.midnight条目;如果未找到钱包则抛出错误(由try/catch捕获) - 连接到网络 —
wallet.connect(networkId)会提示用户授权 - 获取地址 —
getUnshieldedAddress()返回公开的非屏蔽地址 - 检查状态 —
getConnectionStatus()返回{ status: 'connected' | 'disconnected' } - 可选配置 —
getConfiguration()返回服务 URI(索引器、节点等),供下游 SDK 配置使用
14) src/main.tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);
15) index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Midnight Wallet Connector</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
16) 运行
npm install
npm run dev
打开 http://localhost:5173。点击 Connect Wallet — 扩展会提示授权。批准后,应用会显示连接状态和非屏蔽地址。
17) 多钱包选择器(可选增强)
当 listWallets() 返回多个条目时,将 selectWallet() 的自动选择替换为面向用户的选择器:
// src/WalletPicker.tsx
import type { InitialAPI } from '@midnight-ntwrk/dapp-connector-api';
import { listWallets } from './selectWallet';
type Props = {
onSelect: (wallet: InitialAPI) => void;
};
export function WalletPicker({ onSelect }: Props) {
const wallets = listWallets();
if (wallets.length === 0) {
return <p>No Midnight wallet found. Install a wallet extension and refresh.</p>;
}
return (
<ul>
{wallets.map((wallet) => (
<li key={wallet.name}>
<button type="button" onClick={() => onSelect(wallet)}>
{wallet.name}
</button>
</li>
))}
</ul>
);
}
除非对 HTML 进行清理,否则只能将钱包的 name 和 icon 作为文本呈现。切勿对钱包提供的图标 URL 使用 dangerouslySetInnerHTML。
18) 故障排除
| 错误 | 原因 | 修复方法 |
|---|---|---|
window.midnight 为 undefined | 未安装扩展,或页面在注入前已加载 | 安装 Midnight 钱包;安装后刷新页面 |
No Midnight wallet found | 扩展已停用,或检测模式不正确 | 使用 Object.values(window.midnight)——不要使用 window.midnight.mnLace 或其他硬编码键 |
| 连接被拒绝/失败 | 网络不匹配 | 确保 connect('preprod') 与钱包当前使用的网络一致 |
| 连接一直挂起 | 钱包已锁定或尚未同步 | 解锁钱包;等待同步完成 |
安装时出现 ETARGET | 软件包版本过旧 | 运行 npm view @midnight-ntwrk/dapp-connector-api version 并固定到最新版本 |
window.midnight 上的类型错误 | 缺少副作用导入 | 在 App.tsx 中添加 import '@midnight-ntwrk/dapp-connector-api' |
19) 后续步骤
连接器正常运行后,可扩展应用:
| 功能 | 技能/API |
|---|---|
| 部署并调用 Compact 合约 | 1am-wallet/ |
| 代币转账 | token-transfers/ |
| 查询余额和交易历史 | indexer/ |
| 签署任意消息 | connectedApi 签名方法(参见 DApp Connector API 文档) |
| 多网络选择器 | 调用 wallet.connect(selectedNetwork) 的网络下拉菜单 |
| 完整 SDK 提供方接线 | midnight-js/ |
20) Agent 检查清单
为用户生成此模板时:
- 使用 Vite
react-ts搭建项目,或根据此技能编写所有文件 - 安装
@midnight-ntwrk/[email protected] - 创建
types.ts、selectWallet.ts、WalletCard.tsx、App.tsx、main.tsx - 在
App.tsx中加入副作用导入@midnight-ntwrk/dapp-connector-api - 使用
Object.values(window.midnight)检测钱包——切勿硬编码钱包键 - 默认网络设为
'preprod',除非用户指定本地网络('undeployed')或其他网络 - 除非用户要求设置样式,否则省略 CSS
- 提醒用户测试前必须安装钱包扩展