react-wallet-connector
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Wallet Connector Skill
React钱包连接器技能
This skill generates a complete, runnable React application that connects to a Midnight wallet extension using the DApp Connector API. It covers every file needed: Vite + React + TypeScript scaffold, wallet selection, connection logic, and a UI component.
WalletCardPrimary references:
- — React wallet connector guide (DApp Connector API)
docs.midnight.network - — full Midnight documentation index
llms.txt - — latest stable connector package (Feb 2026)
@midnight-ntwrk/dapp-connector-api@4.0.1
Key architecture notes:
- Wallets inject instances on
InitialAPI, each keyed by a UUID — never use hardcoded keys likewindow.midnightwindow.midnight.mnLace - Enumerate wallets with ; when multiple wallets exist, let the user choose
Object.values(window.midnight) - Import as a side effect to augment global
@midnight-ntwrk/dapp-connector-apitypeswindow.midnight - Request the shielded address only when your app actually needs it; the template uses
getUnshieldedAddress() - Network IDs: (local),
'undeployed','preview','preprod'— must match the wallet's configured network'mainnet' - Code examples omit CSS styling by design; add your preferred styling solution after scaffolding
Relationship to other skills:
- For full contract deploy + circuit calls via 1AM wallet → use
1am-wallet/ - For headless Node.js wallet + tests → use or
example-hello-world/midnight-js/ - This skill is the frontend wallet connection foundation only
本技能可生成一个完整的可运行React应用,通过DApp Connector API连接Midnight钱包扩展。涵盖所需的所有文件:Vite + React + TypeScript脚手架、钱包选择、连接逻辑,以及 UI组件。
WalletCard主要参考资料:
- — React钱包连接器指南(DApp Connector API)
docs.midnight.network - — Midnight完整文档索引
llms.txt - — 最新稳定连接器包(2026年2月)
@midnight-ntwrk/dapp-connector-api@4.0.1
核心架构说明:
- 钱包会在上注入
window.midnight实例,每个实例以UUID作为键——切勿使用InitialAPI这类硬编码键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) Project Structure
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.tsmy-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.ts2) Prerequisites
2) 前置条件
- Node.js 18+ and npm
- Basic React + TypeScript familiarity
- A Midnight wallet browser extension installed (1AM, Lace, or any DApp Connector–compatible wallet)
- Node.js 18+和npm
- 基本的React + TypeScript使用经验
- 已安装Midnight钱包浏览器扩展(1AM、Lace或任何兼容DApp Connector的钱包)
3) Scaffold & Install
3) 搭建与安装
bash
npm create vite@latest my-wallet-app -- --template react-ts
cd my-wallet-app
npm install @midnight-ntwrk/dapp-connector-api@4.0.1bash
npm create vite@latest my-wallet-app -- --template react-ts
cd my-wallet-app
npm install @midnight-ntwrk/dapp-connector-api@4.0.14) package.json
package.json4) package.json
package.jsonUse the Vite template output and ensure is listed:
react-ts@midnight-ntwrk/dapp-connector-apijson
{
"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"
}
}Pinto@midnight-ntwrk/dapp-connector-apiunless the user specifies otherwise. Check4.0.1if install fails withnpm view @midnight-ntwrk/dapp-connector-api version.ETARGET
使用Vite 模板输出,并确保已列入依赖:
react-ts@midnight-ntwrk/dapp-connector-apijson
{
"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
vite.config.ts5) vite.config.ts
vite.config.tstypescript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});typescript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});6) tsconfig.json
tsconfig.json6) tsconfig.json
tsconfig.jsonjson
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}json
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}7) tsconfig.app.json
tsconfig.app.json7) tsconfig.app.json
tsconfig.app.jsonjson
{
"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"]
}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
tsconfig.node.json8) tsconfig.node.json
tsconfig.node.jsonjson
{
"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"]
}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
src/vite-env.d.ts9) src/vite-env.d.ts
src/vite-env.d.tstypescript
/// <reference types="vite/client" />The side-effect import in augments — no manual declaration needed.
@midnight-ntwrk/dapp-connector-apiApp.tsxwindow.midnighttypescript
/// <reference types="vite/client" />App.tsx@midnight-ntwrk/dapp-connector-apiwindow.midnight10) src/types.ts
src/types.ts10) src/types.ts
src/types.tstypescript
export interface WalletCardProps {
isConnected: boolean;
walletAddress: string | null;
onConnect: () => void;
onDisconnect: () => void;
}typescript
export interface WalletCardProps {
isConnected: boolean;
walletAddress: string | null;
onConnect: () => void;
onDisconnect: () => void;
}11) src/selectWallet.ts
src/selectWallet.ts11) src/selectWallet.ts
src/selectWallet.tsWallets inject under with UUID keys. Always enumerate — never hardcode a wallet name.
window.midnighttypescript
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];
};When more than one wallet is available, render a picker using and let the user choose. Display each wallet's name and icon safely to prevent XSS.
listWallets()钱包会以UUID为键注入到下。始终枚举钱包——切勿硬编码钱包名称。
window.midnighttypescript
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];
};当存在多个钱包时,使用渲染选择器,让用户自行选择。安全地显示每个钱包的名称和图标,以防止XSS攻击。
listWallets()12) src/WalletCard.tsx
src/WalletCard.tsx12) src/WalletCard.tsx
src/WalletCard.tsxPresentation layer — connection status, address display, connect/disconnect buttons. No CSS included; add styling as needed.
tsx
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;展示层——连接状态、地址显示、连接/断开按钮。不包含CSS;可根据需要添加样式。
tsx
import React from 'react';
import type { WalletCardProps } from './types';
const WalletCard: React.FC<WalletCardProps> = ({
isConnected,
walletAddress,
onConnect,
onDisconnect,
}) => {
return (
<div>
<div>
<h2>连接状态</h2>
<div>{isConnected ? '已连接' : '未连接'}</div>
</div>
<div>
{isConnected && walletAddress ? (
<>
<p>钱包地址:</p>
<p title={walletAddress}>{walletAddress}</p>
</>
) : (
<p>请连接你的钱包以继续。</p>
)}
</div>
<div>
{isConnected ? (
<button onClick={onDisconnect}>断开钱包</button>
) : (
<button onClick={onConnect}>连接钱包</button>
)}
</div>
</div>
);
};
export default WalletCard;13) src/App.tsx
src/App.tsx13) src/App.tsx
src/App.tsxConnection logic using the DApp Connector API.
tsx
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;使用DApp Connector API的连接逻辑。
tsx
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('点击连接按钮');
let connected = false;
let address: string | null = null;
try {
const wallet = selectWallet();
// 本地开发使用'undeployed';线上网络使用'preprod' | 'preview' | 'mainnet'
const connectedApi = await wallet.connect('preprod');
const { unshieldedAddress } = await connectedApi.getUnshieldedAddress();
address = unshieldedAddress;
const serviceUriConfig = await connectedApi.getConfiguration();
console.log('服务URI配置:', serviceUriConfig);
const connectionStatus = await connectedApi.getConnectionStatus();
if (connectionStatus.status === 'connected') {
connected = true;
console.log('已连接到钱包:', address);
}
} catch (error) {
console.log('发生错误:', error);
}
setIsConnected(connected);
setWalletAddress(address);
};
const handleDisconnect = () => {
setWalletAddress(null);
setIsConnected(false);
};
return (
<div>
<header>
<h1>Midnight钱包连接器</h1>
</header>
<main>
<WalletCard
isConnected={isConnected}
walletAddress={walletAddress}
onConnect={handleConnect}
onDisconnect={handleDisconnect}
/>
</main>
</div>
);
};
export default App;Connection flow breakdown
连接流程分解
- Select wallet — reads
selectWallet()entries; throws if none found (caught bywindow.midnight)try/catch - Connect to network — prompts the user to authorize
wallet.connect(networkId) - Retrieve address — returns the public unshielded address
getUnshieldedAddress() - Check status — resolves to
getConnectionStatus(){ status: 'connected' | 'disconnected' } - Optional config — returns service URIs (indexer, node, etc.) for downstream SDK wiring
getConfiguration()
- 选择钱包 — 读取
selectWallet()条目;如果未找到则抛出错误(被window.midnight捕获)try/catch - 连接到网络 — 提示用户授权
wallet.connect(networkId) - 获取地址 — 返回公开的未屏蔽地址
getUnshieldedAddress() - 检查状态 — 返回
getConnectionStatus(){ status: 'connected' | 'disconnected' } - 可选配置 — 返回服务URI(索引器、节点等),用于下游SDK连接
getConfiguration()
14) src/main.tsx
src/main.tsx14) src/main.tsx
src/main.tsxtsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);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
index.html15) index.html
index.htmlhtml
<!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>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) Run
16) 运行
bash
npm install
npm run devOpen . Click Connect Wallet — the extension prompts for authorization. After approval, the app shows connection status and the unshielded address.
http://localhost:5173bash
npm install
npm run dev打开。点击连接钱包——扩展会提示授权。批准后,应用会显示连接状态和未屏蔽地址。
http://localhost:517317) Multi-Wallet Picker (Optional Enhancement)
17) 多钱包选择器(可选增强功能)
When returns more than one entry, replace auto-pick with a user-facing selector:
listWallets()selectWallet()tsx
// 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>
);
}Render wallet and as text only unless you sanitize HTML. Never use for wallet-provided icon URLs.
nameicondangerouslySetInnerHTML当返回多个条目时,将的自动选择替换为面向用户的选择器:
listWallets()selectWallet()tsx
// 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>未找到Midnight钱包。请安装钱包扩展并刷新页面。</p>;
}
return (
<ul>
{wallets.map((wallet) => (
<li key={wallet.name}>
<button type="button" onClick={() => onSelect(wallet)}>
{wallet.name}
</button>
</li>
))}
</ul>
);
}仅以文本形式渲染钱包的和,除非你对HTML进行了清理。切勿对钱包提供的图标URL使用。
nameicondangerouslySetInnerHTML18) Troubleshooting
18) 故障排除
| Error | Cause | Fix |
|---|---|---|
| No extension installed or page loaded before injection | Install a Midnight wallet; refresh after install |
| Extension disabled or wrong detection pattern | Use |
| Connection rejected / fails | Network mismatch | Ensure |
| Connection hangs | Wallet locked or not synced | Unlock wallet; wait for sync |
| Stale package version | Run |
Type errors on | Missing side-effect import | Add |
| 错误 | 原因 | 解决方法 |
|---|---|---|
| 未安装扩展,或页面加载早于扩展注入 | 安装Midnight钱包;安装后刷新页面 |
| 扩展已禁用,或检测模式错误 | 使用 |
| 连接被拒绝/失败 | 网络不匹配 | 确保 |
| 连接挂起 | 钱包已锁定或未同步 | 解锁钱包;等待同步完成 |
安装时出现 | 包版本过时 | 运行 |
| 缺少副作用导入 | 在 |
19) Next Steps
19) 后续步骤
After a working connector, extend the app:
| Feature | Skill / API |
|---|---|
| Deploy & call Compact contracts | |
| Token transfers | |
| Query balances & tx history | |
| Sign arbitrary messages | |
| Multi-network selector | Network dropdown calling |
| Full SDK provider wiring | |
完成可用的连接器后,可扩展应用功能:
| 功能 | 技能/API |
|---|---|
| 部署并调用Compact合约 | |
| 代币转账 | |
| 查询余额与交易历史 | |
| 签署任意消息 | |
| 多网络选择器 | 调用 |
| 完整SDK提供者连接 | |
20) Agent Checklist
20) Agent检查清单
When generating this template for a user:
- Scaffold with Vite or write all files from this skill
react-ts - Install
@midnight-ntwrk/dapp-connector-api@4.0.1 - Create ,
types.ts,selectWallet.ts,WalletCard.tsx,App.tsxmain.tsx - Include side-effect import in
@midnight-ntwrk/dapp-connector-apiApp.tsx - Use for wallet detection — never hardcode wallet keys
Object.values(window.midnight) - Default network to unless user specifies local (
'preprod') or other network'undeployed' - Omit CSS unless the user requests styling
- Mention wallet extension must be installed before testing
为用户生成此模板时:
- 使用Vite 搭建,或根据本技能编写所有文件
react-ts - 安装
@midnight-ntwrk/dapp-connector-api@4.0.1 - 创建、
types.ts、selectWallet.ts、WalletCard.tsx、App.tsxmain.tsx - 在中添加副作用导入
App.tsx@midnight-ntwrk/dapp-connector-api - 使用检测钱包——切勿硬编码钱包键
Object.values(window.midnight) - 默认网络设为,除非用户指定本地(
'preprod')或其他网络'undeployed' - 省略CSS,除非用户要求添加样式
- 提醒用户测试前必须安装钱包扩展