Bug fixes and UI/UX improvements
-- added routing -- added support for multiple receivers -- added conversion page
This commit is contained in:
parent
8abb304d2e
commit
ecd35bac55
131
css/main.css
131
css/main.css
@ -703,6 +703,7 @@ menu {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#logo {
|
||||
@ -746,40 +747,72 @@ theme-toggle {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.header {
|
||||
justify-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#homepage {
|
||||
#main_card {
|
||||
display: grid;
|
||||
gap: 3rem 1rem;
|
||||
padding-top: 0;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-rows: auto 1fr auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: rgba(var(--foreground-color), 1);
|
||||
}
|
||||
|
||||
#page_header {
|
||||
padding: 1rem 0;
|
||||
align-items: center;
|
||||
}
|
||||
#page_header button {
|
||||
margin-left: -0.7rem;
|
||||
}
|
||||
#page_header h3 {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.page h3.heading {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.page > h3.heading {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
main {
|
||||
#main_navbar {
|
||||
display: flex;
|
||||
background: rgba(var(--text-color), 0.03);
|
||||
}
|
||||
#main_navbar.hide-away {
|
||||
position: absolute;
|
||||
}
|
||||
#main_navbar ul {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
#main_navbar ul li {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
padding: 1.5rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.5rem 0.3rem;
|
||||
color: var(--text-color);
|
||||
font-size: 0.8rem;
|
||||
border-radius: 0.3rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.nav-item .icon {
|
||||
width: 2rem;
|
||||
transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
.nav-item__title {
|
||||
transition: opacity 0.2s, transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
.nav-item--active {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
.nav-item--active .icon {
|
||||
fill: var(--accent-color);
|
||||
}
|
||||
.nav-item__indicator {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 2rem;
|
||||
height: 0.3rem;
|
||||
background: var(--accent-color);
|
||||
border-radius: 1rem 1rem 0 0;
|
||||
z-index: 1;
|
||||
view-transition-name: indicator;
|
||||
}
|
||||
|
||||
#page_container {
|
||||
padding: max(1rem, 1.5vw);
|
||||
}
|
||||
|
||||
.primary-action {
|
||||
@ -923,6 +956,46 @@ main {
|
||||
.popup__header {
|
||||
padding: 1rem 1.5rem 0 1.5rem;
|
||||
}
|
||||
#main_card {
|
||||
grid-template-areas: "header" ".";
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.05), 0 1rem 3rem rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
#main_card:not(.nav-hidden) {
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-areas: "nav header" "nav .";
|
||||
}
|
||||
#main_header {
|
||||
grid-area: header;
|
||||
}
|
||||
#main_navbar {
|
||||
grid-area: nav;
|
||||
border-top: none;
|
||||
flex-direction: column;
|
||||
background-color: rgba(37, 110, 255, 0.03);
|
||||
}
|
||||
#main_navbar ul {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 0.3rem;
|
||||
}
|
||||
.nav-item {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
padding: 0.8rem 1rem;
|
||||
}
|
||||
.nav-item__indicator {
|
||||
width: 0.25rem;
|
||||
height: 50%;
|
||||
left: 0;
|
||||
border-radius: 0 1rem 1rem 0;
|
||||
bottom: auto;
|
||||
}
|
||||
body[data-theme=dark] #main_navbar {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
#generate_address_popup,
|
||||
#convert_to_taproot_popup {
|
||||
--width: 28rem;
|
||||
|
||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
132
css/main.scss
132
css/main.scss
@ -653,6 +653,7 @@ menu {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
#logo {
|
||||
color: inherit;
|
||||
@ -692,38 +693,70 @@ theme-toggle {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
.header {
|
||||
justify-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
#homepage {
|
||||
#main_card {
|
||||
display: grid;
|
||||
gap: 3rem 1rem;
|
||||
padding-top: 0;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-rows: auto 1fr auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: rgba(var(--foreground-color), 1);
|
||||
}
|
||||
#page_header {
|
||||
padding: 1rem 0;
|
||||
align-items: center;
|
||||
button {
|
||||
margin-left: -0.7rem;
|
||||
}
|
||||
h3 {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
.page {
|
||||
h3.heading {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
& > h3.heading {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
main {
|
||||
#main_navbar {
|
||||
display: flex;
|
||||
background: rgba(var(--text-color), 0.03);
|
||||
&.hide-away {
|
||||
position: absolute;
|
||||
}
|
||||
ul {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
li {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.nav-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
padding: 1.5rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.5rem 0.3rem;
|
||||
color: var(--text-color);
|
||||
font-size: 0.8rem;
|
||||
border-radius: 0.3rem;
|
||||
font-weight: 500;
|
||||
.icon {
|
||||
width: 2rem;
|
||||
transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
&__title {
|
||||
transition: opacity 0.2s,
|
||||
transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
&--active {
|
||||
color: var(--accent-color);
|
||||
.icon {
|
||||
fill: var(--accent-color);
|
||||
}
|
||||
}
|
||||
&__indicator {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 2rem;
|
||||
height: 0.3rem;
|
||||
background: var(--accent-color);
|
||||
border-radius: 1rem 1rem 0 0;
|
||||
z-index: 1;
|
||||
view-transition-name: indicator;
|
||||
}
|
||||
}
|
||||
#page_container {
|
||||
padding: max(1rem, 1.5vw);
|
||||
}
|
||||
.primary-action {
|
||||
padding: 0.8rem;
|
||||
@ -841,6 +874,49 @@ main {
|
||||
.popup__header {
|
||||
padding: 1rem 1.5rem 0 1.5rem;
|
||||
}
|
||||
#main_card {
|
||||
grid-template-areas: "header" ".";
|
||||
&:not(.nav-hidden) {
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-areas: "nav header" "nav .";
|
||||
}
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.05),
|
||||
0 1rem 3rem rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
#main_header {
|
||||
grid-area: header;
|
||||
}
|
||||
#main_navbar {
|
||||
grid-area: nav;
|
||||
border-top: none;
|
||||
flex-direction: column;
|
||||
background-color: rgba(37 110 255/ 0.03);
|
||||
ul {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 0.3rem;
|
||||
}
|
||||
}
|
||||
.nav-item {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
padding: 0.8rem 1rem;
|
||||
&__indicator {
|
||||
width: 0.25rem;
|
||||
height: 50%;
|
||||
left: 0;
|
||||
border-radius: 0 1rem 1rem 0;
|
||||
bottom: auto;
|
||||
}
|
||||
}
|
||||
body[data-theme="dark"] {
|
||||
#main_navbar {
|
||||
background-color: rgba(0 0 0/ 0.2);
|
||||
}
|
||||
}
|
||||
#generate_address_popup,
|
||||
#convert_to_taproot_popup {
|
||||
--width: 28rem;
|
||||
|
||||
459
index.html
459
index.html
@ -18,7 +18,7 @@
|
||||
<button class="button button--primary confirm-button">OK</button>
|
||||
</div>
|
||||
</sm-popup>
|
||||
<main class="page">
|
||||
<main id="main_card">
|
||||
<header id="main_header">
|
||||
<a href="#/home" id="logo" class="app-brand">
|
||||
<svg id="main_logo" class="icon" viewBox="0 0 27.25 32">
|
||||
@ -35,106 +35,35 @@
|
||||
</a>
|
||||
<theme-toggle></theme-toggle>
|
||||
</header>
|
||||
<menu class="flex gap-0-5">
|
||||
<div id="page_container"> </div>
|
||||
<nav id="main_navbar">
|
||||
<ul id="menu">
|
||||
<li>
|
||||
<button class="button button--colored primary-action" onclick="openPopup('generate_address_popup')">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
fill="#000000">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path
|
||||
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z" />
|
||||
</svg>
|
||||
Create new address
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="button button--colored primary-action" onclick="openPopup('convert_to_taproot_popup')">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
fill="#000000">
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M6.99 11L3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z" />
|
||||
</svg>
|
||||
Retrieve Taproot address
|
||||
</button>
|
||||
</li>
|
||||
</menu>
|
||||
<h3>
|
||||
Perform Transaction
|
||||
</h3>
|
||||
<sm-form>
|
||||
<div class="flex flex-direction-column gap-0-5">
|
||||
<div class="flex space-between align-center">
|
||||
<h4>Sender</h4>
|
||||
<button id="check_balance_button" class="button button--small button--colored" disabled
|
||||
onclick="checkBalance()">
|
||||
Check balance
|
||||
</button>
|
||||
</div>
|
||||
<sm-input id="private_key_input" placeholder="Sender's private key" data-private-key
|
||||
class="password-field" type="password" required>
|
||||
<svg class="icon" slot="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24"
|
||||
height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
<g>
|
||||
<rect fill="none" height="24" width="24"></rect>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M21,10h-8.35C11.83,7.67,9.61,6,7,6c-3.31,0-6,2.69-6,6s2.69,6,6,6c2.61,0,4.83-1.67,5.65-4H13l2,2l2-2l2,2l4-4.04L21,10z M7,15c-1.65,0-3-1.35-3-3c0-1.65,1.35-3,3-3s3,1.35,3,3C10,13.65,8.65,15,7,15z">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
<label slot="right" class="interact">
|
||||
<input type="checkbox" class="hidden" autocomplete="off" readonly=""
|
||||
onchange="togglePrivateKeyVisibility(this)">
|
||||
<svg class="icon invisible" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
|
||||
<a href="#/home" class="nav-item nav-item--active interactive">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
|
||||
width="24px" fill="#000000">
|
||||
<title>Hide password</title>
|
||||
<path d="M0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M0 0h24v24H0V0z" fill="none"></path>
|
||||
<path
|
||||
d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z">
|
||||
d="M4.01 6.03l7.51 3.22-7.52-1 .01-2.22m7.5 8.72L4 17.97v-2.22l7.51-1M2.01 3L2 10l15 2-15 2 .01 7L23 12 2.01 3z">
|
||||
</path>
|
||||
</svg>
|
||||
<svg class="icon visible" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
|
||||
<span class="nav-item__title">
|
||||
Send
|
||||
</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#/convert" class="nav-item interactive">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
|
||||
width="24px" fill="#000000">
|
||||
<title>Show password</title>
|
||||
<path d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path
|
||||
d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z">
|
||||
</path>
|
||||
<path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z"></path>
|
||||
</svg>
|
||||
</label>
|
||||
</sm-input>
|
||||
<div id="sender_balance_container" class="flex align-center gap-0-3 hidden"></div>
|
||||
</div>
|
||||
<div class="flex flex-direction-column gap-1">
|
||||
<div class="flex flex-direction-column gap-0-5">
|
||||
<h4>Receiver</h4>
|
||||
<sm-input id="receiver_address_input" placeholder="Receiver's address" data-btc-address
|
||||
required></sm-input>
|
||||
<sm-input id="amount_input" placeholder="Amount" type="number" step="0.00000001" min="0.0000001"
|
||||
error-text="Amount should be grater than 0.0000001 BTC" required>
|
||||
<div class="currency-symbol flex" slot="icon">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24"
|
||||
height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
<g>
|
||||
<rect fill="none" height="24" width="24"></rect>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
d="M17.06,11.57C17.65,10.88,18,9.98,18,9c0-1.86-1.27-3.43-3-3.87L15,3h-2v2h-2V3H9v2H6v2h2v10H6v2h3v2h2v-2h2v2h2v-2 c2.21,0,4-1.79,4-4C19,13.55,18.22,12.27,17.06,11.57z M10,7h4c1.1,0,2,0.9,2,2s-0.9,2-2,2h-4V7z M15,17h-5v-4h5c1.1,0,2,0.9,2,2 S16.1,17,15,17z">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</sm-input>
|
||||
</div>
|
||||
<sm-input id="fee_input" placeholder="Fee" required></sm-input>
|
||||
<div class="multi-state-button">
|
||||
<button id="send_tx_button" class="button button--primary" type="submit" disabled
|
||||
onclick="sendTx()">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</sm-form>
|
||||
<span class="nav-item__title">
|
||||
Convert
|
||||
</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</main>
|
||||
<sm-popup id="generate_address_popup">
|
||||
<header slot="header" class="popup__header">
|
||||
@ -177,7 +106,7 @@
|
||||
</header>
|
||||
<section class="grid gap-1-5">
|
||||
<div class="grid gap-0-5">
|
||||
<h4>Convert to Taproot</h4>
|
||||
<h4>Retrieve Taproot address</h4>
|
||||
<p>Get Taproot address from your bitcoin private key</p>
|
||||
</div>
|
||||
<sm-form id="convert_to_taproot_form">
|
||||
@ -204,7 +133,7 @@
|
||||
</label>
|
||||
</sm-input>
|
||||
<button class="button button--primary cta" type="submit"
|
||||
onclick="convertToTaproot()">Convert</button>
|
||||
onclick="retrieveTaproot()">Convert</button>
|
||||
</div>
|
||||
</sm-form>
|
||||
<div id="converted_address_wrapper" class="hidden grid gap-0-5">
|
||||
@ -232,6 +161,8 @@
|
||||
<script src="scripts/btcOperator.js"></script>
|
||||
<script src="scripts/floCrypto.js"></script>
|
||||
<script src="scripts/tap_combined.js" type="text/javascript"></script>
|
||||
<script src="scripts/keccak.js"></script>
|
||||
<script src="scripts/floEthereum.js"></script>
|
||||
<script id="ui_utils">
|
||||
const uiGlobals = {}
|
||||
const { html, svg, render: renderElem } = uhtml;
|
||||
@ -359,7 +290,7 @@
|
||||
const { message = '', cancelText = 'Cancel', confirmText = 'OK', danger = false } = options
|
||||
openPopup('confirmation_popup', true)
|
||||
getRef('confirm_title').innerText = title;
|
||||
getRef('confirm_message').innerText = message;
|
||||
renderElem(getRef('confirm_message'), message);
|
||||
const cancelButton = getRef('confirmation_popup').querySelector('.cancel-button');
|
||||
const confirmButton = getRef('confirmation_popup').querySelector('.confirm-button')
|
||||
confirmButton.textContent = confirmText
|
||||
@ -435,6 +366,7 @@
|
||||
createRipple(e, e.target.closest("button, .interactive"));
|
||||
}
|
||||
});
|
||||
router.routeTo(location.hash)
|
||||
});
|
||||
function createRipple(event, target) {
|
||||
const circle = document.createElement("span");
|
||||
@ -559,8 +491,108 @@
|
||||
transform: 'translateY(-1.5rem)'
|
||||
},
|
||||
]
|
||||
class Router {
|
||||
/**
|
||||
* @constructor {object} options - options for the router
|
||||
* @param {object} options.routes - routes for the router
|
||||
* @param {object} options.state - initial state for the router
|
||||
* @param {function} options.routingStart - function to be called before routing
|
||||
* @param {function} options.routingEnd - function to be called after routing
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
const { routes = {}, state = {}, routingStart, routingEnd } = options
|
||||
this.routes = routes
|
||||
this.state = state
|
||||
this.routingStart = routingStart
|
||||
this.routingEnd = routingEnd
|
||||
this.lastPage = null
|
||||
window.addEventListener('hashchange', e => this.routeTo(window.location.hash))
|
||||
}
|
||||
/**
|
||||
* @param {string} route - route to be added
|
||||
* @param {function} callback - function to be called when route is matched
|
||||
*/
|
||||
addRoute(route, callback) {
|
||||
this.routes[route] = callback
|
||||
}
|
||||
/**
|
||||
* @param {string} route
|
||||
*/
|
||||
handleRouting = async (page) => {
|
||||
if (this.routingStart) {
|
||||
this.routingStart(this.state)
|
||||
}
|
||||
if (this.routes[page]) {
|
||||
// Fallback for browsers that don't support View transition API:
|
||||
await this.routes[page](this.state)
|
||||
this.lastPage = page
|
||||
} else {
|
||||
if (this.routes['404']) {
|
||||
this.routes['404'](this.state);
|
||||
} else {
|
||||
console.error(`No route found for '${page}' and no '404' route is defined.`);
|
||||
}
|
||||
}
|
||||
if (this.routingEnd) {
|
||||
this.routingEnd(this.state)
|
||||
}
|
||||
}
|
||||
async routeTo(path) {
|
||||
try {
|
||||
let page
|
||||
let wildcards = []
|
||||
let queryString
|
||||
let params
|
||||
[path, queryString] = path.split('?');
|
||||
if (path.includes('#'))
|
||||
path = path.split('#')[1];
|
||||
if (path.includes('/'))
|
||||
[, page, ...wildcards] = path.split('/')
|
||||
else
|
||||
page = path
|
||||
this.state = { page, wildcards, lastPage: this.lastPage }
|
||||
if (queryString) {
|
||||
params = new URLSearchParams(queryString)
|
||||
this.state.params = Object.fromEntries(params)
|
||||
}
|
||||
if (document.startViewTransition) {
|
||||
document.startViewTransition(async () => {
|
||||
await this.handleRouting(page)
|
||||
})
|
||||
} else {
|
||||
await this.handleRouting(page)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
window.smCompConfig = {
|
||||
'sm-input': [
|
||||
{
|
||||
selector: '[data-btc-address]',
|
||||
customValidation: (value) => {
|
||||
if (!value) return { isValid: false, errorText: 'Please enter a FLO address' }
|
||||
return {
|
||||
isValid: btcOperator.validateAddress(value),
|
||||
errorText: `Invalid address.<br> It usually starts with "1", "3" or "bc1"`
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: '[data-private-key]',
|
||||
customValidation: (value) => {
|
||||
if (!value) return { isValid: false, errorText: 'Please enter a private key' }
|
||||
return {
|
||||
isValid: floCrypto.getPubKeyHex(value),
|
||||
errorText: `Invalid private key.<br> It usually starts with "L" or "R"`
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
function formatAmount(amount = 0, currency = 'btc') {
|
||||
// check if amount is a string and convert it to a number
|
||||
if (typeof amount === 'string') {
|
||||
@ -575,6 +607,102 @@
|
||||
target.type = target.type === 'password' ? 'text' : 'password';
|
||||
target.focusIn()
|
||||
}
|
||||
|
||||
const router = new Router({
|
||||
routingStart(state) {
|
||||
},
|
||||
routingEnd(state) {
|
||||
const { page } = state
|
||||
const previousTarget = getRef('main_navbar').querySelector('.nav-item--active')
|
||||
previousTarget?.classList.remove('nav-item--active')
|
||||
previousTarget.querySelector('.nav-item__indicator')?.remove()
|
||||
const target = getRef('main_navbar').querySelector(`.nav-item[href="#/${page}"]`)
|
||||
target.classList.add('nav-item--active')
|
||||
target.append(html.node`<div class="nav-item__indicator"></div>`)
|
||||
}
|
||||
})
|
||||
router.addRoute('', (state) => {
|
||||
history.replaceState({}, '', '#/home')
|
||||
renderHome(state)
|
||||
})
|
||||
router.addRoute('home', renderHome)
|
||||
function renderHome(state) {
|
||||
console.log(state)
|
||||
renderElem(getRef('page_container'), html`
|
||||
<div class="flex flex-direction-column gap-1-5">
|
||||
<menu class="flex gap-0-5">
|
||||
<li>
|
||||
<button class="button button--colored primary-action" onclick="openPopup('generate_address_popup')">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"> <path d="M0 0h24v24H0V0z" fill="none" /> <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z" /> </svg>
|
||||
Create new address
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button class="button button--colored primary-action" onclick="openPopup('convert_to_taproot_popup')">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"> <path d="M0 0h24v24H0V0z" fill="none" /> <path d="M6.99 11L3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z" /> </svg>
|
||||
Retrieve Taproot address
|
||||
</button>
|
||||
</li>
|
||||
</menu>
|
||||
<h3>
|
||||
Perform Transaction
|
||||
</h3>
|
||||
<sm-form>
|
||||
<div class="flex flex-direction-column gap-0-5">
|
||||
<div class="flex space-between align-center">
|
||||
<h4>Sender</h4>
|
||||
<button id="check_balance_button" class="button button--small button--colored" disabled onclick="checkBalance()">
|
||||
Check balance
|
||||
</button>
|
||||
</div>
|
||||
<sm-input id="private_key_input" placeholder="Sender's private key" data-private-key class="password-field" type="password" oninput=${handlePrivateKeyInput} required>
|
||||
<svg class="icon" slot="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"> <g> <rect fill="none" height="24" width="24"></rect> </g> <g> <path d="M21,10h-8.35C11.83,7.67,9.61,6,7,6c-3.31,0-6,2.69-6,6s2.69,6,6,6c2.61,0,4.83-1.67,5.65-4H13l2,2l2-2l2,2l4-4.04L21,10z M7,15c-1.65,0-3-1.35-3-3c0-1.65,1.35-3,3-3s3,1.35,3,3C10,13.65,8.65,15,7,15z"> </path> </g> </svg>
|
||||
<label slot="right" class="interact">
|
||||
<input type="checkbox" class="hidden" autocomplete="off" readonly="" onchange="togglePrivateKeyVisibility(this)">
|
||||
<svg class="icon invisible" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"> <title>Hide password</title> <path d="M0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0z" fill="none"></path> <path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"> </path> </svg>
|
||||
<svg class="icon visible" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"> <title>Show password</title> <path d="M0 0h24v24H0z" fill="none"></path> <path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"> </path> </svg>
|
||||
</label>
|
||||
</sm-input>
|
||||
<div id="sender_balance_container" class="flex align-center gap-0-3 hidden"></div>
|
||||
</div>
|
||||
<div class="flex flex-direction-column gap-1">
|
||||
<div class="flex flex-direction-column gap-0-5">
|
||||
<h4>Receiver</h4>
|
||||
<ul id="receivers_container" class="grid gap-1">
|
||||
<li class="grid gap-0-5 receiver-wrapper">
|
||||
<sm-input class="receiver-address" placeholder="Receiver's address" data-btc-address required></sm-input>
|
||||
<sm-input class="receiver-amount" placeholder="Amount" type="number" step="0.00000001" min="0.0000001" error-text="Amount should be grater than 0.0000001 BTC" required>
|
||||
<div class="currency-symbol flex" slot="icon">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"> <g> <rect fill="none" height="24" width="24"></rect> </g> <g> <path d="M17.06,11.57C17.65,10.88,18,9.98,18,9c0-1.86-1.27-3.43-3-3.87L15,3h-2v2h-2V3H9v2H6v2h2v10H6v2h3v2h2v-2h2v2h2v-2 c2.21,0,4-1.79,4-4C19,13.55,18.22,12.27,17.06,11.57z M10,7h4c1.1,0,2,0.9,2,2s-0.9,2-2,2h-4V7z M15,17h-5v-4h5c1.1,0,2,0.9,2,2 S16.1,17,15,17z"> </path> </g> </svg>
|
||||
</div>
|
||||
</sm-input>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="button button--colored button--small margin-right-auto" onclick="addReceiver()">
|
||||
Add receiver
|
||||
</button>
|
||||
</div>
|
||||
<sm-input id="fee_input" placeholder="Fee" required></sm-input>
|
||||
<div class="multi-state-button">
|
||||
<button id="send_tx_button" class="button button--primary" type="submit" disabled onclick="sendTx()">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</sm-form>
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
router.addRoute('convert', (state) => {
|
||||
renderElem(getRef('page_container'), html`
|
||||
|
||||
`)
|
||||
})
|
||||
|
||||
router.addRoute('404', () => {
|
||||
renderElem(getRef('page_container'), html`
|
||||
<h1>Page not found</h1>
|
||||
`)
|
||||
})
|
||||
|
||||
function checkBalance() {
|
||||
const wif = getRef('private_key_input').value.trim()
|
||||
if (!wif)
|
||||
@ -593,14 +721,14 @@
|
||||
notify(e, 'error')
|
||||
})
|
||||
}
|
||||
getRef('private_key_input').addEventListener('input', e => {
|
||||
function handlePrivateKeyInput(e) {
|
||||
if (!e.target.isValid) {
|
||||
getRef('sender_balance_container').classList.add('hidden')
|
||||
getRef('check_balance_button').disabled = true
|
||||
} else {
|
||||
getRef('check_balance_button').disabled = false
|
||||
}
|
||||
})
|
||||
}
|
||||
getRef('convert_to_taproot_form').addEventListener('invalid', () => {
|
||||
if (!document.startViewTransition) {
|
||||
getRef('converted_address_wrapper').classList.add('hidden')
|
||||
@ -610,7 +738,7 @@
|
||||
getRef('converted_address_wrapper').classList.add('hidden')
|
||||
})
|
||||
})
|
||||
function convertToTaproot() {
|
||||
function retrieveTaproot() {
|
||||
function convert() {
|
||||
let wif = getRef('convert_to_taproot_input').value.trim();
|
||||
getRef('converted_address_wrapper').classList.remove('hidden');
|
||||
@ -624,24 +752,70 @@
|
||||
convert()
|
||||
})
|
||||
}
|
||||
function addReceiver() {
|
||||
getRef('receivers_container').append(html.node`
|
||||
<div class="grid gap-0-5 receiver-wrapper">
|
||||
<sm-input class="receiver-address" placeholder="Receiver's address" data-btc-address
|
||||
required></sm-input>
|
||||
<div class="flex gap-0-5">
|
||||
<sm-input class="receiver-amount flex-1" placeholder="Amount" type="number" step="0.00000001" min="0.0000001" error-text="Amount should be grater than 0.0000001 BTC" required>
|
||||
<div class="currency-symbol flex" slot="icon">
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"> <g> <rect fill="none" height="24" width="24"></rect> </g> <g> <path d="M17.06,11.57C17.65,10.88,18,9.98,18,9c0-1.86-1.27-3.43-3-3.87L15,3h-2v2h-2V3H9v2H6v2h2v10H6v2h3v2h2v-2h2v2h2v-2 c2.21,0,4-1.79,4-4C19,13.55,18.22,12.27,17.06,11.57z M10,7h4c1.1,0,2,0.9,2,2s-0.9,2-2,2h-4V7z M15,17h-5v-4h5c1.1,0,2,0.9,2,2 S16.1,17,15,17z"> </path> </g> </svg>
|
||||
</div>
|
||||
</sm-input>
|
||||
<button class="button button--danger" onclick="removeReceiver(this)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
function removeReceiver(button) {
|
||||
button.closest('.receiver-wrapper').remove()
|
||||
}
|
||||
async function sendTx() {
|
||||
const senderPrivateKey = getRef('private_key_input').value.trim(),
|
||||
receiverAddress = getRef('receiver_address_input').value.trim(),
|
||||
amount = getRef('amount_input').value.trim(),
|
||||
fee = getRef('fee_input').value.trim();
|
||||
if (!senderPrivateKey || !receiverAddress || !amount || !fee)
|
||||
return notify('Please fill all the fields', 'error')
|
||||
const senderPrivateKey = getRef('private_key_input').value.trim();
|
||||
const receivers = [...getRef('receivers_container').children].reduce((receivers, receiver) => {
|
||||
const receiverAddress = receiver.querySelector('.receiver-address').value.trim()
|
||||
const amount = parseFloat(receiver.querySelector('.receiver-amount').value.trim())
|
||||
if (!receivers[receiverAddress])
|
||||
receivers[receiverAddress] = 0
|
||||
receivers[receiverAddress] += amount
|
||||
return receivers
|
||||
}, {})
|
||||
console.log(receivers, Object.keys(receivers), Object.values(receivers))
|
||||
const fee = parseFloat(getRef('fee_input').value.trim())
|
||||
const senderAddress = getTaprootAddress(senderPrivateKey).tr.address
|
||||
if (btcOperator.validateAddress(senderAddress) !== 'bech32m')
|
||||
return notify('Sender address is not a Taproot address', 'error')
|
||||
const confirmation = await getConfirmation('Confirm transaction', {
|
||||
message: `Send ${formatAmount(amount)} to ${receiverAddress} with a fee of ${formatAmount(fee)}?`,
|
||||
message: html`
|
||||
<div class="grid gap-1-5">
|
||||
<div class="grid gap-0-5">
|
||||
<span class="label">Sender address</span>
|
||||
<sm-copy value=${senderAddress}></sm-copy>
|
||||
</div>
|
||||
<div class="grid gap-0-5">
|
||||
<span class="label">Receivers</span>
|
||||
<div class="grid gap-0-3">
|
||||
${Object.entries(receivers).map(([address, amount]) => html.node`
|
||||
<div class="grid gap-0-5" style="padding:0.5rem;border:solid thin rgba(var(--text-color),0.3);border-radius: 0.3rem;">
|
||||
<b>${address}</b>
|
||||
<b>${formatAmount(amount)}</b>
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid gap-0-5">
|
||||
<span class="label">Fee</span>
|
||||
<b>${formatAmount(fee)}</b>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
confirmText: 'Send',
|
||||
})
|
||||
if (!confirmation)
|
||||
return;
|
||||
buttonLoader('send_tx_button', true)
|
||||
createTx(senderPrivateKey, receiverAddress, amount, fee).then(txHex => {
|
||||
createTx(senderPrivateKey, Object.keys(receivers), Object.values(receivers), fee).then(txHex => {
|
||||
btcOperator.broadcastTx(txHex).then(txid => {
|
||||
notify(`Transaction sent successfully. Txid: ${txid}`, 'success')
|
||||
showTransactionResult(true, txid)
|
||||
@ -695,6 +869,24 @@
|
||||
const DUST_AMT = 546,
|
||||
MIN_FEE_UPDATE = 219;
|
||||
|
||||
const SATOSHI_IN_BTC = 1e8;
|
||||
const BASE_TX_SIZE = 12,
|
||||
BASE_INPUT_SIZE = 41,
|
||||
LEGACY_INPUT_SIZE = 107,
|
||||
BECH32_INPUT_SIZE = 27,
|
||||
SEGWIT_INPUT_SIZE = 59,
|
||||
BECH32M_INPUT_SIZE = 39, // Check this later
|
||||
BASE_OUTPUT_SIZE = 9,
|
||||
LEGACY_OUTPUT_SIZE = 25,
|
||||
BECH32_OUTPUT_SIZE = 23,
|
||||
SEGWIT_OUTPUT_SIZE = 23,
|
||||
BECH32M_OUTPUT_SIZE = 35; // Check this later
|
||||
|
||||
const util = {};
|
||||
|
||||
util.Sat_to_BTC = value => BigInt(parseFloat((value / SATOSHI_IN_BTC).toFixed(8)));
|
||||
util.BTC_to_Sat = value => BigInt(parseInt(value * SATOSHI_IN_BTC));
|
||||
|
||||
const fetch_api = function (api, json_res = true) {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.debug(URL + api);
|
||||
@ -711,14 +903,6 @@
|
||||
}).catch(error => reject(error))
|
||||
})
|
||||
};
|
||||
|
||||
const SATOSHI_IN_BTC = 1e8;
|
||||
|
||||
const util = {};
|
||||
|
||||
util.Sat_to_BTC = value => BigInt(parseFloat((value / SATOSHI_IN_BTC).toFixed(8)));
|
||||
util.BTC_to_Sat = value => BigInt(parseInt(value * SATOSHI_IN_BTC));
|
||||
|
||||
function getTaprootAddress(wif) {
|
||||
if (!wif)
|
||||
wif = coinjs.newKeys().wif
|
||||
@ -731,20 +915,6 @@
|
||||
wif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const BASE_TX_SIZE = 12,
|
||||
BASE_INPUT_SIZE = 41,
|
||||
LEGACY_INPUT_SIZE = 107,
|
||||
BECH32_INPUT_SIZE = 27,
|
||||
SEGWIT_INPUT_SIZE = 59,
|
||||
BECH32M_INPUT_SIZE = 39, // Check this later
|
||||
BASE_OUTPUT_SIZE = 9,
|
||||
LEGACY_OUTPUT_SIZE = 25,
|
||||
BECH32_OUTPUT_SIZE = 23,
|
||||
SEGWIT_OUTPUT_SIZE = 23,
|
||||
BECH32M_OUTPUT_SIZE = 35; // Check this later
|
||||
|
||||
function _sizePerInput(addr) {
|
||||
switch (coinjs.addressDecode(addr).type) {
|
||||
case "standard":
|
||||
@ -770,26 +940,42 @@
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async function createTx(senderPrivateKey, receiver, amount, fee) {
|
||||
/**
|
||||
* @param {string} senderPrivateKey
|
||||
* @param {array} array of receivers
|
||||
* @param {array} array of amounts in BTC
|
||||
* @param {number} fee in BTC
|
||||
*/
|
||||
async function createTx(senderPrivateKey, receivers = [], amounts = [], fee) {
|
||||
try {
|
||||
const { tr: { address, script } } = getTaprootAddress(senderPrivateKey)
|
||||
const amountInSat = util.BTC_to_Sat(amount)
|
||||
const opts = {};
|
||||
const tx = new taproot.Transaction(opts);
|
||||
|
||||
const totalAmount = amounts.reduce((total, amount) => total + amount, 0)
|
||||
const amountInSat = util.BTC_to_Sat(totalAmount)
|
||||
// check if sender has enough balance
|
||||
btcOperator.getBalance(address).then(balance => {
|
||||
if (balance < totalAmount + fee)
|
||||
throw new Error(`Insufficient balance. Balance: ${balance}, Required: ${totalAmount + fee}`)
|
||||
}).catch(err => {
|
||||
throw new Error(err)
|
||||
})
|
||||
await addUTXOs(tx, tr, [address], util.BTC_to_Sat(amount))
|
||||
|
||||
tx.addOutputAddress(receiver, amountInSat);
|
||||
// add receivers
|
||||
receivers.forEach((receiver, i) => {
|
||||
tx.addOutputAddress(receiver, util.BTC_to_Sat(amounts[i]))
|
||||
})
|
||||
// add change address
|
||||
tx.addOutputAddress(address, tx.inputAmount - amountInSat - util.BTC_to_Sat(fee));
|
||||
tx.sign(privKey_arrayform, undefined, new Uint8Array(32));
|
||||
|
||||
tx.sign(privKey_arrayform, undefined, new Uint8Array(32));
|
||||
tx.finalize()
|
||||
return tx.hex
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
const testTaproot = 'bc1p05whkacavgmh77pgsr7v5k4tyg28x3pqyjanfnjkzzdngqur4yks39w8zk'
|
||||
const testTaproot = 'bc1p05whkacavgmh77pgsr7v5k4tyg28x3pqyjanfnjkzzdngqur4yks39w8zk' //remove this later
|
||||
function addUTXOs(tx, tr, senders = [testTaproot], required_amount, fee_rate, rec_args = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
required_amount = parseFloat(required_amount.toFixed(8));
|
||||
@ -816,6 +1002,7 @@
|
||||
continue;
|
||||
const { tx_hash, tx_index, value } = utxos[i];
|
||||
// tx.addinput(utxos[i].tx_hash_big_endian, utxos[i].tx_output_n, script, 0xfffffffd /*sequence*/); //0xfffffffd for Replace-by-fee
|
||||
// changes for taproot
|
||||
const input = { txid: tx_hash, index: tx_index, script: tr.script, amount: value }
|
||||
tx.addInput({ ...input, ...tr, witnessUtxo: { script: input.script, amount: input.amount }, });
|
||||
|
||||
|
||||
51
scripts/floEthereum.js
Normal file
51
scripts/floEthereum.js
Normal file
@ -0,0 +1,51 @@
|
||||
(function (EXPORTS) { //floEthereum v1.0.1a
|
||||
/* FLO Ethereum Operators */
|
||||
/* Make sure you added Taproot, Keccak, FLO and BTC Libraries before */
|
||||
'use strict';
|
||||
const floEthereum = EXPORTS;
|
||||
|
||||
const ethAddressFromPrivateKey = floEthereum.ethAddressFromPrivateKey = function(privateKey){
|
||||
var t1,t2,t3,t4;
|
||||
|
||||
t1 = secp.Point.fromPrivateKey(hex.decode(privateKey));
|
||||
if (!t1.hasEvenY()) { t1 = t1.negate(); }
|
||||
t2 = t1.x.toString(16) + t1.y.toString(16);
|
||||
t3 = keccak.keccak_256(Crypto.util.hexToBytes(t2));
|
||||
t4 = keccak.extractLast20Bytes(t3);
|
||||
return "0x" + t4;
|
||||
}
|
||||
|
||||
const ethAddressFromCompressedPublicKey = floEthereum.ethAddressFromCompressedPublicKey = function(compressedPublicKey){
|
||||
var t1,t2,t3,t4;
|
||||
t1 = coinjs.compressedToUncompressed(compressedPublicKey);
|
||||
t2 = t1.slice(2);
|
||||
t3 = keccak.keccak_256(Crypto.util.hexToBytes(t2));
|
||||
t4 = keccak.extractLast20Bytes(t3);
|
||||
return "0x" + t4;
|
||||
}
|
||||
|
||||
const ethPrivateKeyFromUntweakedPrivateKey = floEthereum.ethPrivateKeyFromUntweakedPrivateKey = function(untweakedPrivateKey) {
|
||||
var t1;
|
||||
t1 = hex.encode(taproot.taprootTweakPrivKey(hex.decode(untweakedPrivateKey)));
|
||||
return t1;
|
||||
}
|
||||
|
||||
const ethAddressFromUntweakedPrivateKey = floEthereum.ethAddressFromUntweakedPrivateKey = function(untweakedPrivateKey) {
|
||||
var t1,t2;
|
||||
t1 = hex.encode(taproot.taprootTweakPrivKey(hex.decode(untweakedPrivateKey)));
|
||||
t2 = ethAddressFromPrivateKey(t1);
|
||||
return t2;
|
||||
}
|
||||
|
||||
const ethAddressFromTaprootAddress = floEthereum.ethAddressFromTaprootAddress = function(taprootAddress) {
|
||||
var t1,t2,t3,t4;
|
||||
t1 = coinjs.addressDecode(taprootAddress);
|
||||
t2 = t1.outstring.slice(4);
|
||||
t3 = "02" + t2;
|
||||
t4 = ethAddressFromCompressedPublicKey(t3);
|
||||
return t4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
})('object' === typeof module ? module.exports : window.floEthereum = {});
|
||||
674
scripts/keccak.js
Normal file
674
scripts/keccak.js
Normal file
@ -0,0 +1,674 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var INPUT_ERROR = 'input is invalid type';
|
||||
var FINALIZE_ERROR = 'finalize already called';
|
||||
var WINDOW = typeof window === 'object';
|
||||
var root = WINDOW ? (window.keccak = window.keccak || {}) : {};
|
||||
if (root.JS_SHA3_NO_WINDOW) {
|
||||
WINDOW = false;
|
||||
}
|
||||
var WEB_WORKER = !WINDOW && typeof self === 'object';
|
||||
var NODE_JS = !root.JS_SHA3_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
|
||||
if (NODE_JS) {
|
||||
root = global;
|
||||
} else if (WEB_WORKER) {
|
||||
root = self;
|
||||
}
|
||||
var COMMON_JS = !root.JS_SHA3_NO_COMMON_JS && typeof module === 'object' && module.exports;
|
||||
var AMD = typeof define === 'function' && define.amd;
|
||||
var ARRAY_BUFFER = !root.JS_SHA3_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
|
||||
var HEX_CHARS = '0123456789abcdef'.split('');
|
||||
var SHAKE_PADDING = [31, 7936, 2031616, 520093696];
|
||||
var CSHAKE_PADDING = [4, 1024, 262144, 67108864];
|
||||
var KECCAK_PADDING = [1, 256, 65536, 16777216];
|
||||
var PADDING = [6, 1536, 393216, 100663296];
|
||||
var SHIFT = [0, 8, 16, 24];
|
||||
var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
|
||||
0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
|
||||
2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
|
||||
2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
|
||||
2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
|
||||
var BITS = [224, 256, 384, 512];
|
||||
var SHAKE_BITS = [128, 256];
|
||||
var OUTPUT_TYPES = ['hex', 'buffer', 'arrayBuffer', 'array', 'digest'];
|
||||
var CSHAKE_BYTEPAD = {
|
||||
'128': 168,
|
||||
'256': 136
|
||||
};
|
||||
|
||||
|
||||
var isArray = root.JS_SHA3_NO_NODE_JS || !Array.isArray
|
||||
? function (obj) {
|
||||
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||
}
|
||||
: Array.isArray;
|
||||
|
||||
var isView = (ARRAY_BUFFER && (root.JS_SHA3_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView))
|
||||
? function (obj) {
|
||||
return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
|
||||
}
|
||||
: ArrayBuffer.isView;
|
||||
|
||||
// [message: string, isString: bool]
|
||||
var formatMessage = function (message) {
|
||||
var type = typeof message;
|
||||
if (type === 'string') {
|
||||
return [message, true];
|
||||
}
|
||||
if (type !== 'object' || message === null) {
|
||||
throw new Error(INPUT_ERROR);
|
||||
}
|
||||
if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
|
||||
return [new Uint8Array(message), false];
|
||||
}
|
||||
if (!isArray(message) && !isView(message)) {
|
||||
throw new Error(INPUT_ERROR);
|
||||
}
|
||||
return [message, false];
|
||||
}
|
||||
|
||||
var empty = function (message) {
|
||||
return formatMessage(message)[0].length === 0;
|
||||
};
|
||||
|
||||
var createOutputMethod = function (bits, padding, outputType) {
|
||||
return function (message) {
|
||||
return new Keccak(bits, padding, bits).update(message)[outputType]();
|
||||
};
|
||||
};
|
||||
|
||||
var createShakeOutputMethod = function (bits, padding, outputType) {
|
||||
return function (message, outputBits) {
|
||||
return new Keccak(bits, padding, outputBits).update(message)[outputType]();
|
||||
};
|
||||
};
|
||||
|
||||
var createCshakeOutputMethod = function (bits, padding, outputType) {
|
||||
return function (message, outputBits, n, s) {
|
||||
return methods['cshake' + bits].update(message, outputBits, n, s)[outputType]();
|
||||
};
|
||||
};
|
||||
|
||||
var createKmacOutputMethod = function (bits, padding, outputType) {
|
||||
return function (key, message, outputBits, s) {
|
||||
return methods['kmac' + bits].update(key, message, outputBits, s)[outputType]();
|
||||
};
|
||||
};
|
||||
|
||||
var createOutputMethods = function (method, createMethod, bits, padding) {
|
||||
for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
|
||||
var type = OUTPUT_TYPES[i];
|
||||
method[type] = createMethod(bits, padding, type);
|
||||
}
|
||||
return method;
|
||||
};
|
||||
|
||||
var createMethod = function (bits, padding) {
|
||||
var method = createOutputMethod(bits, padding, 'hex');
|
||||
method.create = function () {
|
||||
return new Keccak(bits, padding, bits);
|
||||
};
|
||||
method.update = function (message) {
|
||||
return method.create().update(message);
|
||||
};
|
||||
return createOutputMethods(method, createOutputMethod, bits, padding);
|
||||
};
|
||||
|
||||
var createShakeMethod = function (bits, padding) {
|
||||
var method = createShakeOutputMethod(bits, padding, 'hex');
|
||||
method.create = function (outputBits) {
|
||||
return new Keccak(bits, padding, outputBits);
|
||||
};
|
||||
method.update = function (message, outputBits) {
|
||||
return method.create(outputBits).update(message);
|
||||
};
|
||||
return createOutputMethods(method, createShakeOutputMethod, bits, padding);
|
||||
};
|
||||
|
||||
var createCshakeMethod = function (bits, padding) {
|
||||
var w = CSHAKE_BYTEPAD[bits];
|
||||
var method = createCshakeOutputMethod(bits, padding, 'hex');
|
||||
method.create = function (outputBits, n, s) {
|
||||
if (empty(n) && empty(s)) {
|
||||
return methods['shake' + bits].create(outputBits);
|
||||
} else {
|
||||
return new Keccak(bits, padding, outputBits).bytepad([n, s], w);
|
||||
}
|
||||
};
|
||||
method.update = function (message, outputBits, n, s) {
|
||||
return method.create(outputBits, n, s).update(message);
|
||||
};
|
||||
return createOutputMethods(method, createCshakeOutputMethod, bits, padding);
|
||||
};
|
||||
|
||||
var createKmacMethod = function (bits, padding) {
|
||||
var w = CSHAKE_BYTEPAD[bits];
|
||||
var method = createKmacOutputMethod(bits, padding, 'hex');
|
||||
method.create = function (key, outputBits, s) {
|
||||
return new Kmac(bits, padding, outputBits).bytepad(['KMAC', s], w).bytepad([key], w);
|
||||
};
|
||||
method.update = function (key, message, outputBits, s) {
|
||||
return method.create(key, outputBits, s).update(message);
|
||||
};
|
||||
return createOutputMethods(method, createKmacOutputMethod, bits, padding);
|
||||
};
|
||||
|
||||
var algorithms = [
|
||||
{ name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod },
|
||||
{ name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod },
|
||||
{ name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod },
|
||||
{ name: 'cshake', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createCshakeMethod },
|
||||
{ name: 'kmac', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createKmacMethod }
|
||||
];
|
||||
|
||||
var methods = {}, methodNames = [];
|
||||
|
||||
for (var i = 0; i < algorithms.length; ++i) {
|
||||
var algorithm = algorithms[i];
|
||||
var bits = algorithm.bits;
|
||||
for (var j = 0; j < bits.length; ++j) {
|
||||
var methodName = algorithm.name + '_' + bits[j];
|
||||
methodNames.push(methodName);
|
||||
methods[methodName] = algorithm.createMethod(bits[j], algorithm.padding);
|
||||
if (algorithm.name !== 'sha3') {
|
||||
var newMethodName = algorithm.name + bits[j];
|
||||
methodNames.push(newMethodName);
|
||||
methods[newMethodName] = methods[methodName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
methodNames.push("extractLast20Bytes");
|
||||
methods["extractLast20Bytes"] = extractLast20Bytes;
|
||||
|
||||
|
||||
function Keccak(bits, padding, outputBits) {
|
||||
this.blocks = [];
|
||||
this.s = [];
|
||||
this.padding = padding;
|
||||
this.outputBits = outputBits;
|
||||
this.reset = true;
|
||||
this.finalized = false;
|
||||
this.block = 0;
|
||||
this.start = 0;
|
||||
this.blockCount = (1600 - (bits << 1)) >> 5;
|
||||
this.byteCount = this.blockCount << 2;
|
||||
this.outputBlocks = outputBits >> 5;
|
||||
this.extraBytes = (outputBits & 31) >> 3;
|
||||
|
||||
for (var i = 0; i < 50; ++i) {
|
||||
this.s[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Keccak.prototype.update = function (message) {
|
||||
if (this.finalized) {
|
||||
throw new Error(FINALIZE_ERROR);
|
||||
}
|
||||
var result = formatMessage(message);
|
||||
message = result[0];
|
||||
var isString = result[1];
|
||||
var blocks = this.blocks, byteCount = this.byteCount, length = message.length,
|
||||
blockCount = this.blockCount, index = 0, s = this.s, i, code;
|
||||
|
||||
while (index < length) {
|
||||
if (this.reset) {
|
||||
this.reset = false;
|
||||
blocks[0] = this.block;
|
||||
for (i = 1; i < blockCount + 1; ++i) {
|
||||
blocks[i] = 0;
|
||||
}
|
||||
}
|
||||
if (isString) {
|
||||
for (i = this.start; index < length && i < byteCount; ++index) {
|
||||
code = message.charCodeAt(index);
|
||||
if (code < 0x80) {
|
||||
blocks[i >> 2] |= code << SHIFT[i++ & 3];
|
||||
} else if (code < 0x800) {
|
||||
blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||
} else if (code < 0xd800 || code >= 0xe000) {
|
||||
blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||
} else {
|
||||
code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
|
||||
blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
|
||||
blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = this.start; index < length && i < byteCount; ++index) {
|
||||
blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
|
||||
}
|
||||
}
|
||||
this.lastByteIndex = i;
|
||||
if (i >= byteCount) {
|
||||
this.start = i - byteCount;
|
||||
this.block = blocks[blockCount];
|
||||
for (i = 0; i < blockCount; ++i) {
|
||||
s[i] ^= blocks[i];
|
||||
}
|
||||
f(s);
|
||||
this.reset = true;
|
||||
} else {
|
||||
this.start = i;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Keccak.prototype.encode = function (x, right) {
|
||||
var o = x & 255, n = 1;
|
||||
var bytes = [o];
|
||||
x = x >> 8;
|
||||
o = x & 255;
|
||||
while (o > 0) {
|
||||
bytes.unshift(o);
|
||||
x = x >> 8;
|
||||
o = x & 255;
|
||||
++n;
|
||||
}
|
||||
if (right) {
|
||||
bytes.push(n);
|
||||
} else {
|
||||
bytes.unshift(n);
|
||||
}
|
||||
this.update(bytes);
|
||||
return bytes.length;
|
||||
};
|
||||
|
||||
Keccak.prototype.encodeString = function (str) {
|
||||
var result = formatMessage(str);
|
||||
str = result[0];
|
||||
var isString = result[1];
|
||||
var bytes = 0, length = str.length;
|
||||
if (isString) {
|
||||
for (var i = 0; i < str.length; ++i) {
|
||||
var code = str.charCodeAt(i);
|
||||
if (code < 0x80) {
|
||||
bytes += 1;
|
||||
} else if (code < 0x800) {
|
||||
bytes += 2;
|
||||
} else if (code < 0xd800 || code >= 0xe000) {
|
||||
bytes += 3;
|
||||
} else {
|
||||
code = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(++i) & 0x3ff));
|
||||
bytes += 4;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bytes = length;
|
||||
}
|
||||
bytes += this.encode(bytes * 8);
|
||||
this.update(str);
|
||||
return bytes;
|
||||
};
|
||||
|
||||
Keccak.prototype.bytepad = function (strs, w) {
|
||||
var bytes = this.encode(w);
|
||||
for (var i = 0; i < strs.length; ++i) {
|
||||
bytes += this.encodeString(strs[i]);
|
||||
}
|
||||
var paddingBytes = (w - bytes % w) % w;
|
||||
var zeros = [];
|
||||
zeros.length = paddingBytes;
|
||||
this.update(zeros);
|
||||
return this;
|
||||
};
|
||||
|
||||
Keccak.prototype.finalize = function () {
|
||||
if (this.finalized) {
|
||||
return;
|
||||
}
|
||||
this.finalized = true;
|
||||
var blocks = this.blocks, i = this.lastByteIndex, blockCount = this.blockCount, s = this.s;
|
||||
blocks[i >> 2] |= this.padding[i & 3];
|
||||
if (this.lastByteIndex === this.byteCount) {
|
||||
blocks[0] = blocks[blockCount];
|
||||
for (i = 1; i < blockCount + 1; ++i) {
|
||||
blocks[i] = 0;
|
||||
}
|
||||
}
|
||||
blocks[blockCount - 1] |= 0x80000000;
|
||||
for (i = 0; i < blockCount; ++i) {
|
||||
s[i] ^= blocks[i];
|
||||
}
|
||||
f(s);
|
||||
};
|
||||
|
||||
Keccak.prototype.toString = Keccak.prototype.hex = function () {
|
||||
this.finalize();
|
||||
|
||||
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
||||
extraBytes = this.extraBytes, i = 0, j = 0;
|
||||
var hex = '', block;
|
||||
while (j < outputBlocks) {
|
||||
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
|
||||
block = s[i];
|
||||
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F] +
|
||||
HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F] +
|
||||
HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F] +
|
||||
HEX_CHARS[(block >> 28) & 0x0F] + HEX_CHARS[(block >> 24) & 0x0F];
|
||||
}
|
||||
if (j % blockCount === 0) {
|
||||
f(s);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
if (extraBytes) {
|
||||
block = s[i];
|
||||
hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F];
|
||||
if (extraBytes > 1) {
|
||||
hex += HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F];
|
||||
}
|
||||
if (extraBytes > 2) {
|
||||
hex += HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F];
|
||||
}
|
||||
}
|
||||
return hex;
|
||||
};
|
||||
|
||||
Keccak.prototype.arrayBuffer = function () {
|
||||
this.finalize();
|
||||
|
||||
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
||||
extraBytes = this.extraBytes, i = 0, j = 0;
|
||||
var bytes = this.outputBits >> 3;
|
||||
var buffer;
|
||||
if (extraBytes) {
|
||||
buffer = new ArrayBuffer((outputBlocks + 1) << 2);
|
||||
} else {
|
||||
buffer = new ArrayBuffer(bytes);
|
||||
}
|
||||
var array = new Uint32Array(buffer);
|
||||
while (j < outputBlocks) {
|
||||
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
|
||||
array[j] = s[i];
|
||||
}
|
||||
if (j % blockCount === 0) {
|
||||
f(s);
|
||||
}
|
||||
}
|
||||
if (extraBytes) {
|
||||
array[i] = s[i];
|
||||
buffer = buffer.slice(0, bytes);
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
|
||||
Keccak.prototype.buffer = Keccak.prototype.arrayBuffer;
|
||||
|
||||
Keccak.prototype.digest = Keccak.prototype.array = function () {
|
||||
this.finalize();
|
||||
|
||||
var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
|
||||
extraBytes = this.extraBytes, i = 0, j = 0;
|
||||
var array = [], offset, block;
|
||||
while (j < outputBlocks) {
|
||||
for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
|
||||
offset = j << 2;
|
||||
block = s[i];
|
||||
array[offset] = block & 0xFF;
|
||||
array[offset + 1] = (block >> 8) & 0xFF;
|
||||
array[offset + 2] = (block >> 16) & 0xFF;
|
||||
array[offset + 3] = (block >> 24) & 0xFF;
|
||||
}
|
||||
if (j % blockCount === 0) {
|
||||
f(s);
|
||||
}
|
||||
}
|
||||
if (extraBytes) {
|
||||
offset = j << 2;
|
||||
block = s[i];
|
||||
array[offset] = block & 0xFF;
|
||||
if (extraBytes > 1) {
|
||||
array[offset + 1] = (block >> 8) & 0xFF;
|
||||
}
|
||||
if (extraBytes > 2) {
|
||||
array[offset + 2] = (block >> 16) & 0xFF;
|
||||
}
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
function Kmac(bits, padding, outputBits) {
|
||||
Keccak.call(this, bits, padding, outputBits);
|
||||
}
|
||||
|
||||
Kmac.prototype = new Keccak();
|
||||
|
||||
Kmac.prototype.finalize = function () {
|
||||
this.encode(this.outputBits, true);
|
||||
return Keccak.prototype.finalize.call(this);
|
||||
};
|
||||
|
||||
var f = function (s) {
|
||||
var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
|
||||
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
|
||||
b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
|
||||
b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
|
||||
for (n = 0; n < 48; n += 2) {
|
||||
c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
|
||||
c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
|
||||
c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
|
||||
c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
|
||||
c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
|
||||
c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
|
||||
c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
|
||||
c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
|
||||
c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
|
||||
c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
|
||||
|
||||
h = c8 ^ ((c2 << 1) | (c3 >>> 31));
|
||||
l = c9 ^ ((c3 << 1) | (c2 >>> 31));
|
||||
s[0] ^= h;
|
||||
s[1] ^= l;
|
||||
s[10] ^= h;
|
||||
s[11] ^= l;
|
||||
s[20] ^= h;
|
||||
s[21] ^= l;
|
||||
s[30] ^= h;
|
||||
s[31] ^= l;
|
||||
s[40] ^= h;
|
||||
s[41] ^= l;
|
||||
h = c0 ^ ((c4 << 1) | (c5 >>> 31));
|
||||
l = c1 ^ ((c5 << 1) | (c4 >>> 31));
|
||||
s[2] ^= h;
|
||||
s[3] ^= l;
|
||||
s[12] ^= h;
|
||||
s[13] ^= l;
|
||||
s[22] ^= h;
|
||||
s[23] ^= l;
|
||||
s[32] ^= h;
|
||||
s[33] ^= l;
|
||||
s[42] ^= h;
|
||||
s[43] ^= l;
|
||||
h = c2 ^ ((c6 << 1) | (c7 >>> 31));
|
||||
l = c3 ^ ((c7 << 1) | (c6 >>> 31));
|
||||
s[4] ^= h;
|
||||
s[5] ^= l;
|
||||
s[14] ^= h;
|
||||
s[15] ^= l;
|
||||
s[24] ^= h;
|
||||
s[25] ^= l;
|
||||
s[34] ^= h;
|
||||
s[35] ^= l;
|
||||
s[44] ^= h;
|
||||
s[45] ^= l;
|
||||
h = c4 ^ ((c8 << 1) | (c9 >>> 31));
|
||||
l = c5 ^ ((c9 << 1) | (c8 >>> 31));
|
||||
s[6] ^= h;
|
||||
s[7] ^= l;
|
||||
s[16] ^= h;
|
||||
s[17] ^= l;
|
||||
s[26] ^= h;
|
||||
s[27] ^= l;
|
||||
s[36] ^= h;
|
||||
s[37] ^= l;
|
||||
s[46] ^= h;
|
||||
s[47] ^= l;
|
||||
h = c6 ^ ((c0 << 1) | (c1 >>> 31));
|
||||
l = c7 ^ ((c1 << 1) | (c0 >>> 31));
|
||||
s[8] ^= h;
|
||||
s[9] ^= l;
|
||||
s[18] ^= h;
|
||||
s[19] ^= l;
|
||||
s[28] ^= h;
|
||||
s[29] ^= l;
|
||||
s[38] ^= h;
|
||||
s[39] ^= l;
|
||||
s[48] ^= h;
|
||||
s[49] ^= l;
|
||||
|
||||
b0 = s[0];
|
||||
b1 = s[1];
|
||||
b32 = (s[11] << 4) | (s[10] >>> 28);
|
||||
b33 = (s[10] << 4) | (s[11] >>> 28);
|
||||
b14 = (s[20] << 3) | (s[21] >>> 29);
|
||||
b15 = (s[21] << 3) | (s[20] >>> 29);
|
||||
b46 = (s[31] << 9) | (s[30] >>> 23);
|
||||
b47 = (s[30] << 9) | (s[31] >>> 23);
|
||||
b28 = (s[40] << 18) | (s[41] >>> 14);
|
||||
b29 = (s[41] << 18) | (s[40] >>> 14);
|
||||
b20 = (s[2] << 1) | (s[3] >>> 31);
|
||||
b21 = (s[3] << 1) | (s[2] >>> 31);
|
||||
b2 = (s[13] << 12) | (s[12] >>> 20);
|
||||
b3 = (s[12] << 12) | (s[13] >>> 20);
|
||||
b34 = (s[22] << 10) | (s[23] >>> 22);
|
||||
b35 = (s[23] << 10) | (s[22] >>> 22);
|
||||
b16 = (s[33] << 13) | (s[32] >>> 19);
|
||||
b17 = (s[32] << 13) | (s[33] >>> 19);
|
||||
b48 = (s[42] << 2) | (s[43] >>> 30);
|
||||
b49 = (s[43] << 2) | (s[42] >>> 30);
|
||||
b40 = (s[5] << 30) | (s[4] >>> 2);
|
||||
b41 = (s[4] << 30) | (s[5] >>> 2);
|
||||
b22 = (s[14] << 6) | (s[15] >>> 26);
|
||||
b23 = (s[15] << 6) | (s[14] >>> 26);
|
||||
b4 = (s[25] << 11) | (s[24] >>> 21);
|
||||
b5 = (s[24] << 11) | (s[25] >>> 21);
|
||||
b36 = (s[34] << 15) | (s[35] >>> 17);
|
||||
b37 = (s[35] << 15) | (s[34] >>> 17);
|
||||
b18 = (s[45] << 29) | (s[44] >>> 3);
|
||||
b19 = (s[44] << 29) | (s[45] >>> 3);
|
||||
b10 = (s[6] << 28) | (s[7] >>> 4);
|
||||
b11 = (s[7] << 28) | (s[6] >>> 4);
|
||||
b42 = (s[17] << 23) | (s[16] >>> 9);
|
||||
b43 = (s[16] << 23) | (s[17] >>> 9);
|
||||
b24 = (s[26] << 25) | (s[27] >>> 7);
|
||||
b25 = (s[27] << 25) | (s[26] >>> 7);
|
||||
b6 = (s[36] << 21) | (s[37] >>> 11);
|
||||
b7 = (s[37] << 21) | (s[36] >>> 11);
|
||||
b38 = (s[47] << 24) | (s[46] >>> 8);
|
||||
b39 = (s[46] << 24) | (s[47] >>> 8);
|
||||
b30 = (s[8] << 27) | (s[9] >>> 5);
|
||||
b31 = (s[9] << 27) | (s[8] >>> 5);
|
||||
b12 = (s[18] << 20) | (s[19] >>> 12);
|
||||
b13 = (s[19] << 20) | (s[18] >>> 12);
|
||||
b44 = (s[29] << 7) | (s[28] >>> 25);
|
||||
b45 = (s[28] << 7) | (s[29] >>> 25);
|
||||
b26 = (s[38] << 8) | (s[39] >>> 24);
|
||||
b27 = (s[39] << 8) | (s[38] >>> 24);
|
||||
b8 = (s[48] << 14) | (s[49] >>> 18);
|
||||
b9 = (s[49] << 14) | (s[48] >>> 18);
|
||||
|
||||
s[0] = b0 ^ (~b2 & b4);
|
||||
s[1] = b1 ^ (~b3 & b5);
|
||||
s[10] = b10 ^ (~b12 & b14);
|
||||
s[11] = b11 ^ (~b13 & b15);
|
||||
s[20] = b20 ^ (~b22 & b24);
|
||||
s[21] = b21 ^ (~b23 & b25);
|
||||
s[30] = b30 ^ (~b32 & b34);
|
||||
s[31] = b31 ^ (~b33 & b35);
|
||||
s[40] = b40 ^ (~b42 & b44);
|
||||
s[41] = b41 ^ (~b43 & b45);
|
||||
s[2] = b2 ^ (~b4 & b6);
|
||||
s[3] = b3 ^ (~b5 & b7);
|
||||
s[12] = b12 ^ (~b14 & b16);
|
||||
s[13] = b13 ^ (~b15 & b17);
|
||||
s[22] = b22 ^ (~b24 & b26);
|
||||
s[23] = b23 ^ (~b25 & b27);
|
||||
s[32] = b32 ^ (~b34 & b36);
|
||||
s[33] = b33 ^ (~b35 & b37);
|
||||
s[42] = b42 ^ (~b44 & b46);
|
||||
s[43] = b43 ^ (~b45 & b47);
|
||||
s[4] = b4 ^ (~b6 & b8);
|
||||
s[5] = b5 ^ (~b7 & b9);
|
||||
s[14] = b14 ^ (~b16 & b18);
|
||||
s[15] = b15 ^ (~b17 & b19);
|
||||
s[24] = b24 ^ (~b26 & b28);
|
||||
s[25] = b25 ^ (~b27 & b29);
|
||||
s[34] = b34 ^ (~b36 & b38);
|
||||
s[35] = b35 ^ (~b37 & b39);
|
||||
s[44] = b44 ^ (~b46 & b48);
|
||||
s[45] = b45 ^ (~b47 & b49);
|
||||
s[6] = b6 ^ (~b8 & b0);
|
||||
s[7] = b7 ^ (~b9 & b1);
|
||||
s[16] = b16 ^ (~b18 & b10);
|
||||
s[17] = b17 ^ (~b19 & b11);
|
||||
s[26] = b26 ^ (~b28 & b20);
|
||||
s[27] = b27 ^ (~b29 & b21);
|
||||
s[36] = b36 ^ (~b38 & b30);
|
||||
s[37] = b37 ^ (~b39 & b31);
|
||||
s[46] = b46 ^ (~b48 & b40);
|
||||
s[47] = b47 ^ (~b49 & b41);
|
||||
s[8] = b8 ^ (~b0 & b2);
|
||||
s[9] = b9 ^ (~b1 & b3);
|
||||
s[18] = b18 ^ (~b10 & b12);
|
||||
s[19] = b19 ^ (~b11 & b13);
|
||||
s[28] = b28 ^ (~b20 & b22);
|
||||
s[29] = b29 ^ (~b21 & b23);
|
||||
s[38] = b38 ^ (~b30 & b32);
|
||||
s[39] = b39 ^ (~b31 & b33);
|
||||
s[48] = b48 ^ (~b40 & b42);
|
||||
s[49] = b49 ^ (~b41 & b43);
|
||||
|
||||
s[0] ^= RC[n];
|
||||
s[1] ^= RC[n + 1];
|
||||
}
|
||||
};
|
||||
|
||||
function extractLast20Bytes(hexString, addPrefix) {
|
||||
// Ensure the input hexString has '0x' prefix
|
||||
if (!hexString.startsWith('0x')) {
|
||||
hexString = '0x' + hexString;
|
||||
}
|
||||
|
||||
// Remove '0x' prefix and parse the hex string to a BigInt
|
||||
var bigIntValue = BigInt(hexString);
|
||||
|
||||
// Extract the last 20 bytes (160 bits) from the BigInt
|
||||
var last20Bytes = bigIntValue & BigInt('0x' + 'f'.repeat(40)); // 0xf is 4 bits in hexadecimal, repeated 40 times for 160 bits
|
||||
|
||||
// Convert the result back to a hexadecimal string
|
||||
var result = last20Bytes.toString(16).padStart(40, '0'); // 40 characters for 160 bits
|
||||
|
||||
// Add '0x' prefix if addPrefix is truthy
|
||||
if (addPrefix) {
|
||||
result = '0x' + result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof root.keccak === 'object') {
|
||||
Object.assign(root.keccak, methods);
|
||||
}
|
||||
|
||||
if (COMMON_JS) {
|
||||
module.exports = methods;
|
||||
} else {
|
||||
for (i = 0; i < methodNames.length; ++i) {
|
||||
root[methodNames[i]] = methods[methodNames[i]];
|
||||
}
|
||||
if (AMD) {
|
||||
define(function () {
|
||||
return methods;
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user