dogewallet/index.html

2807 lines
105 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="title" content="RanchiMall Dogecoin Web Wallet" />
<meta
name="description"
content="RanchiMall Dogecoin Web Wallet - Generate multi-blockchain addresses, send DOGE, view transactions, translate address and manage your assets."
/>
<meta property="og:type" content="website" />
<meta property="og:title" content="RanchiMall Dogecoin Web Wallet" />
<meta
property="og:description"
content="RanchiMall Dogecoin Web Wallet - Generate multi-blockchain addresses, send DOGE, view transactions, translate address and manage your assets."
/>
<title>Doge Wallet</title>
<link rel="shortcut icon" href="favicon.svg" type="image/x-icon" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"
/>
<link rel="stylesheet" href="style.css" />
<script src="doge/lib.dogecoin.js"></script>
<script src="doge/dogeCrypto.js"></script>
<script src="doge/dogeBlockchainAPI.js"></script>
<script src="doge/dogeSearchDB.js"></script>
</head>
<body>
<!-- Confirmation Popup -->
<div id="confirmationPopup" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3 id="confirmTitle">Confirm Transaction</h3>
<span class="modal-close" onclick="closeConfirmationPopup()"
>&times;</span
>
</div>
<div class="modal-body">
<div class="confirm-details">
<div class="detail-group">
<label><i class="fas fa-coins"></i> Amount:</label>
<div id="confirmAmount" class="confirm-value"></div>
</div>
<div class="detail-group">
<label><i class="fas fa-wallet"></i> From:</label>
<div id="confirmFrom" class="confirm-value address-value"></div>
</div>
<div class="detail-group">
<label><i class="fas fa-user"></i> To:</label>
<div id="confirmTo" class="confirm-value address-value"></div>
</div>
<div class="detail-group">
<label><i class="fas fa-money-bill-wave"></i> Fee:</label>
<div class="confirm-value">0.09 DOGE (Network Fee)</div>
</div>
<div class="detail-group warning">
<i class="fas fa-exclamation-triangle"></i>
<div>
Please verify all details carefully. Cryptocurrency transactions
cannot be reversed once confirmed.
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeConfirmationPopup()">
Cancel
</button>
<button class="btn btn-primary" id="confirmSendBtn">
Confirm & Send
</button>
</div>
</div>
</div>
<div class="notification-drawer"></div>
<header class="header">
<div class="header-content">
<div id="logo" class="app-brand">
<svg id="main_logo" class="icon" viewBox="0 0 27.25 32">
<title>RanchiMall</title>
<path
d="M27.14,30.86c-.74-2.48-3-4.36-8.25-6.94a20,20,0,0,1-4.2-2.49,6,6,0,0,1-1.25-1.67,4,4,0,0,1,0-2.26c.37-1.08.79-1.57,3.89-4.55a11.66,11.66,0,0,0,3.34-4.67,6.54,6.54,0,0,0,.05-2.82C20,3.6,18.58,2,16.16.49c-.89-.56-1.29-.64-1.3-.24a3,3,0,0,1-.3.72l-.3.55L13.42.94C13,.62,12.4.26,12.19.15c-.4-.2-.73-.18-.72.05a9.39,9.39,0,0,1-.61,1.33s-.14,0-.27-.13C8.76.09,8-.27,8,.23A11.73,11.73,0,0,1,6.76,2.6C4.81,5.87,2.83,7.49.77,7.49c-.89,0-.88,0-.61,1,.22.85.33.92,1.09.69A5.29,5.29,0,0,0,3,8.33c.23-.17.45-.29.49-.26a2,2,0,0,1,.22.63A1.31,1.31,0,0,0,4,9.34a5.62,5.62,0,0,0,2.27-.87L7,8l.13.55c.19.74.32.82,1,.65a7.06,7.06,0,0,0,3.46-2.47l.6-.71-.06.64c-.17,1.63-1.3,3.42-3.39,5.42L6.73,14c-3.21,3.06-3,5.59.6,8a46.77,46.77,0,0,0,4.6,2.41c.28.13,1,.52,1.59.87,3.31,2,4.95,3.92,4.95,5.93a2.49,2.49,0,0,0,.07.77h0c.09.09,0,.1.9-.14a2.61,2.61,0,0,0,.83-.32,3.69,3.69,0,0,0-.55-1.83A11.14,11.14,0,0,0,17,26.81a35.7,35.7,0,0,0-5.1-2.91C9.37,22.64,8.38,22,7.52,21.17a3.53,3.53,0,0,1-1.18-2.48c0-1.38.71-2.58,2.5-4.23,2.84-2.6,3.92-3.91,4.67-5.65a3.64,3.64,0,0,0,.42-2A3.37,3.37,0,0,0,13.61,5l-.32-.74.29-.48c.17-.27.37-.63.46-.8l.15-.3.44.64a5.92,5.92,0,0,1,1,2.81,5.86,5.86,0,0,1-.42,1.94c0,.12-.12.3-.15.4a9.49,9.49,0,0,1-.67,1.1,28,28,0,0,1-4,4.29C8.62,15.49,8.05,16.44,8,17.78a3.28,3.28,0,0,0,1.11,2.76c.95,1,2.07,1.74,5.25,3.32,3.64,1.82,5.22,2.9,6.41,4.38A4.78,4.78,0,0,1,21.94,31a3.21,3.21,0,0,0,.14.92,1.06,1.06,0,0,0,.43-.05l.83-.22.46-.12-.06-.46c-.21-1.53-1.62-3.25-3.94-4.8a37.57,37.57,0,0,0-5.22-2.82A13.36,13.36,0,0,1,11,21.19a3.36,3.36,0,0,1-.8-4.19c.41-.85.83-1.31,3.77-4.15,2.39-2.31,3.43-4.13,3.43-6a5.85,5.85,0,0,0-2.08-4.29c-.23-.21-.44-.43-.65-.65A2.5,2.5,0,0,1,15.27.69a10.6,10.6,0,0,1,2.91,2.78A4.16,4.16,0,0,1,19,6.16a4.91,4.91,0,0,1-.87,3c-.71,1.22-1.26,1.82-4.27,4.67a9.47,9.47,0,0,0-2.07,2.6,2.76,2.76,0,0,0-.33,1.54,2.76,2.76,0,0,0,.29,1.47c.57,1.21,2.23,2.55,4.65,3.73a32.41,32.41,0,0,1,5.82,3.24c2.16,1.6,3.2,3.16,3.2,4.8a1.94,1.94,0,0,0,.09.76,4.54,4.54,0,0,0,1.66-.4C27.29,31.42,27.29,31.37,27.14,30.86ZM6.1,7h0a3.77,3.77,0,0,1-1.46.45L4,7.51l.68-.83a25.09,25.09,0,0,0,3-4.82A12,12,0,0,1,8.28.76c.11-.12.77.32,1.53,1l.63.58-.57.84A10.34,10.34,0,0,1,6.1,7Zm5.71-1.78A9.77,9.77,0,0,1,9.24,7.18h0a5.25,5.25,0,0,1-1.17.28l-.58,0,.65-.78a21.29,21.29,0,0,0,2.1-3.12c.22-.41.42-.76.44-.79s.5.43.9,1.24L12,5ZM13.41,3a2.84,2.84,0,0,1-.45.64,11,11,0,0,1-.9-.91l-.84-.9.19-.45c.34-.79.39-.8,1-.31A9.4,9.4,0,0,1,13.8,2.33q-.18.34-.39.69Z"
/>
</svg>
<div class="app-name">
<div class="app-name__company">RanchiMall</div>
<h4 class="app-name__title">Doge Wallet</h4>
</div>
</div>
<div class="header-actions">
<button
id="themeToggle"
class="theme-toggle"
title="Toggle dark/light mode"
>
<i class="fas fa-sun" id="themeIcon"></i>
</button>
</div>
</div>
</header>
<div class="container">
<nav class="sidebar" id="sidebar">
<ul class="sidebar-menu">
<li>
<a href="#" onclick="showPage('walletPage')" class="nav-link active"
><i class="fas fa-wallet"></i>Generate</a
>
</li>
<li>
<a href="#" onclick="showPage('sendPage')" class="nav-link"
><i class="fas fa-paper-plane"></i>Send</a
>
</li>
<li>
<a href="#" onclick="showPage('transactionsPage')" class="nav-link"
><i class="fas fa-exchange-alt"></i>Transactions</a
>
</li>
<li>
<a href="#" onclick="showPage('translatePage')" class="nav-link"
><i class="fas fa-sync-alt"></i>Translate</a
>
</li>
<li>
<a href="#" onclick="showPage('recoverPage')" class="nav-link"
><i class="fas fa-key"></i>Recover</a
>
</li>
</ul>
</nav>
<main class="main-content">
<!-- Wallet Page (Generate) -->
<div id="walletPage" class="page">
<div class="page-header">
<h2>
<i class="fas fa-key"></i> Generate Multi-Blockchain Addresses
</h2>
<p>
Generate addresses for DOGE, FLO, BTC, and LTC from a single
private key
</p>
</div>
<div class="card">
<div class="generate-wallet-intro">
<div class="intro-icon">
<i class="fas fa-wallet"></i>
</div>
<div class="intro-content">
<h3>One Key, Multiple Blockchains</h3>
<p>
Generate a single private key that works across DOGE, FLO,
BTC, and LTC networks. This creates a unified experience
across multiple blockchains.
</p>
</div>
</div>
<div class="generate-actions">
<button
id="generateBtn"
class="btn btn-primary btn-block"
onclick="generateMultiChain()"
>
<span class="btn-text"
><i class="fas fa-wallet"></i> Generate</span
>
<span class="btn-loading" style="display: none">
<i class="fas fa-spinner fa-spin"></i> Generating...
</span>
</button>
</div>
<div id="multiResult" class="output"></div>
</div>
</div>
<!-- Recover Page -->
<div id="recoverPage" class="page hidden">
<div class="page-header">
<h2>
<i class="fas fa-key"></i> Recover Multi-Blockchain Addresses
</h2>
<p>
Recover all blockchain addresses (DOGE, FLO, BTC, LTC) from a
single private key
</p>
</div>
<div class="card">
<div class="form-group">
<label
><i class="fas fa-key"></i> Private Key
(DOGE/BTC/FLO/LTC):</label
>
<div class="input-with-actions">
<input
id="translateWIF"
class="form-input"
type="password"
placeholder="Enter DOGE / BTC / FLO / LTC private key"
/>
<button
type="button"
class="input-action-btn password-toggle"
onclick="togglePasswordVisibility('translateWIF')"
title="Show/Hide Password"
>
<i class="fas fa-eye"></i>
</button>
<button
type="button"
class="input-action-btn clear-btn"
onclick="clearInput('translateWIF')"
title="Clear"
>
<i class="fas fa-times"></i>
</button>
</div>
<small class="form-text">
Enter a private key (DOGE, BTC, FLO, LTC).
</small>
</div>
<div class="recover-actions">
<button
class="btn btn-primary btn-block"
onclick="translateAddress()"
id="recoverBtn"
>
<span class="btn-text"
><i class="fas fa-search"></i> Recover Addresses</span
>
<span class="btn-loading" style="display: none">
<i class="fas fa-spinner fa-spin"></i> Recovering...
</span>
</button>
</div>
<div id="translateResult" class="output"></div>
</div>
</div>
<!-- Address Translation Page -->
<div id="translatePage" class="page hidden">
<div class="page-header">
<h2><i class="fas fa-sync-alt"></i> Address Translation</h2>
<p>Convert addresses between blockchains (DOGE, FLO, BTC, LTC)</p>
</div>
<div class="card">
<div class="form-group">
<label
><i class="fas fa-map-marker-alt"></i> Blockchain
Address:</label
>
<div class="input-with-actions">
<input
id="addressToTranslate"
class="form-input"
placeholder="Enter DOGE / BTC / FLO / LTC address"
/>
<button
type="button"
class="input-action-btn clear-btn"
onclick="clearInput('addressToTranslate')"
title="Clear"
>
<i class="fas fa-times"></i>
</button>
</div>
<small class="form-text"
>Enter any blockchain address to see equivalent addresses on
other chains</small
>
</div>
<button
class="btn btn-primary btn-block"
onclick="translateDirectAddress()"
id="translateBtn"
>
<span class="btn-text"
><i class="fas fa-exchange-alt"></i> Translate Address</span
>
<span class="btn-loading" style="display: none">
<i class="fas fa-spinner fa-spin"></i> Translating...
</span>
</button>
<div id="directTranslateResult" class="output"></div>
</div>
</div>
<!-- Transactions Page -->
<div id="transactionsPage" class="page hidden">
<div class="page-header">
<h2><i class="fas fa-exchange-alt"></i> Dogecoin Transactions</h2>
<p>
Check balance and transaction history for any Dogecoin address
</p>
</div>
<div class="card search-card">
<div class="search-type-selector">
<p class="search-type-label">Search Type:</p>
<div class="search-type-options">
<label
class="radio-button-container active"
id="balanceHistoryRadio"
>
<input
type="radio"
name="searchType"
value="balance"
checked
onclick="toggleSearchType('balance'); return true;"
/>
<span class="radio-icon"><i class="fas fa-coins"></i></span>
<span>Balance & History</span>
</label>
<label
class="radio-button-container"
id="transactionDetailsRadio"
>
<input
type="radio"
name="searchType"
value="transaction"
onclick=" toggleSearchType('transaction'); return true;"
/>
<span class="radio-icon"
><i class="fas fa-exchange-alt"></i
></span>
<span>Transaction Details</span>
</label>
</div>
</div>
<!-- Balance & History Search Form -->
<div id="balanceHistorySearch">
<div class="form-group">
<label
><i class="fas fa-map-marker-alt"></i> DOGE/BTC/FLO/LTC
address:</label
>
<div class="input-with-actions">
<input
id="transactionAddr"
class="form-input"
placeholder="Enter DOGE/BTC/FLO/LTC address"
/>
<button
type="button"
class="input-action-btn clear-btn"
onclick="clearInput('transactionAddr')"
title="Clear"
>
<i class="fas fa-times"></i>
</button>
</div>
</div>
<button
class="btn btn-primary btn-block"
id="loadTransactions"
onclick="loadTransactions()"
>
<span class="btn-text"
><i class="fas fa-search"></i> Check Balance</span
>
<span class="btn-loading" style="display: none">
<i class="fas fa-spinner fa-spin"></i> Loading...
</span>
</button>
</div>
<!-- Transaction Details Search Form -->
<div id="transactionSearch" style="display: none">
<div class="form-group">
<label><i class="fas fa-hashtag"></i> Transaction Hash:</label>
<div class="input-with-actions">
<input
id="txHash"
class="form-input"
placeholder="Enter transaction hash"
/>
<button
type="button"
class="input-action-btn clear-btn"
onclick="clearInput('txHash')"
title="Clear"
>
<i class="fas fa-times"></i>
</button>
</div>
<small class="form-text"
>Enter a transaction hash to see details</small
>
</div>
<button
class="btn btn-primary btn-block"
id="txSearchBtn"
onclick="searchTransactionDetails();"
>
<span class="btn-text"
><i class="fas fa-search"></i> Search Transaction</span
>
<span class="btn-loading" style="display: none">
<i class="fas fa-spinner fa-spin"></i> Searching...
</span>
</button>
</div>
</div>
<!-- Balance & History Results -->
<div id="balanceHistoryResults">
<!-- Account Balance section -->
<div
id="balanceSection"
class="card balance-info"
style="display: none"
>
<div class="balance-header">
<h3><i class="fas fa-coins"></i> Account Balance</h3>
<button
onclick="shareAddress()"
class="btn-icon share-btn"
title="Copy Address Link"
>
<i class="fas fa-share-alt"></i>
</button>
</div>
<div class="balance-display">
<span class="balance-amount" id="balanceValue">0</span>
<span class="currency">DOGE</span>
</div>
<div class="account-details">
<div class="detail-row">
<label>Address:</label>
<div class="address-container">
<span id="displayedAddress" class="address-text"></span>
</div>
</div>
</div>
</div>
<!-- Transaction section -->
<div id="transactionSection" style="display: none">
<div class="transaction-section">
<div class="transaction-header">
<h3>Transactions</h3>
<div class="filter-buttons">
<button
class="filter-btn active"
data-filter="all"
onclick="filterTransactions('all')"
>
All
</button>
<button
class="filter-btn"
data-filter="received"
onclick="filterTransactions('received')"
>
Received
</button>
<button
class="filter-btn"
data-filter="sent"
onclick="filterTransactions('sent')"
>
Sent
</button>
</div>
</div>
</div>
<div id="txList" class="transaction-list"></div>
<div class="pagination-section">
<div id="paginationInfo" class="pagination-info">
Showing 0 - 0 of 0 transactions
</div>
<div class="pagination-controls" id="paginationControls">
<button
class="pagination-btn"
id="prevBtn"
onclick="loadPreviousTransactions()"
disabled
>
<i class="fas fa-chevron-left"></i> Previous
</button>
<div class="page-numbers" id="pageNumbers"></div>
<button
class="pagination-btn"
id="nextBtn"
onclick="loadNextTransactions()"
disabled
>
Next <i class="fas fa-chevron-right"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Transaction Details Results -->
<div id="txOutput" class="transaction-result"></div>
<!-- Searched Addresses History -->
<div
id="searchedAddressesContainer"
class="card searched-addresses-card"
style="display: none"
>
<div class="searched-addresses-header">
<h3><i class="fas fa-history"></i> Recent Addresses</h3>
<button
onclick="clearAllSearchedAddresses()"
class="btn-clear-all"
title="Clear all"
>
<i class="fas fa-trash"></i> Clear All
</button>
</div>
<div class="searched-addresses-list" id="searchedAddressesList">
<!-- Searched addresses will be displayed here -->
</div>
</div>
</div>
<div id="historyPage" class="page hidden"></div>
<div id="balancePage" class="page hidden"></div>
<!-- Send Page -->
<div id="sendPage" class="page hidden">
<div class="page-header">
<h2><i class="fas fa-paper-plane"></i> Send DOGE</h2>
<p>Send Dogecoin to any address</p>
</div>
<div class="card">
<div class="form-group">
<label><i class="fas fa-key"></i> Sender's Private Key (DOGE/BTC/FLO/LTC):</label>
<div class="input-with-actions">
<input
id="privateKey"
class="form-input"
type="password"
placeholder="Sender's Private Key (DOGE/BTC/FLO/LTC)"
onchange="updateSenderAddress()"
onpaste="setTimeout(updateSenderAddress, 10)"
oninput="setTimeout(updateSenderAddress, 10)"
/>
<button
type="button"
class="input-action-btn password-toggle"
onclick="togglePasswordVisibility('privateKey')"
title="Show/Hide Password"
>
<i class="fas fa-eye"></i>
</button>
<button
type="button"
class="input-action-btn clear-btn"
onclick="clearInput('privateKey'); document.getElementById('senderAddress').value = ''"
title="Clear"
>
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="form-group">
<label
><i class="fas fa-user"></i> Recipient's DOGE Address:</label
>
<div class="input-with-actions">
<input
id="receiverAddress"
class="form-input"
placeholder="Recipient's DOGE address"
/>
<button
type="button"
class="input-action-btn clear-btn"
onclick="clearInput('receiverAddress')"
title="Clear"
>
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="form-group">
<label><i class="fas fa-coins"></i> Amount (DOGE):</label>
<div class="input-with-actions">
<input
id="sendAmount"
class="form-input"
type="number"
step="0.00000001"
min="1"
placeholder="Amount to send (DOGE)"
/>
<button
type="button"
class="input-action-btn clear-btn"
onclick="clearInput('sendAmount')"
title="Clear"
>
<i class="fas fa-times"></i>
</button>
</div>
<small class="form-text"
>Transaction fee (0.09 DOGE) will be automatically added</small
>
</div>
<button
class="btn btn-primary btn-block"
onclick="sendDogeRPC()"
id="sendBtn"
>
<span class="btn-text"
><i class="fas fa-paper-plane"></i> Send DOGE</span
>
<span class="btn-loading" style="display: none">
<i class="fas fa-spinner fa-spin"></i> Sending...
</span>
</button>
<div id="sendResult" class="output"></div>
</div>
</div>
</main>
</div>
<!-- Bottom Navigation (Mobile) -->
<div class="nav-box">
<button
onclick="showPage('walletPage')"
class="nav-btn active"
data-page="walletPage"
>
<i class="fas fa-wallet"></i><span>Generate</span>
</button>
<button
onclick="showPage('sendPage')"
class="nav-btn"
data-page="sendPage"
>
<i class="fas fa-paper-plane"></i><span>Send</span>
</button>
<button
onclick="showPage('transactionsPage')"
class="nav-btn"
data-page="transactionsPage"
>
<i class="fas fa-exchange-alt"></i><span>Transaction</span>
</button>
<button
onclick="showPage('translatePage')"
class="nav-btn"
data-page="translatePage"
>
<i class="fas fa-sync-alt"></i><span>Translate</span>
</button>
<button
onclick="showPage('recoverPage')"
class="nav-btn"
data-page="recoverPage"
>
<i class="fas fa-key"></i><span>Recover</span>
</button>
</div>
<div id="notification_drawer" class="notification-drawer"></div>
<script>
// Initialize the searched address database
const searchedAddressDB = new SearchedAddressDB();
let currentTxOffset = 0;
let txPerPage = 10;
let totalTxCount = 0;
let currentTxAddress = "";
// Theme toggle
function initializeTheme() {
const themeToggle = document.getElementById("themeToggle");
const body = document.body;
const savedTheme = localStorage.getItem("theme");
const systemPrefersDark = window.matchMedia(
"(prefers-color-scheme: dark)"
).matches;
let currentTheme = savedTheme || (systemPrefersDark ? "dark" : "light");
body.setAttribute("data-theme", currentTheme);
updateThemeIcon(currentTheme);
themeToggle.addEventListener("click", () => {
currentTheme = currentTheme === "light" ? "dark" : "light";
body.setAttribute("data-theme", currentTheme);
localStorage.setItem("theme", currentTheme);
updateThemeIcon(currentTheme);
notify(`Switched to ${currentTheme} mode`, "success");
});
}
function updateThemeIcon(theme) {
const themeIcon = document.getElementById("themeIcon");
if (!themeIcon) return;
themeIcon.className = theme === "dark" ? "fas fa-sun" : "fas fa-moon";
}
// Notification function
function notify(message, type = "success", durationMs = 3000) {
showNotification(message, type);
}
// Show notification function
function showNotification(message, type = "info") {
const notificationDrawer =
document.querySelector(".notification-drawer") ||
createNotificationDrawer();
const notification = document.createElement("div");
notification.className = `notification ${type}`;
let icon = '<i class="fas fa-info-circle"></i>';
if (type === "success") {
icon = '<i class="fas fa-check-circle"></i>';
} else if (type === "error") {
icon = '<i class="fas fa-exclamation-circle"></i>';
} else if (type === "warning") {
icon = '<i class="fas fa-exclamation-triangle"></i>';
}
notification.innerHTML = `${icon} <span>${message}</span>`;
notificationDrawer.appendChild(notification);
// Remove notification after 3 seconds
setTimeout(() => {
notification.style.transform = "translateX(120%)";
notification.style.opacity = "0";
setTimeout(() => {
notification.remove();
}, 300);
}, 3000);
}
function createNotificationDrawer() {
const drawer = document.createElement("div");
drawer.className = "notification-drawer";
document.body.appendChild(drawer);
return drawer;
}
// Store translated address results when available
let storedTranslateResults = {
directTranslate: null,
recoverTranslate: null,
};
// Calculate the actual amount sent in a transaction
function calculateAmountSent(tx) {
if (tx.outputs.length <= 1) {
return tx.totalOutput;
}
const sortedOutputs = [...tx.outputs].sort((a, b) => b.value - a.value);
let amountSent = sortedOutputs[0].value;
if (
tx.inputs.length > 0 &&
tx.inputs[0].addresses &&
tx.inputs[0].addresses.length > 0
) {
const inputAddresses = new Set();
tx.inputs.forEach((input) => {
if (input.addresses) {
input.addresses.forEach((addr) => inputAddresses.add(addr));
}
});
let externalOutputs = tx.outputs.filter((output) => {
if (!output.addresses || output.addresses.length === 0)
return false;
return output.addresses.some((addr) => !inputAddresses.has(addr));
});
// If we found external outputs, sum them up
if (externalOutputs.length > 0) {
amountSent = externalOutputs.reduce(
(sum, output) => sum + output.value,
0
);
}
}
return amountSent;
}
function handleSharedLinks() {
// Check for hash parameters
if (window.location.hash) {
const hash = window.location.hash.substring(1); // Remove the # character
if (hash.startsWith("transactions")) {
const params = new URLSearchParams(hash.split("?")[1] || "");
const address = params.get("address");
const txid = params.get("tx");
// Show the transactions page first
showPage("transactionsPage");
if (address) {
document.getElementById("transactionAddr").value = address;
document.querySelector(
'input[name="searchType"][value="balance"]'
).checked = true;
toggleSearchType("balance");
notify("Loading shared address data...", "info");
setTimeout(() => loadTransactions(), 100);
} else if (txid) {
document.getElementById("txHash").value = txid;
document.querySelector(
'input[name="searchType"][value="transaction"]'
).checked = true;
toggleSearchType("transaction");
notify("Loading shared transaction data...", "info");
setTimeout(() => searchTransactionDetails(), 100);
}
}
}
}
// Navigation
function showPage(pageId) {
if (document.getElementById("directTranslateResult")) {
storedTranslateResults.directTranslate = document.getElementById(
"directTranslateResult"
).innerHTML;
}
if (document.getElementById("translateResult")) {
storedTranslateResults.recoverTranslate =
document.getElementById("translateResult").innerHTML;
}
document
.querySelectorAll(".sidebar .nav-link, .nav-btn")
.forEach((navItem) => {
navItem.classList.remove("active");
});
const sidebarLink = document.querySelector(
`.sidebar .nav-link[onclick*="${pageId}"]`
);
if (sidebarLink) {
sidebarLink.classList.add("active");
}
const mobileButton = document.querySelector(
`.nav-btn[data-page="${pageId}"]`
);
if (mobileButton) {
mobileButton.classList.add("active");
}
document
.querySelectorAll(".page")
.forEach((page) => page.classList.add("hidden"));
document.getElementById(pageId).classList.remove("hidden");
if (pageId === "transactionsPage") {
const radioInput = document.querySelector(
'input[name="searchType"]:checked'
);
const selectedType = radioInput ? radioInput.value : "balance";
toggleSearchType(selectedType);
} else {
const txElements = [
"txOutput",
"balanceHistoryResults",
"balanceSection",
"transactionSection",
];
txElements.forEach((id) => {
const element = document.getElementById(id);
if (element) element.style.display = "none";
});
}
}
function toggleSidebar() {
const sidebar = document.getElementById("sidebar");
const overlay = document.getElementById("sidebar-overlay");
sidebar.classList.toggle("active");
if (overlay) {
overlay.classList.toggle("active");
}
}
// Toggle password visibility
function togglePasswordVisibility(inputId) {
const input = document.getElementById(inputId);
const toggleBtn =
input.parentElement.querySelector(".password-toggle i");
if (input.type === "password") {
input.type = "text";
toggleBtn.className = "fas fa-eye-slash";
} else {
input.type = "password";
toggleBtn.className = "fas fa-eye";
}
}
// Clear input
function clearInput(inputId) {
const input = document.getElementById(inputId);
input.value = "";
input.focus();
}
// Helper function to create standardized error UI
function createErrorUI(title, message, retryAction = null) {
const retryButton = retryAction
? `<button class="btn btn-secondary" onclick="${retryAction}">
<i class="fas fa-redo"></i> Try Again
</button>`
: "";
return `
<div class="error-state">
<div class="error-icon">
<i class="fas fa-exclamation-triangle"></i>
</div>
<div class="error-message">
<h3>${title}</h3>
<p>${message}</p>
${retryButton}
</div>
</div>`;
switch (inputId) {
case "translateWIF":
document.getElementById("translateResult").innerHTML = "";
storedTranslateResults.recoverTranslate = null; // Also clear stored results
break;
case "addressToTranslate":
document.getElementById("directTranslateResult").innerHTML = "";
storedTranslateResults.directTranslate = null; // Also clear stored results
break;
case "balanceAddress":
document.getElementById("balanceResult").innerHTML = "";
break;
case "transactionAddr":
document.getElementById("balanceSection").style.display = "none";
document.getElementById("transactionSection").style.display =
"none";
// Clear transaction list
document.getElementById("txList").innerHTML = "";
document.getElementById("paginationInfo").innerText = "";
break;
case "historyAddr":
if (document.getElementById("txHistoryResult")) {
document.getElementById("txHistoryResult").innerHTML = "";
}
if (document.getElementById("txPagination")) {
document.getElementById("txPagination").style.display = "none";
}
if (document.getElementById("txPageInfo")) {
document.getElementById("txPageInfo").innerText = "";
}
break;
case "senderAddress":
case "privateKey":
case "receiverAddress":
case "sendAmount":
document.getElementById("sendResult").innerHTML = "";
break;
}
notify("Input cleared", "success", 1000);
}
function setButtonLoading(buttonId, isLoading) {
const button = document.getElementById(buttonId);
if (!button) return;
const btnText = button.querySelector(".btn-text");
const btnLoading = button.querySelector(".btn-loading");
if (isLoading) {
button.disabled = true;
if (btnText) btnText.style.display = "none";
if (btnLoading) btnLoading.style.display = "inline";
} else {
button.disabled = false;
if (btnText) btnText.style.display = "inline";
if (btnLoading) btnLoading.style.display = "none";
}
}
function generateMultiChain() {
setButtonLoading("generateBtn", true);
notify("Generating wallet addresses...", "success", 1500);
try {
const result = dogeCrypto.generateMultiChain();
let formattedResult = `
<div class="wallet-generated-success">
<div class="success-icon">
<i class="fas fa-check-circle"></i>
</div>
<div>
<h3>Addresses Generated Successfully!</h3>
<p>Your multi-blockchain addresses have been created. All addresses are derived from the same private key.</p>
</div>
</div>`;
formattedResult += `
<div class="blockchain-section">
<div class="blockchain-header">
<h4><i class="fas fa-coins"></i> Dogecoin (DOGE)</h4>
<div class="blockchain-badge primary">PRIMARY</div>
</div>
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> DOGE Address</label>
<div class="value-container">
<code>${result.DOGE.address}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${result.DOGE.address}').then(()=>notify('DOGE address copied','success'))" title="Copy DOGE Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="detail-row">
<label><i class="fas fa-key"></i> DOGE Private Key</label>
<div class="value-container">
<code>${result.DOGE.privateKey}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${result.DOGE.privateKey}').then(()=>notify('Private key copied','success'))" title="Copy Private Key">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>`;
for (const chain of ["FLO", "BTC", "LTC"]) {
if (result[chain]) {
formattedResult += `
<div class="blockchain-section">
<div class="blockchain-header">
<h4><i class="fas fa-coins"></i> ${chain}</h4>
<div class="blockchain-badge secondary">SECONDARY</div>
</div>
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> ${chain} Address</label>
<div class="value-container">
<code>${result[chain].address}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${result[chain].address}').then(()=>notify('${chain} address copied','success'))" title="Copy ${chain} Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="detail-row">
<label><i class="fas fa-key"></i> ${chain} Private Key</label>
<div class="value-container">
<code>${result[chain].privateKey}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${result[chain].privateKey}').then(()=>notify('Private key copied','success'))" title="Copy Private Key">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>`;
}
}
formattedResult += `
<div class="wallet-security-notice">
<div class="notice-icon">
<i class="fas fa-shield-alt"></i>
</div>
<div class="notice-content">
<h4>Security Reminder</h4>
<p>Keep your private key safe and secure. Never share it with anyone. Consider backing it up in a secure location.</p>
</div>
</div>`;
document.getElementById("multiResult").innerHTML = formattedResult;
notify("Addresses generated successfully!", "success");
} catch (err) {
document.getElementById("multiResult").innerHTML = `
<div class="error-state">
<div class="error-icon">
<i class="fas fa-exclamation-triangle"></i>
</div>
<h3>Generation Failed</h3>
<p>Error: ${err.message}</p>
<button class="btn btn-secondary" onclick="generateMultiChain()">
<i class="fas fa-redo"></i> Try Again
</button>
</div>`;
notify(err.message, "error");
} finally {
setButtonLoading("generateBtn", false);
}
}
// Function to validate private key format
function isValidPrivateKeyFormat(key) {
if (!key || typeof key !== "string") return false;
// Check length (most WIF private keys are 51-52 chars)
if (key.length < 50 || key.length > 55) return false;
// Check for invalid Base58 characters
const base58Regex =
/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/;
if (!base58Regex.test(key)) return false;
// Check for common prefixes for supported chains
// BTC: 5, K, L
// DOGE: 6, Q
// FLO: 6, R
// LTC: 6, T
const validPrefixes = ["5", "6", "K", "L", "Q", "R", "T"];
if (!validPrefixes.some((prefix) => key.startsWith(prefix))) {
return false;
}
return true;
}
function translateAddress() {
const wif = document.getElementById("translateWIF").value.trim();
if (!wif) {
notify("Please enter a private key", "error");
return;
}
// Validate private key format before attempting to use it
if (!isValidPrivateKeyFormat(wif)) {
document.getElementById("translateResult").innerHTML = createErrorUI(
"Invalid Private Key Format",
"The text entered doesn't appear to be a valid private key for DOGE, BTC, FLO, or LTC.<br>Please check your input and try again."
);
notify("Invalid private key format", "error");
return;
}
setButtonLoading("recoverBtn", true);
try {
const result = dogeCrypto.generateMultiChain(wif);
let formattedResult = `
<div class="wallet-generated-success">
<div class="success-icon">
<i class="fas fa-check-circle"></i>
</div>
<div class="success-message">
<h3>Addresses Recovered Successfully!</h3>
<p>All blockchain addresses have been recovered from your private key.</p>
</div>
</div>`;
for (const chain of ["DOGE", "FLO", "BTC", "LTC"]) {
if (result[chain]) {
const badgeClass = chain === "DOGE" ? "PRIMARY" : "SECONDARY";
const badgeStyle = chain === "DOGE" ? "primary" : "secondary";
formattedResult += `
<div class="blockchain-section">
<div class="blockchain-header">
<h4><i class="fas fa-coins"></i> ${chain}</h4>
<div class="blockchain-badge ${badgeStyle}">${badgeClass}</div>
</div>
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> ${chain} Address</label>
<div class="value-container">
<code>${result[chain].address}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${result[chain].address}').then(()=>notify('${chain} address copied','success'))" title="Copy ${chain} Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="detail-row">
<label><i class="fas fa-key"></i> ${chain} Private Key</label>
<div class="value-container">
<code>${result[chain].privateKey}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${result[chain].privateKey}').then(()=>notify('Private key copied','success'))" title="Copy Private Key">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>`;
}
}
document.getElementById("translateResult").innerHTML = formattedResult;
storedTranslateResults.recoverTranslate = formattedResult;
notify("Addresses recovered successfully!", "success");
} catch (err) {
console.error("Recovery error:", err);
let errorMessage = "Invalid private key or format not supported.";
if (err.message && err.message.includes("Invalid")) {
errorMessage = err.message;
}
document.getElementById("translateResult").innerHTML = `
<div class="error-state">
<div class="error-icon">
<i class="fas fa-exclamation-triangle"></i>
</div>
<div class="error-message">
<h3>Recovery Failed</h3>
<p>${errorMessage}</p>
<p>Please ensure you've entered a valid private key for DOGE, BTC, FLO, or LTC.</p>
<button class="btn btn-secondary" onclick="translateAddress()">
<i class="fas fa-redo"></i> Try Again
</button>
</div>
</div>`;
notify("Failed to recover addresses: " + errorMessage, "error");
} finally {
setButtonLoading("recoverBtn", false);
}
}
function translateDirectAddress() {
const address = document
.getElementById("addressToTranslate")
.value.trim();
if (!address) {
notify("Please enter an address", "error");
return;
}
// Basic validation for address format
const isValidFormat =
address.match(/^[A-Z0-9]{26,45}$/i) || address.startsWith("bc1");
if (!isValidFormat) {
document.getElementById("directTranslateResult").innerHTML =
createErrorUI(
"Invalid Address Format",
"The text entered doesn't appear to be a valid address for DOGE, BTC, FLO, or LTC.<br>Please check your input and try again."
);
notify("Invalid address format", "error");
return;
}
setButtonLoading("translateBtn", true);
try {
const result = dogeCrypto.translateAddress(address);
let formattedResult = `
<div class="wallet-generated-success ">
<div class="success-icon">
<i class="fas fa-check-circle"></i>
</div>
<div class="success-message">
<h3>Address Translated Successfully!</h3>
<p>Equivalent addresses on all supported blockchains are shown below.</p>
</div>
</div>`;
for (const chain of ["DOGE", "FLO", "BTC", "LTC"]) {
if (result[chain]) {
formattedResult += `
<div class="blockchain-section " style="animation-delay: ${
["DOGE", "FLO", "BTC", "LTC"].indexOf(chain) * 0.1
}s">
<div class="blockchain-header">
<h4><i class="fas fa-coins"></i> ${chain}</h4>
<div class="blockchain-badge">TRANSLATION</div>
</div>
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> ${chain} Address</label>
<div class="value-container">
<code>${result[chain]}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${
result[chain]
}').then(()=>notify('${chain} address copied','success'))" title="Copy ${chain} Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>`;
}
}
document.getElementById("directTranslateResult").innerHTML =
formattedResult;
storedTranslateResults.directTranslate = formattedResult;
notify("Address translated successfully!", "success");
} catch (err) {
document.getElementById("directTranslateResult").innerHTML =
createErrorUI(
"Translation Failed",
`Error: ${err.message}`,
"translateDirectAddress()"
);
notify("Failed to translate address: " + err.message, "error");
} finally {
setButtonLoading("translateBtn", false);
}
}
// Dogecoin API Functions
function checkDogeBalance() {
const input = document.getElementById("balanceAddress").value.trim();
if (!input) {
notify("Please enter a Dogecoin address or private key", "error");
return;
}
let address = input;
if (input.length >= 40 && !input.startsWith("D")) {
try {
// Attempt to derive address from private key
address = dogeCrypto.generateMultiChain(input).DOGE.address;
notify("Using address derived from private key", "success");
} catch (error) {
console.error(
"[ERROR] Failed to derive address from input:",
error
);
}
}
setButtonLoading("balanceBtn", true);
document.getElementById("balanceResult").innerHTML = `
<div class="loading-state">
<i class="fas fa-spinner fa-spin"></i>
<p>Loading balance...</p>
</div>
`;
dogeBlockchainAPI
.getBalance(address)
.then((balance) => {
document.getElementById("balanceResult").innerHTML = `
<div class="balance-result">
<div class="balance-header">
<i class="fas fa-check-circle" style="color: var(--success-color);"></i>
<h3>Balance Retrieved</h3>
</div>
<div class="balance-amount">
<span class="balance-value">${balance}</span>
<span class="balance-currency">DOGE</span>
</div>
<div class="balance-address">
<span class="address-label">Address:</span>
<span class="address-value">${address}</span>
</div>
<div class="balance-actions">
<button class="btn btn-secondary" onclick="navigator.clipboard.writeText('${address}').then(()=>notify('Address copied','success'))">
<i class="fas fa-copy"></i> Copy Address
</button>
<button class="btn btn-secondary" onclick="document.getElementById('txHistoryAddress').value='${address}'; showPage('historyPage'); getDogeTransactions();">
<i class="fas fa-history"></i> View Transactions
</button>
</div>
</div>
`;
notify(`Balance: ${balance} DOGE`, "success");
})
.catch((error) => {
console.error("Error using getBalance:", error);
document.getElementById("balanceResult").innerHTML = `
<div class="error-state">
<div class="error-icon">
<i class="fas fa-exclamation-triangle"></i>
</div>
<h3>Failed to retrieve balance</h3>
<p>Error: ${error.message || "Unknown error"}</p>
<button class="btn btn-secondary" onclick="checkDogeBalance()">
<i class="fas fa-redo"></i> Try Again
</button>
</div>
`;
notify("Failed to retrieve balance", "error");
})
.finally(() => {
setButtonLoading("balanceBtn", false);
});
}
function getBlockchainType(address) {
if (address.startsWith("D")) return "DOGE";
if (address.startsWith("F")) return "FLO";
if (address.startsWith("L")) return "LTC";
if (
address.startsWith("bc1") ||
address.startsWith("1") ||
address.startsWith("3")
)
return "BTC";
return "UNKNOWN";
}
function loadTransactions() {
const input = document.getElementById("transactionAddr").value.trim();
if (!input) {
notify("Please enter a blockchain address", "error");
return;
}
// Determine if the input is a valid address
let address = input;
let isDogeAddress = input.startsWith("D");
let isOtherSupportedAddress =
input.startsWith("F") ||
input.startsWith("L") ||
input.startsWith("bc1") ||
input.startsWith("1") ||
input.startsWith("3");
// Check if it's potentially a private key (which we don't want to accept)
if (
input.length >= 40 &&
(input.startsWith("5") ||
input.startsWith("6") ||
input.startsWith("K") ||
input.startsWith("L") ||
input.startsWith("Q") ||
input.startsWith("R") ||
input.startsWith("T"))
) {
document.getElementById("txList").innerHTML = createErrorUI(
"Invalid Input Type",
"Private keys are not accepted in the balance & history search. Please enter a valid blockchain address (DOGE, FLO, BTC, or LTC)."
);
notify("Private keys not allowed in this search", "error");
return;
}
// Check if address is a valid format
if (!isDogeAddress && !isOtherSupportedAddress) {
document.getElementById("txList").innerHTML = createErrorUI(
"Invalid Address Format",
"Please enter a valid blockchain address (DOGE, FLO, BTC, or LTC)."
);
notify("Invalid address format", "error");
return;
}
setButtonLoading("loadTransactions", true);
if (!isDogeAddress && isOtherSupportedAddress) {
try {
notify("Translating address to DOGE equivalent...", "info");
const translatedAddresses = dogeCrypto.translateAddress(input);
address = translatedAddresses.DOGE;
notify("Address translated: " + address, "success");
} catch (error) {
console.error("Translation error:", error);
document.getElementById("balanceSection").style.display = "none";
document.getElementById("transactionSection").style.display =
"none";
document.getElementById("txList").innerHTML = createErrorUI(
"Address Translation Failed",
`Error: ${
error.message ||
"Unable to translate address to DOGE equivalent"
}`,
"loadTransactions()"
);
notify("Failed to translate address: " + error.message, "error");
setButtonLoading("loadTransactions", false);
return;
}
}
// Reset pagination
currentTxOffset = 0;
currentTxAddress = address;
// First, load balance
document.getElementById("balanceSection").style.display = "none";
document.getElementById("transactionSection").style.display = "none";
// Get the search type
const searchType =
document.querySelector('input[name="searchType"]:checked')?.value ||
"balance";
dogeBlockchainAPI
.getBalance(address)
.then((balance) => {
// Set balance values
document.getElementById("balanceValue").textContent =
balance.toFixed(8);
const input = document
.getElementById("transactionAddr")
.value.trim();
if (input !== address) {
document.getElementById(
"displayedAddress"
).innerHTML = `${address}<br><small class="translated-from">translated from ${input}</small>`;
// Save to search history with source info (translated address)
searchedAddressDB.saveSearchedAddress(
address,
balance,
Date.now(),
{
originalAddress: input,
blockchain: getBlockchainType(input),
}
);
} else {
document.getElementById("displayedAddress").textContent = address;
// Save to search history
searchedAddressDB.saveSearchedAddress(address, balance);
}
document.getElementById("balanceSection").style.display = "block";
updateSearchedAddressesList();
fetchTransactionsWithPagination();
document
.getElementById("balanceSection")
.scrollIntoView({ behavior: "smooth" });
showNotification(`Balance: ${balance.toFixed(8)} DOGE`, "success");
})
.catch((error) => {
console.error("Balance error:", error);
showNotification(
"Failed to retrieve balance: " +
(error.message || "Unknown error"),
"error"
);
fetchTransactionsWithPagination();
});
}
function redirectToHistoryWithBalance() {
const address = document.getElementById("balanceAddress").value.trim();
if (!address) {
notify("Please enter a Dogecoin address", "error");
return;
}
document.getElementById("transactionAddr").value = address;
showPage("transactionsPage");
loadTransactions();
}
function loadDogeHistory() {
const address = document.getElementById("historyAddr").value.trim();
if (!address) {
notify("Please enter a Dogecoin address", "error");
return;
}
document.getElementById("transactionAddr").value = address;
showPage("transactionsPage");
loadTransactions();
}
function fetchTransactionsWithPagination() {
// Make sure the transaction section is visible
document.getElementById("transactionSection").style.display = "block";
document.getElementById("paginationInfo").innerText = "";
dogeBlockchainAPI
.getDogeTransactions(currentTxAddress, {
limit: txPerPage,
offset: currentTxOffset,
})
.then((result) => {
if (!result.transactions || result.transactions.length === 0) {
document.getElementById("txList").innerHTML = `
<div class="no-transactions">
<i class="fas fa-search"></i>
<h3>No Transactions Found</h3>
<p>No transactions were found for this Dogecoin address.</p>
</div>
`;
setButtonLoading("loadTransactions", false);
return;
}
totalTxCount = result.total || 0;
const currentPage = Math.floor(currentTxOffset / txPerPage) + 1;
const totalPages = Math.ceil(totalTxCount / txPerPage);
let transactionsHTML = "";
result.transactions.forEach((tx) => {
let numericValue = parseFloat(tx.value);
let valueDisplay = "";
let txType = "";
let txClass = "";
if (numericValue > 0) {
// Coins received
valueDisplay = "+" + numericValue.toFixed(8) + " DOGE";
txType = "Received";
txClass = "incoming";
} else if (numericValue < 0) {
// Coins sent
valueDisplay = numericValue.toFixed(8) + " DOGE";
txType = "Sent";
txClass = "outgoing";
} else {
valueDisplay = numericValue.toFixed(8) + " DOGE (fee)";
txType = "Self / Fee";
txClass = "";
}
const txDate =
tx.formattedTime || new Date(tx.time * 1000).toLocaleString();
transactionsHTML += `
<div class="transaction-card ${txClass}" data-tx-type="${txType.toLowerCase()}">
<div class="tx-main">
<div class="tx-icon">
<i class="fas ${
txClass === "incoming" ? "fa-arrow-down" : "fa-arrow-up"
}"></i>
</div>
<div class="tx-info">
<div class="tx-header">
<div class="tx-direction">
${txType}
</div>
<div class="tx-right-header">
<span class="tx-date">${txDate}</span>
<span class="tx-status ${
tx.confirmations > 0 ? "success" : "pending"
}">
${tx.confirmations > 0 ? "CONFIRMED" : "PENDING"}
</span>
</div>
</div>
<div class="tx-amount ${txClass}">${valueDisplay}</div>
</div>
</div>
<div class="tx-addresses">
<div class="tx-field">
<div class="tx-label">From:</div>
<div class="tx-value">
${
tx.fromAddresses && tx.fromAddresses.length > 0
? tx.fromAddresses[0]
: "Unknown sender"
}
</div>
</div>
<div class="tx-field">
<div class="tx-label">To:</div>
<div class="tx-value">
${
tx.toAddresses && tx.toAddresses.length > 0
? (() => {
// Filter out duplicate addresses
const filteredAddresses = tx.toAddresses.filter(
(addr) => {
return (
!tx.fromAddresses ||
!tx.fromAddresses.includes(addr)
);
}
);
if (filteredAddresses.length > 0) {
return filteredAddresses[0];
} else if (tx.type === "self") {
return "Same as sender (self-transaction)";
} else {
return "Unknown recipient";
}
})()
: "Unknown recipient"
}
</div>
</div>
<div class="tx-field">
<div class="tx-label">Tx:</div>
<div class="tx-value tx-hash-value">
<a href="#" class="txid-link" onclick="viewTransactionDetails('${tx.txid}'); return false;">${tx.txid.substring(0, 12)}...${tx.txid.substring(tx.txid.length - 12)}</a>
</div>
</div>
</div>
<div class="confirmation-count">
${tx.confirmations} confirmation${
tx.confirmations !== 1 ? "s" : ""
}
</div>
</div>
`;
});
document.getElementById("txList").innerHTML = transactionsHTML;
document.getElementById("paginationInfo").innerHTML = `Showing ${
currentTxOffset + 1
}-${Math.min(
currentTxOffset + result.transactions.length,
totalTxCount
)} of ${totalTxCount} transactions`;
updatePaginationControls();
notify("Transactions loaded successfully", "success");
})
.catch((error) => {
console.error("Transaction error:", error);
document.getElementById("txList").innerHTML = `
<div class="error-state">
<div class="error-icon">
<i class="fas fa-exclamation-triangle"></i>
</div>
<h3>Failed to fetch transactions</h3>
<p>Error: ${error.message || "Unknown error"}</p>
<button class="btn btn-secondary" onclick="loadTransactions()">
<i class="fas fa-redo"></i> Try Again
</button>
</div>
`;
notify("Failed to fetch transactions", "error");
})
.finally(() => {
setButtonLoading("loadTransactions", false);
});
}
function filterTransactions(filter) {
document.querySelectorAll(".filter-btn").forEach((btn) => {
btn.classList.remove("active");
});
document
.querySelector(`.filter-btn[data-filter="${filter}"]`)
.classList.add("active");
// Filter transactions
const txCards = document.querySelectorAll(".transaction-card");
txCards.forEach((card) => {
const txType = card.getAttribute("data-tx-type").toLowerCase();
if (
filter === "all" ||
(filter === "received" && txType === "received") ||
(filter === "sent" && txType === "sent")
) {
card.style.display = "block";
} else {
card.style.display = "none";
}
});
}
// Function to show confirmation popup
function showConfirmationPopup(
senderAddress,
receiverAddress,
amount,
onConfirm
) {
document.getElementById("confirmAmount").textContent = `${amount} DOGE`;
document.getElementById("confirmFrom").textContent = senderAddress;
document.getElementById("confirmTo").textContent = receiverAddress;
const confirmBtn = document.getElementById("confirmSendBtn");
const newConfirmBtn = confirmBtn.cloneNode(true);
confirmBtn.parentNode.replaceChild(newConfirmBtn, confirmBtn);
newConfirmBtn.addEventListener("click", function () {
closeConfirmationPopup();
onConfirm();
});
// Show the popup
document.getElementById("confirmationPopup").style.display = "block";
document.body.style.overflow = "hidden";
}
// Function to close confirmation popup
function closeConfirmationPopup() {
document.getElementById("confirmationPopup").style.display = "none";
document.body.style.overflow = "auto";
}
async function updateSearchedAddressesList() {
try {
const searchedAddresses =
await searchedAddressDB.getSearchedAddresses();
displaySearchedAddresses(searchedAddresses);
} catch (error) {
console.error("Error loading searched addresses:", error);
}
}
function displaySearchedAddresses(addresses) {
const container = document.getElementById("searchedAddressesContainer");
const list = document.getElementById("searchedAddressesList");
if (!container || !list) return;
if (addresses.length === 0) {
container.style.display = "none";
return;
}
container.style.display = "block";
let html = "";
addresses.forEach((addr, index) => {
// Check if this was translated from another blockchain
const hasSourceInfo =
addr.sourceInfo && addr.sourceInfo.originalAddress !== addr.address;
html += `
<div class="searched-address-item ${
hasSourceInfo ? "has-source-info" : ""
}" data-index="${index}" data-current-type="${
hasSourceInfo ? addr.sourceInfo.blockchain.toLowerCase() : "doge"
}">
${
hasSourceInfo
? `
<div class="address-toggle-section">
<div class="address-toggle-group">
<button onclick="toggleAddressType(${index}, '${addr.sourceInfo.blockchain.toLowerCase()}')"
class="btn-toggle-address active"
data-type="${addr.sourceInfo.blockchain.toLowerCase()}"
title="Show ${addr.sourceInfo.blockchain} Address">
${addr.sourceInfo.blockchain}
</button>
<button onclick="toggleAddressType(${index}, 'doge')"
class="btn-toggle-address"
data-type="doge"
title="Show DOGE Address">
DOGE
</button>
</div>
</div>
<div class="address-content-wrapper">
<div class="address-info">
<div class="address-display">
<div class="address-text" id="address-display-${index}" title="${
addr.sourceInfo.originalAddress
}">
${addr.sourceInfo.originalAddress}
</div>
</div>
</div>
<div class="address-actions">
<button onclick="copyCurrentAddress(${index})" class="btn-copy-current" title="Copy Selected Address">
<i class="fas fa-copy"></i>
</button>
<button onclick="deleteSearchedAddress('${
addr.address
}')" class="btn-delete" title="Delete">
<i class="fas fa-trash"></i>
</button>
<button onclick="recheckBalance('${
addr.address
}')" class="btn-check" title="Check balance">
<i class="fas fa-search"></i>
</button>
</div>
</div>
`
: `
<div class="address-info">
<div class="address-display">
<div class="address-text" id="address-display-${index}" title="${addr.address}">
${addr.address}
</div>
</div>
</div>
<div class="address-actions">
<button onclick="copyAddressToClipboard('${addr.address}')" class="btn-copy" title="Copy DOGE Address">
<i class="fas fa-copy"></i>
</button>
<button onclick="deleteSearchedAddress('${addr.address}')" class="btn-delete" title="Delete">
<i class="fas fa-trash"></i>
</button>
<button onclick="recheckBalance('${addr.address}')" class="btn-check" title="Check balance">
<i class="fas fa-search"></i>
</button>
</div>
`
}
</div>
`;
});
list.innerHTML = html;
}
// toggle between address types in searched addresses
async function toggleAddressType(addressIndex, type) {
try {
const addresses = await searchedAddressDB.getSearchedAddresses();
if (!addresses[addressIndex]) return;
const addressItem = addresses[addressIndex];
const container = document.querySelector(
`[data-index="${addressIndex}"]`
);
if (!container) return;
const toggleButtons = container.querySelectorAll(
".btn-toggle-address"
);
toggleButtons.forEach((btn) => btn.classList.remove("active"));
const activeButton = container.querySelector(`[data-type="${type}"]`);
if (activeButton) {
activeButton.classList.add("active");
}
container.setAttribute("data-current-type", type);
const addressDisplay = container.querySelector(
`#address-display-${addressIndex}`
);
if (addressDisplay) {
if (type === "doge") {
// Show DOGE address
addressDisplay.textContent = addressItem.address;
addressDisplay.title = addressItem.address;
} else {
// Show original blockchain address (FLO/BTC/LTC)
const originalAddress =
addressItem.sourceInfo?.originalAddress || addressItem.address;
addressDisplay.textContent = originalAddress;
addressDisplay.title = originalAddress;
}
}
} catch (error) {
console.error("Error toggling address type:", error);
}
}
async function copyCurrentAddress(addressIndex) {
try {
const addresses = await searchedAddressDB.getSearchedAddresses();
if (!addresses[addressIndex]) return;
const addressItem = addresses[addressIndex];
const container = document.querySelector(
`[data-index="${addressIndex}"]`
);
if (!container) return;
const currentType =
container.getAttribute("data-current-type") || "doge";
let addressToCopy;
let addressLabel;
if (currentType === "doge") {
addressToCopy = addressItem.address;
addressLabel = "DOGE address";
} else {
addressToCopy =
addressItem.sourceInfo?.originalAddress || addressItem.address;
addressLabel = `${
addressItem.sourceInfo?.blockchain || "Original"
} address`;
}
await copyAddressToClipboard(addressToCopy, addressLabel);
} catch (error) {
console.error("Error copying current address:", error);
notify("Failed to copy address", "error");
}
}
async function deleteSearchedAddress(address) {
try {
await searchedAddressDB.deleteSearchedAddress(address);
await updateSearchedAddressesList();
notify("Address removed from history", "success");
} catch (error) {
console.error("Error deleting searched address:", error);
notify("Failed to remove address", "error");
}
}
async function clearAllSearchedAddresses() {
try {
await searchedAddressDB.clearAllSearchedAddresses();
await updateSearchedAddressesList();
notify("All searched addresses cleared", "success");
} catch (error) {
console.error("Error clearing searched addresses:", error);
notify("Failed to clear addresses", "error");
}
}
async function copyAddressToClipboard(address, label = "Address") {
try {
await navigator.clipboard.writeText(address);
notify(`${label} copied to clipboard`, "success");
} catch (error) {
console.error("Error copying to clipboard:", error);
notify("Failed to copy address", "error");
}
}
async function recheckBalance(address) {
document.getElementById("transactionAddr").value = address;
loadTransactions();
}
// Send Dogecoin using RPC method
function sendDogeRPC() {
let privateKey = document.getElementById("privateKey").value.trim();
const receiverAddress = document
.getElementById("receiverAddress")
.value.trim();
const sendAmount = parseFloat(
document.getElementById("sendAmount").value
);
// Automatically derive the sender address from private key
let senderAddress;
try {
senderAddress =
dogeCrypto.generateMultiChain(privateKey).DOGE.address;
} catch (error) {
notify("Invalid private key format", "error");
return;
}
let dogeprivkey =
dogeCrypto.generateMultiChain(privateKey).DOGE.privateKey;
if (!privateKey || !receiverAddress) {
notify("Please fill all required fields", "error");
return;
}
if (isNaN(sendAmount) || sendAmount <= 0) {
notify("Please enter a valid amount", "error");
return;
}
// Show confirmation popup instead of proceeding directly
showConfirmationPopup(
senderAddress,
receiverAddress,
sendAmount,
function () {
setButtonLoading("sendBtn", true);
dogeBlockchainAPI
.sendDogecoinRPC(
senderAddress,
receiverAddress,
sendAmount,
dogeprivkey
)
.then((txid) => {
document.getElementById("sendResult").innerHTML = `
<div class="transaction-success">
<div class="success-animation">
<div class="checkmark-circle">
<div class="checkmark-circle-bg"></div>
<i class="fas fa-check checkmark"></i>
</div>
</div>
<h3>Transaction Successful!</h3>
<p class="success-message">Your DOGE have been successfully sent.</p>
<div class="tx-details-card">
<div class="tx-detail-item">
<div class="tx-detail-label"><i class="fas fa-coins"></i> Amount</div>
<div class="tx-detail-value">
<span>${sendAmount} DOGE</span>
</div>
</div>
<div class="tx-detail-item">
<div class="tx-detail-label"><i class="fas fa-wallet"></i> From</div>
<div class="tx-detail-value">
<span>${senderAddress}</span>
<button class="copy-small" onclick="navigator.clipboard.writeText('${senderAddress}').then(()=>notify('Sender address copied','success'))" title="Copy Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="tx-detail-item">
<div class="tx-detail-label"><i class="fas fa-user"></i> To</div>
<div class="tx-detail-value">
<span>${receiverAddress}</span>
<button class="copy-small" onclick="navigator.clipboard.writeText('${receiverAddress}').then(()=>notify('Recipient address copied','success'))" title="Copy Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="tx-detail-item">
<div class="tx-detail-label"><i class="fas fa-hashtag"></i> Transaction ID</div>
<div class="tx-detail-value">
<span><a href="#" class="txid-link" onclick="viewTransactionDetails('${txid}'); return false;">${txid}</a></span>
<button class="copy-small" onclick="navigator.clipboard.writeText('${txid}').then(()=>notify('Transaction ID copied','success'))" title="Copy Transaction ID">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
</div>
`;
notify("Transaction successful!", "success");
document.getElementById("sendAmount").value = "";
})
.catch((error) => {
console.error("Transaction error:", error);
document.getElementById("sendResult").innerHTML =
createTransactionErrorUI(
"Transaction Failed",
"Your DOGE transaction could not be processed.",
`Error Details: ${
error.message || "Unknown error"
} (${new Date().toLocaleString()})`,
"sendDogeRPC()"
);
notify(
"Transaction failed: " + (error.message || "Unknown error"),
"error"
);
})
.finally(() => {
setButtonLoading("sendBtn", false);
});
}
);
}
function createTransactionErrorUI(
errorTitle,
errorMessage,
errorDetails = null,
retryAction = null
) {
const retryButton = retryAction
? `<button class="btn btn-secondary" onclick="${retryAction}">
<i class="fas fa-redo"></i> Try Again
</button>`
: "";
const errorDetailsSection = errorDetails
? `<div class="error-details">${errorDetails}</div>`
: "";
return `
<div class="transaction-error">
<div class="error-animation">
<div class="error-circle">
<i class="fas fa-times error-icon"></i>
</div>
</div>
<h3>${errorTitle}</h3>
<p class="error-message">${errorMessage}</p>
${errorDetailsSection}
<div class="tx-actions">
${retryButton}
</div>
</div>
`;
}
// Pagination functions for transaction history
function loadPreviousTransactions() {
if (currentTxOffset >= txPerPage) {
currentTxOffset -= txPerPage;
document
.getElementById("transactionSection")
.scrollIntoView({ behavior: "smooth" });
fetchTransactionsWithPagination();
}
}
function loadNextTransactions() {
if (currentTxOffset + txPerPage < totalTxCount) {
currentTxOffset += txPerPage;
document
.getElementById("transactionSection")
.scrollIntoView({ behavior: "smooth" });
fetchTransactionsWithPagination();
}
}
function updatePaginationControls() {
const currentPage = Math.floor(currentTxOffset / txPerPage) + 1;
const totalPages = Math.ceil(totalTxCount / txPerPage);
// Generate page numbers
let pageNumbersHTML = "";
const displayMax = 5; // Maximum number of page numbers to display
const startPage = Math.max(1, currentPage - Math.floor(displayMax / 2));
const endPage = Math.min(totalPages, startPage + displayMax - 1);
if (startPage > 1) {
pageNumbersHTML += `
<button class="page-number" onclick="goToPage(1)">1</button>
${startPage > 2 ? '<span class="page-ellipsis">...</span>' : ""}
`;
}
for (let i = startPage; i <= endPage; i++) {
pageNumbersHTML += `
<button class="page-number ${i === currentPage ? "active" : ""}"
onclick="goToPage(${i})">${i}</button>
`;
}
if (endPage < totalPages) {
pageNumbersHTML += `
${
endPage < totalPages - 1
? '<span class="page-ellipsis">...</span>'
: ""
}
<button class="page-number" onclick="goToPage(${totalPages})">${totalPages}</button>
`;
}
document.getElementById("pageNumbers").innerHTML = pageNumbersHTML;
// Enable/disable buttons based on current position
document.getElementById("prevBtn").disabled = currentTxOffset === 0;
document.getElementById("nextBtn").disabled =
currentTxOffset + txPerPage >= totalTxCount;
}
// Go to specific page
function goToPage(page) {
currentTxOffset = (page - 1) * txPerPage;
document
.getElementById("transactionSection")
.scrollIntoView({ behavior: "smooth" });
fetchTransactionsWithPagination();
}
function shareAddress() {
const addressElement = document.getElementById("displayedAddress");
let address;
if (
addressElement.firstChild &&
addressElement.firstChild.nodeType === Node.TEXT_NODE
) {
address = addressElement.firstChild.textContent.trim();
} else {
const fullText = addressElement.textContent;
address = fullText.split("translated from")[0].trim();
}
if (!address) {
notify("No address to share", "error");
return;
}
const url = new URL(window.location.href);
url.hash = `#transactions?address=${address}`;
copyToClipboard(url.toString(), "Address link");
}
function shareTransactionLink(txid) {
if (!txid) {
notify("No transaction ID to share", "error");
return;
}
const url = new URL(window.location.href);
url.hash = `#transactions?tx=${txid}`;
copyToClipboard(url.toString(), "Transaction link");
}
// Copy to clipboard function
function copyToClipboard(url, type) {
navigator.clipboard
.writeText(url)
.then(() => {
notify(`${type} copied to clipboard!`, "success");
})
.catch((err) => {
console.error("Clipboard error:", err);
notify("Unable to copy link", "error");
});
}
document.addEventListener("DOMContentLoaded", function () {
const perPageSelect = document.getElementById("perPageSelect");
if (perPageSelect) {
perPageSelect.addEventListener("change", function () {
txPerPage = parseInt(this.value, 10);
currentTxOffset = 0;
if (currentTxAddress) {
fetchTransactionsWithPagination();
}
});
}
});
// Search Transaction Details by TXID
function searchTransactionDetails() {
const txid = document.getElementById("txHash").value.trim();
if (!txid) {
notify("Please enter a transaction ID", "error");
return;
}
setButtonLoading("txSearchBtn", true);
// First, ensure the txOutput element exists
let txOutputElement = document.getElementById("txOutput");
if (!txOutputElement) {
txOutputElement = document.createElement("div");
txOutputElement.id = "txOutput";
txOutputElement.className = "transaction-result";
document
.getElementById("transactionsPage")
.appendChild(txOutputElement);
}
txOutputElement.style.display = "block";
if (document.getElementById("balanceResult")) {
document.getElementById("balanceResult").innerHTML = "";
}
dogeBlockchainAPI
.getTransactionDetails(txid)
.then((tx) => {
const fee = (tx.totalInput - tx.totalOutput).toFixed(8);
const txTime = tx.blockTime || "Pending";
const txStatus = tx.confirmations > 0 ? "Confirmed" : "Pending";
const txStatusClass = tx.confirmations > 0 ? "success" : "pending";
let txDetailsHTML = `
<div class="card transaction-details">
<div class="transaction-header">
<h3><i class="fas fa-exchange-alt"></i> Transaction Details</h3>
<button onclick="shareTransactionLink('${
tx.txid
}')" class="btn-icon share-btn" title="Copy Transaction Link">
<i class="fas fa-share-alt"></i>
</button>
</div>
<div class="tx-main-info">
<div class="tx-detail-row">
<label>Transaction ID:</label>
<div class="tx-detail-value">
<span>${tx.txid}</span>
<button onclick="navigator.clipboard.writeText('${
tx.txid
}').then(()=>notify('Transaction ID copied','success'))" class="copy-btn" title="Copy TX ID">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="tx-detail-row">
<label>Status:</label>
<div class="tx-detail-value">
<span class="tx-status ${txStatusClass}">${txStatus}</span>
<span class="confirmations">${
tx.confirmations
} confirmation${tx.confirmations !== 1 ? "s" : ""}</span>
</div>
</div>
<div class="tx-detail-row">
<label>Timestamp:</label>
<div class="tx-detail-value">${txTime}</div>
</div>
<div class="tx-detail-row">
<label>Block:</label>
<div class="tx-detail-value">
${tx.blockHeight || "Unconfirmed"}
${
tx.blockHash
? `<span class="tx-blockhash">(${tx.blockHash.substring(
0,
10
)}...)</span>`
: ""
}
</div>
</div>
<div class="tx-detail-row">
<label>Size:</label>
<div class="tx-detail-value">${tx.size} bytes</div>
</div>
<div class="tx-detail-row">
<label>Fee:</label>
<div class="tx-detail-value">${fee} DOGE</div>
</div>
<div class="tx-detail-row">
<label>Total Input:</label>
<div class="tx-detail-value">${tx.totalInput.toFixed(
8
)} DOGE</div>
</div>
<div class="tx-detail-row">
<label>Total Output:</label>
<div class="tx-detail-value">${tx.totalOutput.toFixed(
8
)} DOGE</div>
</div>
<div class="tx-detail-row highlight">
<label>Amount Sent:</label>
<div class="tx-detail-value">${calculateAmountSent(
tx
).toFixed(8)} DOGE</div>
</div>
</div>
<!-- Transaction Inputs -->
<div class="tx-section">
<h4>Inputs (${tx.inputs.length})</h4>
<div class="tx-inputs">`;
// Add inputs
tx.inputs.forEach((input, index) => {
txDetailsHTML += `
<div class="tx-io-item">
<div class="tx-io-header">
<span class="tx-io-index">#${index}</span>
<span class="tx-io-value">${input.value.toFixed(
8
)} DOGE</span>
</div>
<div class="tx-io-addresses">`;
if (input.addresses && input.addresses.length > 0) {
input.addresses.forEach((addr) => {
txDetailsHTML += `
<div class="tx-io-address">
<span class="address-text">${addr}</span>
<button onclick="navigator.clipboard.writeText('${addr}').then(()=>notify('Address copied','success'))" class="copy-btn" title="Copy Address">
<i class="fas fa-copy"></i>
</button>
</div>`;
});
} else {
txDetailsHTML += `<div class="tx-io-address">No address data available</div>`;
}
txDetailsHTML += `
</div>
</div>`;
});
txDetailsHTML += `
</div>
</div>
<!-- Transaction Outputs -->
<div class="tx-section">
<h4>Outputs (${tx.outputs.length})</h4>
<div class="tx-outputs">`;
// Add outputs
tx.outputs.forEach((output, index) => {
txDetailsHTML += `
<div class="tx-io-item">
<div class="tx-io-header">
<span class="tx-io-index">#${output.n}</span>
<span class="tx-io-value">${output.value.toFixed(
8
)} DOGE</span>
</div>
<div class="tx-io-addresses">`;
if (output.addresses && output.addresses.length > 0) {
output.addresses.forEach((addr) => {
txDetailsHTML += `
<div class="tx-io-address">
<span class="address-text">${addr}</span>
<button onclick="navigator.clipboard.writeText('${addr}').then(()=>notify('Address copied','success'))" class="copy-btn" title="Copy Address">
<i class="fas fa-copy"></i>
</button>
</div>`;
});
} else {
txDetailsHTML += `<div class="tx-io-address">No address data available</div>`;
}
txDetailsHTML += `
</div>
</div>`;
});
txDetailsHTML += `
</div>
</div>
<!-- Transaction actions section removed as the share button is now in the header -->
</div>
`;
let txOutputElement = document.getElementById("txOutput");
if (!txOutputElement) {
txOutputElement = document.createElement("div");
txOutputElement.id = "txOutput";
txOutputElement.className = "transaction-result";
document
.getElementById("transactionsPage")
.appendChild(txOutputElement);
}
txOutputElement.innerHTML = txDetailsHTML;
notify("Transaction details loaded successfully", "success");
})
.catch((error) => {
let txOutputElement = document.getElementById("txOutput");
if (!txOutputElement) {
txOutputElement = document.createElement("div");
txOutputElement.id = "txOutput";
txOutputElement.className = "transaction-result";
document
.getElementById("transactionsPage")
.appendChild(txOutputElement);
}
txOutputElement.innerHTML = createErrorUI(
"Transaction Search Failed",
`Error: ${error.message || "Unable to find transaction"}`,
"searchTransactionDetails()"
);
notify("Failed to find transaction: " + error.message, "error");
})
.finally(() => {
setButtonLoading("txSearchBtn", false);
});
}
// Function to toggle between balance search and transaction search
function toggleSearchType(type) {
const transactionsPage = document.getElementById("transactionsPage");
if (transactionsPage.classList.contains("hidden")) {
return;
}
const radioContainers = document.querySelectorAll(
".radio-button-container"
);
const radioInput = document.querySelector(
`input[name="searchType"][value="${type}"]`
);
if (radioInput) {
radioInput.checked = true;
}
radioContainers.forEach((container) => {
container.classList.remove("active");
if (container.querySelector(`input[value="${type}"]`)) {
container.classList.add("active");
}
});
let txOutputElement = document.getElementById("txOutput");
if (!txOutputElement) {
txOutputElement = document.createElement("div");
txOutputElement.id = "txOutput";
txOutputElement.className = "transaction-result";
document
.getElementById("transactionsPage")
.appendChild(txOutputElement);
}
if (type === "balance") {
const elements = {
balanceHistorySearch: document.getElementById(
"balanceHistorySearch"
),
transactionSearch: document.getElementById("transactionSearch"),
balanceHistoryResults: document.getElementById(
"balanceHistoryResults"
),
balanceSection: document.getElementById("balanceSection"),
transactionSection: document.getElementById("transactionSection"),
};
if (elements.balanceHistorySearch)
elements.balanceHistorySearch.style.display = "block";
if (elements.transactionSearch)
elements.transactionSearch.style.display = "none";
if (elements.balanceHistoryResults)
elements.balanceHistoryResults.style.display = "block";
txOutputElement.style.display = "none";
txOutputElement.innerHTML = "";
const isClickEvent =
typeof event !== "undefined" && event && event.type === "click";
if (isClickEvent) {
if (elements.balanceSection)
elements.balanceSection.style.display = "none";
if (elements.transactionSection)
elements.transactionSection.style.display = "none";
}
} else if (type === "transaction") {
const elements = {
balanceHistorySearch: document.getElementById(
"balanceHistorySearch"
),
transactionSearch: document.getElementById("transactionSearch"),
balanceHistoryResults: document.getElementById(
"balanceHistoryResults"
),
balanceSection: document.getElementById("balanceSection"),
transactionSection: document.getElementById("transactionSection"),
txHash: document.getElementById("txHash"),
};
if (elements.balanceHistorySearch)
elements.balanceHistorySearch.style.display = "none";
if (elements.transactionSearch)
elements.transactionSearch.style.display = "block";
if (elements.balanceHistoryResults)
elements.balanceHistoryResults.style.display = "none";
txOutputElement.style.display = "block";
if (elements.balanceSection)
elements.balanceSection.style.display = "none";
if (elements.transactionSection)
elements.transactionSection.style.display = "none";
const isClickEvent =
typeof event !== "undefined" && event && event.type === "click";
if (isClickEvent || !document.getElementById("txHash").value) {
setTimeout(() => {
if (elements.txHash) elements.txHash.focus();
}, 100);
}
}
const balanceHistorySearch = document.getElementById(
"balanceHistorySearch"
);
const transactionSearch = document.getElementById("transactionSearch");
const balanceHistoryResults = document.getElementById(
"balanceHistoryResults"
);
}
document.addEventListener("keydown", function (event) {
if (
event.key === "Enter" &&
document.getElementById("txHash") === document.activeElement
) {
event.preventDefault();
const transactionRadio = document.querySelector(
'input[name="searchType"][value="transaction"]'
);
if (transactionRadio) {
transactionRadio.checked = true;
toggleSearchType("transaction");
}
searchTransactionDetails();
}
if (
event.key === "Enter" &&
document.getElementById("balanceAddress") === document.activeElement
) {
checkDogeBalance();
event.preventDefault();
}
if (
event.key === "Enter" &&
document.getElementById("transactionAddr") === document.activeElement
) {
loadTransactions();
event.preventDefault();
}
});
function updateSenderAddress() {
const privateKey = document.getElementById("privateKey").value.trim();
const senderAddressField = document.getElementById("senderAddress");
if (!privateKey) {
senderAddressField.value = "";
return;
}
try {
const derivedAddress =
dogeCrypto.generateMultiChain(privateKey).DOGE.address;
senderAddressField.value = derivedAddress;
} catch (error) {
console.error(
"[WALLET] Error deriving address from private key:",
error
);
senderAddressField.value = "Invalid private key format";
}
}
document.addEventListener("DOMContentLoaded", function () {
const confirmationPopup = document.getElementById("confirmationPopup");
if (confirmationPopup) {
confirmationPopup.addEventListener("click", function (event) {
if (event.target === confirmationPopup) {
closeConfirmationPopup();
}
});
}
// Ensure txOutput element exists
if (!document.getElementById("txOutput")) {
const txOutputElement = document.createElement("div");
txOutputElement.id = "txOutput";
txOutputElement.className = "transaction-result";
document
.getElementById("transactionsPage")
.appendChild(txOutputElement);
}
updateSearchedAddressesList();
initializeTheme();
// Show loading screen
const loadingScreen = document.createElement("div");
loadingScreen.className = "loading-screen";
loadingScreen.innerHTML = `
<div class="loading-content">
<div class="loading-spinner"></div>
<h3>Loading Dogecoin Wallet</h3>
</div>
`;
document.body.appendChild(loadingScreen);
// Create sidebar overlay for mobile
const overlay = document.createElement("div");
overlay.id = "sidebarOverlay";
overlay.className = "sidebar-overlay";
overlay.addEventListener("click", () => {
document.getElementById("sidebar").classList.remove("active");
overlay.classList.remove("active");
});
document.body.appendChild(overlay);
setTimeout(() => {
loadingScreen.style.opacity = "0";
setTimeout(() => {
loadingScreen.remove();
handleSharedLinks();
if (!window.location.hash) {
showPage("walletPage");
}
}, 500);
}, 1500);
});
function viewTransactionDetails(txid) {
const transactionRadio = document.querySelector('input[name="searchType"][value="transaction"]');
if (transactionRadio) {
transactionRadio.checked = true;
toggleSearchType("transaction");
}
// Show the transactions page
showPage("transactionsPage");
// Set the txid in the search input
document.getElementById("txHash").value = txid;
// Search for the transaction
searchTransactionDetails();
}
</script>
</body>
</html>