Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>MyDoge Wallet Explorer</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .nft-card { | |
| transition: all 0.3s ease; | |
| } | |
| .nft-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); | |
| } | |
| .doge-pattern { | |
| background-color: #f8fafc; | |
| background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23c6bebe' fill-opacity='0.2'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); | |
| } | |
| .skeleton-loader { | |
| background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); | |
| background-size: 200% 100%; | |
| animation: shimmer 1.5s infinite; | |
| } | |
| @keyframes shimmer { | |
| 0% { background-position: 200% 0; } | |
| 100% { background-position: -200% 0; } | |
| } | |
| .spinner { | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| </style> | |
| </head> | |
| <body class="doge-pattern min-h-screen"> | |
| <div class="container mx-auto px-4 py-8 max-w-6xl"> | |
| <!-- Header --> | |
| <header class="text-center mb-12"> | |
| <div class="flex items-center justify-center mb-4"> | |
| <i class="fab fa-dochub text-amber-500 text-5xl mr-3"></i> | |
| <h1 class="text-4xl font-bold text-gray-800">MyDoge Wallet Explorer</h1> | |
| </div> | |
| <p class="text-gray-600 max-w-2xl mx-auto"> | |
| Explore Dogecoin wallet balances, transaction history, and associated NFTs in one place. | |
| </p> | |
| </header> | |
| <!-- Search Section --> | |
| <div class="bg-white rounded-xl shadow-lg p-6 mb-8"> | |
| <div class="flex flex-col md:flex-row gap-4"> | |
| <div class="flex-grow"> | |
| <label for="dogeAddress" class="block text-sm font-medium text-gray-700 mb-1"> | |
| Dogecoin Wallet Address | |
| </label> | |
| <div class="relative"> | |
| <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> | |
| <i class="fas fa-wallet text-gray-400"></i> | |
| </div> | |
| <input | |
| type="text" | |
| id="dogeAddress" | |
| placeholder="D8v4f5kS1CKMZCgD8qt7dZ9eWdNd..." | |
| class="pl-10 block w-full rounded-md border-gray-300 shadow-sm focus:border-amber-500 focus:ring focus:ring-amber-200 focus:ring-opacity-50 py-3 px-4 border" | |
| > | |
| </div> | |
| </div> | |
| <div class="flex items-end"> | |
| <button | |
| onclick="fetchWalletData()" | |
| class="bg-amber-500 hover:bg-amber-600 text-white font-bold py-3 px-6 rounded-md transition duration-300 flex items-center" | |
| id="search-btn" | |
| > | |
| <i class="fas fa-search mr-2"></i> Explore Wallet | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Wallet Info Section --> | |
| <div id="wallet-info" class="bg-white rounded-xl shadow-lg p-6"> | |
| <!-- Loading State --> | |
| <div id="loading-state" class="hidden"> | |
| <div class="flex flex-col items-center py-8"> | |
| <div class="w-16 h-16 rounded-full border-4 border-amber-500 border-t-transparent spinner mb-4"></div> | |
| <p class="text-gray-600">Fetching wallet data...</p> | |
| </div> | |
| </div> | |
| <!-- Error State --> | |
| <div id="error-state" class="hidden bg-red-50 border-l-4 border-red-500 p-4 mb-6"> | |
| <div class="flex"> | |
| <div class="flex-shrink-0"> | |
| <i class="fas fa-exclamation-circle text-red-500"></i> | |
| </div> | |
| <div class="ml-3"> | |
| <p id="error-message" class="text-sm text-red-700"></p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Balance Section --> | |
| <div id="balance-section" class="mb-8"> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center"> | |
| <i class="fas fa-coins text-amber-500 mr-2"></i> Wallet Balance | |
| </h2> | |
| <div class="bg-amber-50 rounded-lg p-6"> | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <p class="text-sm text-gray-600">Current Balance</p> | |
| <p id="balance" class="text-3xl font-bold text-gray-800">-- DOGE</p> | |
| </div> | |
| <div class="bg-amber-100 p-3 rounded-full"> | |
| <i class="fas fa-dog text-amber-600 text-2xl"></i> | |
| </div> | |
| </div> | |
| <div class="mt-4 grid grid-cols-1 md:grid-cols-3 gap-4"> | |
| <div class="bg-white p-4 rounded-lg shadow"> | |
| <p class="text-sm text-gray-600">Estimated USD Value</p> | |
| <p id="usd-value" class="text-lg font-semibold">--</p> | |
| </div> | |
| <div class="bg-white p-4 rounded-lg shadow"> | |
| <p class="text-sm text-gray-600">Transactions</p> | |
| <p id="tx-count" class="text-lg font-semibold">--</p> | |
| </div> | |
| <div class="bg-white p-4 rounded-lg shadow"> | |
| <p class="text-sm text-gray-600">First Transaction</p> | |
| <p id="first-tx" class="text-lg font-semibold">--</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Transaction History --> | |
| <div id="transaction-section" class="mb-8"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-gray-800 flex items-center"> | |
| <i class="fas fa-exchange-alt text-amber-500 mr-2"></i> Recent Transactions | |
| </h2> | |
| <button id="view-all-tx" class="text-sm text-amber-600 hover:text-amber-700 hidden"> | |
| View All <i class="fas fa-arrow-right ml-1"></i> | |
| </button> | |
| </div> | |
| <div class="overflow-x-auto"> | |
| <table class="min-w-full divide-y divide-gray-200"> | |
| <thead class="bg-gray-50"> | |
| <tr> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Tx ID</th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th> | |
| <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> | |
| </tr> | |
| </thead> | |
| <tbody id="transactions" class="bg-white divide-y divide-gray-200"> | |
| <tr> | |
| <td colspan="4" class="px-6 py-4 text-center text-gray-500">Enter a Dogecoin address to view transactions</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- NFTs Section --> | |
| <div id="nft-section"> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center"> | |
| <i class="fas fa-image text-amber-500 mr-2"></i> NFT Collection | |
| </h2> | |
| <div id="nft-placeholder" class="text-center py-8"> | |
| <i class="fas fa-images text-gray-300 text-5xl mb-4"></i> | |
| <p class="text-gray-500">No NFTs found for this wallet</p> | |
| </div> | |
| <div id="nfts" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 hidden"> | |
| <!-- NFT cards will be inserted here --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Footer --> | |
| <footer class="mt-12 text-center text-gray-500 text-sm"> | |
| <p>© 2023 MyDoge Wallet Explorer. Not affiliated with Dogecoin.</p> | |
| <p class="mt-1">Data provided by Blockchair API</p> | |
| </footer> | |
| </div> | |
| <script> | |
| // Configuration | |
| const config = { | |
| dogeApiUrl: 'https://api.blockchair.com/dogecoin', | |
| dogePriceApiUrl: 'https://api.coingecko.com/api/v3/simple/price?ids=dogecoin&vs_currencies=usd', | |
| nftApiUrl: 'https://deep-index.moralis.io/api/v2', | |
| transactionsLimit: 5 | |
| }; | |
| // Helper functions | |
| function formatDogeAmount(amount) { | |
| return (amount / 100000000).toFixed(2) + ' DOGE'; | |
| } | |
| function formatDate(timestamp) { | |
| const date = new Date(timestamp * 1000); | |
| return date.toLocaleDateString() + ' ' + date.toLocaleTimeString(); | |
| } | |
| function shortenTxId(txid) { | |
| return txid.substring(0, 10) + '...' + txid.substring(txid.length - 8); | |
| } | |
| function showError(message) { | |
| document.getElementById('error-message').textContent = message; | |
| document.getElementById('error-state').classList.remove('hidden'); | |
| } | |
| function hideError() { | |
| document.getElementById('error-state').classList.add('hidden'); | |
| } | |
| function showLoading() { | |
| document.getElementById('loading-state').classList.remove('hidden'); | |
| } | |
| function hideLoading() { | |
| document.getElementById('loading-state').classList.add('hidden'); | |
| } | |
| function toggleButtonLoading(loading) { | |
| const button = document.getElementById('search-btn'); | |
| if (loading) { | |
| button.innerHTML = '<div class="w-4 h-4 border-2 border-white border-t-transparent rounded-full spinner mr-2"></div> Loading...'; | |
| button.disabled = true; | |
| } else { | |
| button.innerHTML = '<i class="fas fa-search mr-2"></i> Explore Wallet'; | |
| button.disabled = false; | |
| } | |
| } | |
| // Main function to fetch wallet data | |
| async function fetchWalletData() { | |
| const address = document.getElementById('dogeAddress').value.trim(); | |
| if (!address) { | |
| showError('Please enter a Dogecoin address.'); | |
| return; | |
| } | |
| // Validate address format (basic check) | |
| if (!address.startsWith('D') || address.length < 26 || address.length > 35) { | |
| showError('Please enter a valid Dogecoin address (starts with D, 26-35 chars).'); | |
| return; | |
| } | |
| // Reset UI | |
| hideError(); | |
| showLoading(); | |
| toggleButtonLoading(true); | |
| try { | |
| // Fetch Dogecoin price for USD conversion | |
| let dogePrice = 0; | |
| try { | |
| const priceResponse = await fetch(config.dogePriceApiUrl); | |
| const priceData = await priceResponse.json(); | |
| dogePrice = priceData.dogecoin.usd; | |
| } catch (e) { | |
| console.warn('Failed to fetch Dogecoin price:', e); | |
| } | |
| // Fetch wallet balance and transactions | |
| const [balanceResponse, transactionsResponse] = await Promise.all([ | |
| fetch(`${config.dogeApiUrl}/dashboards/address/${address}`), | |
| fetch(`${config.dogeApiUrl}/dashboards/address/${address}/transactions?limit=${config.transactionsLimit}`) | |
| ]); | |
| if (!balanceResponse.ok || !transactionsResponse.ok) { | |
| throw new Error('Failed to fetch wallet data from Blockchair API'); | |
| } | |
| const balanceData = await balanceResponse.json(); | |
| const transactionsData = await transactionsResponse.json(); | |
| // Process balance data | |
| const walletData = balanceData.data[address]; | |
| if (!walletData) { | |
| throw new Error('No data found for this address'); | |
| } | |
| const balance = walletData.address.balance; | |
| const txCount = walletData.address.transaction_count; | |
| const firstTxTime = walletData.address.first_seen_receiving; | |
| // Update balance UI | |
| document.getElementById('balance').textContent = formatDogeAmount(balance); | |
| document.getElementById('usd-value').textContent = dogePrice ? | |
| '$' + ((balance / 100000000) * dogePrice).toFixed(2) : 'Price data unavailable'; | |
| document.getElementById('tx-count').textContent = txCount; | |
| document.getElementById('first-tx').textContent = firstTxTime ? | |
| formatDate(firstTxTime) : 'Unknown'; | |
| // Process transactions | |
| const txTable = document.getElementById('transactions'); | |
| txTable.innerHTML = ''; | |
| if (transactionsData.data && transactionsData.data.length > 0) { | |
| transactionsData.data.forEach(tx => { | |
| const isIncoming = tx.outputs.some(output => output.recipient === address); | |
| const amount = isIncoming ? | |
| tx.outputs.find(output => output.recipient === address).value : | |
| -tx.inputs.find(input => input.recipient === address).value; | |
| const row = document.createElement('tr'); | |
| row.className = 'hover:bg-gray-50'; | |
| row.innerHTML = ` | |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900"> | |
| <a href="https://blockchair.com/dogecoin/transaction/${tx.hash}" target="_blank" class="text-amber-600 hover:text-amber-700"> | |
| ${shortenTxId(tx.hash)} | |
| </a> | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm ${amount < 0 ? 'text-red-500' : 'text-green-500'}"> | |
| ${formatDogeAmount(Math.abs(amount))} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
| ${formatDate(tx.time)} | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"> | |
| Confirmed | |
| </span> | |
| </td> | |
| `; | |
| txTable.appendChild(row); | |
| }); | |
| // Show view all button if there are transactions | |
| if (txCount > config.transactionsLimit) { | |
| document.getElementById('view-all-tx').classList.remove('hidden'); | |
| document.getElementById('view-all-tx').onclick = () => { | |
| window.open(`https://blockchair.com/dogecoin/address/${address}`, '_blank'); | |
| }; | |
| } | |
| } else { | |
| const row = document.createElement('tr'); | |
| row.innerHTML = ` | |
| <td colspan="4" class="px-6 py-4 text-center text-gray-500"> | |
| No transactions found for this address | |
| </td> | |
| `; | |
| txTable.appendChild(row); | |
| } | |
| // Try to fetch NFTs (this would require a separate API) | |
| // Note: Dogecoin doesn't natively support NFTs, so this would be for Ethereum NFTs | |
| // associated with the same address (if it exists on Ethereum) | |
| try { | |
| // This is just a placeholder - you would need to implement actual NFT fetching | |
| // based on your requirements and available APIs | |
| document.getElementById('nft-placeholder').classList.remove('hidden'); | |
| document.getElementById('nfts').classList.add('hidden'); | |
| } catch (e) { | |
| console.warn('Failed to fetch NFTs:', e); | |
| document.getElementById('nft-placeholder').classList.remove('hidden'); | |
| document.getElementById('nfts').classList.add('hidden'); | |
| } | |
| } catch (error) { | |
| console.error('Error fetching wallet data:', error); | |
| showError(error.message || 'Failed to fetch wallet data. Please try again later.'); | |
| } finally { | |
| hideLoading(); | |
| toggleButtonLoading(false); | |
| } | |
| } | |
| // Add event listener for Enter key | |
| document.getElementById('dogeAddress').addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| fetchWalletData(); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |