aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-09-08 20:57:06 -0700
committerAdam Soltys <[email protected]>2024-09-08 20:57:06 -0700
commit5c105c8f4738ab1ead3c5b92f465f899cf0a7446 (patch)
tree9cea2d1b46cdab9869f742e3286c9fb0762f8948
parent92bd9085a576517e06dc000d1e1ff11d27bc34b4 (diff)
downloadcoinos-server-5c105c8f4738ab1ead3c5b92f465f899cf0a7446.tar.gz
coinos-server-5c105c8f4738ab1ead3c5b92f465f899cf0a7446.zip
refactor rpc helper, accounts endpoints
-rw-r--r--index.ts3
-rw-r--r--lib/invoices.ts7
-rw-r--r--lib/lightning.ts2
-rw-r--r--lib/payments.ts29
-rw-r--r--lib/rpc.ts43
-rw-r--r--lib/utils.ts1
-rw-r--r--routes/ecash.ts2
-rw-r--r--routes/invoices.ts7
-rw-r--r--routes/payments.ts116
-rw-r--r--routes/users.ts39
10 files changed, 141 insertions, 108 deletions
diff --git a/index.ts b/index.ts
index b2072952..2aa96f4f 100644
--- a/index.ts
+++ b/index.ts
@@ -79,6 +79,9 @@ app.post("/confirm", payments.confirm);
app.post("/bitcoin/fee", auth, payments.fee);
app.post("/bitcoin/send", auth, payments.send);
+app.get("/accounts", auth, users.accounts);
+app.post("/accounts", auth, users.createAccount);
+
app.get("/users", auth, users.list);
app.get("/me", auth, users.me);
app.get("/users/:key", users.get);
diff --git a/lib/invoices.ts b/lib/invoices.ts
index 6934a2d4..013cb975 100644
--- a/lib/invoices.ts
+++ b/lib/invoices.ts
@@ -5,13 +5,10 @@ import { getUser, bip21, fail, SATS } from "$lib/utils";
import { types } from "$lib/payments";
import { v4 } from "uuid";
import rpc from "$lib/rpc";
-
-import bc_ from "$lib/bitcoin";
-import lq_ from "$lib/liquid";
import ln from "$lib/ln";
-let bc = bc_ as any;
-let lq = lq_ as any;
+let bc = rpc(config.bitcoin);
+let lq = rpc(config.liquid);
export let generate = async ({ invoice, user, sender = undefined }) => {
let {
diff --git a/lib/lightning.ts b/lib/lightning.ts
index 2d44581d..f86b9a2d 100644
--- a/lib/lightning.ts
+++ b/lib/lightning.ts
@@ -44,7 +44,7 @@ export async function listenForLightning() {
if (p) return warn("already processed", bolt11);
- await credit(bolt11, received, invoice.memo, preimage, types.lightning);
+ await credit({ hash:bolt11, amount:received, memo:invoice.memo, ref:preimage, type:types.lightning});
} catch (e) {
console.log(e);
err("problem receiving lightning payment", e.message);
diff --git a/lib/payments.ts b/lib/payments.ts
index 5d578af9..36f18e71 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -133,7 +133,14 @@ export let debit = async ({
return p;
};
-export let credit = async (hash, amount, memo, ref, type = types.internal) => {
+export let credit = async ({
+ hash,
+ amount,
+ memo = undefined,
+ ref = undefined,
+ type = types.internal,
+ account = undefined,
+}) => {
amount = parseInt(amount) || 0;
let inv = await getInvoice(hash);
@@ -169,6 +176,7 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => {
let id = v4();
let p = {
+ account,
id,
iid: inv.id,
hash,
@@ -208,8 +216,8 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => {
m.set(`invoice:${inv.id}`, JSON.stringify(inv))
.set(`payment:${p.id}`, JSON.stringify(p))
- .lPush(`${uid}:payments`, p.id)
- .incrBy(`${balanceKey}:${uid}`, amount)
+ .lPush(`${account || uid}:payments`, p.id)
+ .incrBy(`${balanceKey}:${account || uid}`, amount)
.exec();
emit(uid, "payment", p);
@@ -355,7 +363,7 @@ export let sendOnchain = async (params) => {
if (!hex) ({ hex } = await build(params));
let { tx, type } = await decode(hex);
- let node = getNode(type);
+ let node = rpc(config[type]);
let { txid } = tx;
try {
@@ -582,16 +590,6 @@ export let sendLightning = async ({
}
};
-export let getNode = (type, wallet = undefined) => {
- if (type === types.bitcoin) {
- return bc;
- } else if (type === types.liquid) {
- return lq;
- } else if (wallet) {
- return rpc({ ...config.bitcoin, wallet });
- } else fail("unrecognized transaction type");
-};
-
let getAddressType = async (a) => {
try {
await bc.getAddressInfo(a);
@@ -608,8 +606,7 @@ let getAddressType = async (a) => {
export let build = async ({ amount, address, feeRate, user, subtract }) => {
let type = await getAddressType(address);
-
- let node = getNode(type);
+ let node = rpc(config[type]);
amount = parseInt(amount);
if (amount < 0) fail("invalid amount");
diff --git a/lib/rpc.ts b/lib/rpc.ts
index 0346a3cd..909ec96f 100644
--- a/lib/rpc.ts
+++ b/lib/rpc.ts
@@ -1,28 +1,19 @@
import got from "got";
+import { fail } from "$lib/utils";
+import { err } from "$lib/logging";
-export default ({ host, port, wallet, username, password }) =>
- new Proxy(
- {},
- {
- get:
- (_, prop) =>
- (...params) =>
- ((method, ...params) => {
- let url = `http://${host}:${port}/wallet/${wallet}`;
- return got
- .post(url, {
- json: {
- method,
- params,
- },
- headers: {
- authorization: `Basic ${Buffer.from(
- `${username}:${password}`,
- ).toString("base64")}`,
- },
- })
- .json()
- .then(({ result }) => result);
- })((prop as any).toLowerCase(), ...params),
- },
- );
+export default ({ host, port, wallet, username, password }): any => {
+ let url = `http://${host}:${port}/wallet/${wallet}`;
+ let token = btoa(`${username}:${password}`);
+ let headers = { authorization: `Basic ${token}` };
+ let p = (json) => got.post(url, { json, headers }).json();
+
+ let get =
+ (_, prop: any) =>
+ (...params) =>
+ p({ method: prop.toLowerCase(), params })
+ .then((res: any) => res.result)
+ .catch((e) => (err(e.message), fail(`Failed to call ${prop}`)));
+
+ return new Proxy({}, { get });
+};
diff --git a/lib/utils.ts b/lib/utils.ts
index ada32521..b0875873 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -1,6 +1,5 @@
import { randomBytes, bytesToHex } from "@noble/hashes/utils";
import migrate from "$lib/migrate";
-import store from "$lib/store";
import { g, s } from "$lib/db";
import config from "$config";
diff --git a/routes/ecash.ts b/routes/ecash.ts
index af7f842d..7516c05e 100644
--- a/routes/ecash.ts
+++ b/routes/ecash.ts
@@ -58,7 +58,7 @@ export default {
received: 0,
});
- await credit(hash, amount, memo, user.id, type);
+ await credit({hash, amount, memo, ref:user.id, type});
res.send({ ok: true });
} catch (e) {
diff --git a/routes/invoices.ts b/routes/invoices.ts
index 77ff2ac1..f4034408 100644
--- a/routes/invoices.ts
+++ b/routes/invoices.ts
@@ -1,11 +1,12 @@
import { g } from "$lib/db";
import { generate } from "$lib/invoices";
import { bail, pick } from "$lib/utils";
+import { err } from "$lib/logging";
export default {
async get(req, res) {
let {
- params: { id },
+ params: { id },
} = req;
let invoice = await g(`invoice:${id}`);
if (typeof invoice === "string") invoice = await g(`invoice:${invoice}`);
@@ -34,8 +35,10 @@ export default {
} = req;
try {
- res.send(await generate({ invoice, user, sender }));
+ res.send(await generate({ invoice, user, sender }));
} catch (e) {
+ console.log(e);
+ err("problem generating invoice", e.message);
bail(res, e.message);
}
},
diff --git a/routes/payments.ts b/routes/payments.ts
index cf67e966..c0c6d947 100644
--- a/routes/payments.ts
+++ b/routes/payments.ts
@@ -12,7 +12,6 @@ import {
debit,
credit,
types,
- getNode,
build,
sendLightning,
sendOnchain,
@@ -20,9 +19,13 @@ import {
import { mqtt1, mqtt2 } from "$lib/mqtt";
import got from "got";
import api from "$lib/api";
+import rpc from "$lib/rpc";
import ln from "$lib/ln";
+let { wallet: bcw } = config.bitcoin;
+let { wallet: lqw } = config.liquid;
+
export default {
async info(_, res) {
res.send(await ln.getinfo());
@@ -53,7 +56,7 @@ export default {
if (!p) {
if (hash) {
p = await debit({ hash, amount, memo, user });
- await credit(hash, amount, memo, user.id);
+ await credit({ hash, amount, memo, ref: user.id });
} else if (fund) {
p = await debit({ hash, amount, memo: fund, user, type: types.fund });
await db.incrBy(`fund:${fund}`, amount);
@@ -73,13 +76,13 @@ export default {
async list(req, res) {
let {
user: { id },
- query: { start, end, limit, offset },
+ query: { account, start, end, limit, offset },
} = req;
if (limit) limit = parseInt(limit);
offset = parseInt(offset) || 0;
- let payments = (await db.lRange(`${id}:payments`, 0, -1)) || [];
+ let payments = (await db.lRange(`${account || id}:payments`, 0, -1)) || [];
payments = (
await Promise.all(
payments.map(async (id) => {
@@ -237,7 +240,13 @@ export default {
});
}
- let payment = await credit(iid, amount, id, id, types.fund);
+ let payment = await credit({
+ hash: iid,
+ amount,
+ memo: id,
+ ref: id,
+ type: types.fund,
+ });
await db.lPush(`fund:${id}:payments`, payment.id);
res.send({ payment });
@@ -253,58 +262,65 @@ export default {
} = req;
try {
- let known = await g(`${wallet}:balance`) !== null
- let node = getNode(type, known ? wallet : undefined);
+ let node = rpc({ ...config[type], wallet });
- if (wallet === config.bitcoin.wallet || wallet === config.liquid.wallet) {
- let { confirmations, details } = await node.getTransaction(txid);
- for (let { address, amount, asset, category, vout } of details) {
- if (!address) continue;
- if (type === types.liquid && asset !== config.liquid.btc) continue;
- if (category === "send") {
- let p = await g(`payment:${txid}`);
- if (typeof p === "string") p = await g(`payment:${p}`);
- if (!p) continue;
+ let { confirmations, details } = await node.getTransaction(txid);
- if (confirmations >= 1) p.confirmed = true;
- await s(`payment:${p.id}`, p);
- emit(p.uid, "payment", p);
- continue;
- }
+ for (let { address, amount, asset, category, vout } of details) {
+ if (!address) continue;
+ if (type === types.liquid && asset !== config.liquid.btc) continue;
- let p = await g(`payment:${txid}:${vout}`);
+ if (category === "send") {
+ let p = await g(`payment:${txid}`);
if (typeof p === "string") p = await g(`payment:${p}`);
+ if (!p) continue;
- if (!p) {
- await credit(address, sats(amount), "", `${txid}:${vout}`, type);
- } else if (confirmations >= 1) {
- let id = `payment:${txid}:${vout}`;
- let p = await g(id);
- if (typeof p === "string") p = await g(`payment:${p}`);
- if (!p) return db.sAdd("missed", id);
- if (p.confirmed) return;
+ if (confirmations >= 1) p.confirmed = true;
+ await s(`payment:${p.id}`, p);
+ emit(p.uid, "payment", p);
+ continue;
+ }
- let invoice = await getInvoice(address);
- let { id: iid } = invoice;
+ let p = await g(`payment:${txid}:${vout}`);
+ if (typeof p === "string") p = await g(`payment:${p}`);
- p.confirmed = true;
- invoice.received += parseInt(invoice.pending);
- invoice.pending = 0;
+ if (!p) {
+ let account;
+ if ((await g(`${wallet}:balance`)) !== null) account = wallet;
+ await credit({
+ hash: address,
+ amount: sats(amount),
+ ref: `${txid}:${vout}`,
+ type,
+ account,
+ });
+ } else if (confirmations >= 1) {
+ let id = `payment:${txid}:${vout}`;
+ let p = await g(id);
+ if (typeof p === "string") p = await g(`payment:${p}`);
+ if (!p) return db.sAdd("missed", id);
+ if (p.confirmed) return;
- l("confirming", id, p.id, p.amount);
+ let invoice = await getInvoice(address);
+ let { id: iid } = invoice;
- await db
- .multi()
- .set(`invoice:${iid}`, JSON.stringify(invoice))
- .set(`payment:${p.id}`, JSON.stringify(p))
- .decrBy(`pending:${p.uid}`, p.amount)
- .incrBy(`balance:${p.uid}`, p.amount)
- .exec();
+ p.confirmed = true;
+ invoice.received += parseInt(invoice.pending);
+ invoice.pending = 0;
- emit(p.uid, "payment", p);
- let user = await g(`user:${p.uid}`);
- await completePayment(p, user);
- }
+ l("confirming", id, p.id, p.amount);
+
+ await db
+ .multi()
+ .set(`invoice:${iid}`, JSON.stringify(invoice))
+ .set(`payment:${p.id}`, JSON.stringify(p))
+ .decrBy(`pending:${p.uid}`, p.amount)
+ .incrBy(`balance:${p.uid}`, p.amount)
+ .exec();
+
+ emit(p.uid, "payment", p);
+ let user = await g(`user:${p.uid}`);
+ await completePayment(p, user);
}
}
res.send({});
@@ -416,7 +432,7 @@ export default {
if (!p) {
p = await debit({ hash: pr, amount, memo, user });
- await credit(pr, amount, memo, user.id);
+ await credit({ hash: pr, amount, memo, ref: user.id });
}
res.send(p);
@@ -446,7 +462,7 @@ export default {
if (p.uid !== user.id) fail("unauthorized");
let { tx, type } = await decode(p.hex);
- let node = getNode(type);
+ let node = rpc(config[type]);
let fees: any = await fetch(`${api[type]}/fees/recommended`).then((r) =>
r.json(),
@@ -502,7 +518,7 @@ export default {
let memo;
let p = await debit({ hash, amount, memo, user: sender });
- await credit(hash, amount, memo, recipient.id);
+ await credit({ hash, amount, memo, ref: recipient.id });
res.send(p);
},
diff --git a/routes/users.ts b/routes/users.ts
index 3fcaad66..f49ca212 100644
--- a/routes/users.ts
+++ b/routes/users.ts
@@ -564,12 +564,25 @@ export default {
}
},
- async items(req, _) {
- let {
- params: { id },
- } = req;
- let item = await g(`item:${id}`);
- return item;
+ async accounts(req, res) {
+ let { user } = req;
+
+ let balance = await g(`balance:${user.id}`);
+ let accounts: any = [
+ {
+ balance,
+ type: "Cash",
+ mint: "https://mint.coinos.io",
+ },
+ ];
+
+ for (let id of await db.lRange(`${user.id}:accounts`, 0, 1)) {
+ let account = await g(`account:${id}`);
+ account.balance = await g(`balance:${id}`);
+ accounts.push(account);
+ }
+
+ res.send(accounts);
},
async hidepay(req, res) {
@@ -591,4 +604,18 @@ export default {
await s(`user:${u.id}`, u);
res.send({});
},
+
+ async createAccount(req, res) {
+ let { type, seed } = req.body;
+ let { user } = req;
+
+ let id = v4();
+ let account = { id, seed, type };
+
+ await s(`account:${id}`, account);
+ await s(`balance:${id}`, 0);
+ await db.lPush(`${user.id}:accounts`, id);
+
+ res.send(account);
+ },
};