152 lines
5.6 KiB
JavaScript
152 lines
5.6 KiB
JavaScript
'use strict';
|
|
|
|
var collectAndCall; //container for collectAndCall function from backup module
|
|
var chests; //container for blockchain ids (where assets are stored)
|
|
|
|
const WITHDRAWAL_MESSAGE = "(withdrawal from market)";
|
|
|
|
const balance_locked = {},
|
|
balance_cache = {},
|
|
callbackCollection = {
|
|
Coin: {},
|
|
token: {}
|
|
};
|
|
|
|
function getBalance(sinkID, asset) {
|
|
switch (asset) {
|
|
case "FLO":
|
|
return floBlockchainAPI.getBalance(sinkID);
|
|
case "BTC":
|
|
let btc_id = btcOperator.convert.legacy2bech(sinkID);
|
|
return btcOperator.getBalance(btc_id);
|
|
default:
|
|
return floTokenAPI.getBalance(sinkID, asset);
|
|
}
|
|
}
|
|
|
|
function getSinkID(amount, asset, sinkList = null) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!sinkList)
|
|
sinkList = chests.list.map(s => [s, s in balance_cache ? balance_cache[s][asset] || 0 : 0]) //TODO: improve sorting
|
|
.sort((a, b) => b[1] - a[1]).map(x => x[0]);
|
|
if (!sinkList.length)
|
|
return reject(`Insufficient balance in chests for asset(${asset})`);
|
|
let sinkID = sinkList.shift();
|
|
getBalance(sinkID, asset).then(balance => {
|
|
if (!(sinkID in balance_cache))
|
|
balance_cache[sinkID] = {};
|
|
balance_cache[sinkID][asset] = balance;
|
|
if (balance > (amount + (sinkID in balance_locked ? balance_locked[sinkID][asset] || 0 : 0)))
|
|
return resolve(sinkID);
|
|
else
|
|
getSinkID(amount, asset, sinkList)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error))
|
|
}).catch(error => {
|
|
console.error(error);
|
|
getSinkID(amount, asset, sinkList)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error))
|
|
});
|
|
})
|
|
}
|
|
|
|
function sendTx(floID, coin, amount, sinkID, sinkKey) {
|
|
switch (coin) {
|
|
case "FLO":
|
|
return floBlockchainAPI.sendTx(sinkID, floID, amount, sinkKey, WITHDRAWAL_MESSAGE);
|
|
case "BTC":
|
|
}
|
|
}
|
|
|
|
function sendCoin(floID, coin, amount, id) {
|
|
getSinkID(amount, coin).then(sinkID => {
|
|
let callback = (sinkKey) => {
|
|
//Send Coin to user via blockchain API
|
|
sendTx(floID, coin, amount, sinkID, sinkKey).then(txid => {
|
|
if (!txid)
|
|
throw Error("Transaction not successful");
|
|
//Transaction was successful, Add in DB
|
|
DB.query("UPDATE OutputCoin SET status=?, txid=? WHERE id=?", ["WAITING_CONFIRMATION", txid, id])
|
|
.then(_ => null).catch(error => console.error(error));
|
|
}).catch(error => console.error(error)).finally(_ => {
|
|
delete callbackCollection.Coin[id];
|
|
balance_locked[sinkID][coin] -= amount;
|
|
});
|
|
}
|
|
collectAndCall(sinkID, callback);
|
|
callbackCollection.Coin[id] = callback;
|
|
if (!(sinkID in balance_locked))
|
|
balance_locked[sinkID] = {};
|
|
balance_locked[sinkID][coin] = (balance_locked[sinkID][coin] || 0) + amount;
|
|
}).catch(error => console.error(error))
|
|
}
|
|
|
|
function sendCoin_init(floID, coin, amount) {
|
|
DB.query("INSERT INTO OutputCoin (floID, coin, amount, status) VALUES (?, ?, ?, ?)", [floID, coin, amount, "PENDING"])
|
|
.then(result => sendCoin(floID, coin, amount, result.insertId))
|
|
.catch(error => console.error(error))
|
|
}
|
|
|
|
function sendCoin_retry(floID, coin, amount, id) {
|
|
if (id in callbackCollection.Coin)
|
|
console.debug("A callback is already pending for this FLO transfer");
|
|
else
|
|
sendCoin(floID, coin, amount, id);
|
|
}
|
|
|
|
function sendToken(floID, token, amount, id) {
|
|
getSinkID(amount, token).then(sinkID => {
|
|
let callback = (sinkKey) => {
|
|
//Send Token to user via token API
|
|
floTokenAPI.sendToken(sinkKey, amount, floID, WITHDRAWAL_MESSAGE, token).then(txid => {
|
|
if (!txid)
|
|
throw Error("Transaction not successful");
|
|
//Transaction was successful, Add in DB
|
|
DB.query("UPDATE OutputToken SET status=?, txid=? WHERE id=?", ["WAITING_CONFIRMATION", txid, id])
|
|
.then(_ => null).catch(error => console.error(error));
|
|
}).catch(error => console.error(error)).finally(_ => {
|
|
delete callbackCollection.token[id];
|
|
balance_locked[sinkID][token] -= amount;
|
|
});
|
|
}
|
|
collectAndCall(sinkID, callback);
|
|
callbackCollection.token[id] = callback;
|
|
if (!(sinkID in balance_locked))
|
|
balance_locked[sinkID] = {};
|
|
balance_locked[sinkID][token] = (balance_locked[sinkID][token] || 0) + amount;
|
|
}).catch(error => console.error(error))
|
|
}
|
|
|
|
function sendToken_init(floID, token, amount) {
|
|
DB.query("INSERT INTO OutputToken (floID, token, amount, status) VALUES (?, ?, ?, ?)", [floID, token, amount, "PENDING"])
|
|
.then(result => sendToken(floID, amount, result.insertId))
|
|
.catch(error => console.error(error))
|
|
}
|
|
|
|
function sendToken_retry(floID, token, amount, id) {
|
|
if (id in callbackCollection.token)
|
|
console.debug("A callback is already pending for this token transfer");
|
|
else
|
|
sendToken(floID, token, amount, id);
|
|
}
|
|
|
|
module.exports = {
|
|
set collectAndCall(fn) {
|
|
collectAndCall = fn;
|
|
},
|
|
get chests() {
|
|
return chests;
|
|
},
|
|
set chests(c) {
|
|
chests = c;
|
|
},
|
|
sendCoin: {
|
|
init: sendCoin_init,
|
|
retry: sendCoin_retry
|
|
},
|
|
sendToken: {
|
|
init: sendToken_init,
|
|
retry: sendToken_retry
|
|
}
|
|
} |