aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2022-09-13 15:59:07 -0700
committerAdam Soltys <[email protected]>2022-09-13 15:59:07 -0700
commit48108047dc4ab1557a2ca8f435885ff88d81e858 (patch)
treedf6fb758a2b602e3e2a532c362476f88e17b4eee /lib
parentff7514b07057ac5369a1c857f546ac0058fa310b (diff)
downloadcoinos-server-48108047dc4ab1557a2ca8f435885ff88d81e858.tar.gz
coinos-server-48108047dc4ab1557a2ca8f435885ff88d81e858.zip
refactor send internal into function
Diffstat (limited to 'lib')
-rw-r--r--lib/sendInternal.js217
1 files changed, 217 insertions, 0 deletions
diff --git a/lib/sendInternal.js b/lib/sendInternal.js
new file mode 100644
index 00000000..80701ca3
--- /dev/null
+++ b/lib/sendInternal.js
@@ -0,0 +1,217 @@
+import config from "$config";
+import db from "$db";
+import { emit } from "$lib/sockets";
+import store from "$lib/store";
+import { callWebhook } from "$lib/webhooks";
+import axios from "axios";
+import { v4 } from "uuid";
+import { notify } from "$lib/notifications";
+import { sendLiquid } from "$routes/liquid/send";
+import { l, err } from "$lib/logging";
+import { fail } from "$lib/utils";
+
+export default async ({
+ amount,
+ address,
+ payreq,
+ unconfidential,
+ asset,
+ memo,
+ username,
+}, hostname, user) => {
+ amount = parseInt(amount);
+
+ if (!asset) asset = config.liquid.btcasset;
+
+ if (!amount || amount < 0) fail("Amount must be greater than zero");
+
+ return db.transaction(async transaction => {
+ let account;
+ if (user.account.asset === asset && !user.account.pubkey) {
+ account = await db.Account.findOne({
+ where: {
+ id: user.account.id,
+ pubkey: null
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+ } else {
+ account = await db.Account.findOne({
+ where: {
+ user_id: user.id,
+ asset,
+ pubkey: null
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+ }
+
+ if (!account) throw new Error("Account not found");
+
+ if (account.balance < amount) {
+ throw new Error("Insufficient funds");
+ }
+
+ let fee = 0;
+
+ await account.decrement({ balance: amount }, { transaction });
+ await account.reload({ transaction });
+
+ let params = {
+ amount: -amount,
+ account_id: account.id,
+ memo,
+ user_id: user.id,
+ rate: store.rates[user.currency],
+ currency: user.currency,
+ confirmed: true,
+ hash: `#${v4().substr(0, 6)} ${
+ username ? `Payment to ${username}` : "Internal Transfer"
+ }`,
+ network: "COINOS"
+ };
+
+ if (!username) {
+ l("creating redeemable payment");
+ params.redeemcode = v4();
+ params.hash = `${hostname}/redeem/${params.redeemcode}`;
+ }
+
+ let payment = await db.Payment.create(params, { transaction });
+
+ payment = payment.get({ plain: true });
+ payment.account = account.get({ plain: true });
+
+ l("sent internal", user.username, -payment.amount);
+
+ emit(user.username, "payment", payment);
+ emit(user.username, "account", account);
+
+ if (username) {
+ let recipient = await db.User.findOne(
+ {
+ where: { username },
+ include: {
+ model: db.Account,
+ as: "account"
+ }
+ },
+ { transaction }
+ );
+
+ params = {
+ where: { user_id: recipient.id },
+ order: [["id", "DESC"]]
+ };
+ if (address) params.where.address = address;
+ else if (payreq) params.where.text = payreq;
+ let invoice = (address || payreq) && (await db.Invoice.findOne(params));
+
+ let a2;
+ let acc = {
+ user_id: recipient.id,
+ asset,
+ pubkey: null
+ };
+
+ if (recipient.account.asset === asset && !recipient.account.pubkey)
+ a2 = recipient.account;
+ else {
+ a2 = await db.Account.findOne({
+ where: acc,
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+ }
+
+ if (a2) {
+ await a2.increment({ balance: amount }, { transaction });
+ await a2.reload({ transaction });
+ } else {
+ let name = asset.substr(0, 6);
+ let domain;
+ let ticker = asset.substr(0, 3).toUpperCase();
+ let precision = 8;
+
+ if (store.assets[asset]) {
+ ({ domain, ticker, precision, name } = store.assets[asset]);
+ } else {
+ const existing = await db.Account.findOne({
+ where: {
+ asset
+ },
+ order: [["id", "ASC"]],
+ limit: 1,
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+
+ if (existing) {
+ ({ domain, ticker, precision, name } = existing);
+ }
+ }
+
+ acc = { ...acc, ...{ domain, ticker, precision, name } };
+ acc.balance = amount;
+ acc.pending = 0;
+ acc.network = "liquid";
+ a2 = await db.Account.create(acc, { transaction });
+ }
+
+ let spread = user.username === "coinos" ? 1.02 : 1;
+ amount = Math.round(amount / spread);
+
+ params = {
+ amount,
+ account_id: a2.id,
+ user_id: recipient.id,
+ rate: store.rates[recipient.currency] * spread,
+ currency: recipient.currency,
+ confirmed: true,
+ hash: `#${v4().substr(0, 6)} Payment from ${user.username}`,
+ memo,
+ network: "COINOS",
+ received: true
+ };
+
+ if (invoice) {
+ params.invoice_id = invoice.id;
+
+ let c = store.convert[invoice.text];
+ if (c) {
+ l(
+ "internal payment detected for conversion",
+ invoice.text,
+ c.address,
+ recipient.username
+ );
+
+ recipient.account = a2;
+
+ sendLiquid({
+ address: c.address,
+ amount: amount - 100,
+ user: recipient,
+ limit: amount
+ }).catch(console.log);
+ }
+ }
+
+ let p2 = await db.Payment.create(params, { transaction });
+
+ p2 = p2.get({ plain: true });
+ p2.account = a2.get({ plain: true });
+ emit(recipient.username, "payment", p2);
+ emit(recipient.username, "account", p2.account);
+
+ l("received internal", recipient.username, amount);
+ notify(recipient, `Received ${amount} ${a2.ticker} sats`);
+ callWebhook(invoice, p2);
+
+ }
+
+ return payment;
+ });
+};