aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-13 21:07:55 -0800
committerAdam Soltys <[email protected]>2026-02-13 21:08:04 -0800
commitc7c6ee7ba59467b8ddcfe8196b4f7586a864f3c0 (patch)
treefccdf94755fb1b91c1de65e9d18f2f94742ebe09
parentb2b9fe837db7308285909ec1b374546194405d00 (diff)
downloadcoinos-server-c7c6ee7ba59467b8ddcfe8196b4f7586a864f3c0.tar.gz
coinos-server-c7c6ee7ba59467b8ddcfe8196b4f7586a864f3c0.zip
account level currency and autowithdraw settings
-rw-r--r--index.ts3
-rw-r--r--lib/invoices.ts5
-rw-r--r--lib/migrate.ts40
-rw-r--r--lib/payments.ts26
-rw-r--r--routes/users.ts10
5 files changed, 73 insertions, 11 deletions
diff --git a/index.ts b/index.ts
index 507e62f4..9e7beefd 100644
--- a/index.ts
+++ b/index.ts
@@ -4,7 +4,7 @@ import { admin, auth, optional } from "$lib/auth";
import { fixBolt12, listenForLightning, replay } from "$lib/lightning";
import { startHealthCheck } from "$lib/health";
import { getLocations } from "$lib/locations";
-import { migrateAccounts, migrateBalancesToTB, migrateToMicrosats } from "$lib/migrate";
+import { migrateAccounts, migrateAutowithdraw, migrateBalancesToTB, migrateToMicrosats } from "$lib/migrate";
import nwc from "$lib/nwc";
import { catchUp, check } from "$lib/payments";
import { getFx } from "$lib/rates";
@@ -38,6 +38,7 @@ try {
migrateAccounts().then((n) => n && console.log(`Migrated ${n} accounts`));
migrateBalancesToTB().then((n) => n && console.log(`Migrated ${n} balances to TB`));
migrateToMicrosats().then((n) => n && console.log(`Migrated ${n} users to microsats`));
+ migrateAutowithdraw().then((n) => n && console.log(`Migrated autowithdraw to ${n} accounts`));
} catch (e) {
console.log(e);
}
diff --git a/lib/invoices.ts b/lib/invoices.ts
index e6b6e146..bf6c511e 100644
--- a/lib/invoices.ts
+++ b/lib/invoices.ts
@@ -52,7 +52,10 @@ export const generate = async ({ invoice, user }) => {
aid = account.id;
const rates = await g("rates");
- if (!currency) currency = user.currency;
+ if (!currency) {
+ const acct = aid ? await g(`account:${aid}`) : null;
+ currency = acct?.currency || user.currency;
+ }
if (!rate) rate = rates[currency];
if (fiat) amount = Math.round((SATS * fiat) / rate);
if (amount < 0) fail("invalid amount");
diff --git a/lib/migrate.ts b/lib/migrate.ts
index 4b64bee5..d0e38276 100644
--- a/lib/migrate.ts
+++ b/lib/migrate.ts
@@ -136,4 +136,44 @@ export const migrateToMicrosats = async () => {
return count;
};
+export const migrateAutowithdraw = async () => {
+ const migrated = await db.get("autowithdraw:migrated");
+ if (migrated) return 0;
+
+ let count = 0;
+
+ for await (const k of db.scanIterator({ MATCH: "user:*" })) {
+ try {
+ const raw = await db.get(k);
+ if (!raw || !raw.startsWith("{")) continue;
+
+ const user = JSON.parse(raw);
+ if (!user.id || !user.autowithdraw) continue;
+
+ const accountIds = await db.lRange(`${user.id}:accounts`, 0, -1);
+
+ for (const aid of accountIds) {
+ const account = await g(`account:${aid}`);
+ if (!account) continue;
+ if (account.seed || account.type === "ark") continue;
+ if (account.autowithdraw) continue;
+
+ account.autowithdraw = true;
+ account.threshold = account.threshold ?? user.threshold;
+ account.reserve = account.reserve ?? user.reserve;
+ account.destination = account.destination ?? user.destination;
+
+ await s(`account:${aid}`, account);
+ count++;
+ }
+ } catch (e) {
+ warn("autowithdraw migration error", k, e.message);
+ }
+ }
+
+ await db.set("autowithdraw:migrated", Date.now().toString());
+ l(`Migrated autowithdraw to ${count} accounts`);
+ return count;
+};
+
export default migrate;
diff --git a/lib/payments.ts b/lib/payments.ts
index 3b6a4a86..e1254bad 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -166,8 +166,10 @@ export const debit = async ({
}
let ref;
- const { id: uid, currency } = user;
+ const { id: uid } = user;
if (!aid) aid = uid;
+ const debitAccount = aid && aid !== uid ? await g(`account:${aid}`) : null;
+ const currency = debitAccount?.currency || user.currency;
const rates = await g("rates");
if (!rate) rate = rates[currency];
@@ -293,7 +295,9 @@ export const credit = async ({
if (type === PaymentType.internal) amount += tip;
const user = await getUser(inv.uid);
- const { id: uid, currency } = user;
+ const { id: uid } = user;
+ const creditAccount = aid && aid !== uid ? await g(`account:${aid}`) : null;
+ const currency = creditAccount?.currency || user.currency;
const rates = await g("rates");
let rate = rates[currency];
@@ -373,7 +377,14 @@ export const credit = async ({
};
export const completePayment = async (inv, p, user) => {
- const { id, autowithdraw, threshold, reserve, destination, username } = user;
+ const { id, username } = user;
+ const aid = p.aid || id;
+ const account = aid !== id ? await g(`account:${aid}`) : null;
+ const autowithdraw = account?.autowithdraw;
+ const threshold = account?.threshold ?? user.threshold;
+ const reserve = account?.reserve ?? user.reserve;
+ const destination = account?.destination ?? user.destination;
+
let withdrawal;
if (p.confirmed) {
if (inv.forward && !inv.forwarded) {
@@ -399,11 +410,11 @@ export const completePayment = async (inv, p, user) => {
} else if (autowithdraw && p.type !== "ark") {
try {
const to = destination.trim();
- const balance = await getBalance(id);
+ const balance = await getBalance(aid);
const amount = balance - reserve;
if (balance > threshold) {
l("initiating autowithdrawal", amount, to, balance, threshold);
- const w = await pay({ amount, to, user });
+ const w = await pay({ aid, amount, to, user });
withdrawal = {
amount: fmt(-w.amount),
link: link(w.id),
@@ -1239,9 +1250,10 @@ export const importAccountHistory = async (account) => {
const total = confirmedUtxos.reduce((sum, u) => sum + u.value, 0);
const rates = await g("rates");
- let rate = rates[user.currency];
+ const currency = account.currency || user.currency;
+ let rate = rates[currency];
if (!rate) await sleep(1000);
- rate = rate || rates[user.currency];
+ rate = rate || rates[currency];
for (const u of confirmedUtxos) {
const existing = await getPayment(`${u.txid}:${u.vout}`);
diff --git a/routes/users.ts b/routes/users.ts
index 89920748..d1df6e5d 100644
--- a/routes/users.ts
+++ b/routes/users.ts
@@ -954,13 +954,19 @@ export default {
async updateAccount(req, res) {
const { id } = req.params;
const { id: uid } = req.user;
- const { name } = req.body;
+ const { name, autowithdraw, threshold, reserve, destination, currency } =
+ req.body;
const pos = await db.lPos(`${uid}:accounts`, id);
if (pos == null) fail("account not found");
const account = await g(`account:${id}`);
- account.name = name;
+ if (name !== undefined) account.name = name;
+ if (autowithdraw !== undefined) account.autowithdraw = autowithdraw;
+ if (threshold !== undefined) account.threshold = threshold;
+ if (reserve !== undefined) account.reserve = reserve;
+ if (destination !== undefined) account.destination = destination.trim();
+ if (currency !== undefined) account.currency = currency;
await s(`account:${id}`, account);
res.send(account);