3456 lines
138 KiB
HTML
3456 lines
138 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Binance Smart Chain</title>
|
||
<link rel="stylesheet" href="css/main.min.css" />
|
||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||
<link
|
||
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap"
|
||
rel="stylesheet"
|
||
/>
|
||
</head>
|
||
|
||
<body class="hidden">
|
||
<sm-notifications id="notification_drawer"></sm-notifications>
|
||
<sm-popup id="confirmation_popup">
|
||
<h4 id="confirm_title"></h4>
|
||
<p id="confirm_message" class="breakable"></p>
|
||
<div class="flex align-center gap-0-5 margin-left-auto">
|
||
<button class="button cancel-button">Cancel</button>
|
||
<button class="button button--primary confirm-button ok-button">Ok</button>
|
||
</div>
|
||
</sm-popup>
|
||
<main>
|
||
<header id="main_header">
|
||
<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">Binance Smart Chain</h4>
|
||
</div>
|
||
</div>
|
||
<button
|
||
id="meta_mask_status_button"
|
||
class="button interactive flex align-center hidden"
|
||
data-status="disconnected"
|
||
onclick="connectToMetaMask()"
|
||
>
|
||
<div class="icon-wrapper">
|
||
<svg
|
||
class="icon"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
xml:space="preserve"
|
||
id="Layer_1"
|
||
x="0"
|
||
y="0"
|
||
version="1.1"
|
||
viewBox="0 0 318.6 318.6"
|
||
>
|
||
<style>
|
||
.st1,
|
||
.st6 {
|
||
fill: #e4761b;
|
||
stroke: #e4761b;
|
||
stroke-linecap: round;
|
||
stroke-linejoin: round;
|
||
}
|
||
|
||
.st6 {
|
||
fill: #f6851b;
|
||
stroke: #f6851b;
|
||
}
|
||
</style>
|
||
<path
|
||
fill="#e2761b"
|
||
stroke="#e2761b"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m274.1 35.5-99.5 73.9L193 65.8z"
|
||
/>
|
||
<path
|
||
d="m44.4 35.5 98.7 74.6-17.5-44.3zm193.9 171.3-26.5 40.6 56.7 15.6 16.3-55.3zm-204.4.9L50.1 263l56.7-15.6-26.5-40.6z"
|
||
class="st1"
|
||
/>
|
||
<path
|
||
d="m103.6 138.2-15.8 23.9 56.3 2.5-2-60.5zm111.3 0-39-34.8-1.3 61.2 56.2-2.5zM106.8 247.4l33.8-16.5-29.2-22.8zm71.1-16.5 33.9 16.5-4.7-39.3z"
|
||
class="st1"
|
||
/>
|
||
<path
|
||
fill="#d7c1b3"
|
||
stroke="#d7c1b3"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m211.8 247.4-33.9-16.5 2.7 22.1-.3 9.3zm-105 0 31.5 14.9-.2-9.3 2.5-22.1z"
|
||
/>
|
||
<path
|
||
fill="#233447"
|
||
stroke="#233447"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m138.8 193.5-28.2-8.3 19.9-9.1zm40.9 0 8.3-17.4 20 9.1z"
|
||
/>
|
||
<path
|
||
fill="#cd6116"
|
||
stroke="#cd6116"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m106.8 247.4 4.8-40.6-31.3.9zM207 206.8l4.8 40.6 26.5-39.7zm23.8-44.7-56.2 2.5 5.2 28.9 8.3-17.4 20 9.1zm-120.2 23.1 20-9.1 8.2 17.4 5.3-28.9-56.3-2.5z"
|
||
/>
|
||
<path
|
||
fill="#e4751f"
|
||
stroke="#e4751f"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m87.8 162.1 23.6 46-.8-22.9zm120.3 23.1-1 22.9 23.7-46zm-64-20.6-5.3 28.9 6.6 34.1 1.5-44.9zm30.5 0-2.7 18 1.2 45 6.7-34.1z"
|
||
/>
|
||
<path
|
||
d="m179.8 193.5-6.7 34.1 4.8 3.3 29.2-22.8 1-22.9zm-69.2-8.3.8 22.9 29.2 22.8 4.8-3.3-6.6-34.1z"
|
||
class="st6"
|
||
/>
|
||
<path
|
||
fill="#c0ad9e"
|
||
stroke="#c0ad9e"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m180.3 262.3.3-9.3-2.5-2.2h-37.7l-2.3 2.2.2 9.3-31.5-14.9 11 9 22.3 15.5h38.3l22.4-15.5 11-9z"
|
||
/>
|
||
<path
|
||
fill="#161616"
|
||
stroke="#161616"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m177.9 230.9-4.8-3.3h-27.7l-4.8 3.3-2.5 22.1 2.3-2.2h37.7l2.5 2.2z"
|
||
/>
|
||
<path
|
||
fill="#763d16"
|
||
stroke="#763d16"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m278.3 114.2 8.5-40.8-12.7-37.9-96.2 71.4 37 31.3 52.3 15.3 11.6-13.5-5-3.6 8-7.3-6.2-4.8 8-6.1zM31.8 73.4l8.5 40.8-5.4 4 8 6.1-6.1 4.8 8 7.3-5 3.6 11.5 13.5 52.3-15.3 37-31.3-96.2-71.4z"
|
||
/>
|
||
<path
|
||
d="m267.2 153.5-52.3-15.3 15.9 23.9-23.7 46 31.2-.4h46.5zm-163.6-15.3-52.3 15.3-17.4 54.2h46.4l31.1.4-23.6-46zm71 26.4 3.3-57.7 15.2-41.1h-67.5l15 41.1 3.5 57.7 1.2 18.2.1 44.8h27.7l.2-44.8z"
|
||
class="st6"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
<div id="meta_mask_status">Disconnected</div>
|
||
</button>
|
||
<div class="flex align-center gap-0-3">
|
||
<sm-select id="currency_selector" class="margin-right-0-5">
|
||
<sm-option value="bsc" selected>BSC</sm-option>
|
||
<sm-option value="inr">INR</sm-option>
|
||
<sm-option value="usd">USD</sm-option>
|
||
</sm-select>
|
||
<theme-toggle></theme-toggle>
|
||
</div>
|
||
</header>
|
||
<nav id="main_navbar">
|
||
<ul>
|
||
<li>
|
||
<a class="nav-item nav-item--active interactive" href="#/balance">
|
||
<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="M21 7.28V5c0-1.1-.9-2-2-2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-2.28c.59-.35 1-.98 1-1.72V9c0-.74-.41-1.37-1-1.72zM20 9v6h-7V9h7zM5 19V5h14v2h-6c-1.1 0-2 .9-2 2v6c0 1.1.9 2 2 2h6v2H5z"
|
||
/>
|
||
<circle cx="16" cy="12" r="1.5" />
|
||
</svg>
|
||
<span class="nav-item__title"> Balance </span>
|
||
<div class="nav-item__indicator"></div>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="nav-item interactive" href="#/send">
|
||
<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>
|
||
<path
|
||
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>
|
||
<span class="nav-item__title"> Send </span>
|
||
</a>
|
||
</li>
|
||
<li>
|
||
<a class="nav-item interactive" href="#/create">
|
||
<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>
|
||
<path
|
||
d="M13 7h-2v4H7v2h4v4h2v-4h4v-2h-4V7zm-1-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"
|
||
></path>
|
||
</svg>
|
||
<span class="nav-item__title"> Create </span>
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
<div id="page_container"></div>
|
||
</main>
|
||
<sm-popup id="transaction_result_popup">
|
||
<header slot="header" class="popup__header">
|
||
<div class="flex align-center">
|
||
<button class="popup__header__close" onclick="closePopup()">
|
||
<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="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"
|
||
/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
<div id="transaction_result_popup__content" class="grid gap-2"></div>
|
||
</sm-popup>
|
||
<sm-popup id="retrieve_btc_addr_popup">
|
||
<header slot="header" class="popup__header">
|
||
<div class="flex align-center">
|
||
<button class="popup__header__close" onclick="closePopup()">
|
||
<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="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"
|
||
/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
<section class="grid gap-1-5">
|
||
<div class="grid gap-0-5">
|
||
<h4>Did you forget your Binance Address?</h4>
|
||
<p>
|
||
If you have your BSC/BTC/FLO Private Key, enter it here and recover
|
||
your Binance Address.
|
||
</p>
|
||
</div>
|
||
<sm-form>
|
||
<div id="recovered_btc_addr_wrapper" class="hidden">
|
||
<h5>Recovered Binance Address</h5>
|
||
<sm-copy id="recovered_btc_addr"></sm-copy>
|
||
</div>
|
||
<sm-input
|
||
id="retrieve_btc_addr_field"
|
||
type="password"
|
||
placeholder="BSC/BTC/FLO Private Key"
|
||
class="password-field"
|
||
data-private-key
|
||
required
|
||
autofocus
|
||
>
|
||
<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
|
||
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"
|
||
/>
|
||
</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
|
||
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"
|
||
/>
|
||
</svg>
|
||
</label>
|
||
</sm-input>
|
||
<button
|
||
class="button button--primary cta"
|
||
type="submit"
|
||
onclick="retrieveBinanceAddr()"
|
||
>
|
||
Recover
|
||
</button>
|
||
</sm-form>
|
||
</section>
|
||
</sm-popup>
|
||
<script>
|
||
/* Constants for FLO blockchain operations !!Make sure to add this at beginning!! */
|
||
const floGlobals = {
|
||
blockchain: "FLO",
|
||
tokenURL: "https://ranchimallflo.ranchimall.net/",
|
||
expirationDays: 60,
|
||
};
|
||
</script>
|
||
<script src="https://unpkg.com/uhtml@3.0.1/es.js"></script>
|
||
<script src="scripts/components.min.js" type="text/javascript"></script>
|
||
<script
|
||
src="scripts/btcwallet_scripts_lib.js"
|
||
type="text/javascript"
|
||
></script>
|
||
|
||
<!-- ethers.js version 5.6 -->
|
||
|
||
<script src="scripts/btcOperator.js" type="text/javascript"></script>
|
||
<script src="scripts/floCrypto.js" type="text/javascript"></script>
|
||
<script src="scripts/tap_combined.js" type="text/javascript"></script>
|
||
<script src="scripts/keccak.js" type="text/javascript"></script>
|
||
<script src="scripts/floEthereum.js" type="text/javascript"></script>
|
||
<script src="scripts/compactIDB.js" type="text/javascript"></script>
|
||
<script
|
||
src="scripts/ether.umd.min.js"
|
||
type="text/javascript"
|
||
></script>
|
||
<script src="scripts/bscOperator.js" type="text/javascript"></script>
|
||
<script>
|
||
let showCurrentValue = false;
|
||
let currentCurrency = localStorage.getItem("preferredCurrency") || "bsc";
|
||
let exchangeRates = {
|
||
usd: 0,
|
||
inr: 0,
|
||
};
|
||
let historicalRates = {};
|
||
let isHistoricApiAvailable = false;
|
||
|
||
function syncCurrencySelector() {
|
||
const selector = getRef("currency_selector");
|
||
if (selector && selector.value !== currentCurrency) {
|
||
selector.value = currentCurrency;
|
||
const options = selector.querySelectorAll("sm-option");
|
||
options.forEach((option) => {
|
||
if (option.value === currentCurrency) {
|
||
option.setAttribute("selected", "");
|
||
} else {
|
||
option.removeAttribute("selected");
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
async function fetchExchangeRates() {
|
||
try {
|
||
const response = await fetch(
|
||
"https://api.coingecko.com/api/v3/simple/price?ids=binancecoin&vs_currencies=usd,inr"
|
||
);
|
||
const data = await response.json();
|
||
|
||
if (data) {
|
||
exchangeRates = {
|
||
usd: parseFloat(data.binancecoin.usd) || 0,
|
||
inr: parseFloat(data.binancecoin.inr) || 0,
|
||
};
|
||
} else {
|
||
exchangeRates = {
|
||
usd: 500, // Fallback rate
|
||
inr: 40000, // Fallback rate
|
||
};
|
||
}
|
||
return exchangeRates;
|
||
} catch (error) {
|
||
console.error("Error fetching exchange rates:", error);
|
||
notify("Failed to fetch currency rates", "error");
|
||
exchangeRates = {
|
||
usd: 500, // Fallback rate
|
||
inr: 40000, // Fallback rate
|
||
};
|
||
isHistoricApiAvailable = false;
|
||
return exchangeRates;
|
||
}
|
||
}
|
||
|
||
async function fetchHistoricalRate(timestamp) {
|
||
if (historicalRates[timestamp]) {
|
||
return historicalRates[timestamp];
|
||
}
|
||
const cacheKey = `bsc-historical-${timestamp}`;
|
||
const cachedData = localStorage.getItem(cacheKey);
|
||
if (cachedData) {
|
||
const parsedData = JSON.parse(cachedData);
|
||
historicalRates[timestamp] = parsedData;
|
||
return parsedData;
|
||
}
|
||
|
||
try {
|
||
const date = new Date(timestamp * 1000);
|
||
const day = date.getDate().toString().padStart(2, "0");
|
||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||
const year = date.getFullYear();
|
||
const formattedDate = `${day}-${month}-${year}`;
|
||
const response = await fetch(
|
||
`https://api.coingecko.com/api/v3/coins/binancecoin/history?date=${formattedDate}`
|
||
);
|
||
const data = await response.json();
|
||
|
||
if (data && data.market_data && data.market_data.current_price) {
|
||
const rates = {
|
||
usd: parseFloat(data.market_data.current_price.usd) || 0,
|
||
inr: parseFloat(data.market_data.current_price.inr) || 0,
|
||
};
|
||
historicalRates[timestamp] = rates;
|
||
localStorage.setItem(cacheKey, JSON.stringify(rates));
|
||
return rates;
|
||
}
|
||
return exchangeRates;
|
||
} catch (error) {
|
||
console.error("Error fetching historical rate:", error);
|
||
return exchangeRates;
|
||
}
|
||
}
|
||
|
||
async function updateCurrency(currency) {
|
||
const previousCurrency = currentCurrency;
|
||
currentCurrency = currency;
|
||
localStorage.setItem("preferredCurrency", currency);
|
||
|
||
if (
|
||
currency !== "bsc" &&
|
||
(exchangeRates.usd === 0 || exchangeRates.inr === 0)
|
||
) {
|
||
await fetchExchangeRates();
|
||
}
|
||
if (getRef("page_container")?.dataset?.page === "tdx") {
|
||
// Get the transaction value and fee elements if they exist
|
||
const valueElement = getRef("tx_value");
|
||
const feeElement = getRef("tx_fee");
|
||
|
||
if (valueElement && valueElement.dataset.eth) {
|
||
const ethAmount = parseFloat(valueElement.dataset.eth);
|
||
const timestamp = valueElement.dataset.timestamp;
|
||
valueElement.textContent = await getConvertedAmount(
|
||
ethAmount,
|
||
timestamp
|
||
);
|
||
}
|
||
|
||
if (feeElement && feeElement.dataset.eth) {
|
||
const ethFee = parseFloat(feeElement.dataset.eth);
|
||
const timestamp = feeElement.dataset.timestamp;
|
||
feeElement.textContent = await getConvertedAmount(
|
||
ethFee,
|
||
timestamp
|
||
);
|
||
}
|
||
}
|
||
|
||
if (currency !== "bsc" && previousCurrency === "bsc") {
|
||
testHistoricalAPIAndRenderToggle();
|
||
} else if (currency === "bsc" && previousCurrency !== "bsc") {
|
||
const container = document.querySelector(
|
||
".flex.flex-direction-column.gap-0-5.sticky.top-0"
|
||
);
|
||
if (container) {
|
||
const existingToggle =
|
||
container.querySelector("sm-switch")?.parentElement;
|
||
if (existingToggle) existingToggle.remove();
|
||
}
|
||
}
|
||
|
||
updateTransactionDetailsToggle();
|
||
convertAllDisplayedCurrency();
|
||
syncCurrencySelector();
|
||
}
|
||
|
||
function handleValuationTypeChange(isCurrentValue) {
|
||
if (isCurrentValue && isCurrentValue.target) {
|
||
isCurrentValue = isCurrentValue.target.checked;
|
||
}
|
||
showCurrentValue = isCurrentValue;
|
||
localStorage.setItem("bsc-wallet-show-current-value", isCurrentValue);
|
||
convertAllDisplayedCurrency();
|
||
}
|
||
|
||
async function convertAllDisplayedCurrency() {
|
||
const currency = currentCurrency;
|
||
const elements = document.querySelectorAll(".bsc-value");
|
||
|
||
for (const el of elements) {
|
||
const bscAmount = parseFloat(el.dataset.bsc || "0");
|
||
const timestamp = el.dataset.timestamp;
|
||
if (el.id === "usdc_balance" || el.id === "usdt_balance") {
|
||
continue;
|
||
}
|
||
let formattedValue;
|
||
if (currency === "bsc") {
|
||
formattedValue = formatCurrency(bscAmount);
|
||
} else {
|
||
formattedValue = await getConvertedAmount(bscAmount, timestamp);
|
||
}
|
||
|
||
el.textContent = formattedValue;
|
||
}
|
||
}
|
||
|
||
async function getConvertedAmount(amountInBsc, timestamp) {
|
||
if (!amountInBsc) return "0 BSC";
|
||
|
||
const value = Number(amountInBsc);
|
||
if (isNaN(value)) return amountInBsc;
|
||
|
||
if (currentCurrency === "bsc") {
|
||
return `${value.toFixed(6)} BSC`;
|
||
} else {
|
||
let rates = exchangeRates;
|
||
|
||
if (!showCurrentValue && timestamp) {
|
||
try {
|
||
const historicalRates = await fetchHistoricalRate(timestamp);
|
||
if (historicalRates) {
|
||
rates = historicalRates;
|
||
}
|
||
} catch (error) {
|
||
console.error(
|
||
"Error getting historical rates, using current rates:",
|
||
error
|
||
);
|
||
}
|
||
}
|
||
if (rates && currentCurrency === "usd" && rates.usd) {
|
||
return `${(value * rates.usd).toFixed(2)} USD`;
|
||
} else if (rates && currentCurrency === "inr" && rates.inr) {
|
||
return `${(value * rates.inr).toFixed(2)} INR`;
|
||
}
|
||
return `${value.toFixed(6)} BSC`;
|
||
}
|
||
}
|
||
|
||
function formatCurrency(amountInBsc) {
|
||
if (!amountInBsc) return "0 BSC";
|
||
|
||
const value = Number(amountInBsc);
|
||
if (isNaN(value)) return amountInBsc;
|
||
|
||
if (currentCurrency === "bsc") {
|
||
return `${value.toFixed(6)} BSC`;
|
||
} else if (currentCurrency === "usd") {
|
||
return `${(value * exchangeRates.usd).toFixed(2)} USD`;
|
||
} else if (currentCurrency === "inr") {
|
||
return `${(value * exchangeRates.inr).toFixed(2)} INR`;
|
||
}
|
||
|
||
return `${value.toFixed(6)} BSC`;
|
||
}
|
||
|
||
function initCurrencySelector() {
|
||
const selector = getRef("currency_selector");
|
||
if (selector) {
|
||
selector.addEventListener("change", (e) => {
|
||
updateCurrency(e.target.value);
|
||
});
|
||
}
|
||
|
||
const stored = localStorage.getItem("preferredCurrency");
|
||
if (stored) {
|
||
currentCurrency = stored;
|
||
showCurrentValue =
|
||
localStorage.getItem("bsc-wallet-show-current-value") === "true";
|
||
syncCurrencySelector();
|
||
|
||
if (
|
||
currentCurrency !== "bsc" &&
|
||
(exchangeRates.usd === 0 || exchangeRates.inr === 0)
|
||
) {
|
||
fetchExchangeRates().then(() => {
|
||
convertAllDisplayedCurrency();
|
||
testHistoricalAPIAndRenderToggle();
|
||
});
|
||
} else if (currentCurrency !== "bsc") {
|
||
testHistoricalAPIAndRenderToggle();
|
||
}
|
||
}
|
||
}
|
||
const uiGlobals = {};
|
||
const { html, svg, render: renderElem } = uhtml;
|
||
uiGlobals.connectionErrorNotification = [];
|
||
//Checks for internet connection status
|
||
if (!navigator.onLine)
|
||
uiGlobals.connectionErrorNotification.push(
|
||
notify(
|
||
"There seems to be a problem connecting to the internet, Please check you internet connection.",
|
||
"error"
|
||
)
|
||
);
|
||
window.addEventListener("offline", () => {
|
||
uiGlobals.connectionErrorNotification.push(
|
||
notify(
|
||
"There seems to be a problem connecting to the internet, Please check you internet connection.",
|
||
"error"
|
||
)
|
||
);
|
||
});
|
||
window.addEventListener("online", () => {
|
||
uiGlobals.connectionErrorNotification.forEach((notification) => {
|
||
getRef("notification_drawer").remove(notification);
|
||
});
|
||
notify("We are back online.", "success");
|
||
});
|
||
|
||
function getRef(elementId) {
|
||
return document.getElementById(elementId);
|
||
}
|
||
let zIndex = 50;
|
||
// function required for popups or modals to appear
|
||
function openPopup(popupId, pinned) {
|
||
if (popupStack.peek() === undefined) {
|
||
document.addEventListener("keydown", (e) => {
|
||
if (e.key === "Escape") {
|
||
closePopup();
|
||
}
|
||
});
|
||
}
|
||
zIndex++;
|
||
getRef(popupId).setAttribute("style", `z-index: ${zIndex}`);
|
||
return getRef(popupId).show({ pinned });
|
||
}
|
||
|
||
// hides the popup or modal
|
||
function closePopup(options = {}) {
|
||
if (popupStack.peek() === undefined) return;
|
||
popupStack.peek().popup.hide(options);
|
||
}
|
||
|
||
document.addEventListener("popupopened", async (e) => {
|
||
switch (e.target.id) {
|
||
}
|
||
});
|
||
document.addEventListener("popupclosed", (e) => {
|
||
zIndex--;
|
||
switch (e.target.id) {
|
||
case "retrieve_btc_addr_popup":
|
||
getRef("recovered_btc_addr_wrapper").classList.add("hidden");
|
||
break;
|
||
}
|
||
});
|
||
//Function for displaying toast notifications. pass in error for mode param if you want to show an error.
|
||
function notify(message, mode, options = {}) {
|
||
let icon;
|
||
switch (mode) {
|
||
case "success":
|
||
icon = `<svg class="icon icon--success" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg>`;
|
||
break;
|
||
case "error":
|
||
icon = `<svg class="icon icon--error" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-7v2h2v-2h-2zm0-8v6h2V7h-2z"/></svg>`;
|
||
options.pinned = true;
|
||
break;
|
||
}
|
||
if (mode === "error") {
|
||
console.error(message);
|
||
}
|
||
return getRef("notification_drawer").push(message, {
|
||
icon,
|
||
...options,
|
||
});
|
||
}
|
||
// displays a popup for asking permission. Use this instead of JS confirm
|
||
/**
|
||
@param {string} title - Title of the popup
|
||
@param {object} options - Options for the popup
|
||
@param {string} options.message - Message to be displayed in the popup
|
||
@param {string} options.cancelText - Text for the cancel button
|
||
@param {string} options.confirmText - Text for the confirm button
|
||
@param {boolean} options.danger - If true, confirm button will be red
|
||
*/
|
||
const getConfirmation = (title, options = {}) => {
|
||
return new Promise((resolve) => {
|
||
const {
|
||
message = "",
|
||
cancelText = "Cancel",
|
||
confirmText = "OK",
|
||
danger = false,
|
||
} = options;
|
||
getRef("confirm_title").innerText = title;
|
||
getRef("confirm_message").innerText = message;
|
||
const cancelButton =
|
||
getRef("confirmation_popup").querySelector(".cancel-button");
|
||
const confirmButton =
|
||
getRef("confirmation_popup").querySelector(".confirm-button");
|
||
confirmButton.textContent = confirmText;
|
||
cancelButton.textContent = cancelText;
|
||
if (danger) confirmButton.classList.add("button--danger");
|
||
else confirmButton.classList.remove("button--danger");
|
||
const { opened, closed } = openPopup("confirmation_popup");
|
||
confirmButton.onclick = () => {
|
||
closePopup({ payload: true });
|
||
};
|
||
cancelButton.onclick = () => {
|
||
closePopup();
|
||
};
|
||
closed.then((payload) => {
|
||
confirmButton.onclick = null;
|
||
cancelButton.onclick = null;
|
||
if (payload) resolve(true);
|
||
else resolve(false);
|
||
});
|
||
});
|
||
};
|
||
function createRipple(event, target) {
|
||
const circle = document.createElement("span");
|
||
const diameter = Math.max(target.clientWidth, target.clientHeight);
|
||
const radius = diameter / 2;
|
||
const targetDimensions = target.getBoundingClientRect();
|
||
circle.style.width = circle.style.height = `${diameter}px`;
|
||
circle.style.left = `${
|
||
event.clientX - (targetDimensions.left + radius)
|
||
}px`;
|
||
circle.style.top = `${
|
||
event.clientY - (targetDimensions.top + radius)
|
||
}px`;
|
||
circle.classList.add("ripple");
|
||
const rippleAnimation = circle.animate(
|
||
[
|
||
{
|
||
opacity: 1,
|
||
transform: `scale(0)`,
|
||
},
|
||
{
|
||
transform: "scale(4)",
|
||
opacity: 0,
|
||
},
|
||
],
|
||
{
|
||
duration: 600,
|
||
fill: "forwards",
|
||
easing: "ease-out",
|
||
}
|
||
);
|
||
target.append(circle);
|
||
rippleAnimation.onfinish = () => {
|
||
circle.remove();
|
||
};
|
||
}
|
||
function buttonLoader(id, show) {
|
||
const button =
|
||
typeof id === "string" ? document.getElementById(id) : id;
|
||
if (!button) return;
|
||
if (!button.dataset.hasOwnProperty("wasDisabled"))
|
||
button.dataset.wasDisabled = button.disabled;
|
||
const animOptions = {
|
||
duration: 200,
|
||
fill: "forwards",
|
||
easing: "ease",
|
||
};
|
||
if (show) {
|
||
button.disabled = true;
|
||
button.parentNode.append(document.createElement("sm-spinner"));
|
||
button.animate(
|
||
[
|
||
{
|
||
clipPath: "circle(100%)",
|
||
},
|
||
{
|
||
clipPath: "circle(0)",
|
||
},
|
||
],
|
||
animOptions
|
||
);
|
||
} else {
|
||
button.disabled = button.dataset.wasDisabled === "true";
|
||
button.animate(
|
||
[
|
||
{
|
||
clipPath: "circle(0)",
|
||
},
|
||
{
|
||
clipPath: "circle(100%)",
|
||
},
|
||
],
|
||
animOptions
|
||
).onfinish = (e) => {
|
||
button.removeAttribute("data-original-state");
|
||
};
|
||
const potentialTarget = button.parentNode.querySelector("sm-spinner");
|
||
if (potentialTarget) potentialTarget.remove();
|
||
}
|
||
}
|
||
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]) {
|
||
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(destination) {
|
||
try {
|
||
let page;
|
||
let wildcards = [];
|
||
let params = {};
|
||
let [path, queryString] = destination.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, params };
|
||
if (queryString) {
|
||
params = new URLSearchParams(queryString);
|
||
this.state.params = Object.fromEntries(params);
|
||
}
|
||
if (document.startViewTransition) {
|
||
document.startViewTransition(async () => {
|
||
await this.handleRouting(page);
|
||
});
|
||
} else {
|
||
// Fallback for browsers that don't support View transition API:
|
||
await this.handleRouting(page);
|
||
}
|
||
} catch (e) {
|
||
console.error(e);
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
<script>
|
||
const assetIcons = {
|
||
Binance: `<svg hight="34" width="34" version="1.0" id="katman_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||
viewBox="0 0 800 600" style="enable-background:new 0 0 800 600;" xml:space="preserve">
|
||
<style type="text/css">
|
||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#F3BA2F;}
|
||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#131415;}
|
||
</style>
|
||
<g id="Light">
|
||
<g id="OneArt-_x2022_-Desktop-_x2022_-Light" transform="translate(-457.000000, -1515.000000)">
|
||
<g id="Block" transform="translate(41.000000, 1263.000000)">
|
||
<g id="TVL" transform="translate(48.000000, 252.000000)">
|
||
<g id="Icons_x2F_Icon-24_x2F_cake" transform="translate(368.000000, 0.000000)">
|
||
<circle id="Oval" class="st0" cx="399.8" cy="299.6" r="230.7"/>
|
||
<g id="Icons_x2F_icon-24_x2F_networks_x2F_binance_x5F_smart_x5F_chain" transform="translate(3.333333, 3.333333)">
|
||
<path id="Combined-Shape" class="st1" d="M456.3,320.8l34.8,34.7L396.5,450L302,355.5l34.8-34.7l59.7,59.7L456.3,320.8z
|
||
M396.5,261l35.3,35.3h0l0,0l-35.3,35.3l-35.2-35.2l0-0.1l0,0l6.2-6.2l3-3L396.5,261z M277.5,261.5l34.8,34.8L277.5,331
|
||
l-34.8-34.8L277.5,261.5z M515.5,261.5l34.8,34.8L515.5,331l-34.8-34.8L515.5,261.5z M396.5,142.5L491,237l-34.8,34.8
|
||
L396.5,212l-59.7,59.7L302,237L396.5,142.5z"/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>`,
|
||
usdc: `<svg class="icon" xmlns="http://www.w3.org/2000/svg" data-name="86977684-12db-4850-8f30-233a7c267d11" viewBox="0 0 2000 2000"> <path d="M1000 2000c554.17 0 1000-445.83 1000-1000S1554.17 0 1000 0 0 445.83 0 1000s445.83 1000 1000 1000z" fill="#2775ca"/> <path d="M1275 1158.33c0-145.83-87.5-195.83-262.5-216.66-125-16.67-150-50-150-108.34s41.67-95.83 125-95.83c75 0 116.67 25 137.5 87.5 4.17 12.5 16.67 20.83 29.17 20.83h66.66c16.67 0 29.17-12.5 29.17-29.16v-4.17c-16.67-91.67-91.67-162.5-187.5-170.83v-100c0-16.67-12.5-29.17-33.33-33.34h-62.5c-16.67 0-29.17 12.5-33.34 33.34v95.83c-125 16.67-204.16 100-204.16 204.17 0 137.5 83.33 191.66 258.33 212.5 116.67 20.83 154.17 45.83 154.17 112.5s-58.34 112.5-137.5 112.5c-108.34 0-145.84-45.84-158.34-108.34-4.16-16.66-16.66-25-29.16-25h-70.84c-16.66 0-29.16 12.5-29.16 29.17v4.17c16.66 104.16 83.33 179.16 220.83 200v100c0 16.66 12.5 29.16 33.33 33.33h62.5c16.67 0 29.17-12.5 33.34-33.33v-100c125-20.84 208.33-108.34 208.33-220.84z" fill="#fff"/> <path d="M787.5 1595.83c-325-116.66-491.67-479.16-370.83-800 62.5-175 200-308.33 370.83-370.83 16.67-8.33 25-20.83 25-41.67V325c0-16.67-8.33-29.17-25-33.33-4.17 0-12.5 0-16.67 4.16-395.83 125-612.5 545.84-487.5 941.67 75 233.33 254.17 412.5 487.5 487.5 16.67 8.33 33.34 0 37.5-16.67 4.17-4.16 4.17-8.33 4.17-16.66v-58.34c0-12.5-12.5-29.16-25-37.5zM1229.17 295.83c-16.67-8.33-33.34 0-37.5 16.67-4.17 4.17-4.17 8.33-4.17 16.67v58.33c0 16.67 12.5 33.33 25 41.67 325 116.66 491.67 479.16 370.83 800-62.5 175-200 308.33-370.83 370.83-16.67 8.33-25 20.83-25 41.67V1700c0 16.67 8.33 29.17 25 33.33 4.17 0 12.5 0 16.67-4.16 395.83-125 612.5-545.84 487.5-941.67-75-237.5-258.34-416.67-487.5-491.67z" fill="#fff"/></svg>`,
|
||
usdt: `<svg class="icon" xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 339.43 295.27"><title>tether-usdt-logo</title><path d="M62.15,1.45l-61.89,130a2.52,2.52,0,0,0,.54,2.94L167.95,294.56a2.55,2.55,0,0,0,3.53,0L338.63,134.4a2.52,2.52,0,0,0,.54-2.94l-61.89-130A2.5,2.5,0,0,0,275,0H64.45a2.5,2.5,0,0,0-2.3,1.45h0Z" style="fill:#50af95;fill-rule:evenodd"/><path d="M191.19,144.8v0c-1.2.09-7.4,0.46-21.23,0.46-11,0-18.81-.33-21.55-0.46v0c-42.51-1.87-74.24-9.27-74.24-18.13s31.73-16.25,74.24-18.15v28.91c2.78,0.2,10.74.67,21.74,0.67,13.2,0,19.81-.55,21-0.66v-28.9c42.42,1.89,74.08,9.29,74.08,18.13s-31.65,16.24-74.08,18.12h0Zm0-39.25V79.68h59.2V40.23H89.21V79.68H148.4v25.86c-48.11,2.21-84.29,11.74-84.29,23.16s36.18,20.94,84.29,23.16v82.9h42.78V151.83c48-2.21,84.12-11.73,84.12-23.14s-36.09-20.93-84.12-23.15h0Zm0,0h0Z" style="fill:#fff;fill-rule:evenodd"/><script xmlns=""/></svg>`,
|
||
};
|
||
window.smCompConfig = {
|
||
"sm-input": [
|
||
{
|
||
selector: "[data-BSC-address]",
|
||
customValidation: (value) => {
|
||
if (!value)
|
||
return {
|
||
isValid: false,
|
||
errorText: "Please enter a Binance address",
|
||
};
|
||
return {
|
||
isValid: bscOperator.isValidAddress(value),
|
||
errorText: `Invalid address.<br> It usually starts with "0x"`,
|
||
};
|
||
},
|
||
},
|
||
{
|
||
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.`,
|
||
};
|
||
},
|
||
},
|
||
{
|
||
selector: "#check_balance_input",
|
||
customValidation: (value) => {
|
||
if (!value)
|
||
return {
|
||
isValid: false,
|
||
errorText: "Please enter a private key or BSC address",
|
||
};
|
||
|
||
if (/^0x[0-9a-fA-F]{64}$/.test(value)) {
|
||
return { isValid: true };
|
||
}
|
||
|
||
return {
|
||
isValid:
|
||
floCrypto.getPubKeyHex(value) ||
|
||
bscOperator.isValidAddress(value),
|
||
errorText: `Invalid private key or BSC address"`,
|
||
};
|
||
},
|
||
},
|
||
],
|
||
};
|
||
const router = new Router({
|
||
routingStart(state) {},
|
||
routingEnd(state) {
|
||
let { page } = state;
|
||
if (!page) page = "balance";
|
||
if (page !== "send") {
|
||
taprootScriptTxDetails = {};
|
||
}
|
||
const previousTarget =
|
||
getRef("main_navbar").querySelector(".nav-item--active");
|
||
if (previousTarget) {
|
||
previousTarget.classList.remove("nav-item--active");
|
||
previousTarget.querySelector(".nav-item__indicator")?.remove();
|
||
}
|
||
|
||
if (page === "tdx") {
|
||
renderTdx(state);
|
||
} else {
|
||
const target = getRef("main_navbar").querySelector(
|
||
`.nav-item[href="#/${page}"]`
|
||
);
|
||
if (target) {
|
||
target.classList.add("nav-item--active");
|
||
target.append(html.node`<div class="nav-item__indicator"></div>`);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
function setMetaMaskStatus(isConnected) {
|
||
if (isConnected) {
|
||
getRef("meta_mask_status").textContent = "Connected";
|
||
getRef("meta_mask_status_button").dataset.status = "connected";
|
||
} else {
|
||
getRef("meta_mask_status").textContent = "Disconnected";
|
||
getRef("meta_mask_status_button").dataset.status = "disconnected";
|
||
}
|
||
}
|
||
window.addEventListener("load", () => {
|
||
router.routeTo(location.hash);
|
||
document.body.classList.remove("hidden");
|
||
document.addEventListener("copy", () => {
|
||
notify("copied", "success");
|
||
});
|
||
currentCurrency = "bsc";
|
||
localStorage.setItem("preferredCurrency", "bsc");
|
||
syncCurrencySelector();
|
||
initCurrencySelector();
|
||
document.addEventListener("pointerdown", (e) => {
|
||
if (
|
||
e.target.closest(
|
||
"button:not(:disabled), .interactive:not(:disabled)"
|
||
)
|
||
) {
|
||
createRipple(e, e.target.closest("button, .interactive"));
|
||
}
|
||
});
|
||
compactIDB
|
||
.initDB("floBinance", {
|
||
contacts: {},
|
||
})
|
||
.then((result) => {
|
||
console.log(result);
|
||
})
|
||
.catch((error) => {
|
||
console.error(error);
|
||
});
|
||
// connectToMetaMask().then(() => {
|
||
if (window.ethereum) {
|
||
// setMetaMaskStatus(window.ethereum.isConnected())
|
||
window.ethereum.on("chainChanged", (chainId) => {
|
||
window.currentChainId = chainId;
|
||
if (chainId !== "0x38") {
|
||
renderError("Please switch MetaMask to Binance Mainnet");
|
||
} else {
|
||
router.routeTo(location.hash);
|
||
}
|
||
});
|
||
window.ethereum
|
||
.request({
|
||
method: "eth_chainId",
|
||
})
|
||
.then((chainId) => {
|
||
window.currentChainId = chainId;
|
||
if (chainId !== "0x38") {
|
||
renderError(
|
||
"Please switch MetaMask to Binance Smart Chain Mainnet"
|
||
);
|
||
} else {
|
||
router.routeTo(location.hash);
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
console.error("Error fetching chain ID:", error);
|
||
renderError("Failed to fetch chain ID");
|
||
});
|
||
}
|
||
// }).catch((error) => {
|
||
// setMetaMaskStatus(false)
|
||
// if (error.code === 4001) {
|
||
// // EIP-1193 userRejectedRequest error
|
||
// notify('Please connect to MetaMask to continue', 'error')
|
||
// } else {
|
||
// if (error === 'MetaMask not installed') {
|
||
// getRef('balance_section').classList.add('hidden')
|
||
// getRef('error_section').classList.remove('hidden')
|
||
// }
|
||
// else
|
||
// console.error(error)
|
||
// }
|
||
// })
|
||
if (typeof window.ethereum !== "undefined") {
|
||
ethereum.on("accountsChanged", (accounts) => {
|
||
getRef("bsc_balance_wrapper").classList.add("hidden");
|
||
setMetaMaskStatus(accounts.length > 0);
|
||
});
|
||
ethereum.on("connect", (accounts) => {
|
||
setMetaMaskStatus(accounts.length > 0);
|
||
});
|
||
ethereum.on("disconnect", (accounts) => {
|
||
setMetaMaskStatus(false);
|
||
});
|
||
}
|
||
});
|
||
router.addRoute("404", () => {
|
||
renderElem(getRef("page_container"), html` <h1>Page not found</h1> `);
|
||
});
|
||
router.addRoute("", renderHome);
|
||
router.addRoute("balance", (state) => {
|
||
renderHome();
|
||
if (state.wildcards && state.wildcards[0]) {
|
||
checkBalance(state.wildcards[0]);
|
||
}
|
||
});
|
||
router.addRoute("tdx", renderTdx);
|
||
|
||
class LazyLoader {
|
||
constructor(container, elementsToRender, renderFn, options = {}) {
|
||
const { pageSize = 10 } = options;
|
||
|
||
this.container = document.querySelector(container);
|
||
this.elementsToRender = elementsToRender;
|
||
this.renderFn = renderFn;
|
||
this.pageSize = pageSize;
|
||
this.currentPage = 1;
|
||
this.totalPages = Math.ceil(elementsToRender.length / pageSize);
|
||
}
|
||
|
||
init() {
|
||
this.container.innerHTML = "";
|
||
this.renderCurrentPage();
|
||
this.renderPagination();
|
||
}
|
||
|
||
renderCurrentPage() {
|
||
const start = (this.currentPage - 1) * this.pageSize;
|
||
const end = start + this.pageSize;
|
||
const currentElements = this.elementsToRender.slice(start, end);
|
||
|
||
currentElements.forEach((element) => {
|
||
this.container.appendChild(this.renderFn(element));
|
||
});
|
||
}
|
||
|
||
renderPagination() {
|
||
const existingPagination = document.querySelector(
|
||
".pagination-controls"
|
||
);
|
||
if (existingPagination) {
|
||
existingPagination.remove();
|
||
}
|
||
|
||
const paginationDiv = document.createElement("div");
|
||
paginationDiv.className = "pagination-controls";
|
||
paginationDiv.style.cssText = `width: 100%;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 0.75rem;
|
||
border-radius: 0.5rem;
|
||
background-color: rgba(var(--foreground-color), 1);
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||
border: 1px solid rgba(var(--text-color), 0.1);
|
||
`;
|
||
|
||
// Previous button
|
||
const prevButton = document.createElement("button");
|
||
prevButton.className = "button";
|
||
prevButton.textContent = "Previous";
|
||
prevButton.disabled = this.currentPage === 1;
|
||
prevButton.onclick = () => this.goToPage(this.currentPage - 1);
|
||
|
||
// Next button
|
||
const nextButton = document.createElement("button");
|
||
nextButton.className = "button";
|
||
nextButton.textContent = "Next";
|
||
nextButton.disabled = this.currentPage === this.totalPages;
|
||
nextButton.onclick = () => this.goToPage(this.currentPage + 1);
|
||
|
||
// Page info
|
||
const pageInfo = document.createElement("span");
|
||
pageInfo.textContent = `Page ${this.currentPage} of ${this.totalPages}`;
|
||
|
||
paginationDiv.appendChild(prevButton);
|
||
paginationDiv.appendChild(pageInfo);
|
||
paginationDiv.appendChild(nextButton);
|
||
this.container.parentNode.insertBefore(
|
||
paginationDiv,
|
||
this.container.nextSibling
|
||
);
|
||
}
|
||
|
||
goToPage(pageNumber) {
|
||
if (pageNumber < 1 || pageNumber > this.totalPages) return;
|
||
this.currentPage = pageNumber;
|
||
this.container.innerHTML = "";
|
||
this.renderCurrentPage();
|
||
this.renderPagination();
|
||
}
|
||
|
||
update(newElements) {
|
||
this.elementsToRender = newElements;
|
||
this.totalPages = Math.ceil(newElements.length / this.pageSize);
|
||
this.currentPage = 1;
|
||
this.init();
|
||
}
|
||
|
||
clear() {
|
||
this.container.innerHTML = "";
|
||
this.elementsToRender = [];
|
||
this.currentPage = 1;
|
||
this.totalPages = 0;
|
||
const paginationDiv = document.querySelector(".pagination-controls");
|
||
if (paginationDiv) {
|
||
paginationDiv.remove();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Initialize transactions variable
|
||
let transactionsLazyLoader;
|
||
|
||
// Add render object with transaction rendering functions
|
||
const render = {
|
||
async transactions(address) {
|
||
try {
|
||
if (!address || !bscOperator.isValidAddress(address)) {
|
||
return;
|
||
}
|
||
getRef("address_transactions").classList.remove("hidden");
|
||
getRef("transactions_list").innerHTML =
|
||
'<sm-spinner class="justify-self-center margin-top-1-5"></sm-spinner>';
|
||
|
||
Promise.all([bscOperator.getTransactionHistory(address)]).then(
|
||
async ([response]) => {
|
||
if (!response || response.error) {
|
||
getRef("transactions_list").textContent =
|
||
"Error fetching transactions";
|
||
return;
|
||
}
|
||
const data = response;
|
||
|
||
if (data && data.length > 0) {
|
||
const transactions = data.map((tx) => {
|
||
const isOutgoing =
|
||
tx.from.toUpperCase() === address.toUpperCase();
|
||
const txObj = {
|
||
txid: tx.hash,
|
||
time: getFormattedTime(tx.timeStamp),
|
||
originalTimestamp: tx.timeStamp,
|
||
block: tx.blockNumber || -1,
|
||
confirmations: tx.confirmations || 0,
|
||
type: isOutgoing ? "out" : "in",
|
||
amount: tx.value / 1000000000000000000,
|
||
sender: tx.from,
|
||
receiver: tx.to,
|
||
address: address,
|
||
};
|
||
return txObj;
|
||
});
|
||
// Filter
|
||
const filter = getRef("filter_selector").value || "all";
|
||
let filteredTransactions = transactions;
|
||
|
||
if (filter === "sent") {
|
||
filteredTransactions = transactions.filter(
|
||
(tx) => tx.type === "out"
|
||
);
|
||
} else if (filter === "received") {
|
||
filteredTransactions = transactions.filter(
|
||
(tx) => tx.type === "in"
|
||
);
|
||
}
|
||
|
||
transactionsLazyLoader = new LazyLoader(
|
||
"#transactions_list",
|
||
filteredTransactions,
|
||
render.transactionCard,
|
||
{
|
||
pageSize: 10,
|
||
}
|
||
);
|
||
if (transactionsLazyLoader) {
|
||
transactionsLazyLoader.update(filteredTransactions);
|
||
} else {
|
||
transactionsLazyLoader = new LazyLoader(
|
||
"#transactions_list",
|
||
filteredTransactions,
|
||
render.transactionCard,
|
||
{
|
||
pageSize: 10,
|
||
}
|
||
);
|
||
transactionsLazyLoader.init();
|
||
}
|
||
getRef("filter_selector").addEventListener("change", (e) => {
|
||
const newFilter = e.target.value;
|
||
let newFilteredTransactions = transactions;
|
||
if (newFilter === "sent") {
|
||
newFilteredTransactions = transactions.filter(
|
||
(tx) => tx.type === "out"
|
||
);
|
||
} else if (newFilter === "received") {
|
||
newFilteredTransactions = transactions.filter(
|
||
(tx) => tx.type === "in"
|
||
);
|
||
}
|
||
transactionsLazyLoader.update(newFilteredTransactions);
|
||
});
|
||
const list = getRef("transactions_list");
|
||
list.onclick = (event) => {
|
||
const transactionCard =
|
||
event.target.closest(".transaction");
|
||
if (transactionCard && event.target === transactionCard) {
|
||
const txid = transactionCard.dataset.txid;
|
||
window.location.hash = `#/tdx/${txid}`;
|
||
}
|
||
};
|
||
getRef(
|
||
"transactions_list"
|
||
).previousElementSibling.classList.remove("hidden");
|
||
} else {
|
||
getRef("transactions_list").textContent =
|
||
"No transactions found";
|
||
}
|
||
}
|
||
);
|
||
} catch (error) {
|
||
console.error("[DEBUG] Error fetching transactions:", error);
|
||
getRef("transactions_list").textContent =
|
||
"Error fetching transactions";
|
||
}
|
||
},
|
||
|
||
transactionCard(transactionDetails) {
|
||
const transactionCard = document.createElement("li");
|
||
transactionCard.className = `transaction ${transactionDetails.type} ${
|
||
transactionDetails.block < 0 ? "unconfirmed-tx" : ""
|
||
}`;
|
||
transactionCard.dataset.txid = transactionDetails.txid;
|
||
transactionCard.style.cursor = "pointer";
|
||
transactionCard.onclick = () => {
|
||
window.location.hash = `#/tdx/${transactionDetails.txid}`;
|
||
};
|
||
|
||
let icon;
|
||
if (transactionDetails.type === "out") {
|
||
icon = `<svg class="icon sent" style="display: block; margin: auto;" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FF4B4B"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/></svg>`;
|
||
} else {
|
||
icon = `<svg class="icon received" style="display: block; margin: auto;" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#4BC84B"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/></svg>`;
|
||
}
|
||
|
||
const shortenAddress = (addr) => {
|
||
if (!addr) return "";
|
||
return (
|
||
addr.substring(0, 8) + "..." + addr.substring(addr.length - 6)
|
||
);
|
||
};
|
||
|
||
const fromAddress = transactionDetails.sender
|
||
? shortenAddress(transactionDetails.sender)
|
||
: "";
|
||
const toAddress = transactionDetails.receiver
|
||
? shortenAddress(transactionDetails.receiver)
|
||
: "";
|
||
const txHashShort = shortenAddress(transactionDetails.txid);
|
||
|
||
transactionCard.innerHTML = `
|
||
<div class="flex gap-1 align-center w-100 padding-0-5" style="border-radius: 0.5rem; border: 1px solid rgba(var(--text-color), 0.1); padding: 0.75rem; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">
|
||
<div class="transaction__icon flex align-center justify-center" style="background-color: ${
|
||
transactionDetails.type === "out"
|
||
? "rgba(255,75,75,0.1)"
|
||
: "rgba(75,200,75,0.1)"
|
||
}; border-radius: 50%; min-width: 40px; height: 40px; padding: 8px;">
|
||
${icon}
|
||
</div>
|
||
<div class="grid gap-0-5 flex-1">
|
||
<div class="flex align-center space-between flex-wrap">
|
||
<div class="transaction__type" style="font-weight: 500; margin-right: 8px;">
|
||
${transactionDetails.type === "out" ? "Sent" : "Received"}
|
||
</div>
|
||
<time style="color: rgba(var(--text-color), 0.6); font-size: 0.85rem;">${
|
||
typeof transactionDetails.time === "string"
|
||
? transactionDetails.time
|
||
: getFormattedTime(transactionDetails.time)
|
||
}</time>
|
||
</div>
|
||
<div class="flex align-center space-between flex-wrap">
|
||
<div class="transaction__amount bsc-value" data-bsc="${
|
||
transactionDetails.amount
|
||
}" data-timestamp="${
|
||
transactionDetails.originalTimestamp || ""
|
||
}" style="font-weight: 600; color: ${
|
||
transactionDetails.type === "out"
|
||
? "var(--color-danger, #ff4b4b)"
|
||
: "var(--color-success, #4bc84b)"
|
||
}; margin-right: 8px; text-overflow: ellipsis; overflow: hidden; max-width: 100%;">
|
||
${formatCurrency(transactionDetails.amount)}
|
||
</div>
|
||
<div class="transaction__status" style="font-size: 0.85rem; color: rgba(var(--text-color), ${
|
||
transactionDetails.block < 0 ? "0.5" : "0.7"
|
||
});">
|
||
${
|
||
transactionDetails.block < 0
|
||
? "Pending"
|
||
: transactionDetails.confirmations > 6
|
||
? "Confirmed"
|
||
: `${transactionDetails.confirmations} confirmations`
|
||
}
|
||
</div>
|
||
</div>
|
||
<div class="flex flex-wrap gap-0-5 margin-top-0-5">
|
||
${
|
||
transactionDetails.sender
|
||
? `<div class="flex align-center gap-0-3">
|
||
<span style="font-size: 0.85rem; color: rgba(var(--text-color), 0.7);">From:</span>
|
||
<span class="address-from interactive" data-address="${transactionDetails.sender}" style="font-size: 0.85rem; color: var(--color-primary); cursor: pointer;">${fromAddress}</span>
|
||
</div>`
|
||
: ""
|
||
}
|
||
${
|
||
transactionDetails.receiver
|
||
? `<div class="flex align-center gap-0-3">
|
||
<span style="font-size: 0.85rem; color: rgba(var(--text-color), 0.7);">To:</span>
|
||
<span class="address-to interactive" data-address="${transactionDetails.receiver}" style="font-size: 0.85rem; color: var(--color-primary); cursor: pointer;">${toAddress}</span>
|
||
</div>`
|
||
: ""
|
||
}
|
||
<div class="flex align-center gap-0-3">
|
||
<span style="font-size: 0.85rem; color: rgba(var(--text-color), 0.7);">Tx:</span>
|
||
<span class="txid interactive" data-txid="${
|
||
transactionDetails.txid
|
||
}" style="font-size: 0.85rem; color: var(--color-primary,#92a2ff); cursor: pointer;">${txHashShort}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// Add event listeners
|
||
transactionCard
|
||
.querySelectorAll(".address-from, .address-to")
|
||
.forEach((el) => {
|
||
el.addEventListener("click", (e) => {
|
||
e.stopPropagation();
|
||
const address = el.dataset.address;
|
||
if (address) {
|
||
window.location.hash = `#/balance/${address}`;
|
||
}
|
||
});
|
||
});
|
||
|
||
transactionCard
|
||
.querySelector(".txid")
|
||
.addEventListener("click", (e) => {
|
||
e.stopPropagation();
|
||
});
|
||
|
||
transactionCard.classList.add("interactive");
|
||
return transactionCard;
|
||
},
|
||
|
||
queryResult(query) {
|
||
const type = checkQueryStringType(query);
|
||
|
||
if (type === "address") {
|
||
getRef("tx_details").classList.add("hidden");
|
||
getRef("address_transactions").classList.remove("hidden");
|
||
render.transactions(query);
|
||
|
||
if (bscOperator.isValidAddress(query)) {
|
||
checkBalance(query);
|
||
}
|
||
} else if (type === "txid") {
|
||
getRef("bsc_balance_wrapper").classList.add("hidden");
|
||
getRef("address_transactions").classList.add("hidden");
|
||
render.txDetails(query);
|
||
if (transactionsLazyLoader) {
|
||
transactionsLazyLoader.clear();
|
||
transactionsLazyLoader = null;
|
||
}
|
||
} else {
|
||
if (transactionsLazyLoader) {
|
||
transactionsLazyLoader.clear();
|
||
transactionsLazyLoader = null;
|
||
}
|
||
getRef("address_transactions").classList.add("hidden");
|
||
getRef("tx_details").classList.add("hidden");
|
||
notify("Invalid Binance address or transaction hash", "error");
|
||
}
|
||
},
|
||
|
||
// Transaction details view
|
||
async txDetails(txId) {
|
||
if (!txId || !/^0x([A-Fa-f0-9]{64})$/.test(txId)) {
|
||
notify("Invalid transaction hash", "error");
|
||
return;
|
||
}
|
||
|
||
getRef("tx_details").classList.remove("hidden");
|
||
renderElem(
|
||
getRef("tx_details"),
|
||
html`
|
||
<div class="tx-details-container">
|
||
<div class="tx-header">
|
||
<h2 class="tx-title">Transaction Details</h2>
|
||
</div>
|
||
|
||
<div id="tx_details_container" class="tx-content">
|
||
<div class="tx-card">
|
||
<!-- Status Header -->
|
||
<div class="tx-status-header">
|
||
<div
|
||
id="tx_status_indicator"
|
||
class="status-indicator"
|
||
></div>
|
||
<div class="status-details">
|
||
<h3 id="tx_status_title" class="status-title"></h3>
|
||
<p id="tx_status_subtext" class="status-subtext"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Main Transaction Info -->
|
||
<div class="tx-info-grid">
|
||
<!-- From/To Section -->
|
||
<div class="tx-address-section">
|
||
<div class="address-card">
|
||
<label class="address-label">From</label>
|
||
<sm-copy
|
||
id="tx_from"
|
||
class="address-value"
|
||
button-class="copy-button"
|
||
></sm-copy>
|
||
</div>
|
||
|
||
<div class="tx-arrow">→</div>
|
||
|
||
<div class="address-card">
|
||
<label class="address-label">To</label>
|
||
<sm-copy
|
||
id="tx_to"
|
||
class="address-value"
|
||
button-class="copy-button"
|
||
></sm-copy>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Transaction Hash -->
|
||
<div class="tx-hash-section">
|
||
<label class="section-label">Transaction Hash</label>
|
||
<sm-copy
|
||
id="tx_hash"
|
||
class="hash-value"
|
||
button-class="copy-button"
|
||
></sm-copy>
|
||
</div>
|
||
|
||
<!-- Metrics Grid -->
|
||
<div class="tx-metrics-grid">
|
||
<div class="metric-card">
|
||
<label class="metric-label">Amount</label>
|
||
<div id="tx_value" class="metric-value"></div>
|
||
</div>
|
||
|
||
<div class="metric-card">
|
||
<label class="metric-label">Transaction Fee</label>
|
||
<div id="tx_fee" class="metric-value"></div>
|
||
</div>
|
||
|
||
<div class="metric-card">
|
||
<label class="metric-label">Status</label>
|
||
<div id="tx_status" class="metric-value"></div>
|
||
</div>
|
||
|
||
<div class="metric-card">
|
||
<label class="metric-label">Timestamp</label>
|
||
<div id="tx_timestamp" class="metric-value"></div>
|
||
</div>
|
||
|
||
<div class="metric-card">
|
||
<label class="metric-label">Block</label>
|
||
<div id="tx_block" class="metric-value"></div>
|
||
</div>
|
||
|
||
<div class="metric-card">
|
||
<label class="metric-label">Gas Price</label>
|
||
<div id="tx_gasPrice" class="metric-value"></div>
|
||
</div>
|
||
|
||
<div class="metric-card">
|
||
<label class="metric-label">Gas Used</label>
|
||
<div id="tx_gasUsed" class="metric-value"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`
|
||
);
|
||
|
||
try {
|
||
// Fetch and display transaction details
|
||
const tx = await bscOperator.getTransactionDetails(txId);
|
||
|
||
if (!tx) {
|
||
renderElem(
|
||
getRef("tx_details_container"),
|
||
html`
|
||
<div class="error-state">
|
||
<div class="error-icon">!</div>
|
||
<h3>Transaction Not Found</h3>
|
||
<p>
|
||
The requested transaction could not be found on the
|
||
network.
|
||
</p>
|
||
<button
|
||
class="button"
|
||
onclick="getRef('tx_details').classList.add('hidden')"
|
||
>
|
||
Close
|
||
</button>
|
||
</div>
|
||
`
|
||
);
|
||
return;
|
||
}
|
||
|
||
updateTransactionDetails(tx);
|
||
} catch (error) {
|
||
console.error("Error fetching transaction details:", error);
|
||
renderElem(
|
||
getRef("tx_details_container"),
|
||
html`
|
||
<div class="error-state">
|
||
<div class="error-icon">⚠️</div>
|
||
<h3>Error Loading Transaction</h3>
|
||
<p>
|
||
We encountered an issue fetching the transaction details.
|
||
Please try again later.
|
||
</p>
|
||
<button
|
||
class="button"
|
||
onclick="getRef('tx_details').classList.add('hidden')"
|
||
>
|
||
Close
|
||
</button>
|
||
</div>
|
||
`
|
||
);
|
||
}
|
||
},
|
||
};
|
||
|
||
function checkQueryStringType(str) {
|
||
if (bscOperator.isValidAddress(str)) {
|
||
return "address";
|
||
} else if (/^0x([A-Fa-f0-9]{64})$/.test(str)) {
|
||
return "txid";
|
||
} else {
|
||
return "invalid";
|
||
}
|
||
}
|
||
|
||
function getFormattedTime(timestamp) {
|
||
try {
|
||
if (String(timestamp).length < 13) timestamp *= 1000;
|
||
let [day, month, date, year] = new Date(timestamp)
|
||
.toString()
|
||
.split(" "),
|
||
minutes = new Date(timestamp).getMinutes(),
|
||
hours = new Date(timestamp).getHours();
|
||
|
||
minutes = minutes < 10 ? `0${minutes}` : minutes;
|
||
const finalHours =
|
||
hours >= 12
|
||
? `${hours === 12 ? 12 : hours - 12}:${minutes} PM`
|
||
: `${hours === 0 ? 12 : hours}:${minutes} AM`;
|
||
|
||
return `${date} ${month} ${year}, ${finalHours}`;
|
||
} catch (e) {
|
||
console.error(e);
|
||
return timestamp;
|
||
}
|
||
}
|
||
|
||
function renderHome(state) {
|
||
getRef("page_container").dataset.page = "home";
|
||
renderElem(
|
||
getRef("page_container"),
|
||
html`
|
||
<aside
|
||
id="saved_addresses_wrapper"
|
||
class="flex flex-direction-column"
|
||
>
|
||
<h4>Searched addresses</h4>
|
||
<ul id="searched_addresses_list" class="grid gap-0-5"></ul>
|
||
</aside>
|
||
<section id="balance_section" class="grid gap-1-5">
|
||
<h2>Check Binance, USDC and USDT balance</h2>
|
||
<sm-form oninvalid="handleInvalidSearch()">
|
||
<div id="input_wrapper">
|
||
<sm-input
|
||
id="check_balance_input"
|
||
class="password-field flex-1"
|
||
placeholder="FLO/BTC private key or BSC address"
|
||
type="password"
|
||
animate
|
||
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 class="multi-state-button">
|
||
<button
|
||
id="check_balance_button"
|
||
class="button button--primary h-100 w-100"
|
||
type="submit"
|
||
onclick=${() => checkBalance()}
|
||
disabled
|
||
>
|
||
Check balance
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</sm-form>
|
||
<div id="bsc_balance_wrapper" class="grid gap-2 hidden">
|
||
<div class="balance-card">
|
||
<div class="grid">
|
||
<div class="label">BSC address</div>
|
||
<sm-copy id="BSC_address"></sm-copy>
|
||
</div>
|
||
<div class="grid gap-1">
|
||
<h4>Balance</h4>
|
||
<ul
|
||
id="token_balances"
|
||
class="flex flex-direction-column gap-0-5"
|
||
>
|
||
<li class="flex align-center space-between">
|
||
<p>BNB</p>
|
||
<b id="Binance_balance" class="bsc-value"></b>
|
||
</li>
|
||
<li class="flex align-center space-between">
|
||
<p>USDC</p>
|
||
<b id="usdc_balance" class="bsc-value"></b>
|
||
</li>
|
||
<li class="flex align-center space-between">
|
||
<p>USDT</p>
|
||
<b id="usdt_balance" class="bsc-value"></b>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div></div>
|
||
<div
|
||
id="address_transactions"
|
||
class="grid gap-2 hidden"
|
||
style="width: min(32rem, 100%);"
|
||
>
|
||
<div
|
||
class="flex flex-direction-column gap-0-5 sticky top-0"
|
||
style="background-color: rgba(var(--foreground-color), 1); z-index: 2; padding: 1rem 0; border-bottom: 1px solid rgba(var(--text-color), 0.1);"
|
||
>
|
||
<div class="flex align-center gap-0-5 space-between">
|
||
<h4>Transactions</h4>
|
||
<sm-chips
|
||
id="filter_selector"
|
||
onchange=${(e) =>
|
||
render.transactions(
|
||
getRef("check_balance_input").value
|
||
)}
|
||
>
|
||
<sm-chip value="all" selected>All</sm-chip>
|
||
<sm-chip value="sent">Sent</sm-chip>
|
||
<sm-chip value="received">Received</sm-chip>
|
||
</sm-chips>
|
||
</div>
|
||
</div>
|
||
<ul
|
||
id="transactions_list"
|
||
class="observe-empty-state grid gap-1"
|
||
></ul>
|
||
<div
|
||
class="empty-state align-self-center text-center margin-top-2"
|
||
>
|
||
<svg
|
||
class="icon"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
height="48px"
|
||
viewBox="0 0 24 24"
|
||
width="48px"
|
||
fill="rgba(var(--text-color), 0.5)"
|
||
>
|
||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||
<path
|
||
d="M19.5 3.5L18 2l-1.5 1.5L15 2l-1.5 1.5L12 2l-1.5 1.5L9 2 7.5 3.5 6 2 4.5 3.5 3 2v20l1.5-1.5L6 22l1.5-1.5L9 22l1.5-1.5L12 22l1.5-1.5L15 22l1.5-1.5L18 22l1.5-1.5L21 22V2l-1.5 1.5zM19 19.09H5V4.91h14v14.18zM6 15h12v2H6zm0-4h12v2H6zm0-4h12v2H6z"
|
||
/>
|
||
</svg>
|
||
<p
|
||
class="margin-top-1"
|
||
style="color: rgba(var(--text-color), 0.7);"
|
||
>
|
||
Transactions will appear here
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<div id="tx_details" class="hidden"></div>
|
||
`
|
||
);
|
||
if (
|
||
window.ethereum &&
|
||
!(window.currentChainId && window.currentChainId === "0x38")
|
||
) {
|
||
renderError("Please switch MetaMask to Binance Mainnet");
|
||
}
|
||
renderSearchedAddressList();
|
||
}
|
||
function renderError(title, description) {
|
||
if (!title) title = "MetaMask not installed";
|
||
if (!description) description = "";
|
||
renderElem(
|
||
getRef("page_container"),
|
||
html`
|
||
<section id="error_section">
|
||
<svg
|
||
class="icon"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
xml:space="preserve"
|
||
id="Layer_1"
|
||
x="0"
|
||
y="0"
|
||
version="1.1"
|
||
viewBox="0 0 318.6 318.6"
|
||
>
|
||
<style>
|
||
.st1,
|
||
.st6 {
|
||
fill: #e4761b;
|
||
stroke: #e4761b;
|
||
stroke-linecap: round;
|
||
stroke-linejoin: round;
|
||
}
|
||
|
||
.st6 {
|
||
fill: #f6851b;
|
||
stroke: #f6851b;
|
||
}
|
||
</style>
|
||
<path
|
||
fill="#e2761b"
|
||
stroke="#e2761b"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m274.1 35.5-99.5 73.9L193 65.8z"
|
||
/>
|
||
<path
|
||
d="m44.4 35.5 98.7 74.6-17.5-44.3zm193.9 171.3-26.5 40.6 56.7 15.6 16.3-55.3zm-204.4.9L50.1 263l56.7-15.6-26.5-40.6z"
|
||
class="st1"
|
||
/>
|
||
<path
|
||
d="m103.6 138.2-15.8 23.9 56.3 2.5-2-60.5zm111.3 0-39-34.8-1.3 61.2 56.2-2.5zM106.8 247.4l33.8-16.5-29.2-22.8zm71.1-16.5 33.9 16.5-4.7-39.3z"
|
||
class="st1"
|
||
/>
|
||
<path
|
||
fill="#d7c1b3"
|
||
stroke="#d7c1b3"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m211.8 247.4-33.9-16.5 2.7 22.1-.3 9.3zm-105 0 31.5 14.9-.2-9.3 2.5-22.1z"
|
||
/>
|
||
<path
|
||
fill="#233447"
|
||
stroke="#233447"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m138.8 193.5-28.2-8.3 19.9-9.1zm40.9 0 8.3-17.4 20 9.1z"
|
||
/>
|
||
<path
|
||
fill="#cd6116"
|
||
stroke="#cd6116"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m106.8 247.4 4.8-40.6-31.3.9zM207 206.8l4.8 40.6 26.5-39.7zm23.8-44.7-56.2 2.5 5.2 28.9 8.3-17.4 20 9.1zm-120.2 23.1 20-9.1 8.2 17.4 5.3-28.9-56.3-2.5z"
|
||
/>
|
||
<path
|
||
fill="#e4751f"
|
||
stroke="#e4751f"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m87.8 162.1 23.6 46-.8-22.9zm120.3 23.1-1 22.9 23.7-46zm-64-20.6-5.3 28.9 6.6 34.1 1.5-44.9zm30.5 0-2.7 18 1.2 45 6.7-34.1z"
|
||
/>
|
||
<path
|
||
d="m179.8 193.5-6.7 34.1 4.8 3.3 29.2-22.8 1-22.9zm-69.2-8.3.8 22.9 29.2 22.8 4.8-3.3-6.6-34.1z"
|
||
class="st6"
|
||
/>
|
||
<path
|
||
fill="#c0ad9e"
|
||
stroke="#c0ad9e"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m180.3 262.3.3-9.3-2.5-2.2h-37.7l-2.3 2.2.2 9.3-31.5-14.9 11 9 22.3 15.5h38.3l22.4-15.5 11-9z"
|
||
/>
|
||
<path
|
||
fill="#161616"
|
||
stroke="#161616"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m177.9 230.9-4.8-3.3h-27.7l-4.8 3.3-2.5 22.1 2.3-2.2h37.7l2.5 2.2z"
|
||
/>
|
||
<path
|
||
fill="#763d16"
|
||
stroke="#763d16"
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
d="m278.3 114.2 8.5-40.8-12.7-37.9-96.2 71.4 37 31.3 52.3 15.3 11.6-13.5-5-3.6 8-7.3-6.2-4.8 8-6.1zM31.8 73.4l8.5 40.8-5.4 4 8 6.1-6.1 4.8 8 7.3-5 3.6 11.5 13.5 52.3-15.3 37-31.3-96.2-71.4z"
|
||
/>
|
||
<path
|
||
d="m267.2 153.5-52.3-15.3 15.9 23.9-23.7 46 31.2-.4h46.5zm-163.6-15.3-52.3 15.3-17.4 54.2h46.4l31.1.4-23.6-46zm71 26.4 3.3-57.7 15.2-41.1h-67.5l15 41.1 3.5 57.7 1.2 18.2.1 44.8h27.7l.2-44.8z"
|
||
class="st6"
|
||
/>
|
||
</svg>
|
||
<h2 id="error__title">${title}</h2>
|
||
<p>${description}</p>
|
||
</section>
|
||
`
|
||
);
|
||
}
|
||
function renderSearchedAddressList() {
|
||
compactIDB
|
||
.readAllData("contacts")
|
||
.then((contacts) => {
|
||
if (!getRef("searched_addresses_list")) return;
|
||
if (Object.keys(contacts).length === 0) {
|
||
renderElem(
|
||
getRef("searched_addresses_list"),
|
||
html`<li class="flex align-center justify-center">
|
||
<p>
|
||
Your searched addresses will appear here for easier access
|
||
in future.
|
||
</p>
|
||
</li>`
|
||
);
|
||
return;
|
||
}
|
||
const renderedContacts = [];
|
||
for (const floAddress in contacts) {
|
||
const { BSCAddress } = contacts[floAddress];
|
||
renderedContacts.push(html` <li
|
||
class="contact"
|
||
.dataset=${{ floAddress, BSCAddress }}
|
||
>
|
||
${floAddress === BSCAddress
|
||
? html``
|
||
: html`
|
||
<sm-chips
|
||
onchange=${(e) =>
|
||
(e.target
|
||
.closest(".contact")
|
||
.querySelector("sm-copy").value = e.target.value)}
|
||
>
|
||
<sm-chip value=${floAddress} selected
|
||
>${floAddress.startsWith("F")
|
||
? "FLO"
|
||
: "BTC"}</sm-chip
|
||
>
|
||
<sm-chip value=${BSCAddress}>BSC</sm-chip>
|
||
</sm-chips>
|
||
`}
|
||
<sm-copy value="${floAddress}"></sm-copy>
|
||
<div class="flex align-center space-between gap-0-5">
|
||
<button
|
||
class="button button--small"
|
||
onclick=${() => deleteContact(floAddress)}
|
||
>
|
||
Delete
|
||
</button>
|
||
<button
|
||
class="button button--colored button--small"
|
||
onclick=${() => checkBalance(BSCAddress, floAddress)}
|
||
>
|
||
Check balance
|
||
</button>
|
||
</div>
|
||
</li>`);
|
||
}
|
||
renderElem(
|
||
getRef("searched_addresses_list"),
|
||
html`${renderedContacts}`
|
||
);
|
||
})
|
||
.catch((error) => {
|
||
console.error(error);
|
||
});
|
||
}
|
||
function renderTdx(state) {
|
||
getRef("page_container").dataset.page = "tdx";
|
||
|
||
const txId = state.wildcards[0];
|
||
|
||
if (!txId || !/^0x([A-Fa-f0-9]{64})$/.test(txId)) {
|
||
notify("Invalid transaction hash", "error");
|
||
router.routeTo("#/balance");
|
||
return;
|
||
}
|
||
|
||
renderElem(
|
||
getRef("page_container"),
|
||
html`
|
||
<section class="tx-details-container">
|
||
<div class="tx-header">
|
||
<h2 class="tx-title">Transaction Details</h2>
|
||
|
||
${currentCurrency !== "bsc" && isHistoricApiAvailable
|
||
? html`
|
||
<div class="margin-left-1">
|
||
<sm-switch
|
||
id="tx_valuation_toggle"
|
||
?checked=${showCurrentValue}
|
||
>
|
||
<p slot="left" class="margin-right-0-5">
|
||
Show current value
|
||
</p>
|
||
</sm-switch>
|
||
</div>
|
||
`
|
||
: ""}
|
||
</div>
|
||
|
||
<div id="tx_details_container" class="tx-content">
|
||
<div class="loading-state" style="display: flex; flex-direction: column; align-items: center;">
|
||
<sm-spinner size="lg"></sm-spinner>
|
||
<p class="loading-text">Fetching transaction details...</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
`
|
||
);
|
||
|
||
// Fetch and display transaction details
|
||
bscOperator
|
||
.getTransactionDetails(txId)
|
||
.then(async (tx) => {
|
||
if (!tx) {
|
||
renderNotFound();
|
||
return;
|
||
}
|
||
|
||
// Store the address for back navigation
|
||
window.lastViewedAddress = tx.from;
|
||
|
||
const value = tx.value
|
||
? ethers.utils.formatEther(ethers.BigNumber.from(tx.value._hex))
|
||
: "0";
|
||
const gasPrice = tx.gasPrice
|
||
? ethers.utils.formatUnits(
|
||
ethers.BigNumber.from(tx.gasPrice._hex),
|
||
"gwei"
|
||
)
|
||
: "N/A";
|
||
const gasUsed = tx.gasUsed
|
||
? ethers.utils.formatUnits(ethers.BigNumber.from(tx.gasUsed._hex))
|
||
: "Pending";
|
||
const timestamp = tx.timeStamp
|
||
? getFormattedTime(tx.timeStamp)
|
||
: "Pending";
|
||
const status = tx.blockNumber ? "confirmed" : "pending";
|
||
|
||
const fee =
|
||
tx.gasUsed && tx.gasPrice
|
||
? ethers.utils.formatEther(
|
||
ethers.BigNumber.from(tx.gasUsed._hex).mul(
|
||
ethers.BigNumber.from(tx.gasPrice._hex)
|
||
)
|
||
)
|
||
: "Pending";
|
||
|
||
renderElem(
|
||
getRef("tx_details_container"),
|
||
html`
|
||
<div class="tx-card" id="tx-status">
|
||
<!-- Status Header -->
|
||
<div class="tx-status-header">
|
||
<div
|
||
id="tx_status_indicator"
|
||
class="status-indicator"
|
||
></div>
|
||
<div class="status-details">
|
||
<h3 id="tx_status_title" class="status-title"></h3>
|
||
<p id="tx_status_subtext" class="status-subtext"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Main Transaction Info -->
|
||
<div class="tx-info-grid">
|
||
<!-- From/To Section -->
|
||
<div class="tx-address-section">
|
||
<div class="address-card">
|
||
<label class="address-label">From</label>
|
||
<sm-copy id="tx_from" class="address-value"></sm-copy>
|
||
</div>
|
||
|
||
<div class="tx-arrow">→</div>
|
||
|
||
<div class="address-card">
|
||
<label class="address-label">To</label>
|
||
<sm-copy id="tx_to" class="address-value"></sm-copy>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Transaction Hash -->
|
||
<div class="tx-hash-section">
|
||
<label class="section-label">Transaction Hash</label>
|
||
<sm-copy id="tx_hash" class="hash-value"></sm-copy>
|
||
</div>
|
||
|
||
<!-- Metrics Grid -->
|
||
<div class="tx-metrics-grid">
|
||
<div class="metric-card">
|
||
<label class="metric-label">Value</label>
|
||
<div
|
||
id="tx_value"
|
||
class="metric-value bsc-value"
|
||
data-eth="${value}"
|
||
data-timestamp="${tx.timeStamp || ""}"
|
||
></div>
|
||
</div>
|
||
|
||
<div class="metric-card">
|
||
<label class="metric-label">Gas Price</label>
|
||
<div id="tx_gasPrice" class="metric-value"></div>
|
||
</div>
|
||
|
||
<div class="metric-card">
|
||
<label class="metric-label">Transaction Fee</label>
|
||
<div
|
||
id="tx_fee"
|
||
class="metric-value bsc-value"
|
||
data-eth="${fee}"
|
||
data-timestamp="${tx.timeStamp || ""}"
|
||
></div>
|
||
</div>
|
||
|
||
<div class="metric-card">
|
||
<label class="metric-label">Timestamp</label>
|
||
<div id="tx_timestamp" class="metric-value"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`
|
||
);
|
||
|
||
if (getRef("tx_status_indicator")) {
|
||
const statusIndicator = getRef("tx_status_indicator");
|
||
if (status === "confirmed") {
|
||
statusIndicator.style.backgroundColor = "var(--color-success)";
|
||
statusIndicator.style.boxShadow =
|
||
"0 0 0 4px rgba(var(--color-success-rgb), 0.2)";
|
||
} else {
|
||
statusIndicator.style.backgroundColor = "var(--color-warning)";
|
||
statusIndicator.style.boxShadow =
|
||
"0 0 0 4px rgba(var(--color-warning-rgb), 0.2)";
|
||
}
|
||
}
|
||
|
||
if (getRef("tx_status_title")) {
|
||
getRef("tx_status_title").textContent =
|
||
status === "confirmed"
|
||
? "Transaction Confirmed"
|
||
: "Transaction Pending";
|
||
}
|
||
|
||
if (getRef("tx_status_subtext")) {
|
||
getRef("tx_status_subtext").textContent =
|
||
status === "confirmed"
|
||
? `Included in Block #${tx.blockNumber}`
|
||
: "";
|
||
}
|
||
|
||
if (getRef("tx_status")) getRef("tx_status").value = status;
|
||
if (getRef("tx-status")) getRef("tx-status").value = tx.status;
|
||
if (getRef("tx_from")) {
|
||
getRef("tx_from").value = tx.from;
|
||
getRef("tx_from").addEventListener("click", function (e) {
|
||
if (!e.target.closest("sm-copy button")) {
|
||
const address = getRef("tx_from").value;
|
||
window.location.hash = `#/balance/${address}`;
|
||
checkBalance(address);
|
||
}
|
||
});
|
||
getRef("tx_from").style.cursor = "pointer";
|
||
getRef("tx_from").title = "View address details";
|
||
}
|
||
|
||
if (getRef("tx_to")) {
|
||
getRef("tx_to").value = tx.to || "Contract Creation";
|
||
if (tx.to) {
|
||
getRef("tx_to").addEventListener("click", function (e) {
|
||
if (!e.target.closest("sm-copy button")) {
|
||
const address = getRef("tx_to").value;
|
||
window.location.hash = `#/balance/${address}`;
|
||
checkBalance(address);
|
||
}
|
||
});
|
||
getRef("tx_to").style.cursor = "pointer";
|
||
getRef("tx_to").title = "View address details";
|
||
}
|
||
}
|
||
|
||
if (getRef("tx_hash")) getRef("tx_hash").value = tx.hash;
|
||
|
||
if (getRef("tx_value")) {
|
||
getRef("tx_value").dataset.bsc = value;
|
||
getRef("tx_value").dataset.timestamp = tx.timeStamp || "";
|
||
getRef("tx_value").classList.add("bsc-value");
|
||
getRef("tx_value").textContent = formatCurrency(value);
|
||
}
|
||
|
||
if (getRef("tx_gasPrice"))
|
||
getRef("tx_gasPrice").textContent = `${gasPrice} Gwei`;
|
||
if (getRef("tx_fee")) {
|
||
getRef("tx_fee").dataset.bsc = fee;
|
||
getRef("tx_fee").dataset.timestamp = tx.timeStamp || "";
|
||
getRef("tx_fee").classList.add("bsc-value");
|
||
getRef("tx_fee").textContent = formatCurrency(fee);
|
||
}
|
||
if (getRef("tx_timestamp"))
|
||
getRef("tx_timestamp").textContent = timestamp;
|
||
|
||
if (getRef("tx_valuation_toggle")) {
|
||
// Clear any previous event listeners by replacing with a fresh element
|
||
const toggle = getRef("tx_valuation_toggle");
|
||
const toggleParent = toggle.parentElement;
|
||
const newToggle = toggle.cloneNode(true);
|
||
|
||
// Add our event listener to the new element before inserting it
|
||
newToggle.addEventListener("change", async (e) => {
|
||
showCurrentValue = e.target.checked;
|
||
localStorage.setItem(
|
||
"eth-wallet-show-current-value",
|
||
showCurrentValue
|
||
);
|
||
|
||
// Get elements directly to ensure we update them
|
||
const valueElement = getRef("tx_value");
|
||
const feeElement = getRef("tx_fee");
|
||
|
||
if (valueElement && valueElement.dataset.eth) {
|
||
const ethAmount = parseFloat(valueElement.dataset.eth);
|
||
const timestamp = valueElement.dataset.timestamp;
|
||
valueElement.textContent = await getConvertedAmount(
|
||
ethAmount,
|
||
timestamp
|
||
);
|
||
}
|
||
|
||
if (feeElement && feeElement.dataset.eth) {
|
||
const ethFee = parseFloat(feeElement.dataset.eth);
|
||
const timestamp = feeElement.dataset.timestamp;
|
||
feeElement.textContent = await getConvertedAmount(
|
||
ethFee,
|
||
timestamp
|
||
);
|
||
}
|
||
});
|
||
|
||
// Replace the original toggle with our new one
|
||
toggleParent.replaceChild(newToggle, toggle);
|
||
}
|
||
convertAllDisplayedCurrency();
|
||
if (currentCurrency !== "bsc" && !isHistoricApiAvailable) {
|
||
testHistoricalAPIAndRenderToggle();
|
||
}
|
||
})
|
||
.catch((error) => {
|
||
console.error("Error:", error);
|
||
renderErrorState();
|
||
});
|
||
|
||
// Helper functions
|
||
function renderNotFound() {
|
||
renderElem(
|
||
getRef("tx_details_container"),
|
||
html`
|
||
<div class="error-state">
|
||
<div class="error-icon">!</div>
|
||
<h3>Transaction Not Found</h3>
|
||
<p>
|
||
The requested transaction could not be found on the network.
|
||
</p>
|
||
<sm-button onclick="router.routeTo('#/balance')">
|
||
Back to Balance
|
||
</sm-button>
|
||
</div>
|
||
`
|
||
);
|
||
}
|
||
|
||
function renderErrorState() {
|
||
renderElem(
|
||
getRef("tx_details_container"),
|
||
html`
|
||
<div class="error-state">
|
||
<div class="error-icon">⚠️</div>
|
||
<h3>Error Loading Transaction</h3>
|
||
<p>
|
||
We encountered an issue fetching the transaction details.
|
||
Please try again later.
|
||
</p>
|
||
<sm-button
|
||
variant="primary"
|
||
onclick="router.routeTo('#/balance')"
|
||
class="back-button"
|
||
>
|
||
Back to Balance
|
||
</sm-button>
|
||
</div>
|
||
`
|
||
);
|
||
}
|
||
}
|
||
|
||
// Function to update transaction details toggle
|
||
function updateTransactionDetailsToggle() {
|
||
if (getRef("page_container")?.dataset?.page !== "tdx") return;
|
||
|
||
const toggleContainer = document.querySelector(".tx-header");
|
||
if (!toggleContainer) return;
|
||
|
||
const existingToggle =
|
||
toggleContainer.querySelector(".margin-left-auto");
|
||
|
||
if (currentCurrency === "bsc") {
|
||
if (existingToggle) existingToggle.remove();
|
||
return;
|
||
}
|
||
|
||
if (
|
||
currentCurrency !== "bsc" &&
|
||
isHistoricApiAvailable &&
|
||
!existingToggle
|
||
) {
|
||
const toggleDiv = document.createElement("div");
|
||
toggleDiv.className = "margin-left-auto";
|
||
toggleDiv.innerHTML = `
|
||
<sm-switch id="tx_valuation_toggle" ${showCurrentValue ? "checked" : ""}>
|
||
<p slot="left" class="margin-right-0-5">
|
||
Show current value
|
||
</p>
|
||
</sm-switch>
|
||
`;
|
||
toggleContainer.appendChild(toggleDiv);
|
||
|
||
const toggle = toggleDiv.querySelector("sm-switch");
|
||
toggle.addEventListener("change", async (e) => {
|
||
showCurrentValue = e.target.checked;
|
||
localStorage.setItem(
|
||
"bsc-wallet-show-current-value",
|
||
showCurrentValue
|
||
);
|
||
|
||
const valueElement = getRef("tx_value");
|
||
const feeElement = getRef("tx_fee");
|
||
|
||
if (valueElement && valueElement.dataset.bsc) {
|
||
const bscAmount = parseFloat(valueElement.dataset.bsc);
|
||
const timestamp = valueElement.dataset.timestamp;
|
||
valueElement.textContent = await getConvertedAmount(
|
||
bscAmount,
|
||
timestamp
|
||
);
|
||
}
|
||
|
||
if (feeElement && feeElement.dataset.bsc) {
|
||
const bscFee = parseFloat(feeElement.dataset.bsc);
|
||
const timestamp = feeElement.dataset.timestamp;
|
||
feeElement.textContent = await getConvertedAmount(
|
||
bscFee,
|
||
timestamp
|
||
);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
function checkBalance(BSCAddress, floAddress) {
|
||
if (BSCAddress && getRef("check_balance_input")) {
|
||
getRef("check_balance_input").value = BSCAddress;
|
||
}
|
||
|
||
if (!BSCAddress) {
|
||
let keyToConvert = document
|
||
.querySelector("#check_balance_input")
|
||
.value.trim();
|
||
|
||
if (/^0x[0-9a-fA-F]{64}$/.test(keyToConvert)) {
|
||
window.location.hash = `#/tdx/${keyToConvert}`;
|
||
return;
|
||
} else if (bscOperator.isValidAddress(keyToConvert)) {
|
||
BSCAddress = keyToConvert;
|
||
} else {
|
||
if (/^[0-9a-fA-F]{64}$/.test(keyToConvert)) {
|
||
keyToConvert = coinjs.privkey2wif(keyToConvert);
|
||
}
|
||
const ethPrivateKey = coinjs.wif2privkey(keyToConvert).privkey;
|
||
BSCAddress = floEthereum.ethAddressFromPrivateKey(ethPrivateKey);
|
||
floAddress = keyToConvert.startsWith("R")
|
||
? floCrypto.getFloID(keyToConvert)
|
||
: btcOperator.bech32Address(keyToConvert);
|
||
}
|
||
}
|
||
|
||
if (!BSCAddress) return;
|
||
|
||
// Store the address for navigation context
|
||
window.lastViewedAddress = BSCAddress;
|
||
|
||
window.location.hash = `#/balance/${BSCAddress}`;
|
||
|
||
buttonLoader("check_balance_button", true);
|
||
Promise.all([
|
||
bscOperator.getBalance(BSCAddress),
|
||
bscOperator.getTokenBalance(BSCAddress, "usdc"),
|
||
bscOperator.getTokenBalance(BSCAddress, "usdt"),
|
||
])
|
||
.then(([BinanceBalance, usdcBalance, usdtBalance]) => {
|
||
compactIDB
|
||
.readData("contacts", floAddress || BSCAddress)
|
||
.then((result) => {
|
||
if (result) return;
|
||
compactIDB
|
||
.addData(
|
||
"contacts",
|
||
{
|
||
BSCAddress,
|
||
},
|
||
floAddress || BSCAddress
|
||
)
|
||
.then(() => {
|
||
renderSearchedAddressList();
|
||
})
|
||
.catch((error) => {
|
||
console.error(error);
|
||
});
|
||
});
|
||
renderElem(
|
||
getRef("bsc_balance_wrapper"),
|
||
html`
|
||
<div class="grid">
|
||
<div class="label">BSC address</div>
|
||
<sm-copy id="BSC_address" value="${BSCAddress}"></sm-copy>
|
||
</div>
|
||
${floAddress && floAddress !== BSCAddress
|
||
? html`
|
||
<div class="grid">
|
||
<div class="label">
|
||
${floAddress.startsWith("F") ? "FLO" : "BTC"} address
|
||
</div>
|
||
<sm-copy
|
||
id="flo_address"
|
||
value="${floAddress}"
|
||
></sm-copy>
|
||
</div>
|
||
`
|
||
: ""}
|
||
<div class="grid gap-1">
|
||
<h4>Balance</h4>
|
||
<ul
|
||
id="bsc_address_balance"
|
||
class="flex flex-direction-column gap-0-5"
|
||
>
|
||
<li class="flex align-center space-between">
|
||
<p>Binance</p>
|
||
<b
|
||
id="Binance_balance"
|
||
class="bsc-value"
|
||
data-bsc="${BinanceBalance}"
|
||
>${formatCurrency(BinanceBalance)}</b
|
||
>
|
||
</li>
|
||
<li class="flex align-center space-between">
|
||
<p>USDC</p>
|
||
<b
|
||
id="usdc_balance"
|
||
class="bsc-value"
|
||
data-bsc="${usdcBalance}"
|
||
>${usdcBalance} USDC</b
|
||
>
|
||
</li>
|
||
<li class="flex align-center space-between">
|
||
<p>USDT</p>
|
||
<b
|
||
id="usdt_balance"
|
||
class="bsc-value"
|
||
data-bsc="${usdtBalance}"
|
||
>${usdtBalance} USDT</b
|
||
>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
`
|
||
);
|
||
getRef("bsc_balance_wrapper").classList.remove("hidden");
|
||
getRef("bsc_balance_wrapper").animate(
|
||
[
|
||
{
|
||
transform: "translateY(-1rem)",
|
||
opacity: 0,
|
||
},
|
||
{
|
||
transform: "none",
|
||
opacity: 1,
|
||
},
|
||
],
|
||
{
|
||
easing: "ease",
|
||
duration: 300,
|
||
fill: "forwards",
|
||
}
|
||
);
|
||
// Show transaction history after balance is shown
|
||
render.transactions(BSCAddress);
|
||
})
|
||
.catch((error) => {
|
||
notify(error, "error");
|
||
})
|
||
.finally(() => {
|
||
buttonLoader("check_balance_button", false);
|
||
});
|
||
}
|
||
|
||
function handleInvalidSearch() {
|
||
if (document.startViewTransition) {
|
||
document.startViewTransition(() => {
|
||
getRef("bsc_balance_wrapper").classList.add("hidden");
|
||
getRef("address_transactions").classList.add("hidden");
|
||
getRef("check_balance_input").value = "";
|
||
});
|
||
} else {
|
||
getRef("bsc_balance_wrapper").classList.add("hidden");
|
||
getRef("address_transactions").classList.add("hidden");
|
||
getRef("check_balance_input").value = "";
|
||
}
|
||
}
|
||
async function deleteContact(floAddress) {
|
||
const confirmed = await getConfirmation("Delete contact", {
|
||
message: "Are you sure you want to delete this contact?",
|
||
});
|
||
if (!confirmed) return;
|
||
compactIDB
|
||
.removeData("contacts", floAddress)
|
||
.then(() => {
|
||
renderSearchedAddressList();
|
||
})
|
||
.catch((error) => {
|
||
console.error(error);
|
||
});
|
||
}
|
||
|
||
router.addRoute("send", (state) => {
|
||
getRef("page_container").dataset.page = "send";
|
||
renderElem(
|
||
getRef("page_container"),
|
||
html`
|
||
<sm-form id="send_tx_form" style="width: min(32rem, 100%)">
|
||
<fieldset class="flex flex-direction-column gap-0-5">
|
||
<div class="flex space-between align-center">
|
||
<div class="flex flex-direction-column gap-0-5">
|
||
<h4>Sender</h4>
|
||
<p>
|
||
Amount will be deducted from equivalent Binance address
|
||
</p>
|
||
</div>
|
||
<button
|
||
id="check_balance_button"
|
||
class="button button--small button--colored"
|
||
onclick="checkSenderBalance()"
|
||
disabled
|
||
>
|
||
Check balance
|
||
</button>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
<sm-input
|
||
id="private_key_input"
|
||
placeholder="Sender's FLO/BTC/BSC private key"
|
||
oninput=${handleSenderInput}
|
||
data-private-key
|
||
class="password-field"
|
||
type="password"
|
||
animate
|
||
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>
|
||
</fieldset>
|
||
<fieldset class="flex flex-direction-column gap-1">
|
||
<div class="flex flex-direction-column gap-0-5">
|
||
<h4>Receiver</h4>
|
||
<div class="grid gap-0-5">
|
||
<sm-input
|
||
class="receiver-address"
|
||
placeholder="Receiver's Binance address"
|
||
data-BSC-address
|
||
animate
|
||
required
|
||
></sm-input>
|
||
<div class="flex flex-direction-column gap-0-5">
|
||
<sm-input
|
||
class="receiver-amount amount-shown flex-1"
|
||
placeholder="Amount"
|
||
type="number"
|
||
step="0.000001"
|
||
min="0.000001"
|
||
error-text="Amount should be grater than 0.000001 Binance"
|
||
animate
|
||
required
|
||
>
|
||
<div class="asset-symbol flex" slot="icon">
|
||
<svg
|
||
hight="34"
|
||
width="34"
|
||
version="1.0"
|
||
id="katman_1"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||
x="0px"
|
||
y="0px"
|
||
viewBox="0 0 800 600"
|
||
style="enable-background:new 0 0 800 600;"
|
||
xml:space="preserve"
|
||
>
|
||
<style type="text/css">
|
||
.st0 {
|
||
fill-rule: evenodd;
|
||
clip-rule: evenodd;
|
||
fill: #f3ba2f;
|
||
}
|
||
.st1 {
|
||
fill-rule: evenodd;
|
||
clip-rule: evenodd;
|
||
fill: #131415;
|
||
}
|
||
</style>
|
||
<g id="Light">
|
||
<g
|
||
id="OneArt-_x2022_-Desktop-_x2022_-Light"
|
||
transform="translate(-457.000000, -1515.000000)"
|
||
>
|
||
<g
|
||
id="Block"
|
||
transform="translate(41.000000, 1263.000000)"
|
||
>
|
||
<g
|
||
id="TVL"
|
||
transform="translate(48.000000, 252.000000)"
|
||
>
|
||
<g
|
||
id="Icons_x2F_Icon-24_x2F_cake"
|
||
transform="translate(368.000000, 0.000000)"
|
||
>
|
||
<circle
|
||
id="Oval"
|
||
class="st0"
|
||
cx="399.8"
|
||
cy="299.6"
|
||
r="230.7"
|
||
/>
|
||
<g
|
||
id="Icons_x2F_icon-24_x2F_networks_x2F_binance_x5F_smart_x5F_chain"
|
||
transform="translate(3.333333, 3.333333)"
|
||
>
|
||
<path
|
||
id="Combined-Shape"
|
||
class="st1"
|
||
d="M456.3,320.8l34.8,34.7L396.5,450L302,355.5l34.8-34.7l59.7,59.7L456.3,320.8z
|
||
M396.5,261l35.3,35.3h0l0,0l-35.3,35.3l-35.2-35.2l0-0.1l0,0l6.2-6.2l3-3L396.5,261z M277.5,261.5l34.8,34.8L277.5,331
|
||
l-34.8-34.8L277.5,261.5z M515.5,261.5l34.8,34.8L515.5,331l-34.8-34.8L515.5,261.5z M396.5,142.5L491,237l-34.8,34.8
|
||
L396.5,212l-59.7,59.7L302,237L396.5,142.5z"
|
||
/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
</sm-input>
|
||
<sm-chips
|
||
id="asset_selector"
|
||
onchange=${handleAssetChange}
|
||
>
|
||
<sm-chip value="Binance" selected>Binance</sm-chip>
|
||
<sm-chip value="usdc">USDC</sm-chip>
|
||
<sm-chip value="usdt">USDT</sm-chip>
|
||
</sm-chips>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="multi-state-button">
|
||
<button
|
||
id="send_tx_button"
|
||
class="button button--primary"
|
||
type="submit"
|
||
disabled
|
||
onclick="sendTx()"
|
||
>
|
||
Send Binance
|
||
</button>
|
||
</div>
|
||
</fieldset>
|
||
</sm-form>
|
||
`
|
||
);
|
||
if (
|
||
window.ethereum &&
|
||
!(window.currentChainId && window.currentChainId === "0x38")
|
||
) {
|
||
renderError("Please switch MetaMask to Binance Mainnet");
|
||
}
|
||
});
|
||
|
||
|
||
|
||
|
||
// function togglePrivateKeyVisibility(input) {
|
||
// const target = input.closest("sm-input") ;
|
||
// target.type = target.type === "password" ? "text" : "password";
|
||
// target.focusIn();
|
||
// }
|
||
|
||
|
||
|
||
|
||
function togglePrivateKeyVisibility(input) {
|
||
console.log(input)
|
||
const target = input.closest("sm-input");
|
||
// const checkBalanceButton = document.querySelector("#check_balance_button");
|
||
console.log(target)
|
||
target.type = target.type === "password" ? "text" : "password";
|
||
// target.focus();
|
||
|
||
|
||
// checkBalanceButton.disabled = false;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
// const toggleButton = document.querySelector('#toggleButton');
|
||
// const privateKeyInput = document.querySelector('#private_key_input');
|
||
|
||
// toggleButton.addEventListener('click', () => {
|
||
// togglePrivateKeyVisibility(privateKeyInput);
|
||
// });
|
||
function checkSenderBalance() {
|
||
let address;
|
||
const privateKey = getRef("private_key_input").value.trim();
|
||
if (!privateKey)
|
||
return notify(`Please enter sender's private key to check balance`);
|
||
if (
|
||
privateKey.startsWith("R") ||
|
||
privateKey.startsWith("L") ||
|
||
privateKey.startsWith("K")
|
||
) {
|
||
address = floEthereum.ethAddressFromPrivateKey(
|
||
coinjs.wif2privkey(privateKey).privkey
|
||
);
|
||
} else {
|
||
address = floEthereum.ethAddressFromPrivateKey(privateKey);
|
||
}
|
||
getRef("sender_balance_container").classList.remove("hidden");
|
||
renderElem(
|
||
getRef("sender_balance_container"),
|
||
html` Loading balance...<sm-spinner></sm-spinner> `
|
||
);
|
||
const promises = [bscOperator.getBalance(address)];
|
||
const selectedAsset = getRef("asset_selector").value;
|
||
if (selectedAsset !== "Binance")
|
||
promises.push(bscOperator.getTokenBalance(address, selectedAsset));
|
||
Promise.all(promises)
|
||
.then(([ethBalance, tokenBalance]) => {
|
||
renderElem(
|
||
getRef("sender_balance_container"),
|
||
html`
|
||
<div
|
||
class="grid gap-1 w-100"
|
||
style="padding: 1rem; border-radius: 0.5rem; border: solid thin rgba(var(--text-color),0.3)"
|
||
>
|
||
<div class="grid">
|
||
<p class="label">Sender address</p>
|
||
<sm-copy value=${address}
|
||
><p>${address}</p>
|
||
<p></p
|
||
></sm-copy>
|
||
</div>
|
||
<p>
|
||
Balance:
|
||
<b class="amount-shown">${ethBalance} BSC</b>
|
||
${selectedAsset !== "Binance"
|
||
? html`|
|
||
<b class="amount-shown"
|
||
>${tokenBalance} ${selectedAsset.toUpperCase()}</b
|
||
>`
|
||
: ""}
|
||
</p>
|
||
</div>
|
||
`
|
||
);
|
||
})
|
||
.catch((err) => {
|
||
notify(err, "error");
|
||
});
|
||
}
|
||
function handleSenderInput(e) {
|
||
console.log(e.target.isValid)
|
||
getRef("check_balance_button").disabled = false;
|
||
if (!e.target.isValid) {
|
||
// getRef("sender_balance_container").classList.add("hidden");
|
||
}
|
||
}
|
||
function handleAssetChange(e) {
|
||
const asset = e.target.value;
|
||
const amountInput =
|
||
getRef("send_tx_form").querySelector(".receiver-amount");
|
||
amountInput.value = "";
|
||
amountInput.setAttribute(
|
||
"error-text",
|
||
`Amount should be grater than 0.000001 ${asset.toUpperCase()}`
|
||
);
|
||
document.querySelectorAll(".asset-symbol").forEach((elem) => {
|
||
elem.innerHTML = assetIcons[asset];
|
||
});
|
||
getRef("send_tx_button").textContent = `Send ${asset.toUpperCase()}`;
|
||
}
|
||
async function sendTx() {
|
||
const receiver = getRef("send_tx_form")
|
||
.querySelector(".receiver-address")
|
||
.value.trim();
|
||
const amount = getRef("send_tx_form")
|
||
.querySelector(".receiver-amount")
|
||
.value.trim();
|
||
const asset = getRef("asset_selector").value;
|
||
try {
|
||
const confirmation = await getConfirmation("Send transaction", {
|
||
message: `You are about to send ${amount} ${asset.toUpperCase()} to ${receiver}`,
|
||
confirmText: "Send",
|
||
});
|
||
buttonLoader("send_tx_button", true);
|
||
if (!confirmation) return;
|
||
let privateKey = getRef("private_key_input").value.trim();
|
||
if (/^[0-9a-fA-F]{64}$/.test(privateKey)) {
|
||
privateKey = coinjs.privkey2wif(privateKey);
|
||
}
|
||
privateKey = coinjs.wif2privkey(privateKey).privkey;
|
||
switch (asset) {
|
||
case "Binance": {
|
||
const tx = await bscOperator.sendTransaction({
|
||
privateKey,
|
||
receiver,
|
||
amount,
|
||
});
|
||
showTransactionResult("pending", { txHash: tx.hash });
|
||
await tx.wait();
|
||
showTransactionResult("confirmed", { txHash: tx.hash });
|
||
break;
|
||
}
|
||
case "usdc":
|
||
case "usdt": {
|
||
const tx = await bscOperator.sendToken({
|
||
privateKey,
|
||
receiver,
|
||
amount,
|
||
token: asset,
|
||
});
|
||
showTransactionResult("pending", { txHash: tx.hash });
|
||
await tx.wait();
|
||
showTransactionResult("confirmed", { txHash: tx.hash });
|
||
break;
|
||
}
|
||
}
|
||
getRef("send_tx_form").reset();
|
||
// getRef("sender_balance_container").classList.add("hidden");
|
||
} catch (e) {
|
||
console.error(e.message);
|
||
showTransactionResult("failed", {
|
||
description: `Insufficient ${asset.toUpperCase()} balance`,
|
||
});
|
||
const regex = /\(error=({.*?}),/;
|
||
const match = e.message.match(regex);
|
||
if (match && match[1]) {
|
||
const { code } = JSON.parse(match[1]);
|
||
if (code === -32000)
|
||
showTransactionResult("failed", {
|
||
description: `Insufficient ${asset.toUpperCase()} balance`,
|
||
});
|
||
else {
|
||
showTransactionResult("failed", { description: e.message });
|
||
}
|
||
}
|
||
} finally {
|
||
buttonLoader("send_tx_button", false);
|
||
}
|
||
}
|
||
// function showTransactionResult(status, { txHash, description = "" }) {
|
||
// switch (status) {
|
||
// case "pending":
|
||
// renderElem(
|
||
// getRef("transaction_result_popup__content"),
|
||
// html`
|
||
// <ul>
|
||
// <li class="transaction__phase">
|
||
// <svg
|
||
// class="icon confirmed"
|
||
// 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="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"
|
||
// />
|
||
// </svg>
|
||
// <h4>Transaction sent</h4>
|
||
// </li>
|
||
// <li class="transaction__phase">
|
||
// <sm-spinner></sm-spinner>
|
||
// <p>Waiting for transaction to be confirmed</p>
|
||
// </li>
|
||
// </ul>
|
||
// <div class="grid">
|
||
// <span class="label">Transaction ID</span>
|
||
// <sm-copy value=${txHash}></sm-copy>
|
||
// </div>
|
||
// <a
|
||
// class="button button--primary"
|
||
// target="_blank"
|
||
// href=${`https://bscscan.com/tx/${txHash}`}
|
||
// >Check transaction status</a
|
||
// >
|
||
// `
|
||
// );
|
||
// break;
|
||
// case "confirmed":
|
||
// renderElem(
|
||
// getRef("transaction_result_popup__content"),
|
||
// html`
|
||
// <svg
|
||
// class="icon user-action-result__icon confirmed"
|
||
// 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="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"
|
||
// />
|
||
// </svg>
|
||
// <div class="grid gap-0-5 justify-center text-center">
|
||
// <h4>Transaction confirmed</h4>
|
||
// <p>Transaction has been confirmed on the blockchain.</p>
|
||
// </div>
|
||
// <div class="grid">
|
||
// <span class="label">Transaction ID</span>
|
||
// <sm-copy value=${txHash}></sm-copy>
|
||
// </div>
|
||
// <a
|
||
// class="button button--primary"
|
||
// target="_blank"
|
||
// href=${`https://bscscan.com/tx/${txHash}`}
|
||
// >Check transaction status</a
|
||
// >
|
||
// `
|
||
// );
|
||
// break;
|
||
// case "failed":
|
||
// renderElem(
|
||
// getRef("transaction_result_popup__content"),
|
||
// html`
|
||
// <svg
|
||
// class="icon user-action-result__icon failed"
|
||
// xmlns="http://www.w3.org/2000/svg"
|
||
// height="24px"
|
||
// viewBox="0 0 24 24"
|
||
// width="24px"
|
||
// fill="#000000"
|
||
// >
|
||
// <path
|
||
// d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"
|
||
// />
|
||
// </svg>
|
||
// <div class="grid gap-0-5 justify-center text-center">
|
||
// <h4>Transaction failed</h4>
|
||
// <p>${description}</p>
|
||
// </div>
|
||
// `
|
||
// );
|
||
// break;
|
||
// }
|
||
// openPopup("transaction_result_popup");
|
||
// }
|
||
|
||
// *************************************************************************
|
||
|
||
// ***************************************************************************
|
||
|
||
// function showTransactionResult(status, { txHash, description = "Insufficient BSC balance" })
|
||
// { switch (status)
|
||
// { case "pending": renderElem( getRef("transaction_result_popup__content"),
|
||
// html` <ul> <li class="transaction__phase"> <svg class="icon confirmed" 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="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" /> </svg> <h4>Transaction sent</h4> </li> <li class="transaction__phase"> <sm-spinner></sm-spinner> <p>Waiting for transaction to be confirmed</p> </li> </ul>
|
||
// <div class="grid"> <span class="label">Transaction ID</span>
|
||
// <sm-copy value=${txHash}></sm-copy> </div>
|
||
// <a class="button button--primary" target="_blank" href=${`https://bscscan.com/tx/${txHash}`} >Check transaction status</a > ` );
|
||
// break;
|
||
// case "confirmed":
|
||
// renderElem( getRef("transaction_result_popup__content"),
|
||
// html` <svg class="icon user-action-result__icon confirmed" 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="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" /> </svg>
|
||
// <div class="grid gap-0-5 justify-center text-center">
|
||
// <h4>Transaction confirmed</h4>
|
||
// <p>Transaction has been confirmed on the blockchain.</p> </div>
|
||
// <div class="grid"> <span class="label">Transaction ID</span>
|
||
// <sm-copy value=${txHash}></sm-copy> </div>
|
||
// <a class="button button--primary" target="_blank" href=${`https://bscscan.com/tx/${txHash}`} >Check transaction status</a > ` );
|
||
// break;
|
||
// case "failed": renderElem( getRef("transaction_result_popup__content"),
|
||
// html` <svg class="icon user-action-result__icon failed" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" > <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" /> </svg> <div class="grid gap-0-5 justify-center text-center">
|
||
// <h4>Transaction failed</h4>
|
||
// <p>"Insufficient BSC balance"</p> </div> ` );
|
||
// break;
|
||
// } openPopup("transaction_result_popup"); }
|
||
|
||
// showTransactionResult("failed", { txHash: "your-tx-hash-here", description: "Insufficient BSC balance" });
|
||
function showTransactionResult(status, { txHash, usdtBalance, bscBalance }) {
|
||
let description = "Insufficient BSC balance";
|
||
|
||
if (usdtBalance <= 0) {
|
||
description = "Insufficient USDT balance";
|
||
} else if (bscBalance <= 0) {
|
||
description = "Insufficient BSC balance";
|
||
}
|
||
|
||
switch (status) {
|
||
case "pending":
|
||
renderElem(
|
||
getRef("transaction_result_popup__content"),
|
||
html`
|
||
<ul>
|
||
<li class="transaction__phase">
|
||
<svg class="icon confirmed" 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="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
|
||
</svg>
|
||
<h4>Transaction sent</h4>
|
||
</li>
|
||
<li class="transaction__phase">
|
||
<sm-spinner></sm-spinner>
|
||
<p>Waiting for transaction to be confirmed</p>
|
||
</li>
|
||
</ul>
|
||
<div class="grid">
|
||
<span class="label">Transaction ID</span>
|
||
<sm-copy value=${txHash}></sm-copy>
|
||
</div>
|
||
<a class="button button--primary" target="_blank" href=${`https://bscscan.com/tx/${txHash}`}>
|
||
Check transaction status
|
||
</a>
|
||
`
|
||
);
|
||
break;
|
||
case "confirmed":
|
||
renderElem(
|
||
getRef("transaction_result_popup__content"),
|
||
html`
|
||
<svg class="icon user-action-result__icon confirmed" 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="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
|
||
</svg>
|
||
<div class="grid gap-0-5 justify-center text-center">
|
||
<h4>Transaction confirmed</h4>
|
||
<p>Transaction has been confirmed on the blockchain.</p>
|
||
</div>
|
||
<div class="grid">
|
||
<span class="label">Transaction ID</span>
|
||
<sm-copy value=${txHash}></sm-copy>
|
||
</div>
|
||
<a class="button button--primary" target="_blank" href=${`https://bscscan.com/tx/${txHash}`}>
|
||
Check transaction status
|
||
</a>
|
||
`
|
||
);
|
||
break;
|
||
case "failed":
|
||
renderElem(
|
||
getRef("transaction_result_popup__content"),
|
||
html`
|
||
<svg class="icon user-action-result__icon failed" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
|
||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" />
|
||
</svg>
|
||
<div class="grid gap-0-5 justify-center text-center">
|
||
<h4>Transaction failed</h4>
|
||
<p>${description}</p>
|
||
</div>
|
||
`
|
||
);
|
||
break;
|
||
}
|
||
openPopup("transaction_result_popup");
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
router.addRoute("create", (state) => {
|
||
getRef("page_container").dataset.page = "create";
|
||
renderElem(
|
||
getRef("page_container"),
|
||
html`
|
||
<div class="grid gap-1">
|
||
<h2>Don't have a Binance address? Create one</h2>
|
||
<section class="create-buttons">
|
||
<button
|
||
class="button button--primary interactive gap-0-5 margin-right-auto"
|
||
onclick=${generateNewID}
|
||
>
|
||
<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" />
|
||
</g>
|
||
<g>
|
||
<g>
|
||
<path
|
||
d="M18.32,4.26C16.84,3.05,15.01,2.25,13,2.05v2.02c1.46,0.18,2.79,0.76,3.9,1.62L18.32,4.26z M19.93,11h2.02 c-0.2-2.01-1-3.84-2.21-5.32L18.31,7.1C19.17,8.21,19.75,9.54,19.93,11z M18.31,16.9l1.43,1.43c1.21-1.48,2.01-3.32,2.21-5.32 h-2.02C19.75,14.46,19.17,15.79,18.31,16.9z M13,19.93v2.02c2.01-0.2,3.84-1,5.32-2.21l-1.43-1.43 C15.79,19.17,14.46,19.75,13,19.93z M13,12V7h-2v5H7l5,5l5-5H13z M11,19.93v2.02c-5.05-0.5-9-4.76-9-9.95s3.95-9.45,9-9.95v2.02 C7.05,4.56,4,7.92,4,12S7.05,19.44,11,19.93z"
|
||
/>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
Generate BSC address
|
||
</button>
|
||
<button
|
||
class="button button--primary interactive gap-0-5 margin-right-auto"
|
||
onclick="openPopup('retrieve_btc_addr_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="M14 12c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2zm-2-9c-4.97 0-9 4.03-9 9H0l4 4 4-4H5c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.51 0-2.91-.49-4.06-1.3l-1.42 1.44C8.04 20.3 9.94 21 12 21c4.97 0 9-4.03 9-9s-4.03-9-9-9z"
|
||
/>
|
||
</svg>
|
||
Retrieve BSC address
|
||
</button>
|
||
</section>
|
||
</div>
|
||
<div id="created_address_wrapper" class="grid gap-1"></div>
|
||
`
|
||
);
|
||
});
|
||
function generateNewID() {
|
||
const { floID, privKey } = floCrypto.generateNewID();
|
||
const ethPrivateKey = coinjs.wif2privkey(privKey).privkey;
|
||
const BSCAddress = floEthereum.ethAddressFromPrivateKey(ethPrivateKey);
|
||
const btcPrivKey = btcOperator.convert.wif(privKey);
|
||
const btcAddr = btcOperator.bech32Address(btcPrivKey);
|
||
renderElem(
|
||
getRef("created_address_wrapper"),
|
||
html`
|
||
<ul id="generated_addresses" class="grid gap-1-5">
|
||
<li class="grid gap-0-5">
|
||
<div>
|
||
<h5>Binance Address</h5>
|
||
<sm-copy value="${BSCAddress}"></sm-copy>
|
||
</div>
|
||
<div>
|
||
<h5>Private Key</h5>
|
||
<sm-copy value="${ethPrivateKey}"></sm-copy>
|
||
</div>
|
||
</li>
|
||
<li class="grid gap-0-5">
|
||
<div>
|
||
<h5>FLO Address</h5>
|
||
<sm-copy value="${floID}"></sm-copy>
|
||
</div>
|
||
<div>
|
||
<h5>Private Key</h5>
|
||
<sm-copy value="${privKey}"></sm-copy>
|
||
</div>
|
||
</li>
|
||
<li class="grid gap-0-5">
|
||
<div>
|
||
<h5>Bitcoin Address</h5>
|
||
<sm-copy value="${btcAddr}"></sm-copy>
|
||
</div>
|
||
<div>
|
||
<h5>Bitcoin Private Key</h5>
|
||
<sm-copy value="${btcPrivKey}"></sm-copy>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
`
|
||
);
|
||
}
|
||
function retrieveBinanceAddr() {
|
||
function retrieve() {
|
||
let bscPrivateKey = getRef("retrieve_btc_addr_field").value.trim();
|
||
getRef("recovered_btc_addr_wrapper").classList.remove("hidden");
|
||
getRef("recovered_btc_addr").value =
|
||
bscPrivateKey.startsWith("R") ||
|
||
bscPrivateKey.startsWith("L") ||
|
||
bscPrivateKey.startsWith("K")
|
||
? floEthereum.ethAddressFromPrivateKey(
|
||
coinjs.wif2privkey(bscPrivateKey).privkey
|
||
)
|
||
: floEthereum.ethAddressFromPrivateKey(bscPrivateKey);
|
||
}
|
||
if (document.startViewTransition) {
|
||
document.startViewTransition(() => {
|
||
retrieve();
|
||
});
|
||
} else retrieve();
|
||
}
|
||
|
||
function testHistoricalAPIAndRenderToggle() {
|
||
const oneWeekAgo = Math.floor(Date.now() / 1000) - 7 * 24 * 60 * 60;
|
||
|
||
fetchHistoricalRate(oneWeekAgo)
|
||
.then((rates) => {
|
||
if (rates && (rates.usd > 0 || rates.inr > 0)) {
|
||
isHistoricApiAvailable = true;
|
||
renderValuationToggle();
|
||
} else {
|
||
isHistoricApiAvailable = false;
|
||
}
|
||
})
|
||
.catch(() => {
|
||
isHistoricApiAvailable = false;
|
||
});
|
||
}
|
||
|
||
function renderValuationToggle() {
|
||
if (isHistoricApiAvailable && currentCurrency !== "bsc") {
|
||
const container = document.querySelector(
|
||
".flex.flex-direction-column.gap-0-5.sticky.top-0"
|
||
);
|
||
if (container) {
|
||
const existingToggle = container.querySelector("sm-switch");
|
||
if (!existingToggle) {
|
||
const toggleDiv = document.createElement("div");
|
||
toggleDiv.className = "margin-left-auto";
|
||
toggleDiv.innerHTML = `
|
||
<sm-switch id="valuation_toggle" ${
|
||
showCurrentValue ? "checked" : ""
|
||
}>
|
||
<p slot="left" class="margin-right-0-5">
|
||
Show current value
|
||
</p>
|
||
</sm-switch>
|
||
`;
|
||
container.appendChild(toggleDiv);
|
||
|
||
document
|
||
.getElementById("valuation_toggle")
|
||
.addEventListener("change", (e) => {
|
||
handleValuationTypeChange(e.target.checked);
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
</script>
|
||
</body>
|
||
</html>
|
||
<!-- function showTransactionResult(status, { txHash, description = "Insufficient BSC balance" }) { switch (status) { case "pending": renderElem( getRef("transaction_result_popup__content"), html` <ul> <li class="transaction__phase"> <svg class="icon confirmed" 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="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" /> </svg> <h4>Transaction sent</h4> </li> <li class="transaction__phase"> <sm-spinner></sm-spinner> <p>Waiting for transaction to be confirmed</p> </li> </ul> <div class="grid"> <span class="label">Transaction ID</span> <sm-copy value=${txHash}></sm-copy> </div> <a class="button button--primary" target="_blank" href=${`https://bscscan.com/tx/${txHash}`} >Check transaction status</a > ` ); break; case "confirmed": renderElem( getRef("transaction_result_popup__content"), html` <svg class="icon user-action-result__icon confirmed" 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="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" /> </svg> <div class="grid gap-0-5 justify-center text-center"> <h4>Transaction confirmed</h4> <p>Transaction has been confirmed on the blockchain.</p> </div> <div class="grid"> <span class="label">Transaction ID</span> <sm-copy value=${txHash}></sm-copy> </div> <a class="button button--primary" target="_blank" href=${`https://bscscan.com/tx/${txHash}`} >Check transaction status</a > ` ); break; case "failed": renderElem( getRef("transaction_result_popup__content"), html` <svg class="icon user-action-result__icon failed" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" > <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" /> </svg> <div class="grid gap-0-5 justify-center text-center"> <h4>Transaction failed</h4> <p>"Insufficient BSC balance"</p> </div> ` ); break; } openPopup("transaction_result_popup"); } showTransactionResult("failed", { txHash: "your-tx-hash-here", description: "Insufficient BSC balance" }); --> |