Ripple wallet created
This commit is contained in:
parent
b9f0325d55
commit
aec3cf0a5b
BIN
Ripple Web Wallet Tasks.pdf
Normal file
BIN
Ripple Web Wallet Tasks.pdf
Normal file
Binary file not shown.
2821
css/main.css
Normal file
2821
css/main.css
Normal file
File diff suppressed because it is too large
Load Diff
12
favicon.svg
Normal file
12
favicon.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns:xodm="http://www.corel.com/coreldraw/odm/2003" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 2500 2500" style="enable-background:new 0 0 2500 2500;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="Layer_x0020_1">
|
||||
<g id="_2082244081712">
|
||||
<circle cx="1250" cy="1250" r="1250"></circle>
|
||||
<path class="st0" d="M1820.4,549.8h233.2l-485.5,503.4c-175.8,182.3-460.8,182.3-636.2,0L446.3,549.8h233.2l368.7,382.4 c111.5,115.3,291.8,115.3,403,0L1820.4,549.8L1820.4,549.8z M676.6,1950.2H443.3l488.6-506.8c175.8-182.3,460.8-182.3,636.6,0 l488.6,506.8h-233.3l-371.8-385.8c-111.5-115.3-291.8-115.3-403,0L676.6,1950.2z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 812 B |
612
index.html
Normal file
612
index.html
Normal file
@ -0,0 +1,612 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Ripple 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.4.0/css/all.min.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="css/main.css" />
|
||||
|
||||
<!-- External Libraries -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/xrpl@2.7.0/build/xrpl-latest-min.js"></script>
|
||||
<script src="https://unpkg.com/uhtml@3.0.1/es.js"></script>
|
||||
|
||||
<script src="https://unpkg.com/@preact/signals-core@1.7.0/dist/signals-core.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/elliptic/6.6.1/elliptic.min.js"></script>
|
||||
</head>
|
||||
<body class="hidden">
|
||||
<!-- Loading Screen -->
|
||||
<div id="loading_page" class="loading-screen">
|
||||
<div class="loading-content">
|
||||
<div class="loading-spinner"></div>
|
||||
<h2>Ripple Wallet</h2>
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Header -->
|
||||
<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">Ripple 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>
|
||||
|
||||
<!-- Main Container -->
|
||||
<div class="container">
|
||||
<!-- Sidebar Navigation -->
|
||||
<nav class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<i class="fas fa-wallet"></i>
|
||||
<h3>Ripple Wallet</h3>
|
||||
</div>
|
||||
<ul class="sidebar-menu">
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
onclick="showPage('connectPage')"
|
||||
class="nav-link active"
|
||||
>
|
||||
<i class="fas fa-search-dollar"></i>Balance
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" onclick="showPage('sendPage')" class="nav-link">
|
||||
<i class="fas fa-paper-plane"></i> Send XRP
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" onclick="showPage('generatePage')" class="nav-link">
|
||||
<i class="fas fa-plus-circle"></i> Generate Address
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" onclick="showPage('recoverPage')" class="nav-link">
|
||||
<i class="fas fa-key"></i> Retrieve Address
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<main class="main-content">
|
||||
<!-- Check Balance & Transactions Page -->
|
||||
<div id="connectPage" class="page">
|
||||
<div class="page-header">
|
||||
<h2><i class="fas fa-search-dollar"></i> Check Balance</h2>
|
||||
<p>
|
||||
Check the XRP balance and transaction history for any Ripple
|
||||
address
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="form-group">
|
||||
<label for="checkAddress">
|
||||
<i class="fas fa-wallet"></i> XRP Address or Private Key
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="checkAddress"
|
||||
class="form-input"
|
||||
placeholder="Enter XRP address (r...), private key (BTC/FLO)"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<div class="input-help">
|
||||
Enter an XRP address,private key (BTC/FLO) to check XRP balance
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onclick="checkBalanceAndTransactions()"
|
||||
class="btn btn-primary btn-block"
|
||||
>
|
||||
<i class="fas fa-search-dollar"></i> Check Balance
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Balance Info Display -->
|
||||
<div class="card balance-info" id="balanceInfo" style="display: none">
|
||||
<div class="balance-header">
|
||||
<h3><i class="fas fa-coins"></i> Account Balance</h3>
|
||||
<div class="balance-display">
|
||||
<span class="balance-amount" id="displayBalance">0 XRP</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="account-details">
|
||||
<div class="detail-row">
|
||||
<label>Address:</label>
|
||||
<div class="address-container">
|
||||
<span id="checkedAddress" class="address-text">-</span>
|
||||
<button
|
||||
onclick="copyAddressToClipboard(document.getElementById('checkedAddress').textContent)"
|
||||
class="btn-icon"
|
||||
title="Copy Address"
|
||||
>
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transaction Section -->
|
||||
<div
|
||||
id="transactionSection"
|
||||
class="card transaction-section"
|
||||
style="display: none"
|
||||
>
|
||||
<!-- Transaction Header and Filter Controls -->
|
||||
<div id="transactionControls" class="transaction-controls">
|
||||
<div class="transaction-header">
|
||||
<h3>Transactions</h3>
|
||||
<div class="filter-buttons">
|
||||
<button
|
||||
class="filter-btn active"
|
||||
data-filter="all"
|
||||
onclick="setTransactionFilter('all')"
|
||||
>
|
||||
<i class="fas fa-list"></i> All
|
||||
</button>
|
||||
<button
|
||||
class="filter-btn"
|
||||
data-filter="received"
|
||||
onclick="setTransactionFilter('received')"
|
||||
>
|
||||
<i class="fas fa-arrow-down"></i> Received
|
||||
</button>
|
||||
<button
|
||||
class="filter-btn"
|
||||
data-filter="sent"
|
||||
onclick="setTransactionFilter('sent')"
|
||||
>
|
||||
<i class="fas fa-arrow-up"></i> Sent
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transaction Results -->
|
||||
<div id="txList" class="transaction-list">
|
||||
<!-- Transaction cards will be inserted here -->
|
||||
</div>
|
||||
|
||||
<!-- Pagination Controls (moved below transaction list) -->
|
||||
<div class="pagination-section">
|
||||
<div class="pagination-info" id="paginationInfo">
|
||||
Showing 0 - 0 of 0 transactions
|
||||
</div>
|
||||
<div class="pagination-controls">
|
||||
<button
|
||||
class="pagination-btn"
|
||||
id="prevBtn"
|
||||
onclick="goToPreviousPage()"
|
||||
disabled
|
||||
>
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</button>
|
||||
<div class="page-numbers" id="pageNumbers">
|
||||
<!-- Page numbers will be inserted here -->
|
||||
</div>
|
||||
<button
|
||||
class="pagination-btn"
|
||||
id="nextBtn"
|
||||
onclick="goToNextPage()"
|
||||
disabled
|
||||
>
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Send XRP Page -->
|
||||
<div id="sendPage" class="page hidden">
|
||||
<div class="page-header">
|
||||
<h2><i class="fas fa-paper-plane"></i> Send XRP</h2>
|
||||
<p>Send XRP to another Ripple address</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="form-group">
|
||||
<label for="sendKey">
|
||||
<i class="fas fa-key"></i> Your Ripple Seed or Private Key
|
||||
(FLO/BTC)
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="sendKey"
|
||||
class="form-input"
|
||||
placeholder="Enter your seed (s...) or private key (FLO/BTC)"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<div class="input-help">Use your Ripple seed (s...)</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="recipient">
|
||||
<i class="fas fa-user"></i> Recipient Address
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="recipient"
|
||||
class="form-input"
|
||||
placeholder="Enter Ripple address (r...)"
|
||||
/>
|
||||
<small class="help-text"
|
||||
>Enter a valid Ripple address starting with 'r' (25-34
|
||||
characters)</small
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="amount">
|
||||
<i class="fas fa-coins"></i> Amount (XRP)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
id="amount"
|
||||
class="form-input"
|
||||
placeholder="0.00"
|
||||
step="0.000001"
|
||||
min="0.000001"
|
||||
/>
|
||||
<div class="input-help">Minimum: 0.000001 XRP</div>
|
||||
</div>
|
||||
|
||||
<button onclick="sendXRP()" class="btn btn-primary btn-block">
|
||||
<i class="fas fa-paper-plane"></i> Send XRP
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generate Address Page -->
|
||||
<div id="generatePage" class="page hidden">
|
||||
<div class="page-header">
|
||||
<h2><i class="fas fa-plus-circle"></i> Generate XRP Address</h2>
|
||||
<p>Generate XRP addresses from private keys of any blockchain</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<!-- Private Key Input for XRP Generation -->
|
||||
<div class="form-group">
|
||||
<label for="generateKey">
|
||||
<i class="fas fa-key"></i> Private Key (Required for XRP)
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="generateKey"
|
||||
placeholder="Enter private key from any blockchain (BTC/FLO)"
|
||||
class="form-input"
|
||||
/>
|
||||
<small class="form-text">
|
||||
Enter a private key from any blockchain (BTC/FLO) to generate
|
||||
the corresponding XRP address.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="generate-actions">
|
||||
<button
|
||||
onclick="generateXRPAddress()"
|
||||
class="btn btn-primary btn-block"
|
||||
>
|
||||
<i class="fas fa-coins"></i> Generate XRP Address
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- XRP Generation Info -->
|
||||
<div
|
||||
id="xrpGenerationInfo"
|
||||
class="card info-card"
|
||||
style="display: none"
|
||||
>
|
||||
<div class="info-header">
|
||||
<h3>
|
||||
<i class="fas fa-info-circle"></i> XRP Address Generation
|
||||
</h3>
|
||||
</div>
|
||||
<div class="info-content">
|
||||
<p>
|
||||
<strong>Note:</strong> This wallet doesn't generate XRP
|
||||
addresses directly.
|
||||
</p>
|
||||
<p>To get an XRP address, please:</p>
|
||||
<ol>
|
||||
<li>Use our BTC wallet to generate a BTC private key</li>
|
||||
<li>
|
||||
Convert that BTC/FLO private key to Ripple seed using the
|
||||
conversion tool below
|
||||
</li>
|
||||
</ol>
|
||||
<button
|
||||
onclick="showPage('convertPage')"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
<i class="fas fa-exchange-alt"></i> Go to Conversion Tool
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generated Address Output -->
|
||||
<div id="walletOutput" class="card output-card" style="display: none">
|
||||
<!-- Generated address info will be inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Retrieve Address Page -->
|
||||
<div id="recoverPage" class="page hidden">
|
||||
<div class="page-header">
|
||||
<h2><i class="fas fa-key"></i> Retrieve XRP Address</h2>
|
||||
<p>Retrieve your XRP address from private key</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="form-group">
|
||||
<label for="recoverKey">
|
||||
<i class="fas fa-key"></i> Private Key / Seed
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
id="recoverKey"
|
||||
class="form-input"
|
||||
placeholder="Enter private key or seed"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<div class="input-help">
|
||||
Enter your private key from any blockchain (BTC/FLO) or XRP seed
|
||||
(s...) to retrieve your XRP address.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="retrieve-actions">
|
||||
<button
|
||||
onclick="retrieveXRPAddress()"
|
||||
class="btn btn-primary btn-block"
|
||||
>
|
||||
<i class="fas fa-coins"></i> Retrieve XRP Address
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Retrieved Address Output -->
|
||||
<div
|
||||
id="recoveryOutput"
|
||||
class="card output-card"
|
||||
style="display: none"
|
||||
>
|
||||
<!-- Retrieved address info will be inserted here -->
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Navigation (Mobile) -->
|
||||
<div class="nav-box">
|
||||
<button
|
||||
onclick="showPage('connectPage')"
|
||||
class="nav-btn active"
|
||||
data-page="connectPage"
|
||||
>
|
||||
<i class="fas fa-search-dollar"></i>
|
||||
<span>Balance</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('generatePage')"
|
||||
class="nav-btn"
|
||||
data-page="generatePage"
|
||||
>
|
||||
<i class="fas fa-plus"></i>
|
||||
<span>Generate</span>
|
||||
</button>
|
||||
<button
|
||||
onclick="showPage('recoverPage')"
|
||||
class="nav-btn"
|
||||
data-page="recoverPage"
|
||||
>
|
||||
<i class="fas fa-key"></i>
|
||||
<span>Retrieve</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Confirmation Popup -->
|
||||
<sm-popup id="sendConfirm">
|
||||
<div class="popup-content">
|
||||
<h3><i class="fas fa-exclamation-triangle"></i> Confirm Transaction</h3>
|
||||
<div class="transaction-details" id="transactionDetails">
|
||||
<!-- Transaction details will be populated here -->
|
||||
</div>
|
||||
<div class="popup-actions">
|
||||
<button onclick="closePopup()" class="btn btn-secondary">
|
||||
<i class="fas fa-times"></i> Cancel
|
||||
</button>
|
||||
<button onclick="confirmSend()" class="btn btn-primary">
|
||||
<i class="fas fa-paper-plane"></i> Send
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</sm-popup>
|
||||
|
||||
<!-- Transaction Success Popup -->
|
||||
<sm-popup id="transactionSuccess">
|
||||
<div class="popup-content">
|
||||
<h3><i class="fas fa-check-circle"></i> Transaction Successful!</h3>
|
||||
<div class="transaction-details" id="successTransactionDetails">
|
||||
<!-- Success transaction details will be populated here -->
|
||||
</div>
|
||||
<div class="popup-actions">
|
||||
<button onclick="closePopup()" class="btn btn-primary">
|
||||
<i class="fas fa-check"></i> Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</sm-popup>
|
||||
|
||||
<!-- Standard UI Notifications -->
|
||||
|
||||
<!-- Confirmation Popup -->
|
||||
<sm-popup id="confirmation_popup">
|
||||
<div class="popup-content">
|
||||
<h3 id="confirm_title">Confirm Action</h3>
|
||||
<div id="confirm_message"></div>
|
||||
<div class="popup-actions">
|
||||
<button class="btn btn-secondary cancel-button">Cancel</button>
|
||||
<button class="btn btn-primary confirm-button">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</sm-popup>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="scripts/components.min.js"></script>
|
||||
<script src="scripts/btcwallet_scripts_lib.js"></script>
|
||||
<script type="text/javascript" src="scripts/floCrypto.js"></script>
|
||||
<script type="text/javascript" src="scripts/btcOperator.js"></script>
|
||||
|
||||
<script type="module" src="scripts/wallet.js"></script>
|
||||
<script>
|
||||
// UI Enhancement Functions
|
||||
function showPage(pageId) {
|
||||
// Hide all pages
|
||||
document.querySelectorAll(".page").forEach((page) => {
|
||||
page.classList.add("hidden");
|
||||
});
|
||||
|
||||
// Show selected page
|
||||
document.getElementById(pageId).classList.remove("hidden");
|
||||
|
||||
// Update navigation states
|
||||
updateNavigation(pageId);
|
||||
}
|
||||
|
||||
function updateNavigation(activePageId) {
|
||||
// Update sidebar navigation - remove active from all first
|
||||
document.querySelectorAll(".sidebar .nav-link").forEach((link) => {
|
||||
link.classList.remove("active");
|
||||
});
|
||||
|
||||
// Update bottom navigation
|
||||
document.querySelectorAll(".nav-btn").forEach((btn) => {
|
||||
btn.classList.remove("active");
|
||||
if (btn.dataset.page === activePageId) {
|
||||
btn.classList.add("active");
|
||||
}
|
||||
});
|
||||
|
||||
// Find and activate corresponding sidebar link
|
||||
const activeLink = document.querySelector(
|
||||
`.sidebar .nav-link[onclick*="${activePageId}"]`
|
||||
);
|
||||
if (activeLink) {
|
||||
activeLink.classList.add("active");
|
||||
}
|
||||
}
|
||||
|
||||
// Theme toggle functionality
|
||||
function initializeTheme() {
|
||||
const themeToggle = document.getElementById("themeToggle");
|
||||
const themeIcon = document.getElementById("themeIcon");
|
||||
const body = document.body;
|
||||
|
||||
// Check for saved theme preference or default to 'light'
|
||||
const savedTheme = localStorage.getItem("theme");
|
||||
const systemPrefersDark = window.matchMedia(
|
||||
"(prefers-color-scheme: dark)"
|
||||
).matches;
|
||||
|
||||
// Set initial theme
|
||||
let currentTheme = savedTheme || (systemPrefersDark ? "dark" : "light");
|
||||
|
||||
// Apply theme
|
||||
body.setAttribute("data-theme", currentTheme);
|
||||
updateThemeIcon(currentTheme);
|
||||
|
||||
// Add event listener for theme toggle
|
||||
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 (theme === "dark") {
|
||||
themeIcon.className = "fas fa-sun";
|
||||
} else {
|
||||
themeIcon.className = "fas fa-moon";
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize theme when DOM is loaded
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
initializeTheme();
|
||||
|
||||
// Initialize searched addresses list
|
||||
if (typeof updateSearchedAddressesList === "function") {
|
||||
updateSearchedAddressesList();
|
||||
}
|
||||
|
||||
// Add real-time validation for recipient address
|
||||
const recipientInput = document.getElementById("recipient");
|
||||
const helpText = recipientInput
|
||||
? recipientInput.nextElementSibling
|
||||
: null;
|
||||
|
||||
if (recipientInput && helpText) {
|
||||
recipientInput.addEventListener("input", function () {
|
||||
const address = this.value.trim();
|
||||
if (address && typeof validateAddressInput === "function") {
|
||||
const validation = validateAddressInput(address);
|
||||
if (validation.valid) {
|
||||
helpText.textContent = "✓ Valid Ripple address";
|
||||
helpText.style.color = "#22c55e";
|
||||
} else {
|
||||
helpText.textContent = validation.message;
|
||||
helpText.style.color = "#ef4444";
|
||||
}
|
||||
} else {
|
||||
helpText.textContent =
|
||||
"Enter a valid Ripple address starting with 'r' (25-34 characters)";
|
||||
helpText.style.color = "";
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<div id="notification_drawer" class="notification-drawer"></div>
|
||||
</body>
|
||||
</html>
|
||||
1499
scripts/btcOperator.js
Normal file
1499
scripts/btcOperator.js
Normal file
File diff suppressed because it is too large
Load Diff
10243
scripts/btcwallet_scripts_lib.js
Normal file
10243
scripts/btcwallet_scripts_lib.js
Normal file
File diff suppressed because it is too large
Load Diff
1
scripts/components.min.js
vendored
Normal file
1
scripts/components.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
606
scripts/floCrypto.js
Normal file
606
scripts/floCrypto.js
Normal file
@ -0,0 +1,606 @@
|
||||
(function (EXPORTS) {
|
||||
//floCrypto v2.3.6a
|
||||
/* FLO Crypto Operators */
|
||||
"use strict";
|
||||
const floCrypto = EXPORTS;
|
||||
|
||||
const p = BigInteger(
|
||||
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",
|
||||
16
|
||||
);
|
||||
const ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
||||
const ascii_alternatives = `‘ '\n’ '\n“ "\n” "\n– --\n— ---\n≥ >=\n≤ <=\n≠ !=\n× *\n÷ /\n← <-\n→ ->\n↔ <->\n⇒ =>\n⇐ <=\n⇔ <=>`;
|
||||
const exponent1 = () => p.add(BigInteger.ONE).divide(BigInteger("4"));
|
||||
coinjs.compressed = true; //defaulting coinjs compressed to true;
|
||||
|
||||
function calculateY(x) {
|
||||
let exp = exponent1();
|
||||
// x is x value of public key in BigInteger format without 02 or 03 or 04 prefix
|
||||
return x
|
||||
.modPow(BigInteger("3"), p)
|
||||
.add(BigInteger("7"))
|
||||
.mod(p)
|
||||
.modPow(exp, p);
|
||||
}
|
||||
|
||||
function getUncompressedPublicKey(compressedPublicKey) {
|
||||
// Fetch x from compressedPublicKey
|
||||
let pubKeyBytes = Crypto.util.hexToBytes(compressedPublicKey);
|
||||
const prefix = pubKeyBytes.shift(); // remove prefix
|
||||
let prefix_modulus = prefix % 2;
|
||||
pubKeyBytes.unshift(0); // add prefix 0
|
||||
let x = new BigInteger(pubKeyBytes);
|
||||
let xDecimalValue = x.toString();
|
||||
// Fetch y
|
||||
let y = calculateY(x);
|
||||
let yDecimalValue = y.toString();
|
||||
// verify y value
|
||||
let resultBigInt = y.mod(BigInteger("2"));
|
||||
let check = resultBigInt.toString() % 2;
|
||||
if (prefix_modulus !== check) yDecimalValue = y.negate().mod(p).toString();
|
||||
return {
|
||||
x: xDecimalValue,
|
||||
y: yDecimalValue,
|
||||
};
|
||||
}
|
||||
|
||||
function getSenderPublicKeyString() {
|
||||
let privateKey = ellipticCurveEncryption.senderRandom();
|
||||
var senderPublicKeyString =
|
||||
ellipticCurveEncryption.senderPublicString(privateKey);
|
||||
return {
|
||||
privateKey: privateKey,
|
||||
senderPublicKeyString: senderPublicKeyString,
|
||||
};
|
||||
}
|
||||
|
||||
function deriveSharedKeySender(receiverPublicKeyHex, senderPrivateKey) {
|
||||
let receiverPublicKeyString =
|
||||
getUncompressedPublicKey(receiverPublicKeyHex);
|
||||
var senderDerivedKey = ellipticCurveEncryption.senderSharedKeyDerivation(
|
||||
receiverPublicKeyString.x,
|
||||
receiverPublicKeyString.y,
|
||||
senderPrivateKey
|
||||
);
|
||||
return senderDerivedKey;
|
||||
}
|
||||
|
||||
function deriveSharedKeyReceiver(senderPublicKeyString, receiverPrivateKey) {
|
||||
return ellipticCurveEncryption.receiverSharedKeyDerivation(
|
||||
senderPublicKeyString.XValuePublicString,
|
||||
senderPublicKeyString.YValuePublicString,
|
||||
receiverPrivateKey
|
||||
);
|
||||
}
|
||||
|
||||
function getReceiverPublicKeyString(privateKey) {
|
||||
return ellipticCurveEncryption.receiverPublicString(privateKey);
|
||||
}
|
||||
|
||||
function wifToDecimal(pk_wif, isPubKeyCompressed = false) {
|
||||
let pk = Bitcoin.Base58.decode(pk_wif);
|
||||
pk.shift();
|
||||
pk.splice(-4, 4);
|
||||
//If the private key corresponded to a compressed public key, also drop the last byte (it should be 0x01).
|
||||
if (isPubKeyCompressed == true) pk.pop();
|
||||
pk.unshift(0);
|
||||
let privateKeyDecimal = BigInteger(pk).toString();
|
||||
let privateKeyHex = Crypto.util.bytesToHex(pk);
|
||||
return {
|
||||
privateKeyDecimal: privateKeyDecimal,
|
||||
privateKeyHex: privateKeyHex,
|
||||
};
|
||||
}
|
||||
|
||||
//generate a random Interger within range
|
||||
floCrypto.randInt = function (min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(securedMathRandom() * (max - min + 1)) + min;
|
||||
};
|
||||
|
||||
//generate a random String within length (options : alphaNumeric chars only)
|
||||
floCrypto.randString = function (length, alphaNumeric = true) {
|
||||
var result = "";
|
||||
var characters = alphaNumeric
|
||||
? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+-./*?@#&$<>=[]{}():";
|
||||
for (var i = 0; i < length; i++)
|
||||
result += characters.charAt(
|
||||
Math.floor(securedMathRandom() * characters.length)
|
||||
);
|
||||
return result;
|
||||
};
|
||||
|
||||
//Encrypt Data using public-key
|
||||
floCrypto.encryptData = function (data, receiverPublicKeyHex) {
|
||||
var senderECKeyData = getSenderPublicKeyString();
|
||||
var senderDerivedKey = deriveSharedKeySender(
|
||||
receiverPublicKeyHex,
|
||||
senderECKeyData.privateKey
|
||||
);
|
||||
let senderKey = senderDerivedKey.XValue + senderDerivedKey.YValue;
|
||||
let secret = Crypto.AES.encrypt(data, senderKey);
|
||||
return {
|
||||
secret: secret,
|
||||
senderPublicKeyString: senderECKeyData.senderPublicKeyString,
|
||||
};
|
||||
};
|
||||
|
||||
//Decrypt Data using private-key
|
||||
floCrypto.decryptData = function (data, privateKeyHex) {
|
||||
var receiverECKeyData = {};
|
||||
if (typeof privateKeyHex !== "string")
|
||||
throw new Error("No private key found.");
|
||||
let privateKey = wifToDecimal(privateKeyHex, true);
|
||||
if (typeof privateKey.privateKeyDecimal !== "string")
|
||||
throw new Error("Failed to detremine your private key.");
|
||||
receiverECKeyData.privateKey = privateKey.privateKeyDecimal;
|
||||
var receiverDerivedKey = deriveSharedKeyReceiver(
|
||||
data.senderPublicKeyString,
|
||||
receiverECKeyData.privateKey
|
||||
);
|
||||
let receiverKey = receiverDerivedKey.XValue + receiverDerivedKey.YValue;
|
||||
let decryptMsg = Crypto.AES.decrypt(data.secret, receiverKey);
|
||||
return decryptMsg;
|
||||
};
|
||||
|
||||
//Sign data using private-key
|
||||
floCrypto.signData = function (data, privateKeyHex) {
|
||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||
var messageHash = Crypto.SHA256(data);
|
||||
var messageSign = Bitcoin.ECDSA.sign(messageHash, key.priv);
|
||||
var sighex = Crypto.util.bytesToHex(messageSign);
|
||||
return sighex;
|
||||
};
|
||||
|
||||
//Verify signatue of the data using public-key
|
||||
floCrypto.verifySign = function (data, signatureHex, publicKeyHex) {
|
||||
var msgHash = Crypto.SHA256(data);
|
||||
var sigBytes = Crypto.util.hexToBytes(signatureHex);
|
||||
var publicKeyPoint = ecparams.getCurve().decodePointHex(publicKeyHex);
|
||||
var verify = Bitcoin.ECDSA.verify(msgHash, sigBytes, publicKeyPoint);
|
||||
return verify;
|
||||
};
|
||||
|
||||
//Generates a new flo ID and returns private-key, public-key and floID
|
||||
const generateNewID = (floCrypto.generateNewID = function () {
|
||||
var key = new Bitcoin.ECKey(false);
|
||||
key.setCompressed(true);
|
||||
return {
|
||||
floID: key.getBitcoinAddress(),
|
||||
pubKey: key.getPubKeyHex(),
|
||||
privKey: key.getBitcoinWalletImportFormat(),
|
||||
};
|
||||
});
|
||||
|
||||
Object.defineProperties(floCrypto, {
|
||||
newID: {
|
||||
get: () => generateNewID(),
|
||||
},
|
||||
hashID: {
|
||||
value: (str) => {
|
||||
let bytes = ripemd160(Crypto.SHA256(str, { asBytes: true }), {
|
||||
asBytes: true,
|
||||
});
|
||||
bytes.unshift(bitjs.pub);
|
||||
var hash = Crypto.SHA256(
|
||||
Crypto.SHA256(bytes, {
|
||||
asBytes: true,
|
||||
}),
|
||||
{
|
||||
asBytes: true,
|
||||
}
|
||||
);
|
||||
var checksum = hash.slice(0, 4);
|
||||
return bitjs.Base58.encode(bytes.concat(checksum));
|
||||
},
|
||||
},
|
||||
tmpID: {
|
||||
get: () => {
|
||||
let bytes = Crypto.util.randomBytes(20);
|
||||
bytes.unshift(bitjs.pub);
|
||||
var hash = Crypto.SHA256(
|
||||
Crypto.SHA256(bytes, {
|
||||
asBytes: true,
|
||||
}),
|
||||
{
|
||||
asBytes: true,
|
||||
}
|
||||
);
|
||||
var checksum = hash.slice(0, 4);
|
||||
return bitjs.Base58.encode(bytes.concat(checksum));
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
//Returns public-key from private-key
|
||||
floCrypto.getPubKeyHex = function (privateKeyHex) {
|
||||
if (!privateKeyHex) return null;
|
||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||
if (key.priv == null) return null;
|
||||
key.setCompressed(true);
|
||||
return key.getPubKeyHex();
|
||||
};
|
||||
|
||||
//Returns flo-ID from public-key or private-key
|
||||
floCrypto.getFloID = function (keyHex) {
|
||||
if (!keyHex) return null;
|
||||
try {
|
||||
var key = new Bitcoin.ECKey(keyHex);
|
||||
if (key.priv == null) key.setPub(keyHex);
|
||||
return key.getBitcoinAddress();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
floCrypto.getAddress = function (privateKeyHex, strict = false) {
|
||||
if (!privateKeyHex) return;
|
||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||
if (key.priv == null) return null;
|
||||
key.setCompressed(true);
|
||||
let pubKey = key.getPubKeyHex(),
|
||||
version = bitjs.Base58.decode(privateKeyHex)[0];
|
||||
switch (version) {
|
||||
case coinjs.priv: //BTC
|
||||
return coinjs.bech32Address(pubKey).address;
|
||||
case bitjs.priv: //FLO
|
||||
return bitjs.pubkey2address(pubKey);
|
||||
default:
|
||||
return strict ? false : bitjs.pubkey2address(pubKey); //default to FLO address (if strict=false)
|
||||
}
|
||||
};
|
||||
|
||||
//Verify the private-key for the given public-key or flo-ID
|
||||
floCrypto.verifyPrivKey = function (
|
||||
privateKeyHex,
|
||||
pubKey_floID,
|
||||
isfloID = true
|
||||
) {
|
||||
if (!privateKeyHex || !pubKey_floID) return false;
|
||||
try {
|
||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||
if (key.priv == null) return false;
|
||||
key.setCompressed(true);
|
||||
if (isfloID && pubKey_floID == key.getBitcoinAddress()) return true;
|
||||
else if (
|
||||
!isfloID &&
|
||||
pubKey_floID.toUpperCase() == key.getPubKeyHex().toUpperCase()
|
||||
)
|
||||
return true;
|
||||
else return false;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
floCrypto.getMultisigAddress = function (publicKeyList, requiredSignatures) {
|
||||
if (!Array.isArray(publicKeyList) || !publicKeyList.length) return null;
|
||||
if (
|
||||
!Number.isInteger(requiredSignatures) ||
|
||||
requiredSignatures < 1 ||
|
||||
requiredSignatures > publicKeyList.length
|
||||
)
|
||||
return null;
|
||||
try {
|
||||
var multisig = bitjs.pubkeys2multisig(publicKeyList, requiredSignatures);
|
||||
return multisig;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
floCrypto.decodeRedeemScript = function (redeemScript) {
|
||||
try {
|
||||
var decoded = bitjs.transaction().decodeRedeemScript(redeemScript);
|
||||
return decoded;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
//Check if the given flo-id is valid or not
|
||||
floCrypto.validateFloID = function (floID, regularOnly = false) {
|
||||
if (!floID) return false;
|
||||
try {
|
||||
let addr = new Bitcoin.Address(floID);
|
||||
if (regularOnly && addr.version != Bitcoin.Address.standardVersion)
|
||||
return false;
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//Check if the given address (any blockchain) is valid or not
|
||||
floCrypto.validateAddr = function (address, std = true, bech = true) {
|
||||
let raw = decodeAddress(address);
|
||||
if (!raw) return false;
|
||||
if (typeof raw.version !== "undefined") {
|
||||
//legacy or segwit
|
||||
if (std == false) return false;
|
||||
else if (
|
||||
std === true ||
|
||||
(!Array.isArray(std) && std === raw.version) ||
|
||||
(Array.isArray(std) && std.includes(raw.version))
|
||||
)
|
||||
return true;
|
||||
else return false;
|
||||
} else if (typeof raw.bech_version !== "undefined") {
|
||||
//bech32
|
||||
if (bech === false) return false;
|
||||
else if (
|
||||
bech === true ||
|
||||
(!Array.isArray(bech) && bech === raw.bech_version) ||
|
||||
(Array.isArray(bech) && bech.includes(raw.bech_version))
|
||||
)
|
||||
return true;
|
||||
else return false;
|
||||
} //unknown
|
||||
else return false;
|
||||
};
|
||||
|
||||
//Check the public-key (or redeem-script) for the address (any blockchain)
|
||||
floCrypto.verifyPubKey = function (pubKeyHex, address) {
|
||||
let raw = decodeAddress(address);
|
||||
if (!raw) return;
|
||||
let pub_hash = Crypto.util.bytesToHex(
|
||||
ripemd160(
|
||||
Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), { asBytes: true })
|
||||
)
|
||||
);
|
||||
if (typeof raw.bech_version !== "undefined" && raw.bytes.length == 32)
|
||||
//bech32-multisig
|
||||
raw.hex = Crypto.util.bytesToHex(ripemd160(raw.bytes, { asBytes: true }));
|
||||
return pub_hash === raw.hex;
|
||||
};
|
||||
|
||||
//Convert the given address (any blockchain) to equivalent floID
|
||||
floCrypto.toFloID = function (address, options = null) {
|
||||
if (!address) return;
|
||||
let raw = decodeAddress(address);
|
||||
if (!raw) return;
|
||||
else if (options) {
|
||||
//if (optional) version check is passed
|
||||
if (
|
||||
typeof raw.version !== "undefined" &&
|
||||
(!options.std || !options.std.includes(raw.version))
|
||||
)
|
||||
return;
|
||||
if (
|
||||
typeof raw.bech_version !== "undefined" &&
|
||||
(!options.bech || !options.bech.includes(raw.bech_version))
|
||||
)
|
||||
return;
|
||||
}
|
||||
raw.bytes.unshift(bitjs.pub);
|
||||
let hash = Crypto.SHA256(
|
||||
Crypto.SHA256(raw.bytes, {
|
||||
asBytes: true,
|
||||
}),
|
||||
{
|
||||
asBytes: true,
|
||||
}
|
||||
);
|
||||
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
|
||||
};
|
||||
|
||||
//Convert raw address bytes to floID
|
||||
floCrypto.rawToFloID = function (raw_bytes) {
|
||||
if (typeof raw_bytes === "string")
|
||||
raw_bytes = Crypto.util.hexToBytes(raw_bytes);
|
||||
if (raw_bytes.length != 20) return null;
|
||||
raw_bytes.unshift(bitjs.pub);
|
||||
let hash = Crypto.SHA256(
|
||||
Crypto.SHA256(raw_bytes, {
|
||||
asBytes: true,
|
||||
}),
|
||||
{
|
||||
asBytes: true,
|
||||
}
|
||||
);
|
||||
return bitjs.Base58.encode(raw_bytes.concat(hash.slice(0, 4)));
|
||||
};
|
||||
|
||||
//Convert the given multisig address (any blockchain) to equivalent multisig floID
|
||||
floCrypto.toMultisigFloID = function (address, options = null) {
|
||||
if (!address) return;
|
||||
let raw = decodeAddress(address);
|
||||
if (!raw) return;
|
||||
else if (options) {
|
||||
//if (optional) version check is passed
|
||||
if (
|
||||
typeof raw.version !== "undefined" &&
|
||||
(!options.std || !options.std.includes(raw.version))
|
||||
)
|
||||
return;
|
||||
if (
|
||||
typeof raw.bech_version !== "undefined" &&
|
||||
(!options.bech || !options.bech.includes(raw.bech_version))
|
||||
)
|
||||
return;
|
||||
}
|
||||
if (typeof raw.bech_version !== "undefined") {
|
||||
if (raw.bytes.length != 32) return; //multisig bech address have 32 bytes
|
||||
//multisig-bech:hash=SHA256 whereas multisig:hash=r160(SHA265), thus ripemd160 the bytes from multisig-bech
|
||||
raw.bytes = ripemd160(raw.bytes, {
|
||||
asBytes: true,
|
||||
});
|
||||
}
|
||||
raw.bytes.unshift(bitjs.multisig);
|
||||
let hash = Crypto.SHA256(
|
||||
Crypto.SHA256(raw.bytes, {
|
||||
asBytes: true,
|
||||
}),
|
||||
{
|
||||
asBytes: true,
|
||||
}
|
||||
);
|
||||
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
|
||||
};
|
||||
|
||||
//Checks if the given addresses (any blockchain) are same (w.r.t keys)
|
||||
floCrypto.isSameAddr = function (addr1, addr2) {
|
||||
if (!addr1 || !addr2) return;
|
||||
let raw1 = decodeAddress(addr1),
|
||||
raw2 = decodeAddress(addr2);
|
||||
if (!raw1 || !raw2) return false;
|
||||
else {
|
||||
if (typeof raw1.bech_version !== "undefined" && raw1.bytes.length == 32)
|
||||
//bech32-multisig
|
||||
raw1.hex = Crypto.util.bytesToHex(
|
||||
ripemd160(raw1.bytes, { asBytes: true })
|
||||
);
|
||||
if (typeof raw2.bech_version !== "undefined" && raw2.bytes.length == 32)
|
||||
//bech32-multisig
|
||||
raw2.hex = Crypto.util.bytesToHex(
|
||||
ripemd160(raw2.bytes, { asBytes: true })
|
||||
);
|
||||
return raw1.hex === raw2.hex;
|
||||
}
|
||||
};
|
||||
|
||||
const decodeAddress = (floCrypto.decodeAddr = function (address) {
|
||||
if (!address) return;
|
||||
else if (address.length == 33 || address.length == 34) {
|
||||
//legacy encoding
|
||||
let decode = bitjs.Base58.decode(address);
|
||||
let bytes = decode.slice(0, decode.length - 4);
|
||||
let checksum = decode.slice(decode.length - 4),
|
||||
hash = Crypto.SHA256(
|
||||
Crypto.SHA256(bytes, {
|
||||
asBytes: true,
|
||||
}),
|
||||
{
|
||||
asBytes: true,
|
||||
}
|
||||
);
|
||||
return hash[0] != checksum[0] ||
|
||||
hash[1] != checksum[1] ||
|
||||
hash[2] != checksum[2] ||
|
||||
hash[3] != checksum[3]
|
||||
? null
|
||||
: {
|
||||
version: bytes.shift(),
|
||||
hex: Crypto.util.bytesToHex(bytes),
|
||||
bytes,
|
||||
};
|
||||
} else if (address.length == 42 || address.length == 62) {
|
||||
//bech encoding
|
||||
let decode = coinjs.bech32_decode(address);
|
||||
if (decode) {
|
||||
let bytes = decode.data;
|
||||
let bech_version = bytes.shift();
|
||||
bytes = coinjs.bech32_convert(bytes, 5, 8, false);
|
||||
return {
|
||||
bech_version,
|
||||
hrp: decode.hrp,
|
||||
hex: Crypto.util.bytesToHex(bytes),
|
||||
bytes,
|
||||
};
|
||||
} else return null;
|
||||
}
|
||||
});
|
||||
|
||||
//Split the str using shamir's Secret and Returns the shares
|
||||
floCrypto.createShamirsSecretShares = function (
|
||||
str,
|
||||
total_shares,
|
||||
threshold_limit
|
||||
) {
|
||||
try {
|
||||
if (str.length > 0) {
|
||||
var strHex = shamirSecretShare.str2hex(str);
|
||||
var shares = shamirSecretShare.share(
|
||||
strHex,
|
||||
total_shares,
|
||||
threshold_limit
|
||||
);
|
||||
return shares;
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//Returns the retrived secret by combining the shamirs shares
|
||||
const retrieveShamirSecret = (floCrypto.retrieveShamirSecret = function (
|
||||
sharesArray
|
||||
) {
|
||||
try {
|
||||
if (sharesArray.length > 0) {
|
||||
var comb = shamirSecretShare.combine(
|
||||
sharesArray.slice(0, sharesArray.length)
|
||||
);
|
||||
comb = shamirSecretShare.hex2str(comb);
|
||||
return comb;
|
||||
}
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
//Verifies the shares and str
|
||||
floCrypto.verifyShamirsSecret = function (sharesArray, str) {
|
||||
if (!str) return null;
|
||||
else if (retrieveShamirSecret(sharesArray) === str) return true;
|
||||
else return false;
|
||||
};
|
||||
|
||||
const validateASCII = (floCrypto.validateASCII = function (
|
||||
string,
|
||||
bool = true
|
||||
) {
|
||||
if (typeof string !== "string") return null;
|
||||
if (bool) {
|
||||
let x;
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
x = string.charCodeAt(i);
|
||||
if (x < 32 || x > 127) return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
let x,
|
||||
invalids = {};
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
x = string.charCodeAt(i);
|
||||
if (x < 32 || x > 127)
|
||||
if (x in invalids) invalids[string[i]].push(i);
|
||||
else invalids[string[i]] = [i];
|
||||
}
|
||||
if (Object.keys(invalids).length) return invalids;
|
||||
else return true;
|
||||
}
|
||||
});
|
||||
|
||||
floCrypto.convertToASCII = function (string, mode = "soft-remove") {
|
||||
let chars = validateASCII(string, false);
|
||||
if (chars === true) return string;
|
||||
else if (chars === null) return null;
|
||||
let convertor,
|
||||
result = string,
|
||||
refAlt = {};
|
||||
ascii_alternatives.split("\n").forEach((a) => (refAlt[a[0]] = a.slice(2)));
|
||||
mode = mode.toLowerCase();
|
||||
if (mode === "hard-unicode")
|
||||
convertor = (c) =>
|
||||
`\\u${("000" + c.charCodeAt().toString(16)).slice(-4)}`;
|
||||
else if (mode === "soft-unicode")
|
||||
convertor = (c) =>
|
||||
refAlt[c] || `\\u${("000" + c.charCodeAt().toString(16)).slice(-4)}`;
|
||||
else if (mode === "hard-remove") convertor = (c) => "";
|
||||
else if (mode === "soft-remove") convertor = (c) => refAlt[c] || "";
|
||||
else return null;
|
||||
for (let c in chars) result = result.replaceAll(c, convertor(c));
|
||||
return result;
|
||||
};
|
||||
|
||||
floCrypto.revertUnicode = function (string) {
|
||||
return string.replace(/\\u[\dA-F]{4}/gi, (m) =>
|
||||
String.fromCharCode(parseInt(m.replace(/\\u/g, ""), 16))
|
||||
);
|
||||
};
|
||||
})("object" === typeof module ? module.exports : (window.floCrypto = {}));
|
||||
2137
scripts/wallet.js
Normal file
2137
scripts/wallet.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user