aboutsummaryrefslogtreecommitdiffstats
path: root/routes
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2020-10-09 10:04:27 -0700
committerAdam Soltys <[email protected]>2020-10-09 10:06:02 -0700
commitd079d8c7c4c1efcb39f05255b2d025605dbbd790 (patch)
tree4c2a30e7daf0f410b1b3e0829320fa48f29f41d9 /routes
parent9a06c7883aec135b6d5243caa960d9cb9436b9c0 (diff)
downloadcoinos-server-d079d8c7c4c1efcb39f05255b2d025605dbbd790.tar.gz
coinos-server-d079d8c7c4c1efcb39f05255b2d025605dbbd790.zip
proposals -> orders
Diffstat (limited to 'routes')
-rw-r--r--routes/swaps.js275
1 files changed, 202 insertions, 73 deletions
diff --git a/routes/swaps.js b/routes/swaps.js
index 991c4abc..8872face 100644
--- a/routes/swaps.js
+++ b/routes/swaps.js
@@ -6,6 +6,8 @@ const { spawn } = require("child_process");
const leveldb = level("leveldb");
const getAccount = require("../lib/account");
const { Op, col } = require("sequelize");
+const uuidv4 = require("uuid/v4");
+
const shallow = a => {
let b = {};
for (x in a) {
@@ -62,6 +64,62 @@ const getInfo = filename =>
);
});
+app.post(
+ "/propose",
+ auth,
+ ah(async (req, res) => {
+ const { a1, v1, a2, v2 } = req.body;
+
+ const text = await new Promise((resolve, reject) => {
+ l.info(
+ "running liquid-cli tool %s %s %s %s %s",
+ config.liquid.conf,
+ a1,
+ v1,
+ a2,
+ v2
+ );
+ const proc = cli(
+ "propose",
+ a1,
+ (v1 / SATS).toFixed(8),
+ a2,
+ (v2 / SATS).toFixed(8)
+ );
+
+ proc.stdout.on("data", data => {
+ resolve(data.toString());
+ });
+
+ proc.stderr.on("error", err => {
+ l.error("order error", err.toString());
+ reject(err.toString());
+ });
+
+ proc.on("close", (code, signal) => {
+ let msg = (code && code.toString()) || (signal && signal.toString());
+ reject(
+ new Error(`Liquid swap tool process closed unexpectedly: ${msg}`)
+ );
+ });
+
+ proc.on("exit", (code, signal) => {
+ let msg = (code && code.toString()) || (signal && signal.toString());
+ reject(
+ new Error(`Liquid swap tool process exited unexpectedly: ${msg}`)
+ );
+ });
+
+ setTimeout(
+ () => reject(new Error("Liquid swap tool timed out"), proc),
+ timeout
+ );
+ });
+
+ res.send(text);
+ })
+);
+
const swap = async (user, { a1, a2, v1, v2 }) => {
let rate = v2 / v1;
@@ -114,7 +172,7 @@ const swap = async (user, { a1, a2, v1, v2 }) => {
emit(user.username, "payment", payment.get({ plain: true }));
emit(user.username, "account", a1acc.get({ plain: true }));
- const proposals = await db.Proposal.findAll(
+ const orders = await db.Order.findAll(
{
where: {
"$acc1.asset$": a2,
@@ -144,9 +202,9 @@ const swap = async (user, { a1, a2, v1, v2 }) => {
{ transaction }
);
- for (let i = 0; i < proposals.length; i++) {
+ for (let i = 0; i < orders.length; i++) {
if (!v1) break;
- p = proposals[i];
+ p = orders[i];
if (p.v2 > v1) {
p.v2 -= v1;
await p.save({ transaction });
@@ -228,7 +286,7 @@ const swap = async (user, { a1, a2, v1, v2 }) => {
p.a2 = p.acc2.asset;
p.rate = p.v2 / p.v1;
- broadcast("proposal", shallow(p));
+ broadcast("order", shallow(p));
v1 = 0;
} else {
v1 -= p.v2;
@@ -284,13 +342,13 @@ const swap = async (user, { a1, a2, v1, v2 }) => {
p.a2 = p.acc2.asset;
p.rate = p.v2 / p.v1;
- broadcast("proposal", shallow(p));
+ broadcast("order", shallow(p));
}
}
- let proposal;
+ let order;
if (v1) {
- proposal = await db.Proposal.create(
+ order = await db.Order.create(
{
v1,
v2,
@@ -301,19 +359,19 @@ const swap = async (user, { a1, a2, v1, v2 }) => {
{ transaction }
);
- proposal = proposal.get({ plain: true });
- proposal.rate = v2 / v1;
- proposal.a1 = a1;
- proposal.a2 = a2;
+ order = order.get({ plain: true });
+ order.rate = v2 / v1;
+ order.a1 = a1;
+ order.a2 = a2;
- broadcast("proposal", proposal);
+ broadcast("order", order);
}
});
};
const cancel = async (user, id) => {
await db.transaction(async transaction => {
- let proposal = await db.Proposal.findOne(
+ let order = await db.Order.findOne(
{
where: {
id,
@@ -328,7 +386,7 @@ const cancel = async (user, id) => {
{ transaction }
);
- let { acc1: account, v1, v2 } = proposal;
+ let { acc1: account, v1, v2 } = order;
await account.increment({ balance: v1 }, { transaction });
await account.reload({ transaction });
@@ -351,13 +409,13 @@ const cancel = async (user, id) => {
emit(user.username, "account", account.get({ plain: true }));
emit(user.username, "payment", payment.get({ plain: true }));
- if (proposal.fee) {
+ if (order.fee) {
const btc = await getAccount(config.liquid.btcasset, user, transaction);
payment = await db.Payment.create(
{
hash: "Swap Fee Refund",
- amount: proposal.fee,
+ amount: order.fee,
account_id: btc.id,
user_id: user.id,
currency: user.currency,
@@ -369,20 +427,20 @@ const cancel = async (user, id) => {
{ transaction }
);
- await btc.increment({ balance: proposal.fee }, { transaction });
+ await btc.increment({ balance: order.fee }, { transaction });
await btc.reload({ transaction });
emit(user.username, "account", btc.get({ plain: true }));
emit(user.username, "payment", payment.get({ plain: true }));
}
- broadcast("removeProposal", id);
- await proposal.destroy({ transaction });
+ broadcast("removeOrder", id);
+ await order.destroy({ transaction });
});
};
app.delete(
- "/proposal/:id",
+ "/order/:id",
auth,
ah(async (req, res) => {
const { id } = req.params;
@@ -413,21 +471,9 @@ app.post(
optionalAuth,
ah(async (req, res) => {
try {
- const { id, text } = req.body;
-
- let proposal = await db.Proposal.findOne({
- where: {
- id
- },
- include: {
- model: db.User,
- as: "user"
- }
- });
-
- if (!proposal) throw new Error("Proposal not found");
-
+ const { text } = req.body;
const { user } = req;
+ const id = uuidv4();
if (text) {
const filename = `acceptance-${id}.txt`;
@@ -445,42 +491,123 @@ app.post(
const leg1 = info.legs.find(leg => !leg.incoming);
const leg2 = info.legs.find(leg => leg.incoming);
+ console.log(info, rate, leg2.amount / leg1.amount);
+ let orders = await db.Order.findAll({
+ where: {
+ rate: { [Op.lte]: leg2.amount / leg1.amount },
+ accepted: false,
+ "$acc1.asset$": leg1.asset,
+ "$acc2.asset$": leg2.asset
+ },
+ attributes: {
+ include: [
+ [col("acc1.asset"), "a1"],
+ [col("acc2.asset"), "a2"]
+ ]
+ },
+ order: [["rate", "DESC"]],
+ include: [
+ {
+ model: db.User,
+ as: "user",
+ },
+ {
+ model: db.Account,
+ as: "acc1",
+ attributes: []
+ },
+ {
+ model: db.Account,
+ as: "acc2",
+ attributes: []
+ }
+ ]
+ });
+
+ console.log(orders.map(p => ({ rate: p.rate, v1: p.v1, a1: p.a1 })));
+
+ let total = orders.reduce((a, b) => a + b.v1, 0);
+ let remaining = Math.round(leg1.amount * SATS);
+ if (total < remaining)
+ throw new Error("Not enough coins for sale at that price");
+
await db.transaction(async transaction => {
- const l2a2 = await getAccount(leg2.asset, proposal.user, transaction);
+ for (let i = 0; i < orders.length; i++) {
+ let order = orders[i];
+ const account = await getAccount(
+ leg1.asset,
+ order.user,
+ transaction
+ );
- let amount = Math.round(leg1.amount * SATS);
- fee = Math.round(fee * SATS);
+ console.log("aid", account.id);
- amount = Math.round(leg2.amount * SATS);
+ let { v1: amount } = order;
+ if (remaining >= amount) {
+ console.log(remaining);
+ remaining -= amount;
- await db.Invoice.create({
- user_id: proposal.user_id,
- text: u_address_p,
- address: u_address_p,
- unconfidential: u_address_p,
- currency: proposal.user.currency,
- memo: "Atomic Swap",
- rate: app.get("rates")[proposal.user.currency],
- amount,
- tip: 0,
- network: "liquid",
- account_id: l2a2.id
- });
+ await db.Invoice.create({
+ user_id: order.user_id,
+ text: u_address_p,
+ address: u_address_p,
+ unconfidential: u_address_p,
+ currency: order.user.currency,
+ memo: "Atomic Swap",
+ rate: app.get("rates")[order.user.currency],
+ amount,
+ tip: 0,
+ network: "liquid",
+ account_id: account.id
+ });
- addresses[u_address_p] = proposal.user.username;
+ addresses[u_address_p] = order.user.username;
- await finalize(filename);
+ order.accepted = true;
+ order.completedAt = new Date();
+ await order.save({ transaction });
+
+ order = order.get({ plain: true });
+ order.a1 = leg2.asset;
+ order.a2 = leg1.asset;
+ order.rate = order.v2 / order.v1;
+
+ console.log("broadcasting", order.id);
+
+ broadcast("order", shallow(order));
+ } else {
+ await db.Invoice.create({
+ user_id: order.user_id,
+ text: u_address_p,
+ address: u_address_p,
+ unconfidential: u_address_p,
+ currency: order.user.currency,
+ memo: "Atomic Swap",
+ rate: app.get("rates")[order.user.currency],
+ amount: remaining,
+ tip: 0,
+ network: "liquid",
+ account_id: account.id
+ });
- proposal.accepted = true;
- proposal.completedAt = new Date();
- await proposal.save({ transaction });
+ addresses[u_address_p] = order.user.username;
- proposal = proposal.get({ plain: true });
- proposal.a1 = leg1.asset;
- proposal.a2 = leg2.asset;
- proposal.rate = proposal.v2 / proposal.v1;
+ console.log("new math", order.v2, Math.round(order.v2 * remaining / amount), amount, remaining, order.v1 - remaining);
+ order.decrement({ v1: remaining, v2: Math.round(order.v2 * remaining / amount) }, { transaction });
+ await order.save({ transaction });
+ await order.reload({ transaction });
- broadcast("proposal", shallow(proposal));
+ order = order.get({ plain: true });
+ order.a1 = leg2.asset;
+ order.a2 = leg1.asset;
+ order.rate = order.v2 / order.v1;
+
+ broadcast("order", shallow(order));
+ }
+ }
+
+ console.log("finalizing", filename);
+ await finalize(filename);
});
res.send(info);
@@ -488,7 +615,7 @@ app.post(
throw new Error("No acceptance provided");
}
} catch (e) {
- l.error(e.message);
+ l.error(e.message, e.stack);
res.status(500).send(e.message);
}
})
@@ -651,11 +778,11 @@ const finalize = (filename = "finalized.txt", text) => {
};
app.get(
- "/proposals",
+ "/orders",
optionalAuth,
ah(async (req, res) => {
try {
- let proposals = await db.Proposal.findAll({
+ let orders = await db.Order.findAll({
attributes: {
include: [
[col("acc1.asset"), "a1"],
@@ -677,7 +804,7 @@ app.get(
});
res.send(
- proposals.map(p => {
+ orders.map(p => {
if (p.user_id !== req.user.id) p.user_id = null;
return p;
})
@@ -694,7 +821,7 @@ app.post(
ah(async (req, res) => {
try {
const { id } = req.body;
- await db.Proposal.update(
+ await db.Order.update(
{ public: true },
{ where: { id, user_id: req.user.id } }
);
@@ -720,7 +847,7 @@ setInterval(async () => {
});
await db.transaction(async transaction => {
- let p = await db.Proposal.findOne(
+ let p = await db.Order.findOne(
{
where: {
user_id: user.id,
@@ -750,7 +877,7 @@ setInterval(async () => {
};
if (p) {
- let bestBid = await db.Proposal.findOne(
+ let bestBid = await db.Order.findOne(
{
where: {
"$acc1.asset$": lcad,
@@ -775,7 +902,8 @@ setInterval(async () => {
},
{ transaction }
);
- if (!bestBid || params.v2 / params.v1 > bestBid.rate) {
+ if (!bestBid || (params.v2 / params.v1 > bestBid.rate && p.acc2.balance >= params.v2)) {
+ p.v1 = params.v1;
p.v2 = params.v2;
await p.save();
p = p.get({ plain: true });
@@ -783,7 +911,7 @@ setInterval(async () => {
p.a2 = params.a2;
p.rate = p.v2 / p.v1;
- broadcast("proposal", shallow(p));
+ broadcast("order", shallow(p));
}
} else {
try {
@@ -793,7 +921,7 @@ setInterval(async () => {
}
}
- p = await db.Proposal.findOne(
+ p = await db.Order.findOne(
{
where: {
user_id: user.id,
@@ -822,15 +950,16 @@ setInterval(async () => {
v2: amount * SATS
};
- if (p) {
+ if (p && p.acc1.balance >= p.v1) {
p.v1 = params.v1;
+ p.v2 = params.v2;
await p.save();
p = p.get({ plain: true });
p.a1 = params.a1;
p.a2 = params.a2;
p.rate = p.v2 / p.v1;
- broadcast("proposal", shallow(p));
+ broadcast("order", shallow(p));
} else {
try {
await swap(user, params);