Adding transfer token feature
Transfer tokens - Added transferToken: Users can directly transfer tokens (including FLO) to other registered users. - Updated schema changes for the above requirement. - Trade transactions and transfer transactions will generate a txid (using SHA256). Other changes - changed all signing type to lowercase
This commit is contained in:
parent
136ba68b42
commit
0520791696
@ -171,7 +171,21 @@ CREATE TABLE PriceHistory (
|
|||||||
FOREIGN KEY (asset) REFERENCES AssetList(asset)
|
FOREIGN KEY (asset) REFERENCES AssetList(asset)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE TransactionHistory (
|
CREATE TABLE TransferTransactions (
|
||||||
|
id INT NOT NULL AUTO_INCREMENT,
|
||||||
|
sender CHAR(34) NOT NULL,
|
||||||
|
receiver CHAR(34) NOT NULL,
|
||||||
|
token VARCHAR(64) NOT NULL,
|
||||||
|
amount FLOAT NOT NULL,
|
||||||
|
tx_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
txid VARCHAR(66) NOT NULL,
|
||||||
|
KEY(id),
|
||||||
|
PRIMARY KEY(txid),
|
||||||
|
FOREIGN KEY (sender) REFERENCES Users(floID),
|
||||||
|
FOREIGN KEY (receiver) REFERENCES Users(floID)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE TradeTransactions (
|
||||||
id INT NOT NULL AUTO_INCREMENT,
|
id INT NOT NULL AUTO_INCREMENT,
|
||||||
seller CHAR(34) NOT NULL,
|
seller CHAR(34) NOT NULL,
|
||||||
buyer CHAR(34) NOT NULL,
|
buyer CHAR(34) NOT NULL,
|
||||||
@ -179,13 +193,15 @@ CREATE TABLE TransactionHistory (
|
|||||||
quantity FLOAT NOT NULL,
|
quantity FLOAT NOT NULL,
|
||||||
unitValue DECIMAL(10, 2) NOT NULL,
|
unitValue DECIMAL(10, 2) NOT NULL,
|
||||||
tx_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
tx_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY(id),
|
txid VARCHAR(66) NOT NULL,
|
||||||
|
KEY(id),
|
||||||
|
PRIMARY KEY(txid),
|
||||||
FOREIGN KEY (buyer) REFERENCES Users(floID),
|
FOREIGN KEY (buyer) REFERENCES Users(floID),
|
||||||
FOREIGN KEY (seller) REFERENCES Users(floID),
|
FOREIGN KEY (seller) REFERENCES Users(floID),
|
||||||
FOREIGN KEY (asset) REFERENCES AssetList(asset)
|
FOREIGN KEY (asset) REFERENCES AssetList(asset)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE AuditTransaction(
|
CREATE TABLE AuditTrade(
|
||||||
id INT NOT NULL AUTO_INCREMENT,
|
id INT NOT NULL AUTO_INCREMENT,
|
||||||
rec_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
rec_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
unit_price FLOAT NOT NULL,
|
unit_price FLOAT NOT NULL,
|
||||||
@ -324,16 +340,23 @@ FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('PriceHistory', NEW.id) ON
|
|||||||
CREATE TRIGGER PriceHistory_D AFTER DELETE ON PriceHistory
|
CREATE TRIGGER PriceHistory_D AFTER DELETE ON PriceHistory
|
||||||
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('PriceHistory', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;
|
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('PriceHistory', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;
|
||||||
|
|
||||||
CREATE TRIGGER AuditTransaction_I AFTER INSERT ON AuditTransaction
|
CREATE TRIGGER AuditTransaction_I AFTER INSERT ON AuditTrade
|
||||||
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTransaction', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
|
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTrade', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
|
||||||
CREATE TRIGGER AuditTransaction_U AFTER UPDATE ON AuditTransaction
|
CREATE TRIGGER AuditTransaction_U AFTER UPDATE ON AuditTrade
|
||||||
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTransaction', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
|
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTrade', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
|
||||||
CREATE TRIGGER AuditTransaction_D AFTER DELETE ON AuditTransaction
|
CREATE TRIGGER AuditTransaction_D AFTER DELETE ON AuditTrade
|
||||||
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTransaction', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;
|
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTrade', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;
|
||||||
|
|
||||||
CREATE TRIGGER TransactionHistory_I AFTER INSERT ON TransactionHistory
|
CREATE TRIGGER TransactionHistory_I AFTER INSERT ON TradeTransactions
|
||||||
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransactionHistory', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
|
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TradeTransactions', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
|
||||||
CREATE TRIGGER TransactionHistory_U AFTER UPDATE ON TransactionHistory
|
CREATE TRIGGER TransactionHistory_U AFTER UPDATE ON TradeTransactions
|
||||||
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransactionHistory', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
|
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TradeTransactions', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
|
||||||
CREATE TRIGGER TransactionHistory_D AFTER DELETE ON TransactionHistory
|
CREATE TRIGGER TransactionHistory_D AFTER DELETE ON TradeTransactions
|
||||||
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransactionHistory', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;
|
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TradeTransactions', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;
|
||||||
|
|
||||||
|
CREATE TRIGGER TransferTransactions_I AFTER INSERT ON TransferTransactions
|
||||||
|
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransferTransactions', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
|
||||||
|
CREATE TRIGGER TransferTransactions_U AFTER UPDATE ON TransferTransactions
|
||||||
|
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransferTransactions', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
|
||||||
|
CREATE TRIGGER TransferTransactions_D AFTER DELETE ON TransferTransactions
|
||||||
|
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransferTransactions', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/* Node data */
|
/* Node data */
|
||||||
TRUNCATE _backup;
|
TRUNCATE _backup;
|
||||||
TRUNCATE AuditTransaction;
|
TRUNCATE AuditTrade;
|
||||||
TRUNCATE BuyOrder;
|
TRUNCATE BuyOrder;
|
||||||
TRUNCATE Cash;
|
TRUNCATE Cash;
|
||||||
TRUNCATE InputFLO;
|
TRUNCATE InputFLO;
|
||||||
@ -12,7 +12,8 @@ TRUNCATE RequestLog;
|
|||||||
TRUNCATE SellOrder;
|
TRUNCATE SellOrder;
|
||||||
TRUNCATE UserSession;
|
TRUNCATE UserSession;
|
||||||
TRUNCATE UserTag;
|
TRUNCATE UserTag;
|
||||||
TRUNCATE TransactionHistory;
|
TRUNCATE TransferTransactions;
|
||||||
|
TRUNCATE TradeTransactions;
|
||||||
TRUNCATE Vault;
|
TRUNCATE Vault;
|
||||||
DELETE FROM Users;
|
DELETE FROM Users;
|
||||||
|
|
||||||
|
|||||||
@ -371,6 +371,41 @@ function cancelOrder(type, id, floID, proxySecret) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function transferToken(receiver, token, amount, floID, proxySecret) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!floCrypto.validateAddr(receiver))
|
||||||
|
return reject(INVALID(`Invalid receiver (${receiver})`));
|
||||||
|
else if (typeof amount !== "number" || amount <= 0)
|
||||||
|
return reject(`Invalid amount (${amount})`);
|
||||||
|
let request = {
|
||||||
|
floID: floID,
|
||||||
|
token: token,
|
||||||
|
receiver: receiver,
|
||||||
|
amount: amount,
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
request.sign = signRequest({
|
||||||
|
type: "transfer_token",
|
||||||
|
receiver: receiver,
|
||||||
|
token: token,
|
||||||
|
amount: amount,
|
||||||
|
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) {
|
function depositFLO(quantity, floID, sinkID, privKey, proxySecret) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (typeof quantity !== "number" || quantity <= floGlobals.fee)
|
if (typeof quantity !== "number" || quantity <= floGlobals.fee)
|
||||||
@ -382,7 +417,7 @@ function depositFLO(quantity, floID, sinkID, privKey, proxySecret) {
|
|||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
};
|
};
|
||||||
request.sign = signRequest({
|
request.sign = signRequest({
|
||||||
type: "deposit_FLO",
|
type: "deposit_flo",
|
||||||
txid: txid,
|
txid: txid,
|
||||||
timestamp: request.timestamp
|
timestamp: request.timestamp
|
||||||
}, proxySecret);
|
}, proxySecret);
|
||||||
@ -410,7 +445,7 @@ function withdrawFLO(quantity, floID, proxySecret) {
|
|||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
};
|
};
|
||||||
request.sign = signRequest({
|
request.sign = signRequest({
|
||||||
type: "withdraw_FLO",
|
type: "withdraw_flo",
|
||||||
amount: quantity,
|
amount: quantity,
|
||||||
timestamp: request.timestamp
|
timestamp: request.timestamp
|
||||||
}, proxySecret);
|
}, proxySecret);
|
||||||
@ -440,7 +475,7 @@ function depositToken(token, quantity, floID, sinkID, privKey, proxySecret) {
|
|||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
};
|
};
|
||||||
request.sign = signRequest({
|
request.sign = signRequest({
|
||||||
type: "deposit_Token",
|
type: "deposit_token",
|
||||||
txid: txid,
|
txid: txid,
|
||||||
timestamp: request.timestamp
|
timestamp: request.timestamp
|
||||||
}, proxySecret);
|
}, proxySecret);
|
||||||
@ -469,7 +504,7 @@ function withdrawToken(token, quantity, floID, proxySecret) {
|
|||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
};
|
};
|
||||||
request.sign = signRequest({
|
request.sign = signRequest({
|
||||||
type: "withdraw_Token",
|
type: "withdraw_token",
|
||||||
token: token,
|
token: token,
|
||||||
amount: quantity,
|
amount: quantity,
|
||||||
timestamp: request.timestamp
|
timestamp: request.timestamp
|
||||||
@ -498,7 +533,7 @@ function addUserTag(tag_user, tag, floID, proxySecret) {
|
|||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
};
|
};
|
||||||
request.sign = signRequest({
|
request.sign = signRequest({
|
||||||
command: "add_Tag",
|
type: "add_tag",
|
||||||
user: tag_user,
|
user: tag_user,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
timestamp: request.timestamp
|
timestamp: request.timestamp
|
||||||
@ -527,7 +562,7 @@ function removeUserTag(tag_user, tag, floID, proxySecret) {
|
|||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
};
|
};
|
||||||
request.sign = signRequest({
|
request.sign = signRequest({
|
||||||
command: "remove_Tag",
|
type: "remove_tag",
|
||||||
user: tag_user,
|
user: tag_user,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
timestamp: request.timestamp
|
timestamp: request.timestamp
|
||||||
|
|||||||
@ -8,7 +8,9 @@ module.exports = {
|
|||||||
INVALID_SERVER_MSG: "INCORRECT_SERVER_ERROR" //Should be reflected in public backend script
|
INVALID_SERVER_MSG: "INCORRECT_SERVER_ERROR" //Should be reflected in public backend script
|
||||||
},
|
},
|
||||||
market: {
|
market: {
|
||||||
MINIMUM_BUY_REQUIREMENT: 0
|
MINIMUM_BUY_REQUIREMENT: 0,
|
||||||
|
TRADE_HASH_PREFIX: "z1",
|
||||||
|
TRANSFER_HASH_PREFIX: "z0"
|
||||||
},
|
},
|
||||||
price: {
|
price: {
|
||||||
MIN_TIME: 1 * 60 * 60 * 1000, // 1 hr
|
MIN_TIME: 1 * 60 * 60 * 1000, // 1 hr
|
||||||
|
|||||||
@ -68,6 +68,9 @@ module.exports = function App(secret, DB) {
|
|||||||
//cancel sell or buy order
|
//cancel sell or buy order
|
||||||
app.post('/cancel', Request.CancelOrder);
|
app.post('/cancel', Request.CancelOrder);
|
||||||
|
|
||||||
|
//transfer amount to another user
|
||||||
|
app.post('/transfer-token', Request.TransferToken);
|
||||||
|
|
||||||
//list sell or buy order
|
//list sell or buy order
|
||||||
app.get('/list-sellorders', Request.ListSellOrders);
|
app.get('/list-sellorders', Request.ListSellOrders);
|
||||||
app.get('/list-buyorders', Request.ListBuyOrders);
|
app.get('/list-buyorders', Request.ListBuyOrders);
|
||||||
@ -86,7 +89,6 @@ module.exports = function App(secret, DB) {
|
|||||||
app.post('/withdraw-token', Request.WithdrawToken);
|
app.post('/withdraw-token', Request.WithdrawToken);
|
||||||
|
|
||||||
//Manage user tags (Access to trusted IDs only)
|
//Manage user tags (Access to trusted IDs only)
|
||||||
|
|
||||||
app.post('/add-tag', Request.addUserTag);
|
app.post('/add-tag', Request.addUserTag);
|
||||||
app.post('/remove-tag', Request.removeUserTag);
|
app.post('/remove-tag', Request.removeUserTag);
|
||||||
|
|
||||||
|
|||||||
@ -3,10 +3,15 @@
|
|||||||
const group = require("./group");
|
const group = require("./group");
|
||||||
const price = require("./price");
|
const price = require("./price");
|
||||||
|
|
||||||
|
const {
|
||||||
|
TRADE_HASH_PREFIX
|
||||||
|
} = require("./_constants")["market"];
|
||||||
|
|
||||||
var DB; //container for database
|
var DB; //container for database
|
||||||
|
|
||||||
function startCouplingForAsset(asset) {
|
function startCouplingForAsset(asset) {
|
||||||
price.getRates(asset).then(cur_rate => {
|
price.getRates(asset).then(cur_rate => {
|
||||||
|
cur_rate = cur_rate.toFixed(3);
|
||||||
group.getBestPairs(asset, cur_rate)
|
group.getBestPairs(asset, cur_rate)
|
||||||
.then(bestPairQueue => processCoupling(bestPairQueue))
|
.then(bestPairQueue => processCoupling(bestPairQueue))
|
||||||
.catch(error => console.error("initiateCoupling", error))
|
.catch(error => console.error("initiateCoupling", error))
|
||||||
@ -112,7 +117,12 @@ function updateBalance(seller_best, buyer_best, txQueries, asset, cur_price, qua
|
|||||||
//Add coins to Buyer
|
//Add coins to Buyer
|
||||||
txQueries.push(["INSERT INTO Vault(floID, asset, base, quantity) VALUES (?, ?, ?, ?)", [buyer_best.floID, asset, cur_price, quantity]])
|
txQueries.push(["INSERT INTO Vault(floID, asset, base, quantity) VALUES (?, ?, ?, ?)", [buyer_best.floID, asset, cur_price, quantity]])
|
||||||
//Record transaction
|
//Record transaction
|
||||||
txQueries.push(["INSERT INTO TransactionHistory (seller, buyer, asset, quantity, unitValue) VALUES (?, ?, ?, ?, ?)", [seller_best.floID, buyer_best.floID, asset, quantity, cur_price]]);
|
let time = Date.now();
|
||||||
|
let hash = TRADE_HASH_PREFIX + Crypto.SHA256([time, seller_best.floID, buyer_best.floID, asset, quantity, cur_price].join("|"));
|
||||||
|
txQueries.push([
|
||||||
|
"INSERT INTO TradeTransactions (seller, buyer, asset, quantity, unitValue, tx_time, txid) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
[seller_best.floID, buyer_best.floID, asset, quantity, cur_price, global.convertDateToString(time), hash]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function beginAudit(sellerID, buyerID, asset, unit_price, quantity) {
|
function beginAudit(sellerID, buyerID, asset, unit_price, quantity) {
|
||||||
@ -125,7 +135,7 @@ function beginAudit(sellerID, buyerID, asset, unit_price, quantity) {
|
|||||||
|
|
||||||
function endAudit(sellerID, buyerID, asset, old_bal, unit_price, quantity) {
|
function endAudit(sellerID, buyerID, asset, old_bal, unit_price, quantity) {
|
||||||
auditBalance(sellerID, buyerID, asset).then(new_bal => {
|
auditBalance(sellerID, buyerID, asset).then(new_bal => {
|
||||||
DB.query("INSERT INTO AuditTransaction (asset, quantity, unit_price, total_cost," +
|
DB.query("INSERT INTO AuditTrade (asset, quantity, unit_price, total_cost," +
|
||||||
" sellerID, seller_old_cash, seller_old_asset, seller_new_cash, seller_new_asset," +
|
" sellerID, seller_old_cash, seller_old_asset, seller_new_cash, seller_new_asset," +
|
||||||
" buyerID, buyer_old_cash, buyer_old_asset, buyer_new_cash, buyer_new_asset)" +
|
" buyerID, buyer_old_cash, buyer_old_asset, buyer_new_cash, buyer_new_asset)" +
|
||||||
" Value (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [
|
" Value (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [
|
||||||
|
|||||||
@ -86,6 +86,7 @@ function Database(user, password, dbname, host = 'localhost') {
|
|||||||
user: user,
|
user: user,
|
||||||
password: password,
|
password: password,
|
||||||
database: dbname,
|
database: dbname,
|
||||||
|
//dateStrings : true,
|
||||||
timezone: 'UTC'
|
timezone: 'UTC'
|
||||||
});
|
});
|
||||||
db.connect.then(conn => {
|
db.connect.then(conn => {
|
||||||
|
|||||||
@ -3,11 +3,21 @@
|
|||||||
const coupling = require('./coupling');
|
const coupling = require('./coupling');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
MINIMUM_BUY_REQUIREMENT
|
MINIMUM_BUY_REQUIREMENT,
|
||||||
|
TRANSFER_HASH_PREFIX
|
||||||
} = require('./_constants')["market"];
|
} = require('./_constants')["market"];
|
||||||
|
|
||||||
var DB, assetList; //container for database and allowed assets
|
var DB, assetList; //container for database and allowed assets
|
||||||
|
|
||||||
|
const checkIfUserRegistered = floID => new Promise((resolve, reject) => {
|
||||||
|
DB.query("SELECT id FROM Users WHERE floID=?", [floID]).then(result => {
|
||||||
|
if (result.length)
|
||||||
|
resolve(result[0].id);
|
||||||
|
else
|
||||||
|
reject(INVALID(`User ${floID} not registered`));
|
||||||
|
}).catch(error => reject(error));
|
||||||
|
});
|
||||||
|
|
||||||
const getAssetBalance = (floID, asset) => new Promise((resolve, reject) => {
|
const getAssetBalance = (floID, asset) => new Promise((resolve, reject) => {
|
||||||
let promises = (asset === floGlobals.currency) ? [
|
let promises = (asset === floGlobals.currency) ? [
|
||||||
DB.query("SELECT balance FROM Cash WHERE floID=?", [floID]),
|
DB.query("SELECT balance FROM Cash WHERE floID=?", [floID]),
|
||||||
@ -32,7 +42,7 @@ getAssetBalance.check = (floID, asset, amount) => new Promise((resolve, reject)
|
|||||||
else
|
else
|
||||||
resolve(true);
|
resolve(true);
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
})
|
});
|
||||||
|
|
||||||
const consumeAsset = (floID, asset, amount, txQueries = []) => new Promise((resolve, reject) => {
|
const consumeAsset = (floID, asset, amount, txQueries = []) => new Promise((resolve, reject) => {
|
||||||
//If asset/token is currency update Cash else consume from Vault
|
//If asset/token is currency update Cash else consume from Vault
|
||||||
@ -60,8 +70,8 @@ const consumeAsset = (floID, asset, amount, txQueries = []) => new Promise((reso
|
|||||||
|
|
||||||
function addSellOrder(floID, asset, quantity, min_price) {
|
function addSellOrder(floID, asset, quantity, min_price) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!floID || !floCrypto.validateAddr(floID))
|
if (!floCrypto.validateAddr(floID))
|
||||||
return reject(INVALID("Invalid FLO ID"));
|
return reject(INVALID(`Invalid floID (${floID})`));
|
||||||
else if (typeof quantity !== "number" || quantity <= 0)
|
else if (typeof quantity !== "number" || quantity <= 0)
|
||||||
return reject(INVALID(`Invalid quantity (${quantity})`));
|
return reject(INVALID(`Invalid quantity (${quantity})`));
|
||||||
else if (typeof min_price !== "number" || min_price <= 0)
|
else if (typeof min_price !== "number" || min_price <= 0)
|
||||||
@ -71,7 +81,7 @@ function addSellOrder(floID, asset, quantity, min_price) {
|
|||||||
getAssetBalance.check(floID, asset, quantity).then(_ => {
|
getAssetBalance.check(floID, asset, quantity).then(_ => {
|
||||||
checkSellRequirement(floID, asset).then(_ => {
|
checkSellRequirement(floID, asset).then(_ => {
|
||||||
DB.query("INSERT INTO SellOrder(floID, asset, quantity, minPrice) VALUES (?, ?, ?, ?)", [floID, asset, quantity, min_price])
|
DB.query("INSERT INTO SellOrder(floID, asset, quantity, minPrice) VALUES (?, ?, ?, ?)", [floID, asset, quantity, min_price])
|
||||||
.then(result => resolve("Added SellOrder to DB"))
|
.then(result => resolve('Sell Order placed successfully'))
|
||||||
.catch(error => reject(error));
|
.catch(error => reject(error));
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
}).catch(error => reject(error));
|
}).catch(error => reject(error));
|
||||||
@ -83,7 +93,7 @@ const checkSellRequirement = (floID, asset) => new Promise((resolve, reject) =>
|
|||||||
if (result.length)
|
if (result.length)
|
||||||
return resolve(true);
|
return resolve(true);
|
||||||
//TODO: Should seller need to buy same type of asset before selling?
|
//TODO: Should seller need to buy same type of asset before selling?
|
||||||
DB.query("SELECT SUM(quantity) AS brought FROM TransactionHistory WHERE buyer=? AND asset=?", [floID, asset]).then(result => {
|
DB.query("SELECT SUM(quantity) AS brought FROM TradeTransactions WHERE buyer=? AND asset=?", [floID, asset]).then(result => {
|
||||||
if (result[0].brought >= MINIMUM_BUY_REQUIREMENT)
|
if (result[0].brought >= MINIMUM_BUY_REQUIREMENT)
|
||||||
resolve(true);
|
resolve(true);
|
||||||
else
|
else
|
||||||
@ -94,8 +104,8 @@ const checkSellRequirement = (floID, asset) => new Promise((resolve, reject) =>
|
|||||||
|
|
||||||
function addBuyOrder(floID, asset, quantity, max_price) {
|
function addBuyOrder(floID, asset, quantity, max_price) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!floID || !floCrypto.validateAddr(floID))
|
if (!floCrypto.validateAddr(floID))
|
||||||
return reject(INVALID("Invalid FLO ID"));
|
return reject(INVALID(`Invalid floID (${floID})`));
|
||||||
else if (typeof quantity !== "number" || quantity <= 0)
|
else if (typeof quantity !== "number" || quantity <= 0)
|
||||||
return reject(INVALID(`Invalid quantity (${quantity})`));
|
return reject(INVALID(`Invalid quantity (${quantity})`));
|
||||||
else if (typeof max_price !== "number" || max_price <= 0)
|
else if (typeof max_price !== "number" || max_price <= 0)
|
||||||
@ -104,7 +114,7 @@ function addBuyOrder(floID, asset, quantity, max_price) {
|
|||||||
return reject(INVALID(`Invalid asset (${asset})`));
|
return reject(INVALID(`Invalid asset (${asset})`));
|
||||||
getAssetBalance.check(floID, floGlobals.currency, quantity * max_price).then(_ => {
|
getAssetBalance.check(floID, floGlobals.currency, quantity * max_price).then(_ => {
|
||||||
DB.query("INSERT INTO BuyOrder(floID, asset, quantity, maxPrice) VALUES (?, ?, ?, ?)", [floID, asset, quantity, max_price])
|
DB.query("INSERT INTO BuyOrder(floID, asset, quantity, maxPrice) VALUES (?, ?, ?, ?)", [floID, asset, quantity, max_price])
|
||||||
.then(result => resolve("Added BuyOrder to DB"))
|
.then(result => resolve('Buy Order placed successfully'))
|
||||||
.catch(error => reject(error));
|
.catch(error => reject(error));
|
||||||
}).catch(error => reject(error));
|
}).catch(error => reject(error));
|
||||||
});
|
});
|
||||||
@ -112,8 +122,8 @@ function addBuyOrder(floID, asset, quantity, max_price) {
|
|||||||
|
|
||||||
function cancelOrder(type, id, floID) {
|
function cancelOrder(type, id, floID) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!floID || !floCrypto.validateAddr(floID))
|
if (!floCrypto.validateAddr(floID))
|
||||||
return reject(INVALID("Invalid FLO ID"));
|
return reject(INVALID(`Invalid floID (${floID})`));
|
||||||
let tableName;
|
let tableName;
|
||||||
if (type === "buy")
|
if (type === "buy")
|
||||||
tableName = "BuyOrder";
|
tableName = "BuyOrder";
|
||||||
@ -166,7 +176,7 @@ function getAccountDetails(floID) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
DB.query("SELECT * FROM TransactionHistory WHERE seller=? OR buyer=?", [floID, floID])
|
DB.query("SELECT * FROM TradeTransactions WHERE seller=? OR buyer=?", [floID, floID])
|
||||||
.then(result => response.transactions = result)
|
.then(result => response.transactions = result)
|
||||||
.catch(error => console.error(error))
|
.catch(error => console.error(error))
|
||||||
.finally(_ => resolve(response));
|
.finally(_ => resolve(response));
|
||||||
@ -174,6 +184,38 @@ function getAccountDetails(floID) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function transferToken(sender, receiver, token, amount) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (floCrypto.validateAddr(sender))
|
||||||
|
return reject(INVALID(`Invalid sender (${sender})`));
|
||||||
|
else if (floCrypto.validateAddr(receiver))
|
||||||
|
return reject(INVALID(`Invalid receiver (${receiver})`));
|
||||||
|
else if (typeof amount !== "number" || amount <= 0)
|
||||||
|
return reject(INVALID(`Invalid amount (${amount})`));
|
||||||
|
else if (token !== floGlobals.currency && !assetList.includes(token))
|
||||||
|
return reject(INVALID(`Invalid token (${token})`));
|
||||||
|
getAssetBalance.check(senderID, token, amount).then(_ => {
|
||||||
|
checkIfUserRegistered(receiver).then(_ => {
|
||||||
|
consumeAsset(sender, token, amount).then(txQueries => {
|
||||||
|
if (token === floGlobals.currency)
|
||||||
|
txQueries.push(["UPDATE Cash SET balance=balance+? WHERE floID=?", [amount, receiver]]);
|
||||||
|
else
|
||||||
|
txQueries.push(["INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [receiver, amount]]);
|
||||||
|
let time = Date.now();
|
||||||
|
let hash = TRANSFER_HASH_PREFIX + Crypto.SHA256([time, sender, receiver, token, amount].join("|"));
|
||||||
|
txQueries.push([
|
||||||
|
"INSERT INTO TransferTransactions (sender, receiver, token, amount, tx_time, txid)",
|
||||||
|
[sender, receiver, token, amount, global.convertDateToString(time), hash]
|
||||||
|
]);
|
||||||
|
DB.transaction(txQueries)
|
||||||
|
.then(result => resolve(hash))
|
||||||
|
.catch(error => reject(error))
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function depositFLO(floID, txid) {
|
function depositFLO(floID, txid) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
DB.query("SELECT status FROM InputFLO WHERE txid=? AND floID=?", [txid, floID]).then(result => {
|
DB.query("SELECT status FROM InputFLO WHERE txid=? AND floID=?", [txid, floID]).then(result => {
|
||||||
@ -238,8 +280,8 @@ confirmDepositFLO.checkTx = function(sender, txid) {
|
|||||||
|
|
||||||
function withdrawFLO(floID, amount) {
|
function withdrawFLO(floID, amount) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!floID || !floCrypto.validateAddr(floID))
|
if (!floCrypto.validateAddr(floID))
|
||||||
return reject(INVALID("Invalid FLO ID"));
|
return reject(INVALID(`Invalid floID (${floID})`));
|
||||||
else if (typeof amount !== "number" || amount <= 0)
|
else if (typeof amount !== "number" || amount <= 0)
|
||||||
return reject(INVALID(`Invalid amount (${amount})`));
|
return reject(INVALID(`Invalid amount (${amount})`));
|
||||||
getAssetBalance.check(floID, "FLO", amount).then(_ => {
|
getAssetBalance.check(floID, "FLO", amount).then(_ => {
|
||||||
@ -372,8 +414,8 @@ confirmDepositToken.checkTx = function(sender, txid) {
|
|||||||
|
|
||||||
function withdrawToken(floID, token, amount) {
|
function withdrawToken(floID, token, amount) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!floID || !floCrypto.validateAddr(floID))
|
if (!floCrypto.validateAddr(floID))
|
||||||
return reject(INVALID("Invalid FLO ID"));
|
return reject(INVALID(`Invalid floID (${floID})`));
|
||||||
else if (typeof amount !== "number" || amount <= 0)
|
else if (typeof amount !== "number" || amount <= 0)
|
||||||
return reject(INVALID(`Invalid amount (${amount})`));
|
return reject(INVALID(`Invalid amount (${amount})`));
|
||||||
else if ((!assetList.includes(token) && token !== floGlobals.currency) || token === "FLO")
|
else if ((!assetList.includes(token) && token !== floGlobals.currency) || token === "FLO")
|
||||||
@ -454,6 +496,7 @@ module.exports = {
|
|||||||
addSellOrder,
|
addSellOrder,
|
||||||
cancelOrder,
|
cancelOrder,
|
||||||
getAccountDetails,
|
getAccountDetails,
|
||||||
|
transferToken,
|
||||||
depositFLO,
|
depositFLO,
|
||||||
withdrawFLO,
|
withdrawFLO,
|
||||||
depositToken,
|
depositToken,
|
||||||
|
|||||||
114
src/request.js
114
src/request.js
@ -165,18 +165,17 @@ function PlaceSellOrder(req, res) {
|
|||||||
min_price: data.min_price,
|
min_price: data.min_price,
|
||||||
timestamp: data.timestamp
|
timestamp: data.timestamp
|
||||||
}, data.sign, data.floID).then(req_str => {
|
}, data.sign, data.floID).then(req_str => {
|
||||||
market.addSellOrder(data.floID, data.asset, data.quantity, data.min_price)
|
market.addSellOrder(data.floID, data.asset, data.quantity, data.min_price).then(result => {
|
||||||
.then(result => {
|
storeRequest(data.floID, req_str, data.sign);
|
||||||
storeRequest(data.floID, req_str, data.sign);
|
res.send(result);
|
||||||
res.send('Sell Order placed successfully');
|
}).catch(error => {
|
||||||
}).catch(error => {
|
if (error instanceof INVALID)
|
||||||
if (error instanceof INVALID)
|
res.status(INVALID.e_code).send(error.message);
|
||||||
res.status(INVALID.e_code).send(error.message);
|
else {
|
||||||
else {
|
console.error(error);
|
||||||
console.error(error);
|
res.status(INTERNAL.e_code).send("Order placement failed! Try again later!");
|
||||||
res.status(INTERNAL.e_code).send("Order placement failed! Try again later!");
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error instanceof INVALID)
|
if (error instanceof INVALID)
|
||||||
res.status(INVALID.e_code).send(error.message);
|
res.status(INVALID.e_code).send(error.message);
|
||||||
@ -196,18 +195,17 @@ function PlaceBuyOrder(req, res) {
|
|||||||
max_price: data.max_price,
|
max_price: data.max_price,
|
||||||
timestamp: data.timestamp
|
timestamp: data.timestamp
|
||||||
}, data.sign, data.floID).then(req_str => {
|
}, data.sign, data.floID).then(req_str => {
|
||||||
market.addBuyOrder(data.floID, data.asset, data.quantity, data.max_price)
|
market.addBuyOrder(data.floID, data.asset, data.quantity, data.max_price).then(result => {
|
||||||
.then(result => {
|
storeRequest(data.floID, req_str, data.sign);
|
||||||
storeRequest(data.floID, req_str, data.sign);
|
res.send(result);
|
||||||
res.send('Buy Order placed successfully');
|
}).catch(error => {
|
||||||
}).catch(error => {
|
if (error instanceof INVALID)
|
||||||
if (error instanceof INVALID)
|
res.status(INVALID.e_code).send(error.message);
|
||||||
res.status(INVALID.e_code).send(error.message);
|
else {
|
||||||
else {
|
console.error(error);
|
||||||
console.error(error);
|
res.status(INTERNAL.e_code).send("Order placement failed! Try again later!");
|
||||||
res.status(INTERNAL.e_code).send("Order placement failed! Try again later!");
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error instanceof INVALID)
|
if (error instanceof INVALID)
|
||||||
res.status(INVALID.e_code).send(error.message);
|
res.status(INVALID.e_code).send(error.message);
|
||||||
@ -226,18 +224,47 @@ function CancelOrder(req, res) {
|
|||||||
id: data.orderID,
|
id: data.orderID,
|
||||||
timestamp: data.timestamp
|
timestamp: data.timestamp
|
||||||
}, data.sign, data.floID).then(req_str => {
|
}, data.sign, data.floID).then(req_str => {
|
||||||
market.cancelOrder(data.orderType, data.orderID, data.floID)
|
market.cancelOrder(data.orderType, data.orderID, data.floID).then(result => {
|
||||||
.then(result => {
|
storeRequest(data.floID, req_str, data.sign);
|
||||||
storeRequest(data.floID, req_str, data.sign);
|
res.send(result);
|
||||||
res.send(result);
|
}).catch(error => {
|
||||||
}).catch(error => {
|
if (error instanceof INVALID)
|
||||||
if (error instanceof INVALID)
|
res.status(INVALID.e_code).send(error.message);
|
||||||
res.status(INVALID.e_code).send(error.message);
|
else {
|
||||||
else {
|
console.error(error);
|
||||||
console.error(error);
|
res.status(INTERNAL.e_code).send("Order cancellation failed! Try again later!");
|
||||||
res.status(INTERNAL.e_code).send("Order cancellation failed! Try again later!");
|
}
|
||||||
}
|
});
|
||||||
});
|
}).catch(error => {
|
||||||
|
if (error instanceof INVALID)
|
||||||
|
res.status(INVALID.e_code).send(error.message);
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
res.status(INTERNAL.e_code).send("Request processing failed! Try again later!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function TransferToken(req, res) {
|
||||||
|
let data = req.body;
|
||||||
|
validateRequestFromFloID({
|
||||||
|
type: "transfer_token",
|
||||||
|
receiver: data.receiver,
|
||||||
|
token: data.token,
|
||||||
|
amount: data.amount,
|
||||||
|
timestamp: data.timestamp
|
||||||
|
}, data.sign, data.floID).then(req_str => {
|
||||||
|
market.transferToken(data.floID, data.receiver, data.token, data.amount).then(result => {
|
||||||
|
storeRequest(data.floID, req_str, data.sign);
|
||||||
|
res.send(result);
|
||||||
|
}).catch(error => {
|
||||||
|
if (error instanceof INVALID)
|
||||||
|
res.status(INVALID.e_code).send(error.message);
|
||||||
|
else {
|
||||||
|
console.error(error);
|
||||||
|
res.status(INTERNAL.e_code).send("Token Transfer failed! Try again later!");
|
||||||
|
}
|
||||||
|
});
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
if (error instanceof INVALID)
|
if (error instanceof INVALID)
|
||||||
res.status(INVALID.e_code).send(error.message);
|
res.status(INVALID.e_code).send(error.message);
|
||||||
@ -264,7 +291,7 @@ function ListBuyOrders(req, res) {
|
|||||||
|
|
||||||
function ListTransactions(req, res) {
|
function ListTransactions(req, res) {
|
||||||
//TODO: Limit size (recent)
|
//TODO: Limit size (recent)
|
||||||
DB.query("SELECT * FROM TransactionHistory ORDER BY tx_time DESC")
|
DB.query("SELECT * FROM TradeTransactions ORDER BY tx_time DESC")
|
||||||
.then(result => res.send(result))
|
.then(result => res.send(result))
|
||||||
.catch(error => res.status(INTERNAL.e_code).send("Try again later!"));
|
.catch(error => res.status(INTERNAL.e_code).send("Try again later!"));
|
||||||
}
|
}
|
||||||
@ -301,7 +328,7 @@ function Account(req, res) {
|
|||||||
function DepositFLO(req, res) {
|
function DepositFLO(req, res) {
|
||||||
let data = req.body;
|
let data = req.body;
|
||||||
validateRequestFromFloID({
|
validateRequestFromFloID({
|
||||||
type: "deposit_FLO",
|
type: "deposit_flo",
|
||||||
txid: data.txid,
|
txid: data.txid,
|
||||||
timestamp: data.timestamp
|
timestamp: data.timestamp
|
||||||
}, data.sign, data.floID).then(req_str => {
|
}, data.sign, data.floID).then(req_str => {
|
||||||
@ -329,7 +356,7 @@ function DepositFLO(req, res) {
|
|||||||
function WithdrawFLO(req, res) {
|
function WithdrawFLO(req, res) {
|
||||||
let data = req.body;
|
let data = req.body;
|
||||||
validateRequestFromFloID({
|
validateRequestFromFloID({
|
||||||
type: "withdraw_FLO",
|
type: "withdraw_flo",
|
||||||
amount: data.amount,
|
amount: data.amount,
|
||||||
timestamp: data.timestamp
|
timestamp: data.timestamp
|
||||||
}, data.sign, data.floID).then(req_str => {
|
}, data.sign, data.floID).then(req_str => {
|
||||||
@ -357,7 +384,7 @@ function WithdrawFLO(req, res) {
|
|||||||
function DepositToken(req, res) {
|
function DepositToken(req, res) {
|
||||||
let data = req.body;
|
let data = req.body;
|
||||||
validateRequestFromFloID({
|
validateRequestFromFloID({
|
||||||
type: "deposit_Token",
|
type: "deposit_token",
|
||||||
txid: data.txid,
|
txid: data.txid,
|
||||||
timestamp: data.timestamp
|
timestamp: data.timestamp
|
||||||
}, data.sign, data.floID).then(req_str => {
|
}, data.sign, data.floID).then(req_str => {
|
||||||
@ -385,7 +412,7 @@ function DepositToken(req, res) {
|
|||||||
function WithdrawToken(req, res) {
|
function WithdrawToken(req, res) {
|
||||||
let data = req.body;
|
let data = req.body;
|
||||||
validateRequestFromFloID({
|
validateRequestFromFloID({
|
||||||
type: "withdraw_Token",
|
type: "withdraw_token",
|
||||||
token: data.token,
|
token: data.token,
|
||||||
amount: data.amount,
|
amount: data.amount,
|
||||||
timestamp: data.timestamp
|
timestamp: data.timestamp
|
||||||
@ -416,7 +443,7 @@ function addUserTag(req, res) {
|
|||||||
if (!trustedIDs.includes(data.floID))
|
if (!trustedIDs.includes(data.floID))
|
||||||
return res.status(INVALID.e_code).send("Access Denied");
|
return res.status(INVALID.e_code).send("Access Denied");
|
||||||
validateRequestFromFloID({
|
validateRequestFromFloID({
|
||||||
command: "add_Tag",
|
type: "add_tag",
|
||||||
user: data.user,
|
user: data.user,
|
||||||
tag: data.tag,
|
tag: data.tag,
|
||||||
timestamp: data.timestamp
|
timestamp: data.timestamp
|
||||||
@ -448,7 +475,7 @@ function removeUserTag(req, res) {
|
|||||||
return res.status(INVALID.e_code).send("Access Denied");
|
return res.status(INVALID.e_code).send("Access Denied");
|
||||||
else
|
else
|
||||||
validateRequestFromFloID({
|
validateRequestFromFloID({
|
||||||
command: "remove_Tag",
|
type: "remove_tag",
|
||||||
user: data.user,
|
user: data.user,
|
||||||
tag: data.tag,
|
tag: data.tag,
|
||||||
timestamp: data.timestamp
|
timestamp: data.timestamp
|
||||||
@ -482,6 +509,7 @@ module.exports = {
|
|||||||
PlaceBuyOrder,
|
PlaceBuyOrder,
|
||||||
PlaceSellOrder,
|
PlaceSellOrder,
|
||||||
CancelOrder,
|
CancelOrder,
|
||||||
|
TransferToken,
|
||||||
ListSellOrders,
|
ListSellOrders,
|
||||||
ListBuyOrders,
|
ListBuyOrders,
|
||||||
ListTransactions,
|
ListTransactions,
|
||||||
|
|||||||
@ -2,6 +2,16 @@
|
|||||||
//fetch for node js (used in floBlockchainAPI.js)
|
//fetch for node js (used in floBlockchainAPI.js)
|
||||||
global.fetch = require("node-fetch");
|
global.fetch = require("node-fetch");
|
||||||
|
|
||||||
|
global.convertDateToString = function(timestamp) {
|
||||||
|
let date = new Date(timestamp);
|
||||||
|
return date.getFullYear() + '-' +
|
||||||
|
('00' + (date.getMonth() + 1)).slice(-2) + '-' +
|
||||||
|
('00' + date.getDate()).slice(-2) + ' ' +
|
||||||
|
('00' + date.getHours()).slice(-2) + ':' +
|
||||||
|
('00' + date.getMinutes()).slice(-2) + ':' +
|
||||||
|
('00' + date.getSeconds()).slice(-2);
|
||||||
|
}
|
||||||
|
|
||||||
//Set browser paramaters from param.json (or param-default.json)
|
//Set browser paramaters from param.json (or param-default.json)
|
||||||
var param;
|
var param;
|
||||||
try {
|
try {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user