aboutsummaryrefslogtreecommitdiffstats
path: root/lib/payments.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-01-27 13:58:06 -0800
committerAdam Soltys <[email protected]>2024-01-27 13:58:06 -0800
commit96f865cd9c9e70205d062e0cda86e0114afb5fbc (patch)
tree7e15e5e0a178374a5f03d02008b6d866df6fe1ac /lib/payments.js
parent1b14654bc2d4766353d3f054a7c7902d560bef59 (diff)
downloadcoinos-server-96f865cd9c9e70205d062e0cda86e0114afb5fbc.tar.gz
coinos-server-96f865cd9c9e70205d062e0cda86e0114afb5fbc.zip
autowithdrawals
Diffstat (limited to 'lib/payments.js')
-rw-r--r--lib/payments.js188
1 files changed, 165 insertions, 23 deletions
diff --git a/lib/payments.js b/lib/payments.js
index c924dd2b..c7cac619 100644
--- a/lib/payments.js
+++ b/lib/payments.js
@@ -8,11 +8,6 @@ import { l, err, warn } from "$lib/logging";
import { btc, fail, getInvoice, sleep, wait, SATS, sats } from "$lib/utils";
import { callWebhook } from "$lib/webhooks";
import got from "got";
-import { spawn } from "child_process";
-import ffmpeg from "fluent-ffmpeg";
-import fs from "fs";
-import EventEmitter from "events";
-import crc32 from "buffer-crc32";
import { mqtt1, mqtt2 } from "$lib/mqtt";
import { mail, templates } from "$lib/mail";
@@ -45,7 +40,7 @@ export let debit = async ({
}) => {
let ref;
let { id: uid, currency, username } = user;
- if (!rate) rate = store.rates[currency];
+ if (!rate) rate = store.rates[currency];
let invoice = await getInvoice(hash);
let iid;
@@ -112,6 +107,7 @@ export let debit = async ({
};
export let credit = async (hash, amount, memo, ref, type = types.internal) => {
+ console.log("crediting");
amount = parseInt(amount) || 0;
let inv = await getInvoice(hash);
@@ -187,6 +183,7 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => {
emit(uid, "payment", p);
notify(user, `Received ${amount} sats`);
callWebhook(inv, p);
+ l(username, "received", type, amount);
if (user.verified && user.notify) {
mail(user, "Payment received", templates.paymentReceived, {
@@ -200,20 +197,159 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => {
});
}
- mqtt1.publish(
- username,
- `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
- );
+ if (config.mqtt1) {
+ mqtt1.publish(
+ username,
+ `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
+ );
+ }
- mqtt2.publish(
- username,
- `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
- );
+ if (config.mqtt2) {
+ mqtt2.publish(
+ username,
+ `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
+ );
+ }
+
+ autowithdraw(user);
+ console.log("done crediting");
return p;
};
-export let payLightning = async (user, payreq, amount, maxfee, memo) => {
+let autowithdraw = async (user) => {
+ let { autowithdraw, id, destination, threshold, withdrawal } = user;
+ destination = destination.trim();
+ if (autowithdraw) {
+ let balance = await g(`balance:${id}`);
+ if (balance > threshold)
+ await pay({ amount: withdrawal, to: destination, user });
+ }
+};
+
+let pay = async ({ amount, to, user }) => {
+ amount = parseInt(amount) || 0;
+ let lnurl;
+ if (to.includes("@") && to.includes(".")) {
+ let [name, domain] = to.split("@");
+ lnurl = `https://${domain}/.well-known/lnurlp/${name}`;
+ } else if (to.startsWith("lnurl")) {
+ lnurl = Buffer.from(
+ bech32.fromWords(bech32.decode(text, 20000).words),
+ ).toString();
+ }
+
+ if (lnurl) {
+ let { callback } = await got(lnurl).json();
+ let { pr } = await got(`${callback}?amount=${amount}`).json();
+ await sendLightning(user, pr, amount);
+ }
+
+ await sendOnchain({ amount, address: to, user });
+};
+
+let decode = async (hex) => {
+ let type, tx;
+ try {
+ tx = await bc.decodeRawTransaction(hex);
+ type = types.bitcoin;
+ } catch (e) {
+ try {
+ tx = await lq.decodeRawTransaction(hex);
+ type = types.liquid;
+ } catch (e) {
+ fail("unrecognized tx");
+ }
+ }
+
+ console.log("TYPE", type);
+
+ return { tx, type };
+};
+
+export let sendOnchain = async (params) => {
+ let { hex, rate, user } = params;
+ if (!hex) ({ hex } = await build(params));
+
+ let { tx, type } = await decode(hex);
+ let node = getNode(type);
+ let { txid } = tx;
+
+ console.log("YO");
+
+ if (config[type].walletpass)
+ await node.walletPassphrase(config[type].walletpass, 300);
+
+ let { hex: signed } = await node.signRawTransactionWithWallet(
+ type === types.liquid ? await node.blindRawTransaction(hex) : hex,
+ );
+
+ ({ txid } = await node.decodeRawTransaction(signed));
+
+ let r = await node.testMempoolAccept([signed]);
+ if (!r[0].allowed) fail("transaction rejected");
+
+ let total = 0;
+ let fee = 0;
+ let change = 0;
+
+ if (type === types.liquid) {
+ for (let {
+ asset,
+ scriptPubKey: { address, type },
+ value,
+ } of tx.vout) {
+ if (asset !== config.liquid.btc) fail("only L-BTC supported");
+ if (type === "fee") fee = sats(value);
+ else {
+ total += sats(value);
+
+ if (address) {
+ if ((await node.getAddressInfo(address)).ismine) {
+ change += sats(value);
+ }
+ }
+ }
+ }
+ } else {
+ let totalIn = 0;
+ for await (let { txid, vout } of tx.vin) {
+ let hex = await node.getRawTransaction(txid);
+ let tx = await node.decodeRawTransaction(hex);
+ totalIn += sats(tx.vout[vout].value);
+ }
+
+ for (let {
+ scriptPubKey: { address },
+ value,
+ } of tx.vout) {
+ total += sats(value);
+ if (await g(`invoice:${address}`))
+ fail("Cannot send to internal address");
+
+ if ((await node.getAddressInfo(address)).ismine) change += sats(value);
+ }
+
+ fee = totalIn - total;
+ }
+
+ let amount = total - change;
+
+ console.log("HERE", amount, signed.length);
+
+ await debit({
+ hash: txid,
+ amount,
+ fee,
+ rate,
+ user,
+ type,
+ });
+
+ return node.sendRawTransaction(signed);
+};
+
+export let sendLightning = async (user, payreq, amount, maxfee, memo) => {
let p;
let hash = payreq;
@@ -325,25 +461,31 @@ export let getNode = (type) => {
else fail("unrecognized transaction type");
};
-export let build = async ({ amount, address, feeRate }, user) => {
- let type;
-
+let getAddressType = async (a) => {
+ console.log("A", a);
try {
- await bc.getAddressInfo(address);
- type = types.bitcoin;
+ await bc.getAddressInfo(a);
+ return types.bitcoin;
} catch (e) {
try {
- await lq.getAddressInfo(address);
- type = types.liquid;
+ await lq.getAddressInfo(a);
+ return types.liquid;
} catch (e) {
fail("unrecognized address");
}
}
+};
+
+export let build = async ({ amount, address, feeRate, user }) => {
+ let type = await getAddressType(address);
+ console.log("type", type, amount);
let node = getNode(type);
amount = parseInt(amount);
- let fees = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json());
+ let fees = { halfHourFee: 50 };
+
+ // let fees = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json());
if (!feeRate) {
feeRate = fees.halfHourFee;