aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-09-23 16:23:19 -0700
committerAdam Soltys <[email protected]>2024-09-23 16:23:19 -0700
commit12ad5c9abb74cbe35c5d07233fdd9a7b7f5c2b70 (patch)
tree8e023771abd75a7b70e407c651aed3bcfe426ea3
parent01e1686265def675445c0f74a4b92ec4ffd71492 (diff)
downloadcoinos-server-12ad5c9abb74cbe35c5d07233fdd9a7b7f5c2b70.tar.gz
coinos-server-12ad5c9abb74cbe35c5d07233fdd9a7b7f5c2b70.zip
reconcile on-chain balance
-rwxr-xr-xbun.lockbbin208922 -> 208929 bytes
-rw-r--r--lib/invoices.ts5
-rw-r--r--lib/migrate.ts21
-rw-r--r--lib/payments.ts34
-rw-r--r--package.json2
-rw-r--r--routes/users.ts29
6 files changed, 47 insertions, 44 deletions
diff --git a/bun.lockb b/bun.lockb
index a8e58556..bb810531 100755
--- a/bun.lockb
+++ b/bun.lockb
Binary files differ
diff --git a/lib/invoices.ts b/lib/invoices.ts
index 4626e257..60bcba0f 100644
--- a/lib/invoices.ts
+++ b/lib/invoices.ts
@@ -30,13 +30,16 @@ export let generate = async ({ invoice, user }) => {
secret,
} = invoice;
- let account = await g(`account:${aid}`);
amount = parseInt(amount || 0);
tip = parseInt(tip) || null;
if (user) user = await getUser(user.username);
if (!user) fail("user not provided");
+ let account = await g(`account:${aid}`);
+ if (!account) account = await g(`account:${user.id}`);
+ if (!account) fail("account not found");
+
let rates = await g("rates");
if (!currency) currency = user.currency;
if (!rate) rate = rates[currency];
diff --git a/lib/migrate.ts b/lib/migrate.ts
index d26c8581..1ff280ea 100644
--- a/lib/migrate.ts
+++ b/lib/migrate.ts
@@ -12,26 +12,7 @@ async function migrate(id) {
let user = await g(`user:${id}`);
if (typeof user === "string") user = await g(`user:${user}`);
-
- if (user) {
- let k = `lock:${user.id}:accounts`;
- const lock = await db.set(k, "1", { NX: true, PX: 5000 });
- if (!lock) return user;
- try {
- let accounts = await db.lLen(`${user.id}:accounts`);
-
- if (!accounts) {
- await db.set(
- `account:${user.id}`,
- JSON.stringify({ id: user.id, name: "Cash", type: types.ecash }),
- );
- await db.lPush(`${user.id}:accounts`, user.id);
- }
- return user;
- } finally {
- await db.del(k);
- }
- }
+ if (user) return user;
user = await ga(`user:${id}`);
diff --git a/lib/payments.ts b/lib/payments.ts
index e2b44ea9..4bec9a93 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -40,6 +40,7 @@ export let types = {
fund: "fund",
liquid: "liquid",
ecash: "ecash",
+ reconcile: "reconcile",
};
export let debit = async ({
@@ -140,8 +141,8 @@ export let debit = async ({
export let credit = async ({
hash,
amount,
- memo = undefined,
- ref = undefined,
+ memo = "",
+ ref = "",
type = types.internal,
aid = undefined,
}) => {
@@ -369,9 +370,7 @@ export let sendOnchain = async (params) => {
let { tx, type } = await decode(hex);
let node =
- aid === user.id
- ? rpc(config[type])
- : rpc({ ...config[type], wallet: aid });
+ aid === user.id ? rpc(config[type]) : rpc({ ...config[type], wallet: aid });
let { txid } = tx;
try {
@@ -428,8 +427,7 @@ export let sendOnchain = async (params) => {
} of tx.vout) {
total += sats(value);
let invoice = await g(`invoice:${address}`);
- if (invoice?.aid === aid)
- fail("Cannot send to internal address");
+ if (invoice?.aid === aid) fail("Cannot send to internal address");
if ((await node.getAddressInfo(address)).ismine) {
change += sats(value);
@@ -604,12 +602,18 @@ export let sendLightning = async ({
}
};
-export let sendInternal = async ({ amount, invoice = undefined, recipient, sender }) => {
- if (!invoice) invoice = await generate({
- invoice: { amount, type: "lightning" },
- user: recipient,
- sender,
- });
+export let sendInternal = async ({
+ amount,
+ invoice = undefined,
+ recipient,
+ sender,
+}) => {
+ if (!invoice)
+ invoice = await generate({
+ invoice: { amount, type: "lightning" },
+ user: recipient,
+ sender,
+ });
let { hash } = invoice;
let memo;
@@ -644,9 +648,7 @@ export let build = async ({
let type = await getAddressType(address);
if (!aid) aid = user.id;
let node =
- aid === user.id
- ? rpc(config[type])
- : rpc({ ...config[type], wallet: aid });
+ aid === user.id ? rpc(config[type]) : rpc({ ...config[type], wallet: aid });
amount = parseInt(amount);
if (amount < 0) fail("invalid amount");
diff --git a/package.json b/package.json
index e61ed771..50c6a0f4 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
},
"dependencies": {
"@aws-sdk/client-ses": "^3.449.0",
- "@cashu/cashu-ts": "github:cashubtc/cashu-ts#development",
+ "@cashu/cashu-ts": "^1.1.0",
"@coinos/rpc": "^0.0.6",
"@fastify/cors": "^8.3.0",
"@fastify/http-proxy": "^9.5.0",
diff --git a/routes/users.ts b/routes/users.ts
index 9746f36a..06e437dd 100644
--- a/routes/users.ts
+++ b/routes/users.ts
@@ -1,7 +1,7 @@
import { appendFile } from "node:fs/promises";
import { g, s, db } from "$lib/db";
import config from "$config";
-import { pick, bail, fail, getUser } from "$lib/utils";
+import { SATS, pick, bail, fail, getUser } from "$lib/utils";
import jwt from "jsonwebtoken";
import { authenticator } from "otplib";
import whitelist from "$lib/whitelist";
@@ -10,7 +10,7 @@ import { emit } from "$lib/sockets";
import register from "$lib/register";
import { requirePin } from "$lib/auth";
import { v4 } from "uuid";
-import { types } from "$lib/payments";
+import { credit, types } from "$lib/payments";
import { bech32 } from "bech32";
import { mail, templates } from "$lib/mail";
import upload from "$lib/upload";
@@ -613,11 +613,11 @@ export default {
async createAccount(req, res) {
try {
- let { fingerprint, pubkey, type, seed } = req.body;
+ let { fingerprint, pubkey, name, seed, type } = req.body;
let { user } = req;
let id = v4();
- let account = { id, seed, type };
+ let account = { id, name, seed, type };
let node = rpc(config[type]);
@@ -644,13 +644,30 @@ export default {
}
await node.importDescriptors(descriptors);
+ let { total_amount } = await node.scanTxOutSet("start", descriptors);
+ let amount = Math.round(total_amount * SATS);
+
+ if (amount) {
+ let hash = v4();
+ let inv = {
+ memo: "On-chain balance reconciliation",
+ type: types.reconcile,
+ hash,
+ amount,
+ uid: user.id,
+ aid: id,
+ };
+ await s(`invoice:${hash}`, inv);
+ let { id: pid } = await credit({ hash, amount, type: types.reconcile });
+ await db.lPush(`${id}:payments`, pid);
+ }
await s(`account:${id}`, account);
- await s(`balance:${id}`, 0);
+ await s(`balance:${id}`, amount);
await s(`pending:${id}`, 0);
await db.lPush(`${user.id}:accounts`, id);
- await appendFile("/bitcoin.conf", `wallet=${id}`);
+ await appendFile("/bitcoin.conf", `wallet=${id}\n`);
res.send(account);
} catch (e) {