Spaces:
Running
Running
| // CryptoSphere - Cryptocurrency Tracker JavaScript | |
| class CryptosphereTracker { | |
| constructor() { | |
| this.web3 = null; | |
| this.provider = null; | |
| this.connectedAccount = null; | |
| this.cryptoData = []; | |
| this.portfolioData = []; | |
| this.currentPage = 1; | |
| this.itemsPerPage = 12; | |
| this.init(); | |
| } | |
| async init() { | |
| await this.registerServiceWorker(); | |
| this.setupEventListeners(); | |
| await this.loadCryptoData(); | |
| this.updateMarketStats(); | |
| this.setupRealTimeUpdates(); | |
| } | |
| // Web3 Wallet Integration | |
| async connectWallet() { | |
| try { | |
| if (window.ethereum) { | |
| this.provider = window.ethereum; | |
| this.web3 = new Web3(this.provider); | |
| // Request account access | |
| const accounts = await window.ethereum.request({ | |
| method: 'eth_requestAccounts' | |
| }); | |
| this.connectedAccount = accounts[0]; | |
| this.updateWalletStatus(); | |
| await this.loadPortfolioData(); | |
| // Listen for account changes | |
| window.ethereum.on('accountsChanged', (accounts) => { | |
| this.connectedAccount = accounts[0] || null; | |
| this.updateWalletStatus(); | |
| }); | |
| } else { | |
| throw new Error('No Ethereum wallet detected. Please install MetaMask.'); | |
| } | |
| } catch (error) { | |
| this.showNotification(error.message, 'error'); | |
| } | |
| } | |
| // Real-time Crypto Data from CoinGecko API | |
| async fetchCryptoData(page = 1) { | |
| const vsCurrency = 'usd'; | |
| const perPage = this.itemsPerPage; | |
| try { | |
| const response = await fetch( | |
| `https://api.coingecko.com/api/v3/coins/markets?vs_currency=${vsCurrency}&order=market_cap_desc&per_page=${perPage}&page=${page}&sparkline=true&price_change_percentage=24h` | |
| ); | |
| if (!response.ok) { | |
| throw new Error('Failed to fetch crypto data'); | |
| } | |
| const data = await response.json(); | |
| return data; | |
| } catch (error) { | |
| console.error('Error fetching crypto data:', error); | |
| return this.getMockCryptoData(page); | |
| } | |
| } | |
| // Mock data for demonstration | |
| getMockCryptoData(page) { | |
| const mockData = []; | |
| const startIndex = (page - 1) * this.itemsPerPage; | |
| const topCryptos = [ | |
| { id: 'bitcoin', symbol: 'btc', name: 'Bitcoin' }, | |
| { id: 'ethereum', symbol: 'eth', name: 'Ethereum' }, | |
| { id: 'binancecoin', symbol: 'bnb', name: 'BNB' }, | |
| { id: 'ripple', symbol: 'xrp', name: 'XRP' }, | |
| { id: 'cardano', symbol: 'ada', name: 'Cardano' }, | |
| { id: 'solana', symbol: 'sol', name: 'Solana' }, | |
| { id: 'polkadot', symbol: 'dot', name: 'Polkadot' } | |
| ]; | |
| for (let i = 0; i < this.itemsPerPage; i++) { | |
| const crypto = topCryptos[i % topCryptos.length]; | |
| const price = Math.random() * 50000 + 1000; | |
| const change24h = (Math.random() - 0.5) * 20; | |
| mockData.push({ | |
| id: crypto.id, | |
| symbol: crypto.symbol, | |
| name: crypto.name, | |
| current_price: price, | |
| price_change_percentage_24h: change24h, | |
| market_cap: price * (Math.random() * 1000000), | |
| total_volume: price * (Math.random() * 100000), | |
| price_change_24h: change24h * price / 100, | |
| image: `http://static.photos/finance/200x200/${startIndex + i + 1}`, | |
| sparkline_in_7d: { price: Array(7).fill(0).map(() => price + (Math.random() - 0.5) * 1000 | |
| }); | |
| } | |
| return mockData; | |
| } | |
| // Load and display crypto data | |
| async loadCryptoData() { | |
| const data = await this.fetchCryptoData(this.currentPage); | |
| this.cryptoData = [...this.cryptoData, ...data]; | |
| this.renderCryptoData(data); | |
| } | |
| renderCryptoData(data) { | |
| const grid = document.getElementById('crypto-grid'); | |
| data.forEach(crypto => { | |
| const cryptoCard = document.createElement('crypto-card'); | |
| cryptoCard.setAttribute('data-crypto', JSON.stringify(crypto)); | |
| grid.appendChild(cryptoCard); | |
| }); | |
| } | |
| // Update global market statistics | |
| updateMarketStats() { | |
| // Simulate real-time market data updates | |
| setInterval(() => { | |
| const marketCap = (2.1 + Math.random() * 0.1).toFixed(1); | |
| const volume = (78.4 + Math.random() * 5).toFixed(1); | |
| const dominance = (52.3 + (Math.random() - 0.5)).toFixed(1); | |
| document.getElementById('global-market-cap').textContent = `$${marketCap}T`; | |
| document.getElementById('total-volume').textContent = `$${volume}B`; | |
| document.getElementById('btc-dominance').textContent = `${dominance}%`; | |
| }, 3000); | |
| } | |
| // Real-time price updates via WebSocket | |
| setupRealTimeUpdates() { | |
| // This would connect to a WebSocket service for real-time updates | |
| // For demo purposes, we'll simulate with intervals | |
| setInterval(() => { | |
| this.updateLivePrices(); | |
| }, 5000); | |
| } | |
| updateLivePrices() { | |
| const cards = document.querySelectorAll('crypto-card'); | |
| cards.forEach(card => { | |
| const cryptoData = JSON.parse(card.getAttribute('data-crypto'))); | |
| // Simulate price changes | |
| const change = (Math.random() - 0.5) * 2; | |
| const newPrice = cryptoData.current_price * (1 + change / 100); | |
| cryptoData.current_price = newPrice; | |
| cryptoData.price_change_percentage_24h = change; | |
| // Update the card | |
| card.setAttribute('data-crypto', JSON.stringify(cryptoData)); | |
| }); | |
| } | |
| // Load portfolio data from connected wallet | |
| async loadPortfolioData() { | |
| if (!this.connectedAccount) return; | |
| try { | |
| // In a real implementation, you would query the blockchain | |
| // for the wallet's token holdings and their current values | |
| // Mock portfolio data | |
| this.portfolioData = [ | |
| { | |
| id: 'bitcoin', | |
| symbol: 'btc', | |
| name: 'Bitcoin', | |
| amount: Math.random() * 0.5, | |
| value: Math.random() * 15000, | |
| change24h: (Math.random() - 0.5) * 10 | |
| }, | |
| { | |
| id: 'ethereum', | |
| symbol: 'eth', | |
| name: 'Ethereum', | |
| amount: Math.random() * 10, | |
| value: Math.random() * 20000 | |
| } | |
| ]; | |
| this.renderPortfolio(); | |
| } catch (error) { | |
| console.error('Error loading portfolio:', error); | |
| } | |
| } | |
| renderPortfolio() { | |
| const container = document.getElementById('portfolio-container'); | |
| if (this.portfolioData.length === 0) { | |
| container.innerHTML = ` | |
| <div class="text-center text-gray-400"> | |
| <i data-feather="alert-circle" class="w-16 h-16 mx-auto mb-4"></i> | |
| <p>No crypto assets detected in your wallet</p> | |
| `; | |
| } else { | |
| let totalValue = 0; | |
| let totalChange = 0; | |
| const portfolioHTML = this.portfolioData.map(asset => { | |
| totalValue += asset.value; | |
| totalChange += asset.change24h || 0; | |
| return ` | |
| <div class="asset ${asset.change24h >= 0 ? 'portfolio-up' : 'portfolio-down'} p-4 mb-4 rounded-xl"> | |
| <div class="flex justify-between items-center"> | |
| <div class="flex items-center gap-3"> | |
| <img src="${asset.image || 'http://static.photos/finance/200x200'}" class="w-10 h-10 rounded-full"> | |
| <div> | |
| <div class="font-semibold">${asset.name} (${asset.symbol.toUpperCase()})</div> | |
| <div class="text-right"> | |
| <div class="font-bold">$${asset.value.toFixed(2)}</div> | |
| <div class="text-sm ${asset.change24h >= 0 ? 'price-up' : 'price-down'}"> | |
| ${asset.change24h >= 0 ? '+' : ''}${asset.change24h?.toFixed(2) || '0.00'}%</div> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| }).join(''); | |
| container.innerHTML = ` | |
| <h3 class="text-2xl font-bold text-white mb-6">Your Portfolio</h3> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| ${portfolioHTML} | |
| </div> | |
| <div class="mt-6 pt-6 border-t border-gray-600"> | |
| <div class="flex justify-between text-xl"> | |
| <span>Total Value:</span> | |
| <span class="font-bold text-primary">$${totalValue.toFixed(2)}</div> | |
| `; | |
| } | |
| } | |
| feather.replace(); | |
| } | |
| // Event listeners setup | |
| setupEventListeners() { | |
| // Connect wallet button | |
| const connectBtn = document.getElementById('connect-wallet'); | |
| if (connectBtn) { | |
| connectBtn.addEventListener('click', () => this.connectWallet()); | |
| } | |
| // Load more cryptocurrencies | |
| const loadMoreBtn = document.getElementById('load-more'); | |
| if (loadMoreBtn) { | |
| loadMoreBtn.addEventListener('click', () => { | |
| this.currentPage++; | |
| this.loadCryptoData(); | |
| }); | |
| } | |
| } | |
| // Notification system | |
| showNotification(message, type = 'info') { | |
| const notification = document.createElement('div'); | |
| notification.className = `crypto-card p-4 mb-4 ${type === 'error' ? 'border-l-4 border-red-500' : 'border-l-4 border-primary'} crypto-notification`; | |
| notification.innerHTML = ` | |
| <div class="flex items-center gap-3"> | |
| <i data-feather="${type === 'error' ? 'alert-triangle' : 'check-circle'}`; | |
| notification.textContent = message; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.remove(); | |
| }, 5000); | |
| feather.replace(); | |
| } | |
| // Update wallet connection status | |
| updateWalletStatus() { | |
| const connectBtn = document.getElementById('connect-wallet'); | |
| if (this.connectedAccount) { | |
| const shortAddress = `${this.connectedAccount.slice(0, 6)}...${this.connectedAccount.slice(-4)}`; | |
| if (connectBtn) { | |
| if (this.connectedAccount) { | |
| connectBtn.innerHTML = ` | |
| <i data-feather="user-check" class="inline mr-2"></i> | |
| ${this.connectedAccount.slice(0, 6)}...${this.connectedAccount.slice(-4)}`; | |
| } else { | |
| connectBtn.innerHTML = ` | |
| <i data-feather="wallet" class="inline mr-2"></i> | |
| ${this.connectedAccount.slice(0, 6)}...${this.connectedAccount.slice(-4)}`; | |
| } | |
| } | |
| // PWA Service Worker Registration | |
| async registerServiceWorker() { | |
| if ('serviceWorker' in navigator) { | |
| try { | |
| await navigator.serviceWorker.register('/crypto-sw.js'); | |
| console.log('Crypto Service Worker registered successfully'); | |
| } catch (error) { | |
| console.error('Crypto Service Worker registration failed:', error); | |
| } | |
| } | |
| } | |
| } | |
| // Initialize the crypto tracker when DOM is loaded | |
| document.addEventListener('DOMContentLoaded', () => { | |
| new CryptosphereTracker(); | |
| }); | |
| // Export for use in other modules | |
| window.CryptosphereTracker = CryptosphereTracker; |