1324 lines
72 KiB
HTML
1324 lines
72 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>Bob's Fund on Blockchain</title>
|
|
<link rel="stylesheet" href="css/main.min.css">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Overpass:ital,wght@0,400;0,500;1,400;1,500;1,700&family=Roboto:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap"
|
|
rel="stylesheet">
|
|
<script id="floGlobals">
|
|
/* Constants for FLO blockchain operations !!Make sure to add this at begining!! */
|
|
const floGlobals = {
|
|
blockchain: "FLO",
|
|
adminID: "FFXy5pJnfzu2fCDLhpUremyXQjGtFpgCDN",
|
|
application: "BobsFund"
|
|
}
|
|
</script>
|
|
<script src="scripts/lib.js"></script>
|
|
<script src="scripts/floCrypto.js"></script>
|
|
<script src="scripts/floBlockchainAPI.min.js"></script>
|
|
<script src="scripts/floExchangeAPI.js"></script>
|
|
<script src="scripts/compactIDB.js"></script>
|
|
<script src="scripts/btcOperator.js"></script>
|
|
<script src="scripts/bobs-fund.js"></script>
|
|
<script src="scripts/components.min.js"></script>
|
|
<script src="https://unpkg.com/uhtml@3.0.1/es.js"></script>
|
|
<script>
|
|
function onLoadStartUp() {
|
|
floExchangeAPI.init().then(result => {
|
|
compactIDB.initDB(floGlobals.application, {
|
|
funds: {},
|
|
appendix: {}
|
|
}).then(result => refresh())
|
|
.catch(error => console.error(error))
|
|
}).catch(error => {
|
|
console.error(error)
|
|
notify("Unable to connect to FLO blockchain", "error")
|
|
})
|
|
}
|
|
</script>
|
|
</head>
|
|
|
|
<body onload="onLoadStartUp()" data-theme="light">
|
|
<sm-notifications id="notification_drawer"></sm-notifications>
|
|
<article id="loading_page" class="page">
|
|
<h3 class="h3 overpass">Bob's Fund</h3>
|
|
<svg id="loader" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
<polygon points="17.76 23.78 17.76 40.22 32 48.44 46.24 40.22 46.24 23.78 32 15.56 17.76 23.78" />
|
|
<polyline points="17.76 23.78 32 32 46.24 23.78" />
|
|
<line x1="32" y1="48.44" x2="32" y2="32" />
|
|
<polyline points="32 2 6.02 17 6.02 47" />
|
|
<polyline points="57.98 47 57.98 17 32 2" />
|
|
<polyline points="6.02 47 32 62 57.98 47" />
|
|
</svg>
|
|
<h4>Loading data from blockchain</h4>
|
|
<footer class="page__footer flex align-items-center">
|
|
<svg id="rm_logo" class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path
|
|
d="M20.46,21.32C20,19.78,18.6,18.59,15.3,17a12.67,12.67,0,0,1-2.64-1.56,4.27,4.27,0,0,1-.79-1,2.6,2.6,0,0,1,0-1.41c.24-.68.49-1,2.43-2.85a7.18,7.18,0,0,0,2.09-2.92,4.25,4.25,0,0,0,0-1.77,6.52,6.52,0,0,0-2.85-3.11c-.56-.36-.81-.4-.81-.15a2.33,2.33,0,0,1-.18.45L12.4,3l-.53-.36c-.28-.21-.64-.41-.77-.49s-.46-.11-.46,0a6.21,6.21,0,0,1-.37.83s-.08,0-.17-.08c-1.15-.83-1.64-1-1.64-.73A7.33,7.33,0,0,1,7.7,3.65C6.48,5.68,5.24,6.7,4,6.7c-.56,0-.54,0-.37.64s.2.58.68.43a3.37,3.37,0,0,0,1.09-.54.86.86,0,0,1,.3-.17,1.34,1.34,0,0,1,.13.39.79.79,0,0,0,.17.4A3.5,3.5,0,0,0,7.37,7.3L7.8,7l.09.34c.12.45.19.51.62.39a4.25,4.25,0,0,0,2.17-1.54l.38-.45,0,.39A5.92,5.92,0,0,1,8.89,9.54L7.67,10.71c-2,1.93-1.89,3.51.37,5a27.41,27.41,0,0,0,2.89,1.51c.17.07.62.32,1,.54C14,19,15,20.23,15,21.48a2,2,0,0,0,0,.49h0c0,.05,0,.05.56-.1a1.89,1.89,0,0,0,.53-.21,2.41,2.41,0,0,0-.34-1.15,7.05,7.05,0,0,0-1.68-1.77,21.91,21.91,0,0,0-3.2-1.83A9.53,9.53,0,0,1,8.16,15.2a2.18,2.18,0,0,1-.74-1.55C7.42,12.79,7.86,12,9,11c1.77-1.64,2.45-2.45,2.92-3.55a2.28,2.28,0,0,0,.26-1.26A2,2,0,0,0,12,5.06l-.2-.45L12,4.3l.28-.49.09-.18L12.6,4a3.69,3.69,0,0,1,.61,1.76A3.47,3.47,0,0,1,12.94,7l-.09.25s-.21.37-.41.69A17.78,17.78,0,0,1,9.91,10.6c-1.07,1-1.43,1.62-1.47,2.47a2.05,2.05,0,0,0,.7,1.73,10.47,10.47,0,0,0,3.28,2.08c2.28,1.13,3.26,1.81,4,2.73a2.94,2.94,0,0,1,.74,1.75,1.26,1.26,0,0,0,.09.57.48.48,0,0,0,.26,0l.51-.13.29-.08,0-.28c-.13-1-1-2-2.47-3a25.52,25.52,0,0,0-3.26-1.77,8.59,8.59,0,0,1-2.23-1.43,2.09,2.09,0,0,1-.5-2.62c.26-.53.5-.83,2.35-2.6,1.51-1.45,2.15-2.58,2.15-3.79A3.67,3.67,0,0,0,13,3.48a3,3,0,0,1-.4-.42A1.85,1.85,0,0,1,13,2.33a6.74,6.74,0,0,1,1.83,1.73,2.62,2.62,0,0,1,.47,1.68,3,3,0,0,1-.55,1.84c-.45.78-.79,1.14-2.67,2.93a5.56,5.56,0,0,0-1.3,1.64,1.77,1.77,0,0,0-.21,1,1.76,1.76,0,0,0,.19.92,6.28,6.28,0,0,0,2.9,2.34,21.6,21.6,0,0,1,3.66,2c1.35,1,2,2,2,3a1.06,1.06,0,0,0,.05.47,2.83,2.83,0,0,0,1-.24C20.56,21.68,20.56,21.66,20.46,21.32ZM7.29,6.4h0a2.23,2.23,0,0,1-.9.28L6,6.72l.43-.53a15.22,15.22,0,0,0,1.89-3,3.52,3.52,0,0,1,.38-.67c.07-.08.49.2,1,.64l.39.35L9.66,4A6.7,6.7,0,0,1,7.29,6.4Zm3.58-1.11A5.8,5.8,0,0,1,9.25,6.51h0a3.3,3.3,0,0,1-.74.17l-.35,0,.39-.49a15.64,15.64,0,0,0,1.32-2,4.63,4.63,0,0,1,.28-.49c.06-.08.33.26.57.77l.28.57Zm1-1.4a1.63,1.63,0,0,1-.28.4A6.63,6.63,0,0,1,11,3.72l-.53-.56.12-.29c.2-.49.24-.51.64-.19a5.57,5.57,0,0,1,.85.78A2.78,2.78,0,0,1,11.87,3.89Z" />
|
|
</svg>
|
|
<h4 class="weight-500">RanchiMall</h4>
|
|
</footer>
|
|
</article>
|
|
<article id="error_page" class="page hidden">
|
|
<h3 class="h3 overpass">Bob's Fund</h3>
|
|
<svg class="sad-face" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
|
|
<g class="face">
|
|
<g class="eyes">
|
|
<circle cx="187.52" cy="249.17" r="13.42" />
|
|
<circle cx="312.48" cy="249.17" r="13.42" />
|
|
</g>
|
|
<path
|
|
d="M288,349.35a6.07,6.07,0,0,1-3.81-1.34c-.09-.07-12.31-9.4-34.14-9.4S216,347.94,215.83,348a6.1,6.1,0,0,1-7.59-9.55c.61-.5,15.4-12.08,41.76-12.08s41.15,11.58,41.76,12.08A6.1,6.1,0,0,1,288,349.35Z" />
|
|
<path
|
|
d="M344.21,237.41a52,52,0,0,1-23.45-6c-15.91-8.09-21.28-19.61-21.5-20.1a4.07,4.07,0,0,1,7.41-3.36c0,.08,4.56,9.49,17.77,16.21S347.93,229,348,229a4.07,4.07,0,0,1,1.63,8A29.26,29.26,0,0,1,344.21,237.41Z" />
|
|
<path
|
|
d="M155.79,237.41a29.26,29.26,0,0,1-5.45-.43,4.07,4.07,0,0,1,1.63-8c.2,0,10.43,1.87,23.59-4.81s17.73-16.13,17.78-16.23a4.07,4.07,0,0,1,7.4,3.38c-.22.49-5.59,12-21.5,20.1A52,52,0,0,1,155.79,237.41Z" />
|
|
</g>
|
|
<path
|
|
d="M450.16,220.16A200.16,200.16,0,0,0,108.46,78.63,200.69,200.69,0,0,0,57.61,275.68V407.4a10.17,10.17,0,1,0,20.33,0V322.6a201.91,201.91,0,0,0,99.41,84.19v52.27l-11.45.62a10.17,10.17,0,0,0,.54,20.32H167l30.69-1.66V413.45a202.3,202.3,0,0,0,104.62,0v64.88L333,480h.56a10.17,10.17,0,0,0,.54-20.32l-11.45-.62V406.79a201.91,201.91,0,0,0,99.41-84.19v84.8a10.17,10.17,0,1,0,20.33,0V275.68A200.81,200.81,0,0,0,450.16,220.16ZM250,400c-99.16,0-179.83-80.67-179.83-179.83S150.84,40.34,250,40.34,429.83,121,429.83,220.16,349.16,400,250,400Z" />
|
|
</svg>
|
|
<h4 class="margin-bottom-0-5r">Oops, Couldn't get data from blockchain</h4>
|
|
<footer class="page__footer flex align-items-center">
|
|
<svg id="rm_logo" class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path
|
|
d="M20.46,21.32C20,19.78,18.6,18.59,15.3,17a12.67,12.67,0,0,1-2.64-1.56,4.27,4.27,0,0,1-.79-1,2.6,2.6,0,0,1,0-1.41c.24-.68.49-1,2.43-2.85a7.18,7.18,0,0,0,2.09-2.92,4.25,4.25,0,0,0,0-1.77,6.52,6.52,0,0,0-2.85-3.11c-.56-.36-.81-.4-.81-.15a2.33,2.33,0,0,1-.18.45L12.4,3l-.53-.36c-.28-.21-.64-.41-.77-.49s-.46-.11-.46,0a6.21,6.21,0,0,1-.37.83s-.08,0-.17-.08c-1.15-.83-1.64-1-1.64-.73A7.33,7.33,0,0,1,7.7,3.65C6.48,5.68,5.24,6.7,4,6.7c-.56,0-.54,0-.37.64s.2.58.68.43a3.37,3.37,0,0,0,1.09-.54.86.86,0,0,1,.3-.17,1.34,1.34,0,0,1,.13.39.79.79,0,0,0,.17.4A3.5,3.5,0,0,0,7.37,7.3L7.8,7l.09.34c.12.45.19.51.62.39a4.25,4.25,0,0,0,2.17-1.54l.38-.45,0,.39A5.92,5.92,0,0,1,8.89,9.54L7.67,10.71c-2,1.93-1.89,3.51.37,5a27.41,27.41,0,0,0,2.89,1.51c.17.07.62.32,1,.54C14,19,15,20.23,15,21.48a2,2,0,0,0,0,.49h0c0,.05,0,.05.56-.1a1.89,1.89,0,0,0,.53-.21,2.41,2.41,0,0,0-.34-1.15,7.05,7.05,0,0,0-1.68-1.77,21.91,21.91,0,0,0-3.2-1.83A9.53,9.53,0,0,1,8.16,15.2a2.18,2.18,0,0,1-.74-1.55C7.42,12.79,7.86,12,9,11c1.77-1.64,2.45-2.45,2.92-3.55a2.28,2.28,0,0,0,.26-1.26A2,2,0,0,0,12,5.06l-.2-.45L12,4.3l.28-.49.09-.18L12.6,4a3.69,3.69,0,0,1,.61,1.76A3.47,3.47,0,0,1,12.94,7l-.09.25s-.21.37-.41.69A17.78,17.78,0,0,1,9.91,10.6c-1.07,1-1.43,1.62-1.47,2.47a2.05,2.05,0,0,0,.7,1.73,10.47,10.47,0,0,0,3.28,2.08c2.28,1.13,3.26,1.81,4,2.73a2.94,2.94,0,0,1,.74,1.75,1.26,1.26,0,0,0,.09.57.48.48,0,0,0,.26,0l.51-.13.29-.08,0-.28c-.13-1-1-2-2.47-3a25.52,25.52,0,0,0-3.26-1.77,8.59,8.59,0,0,1-2.23-1.43,2.09,2.09,0,0,1-.5-2.62c.26-.53.5-.83,2.35-2.6,1.51-1.45,2.15-2.58,2.15-3.79A3.67,3.67,0,0,0,13,3.48a3,3,0,0,1-.4-.42A1.85,1.85,0,0,1,13,2.33a6.74,6.74,0,0,1,1.83,1.73,2.62,2.62,0,0,1,.47,1.68,3,3,0,0,1-.55,1.84c-.45.78-.79,1.14-2.67,2.93a5.56,5.56,0,0,0-1.3,1.64,1.77,1.77,0,0,0-.21,1,1.76,1.76,0,0,0,.19.92,6.28,6.28,0,0,0,2.9,2.34,21.6,21.6,0,0,1,3.66,2c1.35,1,2,2,2,3a1.06,1.06,0,0,0,.05.47,2.83,2.83,0,0,0,1-.24C20.56,21.68,20.56,21.66,20.46,21.32ZM7.29,6.4h0a2.23,2.23,0,0,1-.9.28L6,6.72l.43-.53a15.22,15.22,0,0,0,1.89-3,3.52,3.52,0,0,1,.38-.67c.07-.08.49.2,1,.64l.39.35L9.66,4A6.7,6.7,0,0,1,7.29,6.4Zm3.58-1.11A5.8,5.8,0,0,1,9.25,6.51h0a3.3,3.3,0,0,1-.74.17l-.35,0,.39-.49a15.64,15.64,0,0,0,1.32-2,4.63,4.63,0,0,1,.28-.49c.06-.08.33.26.57.77l.28.57Zm1-1.4a1.63,1.63,0,0,1-.28.4A6.63,6.63,0,0,1,11,3.72l-.53-.56.12-.29c.2-.49.24-.51.64-.19a5.57,5.57,0,0,1,.85.78A2.78,2.78,0,0,1,11.87,3.89Z" />
|
|
</svg>
|
|
<h4 class="h4 color-0-8 weight-500">RanchiMall</h4>
|
|
</footer>
|
|
</article>
|
|
<header id="main_header" class="hidden">
|
|
<div class="flex align-items-center">
|
|
<svg id="main_header__logo" class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path
|
|
d="M20.46,21.32C20,19.78,18.6,18.59,15.3,17a12.67,12.67,0,0,1-2.64-1.56,4.27,4.27,0,0,1-.79-1,2.6,2.6,0,0,1,0-1.41c.24-.68.49-1,2.43-2.85a7.18,7.18,0,0,0,2.09-2.92,4.25,4.25,0,0,0,0-1.77,6.52,6.52,0,0,0-2.85-3.11c-.56-.36-.81-.4-.81-.15a2.33,2.33,0,0,1-.18.45L12.4,3l-.53-.36c-.28-.21-.64-.41-.77-.49s-.46-.11-.46,0a6.21,6.21,0,0,1-.37.83s-.08,0-.17-.08c-1.15-.83-1.64-1-1.64-.73A7.33,7.33,0,0,1,7.7,3.65C6.48,5.68,5.24,6.7,4,6.7c-.56,0-.54,0-.37.64s.2.58.68.43a3.37,3.37,0,0,0,1.09-.54.86.86,0,0,1,.3-.17,1.34,1.34,0,0,1,.13.39.79.79,0,0,0,.17.4A3.5,3.5,0,0,0,7.37,7.3L7.8,7l.09.34c.12.45.19.51.62.39a4.25,4.25,0,0,0,2.17-1.54l.38-.45,0,.39A5.92,5.92,0,0,1,8.89,9.54L7.67,10.71c-2,1.93-1.89,3.51.37,5a27.41,27.41,0,0,0,2.89,1.51c.17.07.62.32,1,.54C14,19,15,20.23,15,21.48a2,2,0,0,0,0,.49h0c0,.05,0,.05.56-.1a1.89,1.89,0,0,0,.53-.21,2.41,2.41,0,0,0-.34-1.15,7.05,7.05,0,0,0-1.68-1.77,21.91,21.91,0,0,0-3.2-1.83A9.53,9.53,0,0,1,8.16,15.2a2.18,2.18,0,0,1-.74-1.55C7.42,12.79,7.86,12,9,11c1.77-1.64,2.45-2.45,2.92-3.55a2.28,2.28,0,0,0,.26-1.26A2,2,0,0,0,12,5.06l-.2-.45L12,4.3l.28-.49.09-.18L12.6,4a3.69,3.69,0,0,1,.61,1.76A3.47,3.47,0,0,1,12.94,7l-.09.25s-.21.37-.41.69A17.78,17.78,0,0,1,9.91,10.6c-1.07,1-1.43,1.62-1.47,2.47a2.05,2.05,0,0,0,.7,1.73,10.47,10.47,0,0,0,3.28,2.08c2.28,1.13,3.26,1.81,4,2.73a2.94,2.94,0,0,1,.74,1.75,1.26,1.26,0,0,0,.09.57.48.48,0,0,0,.26,0l.51-.13.29-.08,0-.28c-.13-1-1-2-2.47-3a25.52,25.52,0,0,0-3.26-1.77,8.59,8.59,0,0,1-2.23-1.43,2.09,2.09,0,0,1-.5-2.62c.26-.53.5-.83,2.35-2.6,1.51-1.45,2.15-2.58,2.15-3.79A3.67,3.67,0,0,0,13,3.48a3,3,0,0,1-.4-.42A1.85,1.85,0,0,1,13,2.33a6.74,6.74,0,0,1,1.83,1.73,2.62,2.62,0,0,1,.47,1.68,3,3,0,0,1-.55,1.84c-.45.78-.79,1.14-2.67,2.93a5.56,5.56,0,0,0-1.3,1.64,1.77,1.77,0,0,0-.21,1,1.76,1.76,0,0,0,.19.92,6.28,6.28,0,0,0,2.9,2.34,21.6,21.6,0,0,1,3.66,2c1.35,1,2,2,2,3a1.06,1.06,0,0,0,.05.47,2.83,2.83,0,0,0,1-.24C20.56,21.68,20.56,21.66,20.46,21.32ZM7.29,6.4h0a2.23,2.23,0,0,1-.9.28L6,6.72l.43-.53a15.22,15.22,0,0,0,1.89-3,3.52,3.52,0,0,1,.38-.67c.07-.08.49.2,1,.64l.39.35L9.66,4A6.7,6.7,0,0,1,7.29,6.4Zm3.58-1.11A5.8,5.8,0,0,1,9.25,6.51h0a3.3,3.3,0,0,1-.74.17l-.35,0,.39-.49a15.64,15.64,0,0,0,1.32-2,4.63,4.63,0,0,1,.28-.49c.06-.08.33.26.57.77l.28.57Zm1-1.4a1.63,1.63,0,0,1-.28.4A6.63,6.63,0,0,1,11,3.72l-.53-.56.12-.29c.2-.49.24-.51.64-.19a5.57,5.57,0,0,1,.85.78A2.78,2.78,0,0,1,11.87,3.89Z" />
|
|
</svg>
|
|
<div class="grid">
|
|
<h5 class="header__company-name">RanchiMall</h5>
|
|
</div>
|
|
</div>
|
|
<div id="current_price" class="grid gap-1 flow-column align-items-center">
|
|
<span id="btc-usd-rate" class="weight-700"></span>
|
|
<span id="usd-rate" class="weight-700"></span>
|
|
</div>
|
|
<div class="dropdown">
|
|
<button id="profile_button" onclick="changeDropdownState('profile_dropdown', 'toggle', this)">
|
|
<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>
|
|
<path d="M0,0h24v24H0V0z" fill="none" />
|
|
<path
|
|
d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z" />
|
|
</g>
|
|
</svg>
|
|
</button>
|
|
<ul id="profile_dropdown" class="dropdown__panel hidden">
|
|
<li class="grid gap-0-5">
|
|
<h4 class="weight-700 margin-bottom-0-5r">Preferred currency</h4>
|
|
<p>This will convert all amounts to preferred currency.</p>
|
|
<sm-select id="currency_selector" class="justify-self-start">
|
|
<sm-option value="inr" selected>INR</sm-option>
|
|
<sm-option value="usd">USD</sm-option>
|
|
</sm-select>
|
|
</li>
|
|
<li class="flex align-items-center space-between">
|
|
<div class="flex weight-700">Dark theme</div>
|
|
<theme-toggle></theme-toggle>
|
|
</li>
|
|
<li class="interact weight-700" onclick="showPage('admin_page')">
|
|
Admin panel
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</header>
|
|
<main id="home_page" class="page hidden">
|
|
<section id="homepage__hero-section">
|
|
<h3 class="h3 overpass margin-bottom-1r">Bob's Fund<br>on FLO Blockchain</h3>
|
|
<p>
|
|
Bob's Fund is a 20 year long term Bitcoin price linked product. Investors are entitled to 100%
|
|
of Bitcoin price gains, but they must hold for 20 years. Over a very long time period, investor returns
|
|
on an asset like Bitcoin should outstrip returns on conventional assets like real estate and stocks. The
|
|
management fees on this product is zero. RanchiMall earns by having invested an equal amount as
|
|
every investor, thus the interests of fund manager, and fund investors are totally aligned.
|
|
</p>
|
|
</section>
|
|
<sm-input id="search_investor" type="search" placeholder="Search investor with FLO/BTC address">
|
|
<svg class="icon" slot="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.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" />
|
|
</svg>
|
|
</sm-input>
|
|
<header class="fund-list__header">
|
|
<h4 class="h4">Funds</h4>
|
|
<button id="refresh_button" class="button justify-right">Refresh</button>
|
|
</header>
|
|
<section>
|
|
<ul id="fund_list"></ul>
|
|
<div id="fund_list__empty-state" class="grid hidden">
|
|
<svg class="icon icon--big" 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 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm-4-6h8v2H8v-2zm0-3a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm8 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z" />
|
|
</svg>
|
|
<h4>No Funds found</h4>
|
|
</div>
|
|
</main>
|
|
<article id="admin_page" class="page page-layout hidden">
|
|
<header class="flex margin-top-1-5 align-items-center margin-bottom-1-5r">
|
|
<button onclick="showPage('home_page')">
|
|
<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="M7.828 11H20v2H7.828l5.364 5.364-1.414 1.414L4 12l7.778-7.778 1.414 1.414z" />
|
|
</svg>
|
|
</button>
|
|
<h3>Admin Panel</h3>
|
|
</header>
|
|
<form id="create_fund_form" class="grid gap-1-5">
|
|
<sm-switch id="fund_creation_toggle">
|
|
<div class="flex" slot="left">
|
|
Add investors to existing fund
|
|
</div>
|
|
</sm-switch>
|
|
<section id="fund_details_form" class="grid gap-1-5 margin-bottom-0-5r">
|
|
<label class="grid gap-0-5">
|
|
Fund start date
|
|
<input type="date" name="start_date" required>
|
|
</label>
|
|
<label class="grid gap-0-5">
|
|
Base BTC value ($)
|
|
<input type="number" name="btc_base" step="0.01" pattern="[\d,]+(.\d+)?" required>
|
|
</label>
|
|
<label class="grid gap-0-5">
|
|
Base USD rate (₹)
|
|
<input type="number" name="usd_rate" min=0 step="0.01" required>
|
|
</label>
|
|
<div class="grid gap-0-5">
|
|
Maximum Duration
|
|
<div class="flex">
|
|
<input type="number" name="max_pv" inputmode="numeric" required>
|
|
<sm-select id="max_pt" align-select="right">
|
|
<sm-option value="year(s)" selected>year(s)</sm-option>
|
|
<sm-option value="month(s)">month(s)</sm-option>
|
|
<sm-option value="week(s)">week(s)</sm-option>
|
|
<sm-option value="day(s)">day(s)</sm-option>
|
|
</sm-select>
|
|
</div>
|
|
</div>
|
|
<sm-switch id="tapout_toggle">
|
|
<div class="flex" slot="left">
|
|
Tapout
|
|
</div>
|
|
</sm-switch>
|
|
<section id="tapout_container" class="grid gap-1-5 hidden">
|
|
<div class="grid gap-0-5">
|
|
Tapout window
|
|
<div class="flex">
|
|
<input type="number" name="tap_wv" inputmode="numeric" disabled required>
|
|
<sm-select id="tap_wt" align-select="right">
|
|
<sm-option value="year(s)">year(s)</sm-option>
|
|
<sm-option value="month(s)" selected>month(s)</sm-option>
|
|
<sm-option value="week(s)">week(s)</sm-option>
|
|
<sm-option value="day(s)">day(s)</sm-option>
|
|
</sm-select>
|
|
</div>
|
|
</div>
|
|
<div class="grid gap-0-5">
|
|
Tapout interval(Use comma separated values. e.g. 10, 15...)
|
|
<div class="flex">
|
|
<input type="text" name="tap_iv" disabled required>
|
|
<sm-select id="tap_it" align-select="right">
|
|
<sm-option value="year(s)" selected>year(s)</sm-option>
|
|
<sm-option value="month(s)">month(s)</sm-option>
|
|
</sm-select>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<label class="grid gap-0-5">
|
|
Fee
|
|
<input type="number" name="fee" min=0 max=100 step="0.01" value="0" inputmode="numeric" required>
|
|
</label>
|
|
</section>
|
|
|
|
<div id="fund_selector_container" class="grid margin-bottom-0-5r hidden">
|
|
<span class="margin-bottom-0-5r">Select Fund</span>
|
|
<sm-select id="fund_selector"></sm-select>
|
|
</div>
|
|
<h4>Add Investors</h4>
|
|
<ul id="investors_input_list" class="grid gap-1">
|
|
<li class="investor-input grid">
|
|
<sm-input placeholder="FLO address" class="outlined" animate></sm-input>
|
|
<sm-input placeholder="Amount(₹)" type="number" min=0 class="outlined" animate></sm-input>
|
|
<button class="remove-investor" title="Remove this investor">
|
|
<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>
|
|
</li>
|
|
</ul>
|
|
<button type="button" class="margin-bottom-1r justify-self-start" style="margin-top: -1rem;"
|
|
onclick="renderInvestor()">
|
|
<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="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z" />
|
|
</svg>
|
|
Add new
|
|
</button>
|
|
<button id="add_investors_button" type="submit" class="button--primary justify-self-start hidden">Add
|
|
Investors</button>
|
|
<button id="create_fund_button" type="submit" class="button--primary justify-self-start">Create
|
|
Fund</button>
|
|
</form>
|
|
</article>
|
|
<article id="confirm_term_page" class="page page-layout hidden">
|
|
<header class="flex margin-top-1-5 align-items-center margin-bottom-1-5r">
|
|
<button class="back-button" onclick="showPage('admin_page')">
|
|
<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="M7.828 11H20v2H7.828l5.364 5.364-1.414 1.414L4 12l7.778-7.778 1.414 1.414z" />
|
|
</svg>
|
|
</button>
|
|
<h3>Confirm Details</h3>
|
|
</header>
|
|
<form class="grid gap-1-5" onsubmit="return false">
|
|
<section id="term_details" class="breakable"></section>
|
|
<div class="grid">
|
|
<h4 class="weight-400 margin-bottom-1r" id="term_admin_id"></h4>
|
|
<sm-input id="get_term_private_key" type="password" placeholder="Private key"></sm-input>
|
|
</div>
|
|
<button id="create_term_button" type="submit" class="button--primary justify-self-start">Create
|
|
term</button>
|
|
</form>
|
|
</article>
|
|
<article id="confirm_fund_page" class="page page-layout hidden">
|
|
<header class="flex margin-top-1-5 align-items-center margin-bottom-1-5r">
|
|
<button class="back-button" onclick="showPage('admin_page')">
|
|
<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="M7.828 11H20v2H7.828l5.364 5.364-1.414 1.414L4 12l7.778-7.778 1.414 1.414z" />
|
|
</svg>
|
|
</button>
|
|
<h3>Confirm Details</h3>
|
|
</header>
|
|
<form class="grid gap-1-5" onsubmit="return false">
|
|
<section id="fund_details" class="breakable"></section>
|
|
<div class="grid">
|
|
<h4 class="weight-400 margin-bottom-1r" id="fund_admin_id"></h4>
|
|
<sm-input id="get_fund_private_key" type="password" placeholder="Private key"></sm-input>
|
|
</div>
|
|
<button id="confirm_fund_button" type="submit" class="button--primary justify-self-start">Create
|
|
Fund</button>
|
|
</form>
|
|
</article>
|
|
<sm-popup id="withdraw_popup">
|
|
<header slot="header" class="popup__header">
|
|
<button class="popup__header__close" onclick="closePopup()">
|
|
<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>Withdraw fund</h3>
|
|
</header>
|
|
<div id="withdraw_process">
|
|
<div id="withdraw_precess__get_priv_key">
|
|
<sm-form class="grid gap-1-5">
|
|
<div id="withdraw__id"></div>
|
|
<sm-input id="withdraw_private_key" class="password-field" type="password"
|
|
placeholder="FLO private key" error-text="Private key is invalid" autofocus data-private-key
|
|
required>
|
|
<label slot="right" class="interact">
|
|
<input type="checkbox" class="hidden" autocomplete="off" readonly
|
|
onchange="togglePrivateKeyVisibility(this)">
|
|
<svg class="icon invisible" xmlns="http://www.w3.org/2000/svg" height="24px"
|
|
viewBox="0 0 24 24" width="24px" fill="#000000">
|
|
<title>Hide password</title>
|
|
<path d="M0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0z" fill="none" />
|
|
<path
|
|
d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z" />
|
|
</svg>
|
|
<svg class="icon visible" xmlns="http://www.w3.org/2000/svg" height="24px"
|
|
viewBox="0 0 24 24" width="24px" fill="#000000">
|
|
<title>Show password</title>
|
|
<path d="M0 0h24v24H0z" fill="none" />
|
|
<path
|
|
d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" />
|
|
</svg>
|
|
</label>
|
|
</sm-input>
|
|
<div class="multi-state-button">
|
|
<button id="withdraw_private_key_button" type="submit"
|
|
class="button button--primary cta">Confirm
|
|
withdrawal</button>
|
|
</div>
|
|
</sm-form>
|
|
</div>
|
|
<div class="grid gap-0-5 hidden justify-center text-center">
|
|
<svg class="icon user-action-result__icon success" xmlns="http://www.w3.org/2000/svg" height="24px"
|
|
viewBox="0 0 24 24" width="24px" fill="#000000">
|
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
|
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
|
|
</svg>
|
|
<h4>Withdraw request sent</h4>
|
|
<p>Balance may take upto 30mins to reflect in your FLO address</p>
|
|
</div>
|
|
<div class="grid gap-0-5 hidden justify-center text-center">
|
|
<svg class="icon user-action-result__icon failed" xmlns="http://www.w3.org/2000/svg" height="24px"
|
|
viewBox="0 0 24 24" width="24px" fill="#000000">
|
|
<path d="M0 0h24v24H0z" fill="none" />
|
|
<path
|
|
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" />
|
|
</svg>
|
|
<h3>Failed</h3>
|
|
<p id="withdraw_failed_message"></p>
|
|
</div>
|
|
</div>
|
|
</sm-popup>
|
|
|
|
|
|
<script id="ui">
|
|
const { html, render: renderElem } = uhtml;
|
|
const domRefs = {}
|
|
//Checks for internet connection status
|
|
if (!navigator.onLine)
|
|
notify('There seems to be a problem connecting to the internet, Please check you internet connection.', 'error', '', true)
|
|
window.addEventListener('offline', () => {
|
|
notify('There seems to be a problem connecting to the internet, Please check you internet connection.', 'error', true, true)
|
|
})
|
|
window.addEventListener('online', () => {
|
|
getRef('notification_drawer').clearAll()
|
|
notify('We are back online.', 'success')
|
|
})
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
//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 });
|
|
}
|
|
|
|
|
|
window.addEventListener("load", () => {
|
|
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.code === 'Escape') {
|
|
closePopup()
|
|
}
|
|
})
|
|
document.addEventListener('keydown', e => {
|
|
if (e.key === '/') {
|
|
e.preventDefault();
|
|
getRef('search_investor').focusIn()
|
|
}
|
|
})
|
|
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"));
|
|
}
|
|
else if (isDropdownOpen && !e.target.closest('.dropdown')) {
|
|
changeDropdownState('profile_dropdown', 'hide')
|
|
}
|
|
});
|
|
|
|
if (localStorage.getItem('preferred-currency')) {
|
|
preferredCurrency = localStorage.getItem('preferred-currency')
|
|
setTimeout(() => {
|
|
getRef('currency_selector').value = preferredCurrency
|
|
}, 1000);
|
|
} else {
|
|
preferredCurrency = 'inr'
|
|
localStorage.setItem('preferred-currency', 'inr')
|
|
}
|
|
});
|
|
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: floGlobals.prefersReducedMotion ? 0 : 600,
|
|
fill: "forwards",
|
|
easing: "ease-out",
|
|
}
|
|
);
|
|
target.append(circle);
|
|
rippleAnimation.onfinish = () => {
|
|
circle.remove();
|
|
};
|
|
}
|
|
|
|
// 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);
|
|
};
|
|
}
|
|
let zIndex = 50
|
|
// function required for popups or modals to appear
|
|
function openPopup(popupId, pinned) {
|
|
zIndex++
|
|
getRef(popupId).setAttribute('style', `z-index: ${zIndex}`)
|
|
getRef(popupId).show({ pinned })
|
|
return getRef(popupId);
|
|
}
|
|
|
|
// hides the popup or modal
|
|
function closePopup() {
|
|
if (popupStack.peek() === undefined)
|
|
return;
|
|
popupStack.peek().popup.hide()
|
|
}
|
|
function buttonLoader(id, show) {
|
|
const button = typeof id === 'string' ? getRef(id) : id;
|
|
button.disabled = show;
|
|
const animOptions = {
|
|
duration: floGlobals.prefersReducedMotion ? 0 : 200,
|
|
fill: 'forwards',
|
|
easing: 'ease'
|
|
}
|
|
if (show) {
|
|
button.animate([
|
|
{
|
|
clipPath: 'circle(100%)',
|
|
},
|
|
{
|
|
clipPath: 'circle(0)',
|
|
},
|
|
], animOptions)
|
|
button.parentNode.append(document.createElement('sm-spinner'))
|
|
} else {
|
|
button.getAnimations().forEach(anim => anim.cancel())
|
|
const potentialTarget = button.parentNode.querySelector('sm-spinner')
|
|
if (potentialTarget) potentialTarget.remove();
|
|
}
|
|
}
|
|
document.addEventListener('popupopened', e => {
|
|
getRef('home_page').setAttribute('inert', '')
|
|
})
|
|
document.addEventListener('popupclosed', e => {
|
|
switch (e.detail.popup.id) {
|
|
case 'withdraw_popup':
|
|
showChildElement(getRef('withdraw_process'), 0);
|
|
buttonLoader('withdraw_private_key_button', false)
|
|
getRef('withdraw_private_key_button').disabled = true;
|
|
break;
|
|
}
|
|
if (popupStack.items.length === 0) {
|
|
getRef('home_page').removeAttribute('inert')
|
|
}
|
|
})
|
|
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 slideOutDown = [
|
|
{
|
|
opacity: 1,
|
|
transform: 'translateY(0)'
|
|
},
|
|
{
|
|
opacity: 0,
|
|
transform: 'translateY(1rem)'
|
|
},
|
|
]
|
|
const slideInUp = [
|
|
{
|
|
opacity: 0,
|
|
transform: 'translateY(1rem)'
|
|
},
|
|
{
|
|
opacity: 1,
|
|
transform: 'translateY(0)'
|
|
},
|
|
]
|
|
const slideOutUp = [
|
|
{
|
|
opacity: 1,
|
|
transform: 'translateY(0)'
|
|
},
|
|
{
|
|
opacity: 0,
|
|
transform: 'translateY(-1rem)'
|
|
},
|
|
]
|
|
|
|
function showChildElement(id, index, options = {}) {
|
|
return new Promise((resolve) => {
|
|
const { mobileView = false, entry, exit } = options
|
|
const animOptions = {
|
|
duration: floGlobals.prefersReducedMotion ? 0 : 150,
|
|
easing: 'ease',
|
|
fill: 'forwards'
|
|
}
|
|
const parent = typeof id === 'string' ? document.getElementById(id) : id;
|
|
const visibleElement = [...parent.children].find(elem => !elem.classList.contains(mobileView ? 'hide-on-mobile' : 'hidden'));
|
|
if (visibleElement === parent.children[index]) return;
|
|
visibleElement.getAnimations().forEach(anim => anim.cancel())
|
|
parent.children[index].getAnimations().forEach(anim => anim.cancel())
|
|
if (visibleElement) {
|
|
if (exit) {
|
|
parent.style.overflow = 'hidden'
|
|
visibleElement.animate(exit, animOptions).onfinish = () => {
|
|
visibleElement.classList.add(mobileView ? 'hide-on-mobile' : 'hidden')
|
|
parent.style.overflow = ''
|
|
}
|
|
parent.children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
|
|
if (entry)
|
|
parent.children[index].animate(entry, animOptions).onfinish = () => resolve()
|
|
} else {
|
|
visibleElement.classList.add(mobileView ? 'hide-on-mobile' : 'hidden')
|
|
parent.children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
|
|
resolve()
|
|
}
|
|
} else {
|
|
parent.children[index].classList.remove(mobileView ? 'hide-on-mobile' : 'hidden')
|
|
parent.children[index].animate(entry, animOptions).onfinish = () => resolve()
|
|
}
|
|
})
|
|
}
|
|
function togglePrivateKeyVisibility(input) {
|
|
const target = input.closest('sm-input')
|
|
target.type = target.type === 'password' ? 'text' : 'password';
|
|
target.focusIn()
|
|
}
|
|
|
|
function formatAmount(amount = 0, currency = 'inr') {
|
|
amount = parseFloat(amount);
|
|
if (!amount)
|
|
return '₹0';
|
|
return amount.toLocaleString(currency === 'inr' ? `en-IN` : 'en-US', { style: 'currency', currency })
|
|
}
|
|
const render = {
|
|
investorInput() {
|
|
const investorInput = document.createElement('li')
|
|
investorInput.classList.add('investor-input', 'grid')
|
|
investorInput.innerHTML = `
|
|
<sm-input placeholder="FLO address" class="outlined" animate></sm-input>
|
|
<sm-input placeholder="Amount(₹)" type="number" min=0 class="outlined" animate></sm-input>
|
|
<button class="remove-investor" title="Remove this investor">
|
|
<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>
|
|
`
|
|
return investorInput
|
|
},
|
|
fundPlaceholder() {
|
|
const fund_ph = document.createElement('li')
|
|
fund_ph.classList.add('fund-placeholder', 'grid')
|
|
fund_ph.innerHTML = `
|
|
<div class="flex-grid justify-start">
|
|
<div class="placeholder__block"></div>
|
|
<div class="placeholder__block"></div>
|
|
<div class="placeholder__block"></div>
|
|
<div class="placeholder__block"></div>
|
|
</div>
|
|
<div class="grid flow-column gap-1">
|
|
<div class="placeholder__block"></div>
|
|
<div class="placeholder__block"></div>
|
|
</div>
|
|
<div class="placeholder__block justify-self-end"></div>
|
|
`
|
|
return fund_ph
|
|
},
|
|
investmentCard(details) {
|
|
const {
|
|
floId,
|
|
amountInvested,
|
|
netValue,
|
|
fundId,
|
|
hasMatured = false,
|
|
allowsEarlyWithdrawal = false,
|
|
} = details
|
|
const isRedeemable = hasMatured || allowsEarlyWithdrawal
|
|
const btcId = btcOperator.convert.legacy2bech(floId)
|
|
return html`
|
|
<li class="fund-investor" id=${`${fundId}_${floId}`} .dataset=${{ btcId }}>
|
|
<div class="grid gap-1">
|
|
<div class="grid">
|
|
<span class="label">FLO address</span>
|
|
<span class="value flo-id">${floId}</span>
|
|
</div>
|
|
<div class="grid">
|
|
<span class="label">BTC address</span>
|
|
<span class="value btc-id">${btcId}</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-1-5">
|
|
<div class="transaction-column">
|
|
<span class="label">Invested</span>
|
|
<span class="value amount-invested">${formatAmount(amountInvested[preferredCurrency], preferredCurrency)}</span>
|
|
</div>
|
|
<div class="transaction-column">
|
|
<span class="label">Present value</span>
|
|
<span class="value net-value" style="color: var(--green)">${formatAmount(netValue[preferredCurrency], preferredCurrency)}</span>
|
|
</div>
|
|
</div>
|
|
<button class="button fund-investor__withdraw" ?disabled=${!isRedeemable}>Withdraw</button>
|
|
</li>
|
|
`
|
|
},
|
|
closedInvestmentCard(details) {
|
|
const {
|
|
floId,
|
|
amountInvested,
|
|
fundId,
|
|
BTC_net,
|
|
endDate,
|
|
finalAmount,
|
|
USD_net,
|
|
payment_refRef
|
|
} = details
|
|
const btcId = btcOperator.convert.legacy2bech(floId)
|
|
return html`
|
|
<li class="fund-investor" id=${`${fundId}_${floId}`} .dataset=${{ btcId }}>
|
|
<div class="flex align-items-center space-between w-100">
|
|
<span class="tag">Closed: ${bobsFund.dateFormat(endDate)}</span>
|
|
</div>
|
|
<div class="grid gap-1">
|
|
<div class="grid">
|
|
<span class="label">FLO address</span>
|
|
<span class="value flo-id">${floId}</span>
|
|
</div>
|
|
<div class="grid">
|
|
<span class="label">BTC address</span>
|
|
<span class="value btc-id">${btcId}</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-1-5">
|
|
<div class="transaction-column">
|
|
<span class="label">Invested</span>
|
|
<span class="value amount-invested">${formatAmount(amountInvested[preferredCurrency], preferredCurrency)}</span>
|
|
</div>
|
|
<div class="transaction-column">
|
|
<span class="label">Withdrawn amount</span>
|
|
<span class="value net-value" style="color: var(--green)">${formatAmount(finalAmount[preferredCurrency], preferredCurrency)}</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-1-5 align-center space-between w-100">
|
|
<div class="transaction-column">
|
|
<a href=${`${floBlockchainAPI.current_server}tx/${payment_refRef}`} target="_blank">See withdrawal transaction</a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
`
|
|
},
|
|
fundBlock(details) {
|
|
let {
|
|
fundTxs,
|
|
startDate,
|
|
endDate,
|
|
baseUsd,
|
|
baseBtc,
|
|
tapouts,
|
|
totalInvestment,
|
|
totalNet,
|
|
investorsFrag
|
|
} = details
|
|
const tapoutsPoints = tapouts.map((tapout, index) => {
|
|
const duration = `${bobsFund.dateFormat(tapout.start)} to ${bobsFund.dateFormat(tapout.end)}`
|
|
return html`
|
|
<li class="tapout-point grid">
|
|
<span class="label">Tapout ${index + 1}</span>
|
|
<span class="value">${duration}</span>
|
|
</li>`;
|
|
})
|
|
const renderedFundTxs = fundTxs.map((tx, index) => {
|
|
return html`<a href=${`${floBlockchainAPI.current_server}tx/${tx.txid}`} target="_blank">Transaction ${index + 1}</a>`
|
|
})
|
|
|
|
return html.node`
|
|
<li class="fund-block">
|
|
<h4 class="start-date">${startDate} Fund</h4>
|
|
<div class="fund-block__details margin-bottom-3r">
|
|
<div class="grid">
|
|
<span class="label">End date</span>
|
|
<span class="value end-date">${bobsFund.dateFormat(endDate)}</span>
|
|
</div>
|
|
<div class="flex flex-wrap gap-1">
|
|
<div class="grid">
|
|
<span class="label">Initial BTC value</span>
|
|
<span class="value base-btc">${formatAmount(baseBtc[preferredCurrency], preferredCurrency)}</span>
|
|
</div>
|
|
<div class="grid">
|
|
<span class="label">Base USD rate</span>
|
|
<span class="value base-usd">${baseUsd.toLocaleString(`en-US`, { style: 'currency', currency: 'INR' })}</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex flex-wrap gap-1">
|
|
<div class="grid">
|
|
<span class="label">Total investment</span>
|
|
<span class="value total-investment">${formatAmount(totalInvestment[preferredCurrency], preferredCurrency)}</span>
|
|
</div>
|
|
<div class="grid">
|
|
<span class="label">Total present value</span>
|
|
<span class="value net-value" style="color: var(--green)">${formatAmount(totalNet[preferredCurrency], preferredCurrency)}</span>
|
|
</div>
|
|
</div>
|
|
${tapoutsPoints.length ? html`
|
|
<div class="grid">
|
|
<span class="value margin-bottom-0-5r"></span>
|
|
<ul class="tapout-list"></ul>
|
|
</div>` : ''}
|
|
<div class="grid">
|
|
<span class="label">Fund transactions</span>
|
|
<div class="flex gap-0-5 flex-wrap">${renderedFundTxs}</div>
|
|
</div>
|
|
</div>
|
|
<div class="grid">
|
|
<h4 class="margin-bottom-0-5r">Investors</h4>
|
|
<ul class="investors-list grid">${investorsFrag}</ul>
|
|
</div>
|
|
</li>
|
|
`;
|
|
}
|
|
}
|
|
|
|
|
|
let preferredCurrency
|
|
|
|
getRef('currency_selector').addEventListener('change', e => {
|
|
preferredCurrency = e.target.value
|
|
localStorage.setItem('preferred-currency', preferredCurrency)
|
|
document.querySelectorAll('.fund-block').forEach(fundBlock => {
|
|
const { baseBtc, totalInvestment, totalNet } = floGlobals.investments[fundBlock.id]
|
|
fundBlock.querySelector('.base-btc').textContent = formatAmount(baseBtc[preferredCurrency], preferredCurrency)
|
|
fundBlock.querySelector('.total-investment').textContent = formatAmount(totalInvestment[preferredCurrency], preferredCurrency)
|
|
fundBlock.querySelector('.net-value').textContent = formatAmount(totalNet[preferredCurrency], preferredCurrency)
|
|
})
|
|
document.querySelectorAll('.fund-investor').forEach(investor => {
|
|
const { amountInvested, netValue } = floGlobals.investments[investor.id]
|
|
investor.querySelector('.amount-invested').textContent = formatAmount(amountInvested[preferredCurrency], preferredCurrency)
|
|
investor.querySelector('.net-value').textContent = formatAmount(netValue[preferredCurrency], preferredCurrency)
|
|
})
|
|
})
|
|
|
|
let isDropdownOpen = false
|
|
|
|
function changeDropdownState(target, mode, trigger) {
|
|
const options = {
|
|
duration: 300,
|
|
easing: 'ease',
|
|
fill: 'both',
|
|
}
|
|
if (mode === 'show') {
|
|
showDropdown(target, options, trigger)
|
|
}
|
|
else if (mode === 'toggle') {
|
|
if (isDropdownOpen) {
|
|
hideDropdown(target, options)
|
|
}
|
|
else {
|
|
showDropdown(target, options, trigger)
|
|
}
|
|
}
|
|
else if (mode === 'hide') {
|
|
hideDropdown(target, options)
|
|
}
|
|
}
|
|
|
|
function showDropdown(target, options, trigger) {
|
|
if (isDropdownOpen) return
|
|
if (trigger) {
|
|
const triggerDimensions = trigger.getBoundingClientRect()
|
|
getRef(target).setAttribute('style', `top: ${triggerDimensions.top + triggerDimensions.height + document.documentElement.scrollTop}px; right: calc(${window.innerWidth - triggerDimensions.right}px - 1.5rem)`)
|
|
}
|
|
getRef(target).classList.remove('hidden')
|
|
getRef(target).animate([
|
|
{ transform: 'translateY(-1rem)', opacity: 0 },
|
|
{ transform: 'translateY(0)', opacity: 1 },
|
|
], options)
|
|
.onfinish = () => {
|
|
isDropdownOpen = true
|
|
}
|
|
}
|
|
|
|
function hideDropdown(target, options) {
|
|
if (!isDropdownOpen) return
|
|
getRef(target).animate([
|
|
{ transform: 'translateY(0)', opacity: 1 },
|
|
{ transform: 'translateY(-1rem)', opacity: 0 },
|
|
], options)
|
|
.onfinish = () => {
|
|
isDropdownOpen = false
|
|
getRef(target).classList.add('hidden')
|
|
}
|
|
}
|
|
|
|
function showPage(target) {
|
|
document.querySelector('.page:not(.hidden)')?.classList.add('hidden')
|
|
getRef(target).classList.remove('hidden')
|
|
if (target === 'home_page') {
|
|
clearAddedInvestors()
|
|
getRef("create_fund_form").reset()
|
|
if (window.location.hash !== '') {
|
|
getRef(`${window.location.hash.split('#').pop()}`).scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
}
|
|
}
|
|
if (target !== 'confirm_term_page') {
|
|
getRef('get_term_private_key').value = ''
|
|
getRef('get_fund_private_key').value = ''
|
|
}
|
|
if (target === 'loading_page' || target === 'error_page') {
|
|
getRef('main_header').classList.add('hidden')
|
|
}
|
|
else {
|
|
getRef('main_header').classList.remove('hidden')
|
|
}
|
|
}
|
|
|
|
getRef('search_investor').addEventListener('input', debounce((e) => {
|
|
const searchKey = e.target.value.trim().toLowerCase();
|
|
document.querySelectorAll('.fund-block').forEach(block => {
|
|
block.querySelectorAll('.fund-investor').forEach(child => {
|
|
if (child.id.toLowerCase().includes(searchKey) || child.dataset.btcId.toLowerCase().includes(searchKey)) {
|
|
child.classList.remove('hidden')
|
|
} else {
|
|
child.classList.add('hidden')
|
|
}
|
|
})
|
|
if (Array.from(block.querySelectorAll('.fund-investor')).every(elem => elem.classList.contains('hidden'))) {
|
|
block.classList.add('hidden')
|
|
} else {
|
|
block.classList.remove('hidden')
|
|
}
|
|
if (Array.from(getRef('fund_list').children).every(elem => elem.classList.contains('hidden'))) {
|
|
document.getElementById('fund_list__empty-state')?.classList.remove('hidden')
|
|
} else {
|
|
document.getElementById('fund_list__empty-state')?.classList.add('hidden')
|
|
}
|
|
})
|
|
}, 100))
|
|
|
|
|
|
getRef('investors_input_list').addEventListener('click', e => {
|
|
if (e.target.closest('.remove-investor')) {
|
|
e.target.closest('.investor-input').remove()
|
|
}
|
|
})
|
|
|
|
function renderInvestor() {
|
|
getRef('investors_input_list').append(render.investorInput())
|
|
getRef('investors_input_list').lastElementChild.scrollIntoView()
|
|
getRef('investors_input_list').lastElementChild.children[0].focusIn()
|
|
}
|
|
|
|
function clearAddedInvestors() {
|
|
getRef('investors_input_list').innerHTML = ``
|
|
getRef('investors_input_list').append(render.investorInput())
|
|
}
|
|
|
|
function getAddedInvestors() {
|
|
const allInvestorsInput = getRef('investors_input_list').querySelectorAll('.investor-input')
|
|
const addedInvestors = []
|
|
allInvestorsInput.forEach(investor => {
|
|
const floId = investor.children[0].value.trim()
|
|
const amount = investor.children[1].value.trim()
|
|
addedInvestors.push([floId, amount])
|
|
})
|
|
return addedInvestors
|
|
}
|
|
|
|
getRef('fund_creation_toggle').addEventListener('change', e => {
|
|
if (e.target.checked) {
|
|
getRef('fund_details_form').classList.add('hidden')
|
|
getRef('fund_details_form').querySelectorAll('input').forEach(input => input.disabled = true)
|
|
getRef('create_fund_button').classList.add('hidden')
|
|
getRef('add_investors_button').classList.remove('hidden')
|
|
getRef('fund_selector_container').classList.remove('hidden')
|
|
}
|
|
else {
|
|
getRef('fund_details_form').classList.remove('hidden')
|
|
getRef('create_fund_button').classList.remove('hidden')
|
|
getRef('fund_details_form').querySelectorAll('input').forEach(input => input.disabled = false)
|
|
getRef('add_investors_button').classList.add('hidden')
|
|
getRef('fund_selector_container').classList.add('hidden')
|
|
}
|
|
})
|
|
|
|
getRef('tapout_toggle').addEventListener('change', e => {
|
|
if (!e.target.checked) {
|
|
getRef('tapout_container').classList.add('hidden')
|
|
getRef('tapout_container').querySelectorAll('input').forEach(input => input.disabled = true)
|
|
}
|
|
else {
|
|
getRef('tapout_container').classList.remove('hidden')
|
|
getRef('tapout_container').querySelectorAll('input').forEach(input => input.disabled = false)
|
|
}
|
|
})
|
|
/* function renderfundPlaceholder(){
|
|
getRef('fund_list').innerHTML = ``
|
|
const frag = document.createDocumentFragment()
|
|
for (let index = 0; index < 4; index++) {
|
|
frag.append(render.fundPlaceholder())
|
|
}
|
|
getRef('fund_list').append(frag)
|
|
} */
|
|
|
|
/* getRef('term_selector').addEventListener('change', e => {
|
|
const floID = e.detail.value
|
|
getRef('fund_selector').querySelectorAll('.fund-option').forEach(option => option.classList.add('hidden'))
|
|
getRef('fund_selector').querySelector(`.fund-option[data-flo-id="${floID}"]`)?.classList.remove('hidden')
|
|
}) */
|
|
|
|
getRef('refresh_button').addEventListener("click", refresh);
|
|
|
|
var USD_current, BTC_current;
|
|
function refresh(showLoader = true) {
|
|
if (showLoader)
|
|
showPage('loading_page')
|
|
getCurrentRates().then(async (rates) => {
|
|
USD_current = rates.USD_INR;
|
|
BTC_current = rates.BTC_USD;
|
|
console.log(`USD rate: ${USD_current} INR\nBTC rate: ${BTC_current} USD`);
|
|
getRef("usd-rate").textContent = `USD: ₹${rates.USD_INR.toFixed(2)}`;
|
|
getRef("btc-usd-rate").textContent = `BTC: ${parseFloat(rates.BTC_USD.toFixed(2)).toLocaleString(`en-US`, { style: 'currency', currency: 'USD' })}`;
|
|
getRef('fund_list').innerHTML = '';
|
|
getRef('fund_selector').innerHTML = ''
|
|
//getRef('term_selector').innerHTML = ''
|
|
refreshBlockchainData().then(funds => {
|
|
renderFunds(funds)
|
|
showPage('home_page')
|
|
}).catch(error => {
|
|
console.error(error)
|
|
showPage('error_page')
|
|
})
|
|
}).catch(error => console.error(error))
|
|
}
|
|
|
|
floGlobals.investments = {}
|
|
function renderFunds(funds) {
|
|
if (!Object.keys(funds).length)
|
|
return;
|
|
const removeElementIfExist = id => {
|
|
let existing = document.getElementById(id);
|
|
if (existing)
|
|
existing.remove();
|
|
}
|
|
|
|
const selectableFunds = []
|
|
let sortArray = [];
|
|
|
|
for (let k in funds) {
|
|
let f = bobsFund.parse(funds[k].map(a => a.data));
|
|
// console.info(f);
|
|
let startDate = new Date(f.start_date).getTime()
|
|
let tapouts = []
|
|
if (f.tapoutInterval)
|
|
tapouts = f.tapoutInterval.map((interval, index) => {
|
|
const start = bobsFund.dateAdder(startDate, interval)
|
|
const end = bobsFund.dateAdder(start, f.topoutWindow)
|
|
return { start, end }
|
|
})
|
|
const fundObj = {
|
|
fundTxs: funds[k],
|
|
startDate: bobsFund.dateFormat(f.start_date),
|
|
endDate: bobsFund.dateAdder(startDate, f.duration),
|
|
baseUsd: f.USD_base,
|
|
baseBtc: {
|
|
usd: f.BTC_base,
|
|
inr: f.BTC_base * f.USD_base
|
|
},
|
|
tapouts,
|
|
}
|
|
const hasMatured = new Date(fundObj.endDate) < new Date()
|
|
const allowsEarlyWithdrawal = tapouts.some(tapout => new Date(tapout.start) < new Date() && new Date(tapout.end) > new Date())
|
|
|
|
// Creating fund selection options
|
|
selectableFunds.push(html`
|
|
<sm-option value="${funds[k][0].txid}">
|
|
<div class="grid gap-0-5">
|
|
<span>Start date: ${fundObj.startDate}</span>
|
|
<span>Base BTC: ${parseFloat(fundObj.baseBtc.usd).toLocaleString(`en-US`, { style: 'currency', currency: 'USD' })} | Base USD: ₹${fundObj.baseUsd}</span>
|
|
</div>
|
|
</sm-option>
|
|
`)
|
|
|
|
const investorsFrag = []
|
|
let total_invested = total_net = 0;
|
|
for (let investor in f.investments) {
|
|
const { amount, closed } = f.investments[investor]
|
|
let netVal = bobsFund.calcNetValue(f.BTC_base, BTC_current, f.USD_base, USD_current, amount, f.fee);
|
|
const obj = {
|
|
fundId: k,
|
|
floId: investor,
|
|
amountInvested: {
|
|
inr: amount.toFixed(2),
|
|
usd: (amount / f.USD_base).toFixed(2),
|
|
},
|
|
netValue: {
|
|
inr: netVal.toFixed(2),
|
|
usd: (netVal / USD_current).toFixed(2),
|
|
},
|
|
hasMatured,
|
|
allowsEarlyWithdrawal,
|
|
}
|
|
total_invested += amount;
|
|
if (closed) {
|
|
console.log(closed)
|
|
/* TODO: UI: render closing data
|
|
if closed, netVal should not be displayed
|
|
f.investments[investor].closed -> Object {
|
|
BTC_net: BTC value at closing date
|
|
endDate: closing date
|
|
amountFinal: final amount (withdrawn)
|
|
payment_refRef: if amount is withdrawn via token system, txid of the token transfer
|
|
USD_net: USD value at closing date
|
|
refSign: signature of the investor for closing (hidden)
|
|
i: index of the txid (of closing tx) ie, funds[k][i].txid
|
|
}
|
|
*/
|
|
total_net += closed.amountFinal;
|
|
obj.finalAmount = {
|
|
inr: closed.amountFinal.toFixed(2),
|
|
usd: (closed.amountFinal / closed.USD_net).toFixed(2),
|
|
}
|
|
investorsFrag.push(render.closedInvestmentCard({ ...obj, ...closed }))
|
|
floGlobals.investments[`${k}_${investor}`] = { amountInvested: obj.amountInvested, netValue: obj.finalAmount }
|
|
} else {
|
|
total_net += netVal;
|
|
investorsFrag.push(render.investmentCard(obj))
|
|
floGlobals.investments[`${k}_${investor}`] = { amountInvested: obj.amountInvested, netValue: obj.netValue }
|
|
}
|
|
}
|
|
fundObj.totalInvestment = {
|
|
inr: total_invested.toFixed(2),
|
|
usd: (total_invested / f.USD_base).toFixed(2)
|
|
}
|
|
fundObj.totalNet = {
|
|
inr: total_net.toFixed(2),
|
|
usd: (total_net / USD_current).toFixed(2)
|
|
}
|
|
floGlobals.investments[k] = { totalInvestment: fundObj.totalInvestment, totalNet: fundObj.totalNet, baseBtc: fundObj.baseBtc }
|
|
fundObj.investorsFrag = investorsFrag
|
|
const fundBlock = render.fundBlock(fundObj)
|
|
removeElementIfExist(k);
|
|
fundBlock.id = k;
|
|
let i = sortArray.length - 1;
|
|
while (i >= 0 && sortArray[i] < startDate) { //[9, 7, 5, 3]
|
|
sortArray[i + 1] = sortArray[i]
|
|
i--;
|
|
}
|
|
sortArray[i + 1] = startDate;
|
|
getRef('fund_list').insertBefore(fundBlock, getRef('fund_list').childNodes[i + 1]);
|
|
}
|
|
|
|
//const fundGroup = document.createElement('section')
|
|
//fundGroup.dataset.floId = term.floID
|
|
//fundGroup.classList.add('fund-option')
|
|
//fundGroup.append(fundsFrag)
|
|
renderElem(getRef('fund_selector'), html`${selectableFunds}`)
|
|
}
|
|
|
|
/* getRef("create_term_form").addEventListener("submit", evt => {
|
|
evt.preventDefault();
|
|
let f = evt.target;
|
|
let tap_it = getRef("tap_it").value
|
|
let tapoutInterval = f["tap_iv"].value.split(",").map(v => v.trim() + " " + tap_it);
|
|
let termStr = createTermString(f["floid"].value, f["max_pv"].value + " " + getRef("max_pt").value, f["tap_wv"].value + " " + getRef("tap_wt").value, tapoutInterval)
|
|
console.log(termStr)
|
|
|
|
getRef('term_details').innerHTML = termStr.replace(/\|/g, "<br>")
|
|
getRef('term_admin_id').innerHTML = `Enter Private key of adminID <h5 class="weight-400">${floGlobals.adminID}</h5>`
|
|
showPage('confirm_term_page')
|
|
getRef('create_term_button').onclick = () => {
|
|
const privKey = getRef('get_term_private_key').value
|
|
if (!floCrypto.verifyPrivKey(privKey, floGlobals.adminID)) {
|
|
notify("Access Denied! incorrect private key", 'error');
|
|
return
|
|
}
|
|
floBlockchainAPI.writeData(floGlobals.adminID, termStr, privKey, f["floid"].value).then(result => {
|
|
console.log(result);
|
|
showPage('admin_page')
|
|
notify("Term added in blockchain", 'success');
|
|
getRef("create_term_form").reset()
|
|
refresh(false)
|
|
}).catch(error => console.error(error))
|
|
}
|
|
}) */
|
|
|
|
getRef("create_fund_form").addEventListener("submit", evt => {
|
|
evt.preventDefault(); //tapout_toggle
|
|
let f, fStr, duration, tapoutWindow, tapoutInterval, investments;
|
|
f = evt.target
|
|
duration = f["max_pv"].value + " " + getRef("max_pt").value
|
|
if (!getRef("tapout_toggle").checked)
|
|
tapoutWindow = tapoutInterval = null
|
|
else {
|
|
let tap_it = getRef("tap_it").value
|
|
tapoutInterval = f["tap_iv"].value.split(",").map(v => v.trim() + " " + tap_it)
|
|
tapoutWindow = f["tap_wv"].value + " " + getRef("tap_wt").value
|
|
}
|
|
investments = getAddedInvestors()
|
|
|
|
let createMod = !getRef("fund_creation_toggle").checked;
|
|
console.info(investments)
|
|
if (createMod) //create new fund
|
|
fStr = bobsFund.stringify.main(f["btc_base"].value, f["usd_rate"].value, f["start_date"].value, duration, investments, f["fee"].value, tapoutWindow, tapoutInterval);
|
|
else //add investments to existing fund
|
|
fStr = bobsFund.stringify.continue(getRef("fund_selector").value, investments);
|
|
|
|
console.log(fStr);
|
|
if (fStr.length >= 1040) {
|
|
console.error("flo data length is too long, Please reduce it and try again");
|
|
notify("floData is too large! Please reduce it and try again.", 'error');
|
|
return
|
|
}
|
|
getRef('fund_details').innerHTML = fStr.replace(/\|/g, "<br>")
|
|
getRef('fund_admin_id').innerHTML = `Enter Private key of admin ID <h5 class="weight-400">${floGlobals.adminID}</h5>`
|
|
showPage('confirm_fund_page')
|
|
|
|
getRef('confirm_fund_button').onclick = () => {
|
|
const privKey = getRef('get_fund_private_key').value
|
|
console.log(privKey)
|
|
if (!floCrypto.verifyPrivKey(privKey, floGlobals.adminID)) {
|
|
notify("Access Denied! incorrect private key", 'error');
|
|
return
|
|
}
|
|
floBlockchainAPI.writeData(floGlobals.adminID, fStr, privKey, floGlobals.adminID).then(result => {
|
|
console.log(result);
|
|
showPage('admin_page')
|
|
notify(createMod ? "New Fund created" : "Invesments added to fund", 'success');
|
|
getRef("create_fund_form").reset()
|
|
clearAddedInvestors()
|
|
refresh(false)
|
|
}).catch(error => console.error(error))
|
|
}
|
|
})
|
|
getRef('fund_list').addEventListener('click', e => {
|
|
if (e.target.closest('.fund-investor__withdraw')) {
|
|
floGlobals.withdrawId = e.target.closest('.fund-investor').id.split('_')
|
|
const [fundId, investorId] = floGlobals.withdrawId
|
|
renderElem(getRef('withdraw__id'), html`<div class='label'>Investor address</div> <strong class="value">${investorId}</strong>`)
|
|
openPopup('withdraw_popup')
|
|
}
|
|
})
|
|
getRef('withdraw_private_key_button').addEventListener('click', e => {
|
|
buttonLoader('withdraw_private_key_button', true)
|
|
const privKey = getRef('withdraw_private_key').value
|
|
const [fundId, investorId] = floGlobals.withdrawId
|
|
floExchangeAPI.closeBobsFundInvestment(fundId, investorId, privKey).then(result => {
|
|
console.log(result)
|
|
showChildElement(getRef('withdraw_process'), 1, { entry: slideInLeft, exit: slideOutLeft });
|
|
const withdrawButton = document.getElementById(floGlobals.withdrawId.join('_'))?.querySelector('.fund-investor__withdraw')
|
|
if (withdrawButton) {
|
|
withdrawButton.textContent = 'Withdrawn'
|
|
withdrawButton.disabled = true
|
|
}
|
|
}).catch(error => {
|
|
getRef('withdraw_failed_message').textContent = error.message
|
|
showChildElement(getRef('withdraw_process'), 2, { entry: slideInLeft, exit: slideOutLeft });
|
|
console.error(error)
|
|
}).finally(() => {
|
|
buttonLoader('withdraw_private_key_button', false)
|
|
})
|
|
})
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|