chore: sync submodule state (parent ref update)
Made-with: Cursor
This commit is contained in:
176
examples/README.md
Normal file
176
examples/README.md
Normal 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
143
examples/provider-e2e.html
Normal 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>
|
||||
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;
|
||||
273
examples/smart-accounts-example.html
Normal file
273
examples/smart-accounts-example.html
Normal 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>
|
||||
187
examples/smart-accounts-example.ts
Normal file
187
examples/smart-accounts-example.ts
Normal 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,
|
||||
};
|
||||
52
examples/smart-accounts-react-example/README.md
Normal file
52
examples/smart-accounts-react-example/README.md
Normal 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.
|
||||
36
examples/smart-accounts-react-example/package.json
Normal file
36
examples/smart-accounts-react-example/package.json
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
93
examples/smart-accounts-react-example/src/App.css
Normal file
93
examples/smart-accounts-react-example/src/App.css
Normal 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;
|
||||
}
|
||||
220
examples/smart-accounts-react-example/src/App.tsx
Normal file
220
examples/smart-accounts-react-example/src/App.tsx
Normal 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;
|
||||
52
examples/smart-accounts-vue-example/README.md
Normal file
52
examples/smart-accounts-vue-example/README.md
Normal 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.
|
||||
21
examples/smart-accounts-vue-example/package.json
Normal file
21
examples/smart-accounts-vue-example/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
297
examples/smart-accounts-vue-example/src/App.vue
Normal file
297
examples/smart-accounts-vue-example/src/App.vue
Normal 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>
|
||||
59
examples/vue-example/README.md
Normal file
59
examples/vue-example/README.md
Normal 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)
|
||||
21
examples/vue-example/package.json
Normal file
21
examples/vue-example/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
277
examples/vue-example/src/App.vue
Normal file
277
examples/vue-example/src/App.vue
Normal 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>
|
||||
Reference in New Issue
Block a user