chore: sync submodule state (parent ref update)

Made-with: Cursor
This commit is contained in:
defiQUG
2026-03-02 12:14:14 -08:00
parent b6a776e5d7
commit 25c96e210a
316 changed files with 29779 additions and 677 deletions

176
examples/README.md Normal file
View File

@@ -0,0 +1,176 @@
# Smart Accounts Examples
Complete examples demonstrating Smart Accounts Kit integration on ChainID 138.
---
## Available Examples
### 1. TypeScript Example (`smart-accounts-example.ts`)
**Type**: TypeScript/Node.js
**Use Case**: Backend integration, testing, automation
**Features**:
- Complete Smart Accounts Kit integration
- Account creation
- Delegation management
- Permission requests
- Batch operations
- AccountWalletRegistry integration
**Usage**:
```bash
# Install dependencies
npm install
# Run with ts-node
npx ts-node examples/smart-accounts-example.ts
```
---
### 2. React Example (`smart-accounts-react-example/`)
**Type**: React + TypeScript
**Use Case**: Frontend dApp integration
**Features**:
- React hooks for Smart Accounts
- Wallet connection
- Smart Account creation
- Delegation UI
- Real-time status updates
**Usage**:
```bash
cd examples/smart-accounts-react-example
npm install
npm start
```
**Documentation**: See `smart-accounts-react-example/README.md`
---
### 3. Vue.js Example (`smart-accounts-vue-example/`)
**Type**: Vue 3 + TypeScript
**Use Case**: Frontend dApp integration (Vue ecosystem)
**Features**:
- Vue 3 Composition API
- Wallet connection
- Smart Account creation
- Delegation UI
- Reactive state management
**Usage**:
```bash
cd examples/smart-accounts-vue-example
npm install
npm run dev
```
**Documentation**: See `smart-accounts-vue-example/README.md`
---
### 4. HTML/JavaScript Example (`smart-accounts-example.html`)
**Type**: Vanilla HTML/JavaScript
**Use Case**: Simple demos, quick prototypes, learning
**Features**:
- No build step required
- Simple HTML page
- Basic Smart Accounts functionality
- Easy to understand and modify
**Usage**:
```bash
# Open in browser
open examples/smart-accounts-example.html
```
**Note**: This is a simplified example. For production, use React or Vue examples.
---
## Configuration
All examples require configuration with deployed contract addresses:
1. **Deploy contracts** (see [Deployment Guide](../docs/QUICK_START_DEPLOYMENT.md))
2. **Update configuration** in `config/smart-accounts-config.json`:
```json
{
"chainId": 138,
"rpcUrl": "https://rpc.d-bis.org",
"entryPointAddress": "0x...",
"accountFactoryAddress": "0x...",
"paymasterAddress": "0x..." // optional
}
```
3. **Update examples** to load configuration
---
## Common Features
All examples demonstrate:
1. **Wallet Connection**: Connect to MetaMask
2. **Smart Account Creation**: Create a new Smart Account
3. **Delegation**: Request and manage delegations
4. **Permissions**: Request Advanced Permissions (ERC-7715)
5. **Batch Operations**: Execute multiple operations in one transaction
---
## Documentation
- [Developer Guide](../docs/SMART_ACCOUNTS_DEVELOPER_GUIDE.md)
- [API Reference](../docs/SMART_ACCOUNTS_API_REFERENCE.md)
- [Delegation Guide](../docs/DELEGATION_USAGE_GUIDE.md)
- [Advanced Permissions Guide](../docs/ADVANCED_PERMISSIONS_GUIDE.md)
- [Quick Start Deployment](../docs/QUICK_START_DEPLOYMENT.md)
---
## Requirements
### Prerequisites
- **MetaMask**: Installed and configured
- **Node.js**: v18 or higher (for React/Vue examples)
- **Network**: ChainID 138 added to MetaMask
- **Contracts**: Smart Accounts contracts deployed
### Dependencies
- `@metamask/smart-accounts-kit`: Smart Accounts Kit SDK
- `ethers`: Ethereum library (v6.0.0+)
---
## Getting Started
1. **Choose an example** based on your framework preference
2. **Install dependencies**: `npm install`
3. **Configure**: Update configuration with contract addresses
4. **Run**: Follow example-specific instructions
5. **Test**: Connect wallet and create Smart Account
---
## Support
For issues or questions:
- Check [Troubleshooting Guide](../docs/SMART_ACCOUNTS_TROUBLESHOOTING.md)
- Review [FAQ](../docs/SMART_ACCOUNTS_FAQ.md)
- See [Developer Guide](../docs/SMART_ACCOUNTS_DEVELOPER_GUIDE.md)
---
**Last Updated**: 2026-01-26

143
examples/provider-e2e.html Normal file
View File

@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Provider E2E Add chains, switch, tokens (Chain 138 + Mainnet + ALL)</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 560px; margin: 24px auto; padding: 0 16px; }
h1 { font-size: 1.25rem; margin-bottom: 8px; }
.sub { color: #666; font-size: 0.875rem; margin-bottom: 20px; }
section { margin-bottom: 24px; padding: 16px; background: #f5f5f5; border-radius: 8px; }
button { padding: 8px 14px; margin-right: 8px; margin-bottom: 8px; cursor: pointer; }
.out { margin-top: 12px; white-space: pre-wrap; word-break: break-all; font-size: 0.8125rem; }
.err { color: #c00; }
.ok { color: #080; }
</style>
</head>
<body>
<h1>Provider E2E (manual)</h1>
<p class="sub">Add Chain 138, Ethereum Mainnet, ALL Mainnet; switch chain; list tokens. Requires MetaMask.</p>
<section>
<strong>1. Add chains</strong>
<p><button id="add-chains">Add all chains to MetaMask</button></p>
<div id="add-out" class="out"></div>
</section>
<section>
<strong>2. Switch chain</strong>
<p>
<button data-chain="138">Switch to 138</button>
<button data-chain="1">Switch to 1</button>
<button data-chain="651940">Switch to 651940</button>
</p>
<div id="switch-out" class="out"></div>
</section>
<section>
<strong>3. Tokens (from provider)</strong>
<p><button id="list-tokens">List tokens for current chain</button></p>
<div id="tokens-out" class="out"></div>
</section>
<section>
<strong>4. ETH/USD price (oracle)</strong>
<p><button id="get-price">Get ETH/USD for current chain</button></p>
<div id="price-out" class="out"></div>
</section>
<script type="module">
const addOut = document.getElementById('add-out');
const switchOut = document.getElementById('switch-out');
const tokensOut = document.getElementById('tokens-out');
const priceOut = document.getElementById('price-out');
function noProvider() {
return !window.ethereum;
}
async function getProvider() {
if (noProvider()) {
addOut.innerHTML = '<span class="err">No window.ethereum. Install MetaMask.</span>';
return null;
}
return window.ethereum;
}
// Provider is in ../provider (same repo); serve this page from a local server (e.g. npx serve .)
const providerUrl = new URL('../provider/index.js', import.meta.url).href;
const oraclesUrl = new URL('../provider/oracles.js', import.meta.url).href;
const { addChainsToWallet, switchChain, getTokensByChain } = await import(providerUrl);
let getEthUsdPrice;
try {
const oracles = await import(oraclesUrl);
getEthUsdPrice = oracles.getEthUsdPrice;
} catch (_) {
getEthUsdPrice = null;
}
document.getElementById('add-chains').onclick = async () => {
addOut.textContent = '';
const ethereum = await getProvider();
if (!ethereum) return;
try {
const result = await addChainsToWallet(ethereum);
addOut.innerHTML = '<span class="ok">Added: ' + (result.added || 0) + ', Skipped: ' + (result.skipped || 0) + (result.errors?.length ? ', Errors: ' + result.errors.length : '') + '</span>';
if (result.errors?.length) addOut.innerHTML += '\n' + result.errors.join('\n');
} catch (e) {
addOut.innerHTML = '<span class="err">' + (e.message || e) + '</span>';
}
};
document.querySelectorAll('[data-chain]').forEach(btn => {
btn.onclick = async () => {
switchOut.textContent = '';
const ethereum = await getProvider();
if (!ethereum) return;
const chainId = parseInt(btn.dataset.chain, 10);
try {
await switchChain(ethereum, chainId);
switchOut.innerHTML = '<span class="ok">Switched to chain ' + chainId + '</span>';
} catch (e) {
switchOut.innerHTML = '<span class="err">' + (e.message || e) + '</span>';
}
};
});
document.getElementById('list-tokens').onclick = async () => {
tokensOut.textContent = '';
const ethereum = await getProvider();
if (!ethereum) return;
try {
const chainId = parseInt(await ethereum.request({ method: 'eth_chainId' }), 16);
const tokens = getTokensByChain(chainId) || [];
const list = tokens.filter(t => !(t.tags || []).includes('oracle')).map(t => t.symbol + ' @ ' + t.address);
tokensOut.textContent = 'Chain ' + chainId + ': ' + (list.length ? list.join('\n') : 'no tokens');
} catch (e) {
tokensOut.innerHTML = '<span class="err">' + (e.message || e) + '</span>';
}
};
document.getElementById('get-price').onclick = async () => {
priceOut.textContent = '';
const ethereum = await getProvider();
if (!ethereum) return;
if (!getEthUsdPrice) {
priceOut.innerHTML = '<span class="err">ethers not loaded; getEthUsdPrice requires ethers.</span>';
return;
}
try {
const { ethers } = await import('https://esm.sh/ethers@6');
const provider = new ethers.BrowserProvider(ethereum);
const chainId = Number((await provider.getNetwork()).chainId);
const result = await getEthUsdPrice(provider, chainId);
if (result) priceOut.innerHTML = '<span class="ok">ETH/USD: ' + result.price + ' (chain ' + chainId + ')</span>';
else priceOut.innerHTML = '<span class="err">No oracle for chain ' + chainId + '</span>';
} catch (e) {
priceOut.innerHTML = '<span class="err">' + (e.message || e) + '</span>';
}
};
</script>
</body>
</html>

View 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)

View 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"
}
}

View 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;
}

View 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;

View File

@@ -0,0 +1,273 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Smart Accounts Example - ChainID 138</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.container {
background: white;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
color: #333;
}
h2 {
color: #555;
margin-top: 0;
}
button {
background-color: #4CAF50;
border: none;
color: white;
padding: 12px 24px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 10px 5px;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s;
}
button:hover:not(:disabled) {
background-color: #45a049;
}
button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.error {
background-color: #f44336;
color: white;
padding: 15px;
border-radius: 4px;
margin: 20px 0;
}
.success {
background-color: #4CAF50;
color: white;
padding: 15px;
border-radius: 4px;
margin: 20px 0;
}
.info {
background-color: #2196F3;
color: white;
padding: 15px;
border-radius: 4px;
margin: 20px 0;
}
code {
background-color: #f4f4f4;
padding: 2px 6px;
border-radius: 3px;
font-family: monospace;
}
.address {
word-break: break-all;
font-family: monospace;
background-color: #f4f4f4;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
</style>
</head>
<body>
<h1>Smart Accounts Example</h1>
<p><strong>ChainID 138 - SMOM-DBIS-138</strong></p>
<div id="error" class="error" style="display: none;"></div>
<div id="success" class="success" style="display: none;"></div>
<div id="info" class="info" style="display: none;"></div>
<div class="container">
<h2>1. Connect Wallet</h2>
<p>Connect your MetaMask wallet to get started.</p>
<button id="connectBtn" onclick="connectWallet()">Connect MetaMask</button>
<div id="walletInfo" style="display: none;">
<p><strong>Connected Address:</strong></p>
<div class="address" id="userAddress"></div>
</div>
</div>
<div class="container" id="smartAccountSection" style="display: none;">
<h2>2. Create Smart Account</h2>
<p>Create a new Smart Account linked to your wallet.</p>
<button id="createAccountBtn" onclick="createSmartAccount()">Create Smart Account</button>
<div id="accountInfo" style="display: none;">
<p><strong>Smart Account Address:</strong></p>
<div class="address" id="accountAddress"></div>
<p><strong>Owner:</strong></p>
<div class="address" id="accountOwner"></div>
</div>
</div>
<div class="container" id="delegationSection" style="display: none;">
<h2>3. Request Delegation</h2>
<p>Request delegation for a dApp or service to execute transactions on your behalf.</p>
<label for="targetAddress">Target Address:</label>
<input type="text" id="targetAddress" placeholder="0x..." style="width: 100%; padding: 8px; margin: 10px 0; border: 1px solid #ddd; border-radius: 4px;">
<button id="requestDelegationBtn" onclick="requestDelegation()">Request Delegation</button>
<div id="delegationInfo" style="display: none;">
<p><strong>Delegation Status:</strong></p>
<div id="delegationStatus"></div>
</div>
</div>
<div class="container" id="batchSection" style="display: none;">
<h2>4. Batch Operations</h2>
<p>Execute multiple operations in a single transaction.</p>
<button id="batchBtn" onclick="batchOperations()">Execute Batch</button>
<div id="batchInfo" style="display: none;">
<p><strong>Batch Transaction:</strong></p>
<div class="address" id="batchTx"></div>
</div>
</div>
<script type="module">
// Note: This is a simplified example. In production, you would:
// 1. Use a bundler (webpack, vite, etc.)
// 2. Import SmartAccountsKit properly
// 3. Handle errors more gracefully
// 4. Add proper TypeScript types
// For this HTML example, we'll use a simplified approach
// In production, use the React or Vue examples instead
let userAddress = '';
let smartAccountAddress = '';
let smartAccountsKit = null;
// Check if MetaMask is installed
if (typeof window.ethereum === 'undefined') {
showError('MetaMask is not installed. Please install MetaMask to continue.');
document.getElementById('connectBtn').disabled = true;
}
// Load configuration (in production, this would be from a config file)
const config = {
chainId: 138,
rpcUrl: 'https://rpc.d-bis.org',
entryPointAddress: '', // Set after deployment
accountFactoryAddress: '', // Set after deployment
};
window.connectWallet = async function() {
try {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
userAddress = accounts[0];
document.getElementById('userAddress').textContent = userAddress;
document.getElementById('walletInfo').style.display = 'block';
document.getElementById('smartAccountSection').style.display = 'block';
showSuccess('Wallet connected successfully!');
} catch (error) {
showError('Failed to connect wallet: ' + error.message);
}
};
window.createSmartAccount = async function() {
try {
showInfo('Creating Smart Account...');
// In production, use SmartAccountsKit:
// const kit = new SmartAccountsKit(config);
// const account = await kit.createAccount({ owner: userAddress });
// For demo purposes, we'll simulate:
smartAccountAddress = '0x' + Array.from({length: 40}, () => Math.floor(Math.random() * 16).toString(16)).join('');
document.getElementById('accountAddress').textContent = smartAccountAddress;
document.getElementById('accountOwner').textContent = userAddress;
document.getElementById('accountInfo').style.display = 'block';
document.getElementById('delegationSection').style.display = 'block';
document.getElementById('batchSection').style.display = 'block';
showSuccess('Smart Account created successfully!');
} catch (error) {
showError('Failed to create Smart Account: ' + error.message);
}
};
window.requestDelegation = async function() {
const target = document.getElementById('targetAddress').value;
if (!target || !target.startsWith('0x')) {
showError('Please enter a valid target address');
return;
}
try {
showInfo('Requesting delegation...');
// In production, use SmartAccountsKit:
// const delegation = await kit.requestDelegation({ target, permissions: [...], expiry: ... });
// For demo purposes, we'll simulate:
document.getElementById('delegationStatus').innerHTML = `
<strong>Target:</strong> ${target}<br>
<strong>Status:</strong> Approved<br>
<strong>Expires:</strong> ${new Date(Date.now() + 86400000).toLocaleString()}
`;
document.getElementById('delegationInfo').style.display = 'block';
showSuccess('Delegation requested successfully!');
} catch (error) {
showError('Failed to request delegation: ' + error.message);
}
};
window.batchOperations = async function() {
try {
showInfo('Executing batch operations...');
// In production, use SmartAccountsKit:
// const userOps = await kit.batchUserOperations([...]);
// const result = await kit.executeBatch(userOps);
// For demo purposes, we'll simulate:
const txHash = '0x' + Array.from({length: 64}, () => Math.floor(Math.random() * 16).toString(16)).join('');
document.getElementById('batchTx').textContent = txHash;
document.getElementById('batchInfo').style.display = 'block';
showSuccess('Batch operations executed successfully!');
} catch (error) {
showError('Failed to execute batch: ' + error.message);
}
};
function showError(message) {
const el = document.getElementById('error');
el.textContent = message;
el.style.display = 'block';
setTimeout(() => el.style.display = 'none', 5000);
}
function showSuccess(message) {
const el = document.getElementById('success');
el.textContent = message;
el.style.display = 'block';
setTimeout(() => el.style.display = 'none', 5000);
}
function showInfo(message) {
const el = document.getElementById('info');
el.textContent = message;
el.style.display = 'block';
setTimeout(() => el.style.display = 'none', 3000);
}
</script>
</body>
</html>

View File

@@ -0,0 +1,187 @@
/**
* Smart Accounts Example
*
* Complete example demonstrating Smart Accounts Kit usage
*/
import { SmartAccountsKit } from '@metamask/smart-accounts-kit';
import { ethers } from 'ethers';
// Load configuration
const config = require('../config/smart-accounts-config.json');
// Initialize Smart Accounts Kit
const smartAccountsKit = new SmartAccountsKit({
chainId: config.chainId,
rpcUrl: config.rpcUrl,
entryPointAddress: config.entryPointAddress,
accountFactoryAddress: config.accountFactoryAddress,
paymasterAddress: config.paymasterAddress || undefined,
});
// Example 1: Create Smart Account
async function createSmartAccount() {
console.log('Creating Smart Account...');
// Get user address from MetaMask
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const userAddress = await signer.getAddress();
// Create Smart Account
const smartAccount = await smartAccountsKit.createAccount({
owner: userAddress,
});
console.log('Smart Account created:', smartAccount.address);
return smartAccount;
}
// Example 2: Request Delegation
async function requestDelegation(smartAccountAddress: string, dAppAddress: string) {
console.log('Requesting delegation...');
const delegation = await smartAccountsKit.requestDelegation({
target: dAppAddress,
permissions: ['execute_transactions', 'batch_operations'],
expiry: Date.now() + 86400000, // 24 hours
});
if (delegation.approved) {
console.log('Delegation approved!');
} else {
console.log('Delegation rejected');
}
return delegation;
}
// Example 3: Check Delegation Status
async function checkDelegation(smartAccountAddress: string, dAppAddress: string) {
console.log('Checking delegation status...');
const status = await smartAccountsKit.getDelegationStatus({
target: dAppAddress,
account: smartAccountAddress,
});
console.log('Active:', status.active);
console.log('Expires:', new Date(status.expiry));
console.log('Permissions:', status.permissions);
return status;
}
// Example 4: Request Advanced Permission
async function requestPermission(
smartAccountAddress: string,
contractAddress: string,
functionSelector: string
) {
console.log('Requesting permission...');
const permission = await smartAccountsKit.requestAdvancedPermission({
target: contractAddress,
functionSelector: functionSelector,
allowed: true,
});
if (permission.approved) {
console.log('Permission granted!');
} else {
console.log('Permission denied');
}
return permission;
}
// Example 5: Batch User Operations
async function batchOperations(smartAccountAddress: string) {
console.log('Creating batch operations...');
const userOps = await smartAccountsKit.batchUserOperations([
{
to: '0x...', // Token address
data: '0x...', // Transfer data
value: '0',
},
{
to: '0x...', // Another address
data: '0x...', // Another operation
value: '0',
},
]);
console.log('Batch created:', userOps.length, 'operations');
// Execute batch
const result = await smartAccountsKit.executeBatch(userOps);
console.log('Transaction hash:', result.hash);
return result;
}
// Example 6: Link Smart Account to Fiat Account
async function linkToFiatAccount(smartAccountAddress: string, accountRefId: string) {
console.log('Linking Smart Account to fiat account...');
// This would typically be done by account manager via AccountWalletRegistryExtended
// Example using ethers:
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const registry = new ethers.Contract(
'0x...', // AccountWalletRegistryExtended address
[
'function linkSmartAccount(bytes32 accountRefId, address smartAccount, bytes32 provider)',
],
signer
);
const tx = await registry.linkSmartAccount(
accountRefId,
smartAccountAddress,
ethers.id('METAMASK_SMART_ACCOUNT')
);
await tx.wait();
console.log('Smart Account linked!');
}
// Complete Example Flow
async function completeExample() {
try {
// 1. Create Smart Account
const account = await createSmartAccount();
// 2. Request Delegation
const dAppAddress = '0x...'; // Your dApp address
await requestDelegation(account.address, dAppAddress);
// 3. Check Delegation Status
await checkDelegation(account.address, dAppAddress);
// 4. Request Permission
const contractAddress = '0x...'; // Contract address
const functionSelector = '0xa9059cbb'; // transfer(address,uint256)
await requestPermission(account.address, contractAddress, functionSelector);
// 5. Batch Operations
await batchOperations(account.address);
console.log('All examples completed successfully!');
} catch (error) {
console.error('Error:', error);
}
}
// Export for use in other files
export {
createSmartAccount,
requestDelegation,
checkDelegation,
requestPermission,
batchOperations,
linkToFiatAccount,
completeExample,
};

View File

@@ -0,0 +1,52 @@
# Smart Accounts React Example
Complete React example demonstrating Smart Accounts Kit integration on ChainID 138.
## Features
- Connect to MetaMask
- Create Smart Accounts
- Request Delegations
- View Delegation Status
- Batch Operations (coming soon)
## Installation
```bash
npm install
```
## Configuration
Update `src/App.tsx` to use your configuration:
```typescript
const config = require('../../config/smart-accounts-config.json');
```
Or create a local config file with your deployed contract addresses.
## Running
```bash
npm start
```
The app will open at `http://localhost:3000`.
## Usage
1. **Connect Wallet**: Click "Connect MetaMask" to connect your wallet
2. **Create Smart Account**: Click "Create Smart Account" to create a new Smart Account
3. **Request Delegation**: Request delegation for a dApp or service
4. **View Status**: View delegation status and expiry
## Documentation
- [Developer Guide](../../docs/SMART_ACCOUNTS_API_REFERENCE.md)
- [API Reference](../../docs/SMART_ACCOUNTS_API_REFERENCE.md)
- [Delegation Guide](../../docs/SMART_ACCOUNTS_FAQ.md)
## License
See parent repository for license information.

View File

@@ -0,0 +1,36 @@
{
"name": "smart-accounts-react-example",
"version": "1.0.0",
"description": "React example for Smart Accounts Kit on ChainID 138",
"private": true,
"dependencies": {
"@metamask/smart-accounts-kit": "^0.3.0",
"ethers": "^6.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

View File

@@ -0,0 +1,93 @@
.App {
text-align: center;
min-height: 100vh;
display: flex;
flex-direction: column;
}
.App-header {
background-color: #282c34;
padding: 20px;
color: white;
}
.App-main {
flex: 1;
padding: 20px;
max-width: 800px;
margin: 0 auto;
width: 100%;
}
.section {
background: #f5f5f5;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
text-align: left;
}
.section h2 {
margin-top: 0;
color: #333;
}
.section p {
margin: 10px 0;
word-break: break-all;
}
button {
background-color: #4CAF50;
border: none;
color: white;
padding: 12px 24px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 10px 0;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s;
}
button:hover:not(:disabled) {
background-color: #45a049;
}
button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.error {
background-color: #f44336;
color: white;
padding: 15px;
border-radius: 4px;
margin: 20px 0;
}
.App-footer {
background-color: #282c34;
padding: 20px;
color: white;
}
.App-footer a {
color: #61dafb;
}
ul {
list-style-type: none;
padding: 0;
}
li {
background: white;
padding: 15px;
margin: 10px 0;
border-radius: 4px;
text-align: left;
}

View File

@@ -0,0 +1,220 @@
/**
* Smart Accounts React Example
*
* Complete React example demonstrating Smart Accounts Kit integration
*/
import React, { useState, useEffect } from 'react';
import { SmartAccountsKit } from '@metamask/smart-accounts-kit';
import { ethers } from 'ethers';
import './App.css';
// Load configuration
const config = require('../../config/smart-accounts-config.json');
interface SmartAccount {
address: string;
owner: string;
}
interface Delegation {
target: string;
active: boolean;
expiry: number;
permissions: string[];
}
function App() {
const [provider, setProvider] = useState<ethers.BrowserProvider | null>(null);
const [signer, setSigner] = useState<ethers.JsonRpcSigner | null>(null);
const [userAddress, setUserAddress] = useState<string>('');
const [smartAccount, setSmartAccount] = useState<SmartAccount | null>(null);
const [smartAccountsKit, setSmartAccountsKit] = useState<SmartAccountsKit | null>(null);
const [delegations, setDelegations] = useState<Delegation[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// Initialize MetaMask connection
useEffect(() => {
if (window.ethereum) {
const initProvider = new ethers.BrowserProvider(window.ethereum);
setProvider(initProvider);
// Initialize Smart Accounts Kit
const kit = new SmartAccountsKit({
chainId: config.chainId,
rpcUrl: config.rpcUrl,
entryPointAddress: config.entryPointAddress,
accountFactoryAddress: config.accountFactoryAddress,
paymasterAddress: config.paymasterAddress || undefined,
});
setSmartAccountsKit(kit);
} else {
setError('MetaMask is not installed');
}
}, []);
// Connect to MetaMask
const connectWallet = async () => {
if (!provider) return;
try {
setLoading(true);
setError(null);
const accounts = await provider.send('eth_requestAccounts', []);
const signer = await provider.getSigner();
const address = await signer.getAddress();
setSigner(signer);
setUserAddress(address);
} catch (err: any) {
setError(err.message || 'Failed to connect wallet');
} finally {
setLoading(false);
}
};
// Create Smart Account
const createSmartAccount = async () => {
if (!smartAccountsKit || !userAddress) return;
try {
setLoading(true);
setError(null);
const account = await smartAccountsKit.createAccount({
owner: userAddress,
});
setSmartAccount({
address: account.address,
owner: userAddress,
});
} catch (err: any) {
setError(err.message || 'Failed to create smart account');
} finally {
setLoading(false);
}
};
// Request Delegation
const requestDelegation = async (target: string) => {
if (!smartAccountsKit || !smartAccount) return;
try {
setLoading(true);
setError(null);
const delegation = await smartAccountsKit.requestDelegation({
target: target,
permissions: ['execute_transactions', 'batch_operations'],
expiry: Date.now() + 86400000, // 24 hours
});
if (delegation.approved) {
// Refresh delegations
await loadDelegations();
}
} catch (err: any) {
setError(err.message || 'Failed to request delegation');
} finally {
setLoading(false);
}
};
// Load Delegations
const loadDelegations = async () => {
if (!smartAccountsKit || !smartAccount) return;
try {
// This would typically fetch from your backend or contract
// For demo purposes, we'll use a placeholder
const status = await smartAccountsKit.getDelegationStatus({
target: '0x...', // Replace with actual target
account: smartAccount.address,
});
// Update delegations state
// Implementation depends on your delegation storage
} catch (err: any) {
console.error('Failed to load delegations:', err);
}
};
return (
<div className="App">
<header className="App-header">
<h1>Smart Accounts Example</h1>
<p>ChainID 138 - SMOM-DBIS-138</p>
</header>
<main className="App-main">
{error && (
<div className="error">
<strong>Error:</strong> {error}
</div>
)}
{!userAddress ? (
<div className="section">
<h2>Connect Wallet</h2>
<button onClick={connectWallet} disabled={loading || !provider}>
{loading ? 'Connecting...' : 'Connect MetaMask'}
</button>
</div>
) : (
<>
<div className="section">
<h2>Wallet Connected</h2>
<p><strong>Address:</strong> {userAddress}</p>
</div>
{!smartAccount ? (
<div className="section">
<h2>Create Smart Account</h2>
<button onClick={createSmartAccount} disabled={loading}>
{loading ? 'Creating...' : 'Create Smart Account'}
</button>
</div>
) : (
<>
<div className="section">
<h2>Smart Account</h2>
<p><strong>Address:</strong> {smartAccount.address}</p>
<p><strong>Owner:</strong> {smartAccount.owner}</p>
</div>
<div className="section">
<h2>Delegations</h2>
<button onClick={() => requestDelegation('0x...')} disabled={loading}>
{loading ? 'Requesting...' : 'Request Delegation'}
</button>
{delegations.length > 0 && (
<ul>
{delegations.map((delegation, index) => (
<li key={index}>
<strong>Target:</strong> {delegation.target}<br />
<strong>Active:</strong> {delegation.active ? 'Yes' : 'No'}<br />
<strong>Expires:</strong> {new Date(delegation.expiry).toLocaleString()}
</li>
))}
</ul>
)}
</div>
</>
)}
</>
)}
</main>
<footer className="App-footer">
<p>
See <a href="../../docs/SMART_ACCOUNTS_DEVELOPER_GUIDE.md">Developer Guide</a> for more information.
</p>
</footer>
</div>
);
}
export default App;

View File

@@ -0,0 +1,52 @@
# Smart Accounts Vue.js Example
Complete Vue.js example demonstrating Smart Accounts Kit integration on ChainID 138.
## Features
- Connect to MetaMask
- Create Smart Accounts
- Request Delegations
- View Delegation Status
- Batch Operations (coming soon)
## Installation
```bash
npm install
```
## Configuration
Update `src/App.vue` to use your configuration:
```typescript
const config = require('../../config/smart-accounts-config.json');
```
Or create a local config file with your deployed contract addresses.
## Running
```bash
npm run dev
```
The app will open at `http://localhost:5173` (or similar port).
## Usage
1. **Connect Wallet**: Click "Connect MetaMask" to connect your wallet
2. **Create Smart Account**: Click "Create Smart Account" to create a new Smart Account
3. **Request Delegation**: Request delegation for a dApp or service
4. **View Status**: View delegation status and expiry
## Documentation
- [Developer Guide](../../docs/SMART_ACCOUNTS_API_REFERENCE.md)
- [API Reference](../../docs/SMART_ACCOUNTS_API_REFERENCE.md)
- [Delegation Guide](../../docs/SMART_ACCOUNTS_FAQ.md)
## License
See parent repository for license information.

View File

@@ -0,0 +1,21 @@
{
"name": "smart-accounts-vue-example",
"version": "1.0.0",
"description": "Vue.js example for Smart Accounts Kit on ChainID 138",
"private": true,
"dependencies": {
"@metamask/smart-accounts-kit": "^0.3.0",
"ethers": "^6.0.0",
"vue": "^3.3.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"vite": "^4.0.0",
"typescript": "^5.0.0"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}

View File

@@ -0,0 +1,297 @@
<template>
<div class="app">
<header class="app-header">
<h1>Smart Accounts Example</h1>
<p>ChainID 138 - SMOM-DBIS-138</p>
</header>
<main class="app-main">
<div v-if="error" class="error">
<strong>Error:</strong> {{ error }}
</div>
<div v-if="!userAddress" class="section">
<h2>Connect Wallet</h2>
<button @click="connectWallet" :disabled="loading || !provider">
{{ loading ? 'Connecting...' : 'Connect MetaMask' }}
</button>
</div>
<template v-else>
<div class="section">
<h2>Wallet Connected</h2>
<p><strong>Address:</strong> {{ userAddress }}</p>
</div>
<div v-if="!smartAccount" class="section">
<h2>Create Smart Account</h2>
<button @click="createSmartAccount" :disabled="loading">
{{ loading ? 'Creating...' : 'Create Smart Account' }}
</button>
</div>
<template v-else>
<div class="section">
<h2>Smart Account</h2>
<p><strong>Address:</strong> {{ smartAccount.address }}</p>
<p><strong>Owner:</strong> {{ smartAccount.owner }}</p>
</div>
<div class="section">
<h2>Delegations</h2>
<button @click="requestDelegation" :disabled="loading">
{{ loading ? 'Requesting...' : 'Request Delegation' }}
</button>
<ul v-if="delegations.length > 0">
<li v-for="(delegation, index) in delegations" :key="index">
<strong>Target:</strong> {{ delegation.target }}<br />
<strong>Active:</strong> {{ delegation.active ? 'Yes' : 'No' }}<br />
<strong>Expires:</strong> {{ new Date(delegation.expiry).toLocaleString() }}
</li>
</ul>
</div>
</template>
</template>
</main>
<footer class="app-footer">
<p>
See <a href="../../docs/SMART_ACCOUNTS_DEVELOPER_GUIDE.md">Developer Guide</a> for more information.
</p>
</footer>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { SmartAccountsKit } from '@metamask/smart-accounts-kit';
import { ethers } from 'ethers';
// Load configuration
const config = require('../../config/smart-accounts-config.json');
interface SmartAccount {
address: string;
owner: string;
}
interface Delegation {
target: string;
active: boolean;
expiry: number;
permissions: string[];
}
const provider = ref<ethers.BrowserProvider | null>(null);
const signer = ref<ethers.JsonRpcSigner | null>(null);
const userAddress = ref<string>('');
const smartAccount = ref<SmartAccount | null>(null);
const smartAccountsKit = ref<SmartAccountsKit | null>(null);
const delegations = ref<Delegation[]>([]);
const loading = ref(false);
const error = ref<string | null>(null);
// Initialize MetaMask connection
onMounted(() => {
if (window.ethereum) {
const initProvider = new ethers.BrowserProvider(window.ethereum);
provider.value = initProvider;
// Initialize Smart Accounts Kit
const kit = new SmartAccountsKit({
chainId: config.chainId,
rpcUrl: config.rpcUrl,
entryPointAddress: config.entryPointAddress,
accountFactoryAddress: config.accountFactoryAddress,
paymasterAddress: config.paymasterAddress || undefined,
});
smartAccountsKit.value = kit;
} else {
error.value = 'MetaMask is not installed';
}
});
// Connect to MetaMask
const connectWallet = async () => {
if (!provider.value) return;
try {
loading.value = true;
error.value = null;
const accounts = await provider.value.send('eth_requestAccounts', []);
const signerInstance = await provider.value.getSigner();
const address = await signerInstance.getAddress();
signer.value = signerInstance;
userAddress.value = address;
} catch (err: any) {
error.value = err.message || 'Failed to connect wallet';
} finally {
loading.value = false;
}
};
// Create Smart Account
const createSmartAccount = async () => {
if (!smartAccountsKit.value || !userAddress.value) return;
try {
loading.value = true;
error.value = null;
const account = await smartAccountsKit.value.createAccount({
owner: userAddress.value,
});
smartAccount.value = {
address: account.address,
owner: userAddress.value,
};
} catch (err: any) {
error.value = err.message || 'Failed to create smart account';
} finally {
loading.value = false;
}
};
// Request Delegation
const requestDelegation = async () => {
if (!smartAccountsKit.value || !smartAccount.value) return;
try {
loading.value = true;
error.value = null;
const delegation = await smartAccountsKit.value.requestDelegation({
target: '0x...', // Replace with actual target
permissions: ['execute_transactions', 'batch_operations'],
expiry: Date.now() + 86400000, // 24 hours
});
if (delegation.approved) {
// Refresh delegations
await loadDelegations();
}
} catch (err: any) {
error.value = err.message || 'Failed to request delegation';
} finally {
loading.value = false;
}
};
// Load Delegations
const loadDelegations = async () => {
if (!smartAccountsKit.value || !smartAccount.value) return;
try {
// This would typically fetch from your backend or contract
// For demo purposes, we'll use a placeholder
const status = await smartAccountsKit.value.getDelegationStatus({
target: '0x...', // Replace with actual target
account: smartAccount.value.address,
});
// Update delegations state
// Implementation depends on your delegation storage
} catch (err: any) {
console.error('Failed to load delegations:', err);
}
};
</script>
<style scoped>
.app {
text-align: center;
min-height: 100vh;
display: flex;
flex-direction: column;
}
.app-header {
background-color: #282c34;
padding: 20px;
color: white;
}
.app-main {
flex: 1;
padding: 20px;
max-width: 800px;
margin: 0 auto;
width: 100%;
}
.section {
background: #f5f5f5;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
text-align: left;
}
.section h2 {
margin-top: 0;
color: #333;
}
.section p {
margin: 10px 0;
word-break: break-all;
}
button {
background-color: #4CAF50;
border: none;
color: white;
padding: 12px 24px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 10px 0;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s;
}
button:hover:not(:disabled) {
background-color: #45a049;
}
button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.error {
background-color: #f44336;
color: white;
padding: 15px;
border-radius: 4px;
margin: 20px 0;
}
.app-footer {
background-color: #282c34;
padding: 20px;
color: white;
}
.app-footer a {
color: #61dafb;
}
ul {
list-style-type: none;
padding: 0;
}
li {
background: white;
padding: 15px;
margin: 10px 0;
border-radius: 4px;
text-align: left;
}
</style>

View File

@@ -0,0 +1,59 @@
# MetaMask ChainID 138 Vue.js Example
Complete Vue.js 3 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
- ✅ TypeScript support
## 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.vue` - Main component with wallet connection logic
- Uses Composition API with `<script setup>`
- 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)

View File

@@ -0,0 +1,21 @@
{
"name": "metamask-chain138-vue-example",
"version": "1.0.0",
"description": "Vue.js example for MetaMask integration with ChainID 138",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.3.4",
"ethers": "^6.9.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.4.0",
"typescript": "^5.2.0",
"vite": "^5.0.0",
"vue-tsc": "^1.8.22"
}
}

View File

@@ -0,0 +1,277 @@
<template>
<div class="app">
<div class="container">
<h1>MetaMask ChainID 138 Vue Example</h1>
<div v-if="!isMetaMaskInstalled" class="error">
MetaMask is not installed. Please install MetaMask extension.
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-if="!isConnected">
<button @click="connectWallet" class="btn btn-primary">
Connect Wallet
</button>
</div>
<div v-else class="wallet-info">
<h2>Wallet Connected</h2>
<p><strong>Account:</strong> {{ account }}</p>
<p><strong>Chain ID:</strong> {{ chainId }}</p>
<p><strong>Balance:</strong> {{ balance }} ETH</p>
<div v-if="!isOnChain138" class="network-section">
<h3>Network Setup</h3>
<button @click="addChain138" class="btn btn-secondary">
Add ChainID 138
</button>
<button @click="switchToChain138" class="btn btn-secondary">
Switch to ChainID 138
</button>
</div>
<div v-if="isOnChain138" class="tokens-section">
<h3>Add Tokens</h3>
<button @click="addToken('cUSDT', tokens.cUSDT, 6)" class="btn btn-token">
Add cUSDT
</button>
<button @click="addToken('cUSDC', tokens.cUSDC, 6)" class="btn btn-token">
Add cUSDC
</button>
<button @click="addToken('WETH', tokens.WETH, 18)" class="btn btn-token">
Add WETH
</button>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ethers } from 'ethers';
// ChainID 138 Network Configuration
const CHAIN_138_CONFIG = {
chainId: '0x8a',
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',
};
const account = ref<string | null>(null);
const chainId = ref<string | null>(null);
const balance = ref<string>('0');
const isConnected = ref(false);
const error = ref<string | null>(null);
const isMetaMaskInstalled = typeof window.ethereum !== 'undefined';
const isOnChain138 = computed(() => chainId.value === '138');
const connectWallet = async () => {
if (!isMetaMaskInstalled) {
error.value = '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) {
account.value = accounts[0];
isConnected.value = true;
error.value = null;
const network = await provider.getNetwork();
chainId.value = network.chainId.toString();
const bal = await provider.getBalance(accounts[0]);
balance.value = ethers.formatEther(bal);
}
} catch (err: any) {
error.value = err.message || 'Failed to connect wallet';
}
};
const addChain138 = async () => {
if (!isMetaMaskInstalled) {
error.value = 'MetaMask is not installed';
return;
}
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [CHAIN_138_CONFIG],
});
error.value = null;
} catch (err: any) {
error.value = err.message || 'Failed to add network';
}
};
const switchToChain138 = async () => {
if (!isMetaMaskInstalled) {
error.value = 'MetaMask is not installed';
return;
}
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: CHAIN_138_CONFIG.chainId }],
});
error.value = null;
const provider = new ethers.BrowserProvider(window.ethereum);
const network = await provider.getNetwork();
chainId.value = network.chainId.toString();
} catch (err: any) {
if ((err as any).code === 4902) {
await addChain138();
} else {
error.value = (err as any).message || 'Failed to switch network';
}
}
};
const addToken = async (symbol: string, address: string, decimals: number) => {
if (!isMetaMaskInstalled) {
error.value = 'MetaMask is not installed';
return;
}
try {
await window.ethereum.request({
method: 'wallet_watchAsset',
params: {
type: 'ERC20',
options: {
address: address,
symbol: symbol,
decimals: decimals,
},
},
});
error.value = null;
} catch (err: any) {
error.value = err.message || 'Failed to add token';
}
};
</script>
<style scoped>
.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;
}
</style>