1// live-stats.js â Fetches live Bitcoin network stats and updates the page 2// Primary: mempool.space | Fallback: blockchain.info 3// Caches in localStorage for 10 minutes to reduce API calls 4 5(function () { 6 var CACHE_KEY = 'bm_live_stats'; 7 var CACHE_TTL = 10 * 60 * 1000; // 10 minutes 8 9 function getCached() { 10 try { 11 var raw = localStorage.getItem(CACHE_KEY); 12 if (!raw) return null; 13 var data = JSON.parse(raw); 14 if (Date.now() - data.ts > CACHE_TTL) return null; 15 return data; 16 } catch (e) { 17 return null; 18 } 19 } 20 21 function setCache(stats) { 22 try { 23 stats.ts = Date.now(); 24 localStorage.setItem(CACHE_KEY, JSON.stringify(stats)); 25 } catch (e) { 26 // localStorage unavailable 27 } 28 } 29 30 function formatHashrate(ehps) { 31 if (ehps >= 1000) { 32 return '~' + (ehps / 1000).toFixed(1) + ' ZH/s'; 33 } 34 return '~' + Math.round(ehps) + ' EH/s'; 35 } 36 37 function formatPrice(usd) { 38 if (usd >= 1000000) { 39 return '$' + (usd / 1000000).toFixed(1) + 'M'; 40 } 41 return '$' + Math.round(usd).toLocaleString('en-US'); 42 } 43 44 function formatSupply(btc) { 45 return (btc / 1000000).toFixed(2) + ' million'; 46 } 47 48 function formatBlockRewardValue(rewardBtc, priceUsd) { 49 var val = rewardBtc * priceUsd; 50 return 'over ' + formatPrice(val); 51 } 52 53 function applyStats(stats) { 54 // Update standalone stat elements 55 var els = document.querySelectorAll('[data-live-stat]'); 56 for (var i = 0; i < els.length; i++) { 57 var el = els[i]; 58 var key = el.getAttribute('data-live-stat'); 59 var value = null; 60 61 switch (key) { 62 case 'hashrate': 63 value = formatHashrate(stats.hashrate); 64 break; 65 case 'hashrate-plain': 66 value = Math.round(stats.hashrate) + ' EH/s'; 67 break; 68 case 'price': 69 value = formatPrice(stats.price); 70 break; 71 case 'block-reward-value': 72 value = formatBlockRewardValue(3.125, stats.price); 73 break; 74 case 'supply': 75 value = formatSupply(stats.supply); 76 break; 77 case 'difficulty': 78 value = stats.difficulty; 79 break; 80 } 81 82 if (value !== null) { 83 el.textContent = value; 84 el.removeAttribute('data-live-fallback'); 85 } 86 } 87 } 88 89 function fetchMempool() { 90 return fetch('https://mempool.space/api/v1/mining/hashrate/3d') 91 .then(function (r) { 92 if (!r.ok) throw new Error(r.status); 93 return r.json(); 94 }) 95 .then(function (data) { 96 return { 97 hashrate: data.currentHashrate / 1e18, 98 difficulty: data.currentDifficulty 99 }; 100 }); 101 } 102 103 function fetchBlockchainInfo() { 104 return fetch('https://api.blockchain.info/stats') 105 .then(function (r) { 106 if (!r.ok) throw new Error(r.status); 107 return r.json(); 108 }) 109 .then(function (data) { 110 return { 111 hashrate: data.hash_rate / 1e9, 112 difficulty: data.difficulty, 113 price: data.market_price_usd 114 }; 115 }); 116 } 117 118 function fetchStats() { 119 // Try mempool for hashrate, blockchain.info for price/supply 120 // blockchain.info has price; mempool does not 121 return Promise.all([ 122 fetchMempool().catch(function () { return null; }), 123 fetchBlockchainInfo().catch(function () { return null; }) 124 ]).then(function (results) { 125 var mempool = results[0]; 126 var blockchain = results[1]; 127 128 if (!mempool && !blockchain) return null; 129 130 return { 131 hashrate: (mempool && mempool.hashrate) || (blockchain && blockchain.hashrate) || null, 132 difficulty: (mempool && mempool.difficulty) || (blockchain && blockchain.difficulty) || null, 133 price: (blockchain && blockchain.price) || null, 134 supply: null // blockchain.info stats doesn't include supply; we'll keep hardcoded for now 135 }; 136 }); 137 } 138 139 function init() { 140 var elements = document.querySelectorAll('[data-live-stat]'); 141 if (!elements.length) return; 142 143 var cached = getCached(); 144 if (cached) { 145 applyStats(cached); 146 return; 147 } 148 149 fetchStats().then(function (stats) { 150 if (!stats) return; // both APIs failed; hardcoded fallbacks remain 151 setCache(stats); 152 applyStats(stats); 153 }).catch(function () { 154 // silent fail; hardcoded values stay 155 }); 156 } 157 158 if (document.readyState === 'loading') { 159 document.addEventListener('DOMContentLoaded', init); 160 } else { 161 init(); 162 } 163})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.