192 lines
7.3 KiB
JavaScript
192 lines
7.3 KiB
JavaScript
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 = '<div class="order-book-column">';
|
|
html += '<div class="order-book-header"><div>Price (USD)</div><div>Size</div></div>';
|
|
|
|
asks.forEach(ask => {
|
|
html += `<div class="ask-row"><div>${parseFloat(ask.px).toFixed(2)}</div><div>${parseFloat(ask.sz).toFixed(4)}</div></div>`;
|
|
});
|
|
|
|
bids.forEach(bid => {
|
|
html += `<div class="bid-row"><div>${parseFloat(bid.px).toFixed(2)}</div><div>${parseFloat(bid.sz).toFixed(4)}</div></div>`;
|
|
});
|
|
|
|
html += '</div>';
|
|
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 = `<div>${price}</div><div>${size}</div><div>${time}</div>`;
|
|
tradeFeedData.prepend(tradeElement);
|
|
});
|
|
|
|
while (tradeFeedData.children.length > maxTrades) {
|
|
tradeFeedData.removeChild(tradeFeedData.lastChild);
|
|
}
|
|
} |