aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--index.js1
-rw-r--r--lib/api.js5
-rw-r--r--lib/payments.js19
-rw-r--r--routes/nostr.js3
-rw-r--r--routes/payments.js77
5 files changed, 96 insertions, 9 deletions
diff --git a/index.js b/index.js
index c2f70c4c..57825fe6 100644
--- a/index.js
+++ b/index.js
@@ -64,6 +64,7 @@ app.post("/take", auth, payments.take);
app.post("/print", auth, payments.print);
app.post("/send/:lnaddress/:amount", auth, payments.lnaddress);
app.post("/gateway", payments.gateway);
+app.post("/replace", auth, payments.replace);
app.get("/encode", lnurl.encode);
app.get("/decode", lnurl.decode);
diff --git a/lib/api.js b/lib/api.js
new file mode 100644
index 00000000..3fa8c35b
--- /dev/null
+++ b/lib/api.js
@@ -0,0 +1,5 @@
+export default {
+ bitcoin: "https://mempool.space/api/v1",
+ liquid: "https://liquid.network/api/v1",
+};
+
diff --git a/lib/payments.js b/lib/payments.js
index 6c98c15e..2b2ed2af 100644
--- a/lib/payments.js
+++ b/lib/payments.js
@@ -11,6 +11,7 @@ import { callWebhook } from "$lib/webhooks";
import got from "got";
import { mqtt1, mqtt2 } from "$lib/mqtt";
import { mail, templates } from "$lib/mail";
+import api from "$lib/api";
import lq from "$lib/liquid";
import bc from "$lib/bitcoin";
@@ -18,11 +19,6 @@ import ln from "$lib/ln";
let { URL } = process.env;
-let api = {
- bitcoin: "https://mempool.space/api/v1",
- liquid: "https://liquid.network/api/v1",
-};
-
export let types = {
internal: "internal",
bitcoin: "bitcoin",
@@ -91,7 +87,7 @@ export let debit = async ({
memo,
iid,
uid,
- confirmed: true,
+ confirmed: ![types.bitcoin, types.liquid].includes(type),
rate,
currency,
type,
@@ -100,6 +96,7 @@ export let debit = async ({
created: Date.now(),
};
+ await s(`payment:${hash}`, id);
await s(`payment:${id}`, p);
await db.lPush(`${uid}:payments`, id);
@@ -267,12 +264,13 @@ let pay = async ({ amount, to, user }) => {
pr = to;
}
+ console.log("PAYING");
return pr
? await sendLightning({ user, pr, amount, maxfee })
: await sendOnchain({ amount, address: to, user, subtract: true });
};
-let decode = async (hex) => {
+export let decode = async (hex) => {
let type, tx;
try {
tx = await bc.decodeRawTransaction(hex);
@@ -282,6 +280,7 @@ let decode = async (hex) => {
tx = await lq.decodeRawTransaction(hex);
type = types.liquid;
} catch (e) {
+ err("invalid hex", hex);
fail("unrecognized tx");
}
}
@@ -364,7 +363,11 @@ export let sendOnchain = async (params) => {
type,
});
+ p.hex = signed;
+ await s(`payment:${p.id}`, p);
+
await node.sendRawTransaction(signed);
+
return p;
};
@@ -517,6 +520,8 @@ export let build = async ({ amount, address, feeRate, user, subtract }) => {
feeRate = fees.halfHourFee;
}
+ if (feeRate < fees.economyFee) fail("fee rate too low");
+
let replaceable = true;
let outs = [{ [address]: btc(amount) }];
diff --git a/routes/nostr.js b/routes/nostr.js
index 95186078..75c001a9 100644
--- a/routes/nostr.js
+++ b/routes/nostr.js
@@ -52,6 +52,7 @@ export default {
},
async messages({ params: { pubkey, since = 0 } }, res) {
+ console.log("H")
let params = {
kinds: [4],
authors: [pubkey],
@@ -84,6 +85,8 @@ export default {
}),
);
+ console.log(messages.length)
+
res.send(messages);
},
diff --git a/routes/payments.js b/routes/payments.js
index 12793495..f377b7e4 100644
--- a/routes/payments.js
+++ b/routes/payments.js
@@ -8,6 +8,7 @@ import { bail, fail, getInvoice, btc, sats, SATS } from "$lib/utils";
import { requirePin } from "$lib/auth";
import {
completePayment,
+ decode,
debit,
credit,
types,
@@ -18,6 +19,7 @@ import {
} from "$lib/payments";
import { mqtt1, mqtt2 } from "$lib/mqtt";
import got from "got";
+import api from "$lib/api";
import lq from "$lib/liquid";
import bc from "$lib/bitcoin";
@@ -240,7 +242,15 @@ export default {
for (let { address, amount, asset, category, vout } of details) {
if (!address) continue;
if (type === types.liquid && asset !== config.liquid.btc) continue;
- if (category !== "receive") continue;
+ if (category === "send") {
+ let p = await g(`payment:${txid}`);
+ if (typeof p === "string") p = await g(`payment:${p}`);
+
+ if (confirmations >= 1) p.confirmed = true;
+ await s(`payment:${p.id}`, p);
+ continue;
+ }
+
let p = await g(`payment:${txid}:${vout}`);
if (typeof p === "string") p = await g(`payment:${p}`);
@@ -272,7 +282,7 @@ export default {
emit(p.uid, "payment", p);
let user = await g(`user:${p.uid}`);
- await completePayment(p, user);
+ await completePayment(p, user);
}
}
}
@@ -305,6 +315,8 @@ export default {
try {
await requirePin({ body, user });
let { hash: txid } = await sendOnchain({ ...body, user });
+ let pid = await g(`payment:${txid}`);
+ let p = await g(`payment:${pid}`);
res.send({ txid });
} catch (e) {
@@ -385,4 +397,65 @@ export default {
await s(short_channel_id, webhook);
res.send({ ok: true });
},
+
+ async replace({ body: { id }, user }, res) {
+ try {
+ let { id: uid } = user;
+ let p = await g(`payment:${id}`);
+ if (!p) fail("Payment not found");
+ if (p.uid !== user.id) fail("unauthorized");
+
+ let { tx, type } = await decode(p.hex);
+ let node = getNode(type);
+
+ let fees = await fetch(`${api[type]}/fees/recommended`).then((r) =>
+ r.json(),
+ );
+
+ let outputs = [];
+ for (let {
+ scriptPubKey: { address },
+ value,
+ } of tx.vout) {
+ if (address && !(await node.getAddressInfo(address)).ismine)
+ outputs.push({ [address]: value });
+ }
+
+ let raw = await node.createRawTransaction(tx.vin, outputs);
+
+ let newTx = await node.fundRawTransaction(raw, {
+ fee_rate: fees.fastestFee,
+ replaceable: true,
+ subtractFeeFromOutputs: [],
+ });
+
+ let diff = sats(newTx.fee) - p.fee;
+ if (diff < 0) fail("fee must increase");
+
+ let ourfee = Math.round(diff * config.fee) || 0;
+
+ ourfee = await db.debit(
+ `balance:${uid}`,
+ `credit:${type}:${uid}`,
+ p.amount || 0,
+ 0,
+ diff,
+ ourfee,
+ );
+
+ p.hex = (await node.signRawTransactionWithWallet(newTx.hex)).hex;
+ let r = await node.testMempoolAccept([p.hex]);
+ if (!r[0].allowed) fail("transaction rejected");
+ p.hash = await node.sendRawTransaction(p.hex);
+ p.fee = sats(newTx.fee);
+ await s(`payment:${id}`, p);
+ await s(`payment:${p.hash}`, id);
+ emit(uid, "payment", p);
+
+ res.send({ ok: true });
+ } catch (e) {
+ err("failed to bump payment", id, e.message);
+ bail(res, e.message);
+ }
+ },
};