Moved all public/ to docs/ so that github pages can be hosted on docs/ alone - Moved floGlobals.js, lib.js, floCrypto.js, floBlockchainAPI.js, tokenAPI.js, KBucket.js to docs/scripts/ - Renamed api.js to exchangeAPI.js and moved to docs/scripts/ - Moved index.html to docs/ - Moved css components to docs/css - Updated all imports for the above changes. - User pages (index.html) uses floCrypto.js and floBlockchainAPI.js in docs/script dir instead of fetching from cdn
622 lines
22 KiB
JavaScript
622 lines
22 KiB
JavaScript
//console.log(document.cookie.toString());
|
|
const INVALID_SERVER_MSG = "INCORRECT_SERVER_ERROR";
|
|
var nodeList, nodeURL, nodeKBucket; //Container for (backup) node list
|
|
|
|
function exchangeAPI(api, options) {
|
|
return new Promise((resolve, reject) => {
|
|
let curPos = exchangeAPI.curPos || 0;
|
|
if (curPos >= nodeList.length)
|
|
return resolve('No Nodes online');
|
|
let url = "https://" + nodeURL[nodeList[curPos]];
|
|
(options ? fetch(url + api, options) : fetch(url + api))
|
|
.then(result => resolve(result)).catch(error => {
|
|
console.warn(nodeList[curPos], 'is offline');
|
|
//try next node
|
|
exchangeAPI.curPos = curPos + 1;
|
|
exchangeAPI(api, options)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error))
|
|
});
|
|
})
|
|
}
|
|
|
|
function ResponseError(status, data) {
|
|
if (data === INVALID_SERVER_MSG)
|
|
location.reload();
|
|
else if (this instanceof ResponseError) {
|
|
this.data = data;
|
|
this.status = status;
|
|
} else
|
|
return new ResponseError(status, data);
|
|
}
|
|
|
|
function responseParse(response, json_ = true) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!response.ok)
|
|
response.text()
|
|
.then(result => reject(ResponseError(response.status, result)))
|
|
.catch(error => reject(error));
|
|
else if (json_)
|
|
response.json()
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error));
|
|
else
|
|
response.text()
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error));
|
|
});
|
|
}
|
|
|
|
function getAccount(floID, proxySecret) {
|
|
return new Promise((resolve, reject) => {
|
|
let request = {
|
|
floID: floID,
|
|
timestamp: Date.now()
|
|
};
|
|
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
|
|
request.sign = signRequest({
|
|
type: "get_account",
|
|
timestamp: request.timestamp
|
|
}, proxySecret);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/account', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error));
|
|
});
|
|
}
|
|
|
|
function getBuyList() {
|
|
return new Promise((resolve, reject) => {
|
|
exchangeAPI('/list-buyorders')
|
|
.then(result => responseParse(result)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error));
|
|
});
|
|
}
|
|
|
|
function getSellList() {
|
|
return new Promise((resolve, reject) => {
|
|
exchangeAPI('/list-sellorders')
|
|
.then(result => responseParse(result)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error));
|
|
});
|
|
}
|
|
|
|
function getTradeList() {
|
|
return new Promise((resolve, reject) => {
|
|
exchangeAPI('/list-trades')
|
|
.then(result => responseParse(result)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error));
|
|
});
|
|
}
|
|
|
|
function getRates(asset = null) {
|
|
return new Promise((resolve, reject) => {
|
|
exchangeAPI('/get-rates' + (asset ? "?asset=" + asset : ""))
|
|
.then(result => responseParse(result)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error));
|
|
});
|
|
}
|
|
|
|
function getTx(txid) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!txid)
|
|
return reject('txid required');
|
|
exchangeAPI('/get-transaction?txid=' + txid)
|
|
.then(result => responseParse(result)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error));
|
|
})
|
|
}
|
|
|
|
function signRequest(request, signKey) {
|
|
if (typeof request !== "object")
|
|
throw Error("Request is not an object");
|
|
let req_str = Object.keys(request).sort().map(r => r + ":" + request[r]).join("|");
|
|
return floCrypto.signData(req_str, signKey);
|
|
}
|
|
|
|
function getLoginCode() {
|
|
return new Promise((resolve, reject) => {
|
|
exchangeAPI('/get-login-code')
|
|
.then(result => responseParse(result)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error));
|
|
})
|
|
}
|
|
|
|
/*
|
|
function signUp(privKey, code, hash) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!code || !hash)
|
|
return reject("Login Code missing")
|
|
let request = {
|
|
pubKey: floCrypto.getPubKeyHex(privKey),
|
|
floID: floCrypto.getFloID(privKey),
|
|
code: code,
|
|
hash: hash,
|
|
timestamp: Date.now()
|
|
};
|
|
request.sign = signRequest({
|
|
type: "create_account",
|
|
random: code,
|
|
timestamp: request.timestamp
|
|
}, privKey);
|
|
console.debug(request);
|
|
|
|
exchangeAPI("/signup", {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error));
|
|
});
|
|
}
|
|
*/
|
|
|
|
function login(privKey, proxyKey, code, hash) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!code || !hash)
|
|
return reject("Login Code missing")
|
|
let request = {
|
|
proxyKey: proxyKey,
|
|
floID: floCrypto.getFloID(privKey),
|
|
pubKey: floCrypto.getPubKeyHex(privKey),
|
|
timestamp: Date.now(),
|
|
code: code,
|
|
hash: hash
|
|
};
|
|
if (!privKey || !request.floID)
|
|
return reject("Invalid Private key");
|
|
request.sign = signRequest({
|
|
type: "login",
|
|
random: code,
|
|
proxyKey: proxyKey,
|
|
timestamp: request.timestamp
|
|
}, privKey);
|
|
console.debug(request);
|
|
|
|
exchangeAPI("/login", {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error));
|
|
})
|
|
}
|
|
|
|
function logout(floID, proxySecret) {
|
|
return new Promise((resolve, reject) => {
|
|
let request = {
|
|
floID: floID,
|
|
timestamp: Date.now()
|
|
};
|
|
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
|
|
request.sign = signRequest({
|
|
type: "logout",
|
|
timestamp: request.timestamp
|
|
}, proxySecret);
|
|
console.debug(request);
|
|
|
|
exchangeAPI("/logout", {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
function buy(asset, quantity, max_price, floID, proxySecret) {
|
|
return new Promise((resolve, reject) => {
|
|
if (typeof quantity !== "number" || quantity <= 0)
|
|
return reject(`Invalid quantity (${quantity})`);
|
|
else if (typeof max_price !== "number" || max_price <= 0)
|
|
return reject(`Invalid max_price (${max_price})`);
|
|
let request = {
|
|
floID: floID,
|
|
asset: asset,
|
|
quantity: quantity,
|
|
max_price: max_price,
|
|
timestamp: Date.now()
|
|
};
|
|
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
|
|
request.sign = signRequest({
|
|
type: "buy_order",
|
|
asset: asset,
|
|
quantity: quantity,
|
|
max_price: max_price,
|
|
timestamp: request.timestamp
|
|
}, proxySecret);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/buy', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
})
|
|
|
|
}
|
|
|
|
function sell(asset, quantity, min_price, floID, proxySecret) {
|
|
return new Promise((resolve, reject) => {
|
|
if (typeof quantity !== "number" || quantity <= 0)
|
|
return reject(`Invalid quantity (${quantity})`);
|
|
else if (typeof min_price !== "number" || min_price <= 0)
|
|
return reject(`Invalid min_price (${min_price})`);
|
|
let request = {
|
|
floID: floID,
|
|
asset: asset,
|
|
quantity: quantity,
|
|
min_price: min_price,
|
|
timestamp: Date.now()
|
|
};
|
|
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
|
|
request.sign = signRequest({
|
|
type: "sell_order",
|
|
quantity: quantity,
|
|
asset: asset,
|
|
min_price: min_price,
|
|
timestamp: request.timestamp
|
|
}, proxySecret);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/sell', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
})
|
|
|
|
}
|
|
|
|
function cancelOrder(type, id, floID, proxySecret) {
|
|
return new Promise((resolve, reject) => {
|
|
if (type !== "buy" && type !== "sell")
|
|
return reject(`Invalid type (${type}): type should be sell (or) buy`);
|
|
let request = {
|
|
floID: floID,
|
|
orderType: type,
|
|
orderID: id,
|
|
timestamp: Date.now()
|
|
};
|
|
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
|
|
request.sign = signRequest({
|
|
type: "cancel_order",
|
|
order: type,
|
|
id: id,
|
|
timestamp: request.timestamp
|
|
}, proxySecret);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/cancel', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
//receiver should be object eg {floID1: amount1, floID2: amount2 ...}
|
|
function transferToken(receiver, token, floID, proxySecret) {
|
|
return new Promise((resolve, reject) => {
|
|
if (typeof receiver !== Object || receiver === null)
|
|
return reject("Invalid receiver: parameter is not an object");
|
|
let invalidIDs = [],
|
|
invalidAmt = [];
|
|
for (let f in receiver) {
|
|
if (!floCrypto.validateAddr(f))
|
|
invalidIDs.push(f);
|
|
else if (typeof receiver[f] !== "number" || receiver[f] <= 0)
|
|
invalidAmt.push(receiver[f])
|
|
}
|
|
if (invalidIDs.length)
|
|
return reject(INVALID(`Invalid receiver (${invalidIDs})`));
|
|
else if (invalidAmt.length)
|
|
return reject(`Invalid amount (${invalidAmt})`);
|
|
let request = {
|
|
floID: floID,
|
|
token: token,
|
|
receiver: receiver,
|
|
timestamp: Date.now()
|
|
};
|
|
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
|
|
request.sign = signRequest({
|
|
type: "transfer_token",
|
|
receiver: JSON.stringify(receiver),
|
|
token: token,
|
|
timestamp: request.timestamp
|
|
}, proxySecret);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/transfer-token', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
function depositFLO(quantity, floID, sinkID, privKey, proxySecret = null) {
|
|
return new Promise((resolve, reject) => {
|
|
if (typeof quantity !== "number" || quantity <= floGlobals.fee)
|
|
return reject(`Invalid quantity (${quantity})`);
|
|
floBlockchainAPI.sendTx(floID, sinkID, quantity, privKey, 'Deposit FLO in market').then(txid => {
|
|
let request = {
|
|
floID: floID,
|
|
txid: txid,
|
|
timestamp: Date.now()
|
|
};
|
|
if (!proxySecret) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(privKey);
|
|
request.sign = signRequest({
|
|
type: "deposit_flo",
|
|
txid: txid,
|
|
timestamp: request.timestamp
|
|
}, proxySecret || privKey);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/deposit-flo', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
function withdrawFLO(quantity, floID, proxySecret) {
|
|
return new Promise((resolve, reject) => {
|
|
let request = {
|
|
floID: floID,
|
|
amount: quantity,
|
|
timestamp: Date.now()
|
|
};
|
|
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
|
|
request.sign = signRequest({
|
|
type: "withdraw_flo",
|
|
amount: quantity,
|
|
timestamp: request.timestamp
|
|
}, proxySecret);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/withdraw-flo', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
function depositToken(token, quantity, floID, sinkID, privKey, proxySecret = null) {
|
|
return new Promise((resolve, reject) => {
|
|
if (!floCrypto.verifyPrivKey(privKey, floID))
|
|
return reject("Invalid Private Key");
|
|
tokenAPI.sendToken(privKey, quantity, sinkID, 'Deposit Rupee in market', token).then(txid => {
|
|
let request = {
|
|
floID: floID,
|
|
txid: txid,
|
|
timestamp: Date.now()
|
|
};
|
|
if (!proxySecret) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(privKey);
|
|
request.sign = signRequest({
|
|
type: "deposit_token",
|
|
txid: txid,
|
|
timestamp: request.timestamp
|
|
}, proxySecret || privKey);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/deposit-token', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
function withdrawToken(token, quantity, floID, proxySecret) {
|
|
return new Promise((resolve, reject) => {
|
|
let request = {
|
|
floID: floID,
|
|
token: token,
|
|
amount: quantity,
|
|
timestamp: Date.now()
|
|
};
|
|
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
|
|
request.sign = signRequest({
|
|
type: "withdraw_token",
|
|
token: token,
|
|
amount: quantity,
|
|
timestamp: request.timestamp
|
|
}, proxySecret);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/withdraw-token', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
function addUserTag(tag_user, tag, floID, proxySecret) {
|
|
return new Promise((resolve, reject) => {
|
|
let request = {
|
|
floID: floID,
|
|
user: tag_user,
|
|
tag: tag,
|
|
timestamp: Date.now()
|
|
};
|
|
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
|
|
request.sign = signRequest({
|
|
type: "add_tag",
|
|
user: tag_user,
|
|
tag: tag,
|
|
timestamp: request.timestamp
|
|
}, proxySecret);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/add-tag', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
function removeUserTag(tag_user, tag, floID, proxySecret) {
|
|
return new Promise((resolve, reject) => {
|
|
let request = {
|
|
floID: floID,
|
|
user: tag_user,
|
|
tag: tag,
|
|
timestamp: Date.now()
|
|
};
|
|
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
|
|
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
|
|
request.sign = signRequest({
|
|
type: "remove_tag",
|
|
user: tag_user,
|
|
tag: tag,
|
|
timestamp: request.timestamp
|
|
}, proxySecret);
|
|
console.debug(request);
|
|
|
|
exchangeAPI('/remove-tag', {
|
|
method: "POST",
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(request)
|
|
}).then(result => responseParse(result, false)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error)))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
function refreshDataFromBlockchain() {
|
|
return new Promise((resolve, reject) => {
|
|
let nodes, lastTx;
|
|
try {
|
|
nodes = JSON.parse(localStorage.getItem('exchange-nodes'));
|
|
if (typeof nodes !== 'object' || nodes === null)
|
|
throw Error('nodes must be an object')
|
|
else
|
|
lastTx = parseInt(localStorage.getItem('exchange-lastTx')) || 0;
|
|
} catch (error) {
|
|
nodes = {};
|
|
lastTx = 0;
|
|
}
|
|
floBlockchainAPI.readData(floGlobals.adminID, {
|
|
ignoreOld: lastTx,
|
|
sentOnly: true,
|
|
pattern: floGlobals.application
|
|
}).then(result => {
|
|
result.data.reverse().forEach(data => {
|
|
var content = JSON.parse(data)[floGlobals.application];
|
|
//Node List
|
|
if (content.Nodes) {
|
|
if (content.Nodes.remove)
|
|
for (let n of content.Nodes.remove)
|
|
delete nodes[n];
|
|
if (content.Nodes.add)
|
|
for (let n in content.Nodes.add)
|
|
nodes[n] = content.Nodes.add[n];
|
|
}
|
|
});
|
|
localStorage.setItem('exchange-lastTx', result.totalTxs);
|
|
localStorage.setItem('exchange-nodes', JSON.stringify(nodes));
|
|
nodeURL = nodes;
|
|
nodeKBucket = new K_Bucket(floGlobals.adminID, Object.keys(nodeURL));
|
|
nodeList = nodeKBucket.order;
|
|
resolve(nodes);
|
|
}).catch(error => reject(error));
|
|
})
|
|
} |