diff options
| author | Adam Soltys <[email protected]> | 2023-01-13 23:50:30 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2023-01-13 23:50:30 -0800 |
| commit | 6bafea9bffe24d2227d269812a73a9a513d92ddf (patch) | |
| tree | 6dd463ffa410f4b7eebc870b24db305785414aa0 /lib/sendInternal.js | |
| parent | 8a114c50efaac41767042766f05390aa906da157 (diff) | |
| download | coinos-server-6bafea9bffe24d2227d269812a73a9a513d92ddf.tar.gz coinos-server-6bafea9bffe24d2227d269812a73a9a513d92ddf.zip | |
internal payments
Diffstat (limited to 'lib/sendInternal.js')
| -rw-r--r-- | lib/sendInternal.js | 242 |
1 files changed, 78 insertions, 164 deletions
diff --git a/lib/sendInternal.js b/lib/sendInternal.js index 5a8b0b9d..f801c9cd 100644 --- a/lib/sendInternal.js +++ b/lib/sendInternal.js @@ -6,197 +6,111 @@ import { v4 } from "uuid"; import { notify } from "$lib/notifications"; import { l, err } from "$lib/logging"; import { fail } from "$lib/utils"; +import ln from "$lib/ln"; +import { g, s, rd } from "$lib/redis"; const { HOSTNAME: hostname } = process.env; export default async ( - { amount, address, payreq, unconfidential, asset, memo, username, tip }, + { amount, address, payreq, unconfidential, memo, username, tip }, 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"); + let fee = 0; + let recipient; - if (account.balance < amount) { - throw new Error("Insufficient funds"); - } + let t = async () => { + await rd.watch(`user:${user_id}`); + user = await g(`user:${user_id}`); + if (user.balance < amount) throw new Error("Insufficient funds"); - let fee = 0; + user.balance -= amount; + let m = await rd.multi(); + await s(`user:${user.id}`, user); - await account.decrement({ balance: amount }, { transaction }); - await account.reload({ transaction }); + if (username && (recipient = await g(`user:${username}`))) { + let { payment_hash: h } = await ln.decode(payreq); + let invoice = payreq && (await g(`invoice:${h}`)); - let params, recipient, payment; - - if (username) { - recipient = await db.User.findOne( - { - where: { username }, - include: { - model: db.Account, - as: "account" - } - }, - { transaction } - ); - - params = { - where: { user_id: recipient.id }, - order: [["id", "DESC"]] + let t = async () => { + await rd.watch(`user:${recipient.id}`); + recipient = await g(`user:${recipient.id}`); + recipient.balance += amount; + let m = await rd.multi(); + await s(`user:${recipient.id}`, recipient); + if (!(await m.exec())) t(); }; - 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); - } - } + await t(); - acc = { ...acc, ...{ domain, ticker, precision, name } }; - acc.balance = amount; - acc.pending = 0; - acc.network = "liquid"; - a2 = await db.Account.create(acc, { transaction }); - } - - params = { - amount, - account_id: a2.id, - user_id: recipient.id, - rate: store.rates[recipient.currency], - currency: recipient.currency, - confirmed: true, - hash: `#${v4().substr(0, 6)} Payment from ${user.username}`, - memo, - network: "COINOS", - received: true, - with_id: user.id - }; + if (!(await m.exec())) t(); + } + await t(); - if (invoice) { - let { tip } = invoice; - params.invoice_id = invoice.id; - if (tip) { - params.tip = tip; - params.amount -= tip; - } + let p2 = { + id: v4(), + amount, + user_id: recipient.id, + rate: store.rates[recipient.currency], + currency: recipient.currency, + memo, + type: "internal", + with_id: user.id, + created_at: Date.now() + }; - await invoice.increment({ received: amount }, { transaction }); - await invoice.reload({ transaction }); + if (invoice) { + let { tip } = invoice; + params.invoice_id = invoice.id; + if (tip) { + params.tip = tip; + params.amount -= tip; } - let p2 = await db.Payment.create(params, { transaction }); - - p2 = p2.get({ plain: true }); - p2.account = a2.get({ plain: true }); - if (invoice) p2.invoice = invoice.get({ plain: true }); + invoice.received += amount; + await s(`invoice:${h}`, invoice); + } - emit(recipient.username, "payment", p2); - emit(recipient.username, "account", p2.account); + if (invoice) p2.invoice = invoice; + await s(`payment:${p2.id}`, p2); + await rd.lPush(`${recipient.id}:payments`, p2.id); - l("received internal", recipient.username, amount); - notify(recipient, `Received ${amount} ${a2.ticker} sats`); - callWebhook(invoice, p2); - } + emit(recipient.username, "payment", p2); - params = { - amount: -amount, - account_id: account.id, - memo, - user_id: user.id, - rate: store.rates[user.currency], - currency: user.currency, - confirmed: true, - with_id: recipient && recipient.id, - hash: `#${v4().substr(0, 6)} ${ - username ? `Payment to ${username}` : "Internal Transfer" - }`, - network: "COINOS" - }; + l("received internal", recipient.username, amount); + notify(recipient, `Received ${amount} sats`); + callWebhook(invoice, p2); + }; - if (!username) { - l("creating redeemable payment"); - params.redeemcode = v4(); - params.hash = `${hostname}/redeem/${params.redeemcode}`; - } + let p1 = { + id: v4(), + amount: -amount, + memo, + user_id: user.id, + rate: store.rates[user.currency], + currency: user.currency, + with_id: recipient && recipient.id, + hash: `#${v4().substr(0, 6)} ${ + username ? `Payment to ${username}` : "Internal Transfer" + }`, + type: "internal", + created_at: Date.now() + }; - payment = await db.Payment.create(params, { transaction }); + if (!username) { + l("creating redeemable payment"); + params.redeemcode = v4(); + params.hash = `${hostname}/redeem/${params.redeemcode}`; + } - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); + await s(`payment:${p1.id}`, p1); + await rd.lPush(`${user.id}:payments`, p1.id); - l("sent internal", user.username, -payment.amount); + l("sent internal", user.username, -p1.amount); - emit(user.username, "payment", payment); - emit(user.username, "account", account); + emit(user.username, "payment", p1); - return payment; - }); + return p1; }; |
