Skip to content

Commit 276c4e1

Browse files
committed
feat: Add the base file of the WalletConnect multi-chain sample project
1 parent c824a8e commit 276c4e1

File tree

11 files changed

+733
-0
lines changed

11 files changed

+733
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# WalletConnect Project ID from https://cloud.walletconnect.com
2+
REACT_APP_PROJECT_ID=your_project_id_here
3+
4+
# Infura API Key for multiple chain support
5+
REACT_APP_INFURA_KEY=your_infura_key_here
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# WalletConnect 多链交互示例
2+
3+
这个示例项目展示了如何使用 WalletConnect v2 实现多链交互功能,包括跨链资产查看、多链钱包连接和链间切换等功能。
4+
5+
## 功能特点
6+
7+
- 支持多个区块链网络的同时连接
8+
- 实时显示多链资产余额
9+
- 支持在不同链之间无缝切换
10+
- 展示跨链交易历史
11+
- 集成多链交易签名功能
12+
13+
## 技术栈
14+
15+
- React.js - 前端框架
16+
- wagmi - 以太坊 React Hooks 库
17+
- web3modal - Web3钱包连接组件
18+
- ethers.js - 区块链交互库
19+
- viem - 现代以太坊开发工具包
20+
21+
## 支持的网络
22+
23+
- Ethereum Mainnet
24+
- Polygon
25+
- Arbitrum
26+
- Optimism
27+
- Binance Smart Chain
28+
29+
## 开始使用
30+
31+
1. **安装依赖**
32+
```bash
33+
yarn install
34+
```
35+
36+
2. **环境配置**
37+
- 复制 `.env.example``.env`
38+
-[WalletConnect Cloud](https://cloud.walletconnect.com) 注册并获取项目ID
39+
-[Infura](https://infura.io) 注册并获取API密钥
40+
- 更新 `.env` 文件中的配置
41+
42+
3. **启动开发服务器**
43+
```bash
44+
yarn start
45+
```
46+
47+
## 项目结构
48+
49+
```
50+
├── src/
51+
│ ├── components/ # React组件
52+
│ ├── hooks/ # 自定义Hooks
53+
│ ├── config/ # 配置文件
54+
│ ├── utils/ # 工具函数
55+
│ └── services/ # 区块链服务
56+
```
57+
58+
## 主要功能演示
59+
60+
1. **多链钱包连接**
61+
- 支持同时连接多个区块链网络
62+
- 显示每个网络的连接状态
63+
- 提供网络切换功能
64+
65+
2. **资产管理**
66+
- 查看多链资产余额
67+
- 显示代币价格和市值
68+
- 支持资产转账功能
69+
70+
3. **交易功能**
71+
- 发送跨链交易
72+
- 查看交易历史
73+
- 交易状态追踪
74+
75+
## 测试
76+
77+
```bash
78+
# 运行单元测试
79+
yarn test
80+
81+
# 运行E2E测试
82+
yarn test:e2e
83+
```
84+
85+
## 参考资源
86+
87+
- [WalletConnect v2 文档](https://docs.walletconnect.com/2.0)
88+
- [Wagmi 文档](https://wagmi.sh)
89+
- [Web3Modal 文档](https://docs.walletconnect.com/2.0/web3modal/about)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "multichain-example",
3+
"version": "1.0.0",
4+
"private": true,
5+
"dependencies": {
6+
"@wagmi/core": "^1.4.5",
7+
"@web3modal/ethereum": "^2.7.1",
8+
"@web3modal/react": "^2.7.1",
9+
"ethers": "^5.7.2",
10+
"react": "^18.2.0",
11+
"react-dom": "^18.2.0",
12+
"react-scripts": "5.0.1",
13+
"viem": "^1.18.1",
14+
"wagmi": "^1.4.5"
15+
},
16+
"scripts": {
17+
"start": "react-scripts start",
18+
"build": "react-scripts build",
19+
"test": "react-scripts test",
20+
"eject": "react-scripts eject"
21+
},
22+
"eslintConfig": {
23+
"extends": [
24+
"react-app",
25+
"react-app/jest"
26+
]
27+
},
28+
"browserslist": {
29+
"production": [
30+
">0.2%",
31+
"not dead",
32+
"not op_mini all"
33+
],
34+
"development": [
35+
"last 1 chrome version",
36+
"last 1 firefox version",
37+
"last 1 safari version"
38+
]
39+
}
40+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<meta name="theme-color" content="#000000" />
8+
<meta
9+
name="description"
10+
content="WalletConnect Multichain Example"
11+
/>
12+
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
13+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
14+
<title>WalletConnect Multichain Example</title>
15+
</head>
16+
<body>
17+
<noscript>You need to enable JavaScript to run this app.</noscript>
18+
<div id="root"></div>
19+
</body>
20+
</html>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { EthereumClient, w3mConnectors, w3mProvider } from '@web3modal/ethereum'
2+
import { Web3Modal } from '@web3modal/react'
3+
import { configureChains, createConfig, WagmiConfig } from 'wagmi'
4+
import { arbitrum, mainnet, polygon } from 'wagmi/chains'
5+
import { useAccount, useDisconnect } from 'wagmi'
6+
7+
// 配置链和providers
8+
const chains = [arbitrum, mainnet, polygon]
9+
const projectId = 'YOUR_PROJECT_ID' // 需要替换为你的WalletConnect项目ID
10+
11+
const { publicClient } = configureChains(chains, [w3mProvider({ projectId })])
12+
const wagmiConfig = createConfig({
13+
autoConnect: true,
14+
connectors: w3mConnectors({ projectId, chains }),
15+
publicClient
16+
})
17+
const ethereumClient = new EthereumClient(wagmiConfig, chains)
18+
19+
function Profile() {
20+
const { address, isConnected } = useAccount()
21+
const { disconnect } = useDisconnect()
22+
23+
if (isConnected)
24+
return (
25+
<div>
26+
已连接到: {address}
27+
<button onClick={() => disconnect()}>断开连接</button>
28+
</div>
29+
)
30+
return <div>请连接钱包</div>
31+
}
32+
33+
export default function App() {
34+
return (
35+
<>
36+
<WagmiConfig config={wagmiConfig}>
37+
<Profile />
38+
</WagmiConfig>
39+
40+
<Web3Modal
41+
projectId={projectId}
42+
ethereumClient={ethereumClient}
43+
/>
44+
</>
45+
)
46+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { useAccount, useNetwork, useBalance, useConnect, useDisconnect } from 'wagmi';
3+
import { SUPPORTED_CHAINS } from '../utils/chainUtils';
4+
import { isTransactionSafe, isHighRiskTransaction } from '../utils/securityUtils';
5+
6+
const MultiChainWallet = () => {
7+
const { address, isConnected } = useAccount();
8+
const { chain } = useNetwork();
9+
const { connect, connectors } = useConnect();
10+
const { disconnect } = useDisconnect();
11+
const [selectedChain, setSelectedChain] = useState(null);
12+
13+
// 获取当前链上的余额
14+
const { data: balance } = useBalance({
15+
address,
16+
chainId: selectedChain?.id,
17+
watch: true,
18+
});
19+
20+
// 监听链切换
21+
useEffect(() => {
22+
if (chain) {
23+
const currentChain = Object.values(SUPPORTED_CHAINS).find(
24+
(c) => c.id === chain.id
25+
);
26+
setSelectedChain(currentChain);
27+
}
28+
}, [chain]);
29+
30+
// 处理连接钱包
31+
const handleConnect = async (connector) => {
32+
try {
33+
await connect({ connector });
34+
} catch (error) {
35+
console.error('连接钱包失败:', error);
36+
}
37+
};
38+
39+
// 处理断开连接
40+
const handleDisconnect = async () => {
41+
try {
42+
await disconnect();
43+
setSelectedChain(null);
44+
} catch (error) {
45+
console.error('断开连接失败:', error);
46+
}
47+
};
48+
49+
return (
50+
<div className="p-4">
51+
<h1 className="text-2xl font-bold mb-4">多链钱包</h1>
52+
53+
{/* 连接状态 */}
54+
<div className="mb-4">
55+
{isConnected ? (
56+
<div>
57+
<p>已连接地址: {address}</p>
58+
<p>当前网络: {selectedChain?.name || '未知网络'}</p>
59+
<p>余额: {balance?.formatted || '0'} {balance?.symbol}</p>
60+
<button
61+
onClick={handleDisconnect}
62+
className="bg-red-500 text-white px-4 py-2 rounded mt-2"
63+
>
64+
断开连接
65+
</button>
66+
</div>
67+
) : (
68+
<div>
69+
<p>请连接钱包</p>
70+
<div className="flex gap-2 mt-2">
71+
{connectors.map((connector) => (
72+
<button
73+
key={connector.id}
74+
onClick={() => handleConnect(connector)}
75+
className="bg-blue-500 text-white px-4 py-2 rounded"
76+
>
77+
{connector.name}
78+
</button>
79+
))}
80+
</div>
81+
</div>
82+
)}
83+
</div>
84+
85+
{/* 支持的链列表 */}
86+
{isConnected && (
87+
<div className="mt-4">
88+
<h2 className="text-xl font-semibold mb-2">支持的网络</h2>
89+
<div className="grid grid-cols-2 gap-2">
90+
{Object.values(SUPPORTED_CHAINS).map((supportedChain) => (
91+
<div
92+
key={supportedChain.id}
93+
className={`p-3 rounded border ${
94+
selectedChain?.id === supportedChain.id
95+
? 'bg-green-100 border-green-500'
96+
: 'bg-gray-100 border-gray-300'
97+
}`}
98+
>
99+
<p className="font-medium">{supportedChain.name}</p>
100+
<p className="text-sm text-gray-600">
101+
Chain ID: {supportedChain.id}
102+
</p>
103+
</div>
104+
))}
105+
</div>
106+
</div>
107+
)}
108+
109+
{/* 安全提示 */}
110+
{isConnected && (
111+
<div className="mt-4 p-3 bg-yellow-100 rounded">
112+
<h3 className="font-semibold">安全提示</h3>
113+
<ul className="list-disc list-inside text-sm">
114+
<li>请确保在进行跨链操作时仔细检查网络和地址</li>
115+
<li>大额转账请使用小额测试</li>
116+
<li>谨防钓鱼网站和恶意合约</li>
117+
</ul>
118+
</div>
119+
)}
120+
</div>
121+
);
122+
};
123+
124+
export default MultiChainWallet;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
body {
2+
margin: 0;
3+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5+
sans-serif;
6+
-webkit-font-smoothing: antialiased;
7+
-moz-osx-font-smoothing: grayscale;
8+
}
9+
10+
code {
11+
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12+
monospace;
13+
}
14+
15+
.container {
16+
padding: 20px;
17+
max-width: 800px;
18+
margin: 0 auto;
19+
}
20+
21+
button {
22+
padding: 10px 20px;
23+
margin: 10px;
24+
border: none;
25+
border-radius: 5px;
26+
background-color: #646cff;
27+
color: white;
28+
cursor: pointer;
29+
font-size: 16px;
30+
}
31+
32+
button:hover {
33+
background-color: #535bf2;
34+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom/client';
3+
import App from './App';
4+
import './index.css';
5+
6+
const root = ReactDOM.createRoot(document.getElementById('root'));
7+
root.render(
8+
<React.StrictMode>
9+
<App />
10+
</React.StrictMode>
11+
);

0 commit comments

Comments
 (0)