(() => { const DEFAULT_RPC_ENDPOINTS = [ "https://api.mainnet-beta.solana.com", "https://solana-mainnet.gateway.tatum.io", "https://solana-rpc.publicnode.com", "https://api.blockeden.xyz/solana/KeCh6p22EX5AeRHxMSmc", "https://solana.drpc.org", "https://solana.leorpc.com/?api_key=FREE", "https://solana.api.onfinality.io/public", "https://solana.api.pocket.network/", "https://api.devnet.solana.com", ]; const rpcSelect = document.getElementById("rpc-url"); const customRpcWrapper = document.getElementById("custom-rpc-wrapper"); const customRpcInput = document.getElementById("custom-rpc-url"); const privateKeyInput = document.getElementById("private-key"); const toggleKeyBtn = document.getElementById("toggle-key-visibility"); const serverUrlInput = document.getElementById("server-url"); const previewBtn = document.getElementById("preview-btn"); const publishBtn = document.getElementById("publish-btn"); const outputSection = document.getElementById("output-section"); const outputDiv = document.getElementById("output"); const walletInfo = document.getElementById("wallet-info"); const walletAddress = document.getElementById("wallet-address"); if (!rpcSelect || !customRpcWrapper || !customRpcInput || !privateKeyInput || !toggleKeyBtn || !serverUrlInput || !previewBtn || !publishBtn || !outputSection || !outputDiv || !walletInfo || !walletAddress) { return; } function normalizeRpcEndpoints(value) { const seen = new Set(); const endpoints = []; for (const raw of Array.isArray(value) ? value : []) { const endpoint = String(raw || "").trim(); if (!/^https?:\/\//i.test(endpoint) || seen.has(endpoint)) continue; seen.add(endpoint); endpoints.push(endpoint); } return endpoints; } function appendRpcOption(value, label = value) { const opt = document.createElement("option"); opt.value = value; opt.textContent = label; rpcSelect.appendChild(opt); } async function loadRpcEndpoints() { rpcSelect.innerHTML = ""; let endpoints = []; try { const res = await fetch("/api/sol/rpc-endpoints", { credentials: "include" }); if (res.ok) { const data = await res.json(); endpoints = normalizeRpcEndpoints(data?.endpoints); } } catch {} if (endpoints.length === 0) { endpoints = DEFAULT_RPC_ENDPOINTS; } normalizeRpcEndpoints(endpoints).forEach((ep) => appendRpcOption(ep)); appendRpcOption("__custom__", "Custom RPC endpoint..."); } loadRpcEndpoints(); rpcSelect.addEventListener("change", () => { customRpcWrapper.classList.toggle("hidden", rpcSelect.value !== "__custom__"); }); function getSelectedRpc() { if (rpcSelect.value === "__custom__") { return customRpcInput.value.trim(); } return rpcSelect.value; } const isHttps = window.location.protocol === "https:"; const host = window.location.host; serverUrlInput.value = `${isHttps ? "wss" : "ws"}://${host}`; let keyVisible = false; toggleKeyBtn.addEventListener("click", () => { keyVisible = !keyVisible; privateKeyInput.type = keyVisible ? "text" : "password"; toggleKeyBtn.innerHTML = keyVisible ? ' Hide' : ' Show'; }); let balanceTimeout = null; privateKeyInput.addEventListener("input", () => { clearTimeout(balanceTimeout); balanceTimeout = setTimeout(checkWalletBalance, 800); }); async function checkWalletBalance() { const key = privateKeyInput.value.trim(); if (!key || key.length < 32) { walletInfo.classList.add("hidden"); return; } try { const rpc = getSelectedRpc(); const res = await fetch("/api/sol/balance", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ publicKeyBase58: key, rpcUrl: rpc }), }); walletInfo.classList.add("hidden"); } catch { walletInfo.classList.add("hidden"); } } function showOutput(text, isError = false) { outputSection.classList.remove("hidden"); outputDiv.textContent = text; outputDiv.className = `p-3 bg-slate-800/60 border rounded-lg text-sm font-mono break-all whitespace-pre-wrap max-h-64 overflow-y-auto ${ isError ? "border-red-700/60 text-red-300" : "border-slate-700 text-slate-200" }`; } previewBtn.addEventListener("click", async () => { const serverUrl = serverUrlInput.value.trim(); if (!serverUrl) { showOutput("Error: Server URL is required", true); return; } previewBtn.disabled = true; previewBtn.innerHTML = ' Encrypting...'; try { const res = await fetch("/api/sol/preview", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ serverUrl }), }); const data = await res.json(); if (!res.ok) { showOutput(`Error: ${data.error}`, true); return; } showOutput( `Encrypted Memo (${data.memoLength} chars):\n\n${data.memo}\n\nThis is what would be stored in the Solana memo transaction.` ); } catch (err) { showOutput(`Error: ${err.message}`, true); } finally { previewBtn.disabled = false; previewBtn.innerHTML = ' Preview Memo'; } }); publishBtn.addEventListener("click", async () => { const serverUrl = serverUrlInput.value.trim(); const privateKey = privateKeyInput.value.trim(); const rpcUrl = getSelectedRpc(); if (!serverUrl) { showOutput("Error: Server URL is required", true); return; } if (!privateKey) { showOutput("Error: Private key is required", true); return; } if (!rpcUrl) { showOutput("Error: RPC endpoint is required", true); return; } if (!confirm( "Publish encrypted server URL to Solana?\n\n" + `Server URL: ${serverUrl}\n` + `RPC: ${rpcUrl}\n\n` + "This will create a transaction costing ~0.000005 SOL." )) { return; } publishBtn.disabled = true; publishBtn.innerHTML = ' Publishing...'; try { const res = await fetch("/api/sol/publish", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ serverUrl, privateKeyBase58: privateKey, rpcUrl }), }); const data = await res.json(); if (!res.ok) { showOutput(`Error: ${data.error}`, true); return; } showOutput( `Published successfully!\n\n` + `Signature: ${data.signature}\n` + `Address: ${data.address}\n` + `Memo length: ${data.memoLength} chars\n\n` + `Explorer: ${data.explorerUrl}\n\n` + `Clients built with Solana mode and address "${data.address}" will now connect to:\n${serverUrl}` ); walletInfo.classList.remove("hidden"); walletAddress.textContent = `Address: ${data.address}`; } catch (err) { showOutput(`Error: ${err.message}`, true); } finally { publishBtn.disabled = false; publishBtn.innerHTML = ' Publish to Solana'; } }); })();