Compare commits

..

No commits in common. "main" and "dev" have entirely different histories.
main ... dev

15 changed files with 360 additions and 829 deletions

View File

@ -1,32 +0,0 @@
name: Workflow push to Dappbundle
on: [push]
jobs:
build:
name: Build
runs-on: self-hosted
steps:
- name: Executing remote command
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.R_HOST }}
username: ${{ secrets.P_USERNAME }}
password: ${{ secrets.P_PASSWORD }}
port: ${{ secrets.SSH_PORT }}
script: |
if [ -d "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle" ]; then
echo "Folder exists. Skipping Git clone."
else
echo "Folder does not exist. Cloning repository..."
cd ${{ secrets.DEPLOYMENT_LOCATION}}/ && git clone https://github.com/ranchimall/dappbundle.git
fi
if [ -d "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}" ]; then
echo "Repository exists. Remove folder "
rm -r "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}"
fi
echo "Cloning repository..."
cd ${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle && git clone https://github.com/ranchimall/${{ github.event.repository.name }}
cd "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}" && rm -rf .gitattributes .git .github .gitignore
cd ${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/ && git add . && git commit -m "Workflow updating files of ${{ github.event.repository.name }}" && git push "https://saketongit:${{ secrets.RM_ACCESS_TOKEN }}@github.com/ranchimall/dappbundle.git"

View File

@ -27,6 +27,7 @@ body {
scrollbar-gutter: stable; scrollbar-gutter: stable;
color: rgba(var(--text-color), 1); color: rgba(var(--text-color), 1);
background-color: rgba(var(--background-color), 1); background-color: rgba(var(--background-color), 1);
transition: background-color 0.3s;
} }
body[data-theme=dark] { body[data-theme=dark] {
@ -452,6 +453,24 @@ ul {
align-content: flex-start; align-content: flex-start;
} }
.logo {
display: grid;
align-items: center;
grid-template-columns: auto 1fr;
gap: 0 0.3rem;
margin-right: auto;
}
.logo h4 {
text-transform: capitalize;
font-weight: 600;
}
.logo .main-logo {
height: 1.4rem;
width: 1.4rem;
fill: rgba(var(--text-color), 1);
stroke: none;
}
details:not(:last-of-type) { details:not(:last-of-type) {
border-bottom: thin solid rgba(var(--text-color), 0.3); border-bottom: thin solid rgba(var(--text-color), 0.3);
} }
@ -664,22 +683,6 @@ sm-checkbox {
align-items: center; align-items: center;
} }
.app-brand {
display: flex;
gap: 0.3rem;
align-items: center;
}
.app-brand .icon {
height: 1.7rem;
width: 1.7rem;
}
.app-name__company {
font-size: 0.8rem;
font-weight: 500;
color: rgba(var(--text-color), 0.8);
}
#user_popup_button { #user_popup_button {
background-color: rgba(var(--text-color), 0.06); background-color: rgba(var(--text-color), 0.06);
border-radius: 2rem; border-radius: 2rem;
@ -736,6 +739,7 @@ sm-checkbox {
padding: 1rem 0; padding: 1rem 0;
border-radius: 0.5rem; border-radius: 0.5rem;
font-weight: 400; font-weight: 400;
transition: background-color 0.3s;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
user-select: none; user-select: none;
@ -869,6 +873,7 @@ sm-checkbox {
top: 0; top: 0;
background-color: rgba(var(--background-color), 1); background-color: rgba(var(--background-color), 1);
z-index: 2; z-index: 2;
transition: background-color 0.3s;
} }
.list__item { .list__item {
@ -1574,6 +1579,7 @@ sm-checkbox {
flex: 0; flex: 0;
flex-direction: row; flex-direction: row;
border-radius: 0.5rem; border-radius: 0.5rem;
transition: background-color 0.3s;
} }
.main_navbar__item .icon { .main_navbar__item .icon {
margin-bottom: 0; margin-bottom: 0;

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,7 @@
<script src="scripts/floBlockchainAPI.js"></script> <script src="scripts/floBlockchainAPI.js"></script>
<script src="scripts/floTokenAPI.js"></script> <script src="scripts/floTokenAPI.js"></script>
<script src="scripts/floExchangeAPI.js"></script> <script src="scripts/floExchangeAPI.js"></script>
<script src="https://unpkg.com/lightweight-charts@3.8.0/dist/lightweight-charts.standalone.production.js"></script> <script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
<script> <script>
const DEFAULT_TRADE_PRICE_DEVIATION_BUY = 15 / 100; const DEFAULT_TRADE_PRICE_DEVIATION_BUY = 15 / 100;
const DEFAULT_TRADE_PRICE_DEVIATION_SELL = 10 / 100; const DEFAULT_TRADE_PRICE_DEVIATION_SELL = 10 / 100;
@ -71,17 +71,14 @@
</div> </div>
<article id="home" class="page hidden"> <article id="home" class="page hidden">
<header id="main_header"> <header id="main_header">
<div class="app-brand margin-right-auto"> <div class="logo">
<svg id="main_logo" class="icon" viewBox="0 0 27.25 32"> <svg class="main-logo" viewBox="0 0 27.25 32">
<title>RanchiMall</title> <title>RanchiMall</title>
<path <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" /> 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> </svg>
<div class="app-name"> <div class="grid">
<div class="app-name__company">RanchiMall</div> <h4>RanchiMall Exchange</h4>
<h4 class="app-name__title">
Exchange
</h4>
</div> </div>
</div> </div>
<theme-toggle id="theme_toggle"></theme-toggle> <theme-toggle id="theme_toggle"></theme-toggle>
@ -842,6 +839,7 @@
<h4 id="portfolio_popup__title" class="capitalize"></h4> <h4 id="portfolio_popup__title" class="capitalize"></h4>
</header> </header>
<sm-form id="portfolio_form"> <sm-form id="portfolio_form">
<input type="text" id="sink_id" style="display: none;" hidden />
<sm-input id="get_receiver_id" placeholder="Receiver FLO ID" error-text="Invalid FLO ID" data-flo-id <sm-input id="get_receiver_id" placeholder="Receiver FLO ID" error-text="Invalid FLO ID" data-flo-id
required animate></sm-input> required animate></sm-input>
<sm-input id="get_user_amount" placeholder="Quantity" type="number" min="0.00000001" step="0.00000001" <sm-input id="get_user_amount" placeholder="Quantity" type="number" min="0.00000001" step="0.00000001"
@ -2119,7 +2117,7 @@
entry.querySelector('.history-entry__token-action').textContent = `${type} ${asset}` entry.querySelector('.history-entry__token-action').textContent = `${type} ${asset}`
entry.querySelector('.history-entry__amount').textContent = amount entry.querySelector('.history-entry__amount').textContent = amount
entry.querySelector('.history-entry__time').textContent = getFormattedTime(locktime) entry.querySelector('.history-entry__time').textContent = getFormattedTime(locktime)
entry.querySelector('.history-entry__txid').href = floBlockchainAPI.current_server + `tx/${txid}` entry.querySelector('.history-entry__txid').href = `https://flosight.duckdns.org/tx/${txid}`
return entry; return entry;
} }
} }
@ -2813,6 +2811,7 @@
floExchangeAPI.getAccount(proxy.userID, await proxy.secret).then(async acc => { floExchangeAPI.getAccount(proxy.userID, await proxy.secret).then(async acc => {
document.body.classList.add('is-signed-in'); document.body.classList.add('is-signed-in');
getRef('market_asset_rates').parentNode.remove() getRef('market_asset_rates').parentNode.remove()
location.hash = `#/exchange`
getRef('user_popup_button').addEventListener('click', () => openPopup('user_popup')); getRef('user_popup_button').addEventListener('click', () => openPopup('user_popup'));
getRef('trade_button').addEventListener('click', () => openPopup('confirm_trade_popup')); getRef('trade_button').addEventListener('click', () => openPopup('confirm_trade_popup'));
accountDetails = acc accountDetails = acc
@ -2821,6 +2820,7 @@
document.querySelectorAll(".user-content").forEach(elem => elem.classList.remove('hidden')) document.querySelectorAll(".user-content").forEach(elem => elem.classList.remove('hidden'))
document.querySelectorAll(".my-flo-id").forEach(elem => elem.textContent = acc.floID) document.querySelectorAll(".my-flo-id").forEach(elem => elem.textContent = acc.floID)
getRef("user_id").value = acc.floID; getRef("user_id").value = acc.floID;
getRef("sink_id").value = acc.sinkID;
if (acc.subAdmin) if (acc.subAdmin)
console.info("logged in as subAdmin"); console.info("logged in as subAdmin");

View File

@ -1,13 +1,10 @@
(function (EXPORTS) { //btcOperator v1.1.3b (function (EXPORTS) { //btcOperator v1.1.2a
/* BTC Crypto and API Operator */ /* BTC Crypto and API Operator */
const btcOperator = EXPORTS; const btcOperator = EXPORTS;
//This library uses API provided by chain.so (https://chain.so/) //This library uses API provided by chain.so (https://chain.so/)
const URL = "https://blockchain.info/"; const URL = "https://blockchain.info/";
const DUST_AMT = 546,
MIN_FEE_UPDATE = 219;
const fetch_api = btcOperator.fetch = function (api, json_res = true) { const fetch_api = btcOperator.fetch = function (api, json_res = true) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
console.debug(URL + api); console.debug(URL + api);
@ -403,12 +400,7 @@
return reject("Send amount is less than fee"); return reject("Send amount is less than fee");
} }
//remove all output with value less than DUST amount tx.outs = tx.outs.filter(o => o.value != 0); //remove all output with value 0
let filtered_outputs = [], dust_value = 0;
tx.outs.forEach(o => o.value >= DUST_AMT ? filtered_outputs.push(o) : dust_value += o.value);
tx.outs = filtered_outputs;
//update result values
result.fee += util.Sat_to_BTC(dust_value);
result.output_size = output_size; result.output_size = output_size;
result.output_amount = total_amount - (fee_from_receiver ? result.fee : 0); result.output_amount = total_amount - (fee_from_receiver ? result.fee : 0);
result.total_size = BASE_TX_SIZE + output_size + result.input_size; result.total_size = BASE_TX_SIZE + output_size + result.input_size;
@ -464,7 +456,7 @@
let size_per_input = _sizePerInput(addr, rs); let size_per_input = _sizePerInput(addr, rs);
fetch_api(`unspent?active=${addr}`).then(result => { fetch_api(`unspent?active=${addr}`).then(result => {
let utxos = result.unspent_outputs; let utxos = result.unspent_outputs;
//console.debug("add-utxo", addr, rs, required_amount, utxos); console.debug("add-utxo", addr, rs, required_amount, utxos);
for (let i = 0; i < utxos.length && required_amount > 0; i++) { for (let i = 0; i < utxos.length && required_amount > 0; i++) {
if (!utxos[i].confirmations) //ignore unconfirmed utxo if (!utxos[i].confirmations) //ignore unconfirmed utxo
continue; continue;
@ -544,106 +536,6 @@
} }
*/ */
function tx_fetch_for_editing(tx) {
return new Promise((resolve, reject) => {
if (typeof tx == 'string' && /^[0-9a-f]{64}$/i.test(tx)) { //tx is txid
getTx.hex(tx)
.then(txhex => resolve(deserializeTx(txhex)))
.catch(error => reject(error))
} else resolve(deserializeTx(tx));
})
}
btcOperator.editFee = function (tx_hex, new_fee, private_keys, change_only = true) {
return new Promise((resolve, reject) => {
if (!Array.isArray(private_keys))
private_keys = [private_keys];
tx_fetch_for_editing(tx_hex).then(tx => {
parseTransaction(tx).then(tx_parsed => {
if (tx_parsed.fee >= new_fee)
return reject("Fees can only be increased");
//editable addresses in output values (for fee increase)
var edit_output_address = new Set();
if (change_only === true) //allow only change values (ie, sender address) to be edited to inc fee
tx_parsed.inputs.forEach(inp => edit_output_address.add(inp.address));
else if (change_only === false) //allow all output values to be edited
tx_parsed.outputs.forEach(out => edit_output_address.add(out.address));
else if (typeof change_only == 'string') // allow only given receiver id output to be edited
edit_output_address.add(change_only);
else if (Array.isArray(change_only)) //allow only given set of receiver id outputs to be edited
change_only.forEach(id => edit_output_address.add(id));
//edit output values to increase fee
let inc_fee = util.BTC_to_Sat(new_fee - tx_parsed.fee);
if (inc_fee < MIN_FEE_UPDATE)
return reject(`Insufficient additional fee. Minimum increment: ${MIN_FEE_UPDATE}`);
for (let i = tx.outs.length - 1; i >= 0 && inc_fee > 0; i--) //reduce in reverse order
if (edit_output_address.has(tx_parsed.outputs[i].address)) {
let current_value = tx.outs[i].value;
if (current_value instanceof BigInteger) //convert BigInteger class to inv value
current_value = current_value.intValue();
//edit the value as required
if (current_value > inc_fee) {
tx.outs[i].value = current_value - inc_fee;
inc_fee = 0;
} else {
inc_fee -= current_value;
tx.outs[i].value = 0;
}
}
if (inc_fee > 0) {
let max_possible_fee = util.BTC_to_Sat(new_fee) - inc_fee; //in satoshi
return reject(`Insufficient output values to increase fee. Maximum fee possible: ${util.Sat_to_BTC(max_possible_fee)}`);
}
tx.outs = tx.outs.filter(o => o.value >= DUST_AMT); //remove all output with value less than DUST amount
//remove existing signatures and reset the scripts
let wif_keys = [];
for (let i in tx.ins) {
var addr = tx_parsed.inputs[i].address,
value = util.BTC_to_Sat(tx_parsed.inputs[i].value);
let addr_decode = coinjs.addressDecode(addr);
//find the correct key for addr
var privKey = private_keys.find(pk => verifyKey(addr, pk));
if (!privKey)
return reject(`Private key missing for ${addr}`);
//find redeemScript (if any)
const rs = _redeemScript(addr, privKey);
rs === false ? wif_keys.unshift(privKey) : wif_keys.push(privKey); //sorting private-keys (wif)
//reset the script for re-signing
var script;
if (!rs || !rs.length) {
//legacy script (derive from address)
let s = coinjs.script();
s.writeOp(118); //OP_DUP
s.writeOp(169); //OP_HASH160
s.writeBytes(addr_decode.bytes);
s.writeOp(136); //OP_EQUALVERIFY
s.writeOp(172); //OP_CHECKSIG
script = Crypto.util.bytesToHex(s.buffer);
} else if (((rs.match(/^00/) && rs.length == 44)) || (rs.length == 40 && rs.match(/^[a-f0-9]+$/gi)) || addr_decode.type === 'multisigBech32') {
//redeemScript for segwit/bech32 and multisig (bech32)
let s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(rs));
s.writeOp(0);
s.writeBytes(coinjs.numToBytes(value.toFixed(0), 8));
script = Crypto.util.bytesToHex(s.buffer);
} else //redeemScript for multisig (segwit)
script = rs;
tx.ins[i].script = coinjs.script(script);
}
tx.witness = false; //remove all witness signatures
console.debug("Unsigned:", tx.serialize());
//re-sign the transaction
new Set(wif_keys).forEach(key => tx.sign(key, 1 /*sighashtype*/)); //Sign the tx using private key WIF
resolve(tx.serialize());
}).catch(error => reject(error))
}).catch(error => reject(error))
})
}
btcOperator.sendTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) { btcOperator.sendTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
createSignedTx(senders, privkeys, receivers, amounts, fee, options).then(result => { createSignedTx(senders, privkeys, receivers, amounts, fee, options).then(result => {
@ -687,7 +579,7 @@
createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver).then(result => { createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver).then(result => {
let tx = result.transaction; let tx = result.transaction;
console.debug("Unsigned:", tx.serialize()); console.debug("Unsigned:", tx.serialize());
new Set(wif_keys).forEach(key => tx.sign(key, 1 /*sighashtype*/)); //Sign the tx using private key WIF new Set(wif_keys).forEach(key => console.debug("Signing key:", key, tx.sign(key, 1 /*sighashtype*/))); //Sign the tx using private key WIF
console.debug("Signed:", tx.serialize()); console.debug("Signed:", tx.serialize());
resolve(result); resolve(result);
}).catch(error => reject(error)); }).catch(error => reject(error));
@ -831,7 +723,7 @@
.catch(error => reject(error)) .catch(error => reject(error))
}); });
const parseTransaction = btcOperator.parseTransaction = function (tx) { btcOperator.parseTransaction = function (tx) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
tx = deserializeTx(tx); tx = deserializeTx(tx);
let result = {}; let result = {};
@ -888,7 +780,7 @@
.catch(error => reject(error)) .catch(error => reject(error))
}) })
const getTx = btcOperator.getTx = txid => new Promise((resolve, reject) => { btcOperator.getTx = txid => new Promise((resolve, reject) => {
fetch_api(`rawtx/${txid}`).then(result => { fetch_api(`rawtx/${txid}`).then(result => {
getLatestBlock().then(latest_block => resolve({ getLatestBlock().then(latest_block => resolve({
block: result.block_height, block: result.block_height,
@ -905,7 +797,7 @@
}).catch(error => reject(error)) }).catch(error => reject(error))
}); });
getTx.hex = txid => new Promise((resolve, reject) => { btcOperator.getTx.hex = txid => new Promise((resolve, reject) => {
fetch_api(`rawtx/${txid}?format=hex`, false) fetch_api(`rawtx/${txid}?format=hex`, false)
.then(result => resolve(result)) .then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
(function (EXPORTS) { //floExchangeAPI v1.2.1 (function (EXPORTS) { //floExchangeAPI v1.2.0
const exchangeAPI = EXPORTS; const exchangeAPI = EXPORTS;
const DEFAULT = { const DEFAULT = {
@ -608,7 +608,7 @@
BOBS_FUND: "bobs-fund" BOBS_FUND: "bobs-fund"
} }
const getSink = exchangeAPI.getSink = function (service = serviceList.EXCHANGE) { exchangeAPI.getSink = function (service = serviceList.EXCHANGE) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!(Object.values(serviceList).includes(service))) if (!(Object.values(serviceList).includes(service)))
return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, 'service required', errorCode.INVALID_VALUE)); return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, 'service required', errorCode.INVALID_VALUE));
@ -1720,7 +1720,7 @@
const _l = key => DEFAULT.marketApp + '-' + key; const _l = key => DEFAULT.marketApp + '-' + key;
function refreshDataFromBlockchain() { exchangeAPI.init = function refreshDataFromBlockchain() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let nodes, trusted = new Set(), assets, tags, lastTx; let nodes, trusted = new Set(), assets, tags, lastTx;
try { try {
@ -1731,12 +1731,13 @@
if (typeof nodes !== 'object' || nodes === null) if (typeof nodes !== 'object' || nodes === null)
throw Error('nodes must be an object') throw Error('nodes must be an object')
else else
lastTx = localStorage.getItem(_l('lastTx')); lastTx = parseInt(localStorage.getItem(_l('lastTx'))) || 0;
} catch (error) { } catch (error) {
nodes = {}; nodes = {};
trusted = new Set(); trusted = new Set();
assets = new Set(); assets = new Set();
tags = new Set(); tags = new Set();
lastTx = undefined;
} }
var query_options = { sentOnly: true, pattern: DEFAULT.marketApp }; var query_options = { sentOnly: true, pattern: DEFAULT.marketApp };
@ -1783,7 +1784,7 @@
tags.add(t); tags.add(t);
} }
}); });
localStorage.setItem(_l('lastTx'), result.lastItem); localStorage.setItem(_l('lastTx'), result.totalTxs);
localStorage.setItem(_l('nodes'), JSON.stringify(nodes)); localStorage.setItem(_l('nodes'), JSON.stringify(nodes));
localStorage.setItem(_l('trusted'), Array.from(trusted).join(",")); localStorage.setItem(_l('trusted'), Array.from(trusted).join(","));
localStorage.setItem(_l('assets'), Array.from(assets).join(",")); localStorage.setItem(_l('assets'), Array.from(assets).join(","));
@ -1796,17 +1797,6 @@
}) })
} }
exchangeAPI.init = function (service = serviceList.EXCHANGE) {
return new Promise((resolve, reject) => {
refreshDataFromBlockchain().then(nodes => {
getSink(service)
.then(sinkID => floCrypto.validateAddr(sinkID) ? _sinkID = sinkID : undefined)
.catch(error => console.warn("Unable to fetch sinkID", error))
.finally(_ => resolve(nodes))
}).catch(error => reject(error))
})
}
const config = exchangeAPI.config = { const config = exchangeAPI.config = {
get trustedList() { get trustedList() {
return new Set((localStorage.getItem(_l('trusted')) || "").split(',')); return new Set((localStorage.getItem(_l('trusted')) || "").split(','));
@ -1831,10 +1821,12 @@
} }
//container for user ID and proxy private-key //container for user ID and proxy private-key
var _userID, _publicKey, _privateKey, _sinkID;
const proxy = exchangeAPI.proxy = { const proxy = exchangeAPI.proxy = {
user: null,
private: null,
public: null,
async lock() { async lock() {
if (!_privateKey) if (!this.private)
return notify("No proxy key found!", 'error'); return notify("No proxy key found!", 'error');
getPromptInput("Add password", 'This password applies to this browser only!', { getPromptInput("Add password", 'This password applies to this browser only!', {
isPassword: true, isPassword: true,
@ -1845,7 +1837,7 @@
else if (pwd.length < 4) else if (pwd.length < 4)
notify("Password minimum length is 4", 'error'); notify("Password minimum length is 4", 'error');
else { else {
let tmp = Crypto.AES.encrypt(_privateKey, pwd); let tmp = Crypto.AES.encrypt(this.private, pwd);
localStorage.setItem(_l('proxy_secret'), "?" + tmp); localStorage.setItem(_l('proxy_secret'), "?" + tmp);
notify("Successfully locked with Password", 'success'); notify("Successfully locked with Password", 'success');
} }
@ -1854,37 +1846,35 @@
clear() { clear() {
localStorage.removeItem(_l('proxy_secret')); localStorage.removeItem(_l('proxy_secret'));
localStorage.removeItem(_l('user_ID')); localStorage.removeItem(_l('user_ID'));
_userID = null; this.user = null;
_privateKey = null; this.private = null;
_publicKey = null; this.public = null;
}, },
get sinkID() { get sinkID() {
return _sinkID; return getRef("sink_id").value;
}, },
set userID(id) { set userID(id) {
localStorage.setItem(_l('user_ID'), id); localStorage.setItem(_l('user_ID'), id);
_userID = id; this.user = id;
}, },
get userID() { get userID() {
if (_userID) if (this.user)
return _userID; return this.user;
else { else {
let id = localStorage.getItem(_l('user_ID')); let id = localStorage.getItem(_l('user_ID'));
return id ? _userID = id : undefined; return id ? this.user = id : undefined;
} }
}, },
get user() {
return this.userID;
},
set secret(key) { set secret(key) {
localStorage.setItem(_l('proxy_secret'), key); localStorage.setItem(_l('proxy_secret'), key);
_privateKey = key; this.private = key;
_publicKey = floCrypto.getPubKeyHex(key); this.public = floCrypto.getPubKeyHex(key);
}, },
get secret() { get secret() {
const self = this;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (_privateKey) if (self.private)
return resolve(_privateKey); return resolve(self.private);
const Reject = reason => { const Reject = reason => {
notify(reason, 'error'); notify(reason, 'error');
@ -1892,9 +1882,9 @@
} }
const setValues = priv => { const setValues = priv => {
try { try {
_privateKey = priv; self.private = priv;
_publicKey = floCrypto.getPubKeyHex(priv); self.public = floCrypto.getPubKeyHex(priv);
resolve(_privateKey); resolve(self.private);
} catch (error) { } catch (error) {
Reject("Unable to fetch Proxy secret"); Reject("Unable to fetch Proxy secret");
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -48,7 +48,7 @@ function confirmDepositFLO() {
verifyTx.FLO = function (sender, txid, group) { verifyTx.FLO = function (sender, txid, group) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
floBlockchainAPI.getTx(txid).then(tx => { floBlockchainAPI.getTx(txid).then(tx => {
let vin_sender = tx.vin.filter(v => v.addresses[0] === sender) let vin_sender = tx.vin.filter(v => v.addr === sender)
if (!vin_sender.length) if (!vin_sender.length)
return reject([true, "Transaction not sent by the sender"]); return reject([true, "Transaction not sent by the sender"]);
if (vin_sender.length !== tx.vin.length) if (vin_sender.length !== tx.vin.length)
@ -129,24 +129,20 @@ function confirmDepositToken() {
verifyTx.token = function (sender, txid, group, currencyOnly = false) { verifyTx.token = function (sender, txid, group, currencyOnly = false) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
floTokenAPI.getTx(txid).then(tx => { floTokenAPI.getTx(txid).then(tx => {
if (tx.type !== "transfer") if (tx.parsedFloData.type !== "transfer")
return reject([true, "Transaction type not 'transfer'"]); return reject([true, "Transaction type not 'transfer'"]);
else if (tx.transferType !== "token") else if (tx.parsedFloData.transferType !== "token")
return reject([true, "Transaction transfer is not 'token'"]); return reject([true, "Transaction transfer is not 'token'"]);
var token = tx.tokenIdentification, var token = tx.parsedFloData.tokenIdentification,
amount = tx.tokenAmount, amount = tx.parsedFloData.tokenAmount;
receiverAddress = tx.receiverAddress,
senderAddress = tx.senderAddress;
if (currencyOnly && token !== floGlobals.currency) if (currencyOnly && token !== floGlobals.currency)
return reject([true, "Token not currency"]); return reject([true, "Token not currency"]);
else if (!currencyOnly && ((!assetList.includes(token) && token !== floGlobals.currency) || token === "FLO")) else if (!currencyOnly && ((!assetList.includes(token) && token !== floGlobals.currency) || token === "FLO"))
return reject([true, "Token not authorised"]); return reject([true, "Token not authorised"]);
//let vin_sender = tx.vin.filter(v => v.addr === sender) let vin_sender = tx.transactionDetails.vin.filter(v => v.addr === sender)
if (senderAddress != sender) if (!vin_sender.length)
return reject([true, "Transaction not sent by the sender"]); return reject([true, "Transaction not sent by the sender"]);
if (!keys.sink_chest.includes(group, receiverAddress)) let flo_amount = tx.transactionDetails.vout.reduce((a, v) => keys.sink_chest.includes(group, v.scriptPubKey.addresses[0]) ? a + v.value : a, 0);
return reject([true, "Transaction receiver is not market ID"]); //Maybe reject as false? (to compensate delay in chestsList loading from other nodes)
let flo_amount = tx.vout.reduce((a, v) => keys.sink_chest.includes(group, v.scriptPubKey.addresses[0]) ? a + v.value : a, 0);
if (flo_amount == 0) if (flo_amount == 0)
return reject([true, "Transaction receiver is not market ID"]); //Maybe reject as false? (to compensate delay in chestsList loading from other nodes) return reject([true, "Transaction receiver is not market ID"]); //Maybe reject as false? (to compensate delay in chestsList loading from other nodes)
else else
@ -189,7 +185,7 @@ function confirmVaultWithdraw() {
}).catch(error => console.error(error)); }).catch(error => console.error(error));
} else if (r.asset_type == pCode.ASSET_TYPE_TOKEN) } else if (r.asset_type == pCode.ASSET_TYPE_TOKEN)
floTokenAPI.getTx(r.txid).then(tx => { floTokenAPI.getTx(r.txid).then(tx => {
if (!tx.blockheight || !tx.confirmations) //Still not confirmed if (!tx.transactionDetails.blockheight || !tx.transactionDetails.confirmations) //Still not confirmed
return; return;
DB.query("UPDATE VaultTransactions SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id]) DB.query("UPDATE VaultTransactions SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id])
.then(result => console.info("Token withdrawed:", r.floID, r.asset, r.amount)) .then(result => console.info("Token withdrawed:", r.floID, r.asset, r.amount))
@ -290,7 +286,7 @@ function confirmConvert() {
}).catch(error => console.error(error)); }).catch(error => console.error(error));
else if (r.mode == pCode.CONVERT_MODE_PUT) else if (r.mode == pCode.CONVERT_MODE_PUT)
floTokenAPI.getTx(r.out_txid).then(tx => { floTokenAPI.getTx(r.out_txid).then(tx => {
if (!tx.blockheight || !tx.confirmations) //Still not confirmed if (!tx.transactionDetails.blockheight || !tx.transactionDetails.confirmations) //Still not confirmed
return; return;
DB.query("UPDATE DirectConvert SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id]) DB.query("UPDATE DirectConvert SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id])
.then(result => console.info(`${r.floID} converted ${r.quantity} BTC to ${r.amount}`)) .then(result => console.info(`${r.floID} converted ${r.quantity} BTC to ${r.amount}`))
@ -354,7 +350,7 @@ function confirmConvertFundWithdraw() {
}).catch(error => console.error(error)); }).catch(error => console.error(error));
} else if (r.mode == pCode.CONVERT_MODE_PUT) {//withdraw currency } else if (r.mode == pCode.CONVERT_MODE_PUT) {//withdraw currency
floTokenAPI.getTx(r.txid).then(tx => { floTokenAPI.getTx(r.txid).then(tx => {
if (!tx.blockheight || !tx.confirmations) //Still not confirmed if (!tx.transactionDetails.blockheight || !tx.transactionDetails.confirmations) //Still not confirmed
return; return;
DB.query("UPDATE ConvertFund SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id]) DB.query("UPDATE ConvertFund SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id])
.then(result => console.info(`Withdraw-fund ${r.amount} ${floGlobals.currency} successful`)) .then(result => console.info(`Withdraw-fund ${r.amount} ${floGlobals.currency} successful`))
@ -411,7 +407,7 @@ function confirmConvertRefund() {
}).catch(error => console.error(error)); }).catch(error => console.error(error));
} else if (r.ASSET_TYPE_TOKEN) } else if (r.ASSET_TYPE_TOKEN)
floTokenAPI.getTx(r.out_txid).then(tx => { floTokenAPI.getTx(r.out_txid).then(tx => {
if (!tx.blockheight || !tx.confirmations) //Still not confirmed if (!tx.transactionDetails.blockheight || !tx.transactionDetails.confirmations) //Still not confirmed
return; return;
DB.query("UPDATE RefundConvert SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id]) DB.query("UPDATE RefundConvert SET r_status=? WHERE id=?", [pCode.STATUS_SUCCESS, r.id])
.then(result => console.info(`Refunded ${r.amount} ${r.asset} to ${r.floID}`)) .then(result => console.info(`Refunded ${r.amount} ${r.asset} to ${r.floID}`))
@ -504,10 +500,9 @@ function processAll() {
var lastSyncBlockHeight = 0; var lastSyncBlockHeight = 0;
function periodicProcess() { function periodicProcess() {
floBlockchainAPI.promisedAPI('api/status').then(result => { floBlockchainAPI.promisedAPI('api/blocks?limit=1').then(result => {
let blockbook_height = result.blockbook.bestHeight; if (lastSyncBlockHeight < result.blocks[0].height) {
if (lastSyncBlockHeight < blockbook_height) { lastSyncBlockHeight = result.blocks[0].height;
lastSyncBlockHeight = blockbook_height;
processAll(); processAll();
console.log("Last Block :", lastSyncBlockHeight); console.log("Last Block :", lastSyncBlockHeight);
} }

View File

@ -118,7 +118,7 @@ function sendAsset(floID, asset, quantity, type, id) {
balance_locked[sinkID][asset] -= quantity; balance_locked[sinkID][asset] -= quantity;
}); });
} }
collectAndCall(SINK_GROUP[type], sinkID, callback); //TODO: add timeout to prevent infinite wait collectAndCall(sinkID, callback); //TODO: add timeout to prevent infinite wait
callbackCollection[type][id] = callback; callbackCollection[type][id] = callback;
if (!(sinkID in balance_locked)) if (!(sinkID in balance_locked))
balance_locked[sinkID] = {}; balance_locked[sinkID] = {};