document.addEventListener('DOMContentLoaded', () => { // Theme Toggle const themeToggle = document.getElementById('theme-toggle'); if (themeToggle) { const currentTheme = localStorage.getItem('theme') || 'dark-theme'; document.body.classList.add(currentTheme); themeToggle.textContent = currentTheme === 'dark-theme' ? 'Light Mode' : 'Dark Mode'; themeToggle.addEventListener('click', () => { if (document.body.classList.contains('dark-theme')) { document.body.classList.replace('dark-theme', 'light-theme'); localStorage.setItem('theme', 'light-theme'); themeToggle.textContent = 'Dark Mode'; } else { document.body.classList.replace('light-theme', 'dark-theme'); localStorage.setItem('theme', 'dark-theme'); themeToggle.textContent = 'Light Mode'; } }); } // Leverage Slider const leverageSlider = document.getElementById('leverage'); const leverageValue = document.getElementById('leverage-value'); if (leverageSlider && leverageValue) { leverageValue.textContent = leverageSlider.value; leverageSlider.addEventListener('input', () => { leverageValue.textContent = leverageSlider.value; }); } // API Key Modal const apiKeyModal = document.getElementById('api-key-modal'); const apiKeyModalBtn = document.getElementById('api-key-modal-btn'); if (apiKeyModal && apiKeyModalBtn) { const closeBtn = apiKeyModal.querySelector('.close-btn'); const apiKeyForm = document.getElementById('api-key-form'); const apiKeyStatus = document.getElementById('api-key-status'); apiKeyModalBtn.addEventListener('click', () => { apiKeyModal.style.display = 'flex'; }); if (closeBtn) { closeBtn.addEventListener('click', () => { apiKeyModal.style.display = 'none'; }); } window.addEventListener('click', (event) => { if (event.target == apiKeyModal) { apiKeyModal.style.display = 'none'; } }); if (apiKeyForm) { apiKeyForm.addEventListener('submit', (event) => { event.preventDefault(); const apiKey = document.getElementById('api-key').value; const apiSecret = document.getElementById('api-secret').value; fetch('api/save_key.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey, apiSecret }), }) .then(response => response.json()) .then(data => { if (data.success) { apiKeyStatus.textContent = 'API Key saved successfully!'; apiKeyStatus.style.color = 'var(--success)'; setTimeout(() => { apiKeyModal.style.display = 'none'; }, 2000); } else { apiKeyStatus.textContent = `Error: ${data.error}`; apiKeyStatus.style.color = 'var(--danger)'; } }) .catch(error => { apiKeyStatus.textContent = `Error: ${error.message}`; apiKeyStatus.style.color = 'var(--danger)'; }); }); } } // User State Button const getUserStateButton = document.getElementById('getUserStateButton'); const userStateContainer = document.getElementById('user-state-container'); const userStateData = document.getElementById('user-state-data'); if (getUserStateButton && userStateContainer && userStateData) { getUserStateButton.addEventListener('click', () => { fetch('api/proxy.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: 'clearinghouseState' }), }) .then(response => { if (response.status === 401) { alert('API Key not configured. Please add it via the API Key button.'); return null; } return response.json(); }) .then(data => { if (data) { userStateData.textContent = JSON.stringify(data, null, 2); userStateContainer.style.display = 'block'; } }) .catch(error => { console.error('Error fetching user state:', error); alert('An error occurred while fetching user state.'); }); }); } // WebSocket Connection connectWebSocket(); }); function connectWebSocket() { const ws = new WebSocket('wss://api.hyperliquid.xyz/ws'); ws.onopen = () => { console.log('Connected to Hyperliquid WebSocket API'); ws.send(JSON.stringify({ "method": "subscribe", "subscription": { "type": "l2Book", "coin": "ETH" } })); ws.send(JSON.stringify({ "method": "subscribe", "subscription": { "type": "trades", "coin": "ETH" } })); }; ws.onmessage = (event) => { const response = JSON.parse(event.data); if (response.channel === 'l2Book' && response.data) { updateOrderBook(response.data); } if (response.channel === 'trades' && response.data) { updateTradeFeed(response.data); } }; ws.onerror = (error) => { console.error('WebSocket Error:', error); }; ws.onclose = () => { console.log('WebSocket connection closed. Reconnecting...'); setTimeout(connectWebSocket, 5000); }; } function updateOrderBook(data) { const orderBookData = document.getElementById('order-book-data'); if (!orderBookData) return; const asks = data.levels[1].slice(0, 8).reverse(); const bids = data.levels[0].slice(0, 8); let html = '
'; html += '
Price (USD)
Size
'; asks.forEach(ask => { html += `
${parseFloat(ask.px).toFixed(2)}
${parseFloat(ask.sz).toFixed(4)}
`; }); bids.forEach(bid => { html += `
${parseFloat(bid.px).toFixed(2)}
${parseFloat(bid.sz).toFixed(4)}
`; }); html += '
'; orderBookData.innerHTML = html; } function updateTradeFeed(trades) { const tradeFeedData = document.getElementById('trade-feed-data'); if (!tradeFeedData) return; const maxTrades = 15; trades.forEach(trade => { const tradeElement = document.createElement('div'); tradeElement.className = `trade-row ${trade.side === 'B' ? 'buy' : 'sell'}`; const time = new Date(trade.time).toLocaleTimeString(); const price = parseFloat(trade.px).toFixed(2); const size = parseFloat(trade.sz).toFixed(4); tradeElement.innerHTML = `
${price}
${size}
${time}
`; tradeFeedData.prepend(tradeElement); }); while (tradeFeedData.children.length > maxTrades) { tradeFeedData.removeChild(tradeFeedData.lastChild); } }