2985 lines
185 KiB
HTML
2985 lines
185 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>RanchiMall Exchange</title>
|
|
<meta name="description" content="Trade FLO and FLO based RanchiMall tokens.">
|
|
<meta name="theme-color" content="#516beb" />
|
|
<script src="scripts/components.js" defer></script>
|
|
<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/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet">
|
|
<script src="https://unpkg.com/uhtml@3.0.1/es.js"></script>
|
|
<script src="scripts/floGlobals.js"></script>
|
|
<script src="scripts/lib.js"></script>
|
|
<script src="scripts/btcOperator.js"></script>
|
|
<script src="scripts/floCrypto.js"></script>
|
|
<script src="scripts/floBlockchainAPI.js"></script>
|
|
<script src="scripts/floTokenAPI.js"></script>
|
|
<script src="scripts/floExchangeAPI.js"></script>
|
|
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
|
|
<script>
|
|
const DEFAULT_TRADE_PRICE_DEVIATION_BUY = 15 / 100;
|
|
const DEFAULT_TRADE_PRICE_DEVIATION_SELL = 10 / 100;
|
|
const deviation = {
|
|
'buy': 1 + DEFAULT_TRADE_PRICE_DEVIATION_BUY,
|
|
'sell': 1 - DEFAULT_TRADE_PRICE_DEVIATION_SELL
|
|
}
|
|
</script>
|
|
</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"></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>
|
|
</div>
|
|
</sm-popup>
|
|
<sm-popup id="prompt_popup">
|
|
<h4 id="prompt_title"></h4>
|
|
<p id="prompt_message"></p>
|
|
<sm-form>
|
|
<sm-input id="prompt_input"></sm-input>
|
|
<div class="flex align-center gap-0-5 margin-left-auto">
|
|
<button class="button cancel-button">Cancel</button>
|
|
<button class="button confirm-button button--primary" type="submit">OK</button>
|
|
</div>
|
|
</sm-form>
|
|
</sm-popup>
|
|
<div id="loading" class="page flex align-center justify-center">
|
|
<div class="grid gap-2 text-center">
|
|
<svg class="icon rm-logo" 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">
|
|
</path>
|
|
</svg>
|
|
<div class="grid gap-0-5">
|
|
<h4>RanchiMall Exchange</h4>
|
|
<p>Getting everything ready</p>
|
|
</div>
|
|
<line-loader></line-loader>
|
|
<button class="button" onclick="floExchangeAPI.clearAllLocalData()">Reset</button>
|
|
</div>
|
|
</div>
|
|
<article id="home" class="page hidden">
|
|
<header id="main_header">
|
|
<div class="app-brand margin-right-auto">
|
|
<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">
|
|
Exchange
|
|
</h4>
|
|
</div>
|
|
</div>
|
|
<theme-toggle id="theme_toggle"></theme-toggle>
|
|
<button id="user_popup_button" class="hidden user-content button--small">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon margin-right-0-5" height="24px" viewBox="0 0 24 24"
|
|
width="24px" fill="#000000">
|
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
|
<path
|
|
d="M12 5.9c1.16 0 2.1.94 2.1 2.1s-.94 2.1-2.1 2.1S9.9 9.16 9.9 8s.94-2.1 2.1-2.1m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z" />
|
|
</svg>
|
|
<div class="my-flo-id"></div>
|
|
</button>
|
|
</header>
|
|
<section id="pages_container">
|
|
<div id="exchange" class="mobile-page hidden">
|
|
<div id="login_section">
|
|
<sm-form id="login_form">
|
|
<div class="grid gap-0-5">
|
|
<h2>Login</h2>
|
|
<p>Enter your FLO private key to login</p>
|
|
</div>
|
|
<sm-input type="password" id="login_form__priv_key" placeholder="Private key"
|
|
error-text="Invalid private key" data-private-key autofocus animate required>
|
|
</sm-input>
|
|
<!-- <sm-checkbox id="remember_me" checked>
|
|
<span class="margin-left-0-5">
|
|
Remember me
|
|
</span>
|
|
</sm-checkbox> -->
|
|
<div class="loader-button-wrapper">
|
|
<button id="login_button" class="button button--primary" onclick="UI_evt.login('login');"
|
|
type="submit">Log
|
|
in</button>
|
|
</div>
|
|
<p>
|
|
New here? <a href="#/exchange/generate">get your FLO login credentials</a>
|
|
</p>
|
|
<button class="button" onclick="floExchangeAPI.clearAllLocalData()">Reset</button>
|
|
</sm-form>
|
|
<section class="grid gap-1-5 hidden">
|
|
<a class="button margin-right-auto" href="#/exchange/login">
|
|
<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="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
|
|
</svg>
|
|
</a>
|
|
<div id="flo_id_warning" class="flex gap-1">
|
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
|
|
width="24px" fill="#000000">
|
|
<path d="M0 0h24v24H0z" fill="none" />
|
|
<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" />
|
|
</svg>
|
|
<div class="grid gap-0-5">
|
|
<strong>
|
|
<h3>
|
|
Keep your keys safe!
|
|
</h3>
|
|
</strong>
|
|
<p>Don't share with anyone. Once lost private key can't be
|
|
recovered.</p>
|
|
</div>
|
|
</div>
|
|
<div class="grid gap-1-5 generated-keys-wrapper">
|
|
<div class="grid gap-0-5">
|
|
<h5>FLO address</h5>
|
|
<sm-copy id="generated_flo_address"></sm-copy>
|
|
</div>
|
|
<div class="grid gap-0-5">
|
|
<h5>Private key</h5>
|
|
<sm-copy id="generated_private_key"></sm-copy>
|
|
</div>
|
|
</div>
|
|
<div class="loader-button-wrapper">
|
|
<button id="sign_in_with_credentials" onclick="signInWithCredentials()"
|
|
class="button button--primary w-100">Sign in with these
|
|
credentials</button>
|
|
</div>
|
|
<p class="margin-top-1">You can use these FLO credentials with other RanchiMall apps
|
|
too. </p>
|
|
</section>
|
|
</div>
|
|
<section id="exchange_wrapper" class="hidden user-content">
|
|
<div id="asset_list_wrapper" class="grid gap-1 align-start">
|
|
<div class="grid gap-0-3">
|
|
<div class="flex align-center space-between">
|
|
<h5>ASSETS</h5>
|
|
<h5>PRICE</h5>
|
|
</div>
|
|
<p>Countdown shows est. time until asset prices are updated</p>
|
|
</div>
|
|
<ul id="listed_assets" class="user-content hidden"></ul>
|
|
</div>
|
|
<div id="asset_page" class="hide-on-mobile">
|
|
<nav id="asset_page__header" class="hide-on-desktop">
|
|
<a href="#/exchange" class="button icon-only">
|
|
<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="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
|
|
</svg>
|
|
</a>
|
|
</nav>
|
|
<div id="asset_page_wrapper">
|
|
<div id="price_chart_container" class="flex flex-direction-column gap-0-5 hide-on-mobile">
|
|
<div id="chart_header" class="flex align-center space-between">
|
|
<H4 id="chart_asset"></H4>
|
|
<sm-chips id="price_duration_selector">
|
|
<sm-chip value="hour" title="1 Hour price interval" selected>Hour
|
|
</sm-chip>
|
|
<sm-chip value="day" title="1 Day price interval">Day</sm-chip>
|
|
</sm-chips>
|
|
</div>
|
|
<div id="price_chart_wrapper" class="flex" style="position: relative; flex: 1">
|
|
<div id="price_history_chart"></div>
|
|
</div>
|
|
</div>
|
|
<sm-form id="trade_form">
|
|
<div class="flex space-between align-center">
|
|
<h4 id="traded_asset">Trade FLO</h4>
|
|
<sm-chips id="trade_type_selector" class="tab">
|
|
<sm-chip value="buy" selected>Buy</sm-chip>
|
|
<sm-chip value="sell">Sell</sm-chip>
|
|
</sm-chips>
|
|
</div>
|
|
<div id="get_price" class="flex align-center space-between"></div>
|
|
<sm-input id="get_quantity" placeholder="Quantity" type="number" step="0.00000001"
|
|
required animate>
|
|
</sm-input>
|
|
</sm-input>
|
|
<sm-input id="get_total" placeholder="Locked amount (₹)" type="number" min="0.00000001"
|
|
step="0.00000001" required animate>
|
|
</sm-input>
|
|
<div class="grid gap-0-5">
|
|
<div id="quantity_selector" class="flex align-center quantity-selector">
|
|
<span id="quantity_type">Rupee</span>
|
|
<button class="button" value="0.25"
|
|
title="Buy FLO worth 25% of total rupee">25%</button>
|
|
<button class="button" value="0.5"
|
|
title="Buy FLO worth 50% of total rupee">50%</button>
|
|
<button class="button" value="0.75"
|
|
title="Buy FLO worth 75% of total rupee">75%</button>
|
|
<button class="button" value="1"
|
|
title="Buy FLO worth 100% of total rupee">100%</button>
|
|
</div>
|
|
<div class="flex align-center tip">
|
|
<svg class="icon margin-right-0-5" style="height: 1em; width: 1em;"
|
|
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">
|
|
<rect fill="none" height="24" width="24" y="0" />
|
|
<path
|
|
d="M7,20h4c0,1.1-0.9,2-2,2S7,21.1,7,20z M5,19h8v-2H5V19z M16.5,9.5c0,3.82-2.66,5.86-3.77,6.5H5.27 C4.16,15.36,1.5,13.32,1.5,9.5C1.5,5.36,4.86,2,9,2S16.5,5.36,16.5,9.5z M14.5,9.5C14.5,6.47,12.03,4,9,4S3.5,6.47,3.5,9.5 c0,2.47,1.49,3.89,2.35,4.5h6.3C13.01,13.39,14.5,11.97,14.5,9.5z M21.37,7.37L20,8l1.37,0.63L22,10l0.63-1.37L24,8l-1.37-0.63L22,6 L21.37,7.37z M19,6l0.94-2.06L22,3l-2.06-0.94L19,0l-0.94,2.06L16,3l2.06,0.94L19,6z" />
|
|
</svg>
|
|
<p id="quantity_selector_tip" style="font-size: inherit;">
|
|
Buy FLO worth of the above percentage rupee
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="loader-button-wrapper">
|
|
<button id="trade_button" class="uppercase w-100 button button--primary"
|
|
type="submit">BUY FLO </button>
|
|
</div>
|
|
</sm-form>
|
|
</div>
|
|
<nav id="asset_page__footer" class="flex w-100 hide-on-desktop">
|
|
<button class="flex-1 active" value="trade">
|
|
<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>
|
|
<path
|
|
d="M12.22,19.85c-0.18,0.18-0.5,0.21-0.71,0c-0.18-0.18-0.21-0.5,0-0.71l3.39-3.39l-1.41-1.41l-3.39,3.39 c-0.19,0.2-0.51,0.19-0.71,0c-0.21-0.21-0.18-0.53,0-0.71l3.39-3.39l-1.41-1.41l-3.39,3.39c-0.18,0.18-0.5,0.21-0.71,0 c-0.19-0.19-0.19-0.51,0-0.71l3.39-3.39L9.24,10.1l-3.39,3.39c-0.18,0.18-0.5,0.21-0.71,0c-0.19-0.2-0.19-0.51,0-0.71L9.52,8.4 l1.87,1.86c0.95,0.95,2.59,0.94,3.54,0c0.98-0.98,0.98-2.56,0-3.54l-1.86-1.86l0.28-0.28c0.78-0.78,2.05-0.78,2.83,0l4.24,4.24 c0.78,0.78,0.78,2.05,0,2.83L12.22,19.85z M21.83,13.07c1.56-1.56,1.56-4.09,0-5.66l-4.24-4.24c-1.56-1.56-4.09-1.56-5.66,0 l-0.28,0.28l-0.28-0.28c-1.56-1.56-4.09-1.56-5.66,0L2.17,6.71c-1.42,1.42-1.55,3.63-0.4,5.19l1.45-1.45 C2.83,9.7,2.96,8.75,3.59,8.12l3.54-3.54c0.78-0.78,2.05-0.78,2.83,0l3.56,3.56c0.18,0.18,0.21,0.5,0,0.71 c-0.21,0.21-0.53,0.18-0.71,0L9.52,5.57l-5.8,5.79c-0.98,0.97-0.98,2.56,0,3.54c0.39,0.39,0.89,0.63,1.42,0.7 c0.07,0.52,0.3,1.02,0.7,1.42c0.4,0.4,0.9,0.63,1.42,0.7c0.07,0.52,0.3,1.02,0.7,1.42c0.4,0.4,0.9,0.63,1.42,0.7 c0.07,0.54,0.31,1.03,0.7,1.42c0.47,0.47,1.1,0.73,1.77,0.73c0.67,0,1.3-0.26,1.77-0.73L21.83,13.07z" />
|
|
</g>
|
|
</svg>
|
|
Trade
|
|
</button>
|
|
<button class="flex-1" value="'chart">
|
|
<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="M21,8c-1.45,0-2.26,1.44-1.93,2.51l-3.55,3.56c-0.3-0.09-0.74-0.09-1.04,0l-2.55-2.55C12.27,10.45,11.46,9,10,9 c-1.45,0-2.27,1.44-1.93,2.52l-4.56,4.55C2.44,15.74,1,16.55,1,18c0,1.1,0.9,2,2,2c1.45,0,2.26-1.44,1.93-2.51l4.55-4.56 c0.3,0.09,0.74,0.09,1.04,0l2.55,2.55C12.73,16.55,13.54,18,15,18c1.45,0,2.27-1.44,1.93-2.52l3.56-3.55 C21.56,12.26,23,11.45,23,10C23,8.9,22.1,8,21,8z" />
|
|
<polygon
|
|
points="15,9 15.94,6.93 18,6 15.94,5.07 15,3 14.08,5.07 12,6 14.08,6.93" />
|
|
<polygon points="3.5,11 4,9 6,8.5 4,8 3.5,6 3,8 1,8.5 3,9" />
|
|
</g>
|
|
</g>
|
|
</svg>
|
|
Chart
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
<div id="my_orders" class="grid gap-1 hidden mobile-page">
|
|
<div id="my_orders_section__header" class="orders_section__header flex">
|
|
<div id="orders_section__header--primary" class="flex w-100 align-center space-between">
|
|
<h4 id="my_orders__title">
|
|
My orders
|
|
</h4>
|
|
<sm-chips id="my_orders_category_selector" class="tab">
|
|
<sm-chip value="open" selected>Live</sm-chip>
|
|
<sm-chip value="completed">Trades</sm-chip>
|
|
</sm-chips>
|
|
</div>
|
|
<div id="orders_section__header--secondary" class="flex w-100 align-center space-between hidden">
|
|
<button class="" onclick="clearSelection()" title="Clear all selection">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon margin-right-0-5" height="24px"
|
|
viewBox="0 0 24 24" width="24px">
|
|
<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>
|
|
<span id="selected_orders"></span>
|
|
</button>
|
|
<div class="options">
|
|
<button class="button" title="Cancel selected orders" onclick="cancelAll()">
|
|
Cancel all
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<ul id="orders_list" class="observe-empty-state"></ul>
|
|
<div class="grid gap-0-5 empty-state">
|
|
<svg class="icon empty-state__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="M13 3c-4.97 0-9 4.03-9 9H1l3.89 3.89.07.14L9 12H6c0-3.87 3.13-7 7-7s7 3.13 7 7-3.13 7-7 7c-1.93 0-3.68-.79-4.94-2.06l-1.42 1.42C8.27 19.99 10.51 21 13 21c4.97 0 9-4.03 9-9s-4.03-9-9-9zm-1 5v5l4.25 2.52.77-1.28-3.52-2.09V8z" />
|
|
</svg>
|
|
<h4>
|
|
No live orders
|
|
</h4>
|
|
<p>My orders will only show completed orders</p>
|
|
</div>
|
|
</div>
|
|
<div id="market" class="grid gap-1-5 hidden mobile-page">
|
|
<div class="grid gap-1 align-start">
|
|
<div class="flex align-center space-between">
|
|
<h5>ASSETS</h5>
|
|
<h5>PRICE</h5>
|
|
</div>
|
|
<ul id="market_asset_rates"></ul>
|
|
</div>
|
|
<div class="grid gap-0-5 align-start">
|
|
<div class="orders_section__header flex align-center">
|
|
<h4 id="market_orders__title" class="flex align-center">
|
|
Market-wide trades
|
|
</h4>
|
|
<button class="icon-only" onclick="refresh();" title="Refresh">
|
|
<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="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" />
|
|
</svg>
|
|
</button>
|
|
<sm-chips id="market_orders_category_selector" class="tab justify-right">
|
|
<sm-chip value="open" selected>Live</sm-chip>
|
|
<sm-chip value="completed">Trades</sm-chip>
|
|
</sm-chips>
|
|
</div>
|
|
<div id="market_orders_wrapper">
|
|
<div class="grid gap-1">
|
|
<p>Order placement denotes priority at which orders will get executed.</p>
|
|
<div class="flex text-center">
|
|
<b class="flex-1" style="color: var(--green);">Buy</b>
|
|
<b class="flex-1" style="color: var(--danger-color);">Sell</b>
|
|
</div>
|
|
<div id="market_open_orders_wrapper" class="flex gap-0-5">
|
|
<div class="flex-1">
|
|
<ul id="market_buy_orders" class="observe-empty-state"></ul>
|
|
<p class="empty-state">
|
|
No buy orders placed
|
|
</p>
|
|
</div>
|
|
<div class="flex-1">
|
|
<ul id="market_sell_orders" class="observe-empty-state"></ul>
|
|
<p class="empty-state">
|
|
No sell orders placed
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="hidden">
|
|
<ul id="market_trades" class="observe-empty-state"></ul>
|
|
<p class="empty-state">
|
|
No trades happened
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="portfolio" class="mobile-page hidden">
|
|
<div class="grid gap-1-5">
|
|
<div class="flex align-center space-between">
|
|
<sm-chips id="portfolio_pages_selector">
|
|
<sm-chip value="exchange" selected>Exchange</sm-chip>
|
|
<sm-chip value="personal">On-chain</sm-chip>
|
|
</sm-chips>
|
|
<div class="loader-button-wrapper">
|
|
<button id="portfolio_balance_refresh_button" class="button button--small"
|
|
onclick="refresh(false,this);" title="Refresh">
|
|
Refresh
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="portfolio_pages_wrapper">
|
|
<div class="grid gap-1-5">
|
|
<h4>
|
|
My portfolio
|
|
</h4>
|
|
<div class="grid">
|
|
<span class="label">Portfolio valuation</span>
|
|
<h1 id="portfolio_value" style="font-size: 2.5rem;"></h1>
|
|
</div>
|
|
<div class="grid gap-0-5">
|
|
<div class="flex align-center space-between">
|
|
<h4>Asset balance</h4>
|
|
<div class="flex align-center gap-0-5">
|
|
<a href="#/history" class="button">See history</a>
|
|
<div class="tooltip">
|
|
<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="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.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" />
|
|
</svg>
|
|
<p class="banner">
|
|
Your assets on exchange remains separate until they are withdrawn to
|
|
your
|
|
FLO
|
|
ID.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p>Deposit rupee from your FLO ID to exchange to purchase assets.</p>
|
|
<div class="grid gap-1">
|
|
<div id="exchange_rupee_balance"></div>
|
|
<div class="grid">
|
|
<div class="flex align-center space-between">
|
|
<span class="label">Tradable assets</span>
|
|
<span class="label">Quantity | value</span>
|
|
</div>
|
|
<fieldset id="my_assets" class="grid"></fieldset>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="grid gap-1 hidden">
|
|
<div class="flex align-center space-between">
|
|
<h4>Assets in <span class="my-flo-id"></span></h4>
|
|
<div class="tooltip">
|
|
<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="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.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" />
|
|
</svg>
|
|
<p class="banner">
|
|
You need to have rupee tokens in your FLO ID to purchase assets, which then can
|
|
be
|
|
deposited
|
|
to your exchange account.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div id="portfolio_cards_wrapper">
|
|
<div class="grid gap-0-5 personal-asset-balance">
|
|
<div class="flex align-center">
|
|
<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="M13.66,7C13.1,5.82,11.9,5,10.5,5L6,5V3h12v2l-3.26,0c0.48,0.58,0.84,1.26,1.05,2L18,7v2l-2.02,0c-0.25,2.8-2.61,5-5.48,5 H9.77l6.73,7h-2.77L7,14v-2h3.5c1.76,0,3.22-1.3,3.46-3L6,9V7L13.66,7z" />
|
|
</g>
|
|
</g>
|
|
</svg>
|
|
<h5>Rupee token</h5>
|
|
</div>
|
|
<h1 class="h1" id="personal_rupee_balance"></h1>
|
|
<a href="https://ranchimall.github.io/flopay/#/home" target="_blank"
|
|
class="button interact">
|
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px"
|
|
viewBox="0 0 24 24" width="24px" fill="#000000">
|
|
<path d="M0 0h24v24H0z" fill="none"></path>
|
|
<path
|
|
d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z">
|
|
</path>
|
|
</svg>
|
|
Top-up
|
|
</a>
|
|
</div>
|
|
<div class="grid gap-0-5 personal-asset-balance">
|
|
<div class="flex align-center">
|
|
<svg class="icon flo-icon" width="64" height="64" viewBox="0 0 64 64"
|
|
fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<mask id="path-1-outside-1_16_6" maskUnits="userSpaceOnUse" x="3" y="3"
|
|
width="58" height="58">
|
|
<rect fill="white" x="3" y="3" width="58" height="58" />
|
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
|
d="M31.946 43.6019C28.8545 47.1308 28.7789 51.9719 31.9811 55.1957C35.0078 52.2797 35.0105 46.8446 31.946 43.6019ZM31.9487 10.6835C24.6452 19.1291 24.9206 29.1137 31.9433 37.4108C39.0929 28.9436 39.1118 19.0076 31.9487 10.6808V10.6835ZM37.1111 35.051C43.1861 28.841 50.5976 27.4208 59 28.7654C56.2919 34.9754 52.4714 39.9353 45.7214 42.53C47.2118 41.3609 48.5699 40.4051 49.7984 39.3089C51.8504 37.481 53.303 35.267 54.0293 32.6075C54.2588 31.7678 53.9618 31.5302 53.1896 31.406C50.3627 30.9524 47.7086 31.5464 45.1733 32.702C40.9073 34.646 37.4324 37.6403 34.1735 40.8938C34.1168 40.9532 34.0925 41.0396 33.9899 41.2259C40.3754 41.4689 45.2381 44.0177 48.119 49.9064C44.3768 50.738 40.9532 50.5625 37.778 48.5213C40.1702 49.2557 42.557 49.7903 45.176 48.5483C44.852 48.0083 44.663 47.3765 44.231 47.0228C43.0511 46.0589 41.8415 45.0842 40.505 44.3498C38.4395 43.2212 36.131 42.692 33.5768 42.1682C37.0247 48.2081 36.1607 53.6918 31.946 59C29.9858 56.5295 28.6142 53.9456 28.2389 50.8946C27.8609 47.8328 28.7222 45.0518 30.1991 42.3896C26.9321 41.8658 19.8824 45.6485 18.7916 48.5645C21.3215 49.8443 23.7893 49.2611 26.33 48.3161C24.5048 50.1953 19.2425 50.9648 15.8108 49.8335C18.6593 44.0285 23.4977 41.4716 30.0263 41.2286C29.5457 40.7426 29.1893 40.3673 28.8167 40.0082C26.1005 37.3892 23.2142 34.9862 19.823 33.2366C17.0501 31.8056 14.1422 30.9092 10.9481 31.3871C10.5701 31.4438 10.1948 31.5356 9.6656 31.6436C10.6376 36.6386 13.9856 39.7355 18.1274 42.3464C13.775 41.8199 6.431 34.214 5 28.7546C13.397 27.3938 20.849 28.8842 26.897 35.2319C21.8939 24.089 24.5561 14.2259 31.946 5C39.2765 14.153 41.9657 23.9459 37.1111 35.051" />
|
|
</mask>
|
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
|
d="M31.946 43.6019C28.8545 47.1308 28.7789 51.9719 31.9811 55.1957C35.0078 52.2797 35.0105 46.8446 31.946 43.6019ZM31.9487 10.6835C24.6452 19.1291 24.9206 29.1137 31.9433 37.4108C39.0929 28.9436 39.1118 19.0076 31.9487 10.6808V10.6835ZM37.1111 35.051C43.1861 28.841 50.5976 27.4208 59 28.7654C56.2919 34.9754 52.4714 39.9353 45.7214 42.53C47.2118 41.3609 48.5699 40.4051 49.7984 39.3089C51.8504 37.481 53.303 35.267 54.0293 32.6075C54.2588 31.7678 53.9618 31.5302 53.1896 31.406C50.3627 30.9524 47.7086 31.5464 45.1733 32.702C40.9073 34.646 37.4324 37.6403 34.1735 40.8938C34.1168 40.9532 34.0925 41.0396 33.9899 41.2259C40.3754 41.4689 45.2381 44.0177 48.119 49.9064C44.3768 50.738 40.9532 50.5625 37.778 48.5213C40.1702 49.2557 42.557 49.7903 45.176 48.5483C44.852 48.0083 44.663 47.3765 44.231 47.0228C43.0511 46.0589 41.8415 45.0842 40.505 44.3498C38.4395 43.2212 36.131 42.692 33.5768 42.1682C37.0247 48.2081 36.1607 53.6918 31.946 59C29.9858 56.5295 28.6142 53.9456 28.2389 50.8946C27.8609 47.8328 28.7222 45.0518 30.1991 42.3896C26.9321 41.8658 19.8824 45.6485 18.7916 48.5645C21.3215 49.8443 23.7893 49.2611 26.33 48.3161C24.5048 50.1953 19.2425 50.9648 15.8108 49.8335C18.6593 44.0285 23.4977 41.4716 30.0263 41.2286C29.5457 40.7426 29.1893 40.3673 28.8167 40.0082C26.1005 37.3892 23.2142 34.9862 19.823 33.2366C17.0501 31.8056 14.1422 30.9092 10.9481 31.3871C10.5701 31.4438 10.1948 31.5356 9.6656 31.6436C10.6376 36.6386 13.9856 39.7355 18.1274 42.3464C13.775 41.8199 6.431 34.214 5 28.7546C13.397 27.3938 20.849 28.8842 26.897 35.2319C21.8939 24.089 24.5561 14.2259 31.946 5C39.2765 14.153 41.9657 23.9459 37.1111 35.051" />
|
|
<path
|
|
d="M31.946 43.6019L32.6728 42.915L31.918 42.1163L31.1938 42.943L31.946 43.6019ZM31.9811 55.1957L31.2716 55.9004L31.9657 56.5992L32.6749 55.9159L31.9811 55.1957ZM31.9487 10.6835L32.7051 11.3376L32.9487 11.0559V10.6835H31.9487ZM31.9433 37.4108L31.18 38.0569L31.9442 38.9597L32.7074 38.056L31.9433 37.4108ZM31.9487 10.6808L32.7068 10.0287L30.9487 7.98495V10.6808H31.9487ZM59 28.7654L59.9166 29.1651L60.4326 27.9819L59.158 27.778L59 28.7654ZM45.7214 42.53L45.1042 41.7432L46.0802 43.4634L45.7214 42.53ZM49.7984 39.3089L49.1332 38.5622L49.1326 38.5628L49.7984 39.3089ZM54.0293 32.6075L53.0647 32.3439L53.0646 32.3441L54.0293 32.6075ZM53.1896 31.406L53.3484 30.4187L53.348 30.4186L53.1896 31.406ZM45.1733 32.702L45.588 33.612L45.5881 33.6119L45.1733 32.702ZM34.1735 40.8938L33.467 40.1861L33.4585 40.1946L33.4501 40.2033L34.1735 40.8938ZM33.9899 41.2259L33.114 40.7435L32.3319 42.1635L33.9519 42.2252L33.9899 41.2259ZM48.119 49.9064L48.3359 50.8826L49.5751 50.6072L49.0173 49.4669L48.119 49.9064ZM37.778 48.5213L38.0715 47.5653L37.2372 49.3625L37.778 48.5213ZM45.176 48.5483L45.6045 49.4518L46.6008 48.9794L46.0335 48.0338L45.176 48.5483ZM44.231 47.0228L44.8645 46.2491L44.8637 46.2484L44.231 47.0228ZM40.505 44.3498L40.9866 43.4734L40.9845 43.4723L40.505 44.3498ZM33.5768 42.1682L33.7777 41.1886L31.6127 40.7446L32.7083 42.664L33.5768 42.1682ZM31.946 59L31.1626 59.6216L31.9457 60.6085L32.7292 59.6218L31.946 59ZM28.2389 50.8946L29.2314 50.7725L29.2314 50.7721L28.2389 50.8946ZM30.1991 42.3896L31.0736 42.8747L31.7652 41.6279L30.3574 41.4022L30.1991 42.3896ZM18.7916 48.5645L17.855 48.2141L17.5413 49.0527L18.3402 49.4568L18.7916 48.5645ZM26.33 48.3161L27.0473 49.0128L25.9814 47.3788L26.33 48.3161ZM15.8108 49.8335L14.9131 49.393L14.4073 50.4237L15.4977 50.7832L15.8108 49.8335ZM30.0263 41.2286L30.0635 42.2279L32.3372 42.1433L30.7373 40.5255L30.0263 41.2286ZM28.8167 40.0082L28.1226 40.7281L28.1228 40.7282L28.8167 40.0082ZM19.823 33.2366L19.3644 34.1252L19.3645 34.1253L19.823 33.2366ZM10.9481 31.3871L10.8001 30.3981L10.7998 30.3982L10.9481 31.3871ZM9.6656 31.6436L9.46564 30.6638L8.49474 30.8619L8.68401 31.8346L9.6656 31.6436ZM18.1274 42.3464L18.0073 43.3392L18.6607 41.5005L18.1274 42.3464ZM5 28.7546L4.84003 27.7675L3.75363 27.9435L4.03268 29.0082L5 28.7546ZM26.897 35.2319L26.173 35.9217L27.8093 34.8223L26.897 35.2319ZM31.946 5L32.7265 4.37488L31.9461 3.40036L31.1655 4.37483L31.946 5ZM31.1938 42.943C27.7974 46.8199 27.6566 52.261 31.2716 55.9004L32.6906 54.491C29.9012 51.6828 29.9116 47.4417 32.6982 44.2609L31.1938 42.943ZM32.6749 55.9159C36.1304 52.5867 36.081 46.5214 32.6728 42.915L31.2192 44.2888C33.94 47.1678 33.8852 51.9727 31.2873 54.4755L32.6749 55.9159ZM31.1923 10.0294C27.4048 14.4092 25.5379 19.2455 25.5737 24.1102C25.6095 28.971 27.5438 33.7608 31.18 38.0569L32.7066 36.7647C29.3201 32.7637 27.6054 28.4127 27.5736 24.0955C27.5419 19.7822 29.1891 15.4034 32.7051 11.3376L31.1923 10.0294ZM32.7074 38.056C36.4083 33.6729 38.31 28.8504 38.3133 23.9938C38.3165 19.1359 36.42 14.3451 32.7068 10.0287L31.1906 11.3329C34.6405 15.3433 36.3161 19.6839 36.3133 23.9925C36.3104 28.3024 34.6279 32.6815 31.1792 36.7656L32.7074 38.056ZM30.9487 10.6808V10.6835H32.9487V10.6808H30.9487ZM37.8259 35.7503C43.6131 29.8345 50.6632 28.444 58.842 29.7528L59.158 27.778C50.532 26.3976 42.7591 27.8475 36.3963 34.3517L37.8259 35.7503ZM58.0834 28.3657C55.4402 34.4268 51.7791 39.1301 45.3626 41.5966L46.0802 43.4634C53.1637 40.7405 57.1436 35.524 59.9166 29.1651L58.0834 28.3657ZM46.3386 43.3168C47.7733 42.1914 49.2057 41.178 50.4642 40.055L49.1326 38.5628C47.9341 39.6322 46.6503 40.5304 45.1042 41.7432L46.3386 43.3168ZM50.4636 40.0556C52.6466 38.111 54.2121 35.7338 54.994 32.871L53.0646 32.3441C52.3939 34.8002 51.0542 36.851 49.1332 38.5622L50.4636 40.0556ZM54.9939 32.8711C55.1262 32.3872 55.2248 31.6946 54.7698 31.1186C54.3643 30.6053 53.7257 30.4794 53.3484 30.4187L53.0308 32.3933C53.1086 32.4058 53.17 32.4181 53.2184 32.43C53.2671 32.442 53.2955 32.4518 53.3091 32.4572C53.3227 32.4626 53.3139 32.4606 53.2926 32.4461C53.2699 32.4307 53.2353 32.4025 53.2004 32.3583C53.1832 32.3366 53.1677 32.3132 53.1543 32.2886C53.1409 32.264 53.1307 32.2402 53.1231 32.2182C53.1079 32.1742 53.1053 32.144 53.1049 32.1366C53.1046 32.1302 53.1059 32.1427 53.1007 32.1797C53.0955 32.2165 53.0849 32.27 53.0647 32.3439L54.9939 32.8711ZM53.348 30.4186C50.274 29.9254 47.4176 30.5801 44.7585 31.7921L45.5881 33.6119C47.9996 32.5127 50.4514 31.9794 53.0312 32.3934L53.348 30.4186ZM44.7586 31.792C40.3293 33.8104 36.7535 36.9051 33.467 40.1861L34.88 41.6015C38.1113 38.3755 41.4853 35.4816 45.588 33.612L44.7586 31.792ZM33.4501 40.2033C33.3063 40.354 33.2291 40.5156 33.2033 40.5674C33.1672 40.64 33.1517 40.6749 33.114 40.7435L34.8658 41.7083C34.9307 41.5906 34.9786 41.4891 34.9945 41.4572C35.0049 41.4363 35.0004 41.4467 34.9882 41.4667C34.9731 41.4914 34.9435 41.5355 34.8969 41.5843L33.4501 40.2033ZM33.9519 42.2252C37.0233 42.3421 39.6629 43.0109 41.8597 44.3098C44.0485 45.6039 45.857 47.5582 47.2207 50.3459L49.0173 49.4669C47.5001 46.3659 45.4368 44.1014 42.8776 42.5882C40.3265 41.0798 37.342 40.3527 34.0279 40.2266L33.9519 42.2252ZM47.9021 48.9302C44.3241 49.7253 41.1983 49.5313 38.3188 47.6801L37.2372 49.3625C40.7081 51.5937 44.4295 51.7507 48.3359 50.8826L47.9021 48.9302ZM37.4845 49.4773C39.9089 50.2215 42.6205 50.8669 45.6045 49.4518L44.7475 47.6448C42.4935 48.7137 40.4315 48.2899 38.0715 47.5653L37.4845 49.4773ZM46.0335 48.0338C45.8849 47.7862 45.8212 47.6158 45.6292 47.2504C45.4736 46.9544 45.2423 46.5584 44.8645 46.2491L43.5975 47.7965C43.6517 47.8409 43.7309 47.9377 43.8588 48.181C43.9503 48.3549 44.1431 48.7704 44.3185 49.0628L46.0335 48.0338ZM44.8637 46.2484C43.6913 45.2906 42.4149 44.2582 40.9866 43.4734L40.0234 45.2262C41.2681 45.9102 42.4109 46.8272 43.5983 47.7972L44.8637 46.2484ZM40.9845 43.4723C38.7711 42.2628 36.3236 41.7107 33.7777 41.1886L33.3759 43.1478C35.9384 43.6733 38.1079 44.1796 40.0255 45.2273L40.9845 43.4723ZM32.7083 42.664C34.346 45.5328 34.9255 48.203 34.6411 50.7474C34.3555 53.3015 33.1882 55.8274 31.1628 58.3782L32.7292 59.6218C34.9185 56.8644 36.2905 53.9944 36.6287 50.9696C36.9679 47.9351 36.2555 44.8435 34.4453 41.6724L32.7083 42.664ZM32.7294 58.3784C30.8397 55.9968 29.577 53.5818 29.2314 50.7725L27.2464 51.0167C27.6514 54.3094 29.1319 57.0622 31.1626 59.6216L32.7294 58.3784ZM29.2314 50.7721C28.8877 47.9881 29.6602 45.4224 31.0736 42.8747L29.3247 41.9045C27.7842 44.6812 26.8341 47.6775 27.2464 51.0171L29.2314 50.7721ZM30.3574 41.4022C29.3303 41.2375 28.1125 41.4187 26.9159 41.7622C25.6987 42.1117 24.4105 42.6564 23.2045 43.307C21.999 43.9574 20.8488 44.728 19.9166 45.543C19.005 46.3399 18.2137 47.2553 17.855 48.2141L19.7282 48.9149C19.9149 48.4157 20.4139 47.7647 21.233 47.0487C22.0314 46.3507 23.0524 45.6616 24.1541 45.0672C25.2552 44.4732 26.4102 43.9882 27.4678 43.6846C28.5458 43.3751 29.4344 43.2798 30.0408 43.377L30.3574 41.4022ZM18.3402 49.4568C21.2702 50.939 24.0958 50.214 26.6786 49.2534L25.9814 47.3788C23.4828 48.3082 21.3728 48.7496 19.243 47.6722L18.3402 49.4568ZM25.6127 47.6194C24.9334 48.3187 23.4333 48.9382 21.5294 49.2141C19.6667 49.484 17.6562 49.3889 16.1239 48.8838L15.4977 50.7832C17.3971 51.4094 19.7336 51.4952 21.8162 51.1934C23.8578 50.8976 25.9014 50.1927 27.0473 49.0128L25.6127 47.6194ZM16.7085 50.274C18.0554 47.5292 19.8486 45.5942 22.044 44.3081C24.2482 43.0169 26.918 42.345 30.0635 42.2279L29.9891 40.2293C26.606 40.3552 23.5923 41.0832 21.0331 42.5824C18.4649 44.0869 16.4147 46.3328 14.9131 49.393L16.7085 50.274ZM30.7373 40.5255C30.2757 40.0586 29.8942 39.6578 29.5106 39.2882L28.1228 40.7282C28.4844 41.0768 28.8157 41.4266 29.3153 41.9317L30.7373 40.5255ZM29.5108 39.2883C26.7637 36.6396 23.7967 34.1615 20.2815 32.3479L19.3645 34.1253C22.6317 35.8109 25.4373 38.1388 28.1226 40.7281L29.5108 39.2883ZM20.2816 32.348C17.4111 30.8666 14.2829 29.877 10.8001 30.3981L11.0961 32.3761C14.0015 31.9414 16.6891 32.7446 19.3644 34.1252L20.2816 32.348ZM10.7998 30.3982C10.388 30.4599 9.94468 30.566 9.46564 30.6638L9.86556 32.6234C10.4449 32.5052 10.7522 32.4277 11.0964 32.376L10.7998 30.3982ZM8.68401 31.8346C9.73479 37.2345 13.3673 40.5279 17.5941 43.1923L18.6607 41.5005C14.6039 38.9431 11.5404 36.0427 10.6472 31.4526L8.68401 31.8346ZM18.2475 41.3536C17.3819 41.2489 16.2594 40.7693 14.9918 39.9335C13.742 39.1093 12.427 37.9891 11.1875 36.7057C8.68437 34.1137 6.62095 30.9947 5.96732 28.5011L4.03268 29.0082C4.81005 31.9739 7.13413 35.3875 9.74885 38.095C11.0683 39.4613 12.4947 40.6825 13.8908 41.6031C15.2691 42.512 16.6967 43.1806 18.0073 43.3392L18.2475 41.3536ZM5.15997 29.7417C13.3246 28.4186 20.4108 29.874 26.173 35.9217L27.621 34.5421C21.2872 27.8944 13.4694 26.369 4.84003 27.7675L5.15997 29.7417ZM27.8093 34.8223C25.3851 29.4232 24.8341 24.3801 25.7561 19.5859C26.6809 14.7778 29.1009 10.1516 32.7265 5.62517L31.1655 4.37483C27.4012 9.07433 24.7952 13.9927 23.7921 19.2082C22.7863 24.4378 23.4058 29.8977 25.9847 35.6415L27.8093 34.8223ZM31.1655 5.62512C34.7619 10.1157 37.1747 14.7077 38.1168 19.4869C39.0562 24.2523 38.5469 29.2701 36.1948 34.6504L38.0274 35.4515C40.5299 29.7268 41.1033 24.2956 40.0791 19.1001C39.0576 13.9183 36.4606 9.03731 32.7265 4.37488L31.1655 5.62512Z"
|
|
mask="url(#path-1-outside-1_16_6)" />
|
|
</svg>
|
|
<h5>FLO</h5>
|
|
</div>
|
|
<h1 class="h1" id="personal_flo_balance"></h1>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="portfolio_asset_page" class="hidden grid gap-1-5">
|
|
<div class="flex align-center sticky top-0">
|
|
<a href="#/portfolio" class="button icon-only">
|
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
|
|
width="24px" fill="#000000">
|
|
<path d="M0 0h24v24H0z" fill="none"></path>
|
|
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></path>
|
|
</svg>
|
|
</a>
|
|
<b id="portfolio_asset__name"></b>
|
|
</div>
|
|
<div id="portfolio_asset__balance" class="flex flex-direction-column gap-0-5"></div>
|
|
<fieldset id="portfolio_asset__actions" class="flex gap-0-3">
|
|
<button class="button flex-1" value="deposit">
|
|
Deposit
|
|
</button>
|
|
<button class="button flex-1" value="withdraw">
|
|
Withdraw
|
|
</button>
|
|
<button class="button flex-1" value="transfer">
|
|
Transfer
|
|
</button>
|
|
</fieldset>
|
|
<div>
|
|
<h4>History</h4>
|
|
<ul id="portfolio_asset__history" class="observe-empty-state"></ul>
|
|
<div class="empty-state">
|
|
<p>No transactions to see</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
<div id="history" class="mobile-page hidden">
|
|
<div class="flex align-center sticky top-0">
|
|
<a href="#/portfolio" class="button icon-only">
|
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
|
|
width="24px" fill="#000000">
|
|
<path d="M0 0h24v24H0z" fill="none" />
|
|
<path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" />
|
|
</svg>
|
|
</a>
|
|
<h1>History</h1>
|
|
</div>
|
|
<ul id="exchange_history" class="observe-empty-state"></ul>
|
|
<div class="empty-state">
|
|
<p>No exchange transactions</p>
|
|
</div>
|
|
</div>
|
|
<div id="help" class="mobile-page hidden grid gap-2">
|
|
<h1>Hey there! How can we help you today?</h1>
|
|
<div class="grid gap-0-5">
|
|
<h5>F.A.Qs</h5>
|
|
<div class="grid gap-0-5">
|
|
<details>
|
|
<summary class="interact">
|
|
<h4>
|
|
What are the rules of the exchange that I must know?
|
|
</h4>
|
|
<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="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
|
|
</svg>
|
|
</summary>
|
|
<ul>
|
|
<li>Only the buyers of a token are permitted to sell that token</li>
|
|
<li>The maximum quantity of a token that can be sold is capped by the number of tokens
|
|
purchased for each token </li>
|
|
<li>You need sell-chips in order to sell. Sell-chips are given against each purchase
|
|
when current token prices are at least 10% higher</li>
|
|
<li><strong>Sell side priority</strong>: The first to buy gets the first priority to
|
|
sell no matter when
|
|
that person enters the order</li>
|
|
<li><strong>Buy side priority</strong>: the first to order gets the first priority to
|
|
buy</li>
|
|
<li>Launch sellers can sell without sell-chips in order to provide initial inventory
|
|
</li>
|
|
</ul>
|
|
</details>
|
|
<details>
|
|
<summary class="interact">
|
|
<h4>
|
|
What are rupee tokens?
|
|
</h4>
|
|
<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="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
|
|
</svg>
|
|
</summary>
|
|
<p>
|
|
Rupee tokens are a digital asset which has value equal to 1 INR. Which can be used with
|
|
RanchiMall exchange and other RanchiMall products.
|
|
</p>
|
|
</details>
|
|
<details>
|
|
<summary class="interact">
|
|
<h4>
|
|
Where can I get rupee tokens?
|
|
</h4>
|
|
<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="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
|
|
</svg>
|
|
</summary>
|
|
<p>
|
|
Rupee tokens can be acquired by paying equivalent amount of rupees using our <strong>
|
|
<a href="https://ranchimall.github.io/flopay/" target="_blank">FLO Pay</a>
|
|
</strong>
|
|
app.
|
|
</p>
|
|
</details>
|
|
<details>
|
|
<summary class="interact">
|
|
<h4>
|
|
Why is there a difference between current asset prices and prices shown as
|
|
buying/selling price?
|
|
</h4>
|
|
<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="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
|
|
</svg>
|
|
</summary>
|
|
<p>
|
|
Contrary to other exchanges, the price you see as buying/selling price works as a upper
|
|
bound for buying and lower bound for selling. you order will bne executed at
|
|
best price determined by the exchange algorithm.
|
|
</p>
|
|
</details>
|
|
<details>
|
|
<summary class="interact">
|
|
<h4>
|
|
Why my order hasn't been filled yet?
|
|
</h4>
|
|
<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="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
|
|
</svg>
|
|
</summary>
|
|
<p>
|
|
Our Exchange works a little differently than other exchanges. You can only buy or
|
|
sell at
|
|
the
|
|
market price.
|
|
If you have placed an order at lower/higher price than current price, your order will
|
|
not be
|
|
executed until the market price matches your order price.
|
|
</p>
|
|
</details>
|
|
<details>
|
|
<summary class="interact">
|
|
<h4>
|
|
Why am I not able to sell my assets?
|
|
</h4>
|
|
<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="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
|
|
</svg>
|
|
</summary>
|
|
<p>
|
|
We only allows selling of assets that were bought in this exchange.
|
|
Assets bought in other exchanges can not be sold here.
|
|
</p>
|
|
</details>
|
|
<details>
|
|
<summary class="interact">
|
|
<h4>
|
|
I have rupee tokens in FLO ID, so why can't I see them in my portfolio?
|
|
</h4>
|
|
<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="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
|
|
</svg>
|
|
</summary>
|
|
<p>
|
|
In order to user your rupee tokens in exchange, you have to deposit them in exchange
|
|
first.
|
|
Assets owned by a FLO ID are different than assets you own in exchange.
|
|
</p>
|
|
</details>
|
|
<details>
|
|
<summary class="interact">
|
|
<h4>
|
|
Why don't prices fluctuate often?
|
|
</h4>
|
|
<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="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
|
|
</svg>
|
|
</summary>
|
|
<p>
|
|
Our exchange has a different pricing model than other exchanges. We have an algorithm
|
|
based
|
|
pricing model which updates prices every hour
|
|
based on predefined rules. This prevents rapid fluctuations in price and ensures that
|
|
you
|
|
can always buy or sell at the best price.
|
|
</p>
|
|
</details>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<nav id="main_navbar" class="flex align-center">
|
|
<a href="#/exchange" class="main_navbar__item main_navbar__item--active interact">
|
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
|
|
fill="#000000">
|
|
<path d="M0 0h24v24H0z" fill="none" />
|
|
<path d="M16 17.01V10h-2v7.01h-3L15 21l4-3.99h-3zM9 3L5 6.99h3V14h2V6.99h3L9 3z" />
|
|
</svg>
|
|
<div class="item__title">Exchange</div>
|
|
</a>
|
|
<a href="#/my_orders" class="main_navbar__item user-content hidden interact">
|
|
<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">
|
|
<path d="M0,0h24v24H0V0z" fill="none" />
|
|
<g>
|
|
<path
|
|
d="M19.5,3.5L18,2l-1.5,1.5L15,2l-1.5,1.5L12,2l-1.5,1.5L9,2L7.5,3.5L6,2v14H3v3c0,1.66,1.34,3,3,3h12c1.66,0,3-1.34,3-3V2 L19.5,3.5z M15,20H6c-0.55,0-1-0.45-1-1v-1h10V20z M19,19c0,0.55-0.45,1-1,1s-1-0.45-1-1v-3H8V5h11V19z" />
|
|
<rect height="2" width="6" x="9" y="7" />
|
|
<rect height="2" width="2" x="16" y="7" />
|
|
<rect height="2" width="6" x="9" y="10" />
|
|
<rect height="2" width="2" x="16" y="10" />
|
|
</g>
|
|
</svg>
|
|
<div class="item__title">My orders</div>
|
|
</a>
|
|
<a href="#/market" class="main_navbar__item interact">
|
|
<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 />
|
|
<g>
|
|
<path
|
|
d="M21.9,8.89l-1.05-4.37c-0.22-0.9-1-1.52-1.91-1.52H5.05C4.15,3,3.36,3.63,3.15,4.52L2.1,8.89 c-0.24,1.02-0.02,2.06,0.62,2.88C2.8,11.88,2.91,11.96,3,12.06V19c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2v-6.94 c0.09-0.09,0.2-0.18,0.28-0.28C21.92,10.96,22.15,9.91,21.9,8.89z M18.91,4.99l1.05,4.37c0.1,0.42,0.01,0.84-0.25,1.17 C19.57,10.71,19.27,11,18.77,11c-0.61,0-1.14-0.49-1.21-1.14L16.98,5L18.91,4.99z M13,5h1.96l0.54,4.52 c0.05,0.39-0.07,0.78-0.33,1.07C14.95,10.85,14.63,11,14.22,11C13.55,11,13,10.41,13,9.69V5z M8.49,9.52L9.04,5H11v4.69 C11,10.41,10.45,11,9.71,11c-0.34,0-0.65-0.15-0.89-0.41C8.57,10.3,8.45,9.91,8.49,9.52z M4.04,9.36L5.05,5h1.97L6.44,9.86 C6.36,10.51,5.84,11,5.23,11c-0.49,0-0.8-0.29-0.93-0.47C4.03,10.21,3.94,9.78,4.04,9.36z M5,19v-6.03C5.08,12.98,5.15,13,5.23,13 c0.87,0,1.66-0.36,2.24-0.95c0.6,0.6,1.4,0.95,2.31,0.95c0.87,0,1.65-0.36,2.23-0.93c0.59,0.57,1.39,0.93,2.29,0.93 c0.84,0,1.64-0.35,2.24-0.95c0.58,0.59,1.37,0.95,2.24,0.95c0.08,0,0.15-0.02,0.23-0.03V19H5z" />
|
|
</g>
|
|
</g>
|
|
</svg>
|
|
<div class="item__title">Market</div>
|
|
</a>
|
|
<a href="#/portfolio" class="main_navbar__item user-content hidden interact">
|
|
<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">
|
|
<rect fill="none" height="24" width="24" />
|
|
<path
|
|
d="M12,2C6.48,2,2,6.48,2,12s4.48,10,10,10h8c1.1,0,2-0.9,2-2v-8C22,6.48,17.52,2,12,2z M12,20c-2.89,0-5.43-1.54-6.83-3.84 l2.95-2.95L11.41,16L16,11.42V13h2V8h-5v2h1.58l-3.28,3.28L8,10.5l-3.69,3.7C4.11,13.5,4,12.76,4,12c0-4.41,3.59-8,8-8s8,3.59,8,8 S16.41,20,12,20z M19.5,20.5c-0.55,0-1-0.45-1-1s0.45-1,1-1s1,0.45,1,1S20.05,20.5,19.5,20.5z" />
|
|
</svg>
|
|
<div class="item__title">Portfolio</div>
|
|
</a>
|
|
<a href="#/help" class="main_navbar__item interact">
|
|
<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="M11 18h2v-2h-2v2zm1-16C6.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 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z" />
|
|
</svg>
|
|
<div class="item__title">Help</div>
|
|
</a>
|
|
</nav>
|
|
</article>
|
|
<!--
|
|
* add warning to show if an order is not considered
|
|
-->
|
|
|
|
<sm-popup id="confirm_trade_popup">
|
|
<header slot="header" class="popup__header">
|
|
<button class="popup__header__close">
|
|
<svg class="icon" 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 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
|
|
</svg>
|
|
</button>
|
|
<h3 id="confirm_trade__title" class="uppercase">BUY</h3>
|
|
</header>
|
|
<section class="grid gap-1-5">
|
|
<div id="confirm_trade__details" class="grid gap-1-5"></div>
|
|
<button class="button button--primary" onclick="tradeAsset()" autofocus>Confirm</button>
|
|
</section>
|
|
</sm-popup>
|
|
<sm-popup id="user_popup">
|
|
<header slot="header" class="popup__header">
|
|
<button class="popup__header__close">
|
|
<svg class="icon" 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 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
|
|
</svg>
|
|
</button>
|
|
<h3>Profile</h3>
|
|
</header>
|
|
<section class="grid gap-2">
|
|
<div class="grid gap-0-5">
|
|
<div class="label">My FLO ID</div>
|
|
<sm-copy id="user_id"></sm-copy>
|
|
</div>
|
|
<div class="grid gap-1">
|
|
<div class="grid gap-0-5">
|
|
<h4>Add password lock</h4>
|
|
<p>
|
|
Adding a password lock allows to authenticate transactions with a custom password instead of
|
|
private key.
|
|
</p>
|
|
</div>
|
|
<button class="button" onclick="proxy.lock();">Add password</button>
|
|
</div>
|
|
<button class="button button--danger" onclick="UI_evt.logout();">Log out</button>
|
|
</section>
|
|
</sm-popup>
|
|
<sm-popup id="portfolio_popup">
|
|
<header slot="header" class="popup__header">
|
|
<button class="popup__header__close">
|
|
<svg class="icon" 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 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
|
|
</svg>
|
|
</button>
|
|
<h4 id="portfolio_popup__title" class="capitalize"></h4>
|
|
</header>
|
|
<sm-form id="portfolio_form">
|
|
<sm-input id="get_receiver_id" placeholder="Receiver FLO ID" error-text="Invalid FLO ID" data-flo-id
|
|
required animate></sm-input>
|
|
<sm-input id="get_user_amount" placeholder="Quantity" type="number" min="0.00000001" step="0.00000001"
|
|
required animate>
|
|
</sm-input>
|
|
<div id="portfolio_quantity_selector" class="flex align-center quantity-selector">
|
|
<span id="portfolio_quantity_type">Rupee</span>
|
|
<button class="button" value="0.25">25%</button>
|
|
<button class="button" value="0.5">50%</button>
|
|
<button class="button" value="0.75">75%</button>
|
|
<button class="button" value="1">100%</button>
|
|
</div>
|
|
<p id="withdraw_flo_tip" class="hidden">*While withdrawing 100% of balance FLO, <b>0.1 FLO</b> is reserved
|
|
for
|
|
future transactions on blockchain.
|
|
</p>
|
|
<sm-input id="get_private_key" placeholder="FLO private key" type="password" required
|
|
error-text="Invalid private key" data-private-key="" animate>
|
|
</sm-input>
|
|
<div class="loader-button-wrapper">
|
|
<button id="portfolio_popup__cta" class="button button--primary uppercase" type="submit"></button>
|
|
</div>
|
|
</sm-form>
|
|
<div id="portfolio_result" class="grid gap-2 hidden">
|
|
<div id="portfolio_result__icon"></div>
|
|
<div class="grid gap-0-5">
|
|
<h4 id="portfolio_result__title"></h4>
|
|
<p id="portfolio_result__description"></p>
|
|
</div>
|
|
<div id="portfolio_result__cta" class="hidden grid gap-1-5">
|
|
<div class="grid gap-0-5">
|
|
<h4>Low on rupee balance?</h4>
|
|
<p>Visit RanchiMall Pay to deposit rupee in your FLO ID.</p>
|
|
</div>
|
|
<a href="https://ranchimall.github.io/upi/" target="_blank" rel="noopener noreferrer"
|
|
class="button button--primary">Open RanchiMall Pay</a>
|
|
</div>
|
|
</div>
|
|
</sm-popup>
|
|
<sm-popup id="transaction_info_popup">
|
|
<header slot="header" class="popup__header">
|
|
<button class="popup__header__close">
|
|
<svg class="icon" 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 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" />
|
|
</svg>
|
|
</button>
|
|
<h4>Transaction details</h4>
|
|
</header>
|
|
<div id="transaction_details" class="grid gap-1-5"></div>
|
|
</sm-popup>
|
|
|
|
<!-- templates -->
|
|
<template id="listed_asset_template">
|
|
<li>
|
|
<a class="listed-asset interact grid align-center">
|
|
<div class="listed-asset__icon"></div>
|
|
<h4 class="listed-asset__name"></h4>
|
|
<b class="listed-asset__rate"></b>
|
|
<div class="listed-asset__countdown">
|
|
timer
|
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 24">
|
|
<path class="path-a" d="M11.5,2a10,10,0,1,1-10,10,10,10,0,0,1,10-10" />
|
|
<path class="path-b" d="M11.5,2a10,10,0,1,1-10,10,10,10,0,0,1,10-10" />
|
|
</svg>
|
|
</div>
|
|
<svg class="icon listed-asset__right-arrow hide-on-desktop" xmlns="http://www.w3.org/2000/svg"
|
|
height="24px" viewBox="0 0 24 24" width="24px" fill="#000000">
|
|
<path d="M0 0h24v24H0z" fill="none"></path>
|
|
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path>
|
|
</svg>
|
|
</a>
|
|
</li>
|
|
</template>
|
|
<template id="asset_balance_card_template">
|
|
<a class="balance-card interact">
|
|
<div class="balance-card__icon"></div>
|
|
<h4 class="balance-card__token"></h4>
|
|
<svg class="icon balance-card__right-arrow" xmlns="http://www.w3.org/2000/svg" height="24px"
|
|
viewBox="0 0 24 24" width="24px" fill="#000000">
|
|
<path d="M0 0h24v24H0z" fill="none"></path>
|
|
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path>
|
|
</svg>
|
|
</a>
|
|
</template>
|
|
<template id="net_balance_template">
|
|
<span class="available-balance text-align-right"></span>
|
|
</template>
|
|
<template id="locked_balance_template">
|
|
<div class="grid balance-card__amount-wrapper">
|
|
<div class="grid">
|
|
<span class="label">Locked</span>
|
|
<span class="locked-balance"></span>
|
|
</div>
|
|
<div class="grid">
|
|
<span class="label">Available</span>
|
|
<span class="available-balance"></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template id="asset_balance_actions">
|
|
<fieldset class="flex portfolio_actions__wrapper">
|
|
<button class="button" value="deposit">
|
|
Deposit
|
|
</button>
|
|
<button class="button" value="withdraw">
|
|
Withdraw
|
|
</button>
|
|
<button class="button" value="transfer">
|
|
Transfer
|
|
</button>
|
|
</fieldset>
|
|
</template>
|
|
<template id="order_template">
|
|
<li class="list__item order-card">
|
|
<sm-checkbox></sm-checkbox>
|
|
<div class="grid">
|
|
<div class="order-card__type capitalize"></div>
|
|
<div class="order-card__quantity"></div>
|
|
</div>
|
|
<time class="order-card__time"></time>
|
|
<div class="grid">
|
|
<span class="label order-card__price-type"></span>
|
|
<div class="order-card__price"></div>
|
|
</div>
|
|
<div class="grid">
|
|
<span class="label order-card__amount-type"></span>
|
|
<div class="order-card__amount"></div>
|
|
</div>
|
|
<button class="cancel-order" title="Cancel this order">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" height="24px" viewBox="0 0 24 24" width="24px">
|
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
|
<path
|
|
d="M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z" />
|
|
</svg>
|
|
<span>Cancel</span>
|
|
</button>
|
|
</li>
|
|
</template>
|
|
<template id="market_order_template">
|
|
<li class="live-order align-center">
|
|
<div class="flex gap-1 space-between align-center">
|
|
<b class="transaction-card__quantity wrap-around"></b>
|
|
<button class="more-info" title="View order details">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" height="24px" viewBox="0 0 24 24" width="24px">
|
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
|
<path
|
|
d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.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" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="flex gap-0-5">
|
|
<div class="grid flex-1">
|
|
<span class="label">Valid upto</span>
|
|
<b class="transaction-card__price wrap-around"></b>
|
|
</div>
|
|
<div class="grid flex-1">
|
|
<span class="label">Amount</span>
|
|
<div class="transaction-card__total wrap-around"></div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</template>
|
|
<template id="transaction_template">
|
|
<li class="completed-trade align-center">
|
|
<div class="grid quantity-block">
|
|
<div class="completed-trade__type capitalize"></div>
|
|
<b class="completed-trade__quantity"></b>
|
|
</div>
|
|
<div class="flex align-center info-block">
|
|
<time class="completed-trade__time"></time>
|
|
<button class="more-info" title="View transaction details">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" height="24px" viewBox="0 0 24 24" width="24px">
|
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
|
<path
|
|
d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.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" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="grid price-block">
|
|
<span class="label">Unit price</span>
|
|
<b class="completed-trade__price"></b>
|
|
</div>
|
|
<div class="grid amount-block">
|
|
<span class="label">Amount</span>
|
|
<b class="completed-trade__total"></b>
|
|
</div>
|
|
</li>
|
|
</template>
|
|
<template id="market_transaction_template">
|
|
<li class="completed-trade align-center">
|
|
<b class="completed-trade__quantity quantity-block"></b>
|
|
<div class="flex align-center info-block">
|
|
<time class="completed-trade__time"></time>
|
|
<button class="more-info" title="View transaction details">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" height="24px" viewBox="0 0 24 24" width="24px">
|
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
|
<path
|
|
d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.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" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="grid price-block">
|
|
<span class="label">Traded at</span>
|
|
<b class="completed-trade__price"></b>
|
|
</div>
|
|
<div class="grid amount-block">
|
|
<span class="label">Amount</span>
|
|
<b class="completed-trade__total"></b>
|
|
</div>
|
|
</li>
|
|
</template>
|
|
<template id="market_order_list_template">
|
|
<h4 class="grid list__header">
|
|
<div>Quantity</div>
|
|
<div>Buy price</div>
|
|
<div>Sell price</div>
|
|
<div>Quantity</div>
|
|
</h4>
|
|
<section id="buyers_list"></section>
|
|
<section id="sellers_list"></section>
|
|
</template>
|
|
<template id="market_transaction_list_template">
|
|
<h4 class="grid list__header">
|
|
<div>Quantity</div>
|
|
<div>Traded at</div>
|
|
<div>Amount</div>
|
|
<div></div>
|
|
</h4>
|
|
</template>
|
|
<template id="success_template">
|
|
<div class="stateful-result stateful-result--success">
|
|
<div class="result__background result__background--success"></div>
|
|
<div class="icon-wrapper flex align-center">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon margin-right-0-5" 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>
|
|
<span>Success</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template id="failure_template">
|
|
<div class="stateful-result stateful-result--failure">
|
|
<div class="result__background"></div>
|
|
<div class="icon-wrapper flex align-center">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon margin-right-0-5" height="24px" viewBox="0 0 24 24"
|
|
width="24px" fill="#000000">
|
|
<path
|
|
d="M11 15h2v2h-2v-2zm0-8h2v6h-2V7zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" />
|
|
</svg>
|
|
<span>Failed</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template id="history_entry">
|
|
<li class="history-entry">
|
|
<span class="history-entry__status"></span>
|
|
<span class="history-entry__token-action capitalize"></span>
|
|
<span class="history-entry__amount"></span>
|
|
<time class="history-entry__time"></time>
|
|
<a class="history-entry__txid" target="_blank">See on blockchain</a>
|
|
</li>
|
|
</template>
|
|
<script>
|
|
|
|
/**
|
|
* @yaireo/relative-time - javascript function to transform timestamp or date to local relative-time
|
|
*
|
|
* @version v1.0.0
|
|
* @homepage https://github.com/yairEO/relative-time
|
|
*/
|
|
|
|
!function (e, t) { var o = o || {}; "function" == typeof o && o.amd ? o([], t) : "object" == typeof exports && "object" == typeof module ? module.exports = t() : "object" == typeof exports ? exports.RelativeTime = t() : e.RelativeTime = t() }(this, (function () { const e = { year: 31536e6, month: 2628e6, day: 864e5, hour: 36e5, minute: 6e4, second: 1e3 }, t = "en", o = { numeric: "auto" }; function n(e) { e = { locale: (e = e || {}).locale || t, options: { ...o, ...e.options } }, this.rtf = new Intl.RelativeTimeFormat(e.locale, e.options) } return n.prototype = { from(t, o) { const n = t - (o || new Date); for (let t in e) if (Math.abs(n) > e[t] || "second" == t) return this.rtf.format(Math.round(n / e[t]), t) } }, n }));
|
|
</script>
|
|
<script id="ui_utils">
|
|
// Global variables
|
|
const { html, render: renderElem } = uhtml;
|
|
const domRefs = {};
|
|
let timerId;
|
|
const currentYear = new Date().getFullYear();
|
|
const relativeTime = new RelativeTime('en', { style: 'short' });
|
|
|
|
//Checks for internet connection status
|
|
if (!navigator.onLine)
|
|
floGlobals.connectionErrorNotification = notify('There seems to be a problem connecting to the internet, Please check you internet connection.', 'error')
|
|
window.addEventListener('offline', () => {
|
|
floGlobals.connectionErrorNotification = notify('There seems to be a problem connecting to the internet, Please check you internet connection.', 'error')
|
|
})
|
|
window.addEventListener('online', () => {
|
|
getRef('notification_drawer').remove(floGlobals.connectionErrorNotification)
|
|
notify('We are back online.', 'success')
|
|
location.reload()
|
|
})
|
|
|
|
// Use instead of document.getElementById
|
|
function getRef(elementId) {
|
|
if (!domRefs.hasOwnProperty(elementId)) {
|
|
domRefs[elementId] = {
|
|
count: 1,
|
|
ref: null,
|
|
};
|
|
return document.getElementById(elementId);
|
|
} else {
|
|
if (domRefs[elementId].count < 3) {
|
|
domRefs[elementId].count = domRefs[elementId].count + 1;
|
|
return document.getElementById(elementId);
|
|
} else {
|
|
if (!domRefs[elementId].ref)
|
|
domRefs[elementId].ref = document.getElementById(elementId);
|
|
return domRefs[elementId].ref;
|
|
}
|
|
}
|
|
}
|
|
|
|
// returns dom with specified element
|
|
function createElement(tagName, options = {}) {
|
|
const { className, textContent, innerHTML, attributes = {} } = options
|
|
const elem = document.createElement(tagName)
|
|
for (let attribute in attributes) {
|
|
elem.setAttribute(attribute, attributes[attribute])
|
|
}
|
|
if (className)
|
|
elem.className = className
|
|
if (textContent)
|
|
elem.textContent = textContent
|
|
if (innerHTML)
|
|
elem.innerHTML = innerHTML
|
|
return elem
|
|
}
|
|
|
|
// Use when a function needs to be executed after user finishes changes
|
|
const debounce = (callback, wait) => {
|
|
let timeoutId = null;
|
|
return (...args) => {
|
|
window.clearTimeout(timeoutId);
|
|
timeoutId = window.setTimeout(() => {
|
|
callback.apply(null, args);
|
|
}, wait);
|
|
};
|
|
}
|
|
|
|
// adds a class to all elements in an array
|
|
function addClass(elements, className) {
|
|
elements.forEach((element) => {
|
|
document.querySelector(element).classList.add(className);
|
|
});
|
|
}
|
|
// removes a class from all elements in an array
|
|
function removeClass(elements, className) {
|
|
elements.forEach((element) => {
|
|
document.querySelector(element).classList.remove(className);
|
|
});
|
|
}
|
|
// return querySelectorAll elements as an array
|
|
function getAllElements(selector) {
|
|
return Array.from(document.querySelectorAll(selector));
|
|
}
|
|
|
|
let zIndex = 50
|
|
// function required for popups or modals to appear
|
|
function openPopup(popupId, pinned) {
|
|
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)
|
|
}
|
|
|
|
// displays a popup for asking permission. Use this instead of JS confirm
|
|
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)
|
|
})
|
|
})
|
|
}
|
|
// displays a popup for asking user input. Use this instead of JS prompt
|
|
function getPromptInput(title, message = '', options = {}) {
|
|
let { placeholder = '', isPassword = false, cancelText = 'Cancel', confirmText = 'OK' } = options
|
|
getRef('prompt_title').innerText = title;
|
|
getRef('prompt_message').innerText = message;
|
|
const cancelButton = getRef('prompt_popup').querySelector('.cancel-button');
|
|
const confirmButton = getRef('prompt_popup').querySelector('.confirm-button')
|
|
if (isPassword) {
|
|
placeholder = 'Password'
|
|
getRef('prompt_input').setAttribute("type", "password")
|
|
}
|
|
getRef('prompt_input').setAttribute("placeholder", placeholder)
|
|
getRef('prompt_input').focusIn()
|
|
cancelButton.textContent = cancelText;
|
|
confirmButton.textContent = confirmText;
|
|
openPopup('prompt_popup', true)
|
|
return new Promise((resolve, reject) => {
|
|
cancelButton.addEventListener('click', () => {
|
|
closePopup()
|
|
return null
|
|
}, { once: true })
|
|
confirmButton.addEventListener('click', () => {
|
|
closePopup()
|
|
resolve(getRef('prompt_input').value)
|
|
}, { once: true })
|
|
})
|
|
}
|
|
|
|
//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 });
|
|
}
|
|
function getFormattedTime(time, format) {
|
|
try {
|
|
let [day, month, date, year] = new Date(time).toString().split(' '),
|
|
minutes = new Date(time).getMinutes(),
|
|
hours = new Date(time).getHours();
|
|
|
|
minutes = minutes < 10 ? `0${minutes}` : minutes
|
|
let finalHours = ``;
|
|
if (hours > 12)
|
|
finalHours = `${hours - 12}:${minutes}`
|
|
else if (hours === 0)
|
|
finalHours = `12:${minutes}`
|
|
else
|
|
finalHours = `${hours}:${minutes}`
|
|
|
|
finalHours = hours >= 12 ? `${finalHours} PM` : `${finalHours} AM`
|
|
switch (format) {
|
|
case 'date-only':
|
|
return `${month} ${date}, ${year}`;
|
|
break;
|
|
default:
|
|
return `${month} ${date} ${year}, ${finalHours}`;
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
return time;
|
|
}
|
|
}
|
|
|
|
window.addEventListener('hashchange', e => routeTo(window.location.hash))
|
|
window.addEventListener("load", () => {
|
|
routeTo(window.location.hash, { firstLoad: true });
|
|
document.body.classList.remove('hidden')
|
|
document.querySelectorAll('sm-input[data-flo-id]').forEach(input => input.customValidation = floCrypto.validateAddr)
|
|
document.querySelectorAll('sm-input[data-private-key]').forEach(input => input.customValidation = floCrypto.getPubKeyHex)
|
|
document.addEventListener('keyup', (e) => {
|
|
if (e.key === 'Escape') {
|
|
closePopup()
|
|
}
|
|
})
|
|
document.addEventListener('copy', () => {
|
|
notify('copied', 'success')
|
|
})
|
|
document.addEventListener("pointerdown", (e) => {
|
|
if (e.target.closest("button:not([disabled]), .interact")) {
|
|
createRipple(e, e.target.closest("button, .interact"));
|
|
}
|
|
});
|
|
document.querySelectorAll('.popup__header__close, .close-popup-on-click').forEach(elem => {
|
|
elem.addEventListener('click', () => {
|
|
closePopup()
|
|
})
|
|
})
|
|
});
|
|
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(
|
|
[
|
|
{
|
|
transform: "scale(4)",
|
|
opacity: 0,
|
|
},
|
|
],
|
|
{
|
|
duration: 600,
|
|
fill: "forwards",
|
|
easing: "ease-out",
|
|
}
|
|
);
|
|
target.append(circle);
|
|
rippleAnimation.onfinish = () => {
|
|
circle.remove();
|
|
};
|
|
}
|
|
|
|
const pagesData = {
|
|
lastPage: '',
|
|
currentPage: '',
|
|
params: {}
|
|
}
|
|
async function routeTo(targetPage, options = {}) {
|
|
const { firstLoad, hashChange } = options
|
|
let pageId
|
|
let subPageId1
|
|
let params = {}
|
|
let searchParams
|
|
if (targetPage === '') {
|
|
pageId = 'exchange'
|
|
} else {
|
|
if (targetPage.includes('/')) {
|
|
let path;
|
|
[path, searchParams] = targetPage.split('?');
|
|
[, pageId, subPageId1] = path.split('/')
|
|
} else {
|
|
pageId = targetPage
|
|
}
|
|
}
|
|
if (searchParams) {
|
|
const urlSearchParams = new URLSearchParams('?' + searchParams);
|
|
params = Object.fromEntries(urlSearchParams.entries());
|
|
}
|
|
if (typeof proxy.userID === "undefined" && !['exchange', 'market', 'help'].includes(pageId)) {
|
|
pageId = 'exchange'
|
|
history.replaceState(null, null, '#/exchange')
|
|
}
|
|
pagesData.currentPage = pageId
|
|
if (!firstLoad) {
|
|
switch (pageId) {
|
|
case 'exchange':
|
|
if (document.body.classList.contains('is-signed-in')) {
|
|
getRef('login_section').classList.add('hidden')
|
|
if (!isMobileView && !params.hasOwnProperty('asset') || params.asset === '') {
|
|
if (getRef('listed_assets').querySelector(`[href="#/exchange?asset=FLO"]`))
|
|
getRef('listed_assets').querySelector(`[href="#/exchange?asset=FLO"]`).click()
|
|
}
|
|
if (params.hasOwnProperty('asset') && params.asset !== '') {
|
|
if (getRef('listed_assets').querySelector('.listed-asset--active'))
|
|
getRef('listed_assets').querySelector('.listed-asset--active').classList.remove('listed-asset--active')
|
|
getRef('listed_assets').querySelector(`[href="#/exchange?asset=${params.asset}"]`).classList.add('listed-asset--active')
|
|
getRef('chart_asset').textContent = `${params.asset}/INR`
|
|
showSuggestedPrice(params.asset)
|
|
getRef('traded_asset').textContent = `Trade ${params.asset}`
|
|
getRef('trade_button').textContent = `${tradeType} ${params.asset}`
|
|
getRef('quantity_type').textContent = tradeType === 'buy' ? formatAmount(allTokens.rupee.net, true) : `${parseFloat(allTokens[params.asset].net.toFixed(4))} ${params.asset}`
|
|
updateTooltip(params.asset, tradeType)
|
|
}
|
|
const animOptions = {
|
|
duration: 150,
|
|
easing: 'ease',
|
|
fill: 'forwards',
|
|
}
|
|
if (params.hasOwnProperty('asset') && params.asset !== '') {
|
|
if (isMobileView) {
|
|
if (!getRef('asset_list_wrapper').classList.contains('hide-on-mobile')) {
|
|
animateTo(getRef('main_header'), slideOutLeft, animOptions)
|
|
animateTo(getRef('main_navbar'), slideOutLeft, animOptions)
|
|
animateTo(getRef('asset_list_wrapper'), slideOutLeft, animOptions).onfinish = () => {
|
|
addClass(['#main_header', '#asset_list_wrapper', '#main_navbar'], 'hide-on-mobile')
|
|
removeClass(['#asset_page'], 'hide-on-mobile')
|
|
animateTo(getRef('asset_page'), slideInLeft, animOptions)
|
|
}
|
|
}
|
|
} else {
|
|
addClass(['#main_header', '#asset_list_wrapper', '#main_navbar'], 'hide-on-mobile')
|
|
removeClass(['#asset_page'], 'hide-on-mobile')
|
|
}
|
|
|
|
if (!chart) {
|
|
chart = LightweightCharts.createChart(getRef('price_history_chart'), {
|
|
width: 800, height: 600,
|
|
timeScale: {
|
|
timeVisible: true,
|
|
},
|
|
});
|
|
lineSeries = chart.addLineSeries();
|
|
setChartTheme()
|
|
chartObserver.observe(getRef('price_chart_wrapper'))
|
|
}
|
|
render.chart(params.asset)
|
|
} else {
|
|
if (isMobileView) {
|
|
if (!getRef('asset_page').classList.contains('hide-on-mobile')) {
|
|
animateTo(getRef('asset_page'), slideOutRight, animOptions).onfinish = () => {
|
|
getRef('asset_page').classList.add('hide-on-mobile')
|
|
getRef('asset_list_wrapper').classList.remove('hide-on-mobile')
|
|
animateTo(getRef('asset_list_wrapper'), slideInRight, animOptions)
|
|
getRef('main_header').classList.remove('hide-on-mobile')
|
|
getRef('main_navbar').classList.remove('hide-on-mobile')
|
|
animateTo(getRef('main_header'), slideInRight, animOptions)
|
|
animateTo(getRef('main_navbar'), slideInRight, animOptions)
|
|
}
|
|
}
|
|
} else {
|
|
getRef('asset_page').classList.add('hide-on-mobile')
|
|
getRef('asset_list_wrapper').classList.remove('hide-on-mobile')
|
|
}
|
|
}
|
|
} else {
|
|
getRef('login_section').classList.remove('hidden')
|
|
if (subPageId1 === 'generate') {
|
|
const { floID, privKey } = floCrypto.generateNewID()
|
|
getRef('generated_flo_address').value = floID
|
|
getRef('generated_private_key').value = privKey
|
|
showChildElement('login_section', 1)
|
|
} else {
|
|
showChildElement('login_section', 0)
|
|
getRef('generated_flo_address').value = ''
|
|
getRef('generated_private_key').value = ''
|
|
history.replaceState({}, '', '#/exchange/login')
|
|
}
|
|
}
|
|
break;
|
|
case 'my_orders':
|
|
break;
|
|
case 'market':
|
|
break;
|
|
case 'portfolio':
|
|
if (params.asset) {
|
|
getRef('portfolio_asset__name').textContent = params.asset
|
|
const { net, locked } = allTokens[params.asset]
|
|
const lockedBalance = params.asset === 'rupee' ? formatAmount(locked, true) : toFixed(locked)
|
|
showChildElement('portfolio', 1, {
|
|
entry: slideInLeft,
|
|
exit: slideOutLeft,
|
|
})
|
|
renderElem(getRef('portfolio_asset__balance'), html`
|
|
<div class="grid">
|
|
<span class="label">Available</span>
|
|
<b style="font-size:2rem">${params.asset === 'rupee' ? formatAmount(net, true) : toFixed(net)}</b>
|
|
${params.asset !== 'rupee' ? html`<span style="font-size: 1rem; opacity:0.8">₹${toFixed(net * floGlobals.exchangeRates[params.asset], 2)}</span>` : ''}
|
|
</div>
|
|
${locked ? html`
|
|
<div class="grid">
|
|
<span class="label">Locked</span>
|
|
<b style="font-size:2rem">${lockedBalance}</b>
|
|
</div>`: ''}
|
|
`)
|
|
getRef('portfolio_asset__history').innerHTML = `
|
|
<div class="flex" style="padding: 1.5rem; justify-content: center;">
|
|
<sm-spinner></sm-spinner>
|
|
</div>`
|
|
floExchangeAPI.getUserTransacts(accountDetails.floID, await proxy.secret).then(exchangeTxs => {
|
|
exchangeTxs = exchangeTxs.filter(tx => tx.asset === params.asset).sort((a, b) => new Date(b.locktime).getTime() - new Date(a.locktime).getTime())
|
|
if (portfolioAssetHistoryLazyLoader) {
|
|
portfolioAssetHistoryLazyLoader.update(exchangeTxs)
|
|
} else {
|
|
portfolioAssetHistoryLazyLoader = new LazyLoader('#portfolio_asset__history', exchangeTxs, render.historyEntry)
|
|
}
|
|
portfolioAssetHistoryLazyLoader.init()
|
|
}).catch(err => {
|
|
console.error(err)
|
|
})
|
|
} else {
|
|
showChildElement('portfolio', 0, {
|
|
entry: slideInRight,
|
|
exit: slideOutRight,
|
|
})
|
|
if (portfolioAssetHistoryLazyLoader)
|
|
portfolioAssetHistoryLazyLoader.clear()
|
|
}
|
|
break;
|
|
case 'history':
|
|
getRef('exchange_history').innerHTML = `
|
|
<div class="flex" style="padding: 1.5rem; justify-content: center;">
|
|
<sm-spinner></sm-spinner>
|
|
</div>
|
|
`
|
|
floExchangeAPI.getUserTransacts(accountDetails.floID, await proxy.secret).then(exchangeTxs => {
|
|
console.log(exchangeTxs)
|
|
exchangeTxs = exchangeTxs.sort((a, b) => new Date(b.locktime).getTime() - new Date(a.locktime).getTime())
|
|
if (historyLazyLoader) {
|
|
historyLazyLoader.update(exchangeTxs)
|
|
} else {
|
|
historyLazyLoader = new LazyLoader('#exchange_history', exchangeTxs, render.historyEntry)
|
|
}
|
|
historyLazyLoader.init()
|
|
}).catch(err => {
|
|
console.error(err)
|
|
})
|
|
break;
|
|
}
|
|
}
|
|
if (pageId !== 'history') {
|
|
if (historyLazyLoader)
|
|
historyLazyLoader.clear()
|
|
}
|
|
if (pageId !== 'portfolio') {
|
|
if (portfolioAssetHistoryLazyLoader)
|
|
portfolioAssetHistoryLazyLoader.clear()
|
|
}
|
|
if (pagesData.lastPage !== pageId) {
|
|
document.querySelectorAll('.mobile-page').forEach(elem => elem.classList.add('hidden'))
|
|
getRef(pageId).classList.remove('hidden')
|
|
document.querySelectorAll('.main_navbar__item').forEach(elem => elem.classList.remove('main_navbar__item--active'))
|
|
if (document.querySelector(`.main_navbar__item[href="#/${pageId}"]`)) {
|
|
document.querySelector(`.main_navbar__item[href="#/${pageId}"]`).classList.add('main_navbar__item--active')
|
|
getRef('main_header').classList.remove('hide-on-mobile')
|
|
} else {
|
|
getRef('main_header').classList.add('hide-on-mobile')
|
|
}
|
|
getRef('pages_container').style.overflowY = "hidden";
|
|
getRef(pageId).animate([
|
|
{
|
|
opacity: 0,
|
|
transform: 'translateY(1rem)'
|
|
},
|
|
{
|
|
opacity: 1,
|
|
transform: 'translateY(0)'
|
|
},
|
|
],
|
|
{
|
|
duration: 300,
|
|
easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)'
|
|
}).onfinish = () => {
|
|
getRef('pages_container').style.overflowY = "";
|
|
}
|
|
pagesData.lastPage = pageId
|
|
}
|
|
pagesData.params = params
|
|
}
|
|
// class based lazy loading
|
|
class LazyLoader {
|
|
constructor(container, elementsToRender, renderFn, options = {}) {
|
|
const { batchSize = 10 } = options
|
|
|
|
this.elementsToRender = elementsToRender
|
|
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
|
|
this.renderFn = renderFn
|
|
this.intersectionObserver
|
|
|
|
this.batchSize = batchSize
|
|
|
|
this.lazyContainer = document.querySelector(container)
|
|
|
|
this.update = this.update.bind(this)
|
|
this.render = this.render.bind(this)
|
|
this.init = this.init.bind(this)
|
|
this.clear = this.clear.bind(this)
|
|
}
|
|
init() {
|
|
this.intersectionObserver = new IntersectionObserver((entries, observer) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
observer.disconnect()
|
|
this.render({ lazyLoad: true })
|
|
}
|
|
})
|
|
}, {
|
|
threshold: 0.3
|
|
})
|
|
this.mutationObserver = new MutationObserver(mutationList => {
|
|
mutationList.forEach(mutation => {
|
|
if (mutation.type === 'childList') {
|
|
if (mutation.addedNodes.length) {
|
|
this.intersectionObserver.observe(this.lazyContainer.lastElementChild)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
this.mutationObserver.observe(this.lazyContainer, {
|
|
childList: true,
|
|
})
|
|
this.render()
|
|
}
|
|
update(elementsToRender) {
|
|
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
|
|
this.render()
|
|
}
|
|
render(options = {}) {
|
|
let { lazyLoad = false } = options
|
|
const frag = document.createDocumentFragment();
|
|
if (lazyLoad) {
|
|
this.updateStartIndex = this.updateEndIndex
|
|
this.updateEndIndex = this.arrayOfElements.length > this.updateEndIndex + this.batchSize ? this.updateEndIndex + this.batchSize : this.arrayOfElements.length
|
|
} else {
|
|
this.intersectionObserver.disconnect()
|
|
this.lazyContainer.innerHTML = ``;
|
|
this.updateStartIndex = 0
|
|
this.updateEndIndex = this.arrayOfElements.length > this.batchSize ? this.batchSize : this.arrayOfElements.length
|
|
}
|
|
for (let index = this.updateStartIndex; index < this.updateEndIndex; index++) {
|
|
frag.append(this.renderFn(this.arrayOfElements[index]))
|
|
}
|
|
this.lazyContainer.append(frag)
|
|
}
|
|
clear() {
|
|
this.intersectionObserver.disconnect()
|
|
this.mutationObserver.disconnect()
|
|
this.lazyContainer.innerHTML = ``;
|
|
}
|
|
reset() {
|
|
this.arrayOfElements = (typeof this.elementsToRender === 'function') ? this.elementsToRender() : this.elementsToRender || []
|
|
this.render()
|
|
}
|
|
}
|
|
function animateTo(element, keyframes, options) {
|
|
const anime = element.animate(keyframes, { ...options, fill: 'both' })
|
|
anime.finished.then(() => {
|
|
anime.commitStyles()
|
|
anime.cancel()
|
|
})
|
|
return anime
|
|
}
|
|
let isMobileView = false
|
|
const mobileQuery = window.matchMedia('(max-width: 40rem)')
|
|
function handleMobileChange(e) {
|
|
isMobileView = e.matches
|
|
if (!isMobileView) {
|
|
getRef('main_header').style = ''
|
|
getRef('asset_list_wrapper').style = ''
|
|
getRef('asset_page').style = ''
|
|
getRef('main_navbar').style = ''
|
|
}
|
|
}
|
|
mobileQuery.addEventListener('change', handleMobileChange)
|
|
|
|
handleMobileChange(mobileQuery)
|
|
document.addEventListener("visibilitychange", handleVisibilityChange, false);
|
|
function handleVisibilityChange() {
|
|
if (document.visibilityState === "hidden") {
|
|
// code if page is hidden
|
|
} else {
|
|
if (floGlobals.exchangeApiLoaded)
|
|
updateRate().catch(err => console.log(err))
|
|
}
|
|
}
|
|
|
|
// animations
|
|
const fadeIn = [
|
|
{ opacity: 0 },
|
|
{ opacity: 1 }
|
|
]
|
|
const fadeOut = [
|
|
{ opacity: 1 },
|
|
{ opacity: 0 }
|
|
]
|
|
|
|
function showChildElement(id, index, options = {}) {
|
|
const { mobileView = false, entry, exit } = options
|
|
const animOptions = {
|
|
duration: 150,
|
|
easing: 'ease',
|
|
fill: 'forwards'
|
|
}
|
|
const visibleElement = [...getRef(id).children].find(elem => !elem.classList.contains(mobileView ? 'hide-on-mobile' : 'hidden'));
|
|
if (visibleElement === getRef(id).children[index]) return;
|
|
if (visibleElement) {
|
|
if (exit) {
|
|
visibleElement.animate(exit, animOptions).onfinish = () => {
|
|
visibleElement.classList.add(mobileView ? 'hide-on-mobile' : 'hidden')
|
|
getRef(id).children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
|
|
if (entry)
|
|
getRef(id).children[index].animate(entry, animOptions)
|
|
}
|
|
} else {
|
|
visibleElement.classList.add(mobileView ? 'hide-on-mobile' : 'hidden')
|
|
getRef(id).children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
|
|
}
|
|
} else {
|
|
getRef(id).children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
|
|
getRef(id).children[index].animate(entry, animOptions)
|
|
}
|
|
}
|
|
</script>
|
|
<script>
|
|
function buttonLoader(id, show = false) {
|
|
getRef(id).disabled = show;
|
|
const animOptions = {
|
|
duration: 200,
|
|
fill: 'forwards',
|
|
easing: 'ease'
|
|
}
|
|
if (show) {
|
|
getRef(id).parentNode.append(createElement('sm-spinner'))
|
|
getRef(id).animate([
|
|
{
|
|
clipPath: 'circle(100%)',
|
|
},
|
|
{
|
|
clipPath: 'circle(0)',
|
|
},
|
|
], animOptions)
|
|
} else {
|
|
getRef(id).animate([
|
|
{
|
|
clipPath: 'circle(0)',
|
|
},
|
|
{
|
|
clipPath: 'circle(100%)',
|
|
},
|
|
], animOptions)
|
|
getRef(id).style = ''
|
|
const potentialTarget = getRef(id).parentNode.querySelector('sm-spinner')
|
|
if (potentialTarget) potentialTarget.remove();
|
|
}
|
|
}
|
|
const listedAssets = {
|
|
rupee: {
|
|
icon: `<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path
|
|
d="M15.3,4.91a4.78,4.78,0,0,0-.39-.5l3.14,0L18.75,2H6L5.25,4.6H9a4.22,4.22,0,0,1,3.06,1,3.16,3.16,0,0,1,.75,1.24H5.93L5.25,9.22h7.62a3.15,3.15,0,0,1-.34.82,3,3,0,0,1-1.37,1.17,5.34,5.34,0,0,1-2.2.4H5.5l0,1.9,7,8.49h3.56v-.17L9.68,14l.09,0a8.07,8.07,0,0,0,3.65-1,5,5,0,0,0,2-2.09A6.29,6.29,0,0,0,16,9.22h2.1l.69-2.42H15.93A5.93,5.93,0,0,0,15.3,4.91Z" />
|
|
</svg>`},
|
|
FLO: {
|
|
icon: `<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path
|
|
d="M16.36,15.39c1.83,0,4.26-2.49,4.36-4.74-5.65-.19-4.91.47-7.28,2.39,2.19-2.4,1.42-7.79-1.43-10V6.17c2.33,1.49,2.21,5.14,0,7.15-2.23-2-2.27-5.69,0-7.15V3c-2.83,2.26-3.62,7.66-1.44,10-2.36-1.93-1.63-2.58-7.28-2.39.1,2.26,2.55,4.73,4.36,4.74,0,0-1.93.22-2.74-2.62,2.38-.37,4.29-.14,6.28,2-.79-.11-4.89,1.13-4.38,3.26.53.06,3,.3,3.58-.83-.17.18-1.25.5-1.53.05.38-1.39,2.32-2,2.32-2-1,1.82-.48,4.63.82,5.72,1.31-1.08,1.8-3.95.82-5.72,0,0,1.95.6,2.32,2-.29.46-1.36.12-1.53-.05.58,1.14,3.06.88,3.58.83.49-2.17-3.58-3.36-4.38-3.26,2-2.17,3.92-2.39,6.28-2C18.3,15.62,16.36,15.39,16.36,15.39ZM12,19.46c-.91-.79-.5-3,0-3.59C12.5,16.45,12.91,18.66,12,19.46Z" />
|
|
</svg>`
|
|
},
|
|
flobnb: {
|
|
icon: `
|
|
<svg class="icon" width="64" height="56" viewBox="0 0 64 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.3261 19.7091C37.2823 19.3891 37.2604 19.0614 37.2604 18.7261C37.2604 17.4352 37.4871 16.2887 37.9405 15.2866C38.4101 14.2675 39.0416 13.4777 39.835 12.9172C40.6284 12.3397 41.519 12.051 42.5067 12.051C43.462 12.051 44.2797 12.3057 44.9598 12.8153C45.6399 13.3079 46.1499 13.9618 46.4899 14.7771C46.83 15.5924 47 16.4671 47 17.4013C47 18.6921 46.7652 19.8471 46.2956 20.8662C45.8261 21.8684 45.1865 22.6582 44.3769 23.2357C43.5835 23.7962 42.701 24.0764 41.7295 24.0764C40.7741 24.0764 39.9564 23.8302 39.2763 23.3376C38.8283 22.9937 38.454 22.5802 38.1535 22.0971C37.7343 22.518 37.2952 22.8891 36.8359 23.2102C36.0263 23.7877 35.1034 24.0764 34.0671 24.0764C32.966 24.0764 32.0835 23.6603 31.4197 22.828C30.7558 21.9788 30.3024 20.8323 30.0595 19.3885C29.509 19.5414 28.9261 19.6178 28.3108 19.6178C27.7764 19.6178 27.1854 19.5754 26.5377 19.4904C26.7644 20.5605 26.9506 21.758 27.0963 23.0828C27.2421 24.4076 27.3149 25.4947 27.3149 26.344C27.3149 28.1783 27.0316 29.5796 26.4649 30.5478C25.8981 31.5159 25.1533 32 24.2303 32C23.0645 32 22.2306 31.1338 21.7286 29.4013C21.2429 27.6688 21 25.0531 21 21.5541C21 18.293 21.3319 14.9894 21.9958 11.6433C22.6759 8.28026 23.5988 5.50318 24.7647 3.3121C25.9467 1.10403 27.2421 0 28.6508 0C29.4118 0 30.0028 0.356688 30.4238 1.07006C30.861 1.78344 31.0796 2.7431 31.0796 3.94905C31.0796 7.94055 28.8046 12.2887 24.2546 16.9936C24.0927 18.6582 24.0117 20.1189 24.0117 21.3758C24.0117 23.1253 24.0684 24.9172 24.1818 26.7516C24.2951 28.603 24.4813 29.5287 24.7404 29.5287C25.2262 29.5287 25.469 28.6454 25.469 26.879C25.469 25.5541 25.38 24.2718 25.2019 23.0318C25.0399 21.7919 24.8133 20.4841 24.5218 19.1083C24.5056 18.8875 24.5704 18.6072 24.7161 18.2675C24.878 17.9278 25.0723 17.6306 25.299 17.3758C25.5257 17.121 25.7281 16.9936 25.9062 16.9936C26.1167 16.9936 26.4487 17.0106 26.902 17.0446C27.4202 17.0786 27.8088 17.0955 28.0679 17.0955C28.6508 17.0955 29.2499 17.0106 29.8652 16.8408V16.7643C29.8652 14.6921 30.1162 12.3312 30.6181 9.68153C31.1363 7.03185 31.8892 4.75584 32.877 2.8535C33.8809 0.951168 35.071 0 36.4473 0C37.2246 0 37.8318 0.382166 38.2689 1.1465C38.7223 1.89384 38.949 2.9724 38.949 4.38217C38.949 5.75796 38.7061 7.3206 38.2204 9.07006C37.7346 10.8195 37.0302 12.5011 36.1073 14.1147C35.1843 15.7113 34.0995 16.9936 32.8527 17.9618C32.9174 19.1677 33.0875 20.0255 33.3627 20.535C33.638 21.0276 34.0428 21.2739 34.5771 21.2739C35.241 21.2739 35.8239 21.0786 36.3259 20.6879C36.614 20.4539 36.9474 20.1277 37.3261 19.7091ZM27.0478 4.07643C27.485 3.04034 27.8493 2.52229 28.1407 2.52229C28.5455 2.52229 28.748 3.04034 28.748 4.07643C28.7318 5.53716 28.3755 7.10828 27.6793 8.78981C26.983 10.4544 25.9872 12.0594 24.6918 13.6051C24.9995 11.7028 25.3638 9.90234 25.7848 8.20382C26.2058 6.48832 26.6268 5.11253 27.0478 4.07643ZM34.7229 4.43312C35.1762 3.15924 35.573 2.52229 35.913 2.52229C36.3664 2.52229 36.5931 3.16773 36.5931 4.4586C36.5931 5.49469 36.4554 6.65817 36.1802 7.94905C35.9211 9.22293 35.5082 10.4883 34.9415 11.7452C34.3747 12.9851 33.6866 14.0382 32.877 14.9045C32.9579 13.0531 33.1765 11.1592 33.5327 9.22293C33.889 7.28663 34.2857 5.69002 34.7229 4.43312ZM43.3811 20.3822C43.0086 21.0106 42.5472 21.3248 41.9966 21.3248C41.5109 21.3248 41.1061 21.0955 40.7822 20.6369C40.4584 20.1783 40.2964 19.465 40.2964 18.4968C40.2964 17.2739 40.4827 16.3567 40.8551 15.7452C41.2437 15.1168 41.7295 14.8025 42.3124 14.8025C42.7658 14.8025 43.1544 15.0403 43.4782 15.5159C43.802 15.9915 43.964 16.6964 43.964 17.6306C43.964 18.8365 43.7697 19.7537 43.3811 20.3822ZM23.5795 37.5556C23.0372 39.1867 22.6225 40.8605 22.3354 42.5769C20.9523 42.8675 19.7215 43.0456 18.6428 43.1111C18.3936 40.9185 17.6149 39.0815 16.3066 37.6C14.9984 36.0889 13.3007 35.2593 11.2137 35.1111C13.4565 32.4741 15.0607 29.9556 16.0263 27.5556C16.9919 25.1556 17.4747 22.7407 17.4747 20.3111C17.4747 18.4444 17.0386 16.9333 16.1665 15.7778C15.2943 14.5926 14.1418 14 12.7089 14C10.0612 14 7.77173 15.5852 5.84048 18.7556C3.94038 21.8963 2.49194 25.7185 1.49516 30.2222C0.498388 34.6963 0 38.8444 0 42.6667C0 46.9926 0.716432 50.2963 2.1493 52.5778C3.58216 54.8593 5.87163 56 9.0177 56C11.1047 56 13.0048 55.2444 14.718 53.7333C16.4624 52.2222 17.646 50.237 18.269 47.7778C19.5276 47.7279 20.7273 47.594 21.8682 47.3762C21.8565 47.76 21.8507 48.1457 21.8507 48.5333C21.8507 51.1407 22.0843 53.037 22.5516 54.2222C23.0499 55.4074 23.891 56 25.0746 56C26.0403 56 26.7411 55.763 27.1772 55.2889C27.6445 54.7852 28.0494 53.837 28.392 52.4444C28.8593 50.637 29.4667 48.9185 30.2143 47.2889C30.993 45.6593 31.7406 44.3556 32.457 43.3778C33.2046 42.3704 33.7809 41.8667 34.1858 41.8667C34.5284 41.8667 34.6998 42.1037 34.6998 42.5778C34.6998 43.2889 34.4973 44.5778 34.0924 46.4444C33.6563 48.5778 33.4382 50.0593 33.4382 50.8889C33.4382 52.5481 33.8432 53.8222 34.653 54.7111C35.4629 55.5704 36.6622 56 38.2508 56C40.2132 56 41.9575 55.4963 43.4838 54.4889C44.678 53.7165 45.808 52.7786 46.874 51.6753C47.0272 51.9921 47.1913 52.2929 47.3663 52.5778C48.7992 54.8593 51.0731 56 54.188 56C55.9012 56 57.5054 55.5111 59.0005 54.5333C60.4957 53.5259 61.695 52.1333 62.5983 50.3556C63.5328 48.5778 64 46.5778 64 44.3556C64 42.7556 63.6885 41.2741 63.0655 39.9111C62.4737 38.5481 61.6015 37.437 60.449 36.5778C59.3276 35.7185 58.0038 35.2296 56.4775 35.1111C58.7202 32.4741 60.3244 29.9556 61.29 27.5556C62.2556 25.1556 62.7385 22.7407 62.7385 20.3111C62.7385 18.4444 62.3024 16.9333 61.4302 15.7778C60.558 14.5926 59.4055 14 57.9726 14C55.3249 14 53.0355 15.5852 51.1042 18.7556C49.2041 21.8963 47.7557 25.7185 46.7589 30.2222C45.7621 34.6963 45.2637 38.8444 45.2637 42.6667C45.2637 44.3055 45.3643 45.7977 45.5655 47.1431C44.3991 48.4396 43.4404 49.4067 42.6895 50.0444C41.8485 50.7556 41.1165 51.1111 40.4935 51.1111C39.5902 51.1111 39.1385 50.5185 39.1385 49.3333C39.1385 48.5037 39.4344 46.8 40.0263 44.2222C40.7116 41.2 41.0542 39.1111 41.0542 37.9556C41.0542 36.0296 40.0419 35.0667 38.0172 35.0667C36.9892 35.0667 35.8523 35.4074 34.6063 36.0889C33.3915 36.7704 32.1455 37.9111 30.8684 39.5111C29.5913 41.1111 28.4388 43.1852 27.4108 45.7333C27.5354 44.4296 27.6912 43.3333 27.8781 42.4444C28.065 41.5556 28.3142 40.5185 28.6257 39.3333C29.0929 37.6741 29.3265 36.6074 29.3265 36.1333C29.3265 35.7185 29.2019 35.437 28.9527 35.2889C28.7035 35.1407 28.2674 35.0667 27.6445 35.0667C26.4919 35.0667 25.5886 35.2741 24.9345 35.6889C24.3115 36.0741 23.8598 36.6963 23.5795 37.5556ZM11.6342 18.4C11.0113 18.4 10.326 19.2889 9.57839 21.0667C8.86196 22.8148 8.19225 25.2 7.56926 28.2222C6.94628 31.2444 6.47904 34.5481 6.16755 38.1333C8.2234 34.5481 9.85873 31.3926 11.0736 28.6667C12.3195 25.9111 12.9425 23.4667 12.9425 21.3333C12.9425 20.3852 12.8179 19.6593 12.5687 19.1556C12.3507 18.6519 12.0392 18.4 11.6342 18.4ZM9.2046 51.2889C10.1702 51.2889 11.0268 50.9037 11.7744 50.1333C12.522 49.363 13.0204 48.2519 13.2696 46.8C12.3039 46.1778 11.5564 45.363 11.0268 44.3556C10.5284 43.3481 10.2792 42.2815 10.2792 41.1556C10.2792 40.7407 10.3415 40.1778 10.4661 39.4667H10.326C9.04885 39.4667 7.97421 40.0741 7.10203 41.2889C6.261 42.4741 5.84048 44.0741 5.84048 46.0889C5.84048 47.7778 6.16755 49.0667 6.82168 49.9556C7.50697 50.8444 8.30127 51.2889 9.2046 51.2889ZM56.3373 28.6667C55.1225 31.3926 53.4871 34.5481 51.4313 38.1333C51.7428 34.5481 52.21 31.2444 52.833 28.2222C53.456 25.2 54.1257 22.8148 54.8421 21.0667C55.5897 19.2889 56.275 18.4 56.898 18.4C57.3029 18.4 57.6144 18.6519 57.8325 19.1556C58.0816 19.6593 58.2062 20.3852 58.2062 21.3333C58.2062 23.4667 57.5833 25.9111 56.3373 28.6667ZM57.0381 49.5556C56.3217 50.6519 55.434 51.2 54.3749 51.2C53.4404 51.2 52.6617 50.8 52.0387 50C51.4157 49.1704 51.1042 47.8667 51.1042 46.0889C51.1042 44.0444 51.478 42.4889 52.2256 41.4222C52.9732 40.3556 53.8921 39.8222 54.9823 39.8222C55.8545 39.8222 56.6021 40.237 57.225 41.0667C57.848 41.8963 58.1595 43.1259 58.1595 44.7556C58.1595 46.8593 57.7857 48.4593 57.0381 49.5556Z"/>
|
|
</svg>
|
|
`
|
|
},
|
|
mining: {
|
|
icon: `<svg class="icon" width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.0861 14.6534C22.3924 14.3472 22.7898 14.1486 23.2186 14.0876L44.6946 11.0319C45.2676 10.9504 45.8455 11.1432 46.2547 11.5524C47.2381 12.5358 46.8168 14.2128 45.4853 14.6146L28.6869 19.6843C28.3711 19.7796 28.0838 19.9515 27.8505 20.1848L27.0092 21.0261L28.3236 22.3404C29.0306 23.0475 29.0976 24.1522 28.5245 24.9346L52.2562 48.6662C53.0372 49.4473 53.0372 50.7136 52.2562 51.4946L51.4947 52.2561C50.7137 53.0372 49.4473 53.0372 48.6663 52.2561L24.9346 28.5245C24.1522 29.0976 23.0475 29.0306 22.3404 28.3236L21.0261 27.0092L20.1848 27.8505C19.9515 28.0838 19.7796 28.3711 19.6843 28.6869L14.6146 45.4853C14.2128 46.8168 12.5358 47.2381 11.5524 46.2547C11.1432 45.8455 10.9504 45.2675 11.0319 44.6946L14.0876 23.2186C14.1486 22.7898 14.3472 22.3924 14.6534 22.0861L15.3949 21.3447C14.8777 20.6386 14.8818 19.67 15.4072 18.9681C14.8209 18.1848 14.8837 17.0693 15.5958 16.3572L16.3573 15.5958C17.0694 14.8837 18.1848 14.8208 18.9681 15.4072C19.6701 14.8818 20.6386 14.8777 21.3447 15.3949L22.0861 14.6534Z"/>
|
|
</svg>`
|
|
},
|
|
default: {
|
|
icon: `<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="M15 4c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zM3 12c0-2.61 1.67-4.83 4-5.65V4.26C3.55 5.15 1 8.27 1 12s2.55 6.85 6 7.74v-2.09c-2.33-.82-4-3.04-4-5.65z"/></svg>`
|
|
}
|
|
}
|
|
function getIcon(asset) {
|
|
return listedAssets.hasOwnProperty(asset) ? listedAssets[asset].icon : listedAssets.default.icon;
|
|
}
|
|
|
|
function formatAmount(amount = 0, shorten = false) {
|
|
return amount.toLocaleString(`en-IN`, { style: 'currency', currency: 'INR', maximumFractionDigits: shorten ? 2 : 8 })
|
|
}
|
|
// Convert milliseconds to time left in HH:MM:SS format
|
|
function getTimeLeft(milliseconds) {
|
|
const diff = milliseconds - Date.now()
|
|
const minutes = Math.floor((diff % 3600000) / 60000)
|
|
const seconds = Math.floor(((diff % 3600000) % 60000) / 1000)
|
|
return [`${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`, ((diff % 3600000) / 60000).toFixed(2)]
|
|
}
|
|
|
|
// remove digitals after specified decimal places without rounding
|
|
function toFixed(num, fixed = 8) {
|
|
const re = new RegExp(`^-?\\d+(?:\\.\\d{0,${fixed}})?`);
|
|
const match = num.toString().match(re);
|
|
if (!match) {
|
|
return NaN;
|
|
}
|
|
return parseFloat(match[0]);
|
|
}
|
|
|
|
|
|
function getSuggestedPrice(asset = pagesData.params.asset || 'FLO') {
|
|
return toFixed(parseFloat(floGlobals.exchangeRates[asset]) * deviation[tradeType])
|
|
}
|
|
|
|
function showSuggestedPrice(asset = pagesData.params.asset || 'FLO') {
|
|
renderElem(
|
|
getRef('get_price'),
|
|
html`
|
|
<div class="grid">
|
|
<div style="font-size: 0.8rem; font-weight: 500; margin-bottom: 0.3rem;">Current price</div>
|
|
<h3>${formatAmount(floGlobals.exchangeRates[asset])}</h3>
|
|
</div>
|
|
<div class="grid">
|
|
<div style="font-size: 0.8rem; font-weight: 500; margin-bottom: 0.3rem;">Valid ${tradeType === 'buy' ? 'upto' : 'until'}</div>
|
|
<h3>${formatAmount(getSuggestedPrice(asset))}</h3>
|
|
</div>
|
|
<div class="tooltip">
|
|
<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="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.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"/></svg>
|
|
<p class="banner">
|
|
${tradeType === 'buy' ?
|
|
html`<b>Valid upto</b> is the max price upto which your order can be executed, which is <b>${DEFAULT_TRADE_PRICE_DEVIATION_BUY * 100}%</b> higher than current price.` :
|
|
html`<b>Valid until</b> is the min price until which your order can be executed, which is <b>${DEFAULT_TRADE_PRICE_DEVIATION_SELL * 100}%</b> lower than current price.`
|
|
} System will always try to execute your order at the <b>nearest price possible to current price</b>.
|
|
</p>
|
|
</div>`
|
|
)
|
|
}
|
|
|
|
const chartDimensions = {
|
|
height: 0,
|
|
width: 0
|
|
}
|
|
floGlobals.countDowns = {
|
|
intervals: {},
|
|
timeouts: {}
|
|
}
|
|
// convert GMT to IST
|
|
function getIST(originalTime) {
|
|
const d = new Date(originalTime);
|
|
return Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds()) / 1000;
|
|
}
|
|
let marketTradesLazyLoader
|
|
let historyLazyLoader
|
|
let portfolioAssetHistoryLazyLoader
|
|
const render = {
|
|
listedAsset(asset, rate, countDown) {
|
|
const clone = getRef('listed_asset_template').content.cloneNode(true).firstElementChild
|
|
clone.firstElementChild.href = `#/exchange?asset=${asset}`
|
|
clone.firstElementChild.dataset.listedAsset = asset
|
|
clone.querySelector('.listed-asset__icon').innerHTML = getIcon(asset)
|
|
clone.querySelector('.listed-asset__rate').textContent = formatAmount(rate)
|
|
clone.querySelector('.listed-asset__name').textContent = asset
|
|
floGlobals.countDowns.intervals[asset] = setInterval(() => {
|
|
const [timeLeft, minutes] = getTimeLeft(countDown)
|
|
clone.querySelector('.listed-asset__countdown').firstChild.textContent = timeLeft;
|
|
const pathLength = 63 - Math.ceil((minutes / 60) * 63)
|
|
clone.querySelector('.listed-asset__countdown').style.setProperty('--path-length', pathLength)
|
|
}, 1000);
|
|
floGlobals.countDowns.timeouts[asset] = setTimeout(() => {
|
|
updateRate().catch(console.error)
|
|
delete floGlobals.countDowns.timeouts[asset]
|
|
}, countDown - Date.now());
|
|
return clone
|
|
},
|
|
orderCard(orderDetails = {}) {
|
|
const { id, asset, quantity, price, time, type } = orderDetails
|
|
const card = getRef('order_template').content.cloneNode(true).firstElementChild
|
|
card.dataset.id = id
|
|
card.dataset.type = type
|
|
card.querySelector('.order-card__type').textContent = type
|
|
card.querySelector('.order-card__quantity').textContent = `${quantity} ${asset}`
|
|
card.querySelector('.order-card__price-type').textContent = type === 'buy' ? 'Valid upto' : 'Valid until'
|
|
card.querySelector('.order-card__price').textContent = formatAmount(price)
|
|
card.querySelector('.order-card__amount-type').textContent = type === 'buy' ? 'Locked amount' : 'Min amount'
|
|
card.querySelector('.order-card__amount').textContent = formatAmount(price * quantity, true)
|
|
card.querySelector('.order-card__time').textContent = relativeTime.from(new Date(time).getTime())
|
|
return card
|
|
},
|
|
transactionCard(transactionDetails = {}) {
|
|
const { buyer, seller, type, other, asset, quantity, unitValue, time } = transactionDetails
|
|
const card = getRef('transaction_template').content.cloneNode(true).firstElementChild
|
|
card.dataset.type = type
|
|
card.querySelector('.completed-trade__type').textContent = type
|
|
card.querySelector('.completed-trade__quantity').textContent = `${quantity} ${asset}`
|
|
card.querySelector('.completed-trade__time').textContent = getFormattedTime(time)
|
|
card.querySelector('.completed-trade__price').textContent = formatAmount(unitValue)
|
|
card.querySelector('.completed-trade__total').textContent = formatAmount(parseFloat(unitValue * quantity), true)
|
|
card.querySelector('.more-info').dataset.buyer = buyer
|
|
card.querySelector('.more-info').dataset.seller = seller
|
|
card.querySelector('.more-info').dataset.other = other
|
|
return card
|
|
},
|
|
marketOrderCard(orderDetails = {}) {
|
|
const { floID, asset, quantity, minPrice, maxPrice, time_placed, type } = orderDetails
|
|
const card = getRef('market_order_template').content.cloneNode(true).firstElementChild
|
|
const unitValue = minPrice || maxPrice;
|
|
card.dataset.type = type
|
|
card.classList.add(`transaction-card--${type}`)
|
|
card.querySelector('.transaction-card__quantity').textContent = `${quantity} ${asset}`
|
|
card.querySelector('.transaction-card__price').textContent = formatAmount(unitValue)
|
|
card.querySelector('.transaction-card__total').textContent = formatAmount(parseFloat(unitValue * quantity), true)
|
|
card.querySelector('.more-info').dataset.time = time_placed
|
|
card.querySelector('.more-info').dataset.buyer = type === 'buy' ? floID : undefined
|
|
card.querySelector('.more-info').dataset.seller = type === 'sell' ? floID : undefined
|
|
card.querySelector('.more-info').dataset.pending = true
|
|
return card
|
|
},
|
|
marketTransactionCard(transactionDetails = {}) {
|
|
const { buyer, seller, asset, quantity, unitValue, time } = transactionDetails
|
|
const card = getRef('market_transaction_template').content.cloneNode(true).firstElementChild
|
|
card.querySelector('.completed-trade__quantity').textContent = `${quantity} ${asset}`
|
|
card.querySelector('.completed-trade__time').textContent = getFormattedTime(time)
|
|
card.querySelector('.completed-trade__price').textContent = `₹${unitValue}`
|
|
card.querySelector('.completed-trade__total').textContent = formatAmount(parseFloat(unitValue * quantity), true)
|
|
card.querySelector('.more-info').dataset.buyer = buyer
|
|
card.querySelector('.more-info').dataset.seller = seller
|
|
return card
|
|
},
|
|
assetBalanceCard(asset) {
|
|
const { net, locked } = allTokens[asset]
|
|
const card = getRef('asset_balance_card_template').content.cloneNode(true).firstElementChild
|
|
card.href = `#/portfolio?asset=${asset}`
|
|
card.className = `balance-card interact ${locked ? 'is-locked' : ''}`
|
|
card.querySelector('.balance-card__token').textContent = asset
|
|
card.querySelector('.balance-card__icon').innerHTML = getIcon(asset)
|
|
const templateToClone = locked ? 'locked_balance_template' : 'net_balance_template';
|
|
const assetBalance = getRef(templateToClone).content.cloneNode(true)
|
|
const availableBalance = asset === 'rupee' ? formatAmount(net, true) : `${toFixed(net)} | <span style="font-size: 0.8rem">₹${toFixed(net * floGlobals.exchangeRates[asset], 2)}</span>`
|
|
const lockedBalance = asset === 'rupee' ? formatAmount(locked, true) : toFixed(locked)
|
|
assetBalance.querySelector('.available-balance').innerHTML = availableBalance
|
|
if (locked) {
|
|
assetBalance.querySelector('.locked-balance').textContent = lockedBalance
|
|
}
|
|
card.appendChild(assetBalance)
|
|
return card
|
|
},
|
|
chart(asset = 'FLO') {
|
|
if (pagesData.currentPage !== 'exchange') return
|
|
const duration = getRef('price_duration_selector').value === 'hour' ? '48 weeks' : 'all-time' || '48 weeks'
|
|
floExchangeAPI.getRateHistory(asset, duration).then(data => {
|
|
data = data.map(({ time, rate }) => {
|
|
return {
|
|
time: getIST(time),
|
|
value: rate
|
|
}
|
|
})
|
|
if (lineSeries)
|
|
lineSeries.setData(data);
|
|
new ResizeObserver(entries => {
|
|
chartDimensions.height = entries[0].contentRect.height - 10;
|
|
chartDimensions.width = entries[0].contentRect.width;
|
|
if (chart) {
|
|
if (chartDimensions.width <= 0 || chartDimensions.height <= 0) return;
|
|
chart.applyOptions({ width: chartDimensions.width, height: chartDimensions.height });
|
|
}
|
|
}).observe(getRef('price_chart_wrapper'));
|
|
})
|
|
},
|
|
userOrders() {
|
|
let { buyOrders, sellOrders, transactions } = accountDetails
|
|
getRef('orders_list').innerHTML = '';
|
|
const frag = document.createDocumentFragment()
|
|
const ordersType = getRef('my_orders_category_selector').value
|
|
if (ordersType === 'open') {
|
|
getRef('my_orders__title').textContent = 'My orders'
|
|
const allOpenOrders = [...buyOrders, ...sellOrders].sort((a, b) => new Date(b.time_placed).getTime() - new Date(a.time_placed).getTime())
|
|
allOpenOrders.forEach(order => {
|
|
const { id, asset, quantity, minPrice = undefined, maxPrice = undefined, time_placed } = order
|
|
const orderDetails = {
|
|
id,
|
|
asset,
|
|
quantity,
|
|
type: minPrice ? 'sell' : 'buy',
|
|
price: minPrice || maxPrice,
|
|
time: time_placed
|
|
}
|
|
frag.append(render.orderCard(orderDetails))
|
|
})
|
|
} else {
|
|
getRef('my_orders__title').textContent = 'My trades'
|
|
transactions.sort((a, b) => new Date(b.tx_time).getTime() - new Date(a.tx_time).getTime()).forEach(transaction => {
|
|
const { asset, quantity, unitValue, tx_time, buyer, seller } = transaction
|
|
let type, other;
|
|
if (seller === proxy.userID) {
|
|
type = 'Sold';
|
|
other = buyer === proxy.userID ? 'MySelf' : buyer;
|
|
} else if (buyer === proxy.userID) {
|
|
type = 'Bought';
|
|
other = seller;
|
|
} else
|
|
return;
|
|
const transactionDetails = {
|
|
buyer,
|
|
seller,
|
|
type,
|
|
other,
|
|
asset,
|
|
quantity,
|
|
unitValue,
|
|
time: tx_time
|
|
}
|
|
frag.append(render.transactionCard(transactionDetails))
|
|
});
|
|
}
|
|
getRef('orders_list').append(frag)
|
|
},
|
|
marketOrders() {
|
|
const frag = document.createDocumentFragment()
|
|
const ordersType = getRef('market_orders_category_selector').value
|
|
if (ordersType === 'open') {
|
|
getRef('market_orders__title').textContent = 'Market-wide orders';
|
|
showChildElement('market_orders_wrapper', 0, { entry: slideInRight, exit: slideOutRight })
|
|
getRef('market_buy_orders').innerHTML = '<sm-spinner></sm-spinner>';
|
|
floExchangeAPI.getBuyList().then(buyOrders => {
|
|
getRef('market_buy_orders').innerHTML = '';
|
|
buyOrders.forEach(order => {
|
|
frag.append(render.marketOrderCard({
|
|
...order,
|
|
type: 'buy'
|
|
}))
|
|
})
|
|
getRef('market_buy_orders').append(frag)
|
|
}).catch(err => {
|
|
console.log(err)
|
|
})
|
|
getRef('market_sell_orders').innerHTML = '<sm-spinner></sm-spinner>';
|
|
floExchangeAPI.getSellList().then(sellOrders => {
|
|
getRef('market_sell_orders').innerHTML = '';
|
|
sellOrders.forEach(order => {
|
|
frag.append(render.marketOrderCard({
|
|
...order,
|
|
type: 'sell'
|
|
}))
|
|
})
|
|
getRef('market_sell_orders').append(frag)
|
|
}).catch(err => {
|
|
console.log(err)
|
|
})
|
|
} else {
|
|
getRef('market_orders__title').textContent = 'Market-wide trades';
|
|
showChildElement('market_orders_wrapper', 1, { entry: slideInLeft, exit: slideOutLeft })
|
|
getRef('market_trades').innerHTML = '<sm-spinner></sm-spinner>';
|
|
floExchangeAPI.getTradeList().then(trades => {
|
|
const allTrades = trades.map(transaction => {
|
|
const { seller, buyer, asset, quantity, unitValue, tx_time } = transaction
|
|
return {
|
|
buyer,
|
|
seller,
|
|
asset,
|
|
quantity,
|
|
unitValue,
|
|
time: tx_time
|
|
}
|
|
})
|
|
if (marketTradesLazyLoader) {
|
|
marketTradesLazyLoader.update(allTrades)
|
|
} else {
|
|
marketTradesLazyLoader = new LazyLoader('#market_trades', allTrades, render.marketTransactionCard)
|
|
}
|
|
marketTradesLazyLoader.init()
|
|
}).catch(err => {
|
|
console.log(err)
|
|
})
|
|
}
|
|
},
|
|
historyEntry(details) {
|
|
const { amount, r_status, asset, txid, mode, locktime } = details
|
|
const type = mode === 1 ? 'Deposit' : 'Withdraw'
|
|
const entry = getRef('history_entry').content.cloneNode(true).firstElementChild
|
|
let status
|
|
if (r_status === 0) {
|
|
status = 'PENDING'
|
|
} else if (r_status === 100) {
|
|
status = 'SUCCESS'
|
|
} else if (r_status === -1) {
|
|
status = 'FAILED'
|
|
}
|
|
entry.dataset.status = status
|
|
entry.dataset.type = type
|
|
if (status === 'SUCCESS') {
|
|
entry.querySelector('.history-entry__status').innerHTML = `<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="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>`
|
|
} else {
|
|
entry.querySelector('.history-entry__status').innerHTML = `<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" x="0"/></g><g><g><path d="M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-4.5,10-10S17.5,2,12,2z M12,20c-4.41,0-8-3.59-8-8s3.59-8,8-8s8,3.59,8,8 S16.41,20,12,20z M12.5,7H11v6l5.2,3.2l0.8-1.3l-4.5-2.7V7z"/></g></g></svg>`
|
|
}
|
|
entry.querySelector('.history-entry__token-action').textContent = `${type} ${asset}`
|
|
entry.querySelector('.history-entry__amount').textContent = amount
|
|
entry.querySelector('.history-entry__time').textContent = getFormattedTime(locktime)
|
|
entry.querySelector('.history-entry__txid').href = floBlockchainAPI.current_server + `tx/${txid}`
|
|
return entry;
|
|
}
|
|
}
|
|
|
|
const chartObserver = new IntersectionObserver(entries => {
|
|
if (entries[0].isIntersecting && chart) {
|
|
const { width, height } = entries[0].target.getBoundingClientRect()
|
|
if (width <= 0 || height <= 0 || chartDimensions.height === height - 10 && chartDimensions.width === width) return
|
|
chartDimensions.height = height - 10;
|
|
chartDimensions.width = width;
|
|
chart.applyOptions({ width: chartDimensions.width, height: chartDimensions.height });
|
|
}
|
|
})
|
|
|
|
let chart
|
|
let lineSeries
|
|
function getChartTheme() {
|
|
const theme = getRef('theme_toggle').value
|
|
const textColor = window.getComputedStyle(document.body).getPropertyValue('--text-color')
|
|
const accentColor = theme === 'dark' ? '#a4b3ff' : '#516beb'
|
|
return {
|
|
chart: {
|
|
layout: {
|
|
background: {
|
|
type: 'solid',
|
|
color: `rgba(${textColor}, 0.01)`
|
|
},
|
|
lineColor: `rgba(${textColor}, 0.1)`,
|
|
textColor: `rgba(${textColor}, 0.8)`,
|
|
},
|
|
watermark: {
|
|
color: 'rgba(0, 0, 0, 0)',
|
|
},
|
|
crosshair: {
|
|
color: '#758696',
|
|
},
|
|
grid: {
|
|
vertLines: {
|
|
color: `rgba(${textColor}, 0.2)`,
|
|
},
|
|
horzLines: {
|
|
color: `rgba(${textColor}, 0.2)`,
|
|
},
|
|
},
|
|
},
|
|
series: {
|
|
color: accentColor
|
|
}
|
|
}
|
|
}
|
|
function setChartTheme(e) {
|
|
if (chart) {
|
|
const themeToApply = getChartTheme()
|
|
chart.applyOptions(themeToApply.chart);
|
|
lineSeries.applyOptions(themeToApply.series);
|
|
}
|
|
}
|
|
|
|
document.addEventListener('themechange', setChartTheme)
|
|
|
|
function updateTooltip(asset, tradeType) {
|
|
getRef('quantity_selector').querySelectorAll('.button').forEach(button => {
|
|
if (tradeType === 'buy') {
|
|
button.title = `Buy ${asset} worth ${button.textContent} of total rupee`
|
|
} else {
|
|
button.title = `Sell ${button.textContent} of total ${asset}`
|
|
}
|
|
})
|
|
getRef('quantity_selector_tip').textContent = `${tradeType} ${asset} worth of the above percentage`
|
|
}
|
|
|
|
getRef('asset_page__footer').addEventListener('click', e => {
|
|
if (e.target.closest('button')) {
|
|
const button = e.target.closest('button');
|
|
[...getRef('asset_page__footer').children].forEach(child => {
|
|
child.classList.remove('active')
|
|
});
|
|
button.classList.add('active')
|
|
showChildElement('asset_page_wrapper', button.value === 'trade' ? 1 : 0, { mobileView: true })
|
|
}
|
|
})
|
|
|
|
let tradeType = 'buy'
|
|
getRef('trade_type_selector').addEventListener('change', e => {
|
|
tradeType = e.target.value
|
|
const selectedAsset = pagesData.params.asset
|
|
showSuggestedPrice()
|
|
getRef('get_total').setAttribute('placeholder', tradeType === 'buy' ? `Locked amount (₹)` : `Min amount received (₹)`)
|
|
getRef('trade_button').textContent = `${tradeType} ${selectedAsset}`
|
|
getRef('quantity_type').textContent = tradeType === 'buy' ? formatAmount(allTokens.rupee.net, true) : `${parseFloat(allTokens[selectedAsset].net.toFixed(4))} ${selectedAsset}`
|
|
updateTooltip(selectedAsset, tradeType)
|
|
})
|
|
getRef('price_duration_selector').addEventListener('change', e => {
|
|
render.chart(pagesData.params.asset)
|
|
})
|
|
async function tradeAsset() {
|
|
closePopup()
|
|
const asset = pagesData.params.asset;
|
|
const quantity = parseFloat(getRef('get_quantity').value)
|
|
const price = getSuggestedPrice()
|
|
if (tradeType === 'buy' && accountDetails.sellOrders.some(order => order.asset === asset)) {
|
|
closePopup()
|
|
return notify('You have an open sell order for this asset. Please close it before buying.', 'error')
|
|
} else if (tradeType === 'sell' && accountDetails.buyOrders.some(order => order.asset === asset)) {
|
|
closePopup()
|
|
return notify('You have an open buy order for this asset. Please close it before selling.', 'error')
|
|
}
|
|
buttonLoader('trade_button', true)
|
|
try {
|
|
if (tradeType === 'buy') {
|
|
await floExchangeAPI.buy(asset, quantity, price, proxy.userID, await proxy.secret)
|
|
} else {
|
|
await floExchangeAPI.sell(asset, quantity, price, proxy.userID, await proxy.secret)
|
|
}
|
|
getRef('trade_button').append(getRef('success_template').content.cloneNode(true))
|
|
notify(`Placed ${tradeType} order`, 'success')
|
|
refresh()
|
|
}
|
|
catch (err) {
|
|
getRef('trade_button').append(getRef('failure_template').content.cloneNode(true))
|
|
if (err.message === 'Insufficient rupee') {
|
|
notify(`Insufficient balance to ${tradeType} ${asset}. Please deposit rupee to portfolio first.`, 'error', { pinned: true })
|
|
} else {
|
|
notify(err.message, 'error')
|
|
}
|
|
}
|
|
finally {
|
|
updateRate()
|
|
getRef('get_quantity').value = 0
|
|
getRef('get_total').value = 0
|
|
setTimeout(() => {
|
|
buttonLoader('trade_button', false)
|
|
getRef('trade_button').disabled = true
|
|
setTimeout(() => {
|
|
getRef('trade_button').querySelector('.stateful-result').remove()
|
|
}, 100);
|
|
}, 4000);
|
|
}
|
|
}
|
|
getRef('quantity_selector').addEventListener('click', e => {
|
|
if (e.target.closest('button')) {
|
|
const selectedAsset = pagesData.params.asset
|
|
const target = e.target.closest('button')
|
|
const unitValue = getSuggestedPrice()
|
|
const fraction = parseFloat(target.value)
|
|
if (tradeType === 'buy') {
|
|
getRef('get_total').value = toFixed(fraction * allTokens['rupee'].net)
|
|
getRef('get_quantity').value = toFixed((allTokens['rupee'].net * fraction) / getSuggestedPrice())
|
|
} else {
|
|
getRef('get_total').value = toFixed(fraction * allTokens[selectedAsset].net * getSuggestedPrice())
|
|
getRef('get_quantity').value = toFixed(allTokens[selectedAsset].net * fraction)
|
|
|
|
}
|
|
}
|
|
})
|
|
getRef('get_quantity').addEventListener('focusin', e => {
|
|
if (tradeType === 'buy') {
|
|
getRef('quantity_selector_tip').innerHTML = `<b>Locked amount</b> is highest total amount you'll be charged for buying specified quantity, most likely you'll charged less than locked amount.`
|
|
} else {
|
|
getRef('quantity_selector_tip').innerHTML = `<b>Min amount</b> received is lowest total amount you'll get by selling specified quantity, most likely you will get more than this amount. `
|
|
}
|
|
})
|
|
getRef('get_total').addEventListener('focusin', e => {
|
|
if (tradeType === 'buy') {
|
|
getRef('quantity_selector_tip').innerHTML = `<b>Locked amount</b> is highest total amount you'll be charged for buying specified quantity, most likely you'll charged less than locked amount.`
|
|
} else {
|
|
getRef('quantity_selector_tip').innerHTML = `<b>Min amount</b> received is lowest total amount you'll get by selling specified quantity, most likely you will get more than this amount. `
|
|
}
|
|
})
|
|
getRef('get_quantity').addEventListener('keyup', e => {
|
|
const unitValue = getSuggestedPrice()
|
|
getRef('get_total').value = toFixed(parseFloat(e.target.value) * unitValue)
|
|
})
|
|
getRef('get_total').addEventListener('keyup', e => {
|
|
const unitValue = getSuggestedPrice()
|
|
getRef('get_quantity').value = toFixed(parseFloat(e.target.value) / unitValue)
|
|
})
|
|
|
|
getRef('portfolio_pages_selector').addEventListener('change', e => {
|
|
if (e.target.value === 'exchange') {
|
|
showChildElement('portfolio_pages_wrapper', 0, { entry: slideInRight, exit: slideOutRight })
|
|
} else {
|
|
showChildElement('portfolio_pages_wrapper', 1, { entry: slideInLeft, exit: slideOutLeft })
|
|
}
|
|
})
|
|
|
|
getRef('portfolio_asset__actions').addEventListener('click', e => {
|
|
if (e.target.closest('.button')) {
|
|
const target = e.target.closest('.button')
|
|
const type = target.value
|
|
const asset = pagesData.params.asset
|
|
|
|
getRef('portfolio_quantity_type').textContent = getRef('quantity_type').textContent = asset === 'rupee' ? formatAmount(allTokens.rupee.net, true) : `${toFixed(allTokens[asset].net)} ${asset}`
|
|
getRef('portfolio_popup__cta').textContent = `${type} ${asset}`
|
|
getRef('portfolio_popup__cta').setAttribute('value', type)
|
|
getRef('portfolio_popup__title').textContent = `${type} ${asset}`
|
|
// default form element state
|
|
getRef('get_receiver_id').removeAttribute('required')
|
|
getRef('get_receiver_id').classList.add('hidden')
|
|
getRef('get_private_key').removeAttribute('required')
|
|
getRef('get_private_key').classList.add('hidden')
|
|
getRef('portfolio_quantity_selector').classList.remove('hidden')
|
|
switch (type) {
|
|
case 'withdraw':
|
|
if (asset === 'FLO') {
|
|
getRef('withdraw_flo_tip').classList.remove('hidden')
|
|
}
|
|
break;
|
|
case 'deposit':
|
|
getRef('get_private_key').setAttribute('required', '')
|
|
getRef('get_private_key').classList.remove('hidden')
|
|
getRef('portfolio_quantity_selector').classList.add('hidden')
|
|
break;
|
|
case 'transfer':
|
|
getRef('get_receiver_id').setAttribute('required', '')
|
|
getRef('get_receiver_id').classList.remove('hidden')
|
|
break;
|
|
}
|
|
getRef('portfolio_form').elementsChanged()
|
|
openPopup('portfolio_popup')
|
|
}
|
|
})
|
|
function showWalletResult(status, title, description, cta = '') {
|
|
const animOptions = {
|
|
duration: 150,
|
|
easing: 'ease',
|
|
fill: 'forwards'
|
|
}
|
|
let icon
|
|
if (status === 'success') {
|
|
icon = `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon--success" 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>`
|
|
} else {
|
|
icon = `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon--failure" height="24px"
|
|
viewBox="0 0 24 24" width="24px" fill="#000000">
|
|
<path
|
|
d="M11 15h2v2h-2v-2zm0-8h2v6h-2V7zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" />
|
|
</svg>`
|
|
}
|
|
getRef('portfolio_result__icon').innerHTML = icon
|
|
getRef('portfolio_result__title').textContent = title
|
|
getRef('portfolio_result__description').textContent = description
|
|
getRef('portfolio_form').animate(slideOutLeft, animOptions)
|
|
.onfinish = () => {
|
|
getRef('portfolio_form').classList.add('hidden')
|
|
getRef('portfolio_result').classList.remove('hidden')
|
|
getRef('portfolio_result').animate(slideInLeft, animOptions)
|
|
}
|
|
getRef('portfolio_result__cta').classList.add('hidden')
|
|
switch (cta) {
|
|
case 'open-upi':
|
|
getRef('portfolio_result__cta').classList.remove('hidden')
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
function hideWalletResult() {
|
|
const animOptions = {
|
|
duration: 150,
|
|
easing: 'ease',
|
|
fill: 'forwards'
|
|
}
|
|
getRef('portfolio_result').animate(slideOutRight, animOptions)
|
|
.onfinish = () => {
|
|
getRef('portfolio_result').classList.add('hidden')
|
|
getRef('portfolio_form').classList.remove('hidden')
|
|
getRef('portfolio_form').animate(slideInRight, animOptions)
|
|
}
|
|
}
|
|
getRef('portfolio_quantity_selector').addEventListener('click', e => {
|
|
// Get latest balance and exchange rate
|
|
if (e.target.closest('button')) {
|
|
const target = e.target.closest('button')
|
|
const asset = pagesData.params.asset
|
|
const fraction = parseFloat(target.value)
|
|
let total = toFixed(allTokens[asset].net * fraction);
|
|
if (asset == 'FLO' && fraction === 1 & total > 0.1) {
|
|
total -= 0.1;
|
|
}
|
|
getRef('get_user_amount').value = toFixed(total)
|
|
}
|
|
})
|
|
getRef('portfolio_popup__cta').addEventListener('click', async e => {
|
|
const asset = pagesData.params.asset
|
|
const type = e.target.getAttribute('value')
|
|
const quantity = parseFloat(getRef('get_user_amount').value)
|
|
try {
|
|
buttonLoader('portfolio_popup__cta', true)
|
|
const proxySecret = await proxy.secret;
|
|
let response
|
|
switch (type) {
|
|
case 'deposit':
|
|
const privKey = getRef('get_private_key').value;
|
|
if (asset === 'FLO') {
|
|
response = await floExchangeAPI.depositFLO(quantity, proxy.userID, proxy.sinkID, privKey, proxySecret)
|
|
} else {
|
|
response = await floExchangeAPI.depositToken(asset, quantity, proxy.userID, proxy.sinkID, privKey, proxySecret)
|
|
}
|
|
console.log(response)
|
|
showWalletResult('success', `Sent ${asset} deposit request`, 'This may take upto 30 mins to reflect in your portfolio.')
|
|
break;
|
|
case 'withdraw':
|
|
if (asset === 'FLO') {
|
|
response = await floExchangeAPI.withdrawFLO(quantity, proxy.userID, proxySecret)
|
|
} else {
|
|
response = await floExchangeAPI.withdrawToken(asset, quantity, proxy.userID, proxySecret)
|
|
}
|
|
console.log(response)
|
|
showWalletResult('success', `Sent ${asset} withdraw request`, 'This may take upto 30 mins to reflect in your portfolio.')
|
|
break;
|
|
case 'transfer':
|
|
const receiverID = getRef('get_receiver_id').value.trim();
|
|
const receiver = {
|
|
[receiverID]: quantity
|
|
}
|
|
response = await floExchangeAPI.transferToken(receiver, asset, proxy.userID, proxySecret)
|
|
console.log(response)
|
|
showWalletResult('success', `Sent ${quantity} ${asset} to ${receiverID}`, `This may take upto 30 mins to reflect in receiver's portfolio.`)
|
|
break;
|
|
}
|
|
}
|
|
catch (err) {
|
|
if ((err.message || err) === 'Insufficient rupee balance') {
|
|
showWalletResult('error', `Failed`, err.message || err, 'open-upi')
|
|
} else {
|
|
showWalletResult('error', `Failed`, err.message || err)
|
|
}
|
|
}
|
|
finally {
|
|
buttonLoader('portfolio_popup__cta', false)
|
|
refresh()
|
|
}
|
|
})
|
|
|
|
const slideInLeft = [
|
|
{
|
|
opacity: 0,
|
|
transform: 'translateX(1rem)'
|
|
},
|
|
{
|
|
opacity: 1,
|
|
transform: 'translateX(0)'
|
|
}
|
|
]
|
|
const slideOutLeft = [
|
|
{
|
|
opacity: 1,
|
|
transform: 'translateX(0)'
|
|
},
|
|
{
|
|
opacity: 0,
|
|
transform: 'translateX(-1rem)'
|
|
},
|
|
]
|
|
const slideInRight = [
|
|
{
|
|
opacity: 0,
|
|
transform: 'translateX(-1rem)'
|
|
},
|
|
{
|
|
opacity: 1,
|
|
transform: 'translateX(0)'
|
|
}
|
|
]
|
|
const slideOutRight = [
|
|
{
|
|
opacity: 1,
|
|
transform: 'translateX(0)'
|
|
},
|
|
{
|
|
opacity: 0,
|
|
transform: 'translateX(1rem)'
|
|
},
|
|
]
|
|
const slideInDown = [
|
|
{
|
|
opacity: 0,
|
|
transform: 'translateY(-1rem)'
|
|
},
|
|
{
|
|
opacity: 1,
|
|
transform: 'translateY(0)'
|
|
},
|
|
]
|
|
const slideOutUp = [
|
|
{
|
|
opacity: 1,
|
|
transform: 'translateY(0)'
|
|
},
|
|
{
|
|
opacity: 0,
|
|
transform: 'translateY(-1rem)'
|
|
},
|
|
]
|
|
|
|
const selectedOrders = new Map()
|
|
getRef('orders_list').addEventListener('change', e => {
|
|
const animOptions = {
|
|
duration: 150,
|
|
easing: 'ease',
|
|
fill: 'forwards'
|
|
}
|
|
const target = e.target.parentNode;
|
|
target.classList.toggle('order-card--selected')
|
|
if (e.target.checked) {
|
|
selectedOrders.set(target.dataset.id, target.dataset.type)
|
|
} else {
|
|
selectedOrders.delete(target.dataset.id)
|
|
}
|
|
getRef('selected_orders').textContent = `${selectedOrders.size} selected`
|
|
if (selectedOrders.size === 1 && !getRef('my_orders_section__header').children[0].classList.contains('hidden')) {
|
|
getRef('my_orders_section__header').children[0].animate(slideOutLeft, animOptions)
|
|
.onfinish = () => {
|
|
getRef('my_orders_section__header').children[0].classList.add('hidden')
|
|
getRef('my_orders_section__header').children[1].classList.remove('hidden')
|
|
getRef('my_orders_section__header').children[1].animate(slideInLeft, animOptions)
|
|
}
|
|
} else if (selectedOrders.size === 0 && getRef('my_orders_section__header').children[0].classList.contains('hidden')) {
|
|
hideMyOrdersOptions()
|
|
}
|
|
})
|
|
function hideMyOrdersOptions() {
|
|
const animOptions = {
|
|
duration: 150,
|
|
easing: 'ease',
|
|
fill: 'forwards'
|
|
}
|
|
getRef('my_orders_section__header').children[1].animate(slideOutRight, animOptions)
|
|
.onfinish = () => {
|
|
getRef('my_orders_section__header').children[1].classList.add('hidden')
|
|
getRef('my_orders_section__header').children[0].classList.remove('hidden')
|
|
getRef('my_orders_section__header').children[0].animate(slideInRight, animOptions)
|
|
}
|
|
}
|
|
function clearSelection() {
|
|
getRef('orders_list').querySelectorAll('sm-checkbox[checked]').forEach(elem => elem.checked = false)
|
|
}
|
|
|
|
function showMoreDetails(e) {
|
|
const target = e.target.closest('.more-info')
|
|
openPopup('transaction_info_popup')
|
|
renderElem(getRef('transaction_details'), html`
|
|
${target.dataset.buyer !== 'undefined' ? html`<div class="grid">
|
|
<h5 class="label capitalize">Buyer</h5>
|
|
<sm-copy value="${target.dataset.buyer}"></sm-copy>
|
|
</div> `: ''
|
|
}
|
|
${target.dataset.seller !== 'undefined' ? html`<div class="grid">
|
|
<h5 class="label capitalize">Seller</h5>
|
|
<sm-copy value="${target.dataset.seller}"></sm-copy>
|
|
</div> `: ''
|
|
}
|
|
${target.dataset.time !== undefined ? html`<div class="grid">
|
|
<h5 class="label">${target.dataset.pending === 'true' ? 'Placed on' : 'Completed on'}</h5>
|
|
<time style="font-size: 0.9rem;">${getFormattedTime(target.dataset.time)}</time>
|
|
</div>`: ''
|
|
}
|
|
`)
|
|
}
|
|
|
|
getRef('orders_list').addEventListener('click', e => {
|
|
if (e.target.closest('.cancel-order')) {
|
|
getConfirmation('Cancel this order?').then(async res => {
|
|
if (res) {
|
|
const target = e.target.closest('.order-card')
|
|
const id = target.dataset.id
|
|
const type = target.dataset.type
|
|
floExchangeAPI.cancelOrder(type, id, proxy.userID, await proxy.secret)
|
|
.then(() => {
|
|
notify('Order cancelled', 'success')
|
|
target.animate([
|
|
{
|
|
opacity: 1
|
|
},
|
|
{
|
|
opacity: 0
|
|
},
|
|
], {
|
|
duration: 100,
|
|
fill: 'forwards'
|
|
})
|
|
.onfinish = () => {
|
|
if (!target.parentNode) return
|
|
const height = target.getBoundingClientRect().height
|
|
const siblings = Array.from(target.parentNode.children)
|
|
const nextSiblings = siblings.slice(siblings.indexOf(target) + 1)
|
|
nextSiblings.forEach(elem => {
|
|
elem.animate([
|
|
{
|
|
transform: 'translateY(0)'
|
|
},
|
|
{
|
|
transform: `translateY(-${height}px)`
|
|
},
|
|
], {
|
|
duration: 200,
|
|
fill: 'forwards'
|
|
})
|
|
.onfinish = (e) => {
|
|
e.target.cancel()
|
|
}
|
|
})
|
|
setTimeout(() => {
|
|
target.remove()
|
|
refresh()
|
|
}, 200);
|
|
}
|
|
})
|
|
.catch(err => notify(err.message, 'error'))
|
|
}
|
|
})
|
|
} else if (e.target.closest('.more-info')) {
|
|
showMoreDetails(e)
|
|
}
|
|
})
|
|
function cancelAll() {
|
|
getConfirmation('Cancel all selected orders?').then(async res => {
|
|
if (res) {
|
|
try {
|
|
const proxy_secret = await proxy.secret;
|
|
const promises = [...selectedOrders].map(([id, type]) => floExchangeAPI.cancelOrder(type, id, proxy.userID, proxy_secret))
|
|
await Promise.all(promises)
|
|
selectedOrders.clear()
|
|
hideMyOrdersOptions()
|
|
}
|
|
catch (err) {
|
|
notify(err.message, 'error')
|
|
}
|
|
finally {
|
|
refresh()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
getRef('my_orders_category_selector').addEventListener('change', render.userOrders)
|
|
|
|
getRef('market_orders_category_selector').addEventListener('change', render.marketOrders)
|
|
getRef('market').addEventListener('click', e => {
|
|
if (e.target.closest('.more-info')) {
|
|
showMoreDetails(e)
|
|
}
|
|
})
|
|
|
|
|
|
document.addEventListener('popupopened', e => {
|
|
switch (e.target.id) {
|
|
case 'confirm_trade_popup':
|
|
const asset = pagesData.params.asset;
|
|
const quantity = parseFloat(getRef('get_quantity').value)
|
|
const currentPrice = toFixed(floGlobals.exchangeRates[asset]);
|
|
const price = toFixed(getSuggestedPrice());
|
|
const total = formatAmount(parseFloat(getRef('get_total').value))
|
|
const minTotal = formatAmount(toFixed(currentPrice * quantity))
|
|
getRef('confirm_trade__title').textContent = `${tradeType} ${asset}`
|
|
getRef('confirm_trade__details').innerHTML = `
|
|
<div class="grid">
|
|
<p>Quantity</p>
|
|
<b>${quantity} ${asset}</b>
|
|
</div>
|
|
<div class="grid">
|
|
<p>${tradeType === 'buy' ? 'Min cost' : 'Max earned'}</p>
|
|
<div>
|
|
<b>${minTotal}</b><span> @ ${currentPrice}/${asset}</span>
|
|
</div>
|
|
</div>
|
|
<div class="grid gap-0-3">
|
|
<p>${tradeType === 'buy' ? 'Max cost (Amount locked)' : 'Min earned'}</p>
|
|
<div>
|
|
<b>${total}</b><span> @ ${price}/${asset}</span>
|
|
</div>
|
|
</div>
|
|
<p>
|
|
<strong>All trades will be executed at current price.</strong>
|
|
</p>
|
|
`;
|
|
break;
|
|
}
|
|
})
|
|
document.addEventListener('popupclosed', e => {
|
|
zIndex--
|
|
switch (e.target.id) {
|
|
case 'portfolio_popup':
|
|
hideWalletResult()
|
|
getRef('withdraw_flo_tip').classList.add('hidden')
|
|
break;
|
|
}
|
|
})
|
|
|
|
</script>
|
|
<script>
|
|
const proxy = floExchangeAPI.proxy;
|
|
|
|
function updateRate(init = false) {
|
|
return new Promise((resolve, reject) => {
|
|
floExchangeAPI.getRates().then(({ rates, countDown }) => {
|
|
console.debug(rates);
|
|
floGlobals.exchangeRates = rates
|
|
if (init) {
|
|
Object.entries(rates).sort((a, b) => a[1] < b[1] ? 1 : -1).forEach(([asset, rate]) => {
|
|
if (!allTokens.hasOwnProperty(asset)) {
|
|
allTokens[asset] = {
|
|
total: 0,
|
|
locked: 0,
|
|
net: 0
|
|
}
|
|
}
|
|
// dynamically render listed assets if not already rendered
|
|
getRef('listed_assets').append(render.listedAsset(asset, formatAmount(parseFloat(rate)), countDown[asset]));
|
|
getRef('market_asset_rates').append(createElement('li', {
|
|
className: 'listed-asset grid align-center',
|
|
innerHTML: `
|
|
<div class="listed-asset__icon">${getIcon(asset)}</div>
|
|
<h4 class="listed-asset__name">${asset}</h4>
|
|
<b class="listed-asset__rate">${formatAmount(rate)}</b>
|
|
`
|
|
}))
|
|
})
|
|
resolve();
|
|
} else {
|
|
// update rates in UI
|
|
for (const asset in rates) {
|
|
const listedAsset = getRef(`listed_assets`).querySelector(`[data-listed-asset="${asset}"]`)
|
|
if (listedAsset) {
|
|
listedAsset.querySelector('.listed-asset__rate').textContent = formatAmount(parseFloat(rates[asset]))
|
|
if (floGlobals.countDowns.intervals.hasOwnProperty(asset)) {
|
|
clearInterval(floGlobals.countDowns.intervals[asset]);
|
|
}
|
|
floGlobals.countDowns.intervals[asset] = setInterval(() => {
|
|
const [timeLeft, minutes] = getTimeLeft(countDown[asset])
|
|
listedAsset.querySelector('.listed-asset__countdown').firstChild.textContent = timeLeft;
|
|
const pathLength = 63 - Math.ceil((minutes / 60) * 63)
|
|
listedAsset.querySelector('.listed-asset__countdown').style.setProperty('--path-length', pathLength)
|
|
}, 1000)
|
|
if (floGlobals.countDowns.timeouts.hasOwnProperty(asset)) {
|
|
clearTimeout(floGlobals.countDowns.timeouts[asset]);
|
|
delete floGlobals.countDowns.timeouts[asset]
|
|
}
|
|
floGlobals.countDowns.timeouts[asset] = setTimeout(() => {
|
|
updateRate()
|
|
}, countDown[asset] - Date.now());
|
|
}
|
|
}
|
|
resolve();
|
|
}
|
|
render.chart(pagesData.params.asset)
|
|
showSuggestedPrice()
|
|
}).catch(error => {
|
|
notify(error.message, 'error');
|
|
reject(error)
|
|
})
|
|
}).catch(error => console.error(error))
|
|
}
|
|
|
|
async function refresh(init = false, refreshButton) {
|
|
if (refreshButton)
|
|
buttonLoader('portfolio_balance_refresh_button', true);
|
|
if (init) {
|
|
console.info("init");
|
|
if (!proxy.userID) {
|
|
floExchangeAPI.getLoginCode().then(({ code, hash }) => {
|
|
document.querySelectorAll(".user-content").forEach(elem => elem.classList.add('hidden'))
|
|
floGlobals.loginDetails = { code, hash }
|
|
proxy.clear();
|
|
location.hash = `#/exchange/login`
|
|
}).catch(error => console.error(error))
|
|
.finally(() => {
|
|
getRef('loading').classList.add('hidden')
|
|
getRef('home').classList.remove('hidden')
|
|
})
|
|
}
|
|
} else
|
|
console.info("refresh");
|
|
render.marketOrders();
|
|
updateRate(init).then(async () => {
|
|
if (proxy.userID) {
|
|
await account();
|
|
if (refreshButton)
|
|
buttonLoader('portfolio_balance_refresh_button', false);
|
|
}
|
|
}).catch(error => console.error(error))
|
|
}
|
|
|
|
let accountDetails = {}
|
|
let allTokens = {}
|
|
async function account() {
|
|
floExchangeAPI.getAccount(proxy.userID, await proxy.secret).then(async acc => {
|
|
document.body.classList.add('is-signed-in');
|
|
getRef('market_asset_rates').parentNode.remove()
|
|
getRef('user_popup_button').addEventListener('click', () => openPopup('user_popup'));
|
|
getRef('trade_button').addEventListener('click', () => openPopup('confirm_trade_popup'));
|
|
accountDetails = acc
|
|
console.debug(acc);
|
|
//Element display
|
|
document.querySelectorAll(".user-content").forEach(elem => elem.classList.remove('hidden'))
|
|
document.querySelectorAll(".my-flo-id").forEach(elem => elem.textContent = acc.floID)
|
|
getRef("user_id").value = acc.floID;
|
|
if (acc.subAdmin)
|
|
console.info("logged in as subAdmin");
|
|
|
|
// rupee balance init
|
|
allTokens[floGlobals.currency] = {
|
|
total: 0,
|
|
locked: 0,
|
|
net: 0
|
|
}
|
|
// token balance
|
|
acc.tokenBalance.forEach(({ token, quantity }) => {
|
|
allTokens[token] = {
|
|
total: quantity,
|
|
locked: 0,
|
|
net: 0
|
|
}
|
|
})
|
|
acc.sellOrders.forEach(({ asset, quantity }) => {
|
|
allTokens[asset].locked += quantity
|
|
})
|
|
allTokens[floGlobals.currency].locked = acc.buyOrders.reduce((a, x) => a + x.quantity * x.maxPrice, 0)
|
|
for (const token in allTokens) {
|
|
allTokens[token].net = allTokens[token].total - allTokens[token].locked
|
|
}
|
|
console.debug("FLO", allTokens['FLO']);
|
|
console.log(await floExchangeAPI.getBalance(acc.floID))
|
|
|
|
console.debug("RUPEE", allTokens['rupee']);
|
|
|
|
// render all assets in portfolio
|
|
const frag = document.createDocumentFragment();
|
|
let totalPortfolio = 0;
|
|
Object.entries(allTokens).sort((a, b) => b[1].net - a[1].net).forEach(([asset], index) => {
|
|
if (asset !== floGlobals.currency) {
|
|
totalPortfolio += allTokens[asset].net * floGlobals.exchangeRates[asset]
|
|
frag.append(render.assetBalanceCard(asset))
|
|
}
|
|
else {
|
|
getRef('exchange_rupee_balance').innerHTML = ``
|
|
getRef('exchange_rupee_balance').append(render.assetBalanceCard(asset))
|
|
}
|
|
})
|
|
getRef('my_assets').innerHTML = ''
|
|
getRef('my_assets').append(frag)
|
|
getRef('portfolio_value').textContent = formatAmount(toFixed(totalPortfolio, 2), true)
|
|
//My orders
|
|
render.userOrders();
|
|
|
|
// show personal assets
|
|
floBlockchainAPI.getBalance(acc.floID).then(balance => {
|
|
const [beforeDecimal, afterDecimal = '00'] = String(balance).split('.')
|
|
getRef('personal_flo_balance').innerHTML = `<span><b>${beforeDecimal}</b></span>.<span>${afterDecimal}</span>`
|
|
})
|
|
floTokenAPI.getBalance(accountDetails.floID, 'rupee').then(balance => {
|
|
const [beforeDecimal, afterDecimal] = formatAmount(balance, true).split('₹')[1].split('.')
|
|
getRef('personal_rupee_balance').innerHTML = `<span><b>${beforeDecimal}</b></span>.<span>${afterDecimal}</span>`
|
|
})
|
|
proxy.secret.then(_ => null).catch(_ => null);
|
|
routeTo(window.location.hash);
|
|
}).catch(error => {
|
|
console.debug(error)
|
|
if (['015', '016', '018', '019'].includes(error)) {
|
|
proxy.clear()
|
|
location.reload()
|
|
}
|
|
else
|
|
notify(error.message, 'error')
|
|
})
|
|
.finally(() => {
|
|
buttonLoader('login_button', false)
|
|
buttonLoader('sign_in_with_credentials', false)
|
|
getRef('loading').classList.add('hidden')
|
|
getRef('home').classList.remove('hidden')
|
|
})
|
|
};
|
|
|
|
const UI_evt = {
|
|
logout() {
|
|
getConfirmation('Log out?', { cancelText: 'Stay', confirmText: 'Log out' }).then(async res => {
|
|
if (res) {
|
|
floExchangeAPI.logout(proxy.userID, await proxy.secret).then(result => {
|
|
console.warn(result);
|
|
proxy.clear();
|
|
location.reload();
|
|
}).catch(error => console.error(error));
|
|
}
|
|
})
|
|
},
|
|
|
|
login(type) {
|
|
if (type === 'login')
|
|
buttonLoader('login_button', true)
|
|
else
|
|
buttonLoader('sign_in_with_credentials', true)
|
|
let privKey = getRef('login_form__priv_key').value;
|
|
const { code, hash } = floGlobals.loginDetails;
|
|
// let rememberMe = getRef('remember_me').checked;
|
|
let tmpKey = floCrypto.generateNewID();
|
|
floExchangeAPI.login(privKey, tmpKey.pubKey, code, hash).then(result => {
|
|
console.log(result);
|
|
proxy.secret = tmpKey.privKey;
|
|
proxy.userID = floCrypto.getFloID(privKey);
|
|
delete floGlobals.loginDetails
|
|
account();
|
|
}).catch(error => {
|
|
notify(error.message, 'error')
|
|
if (type === 'login')
|
|
buttonLoader('login_button', false)
|
|
else
|
|
buttonLoader('sign_in_with_credentials', false)
|
|
})
|
|
}
|
|
|
|
};
|
|
window.addEventListener('load', e => {
|
|
floExchangeAPI.init().then(nodes => {
|
|
floGlobals.exchangeApiLoaded = true
|
|
console.log(nodes);
|
|
refresh(true);
|
|
}).catch(error => console.error(error))
|
|
})
|
|
function signInWithCredentials() {
|
|
const privKey = getRef('generated_private_key').value
|
|
getRef('login_form__priv_key').value = privKey
|
|
UI_evt.login()
|
|
}
|
|
|
|
floGlobals.errorMessage = {
|
|
"000": 'Incorrect server',
|
|
"001": 'Invalid request format',
|
|
"002": 'access denied',
|
|
"011": 'Invalid FLO address',
|
|
"012": 'Invalid login code',
|
|
"013": 'Invalid private key',
|
|
"014": 'Invalid public key',
|
|
"015": 'Invalid signature',
|
|
"016": 'Expired signature',
|
|
"017": 'Duplicate signature',
|
|
"018": 'Session invalid',
|
|
"019": 'Session expired',
|
|
"020": 'Invalid value',
|
|
"021": 'Invalid token name',
|
|
"022": 'Invalid number',
|
|
"023": 'Invalid type',
|
|
"024": 'Invalid transaction ID',
|
|
"025": 'Invalid tag',
|
|
"099": 'Missing parameter',
|
|
"101": 'Not found',
|
|
"102": 'Not owner',
|
|
"103": 'Duplicate entry',
|
|
"201": 'Insufficient balance',
|
|
"203": 'Insufficient sell chips',
|
|
"204": 'Greater sell chip base',
|
|
"206": 'Insufficient period',
|
|
"207": 'Insufficient fund',
|
|
"404": 'Node offline',
|
|
"500": 'Internal server error',
|
|
}
|
|
</script>
|
|
</body>
|
|
|
|
</html> |