aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/payments.js88
-rw-r--r--lib/whitelist.js2
-rw-r--r--routes/payments.js13
-rw-r--r--routes/users.js3
4 files changed, 61 insertions, 45 deletions
diff --git a/lib/payments.js b/lib/payments.js
index c7cac619..275c6787 100644
--- a/lib/payments.js
+++ b/lib/payments.js
@@ -107,7 +107,6 @@ 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);
@@ -211,19 +210,23 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => {
);
}
- autowithdraw(user);
- console.log("done crediting");
+ if (p.confirmed) autowithdraw(user);
return p;
};
-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 });
+export let autowithdraw = async (user) => {
+ try {
+ let { username, autowithdraw, id, destination, threshold, reserve } = user;
+ if (autowithdraw) {
+ let to = destination.trim();
+ let balance = await g(`balance:${id}`);
+ let amount = balance - reserve;
+ console.log("AMT", amount, balance, reserve);
+ if (balance > threshold) await pay({ amount, to, user });
+ }
+ } catch (e) {
+ warn(username, "autowithdraw failed", e);
}
};
@@ -239,13 +242,19 @@ let pay = async ({ amount, to, user }) => {
).toString();
}
+ let pr;
if (lnurl) {
let { callback } = await got(lnurl).json();
- let { pr } = await got(`${callback}?amount=${amount}`).json();
- await sendLightning(user, pr, amount);
+ ({ pr } = await got(`${callback}?amount=${amount}`).json());
+ } else if (to.startsWith("ln")) {
+ try {
+ await ln.decode(to);
+ pr = to;
+ } catch (e) {}
}
- await sendOnchain({ amount, address: to, user });
+ if (pr) await sendLightning({ user, pr, amount });
+ else await sendOnchain({ amount, address: to, user, subtract: true });
};
let decode = async (hex) => {
@@ -262,8 +271,6 @@ let decode = async (hex) => {
}
}
- console.log("TYPE", type);
-
return { tx, type };
};
@@ -275,8 +282,6 @@ export let sendOnchain = async (params) => {
let node = getNode(type);
let { txid } = tx;
- console.log("YO");
-
if (config[type].walletpass)
await node.walletPassphrase(config[type].walletpass, 300);
@@ -335,8 +340,6 @@ export let sendOnchain = async (params) => {
let amount = total - change;
- console.log("HERE", amount, signed.length);
-
await debit({
hash: txid,
amount,
@@ -349,9 +352,9 @@ export let sendOnchain = async (params) => {
return node.sendRawTransaction(signed);
};
-export let sendLightning = async (user, payreq, amount, maxfee, memo) => {
+export let sendLightning = async ({ user, pr, amount, maxfee, memo }) => {
let p;
- let hash = payreq;
+ let hash = pr;
if (typeof amount !== "undefined") {
amount = parseInt(amount);
@@ -359,21 +362,36 @@ export let sendLightning = async (user, payreq, amount, maxfee, memo) => {
}
let total = amount;
- let { amount_msat, payment_hash } = await ln.decode(payreq);
+ let { amount_msat, payment_hash } = await ln.decode(pr);
if (amount_msat) total = Math.round(amount_msat / 1000);
- maxfee = maxfee ? parseInt(maxfee) : Math.round(total * 0.05);
+ maxfee =
+ parseInt(maxfee) ||
+ Math.round(
+ total < 100
+ ? total * 5
+ : total < 1000
+ ? total
+ : total < 10000
+ ? total * 0.5
+ : total < 100000
+ ? total * 0.1
+ : total < 1000000
+ ? total * 0.05
+ : total * 0.01,
+ );
+
if (maxfee < 0) fail("Max fee cannot be negative");
let invoice = await getInvoice(hash);
if (invoice) {
if (invoice.uid === user.id) fail("Cannot send to self");
- hash = payreq;
+ hash = pr;
} else {
let r;
- let { pays } = await ln.listpays(payreq);
+ let { pays } = await ln.listpays(pr);
if (pays.find((p) => p.status === "complete"))
fail("Invoice has already been paid");
@@ -381,7 +399,7 @@ export let sendLightning = async (user, payreq, amount, maxfee, memo) => {
fail("Payment is already underway");
p = await debit({
- hash: payreq,
+ hash: pr,
amount: total,
fee: maxfee,
memo,
@@ -391,7 +409,7 @@ export let sendLightning = async (user, payreq, amount, maxfee, memo) => {
let check = async () => {
try {
- let { pays } = await ln.listpays(payreq);
+ let { pays } = await ln.listpays(pr);
let recordExists = !!(await g(`payment:${p.id}`));
let paymentFailed =
@@ -421,10 +439,10 @@ export let sendLightning = async (user, payreq, amount, maxfee, memo) => {
let interval = setInterval(check, 5000);
try {
- l("paying lightning invoice", payreq.substr(-8), total, amount, maxfee);
+ l("paying lightning invoice", pr.substr(-8), total, amount, maxfee);
r = await ln.pay({
- bolt11: payreq.replace(/\s/g, "").toLowerCase(),
+ bolt11: pr.replace(/\s/g, "").toLowerCase(),
amount_msat: amount_msat ? undefined : amount * 1000,
maxfee: maxfee * 1000,
retry_for: 5,
@@ -462,7 +480,6 @@ export let getNode = (type) => {
};
let getAddressType = async (a) => {
- console.log("A", a);
try {
await bc.getAddressInfo(a);
return types.bitcoin;
@@ -476,16 +493,13 @@ let getAddressType = async (a) => {
}
};
-export let build = async ({ amount, address, feeRate, user }) => {
+export let build = async ({ amount, address, feeRate, user, subtract }) => {
let type = await getAddressType(address);
- console.log("type", type, amount);
let node = getNode(type);
amount = parseInt(amount);
- let fees = { halfHourFee: 50 };
-
- // let fees = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json());
+ let fees = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json());
if (!feeRate) {
feeRate = fees.halfHourFee;
@@ -494,8 +508,6 @@ export let build = async ({ amount, address, feeRate, user }) => {
let replaceable = true;
let ourfee = Math.round(amount * config.fee);
let credit = await g(`credit:${type}:${user.id}`);
- let covered = Math.min(credit, ourfee) || 0;
- ourfee -= covered;
let outs = [{ [address]: btc(amount) }];
@@ -515,7 +527,7 @@ export let build = async ({ amount, address, feeRate, user }) => {
let balance = await g(`balance:${user.id}`);
- if (amount + fee + ourfee > balance) {
+ if (subtract || amount + fee + ourfee > balance) {
if (amount <= fee + ourfee + dust)
fail(
`insufficient funds ⚡️${balance} of ⚡️${amount + fee + ourfee + dust}`,
diff --git a/lib/whitelist.js b/lib/whitelist.js
index 9ef1ceca..fffe779d 100644
--- a/lib/whitelist.js
+++ b/lib/whitelist.js
@@ -28,6 +28,7 @@ export default [
"profile",
"prompt",
"pubkey",
+ "reserve",
"salt",
"seed",
"shopifyStore",
@@ -37,5 +38,4 @@ export default [
"twofa",
"username",
"verified",
- "withdrawal",
];
diff --git a/routes/payments.js b/routes/payments.js
index 13ef20c2..86362a01 100644
--- a/routes/payments.js
+++ b/routes/payments.js
@@ -7,6 +7,7 @@ import { l, err, warn } from "$lib/logging";
import { bail, fail, getInvoice, btc, sats, SATS } from "$lib/utils";
import { requirePin } from "$lib/auth";
import {
+ autowithdraw,
debit,
credit,
types,
@@ -63,7 +64,7 @@ export default {
})
.json();
} else if (payreq) {
- p = await sendLightning(user, payreq, amount, maxfee, memo);
+ p = await sendLightning({ user, pr: payreq, amount, maxfee, memo });
}
if (!p) {
@@ -270,6 +271,8 @@ export default {
.exec();
emit(p.uid, "payment", p);
+ let user = await g(`user:${p.uid}`);
+ autowithdraw(user);
}
}
}
@@ -285,7 +288,7 @@ export default {
try {
res.send(await build({ ...body, user }));
} catch (e) {
- console.log(e)
+ console.log(e);
warn(
"problem estimating fee",
e.message,
@@ -302,11 +305,11 @@ export default {
async send({ body, user }, res) {
try {
await requirePin({ body, user });
- let txid = await sendOnchain({ ...body, user });
+ let txid = await sendOnchain({ ...body, user });
res.send({ txid });
} catch (e) {
- console.log(e)
+ console.log(e);
warn("payment failed", e.message);
res.code(500).send(e.message);
}
@@ -365,7 +368,7 @@ export default {
let r = await got(`${callback}?amount=${amount * 1000}`).json();
if (r.reason) fail(r.rason);
let { pr } = r;
- let p = await sendLightning(user, pr, amount, maxfee, memo);
+ let p = await sendLightning({ user, pr, amount, maxfee, memo });
if (!p) {
p = await debit({ hash: pr, amount, memo, user });
diff --git a/routes/users.js b/routes/users.js
index dff3f7bc..8e2a0d14 100644
--- a/routes/users.js
+++ b/routes/users.js
@@ -41,6 +41,7 @@ export default {
user.prompt = !!user.prompt;
if (user.pubkey)
user.npub = encode("npub", toWords(Buffer.from(user.pubkey, "hex")));
+
res.send(pick(user, whitelist));
} catch (e) {
console.log("problem fetching user", e);
@@ -237,6 +238,7 @@ export default {
"prompt",
"profile",
"pubkey",
+ "reserve",
"salt",
"seed",
"threshold",
@@ -244,7 +246,6 @@ export default {
"twofa",
"shopifyToken",
"shopifyStore",
- "withdrawal",
];
for (let a of attributes) {