litecoinwallet/index.html
void-57 0ec93dd7d2
Some checks failed
Workflow push to Dappbundle / Build (push) Has been cancelled
feat(ltc): Implement dynamic fee estimation and fix send functionality
- Added "View on Explorer" links for transactions.
- Enhanced UI validation for send amounts and balances.
- Optimized transaction confirmation popup with estimated fee display.
2026-01-28 03:34:27 +05:30

2689 lines
102 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 Litecoin Web Wallet" />
<meta name="description"
content="RanchiMall Litecoin Web Wallet - Generate multi-blockchain addresses, send LTC, view transactions and manage your assets." />
<meta property="og:type" content="website" />
<meta property="og:title" content="RanchiMall Litecoin Web Wallet" />
<meta property="og:description"
content="RanchiMall Litecoin Web Wallet - Generate multi-blockchain addresses, send LTC, view transactions and manage your assets." />
<title>Litecoin Wallet</title>
<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="litecoin/lib.litecoin.js"></script>
<script src="litecoin/ltcCrypto.js"></script>
<script src="litecoin/ltcBlockchainAPI.js"></script>
<script src="litecoin/ltcSearchDB.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.00002 LTC (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">Litecoin 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 LTC, DOGE, FLO, and BTC 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 LTC, DOGE, FLO
and BTC 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 (LTC, DOGE, FLO, BTC) from a
single private key
</p>
</div>
<div class="card">
<div class="form-group">
<label><i class="fas fa-key"></i> Private Key
(LTC/DOGE/BTC/FLO):</label>
<div class="input-with-actions">
<input id="translateWIF" class="form-input" type="password"
placeholder="Enter LTC / DOGE / BTC / FLO 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 (LTC, DOGE, BTC, FLO).
</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 (LTC, DOGE, FLO, BTC)</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 LTC / DOGE / BTC / FLO 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> Litecoin Transactions</h2>
<p>
Check balance and transaction history for any Litecoin address
(Legacy L/M or SegWit ltc1)
</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> LTC/DOGE/BTC/FLO
address:</label>
<div class="input-with-actions">
<input id="transactionAddr" class="form-input" placeholder="Enter LTC/DOGE/BTC/FLO 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">LTC</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 LTC</h2>
<p>Send Litecoin to any address</p>
</div>
<div class="card">
<div class="form-group">
<label><i class="fas fa-key"></i> Sender's Private Key
(LTC/DOGE/BTC/FLO):</label>
<div class="input-with-actions">
<input id="privateKey" class="form-input" type="password"
placeholder="Sender's Private Key (LTC/DOGE/BTC/FLO)" 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'); hideSenderBalanceCard()" title="Clear">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<!-- Sender Balance Card (shown when private key is entered) -->
<div id="senderBalanceCard" class="card balance-info" style="display: none; margin-bottom: 1rem;">
<div class="balance-header">
<h3><i class="fas fa-wallet"></i> Sender's Wallet</h3>
</div>
<div class="balance-display">
<span class="balance-amount" id="senderBalanceValue">0</span>
<span class="currency">LTC</span>
</div>
<div class="account-details">
<div class="detail-row">
<label>Address:</label>
<div class="address-container">
<span id="senderAddress" class="address-text"></span>
</div>
</div>
</div>
</div>
<div class="form-group">
<label><i class="fas fa-user"></i> Recipient's LTC Address:</label>
<div class="input-with-actions">
<input id="receiverAddress" class="form-input" placeholder="Recipient's LTC address (L, M, or ltc1)" />
<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 (LTC):</label>
<div class="input-with-actions">
<input id="sendAmount" class="form-input" type="number" step="0.00000001" min="0.0001"
placeholder="Amount to send (LTC)" oninput="validateSendAmount()" />
<button type="button" class="input-action-btn clear-btn"
onclick="clearInput('sendAmount'); validateSendAmount()" title="Clear">
<i class="fas fa-times"></i>
</button>
</div>
<small class="form-text">Minimum: 0.0001 LTC | Fee: ~0.00002 LTC (calculated based on tx size)</small>
<small id="amountError" class="form-text" style="color: var(--error-color, #e74c3c); display: none;">
<i class="fas fa-exclamation-circle"></i> <span id="amountErrorText"></span>
</small>
</div>
<button class="btn btn-primary btn-block" onclick="sendLtcRPC()" id="sendBtn">
<span class="btn-text"><i class="fas fa-paper-plane"></i> Send LTC</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();
const ltcCrypto = window.ltcCrypto;
const ltcBlockchainAPI = window.ltcBlockchainAPI;
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
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() {
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 = ltcCrypto.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> Litecoin (LTC)</h4>
<div class="blockchain-badge primary">PRIMARY</div>
</div>
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> LTC Address</label>
<div class="value-container">
<code>${result.LTC.address}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${result.LTC.address}').then(()=>notify('LTC address copied','success'))" title="Copy LTC Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="detail-row">
<label><i class="fas fa-key"></i> LTC Private Key</label>
<div class="value-container">
<code>${result.LTC.privateKey}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${result.LTC.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 ["DOGE", "FLO", "BTC"]) {
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;
// Exclude SegWit addresses
if (key.toLowerCase().startsWith("ltc1")) 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;
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
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 = ltcCrypto.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 ["LTC", "DOGE", "FLO", "BTC"]) {
if (result[chain]) {
const badgeClass = chain === "LTC" ? "PRIMARY" : "SECONDARY";
const badgeStyle = chain === "LTC" ? "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") ||
address.toLowerCase().startsWith("ltc1");
if (!isValidFormat) {
document.getElementById("directTranslateResult").innerHTML =
createErrorUI(
"Invalid Address Format",
"The text entered doesn't appear to be a valid address for LTC, BTC, FLO, or DOGE.<br>Please check your input and try again."
);
notify("Invalid address format", "error");
return;
}
setButtonLoading("translateBtn", true);
try {
const result = ltcCrypto.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 ["LTC", "DOGE", "FLO", "BTC"]) {
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);
}
}
// Litecoin API Functions
function checkDogeBalance() {
const input = document.getElementById("balanceAddress").value.trim();
if (!input) {
notify("Please enter a Litecoin address or private key", "error");
return;
}
let address = input;
// Check if input is a private key (not an address)
if (
input.length >= 40 &&
!input.startsWith("L") &&
!input.startsWith("M") &&
!input.toLowerCase().startsWith("ltc1")
) {
try {
// Attempt to derive address from private key
address = ltcCrypto.generateMultiChain(input).LTC.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>
`;
ltcBlockchainAPI
.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">LTC</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('transactionAddr').value='${address}'; showPage('transactionsPage'); loadTransactions();">
<i class="fas fa-history"></i> View Transactions
</button>
</div>
</div>
`;
notify(`Balance: ${balance} LTC`, "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("L") ||
address.startsWith("M") ||
address.toLowerCase().startsWith("ltc1")
)
return "LTC";
if (address.startsWith("D")) return "DOGE";
if (address.startsWith("F")) return "FLO";
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 isLtcAddress =
input.startsWith("L") ||
input.startsWith("M") ||
input.toLowerCase().startsWith("ltc1");
let isOtherSupportedAddress =
input.startsWith("D") ||
input.startsWith("F") ||
input.startsWith("bc1") ||
input.startsWith("1") ||
input.startsWith("3");
if (
input.length >= 40 &&
!input.toLowerCase().startsWith("ltc1") &&
(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 (LTC, DOGE, FLO, or BTC)."
);
notify("Private keys not allowed in this search", "error");
return;
}
// Check if address is a valid format
if (!isLtcAddress && !isOtherSupportedAddress) {
document.getElementById("txList").innerHTML = createErrorUI(
"Invalid Address Format",
"Please enter a valid blockchain address (LTC, DOGE, FLO, or BTC)."
);
notify("Invalid address format", "error");
return;
}
setButtonLoading("loadTransactions", true);
if (!isLtcAddress && isOtherSupportedAddress) {
try {
notify("Translating address to LTC equivalent...", "info");
const translatedAddresses = ltcCrypto.translateAddress(input);
address = translatedAddresses.LTC;
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 LTC 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";
ltcBlockchainAPI
.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)} LTC`, "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 Litecoin 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 Litecoin address", "error");
return;
}
document.getElementById("transactionAddr").value = address;
showPage("transactionsPage");
loadTransactions();
}
function fetchTransactionsWithPagination() {
document.getElementById("transactionSection").style.display = "block";
document.getElementById("paginationInfo").innerText = "";
ltcBlockchainAPI
.getLtcTransactions(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 Litecoin 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) + " LTC";
txType = "Received";
txClass = "incoming";
} else if (numericValue < 0) {
// Coins sent
valueDisplay = numericValue.toFixed(8) + " LTC";
txType = "Sent";
txClass = "outgoing";
} else {
valueDisplay = numericValue.toFixed(8) + " LTC (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} LTC`;
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() : "ltc"
}">
${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}, 'ltc')"
class="btn-toggle-address"
data-type="ltc"
title="Show LTC Address">
LTC
</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 LTC 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 === "ltc") {
// Show LTC address
addressDisplay.textContent = addressItem.address;
addressDisplay.title = addressItem.address;
} else {
// Show original blockchain address (FLO/BTC/DOGE)
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") || "ltc";
let addressToCopy;
let addressLabel;
if (currentType === "ltc") {
addressToCopy = addressItem.address;
addressLabel = "LTC 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 Litecoin using RPC method
function sendLtcRPC() {
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 = ltcCrypto.generateMultiChain(privateKey).LTC.address;
} catch (error) {
notify("Invalid private key format", "error");
return;
}
let ltcprivkey =
ltcCrypto.generateMultiChain(privateKey).LTC.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);
ltcBlockchainAPI
.sendLitecoinRPC(
senderAddress,
receiverAddress,
sendAmount,
ltcprivkey
)
.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 LTC 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} LTC</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 class="tx-detail-item" style="margin-top: 1rem;">
<a href="https://live.blockcypher.com/ltc/tx/${txid}/" target="_blank" rel="noopener noreferrer" class="btn btn-secondary btn-block" style="text-decoration: none; display: flex; align-items: center; justify-content: center; gap: 8px;">
<i class="fas fa-external-link-alt"></i> View on Explorer
</a>
</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 LTC transaction could not be processed.",
`Error Details: ${error.message || "Unknown error"
} (${new Date().toLocaleString()})`,
"sendLtcRPC()"
);
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 = "";
}
ltcBlockchainAPI
.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} LTC</div>
</div>
<div class="tx-detail-row">
<label>Total Input:</label>
<div class="tx-detail-value">${tx.totalInput.toFixed(
8
)} LTC</div>
</div>
<div class="tx-detail-row">
<label>Total Output:</label>
<div class="tx-detail-value">${tx.totalOutput.toFixed(
8
)} LTC</div>
</div>
<div class="tx-detail-row highlight">
<label>Amount Sent:</label>
<div class="tx-detail-value">${calculateAmountSent(
tx
).toFixed(8)} LTC</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
)} LTC</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
)} LTC</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>
<!-- View on Explorer button -->
<div class="tx-section" style="margin-top: 1rem;">
<a href="https://live.blockcypher.com/ltc/tx/${tx.txid}/" target="_blank" rel="noopener noreferrer" class="btn btn-secondary btn-block" style="text-decoration: none; display: flex; align-items: center; justify-content: center; gap: 8px;">
<i class="fas fa-external-link-alt"></i> View on Explorer
</a>
</div>
</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 balanceCard = document.getElementById("senderBalanceCard");
const senderAddressField = document.getElementById("senderAddress");
const senderBalanceValue = document.getElementById("senderBalanceValue");
if (!privateKey) {
hideSenderBalanceCard();
return;
}
try {
const derivedAddress =
ltcCrypto.generateMultiChain(privateKey).LTC.address;
// Show the balance card and set address
balanceCard.style.display = "block";
senderAddressField.textContent = derivedAddress;
senderBalanceValue.textContent = "...";
// Fetch balance for the derived address
ltcBlockchainAPI.getBalance(derivedAddress)
.then((balance) => {
senderBalanceValue.textContent = balance.toFixed(8);
})
.catch((error) => {
console.error("[WALLET] Error fetching balance:", error);
senderBalanceValue.textContent = "unavailable";
});
} catch (error) {
console.error(
"[WALLET] Error deriving address from private key:",
error
);
hideSenderBalanceCard();
notify("Invalid private key format", "error");
}
}
function hideSenderBalanceCard() {
const balanceCard = document.getElementById("senderBalanceCard");
const senderAddressField = document.getElementById("senderAddress");
const senderBalanceValue = document.getElementById("senderBalanceValue");
balanceCard.style.display = "none";
senderAddressField.textContent = "";
senderBalanceValue.textContent = "0";
}
function validateSendAmount() {
const sendAmountInput = document.getElementById("sendAmount");
const amountError = document.getElementById("amountError");
const amountErrorText = document.getElementById("amountErrorText");
const senderBalanceValue = document.getElementById("senderBalanceValue");
const amount = parseFloat(sendAmountInput.value);
const MIN_AMOUNT = 0.0001;
const FEE = 0.00005; // Estimated max fee for validation (actual fee calculated based on tx size)
// Hide error by default
amountError.style.display = "none";
sendAmountInput.style.borderColor = "";
if (!sendAmountInput.value || isNaN(amount)) {
return true; // No input yet, no error
}
if (amount < MIN_AMOUNT) {
amountErrorText.textContent = `Amount too small. Minimum is ${MIN_AMOUNT} LTC.`;
amountError.style.display = "block";
sendAmountInput.style.borderColor = "var(--error-color, #e74c3c)";
return false;
}
// Check if balance is available and sufficient
const balance = parseFloat(senderBalanceValue.textContent);
if (!isNaN(balance) && balance > 0) {
const totalNeeded = amount + FEE;
if (totalNeeded > balance) {
amountErrorText.textContent = `Insufficient balance. Need ${totalNeeded.toFixed(8)} LTC (${amount} + ${FEE} fee), but only have ${balance.toFixed(8)} LTC.`;
amountError.style.display = "block";
sendAmountInput.style.borderColor = "var(--error-color, #e74c3c)";
return false;
}
}
return true;
}
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 Litecoin 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>