- Organized 252 files across project - Root directory: 187 → 2 files (98.9% reduction) - Moved configuration guides to docs/04-configuration/ - Moved troubleshooting guides to docs/09-troubleshooting/ - Moved quick start guides to docs/01-getting-started/ - Moved reports to reports/ directory - Archived temporary files - Generated comprehensive reports and documentation - Created maintenance scripts and guides All files organized according to established standards.
555 lines
14 KiB
Markdown
555 lines
14 KiB
Markdown
# Blockscout MetaMask Integration - Complete Recommendations
|
|
|
|
**Date**: $(date)
|
|
**Status**: ✅ Fix Deployed
|
|
**VMID**: 5000
|
|
**Frontend**: `/var/www/html/index.html`
|
|
|
|
---
|
|
|
|
## ✅ Completed Fixes
|
|
|
|
### 1. Ethers Library Loading
|
|
- ✅ Added fallback CDN (unpkg.com)
|
|
- ✅ Added automatic fallback detection
|
|
- ✅ Added ethers availability checks
|
|
- ✅ Improved error handling
|
|
|
|
### 2. Deployment
|
|
- ✅ Fixed frontend deployed to `/var/www/html/index.html`
|
|
- ✅ Nginx reloaded
|
|
- ✅ Changes are live
|
|
|
|
---
|
|
|
|
## 🔧 Additional Recommendations
|
|
|
|
### 1. **CDN Optimization & Caching**
|
|
|
|
#### Current Implementation
|
|
```html
|
|
<script src="https://cdn.ethers.io/lib/ethers-5.7.2.umd.min.js"
|
|
onerror="this.onerror=null; this.src='https://unpkg.com/ethers@5.7.2/dist/ethers.umd.min.js';"></script>
|
|
```
|
|
|
|
#### Recommended Improvements
|
|
|
|
**A. Add Integrity Checks (SRI)**
|
|
```html
|
|
<script src="https://cdn.ethers.io/lib/ethers-5.7.2.umd.min.js"
|
|
integrity="sha384-..."
|
|
crossorigin="anonymous"
|
|
onerror="this.onerror=null; this.src='https://unpkg.com/ethers@5.7.2/dist/ethers.umd.min.js';"></script>
|
|
```
|
|
|
|
**B. Preload Critical Resources**
|
|
```html
|
|
<link rel="preload" href="https://cdn.ethers.io/lib/ethers-5.7.2.umd.min.js" as="script">
|
|
<link rel="dns-prefetch" href="https://cdn.ethers.io">
|
|
<link rel="dns-prefetch" href="https://unpkg.com">
|
|
```
|
|
|
|
**C. Local Fallback (Best Practice)**
|
|
Host ethers.js locally as ultimate fallback:
|
|
```bash
|
|
# Download ethers.js locally
|
|
cd /var/www/html
|
|
wget https://unpkg.com/ethers@5.7.2/dist/ethers.umd.min.js -O js/ethers.umd.min.js
|
|
|
|
# Update HTML to use local fallback
|
|
<script src="https://cdn.ethers.io/lib/ethers-5.7.2.umd.min.js"
|
|
onerror="this.onerror=null; this.src='https://unpkg.com/ethers@5.7.2/dist/ethers.umd.min.js';"
|
|
onerror="this.onerror=null; this.src='/js/ethers.umd.min.js';"></script>
|
|
```
|
|
|
|
---
|
|
|
|
### 2. **MetaMask Connection Enhancements**
|
|
|
|
#### A. Add Connection State Persistence
|
|
```javascript
|
|
// Save connection state to localStorage
|
|
function saveConnectionState(address, chainId) {
|
|
localStorage.setItem('metamask_connected', 'true');
|
|
localStorage.setItem('metamask_address', address);
|
|
localStorage.setItem('metamask_chainId', chainId);
|
|
}
|
|
|
|
// Restore connection on page load
|
|
function restoreConnection() {
|
|
if (localStorage.getItem('metamask_connected') === 'true') {
|
|
const savedAddress = localStorage.getItem('metamask_address');
|
|
if (savedAddress && typeof window.ethereum !== 'undefined') {
|
|
connectMetaMask();
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### B. Add Network Detection
|
|
```javascript
|
|
async function detectNetwork() {
|
|
if (typeof window.ethereum === 'undefined') return null;
|
|
|
|
try {
|
|
const chainId = await window.ethereum.request({ method: 'eth_chainId' });
|
|
const chainIdDecimal = parseInt(chainId, 16);
|
|
|
|
if (chainIdDecimal !== 138) {
|
|
return {
|
|
current: chainIdDecimal,
|
|
required: 138,
|
|
needsSwitch: true
|
|
};
|
|
}
|
|
return { current: chainIdDecimal, required: 138, needsSwitch: false };
|
|
} catch (error) {
|
|
console.error('Network detection failed:', error);
|
|
return null;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### C. Add Connection Retry Logic
|
|
```javascript
|
|
async function connectMetaMaskWithRetry(maxRetries = 3) {
|
|
for (let i = 0; i < maxRetries; i++) {
|
|
try {
|
|
await connectMetaMask();
|
|
return true;
|
|
} catch (error) {
|
|
if (i === maxRetries - 1) throw error;
|
|
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 3. **Error Handling & User Feedback**
|
|
|
|
#### A. Enhanced Error Messages
|
|
```javascript
|
|
const ERROR_MESSAGES = {
|
|
NO_METAMASK: 'MetaMask is not installed. Please install MetaMask extension.',
|
|
NO_ETHERS: 'Ethers library failed to load. Please refresh the page.',
|
|
WRONG_NETWORK: 'Please switch to ChainID 138 (SMOM-DBIS-138) in MetaMask.',
|
|
USER_REJECTED: 'Connection request was rejected. Please try again.',
|
|
NETWORK_ERROR: 'Network error. Please check your connection and try again.'
|
|
};
|
|
|
|
function getErrorMessage(error) {
|
|
if (error.code === 4001) return ERROR_MESSAGES.USER_REJECTED;
|
|
if (error.code === 4902) return ERROR_MESSAGES.WRONG_NETWORK;
|
|
if (error.message.includes('ethers')) return ERROR_MESSAGES.NO_ETHERS;
|
|
return error.message || ERROR_MESSAGES.NETWORK_ERROR;
|
|
}
|
|
```
|
|
|
|
#### B. Toast Notifications
|
|
Add a toast notification system for better UX:
|
|
```javascript
|
|
function showToast(message, type = 'info', duration = 3000) {
|
|
const toast = document.createElement('div');
|
|
toast.className = `toast toast-${type}`;
|
|
toast.textContent = message;
|
|
document.body.appendChild(toast);
|
|
|
|
setTimeout(() => {
|
|
toast.classList.add('show');
|
|
}, 10);
|
|
|
|
setTimeout(() => {
|
|
toast.classList.remove('show');
|
|
setTimeout(() => toast.remove(), 300);
|
|
}, duration);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 4. **Performance Optimizations**
|
|
|
|
#### A. Lazy Load MetaMask Functions
|
|
```javascript
|
|
// Only load MetaMask-related code when needed
|
|
let metamaskLoaded = false;
|
|
|
|
async function loadMetaMaskSupport() {
|
|
if (metamaskLoaded) return;
|
|
|
|
// Dynamically import MetaMask functions
|
|
const module = await import('./metamask-support.js');
|
|
metamaskLoaded = true;
|
|
return module;
|
|
}
|
|
|
|
// Call when user clicks "Connect MetaMask"
|
|
document.getElementById('connectMetaMask').addEventListener('click', async () => {
|
|
await loadMetaMaskSupport();
|
|
connectMetaMask();
|
|
});
|
|
```
|
|
|
|
#### B. Debounce Balance Updates
|
|
```javascript
|
|
function debounce(func, wait) {
|
|
let timeout;
|
|
return function executedFunction(...args) {
|
|
const later = () => {
|
|
clearTimeout(timeout);
|
|
func(...args);
|
|
};
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(later, wait);
|
|
};
|
|
}
|
|
|
|
const debouncedRefresh = debounce(refreshWETHBalances, 1000);
|
|
```
|
|
|
|
#### C. Cache Contract Instances
|
|
```javascript
|
|
let contractCache = {};
|
|
|
|
function getContract(address, abi, provider) {
|
|
const key = `${address}-${provider.connection?.url || 'default'}`;
|
|
if (!contractCache[key]) {
|
|
contractCache[key] = new ethers.Contract(address, abi, provider);
|
|
}
|
|
return contractCache[key];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 5. **Security Enhancements**
|
|
|
|
#### A. Validate Contract Addresses
|
|
```javascript
|
|
function isValidAddress(address) {
|
|
return /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
}
|
|
|
|
function validateContractAddress(address, expectedAddress) {
|
|
if (!isValidAddress(address)) {
|
|
throw new Error('Invalid contract address format');
|
|
}
|
|
if (address.toLowerCase() !== expectedAddress.toLowerCase()) {
|
|
throw new Error('Contract address mismatch');
|
|
}
|
|
}
|
|
```
|
|
|
|
#### B. Add Transaction Confirmation
|
|
```javascript
|
|
async function confirmTransaction(txHash, description) {
|
|
const confirmed = confirm(
|
|
`${description}\n\n` +
|
|
`Transaction: ${txHash}\n\n` +
|
|
`View on explorer: https://explorer.d-bis.org/tx/${txHash}\n\n` +
|
|
`Continue?`
|
|
);
|
|
return confirmed;
|
|
}
|
|
```
|
|
|
|
#### C. Rate Limiting
|
|
```javascript
|
|
const rateLimiter = {
|
|
requests: [],
|
|
maxRequests: 10,
|
|
window: 60000, // 1 minute
|
|
|
|
canMakeRequest() {
|
|
const now = Date.now();
|
|
this.requests = this.requests.filter(time => now - time < this.window);
|
|
|
|
if (this.requests.length >= this.maxRequests) {
|
|
return false;
|
|
}
|
|
|
|
this.requests.push(now);
|
|
return true;
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
### 6. **Monitoring & Analytics**
|
|
|
|
#### A. Error Tracking
|
|
```javascript
|
|
function trackError(error, context) {
|
|
// Send to analytics service
|
|
if (typeof gtag !== 'undefined') {
|
|
gtag('event', 'exception', {
|
|
description: error.message,
|
|
fatal: false,
|
|
context: context
|
|
});
|
|
}
|
|
|
|
// Log to console in development
|
|
if (window.location.hostname === 'localhost') {
|
|
console.error('Error:', error, 'Context:', context);
|
|
}
|
|
}
|
|
```
|
|
|
|
#### B. Connection Metrics
|
|
```javascript
|
|
const connectionMetrics = {
|
|
startTime: null,
|
|
attempts: 0,
|
|
successes: 0,
|
|
failures: 0,
|
|
|
|
start() {
|
|
this.startTime = Date.now();
|
|
this.attempts++;
|
|
},
|
|
|
|
success() {
|
|
this.successes++;
|
|
const duration = Date.now() - this.startTime;
|
|
console.log(`Connection successful in ${duration}ms`);
|
|
},
|
|
|
|
failure(error) {
|
|
this.failures++;
|
|
console.error('Connection failed:', error);
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
### 7. **Accessibility Improvements**
|
|
|
|
#### A. ARIA Labels
|
|
```html
|
|
<button
|
|
id="connectMetaMask"
|
|
onclick="connectMetaMask()"
|
|
aria-label="Connect MetaMask wallet"
|
|
aria-describedby="metamask-help">
|
|
Connect MetaMask
|
|
</button>
|
|
<div id="metamask-help" class="sr-only">
|
|
Connect your MetaMask wallet to interact with WETH utilities
|
|
</div>
|
|
```
|
|
|
|
#### B. Keyboard Navigation
|
|
```javascript
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter' && e.target.id === 'connectMetaMask') {
|
|
connectMetaMask();
|
|
}
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
### 8. **Testing Recommendations**
|
|
|
|
#### A. Unit Tests
|
|
```javascript
|
|
// test/metamask-connection.test.js
|
|
describe('MetaMask Connection', () => {
|
|
test('should detect MetaMask availability', () => {
|
|
window.ethereum = { isMetaMask: true };
|
|
expect(checkMetaMaskConnection()).toBe(true);
|
|
});
|
|
|
|
test('should handle missing ethers library', () => {
|
|
delete window.ethers;
|
|
expect(() => ensureEthers()).toThrow();
|
|
});
|
|
});
|
|
```
|
|
|
|
#### B. Integration Tests
|
|
- Test with MetaMask extension installed
|
|
- Test with MetaMask not installed
|
|
- Test network switching
|
|
- Test transaction signing
|
|
- Test error scenarios
|
|
|
|
#### C. E2E Tests
|
|
```javascript
|
|
// Use Playwright or Cypress
|
|
test('connect MetaMask and wrap WETH', async ({ page }) => {
|
|
await page.goto('https://explorer.d-bis.org');
|
|
await page.click('#connectMetaMask');
|
|
// ... test flow
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
### 9. **Documentation Updates**
|
|
|
|
#### A. User Guide
|
|
Create `docs/METAMASK_USER_GUIDE.md`:
|
|
- How to install MetaMask
|
|
- How to add ChainID 138
|
|
- How to connect wallet
|
|
- How to use WETH utilities
|
|
- Troubleshooting common issues
|
|
|
|
#### B. Developer Guide
|
|
Create `docs/METAMASK_DEVELOPER_GUIDE.md`:
|
|
- Architecture overview
|
|
- API reference
|
|
- Extension points
|
|
- Testing guide
|
|
- Deployment guide
|
|
|
|
---
|
|
|
|
### 10. **Infrastructure Improvements**
|
|
|
|
#### A. Content Security Policy (CSP)
|
|
```nginx
|
|
# Add to nginx config
|
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.ethers.io https://unpkg.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com;";
|
|
```
|
|
|
|
#### B. Service Worker for Offline Support
|
|
```javascript
|
|
// sw.js
|
|
self.addEventListener('fetch', (event) => {
|
|
if (event.request.url.includes('ethers.umd.min.js')) {
|
|
event.respondWith(
|
|
caches.match(event.request).then((response) => {
|
|
return response || fetch(event.request);
|
|
})
|
|
);
|
|
}
|
|
});
|
|
```
|
|
|
|
#### C. Health Check Endpoint
|
|
```javascript
|
|
// Add to API
|
|
app.get('/health/metamask', (req, res) => {
|
|
res.json({
|
|
ethers_loaded: typeof ethers !== 'undefined',
|
|
metamask_available: typeof window.ethereum !== 'undefined',
|
|
network_id: 138,
|
|
status: 'ok'
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
### 11. **Backup & Recovery**
|
|
|
|
#### A. Version Control
|
|
```bash
|
|
# Create backup before updates
|
|
cp /var/www/html/index.html /var/www/html/index.html.backup.$(date +%Y%m%d)
|
|
|
|
# Git version control
|
|
cd /var/www/html
|
|
git init
|
|
git add index.html
|
|
git commit -m "Update: Add ethers fallback CDN"
|
|
```
|
|
|
|
#### B. Rollback Script
|
|
```bash
|
|
#!/bin/bash
|
|
# rollback-frontend.sh
|
|
BACKUP_FILE="/var/www/html/index.html.backup.$(date +%Y%m%d)"
|
|
if [ -f "$BACKUP_FILE" ]; then
|
|
cp "$BACKUP_FILE" /var/www/html/index.html
|
|
systemctl reload nginx
|
|
echo "Rolled back to: $BACKUP_FILE"
|
|
fi
|
|
```
|
|
|
|
---
|
|
|
|
### 12. **Monitoring & Alerts**
|
|
|
|
#### A. Error Monitoring
|
|
- Set up Sentry or similar for error tracking
|
|
- Monitor ethers.js loading failures
|
|
- Track MetaMask connection failures
|
|
- Alert on high error rates
|
|
|
|
#### B. Performance Monitoring
|
|
- Track page load times
|
|
- Monitor CDN response times
|
|
- Track MetaMask connection success rate
|
|
- Monitor transaction success rates
|
|
|
|
---
|
|
|
|
## 📋 Implementation Priority
|
|
|
|
### High Priority (Do Now)
|
|
1. ✅ Deploy ethers fallback fix (DONE)
|
|
2. Add local ethers.js fallback
|
|
3. Add connection state persistence
|
|
4. Improve error messages
|
|
|
|
### Medium Priority (Next Sprint)
|
|
5. Add network detection
|
|
6. Add toast notifications
|
|
7. Add SRI checks
|
|
8. Add CSP headers
|
|
|
|
### Low Priority (Future)
|
|
9. Add service worker
|
|
10. Add comprehensive testing
|
|
11. Add analytics
|
|
12. Add accessibility improvements
|
|
|
|
---
|
|
|
|
## 🔍 Verification Checklist
|
|
|
|
- [x] Ethers library loads from primary CDN
|
|
- [x] Fallback CDN works if primary fails
|
|
- [x] MetaMask connection works
|
|
- [x] Error messages are clear
|
|
- [ ] Local fallback available
|
|
- [ ] Connection state persists
|
|
- [ ] Network switching works
|
|
- [ ] All WETH functions work
|
|
- [ ] Mobile responsive
|
|
- [ ] Accessibility compliant
|
|
|
|
---
|
|
|
|
## 📚 Additional Resources
|
|
|
|
- [Ethers.js Documentation](https://docs.ethers.io/)
|
|
- [MetaMask Documentation](https://docs.metamask.io/)
|
|
- [Web3 Best Practices](https://ethereum.org/en/developers/docs/web2-vs-web3/)
|
|
- [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
|
|
|
|
---
|
|
|
|
## 🎯 Success Metrics
|
|
|
|
- **Connection Success Rate**: > 95%
|
|
- **Ethers Load Time**: < 2 seconds
|
|
- **Error Rate**: < 1%
|
|
- **User Satisfaction**: Positive feedback
|
|
- **Transaction Success Rate**: > 98%
|
|
|
|
---
|
|
|
|
**Status**: ✅ Core fix deployed
|
|
**Next Steps**: Implement high-priority recommendations
|
|
**Last Updated**: $(date)
|
|
|