chore: sync submodule state (parent ref update)
Made-with: Cursor
This commit is contained in:
58
examples/react-example/README.md
Normal file
58
examples/react-example/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# MetaMask ChainID 138 React Example
|
||||
|
||||
Complete React example for integrating MetaMask with ChainID 138.
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Connect to MetaMask wallet
|
||||
- ✅ Add ChainID 138 network
|
||||
- ✅ Switch to ChainID 138 network
|
||||
- ✅ Add tokens (cUSDT, cUSDC, WETH)
|
||||
- ✅ Display wallet balance
|
||||
- ✅ Error handling
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
1. Install MetaMask browser extension
|
||||
2. Open the application
|
||||
3. Click "Connect Wallet"
|
||||
4. Approve connection in MetaMask
|
||||
5. Add ChainID 138 network if needed
|
||||
6. Add tokens to MetaMask
|
||||
|
||||
## Code Structure
|
||||
|
||||
- `App.tsx` - Main component with wallet connection logic
|
||||
- `App.css` - Styling
|
||||
- Uses `ethers.js` v6 for blockchain interactions
|
||||
|
||||
## Network Configuration
|
||||
|
||||
The example includes ChainID 138 network configuration:
|
||||
- Chain ID: 138 (0x8a)
|
||||
- RPC URLs: https://rpc.d-bis.org, https://rpc2.d-bis.org
|
||||
- Explorer: https://explorer.d-bis.org
|
||||
|
||||
## Token Addresses
|
||||
|
||||
- cUSDT: `0x93E66202A11B1772E55407B32B44e5Cd8eda7f22` (6 decimals)
|
||||
- cUSDC: `0xf22258f57794CC8E06237084b353Ab30fFfa640b` (6 decimals)
|
||||
- WETH: `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` (18 decimals)
|
||||
23
examples/react-example/package.json
Normal file
23
examples/react-example/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "metamask-chain138-react-example",
|
||||
"version": "1.0.0",
|
||||
"description": "React example for MetaMask integration with ChainID 138",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"ethers": "^6.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"typescript": "^5.2.0",
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
}
|
||||
97
examples/react-example/src/App.css
Normal file
97
examples/react-example/src/App.css
Normal file
@@ -0,0 +1,97 @@
|
||||
.app {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
padding: 40px;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #555;
|
||||
margin-bottom: 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.wallet-info {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.wallet-info p {
|
||||
margin: 10px 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.network-section,
|
||||
.tokens-section {
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 24px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: #5568d3;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #48bb78;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #38a169;
|
||||
}
|
||||
|
||||
.btn-token {
|
||||
background: #ed8936;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-token:hover {
|
||||
background: #dd6b20;
|
||||
}
|
||||
|
||||
.error {
|
||||
background: #fed7d7;
|
||||
color: #c53030;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
208
examples/react-example/src/App.tsx
Normal file
208
examples/react-example/src/App.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { ethers } from 'ethers';
|
||||
import './App.css';
|
||||
|
||||
// ChainID 138 Network Configuration
|
||||
const CHAIN_138_CONFIG = {
|
||||
chainId: '0x8a', // 138 in hex
|
||||
chainName: 'DeFi Oracle Meta Mainnet',
|
||||
nativeCurrency: {
|
||||
name: 'Ether',
|
||||
symbol: 'ETH',
|
||||
decimals: 18,
|
||||
},
|
||||
rpcUrls: ['https://rpc.d-bis.org', 'https://rpc2.d-bis.org'],
|
||||
blockExplorerUrls: ['https://explorer.d-bis.org'],
|
||||
};
|
||||
|
||||
// Token addresses
|
||||
const TOKENS = {
|
||||
cUSDT: '0x93E66202A11B1772E55407B32B44e5Cd8eda7f22',
|
||||
cUSDC: '0xf22258f57794CC8E06237084b353Ab30fFfa640b',
|
||||
WETH: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
||||
};
|
||||
|
||||
function App() {
|
||||
const [account, setAccount] = useState<string | null>(null);
|
||||
const [chainId, setChainId] = useState<string | null>(null);
|
||||
const [balance, setBalance] = useState<string>('0');
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Check if MetaMask is installed
|
||||
const isMetaMaskInstalled = typeof window.ethereum !== 'undefined';
|
||||
|
||||
// Connect to MetaMask
|
||||
const connectWallet = async () => {
|
||||
if (!isMetaMaskInstalled) {
|
||||
setError('MetaMask is not installed. Please install MetaMask extension.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const provider = new ethers.BrowserProvider(window.ethereum);
|
||||
const accounts = await provider.send('eth_requestAccounts', []);
|
||||
|
||||
if (accounts.length > 0) {
|
||||
setAccount(accounts[0]);
|
||||
setIsConnected(true);
|
||||
setError(null);
|
||||
|
||||
// Get chain ID
|
||||
const network = await provider.getNetwork();
|
||||
setChainId(network.chainId.toString());
|
||||
|
||||
// Get balance
|
||||
const balance = await provider.getBalance(accounts[0]);
|
||||
setBalance(ethers.formatEther(balance));
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to connect wallet');
|
||||
}
|
||||
};
|
||||
|
||||
// Add ChainID 138 to MetaMask
|
||||
const addChain138 = async () => {
|
||||
if (!isMetaMaskInstalled) {
|
||||
setError('MetaMask is not installed');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [CHAIN_138_CONFIG],
|
||||
});
|
||||
setError(null);
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to add network');
|
||||
}
|
||||
};
|
||||
|
||||
// Switch to ChainID 138
|
||||
const switchToChain138 = async () => {
|
||||
if (!isMetaMaskInstalled) {
|
||||
setError('MetaMask is not installed');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [{ chainId: CHAIN_138_CONFIG.chainId }],
|
||||
});
|
||||
setError(null);
|
||||
|
||||
// Refresh connection
|
||||
const provider = new ethers.BrowserProvider(window.ethereum);
|
||||
const network = await provider.getNetwork();
|
||||
setChainId(network.chainId.toString());
|
||||
} catch (err: any) {
|
||||
// If chain is not added, try to add it
|
||||
if ((err as any).code === 4902) {
|
||||
await addChain138();
|
||||
} else {
|
||||
setError((err as any).message || 'Failed to switch network');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add token to MetaMask
|
||||
const addToken = async (symbol: string, address: string, decimals: number) => {
|
||||
if (!isMetaMaskInstalled) {
|
||||
setError('MetaMask is not installed');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await window.ethereum.request({
|
||||
method: 'wallet_watchAsset',
|
||||
params: {
|
||||
type: 'ERC20',
|
||||
options: {
|
||||
address: address,
|
||||
symbol: symbol,
|
||||
decimals: decimals,
|
||||
},
|
||||
},
|
||||
});
|
||||
setError(null);
|
||||
} catch (err: any) {
|
||||
setError(err.message || 'Failed to add token');
|
||||
}
|
||||
};
|
||||
|
||||
// Check if on ChainID 138
|
||||
const isOnChain138 = chainId === '138';
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<div className="container">
|
||||
<h1>MetaMask ChainID 138 Example</h1>
|
||||
|
||||
{!isMetaMaskInstalled && (
|
||||
<div className="error">
|
||||
MetaMask is not installed. Please install MetaMask extension.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="error">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isConnected ? (
|
||||
<button onClick={connectWallet} className="btn btn-primary">
|
||||
Connect Wallet
|
||||
</button>
|
||||
) : (
|
||||
<div className="wallet-info">
|
||||
<h2>Wallet Connected</h2>
|
||||
<p><strong>Account:</strong> {account}</p>
|
||||
<p><strong>Chain ID:</strong> {chainId}</p>
|
||||
<p><strong>Balance:</strong> {parseFloat(balance).toFixed(4)} ETH</p>
|
||||
|
||||
{!isOnChain138 && (
|
||||
<div className="network-section">
|
||||
<h3>Network Setup</h3>
|
||||
<button onClick={addChain138} className="btn btn-secondary">
|
||||
Add ChainID 138
|
||||
</button>
|
||||
<button onClick={switchToChain138} className="btn btn-secondary">
|
||||
Switch to ChainID 138
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isOnChain138 && (
|
||||
<div className="tokens-section">
|
||||
<h3>Add Tokens</h3>
|
||||
<button
|
||||
onClick={() => addToken('cUSDT', TOKENS.cUSDT, 6)}
|
||||
className="btn btn-token"
|
||||
>
|
||||
Add cUSDT
|
||||
</button>
|
||||
<button
|
||||
onClick={() => addToken('cUSDC', TOKENS.cUSDC, 6)}
|
||||
className="btn btn-token"
|
||||
>
|
||||
Add cUSDC
|
||||
</button>
|
||||
<button
|
||||
onClick={() => addToken('WETH', TOKENS.WETH, 18)}
|
||||
className="btn btn-token"
|
||||
>
|
||||
Add WETH
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
Reference in New Issue
Block a user