aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-24 13:18:42 -0800
committerAdam Soltys <[email protected]>2026-02-24 13:18:42 -0800
commitad7b09b43323d0ffc0a8ddabb43e4838a184c697 (patch)
treefbf2a59f81771b2938f474c8e7b35a497d2960e4 /lib
parent9e97e040617bfa0b40150c0eba298bf186bbeeaf (diff)
downloadcoinos-server-ad7b09b43323d0ffc0a8ddabb43e4838a184c697.tar.gz
coinos-server-ad7b09b43323d0ffc0a8ddabb43e4838a184c697.zip
formatting
Diffstat (limited to 'lib')
-rw-r--r--lib/app.ts4
-rw-r--r--lib/ark.ts29
-rw-r--r--lib/auth.ts3
-rw-r--r--lib/ecash.ts1
-rw-r--r--lib/esplora.ts9
-rw-r--r--lib/health.ts29
-rw-r--r--lib/lightning.ts16
-rw-r--r--lib/ln.ts4
-rw-r--r--lib/locales/index.ts8
-rw-r--r--lib/locations.ts5
-rw-r--r--lib/migrate.ts5
-rw-r--r--lib/nostr.ts27
-rw-r--r--lib/notes.ts47
-rw-r--r--lib/notifications.ts16
-rw-r--r--lib/nwc.ts126
-rw-r--r--lib/passkey.ts8
-rw-r--r--lib/payments.ts171
-rw-r--r--lib/rates.ts4
-rw-r--r--lib/register.ts7
-rw-r--r--lib/sockets.ts13
-rw-r--r--lib/tb.ts40
-rw-r--r--lib/upload.ts9
-rw-r--r--lib/utils.ts17
-rw-r--r--lib/zmq.ts39
24 files changed, 184 insertions, 453 deletions
diff --git a/lib/app.ts b/lib/app.ts
index b8bcb610..da29d9fe 100644
--- a/lib/app.ts
+++ b/lib/app.ts
@@ -149,8 +149,6 @@ app.setErrorHandler((error, _, reply) => {
}
});
-app.setNotFoundHandler((_, reply) =>
- reply.code(404).type("text/html").send("Not Found"),
-);
+app.setNotFoundHandler((_, reply) => reply.code(404).type("text/html").send("Not Found"));
export default app;
diff --git a/lib/ark.ts b/lib/ark.ts
index ad708b7f..6558a74a 100644
--- a/lib/ark.ts
+++ b/lib/ark.ts
@@ -2,13 +2,7 @@ import { EventSource } from "eventsource";
if (!globalThis.EventSource) globalThis.EventSource = EventSource;
import config from "$config";
-import {
- SingleKey,
- Wallet,
- Ramps,
- RestArkProvider,
- VtxoManager,
-} from "@arkade-os/sdk";
+import { SingleKey, Wallet, Ramps, RestArkProvider, VtxoManager } from "@arkade-os/sdk";
import { l, warn } from "$lib/logging";
let wallet: any;
@@ -31,9 +25,7 @@ const failedBoardingOutpoints = new Set<string>();
const withTimeout = <T>(promise: Promise<T>, ms: number, label: string) =>
Promise.race([
promise,
- new Promise<T>((_, reject) =>
- setTimeout(() => reject(new Error(`${label} timed out`)), ms),
- ),
+ new Promise<T>((_, reject) => setTimeout(() => reject(new Error(`${label} timed out`)), ms)),
]);
export const refreshArkWallet = async (force = false) => {
@@ -58,11 +50,7 @@ export const refreshArkWallet = async (force = false) => {
// Recover swept/expired VTXOs
if (balance.recoverable > 0) {
try {
- const txid = await withTimeout(
- manager.recoverVtxos(),
- 60_000,
- "ark recovery",
- );
+ const txid = await withTimeout(manager.recoverVtxos(), 60_000, "ark recovery");
l("ark recovered swept vtxos, txid:", txid);
} catch (e: any) {
warn("ark vtxo recovery failed:", e.message);
@@ -74,11 +62,7 @@ export const refreshArkWallet = async (force = false) => {
const expiring = await manager.getExpiringVtxos();
if (expiring.length > 0) {
l("ark renewing", expiring.length, "expiring vtxos");
- const txid = await withTimeout(
- manager.renewVtxos(),
- 60_000,
- "ark renewal",
- );
+ const txid = await withTimeout(manager.renewVtxos(), 60_000, "ark renewal");
l("ark renewed vtxos, txid:", txid);
}
} catch (e: any) {
@@ -98,10 +82,7 @@ export const refreshArkWallet = async (force = false) => {
const outpointKey = (u: any) => `${u.txid}:${u.vout}`;
const sorted = [...confirmed]
.filter((u: any) => !failedBoardingOutpoints.has(outpointKey(u)))
- .sort(
- (a: any, b: any) =>
- (b.status?.block_height || 0) - (a.status?.block_height || 0),
- );
+ .sort((a: any, b: any) => (b.status?.block_height || 0) - (a.status?.block_height || 0));
for (const utxo of sorted) {
try {
diff --git a/lib/auth.ts b/lib/auth.ts
index 79c04e9d..c2eca6d4 100644
--- a/lib/auth.ts
+++ b/lib/auth.ts
@@ -50,8 +50,7 @@ export const jwtStrategy = new jwt.Strategy(
let { id } = payload;
const wl = { GET: ["/invoice", "/payments"], POST: ["/invoice"] };
- if (id.endsWith("-ro") && wl[m].some((p) => u.startsWith(p)))
- id = id.slice(0, -3);
+ if (id.endsWith("-ro") && wl[m].some((p) => u.startsWith(p))) id = id.slice(0, -3);
const user = await getUser(id);
next(null, user);
diff --git a/lib/ecash.ts b/lib/ecash.ts
index bcd2b7f4..5a5776b5 100644
--- a/lib/ecash.ts
+++ b/lib/ecash.ts
@@ -13,7 +13,6 @@ import {
getEncodedTokenV4,
} from "@cashu/cashu-ts";
-
const { URL } = process.env;
const m = new CashuMint(config.mintUrl);
const w = new CashuWallet(m);
diff --git a/lib/esplora.ts b/lib/esplora.ts
index 4fd81b32..d92fad0c 100644
--- a/lib/esplora.ts
+++ b/lib/esplora.ts
@@ -91,8 +91,7 @@ export const getTxHex = async (txid: string) => {
// Address derivation
const hdVersionsForKey = (pubkey: string) => {
- if (pubkey.startsWith("tpub") || pubkey.startsWith("tprv"))
- return REGTEST_VERSIONS;
+ if (pubkey.startsWith("tpub") || pubkey.startsWith("tprv")) return REGTEST_VERSIONS;
// xpub/xprv use default BITCOIN_VERSIONS (mainnet) — pass undefined
return undefined;
};
@@ -136,11 +135,7 @@ export const parseDescriptor = (desc: string) => {
return { fingerprint: match[1], pubkey: match[2] };
};
-export const findLastUsedIndex = async (
- pubkey: string,
- fingerprint: string,
- maxScan = 100,
-) => {
+export const findLastUsedIndex = async (pubkey: string, fingerprint: string, maxScan = 100) => {
let lastUsed = -1;
for (let i = 0; i < maxScan; i++) {
const { address } = deriveAddress(pubkey, fingerprint, i, false);
diff --git a/lib/health.ts b/lib/health.ts
index c2e268c6..fbefa8fa 100644
--- a/lib/health.ts
+++ b/lib/health.ts
@@ -22,13 +22,16 @@ export function getHealthStatus() {
function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {
return new Promise((resolve, reject) => {
- const timer = setTimeout(
- () => reject(new Error(`${label} timed out after ${ms}ms`)),
- ms,
- );
+ const timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
promise.then(
- (v) => { clearTimeout(timer); resolve(v); },
- (e) => { clearTimeout(timer); reject(e); },
+ (v) => {
+ clearTimeout(timer);
+ resolve(v);
+ },
+ (e) => {
+ clearTimeout(timer);
+ reject(e);
+ },
);
});
}
@@ -79,11 +82,7 @@ async function checkLightningHealth(): Promise<boolean> {
const errorCode = e?.code ?? e?.errno ?? "unknown";
const errorMsg = e?.message ?? String(e);
- err(
- `health check: FAILED after ${duration}ms`,
- `code=${errorCode}`,
- `error=${errorMsg}`,
- );
+ err(`health check: FAILED after ${duration}ms`, `code=${errorCode}`, `error=${errorMsg}`);
if (e instanceof LightningUnavailableError) {
err("health check: lightning RPC socket unavailable");
@@ -102,18 +101,14 @@ export async function runHealthCheck() {
if (healthy) {
if (consecutiveFailures > 0) {
- l(
- `health check: recovered after ${consecutiveFailures} consecutive failures`,
- );
+ l(`health check: recovered after ${consecutiveFailures} consecutive failures`);
}
consecutiveFailures = 0;
lastSuccessTime = Date.now();
lastFailureReason = "";
} else {
consecutiveFailures++;
- warn(
- `health check: failure ${consecutiveFailures}/${MAX_CONSECUTIVE_FAILURES}`,
- );
+ warn(`health check: failure ${consecutiveFailures}/${MAX_CONSECUTIVE_FAILURES}`);
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
err(
diff --git a/lib/lightning.ts b/lib/lightning.ts
index 4824d9ab..a259b601 100644
--- a/lib/lightning.ts
+++ b/lib/lightning.ts
@@ -66,8 +66,7 @@ export async function listenForLightning() {
handleZap(inv, pubkey);
}
} catch (e) {
- if (!e.message.includes("Unexpected"))
- warn("failed to handle zap", e.message);
+ if (!e.message.includes("Unexpected")) warn("failed to handle zap", e.message);
}
}
@@ -88,11 +87,7 @@ export async function listenForLightning() {
const errorCode = e?.code ?? e?.errno ?? "unknown";
const errorMsg = e?.message ?? String(e);
- err(
- `lightning listener: error waiting for invoice`,
- `code=${errorCode}`,
- `error=${errorMsg}`
- );
+ err(`lightning listener: error waiting for invoice`, `code=${errorCode}`, `error=${errorMsg}`);
if (e instanceof LightningUnavailableError) {
err("lightning listener: RPC socket unavailable");
@@ -103,7 +98,7 @@ export async function listenForLightning() {
if (listenerRetries >= MAX_LISTENER_RETRIES) {
err(
`lightning listener: ${MAX_LISTENER_RETRIES} consecutive failures, ` +
- `last error: ${errorMsg}`
+ `last error: ${errorMsg}`,
);
err("lightning listener: exiting process to trigger container restart");
@@ -115,7 +110,7 @@ export async function listenForLightning() {
warn(
`lightning listener: retry ${listenerRetries}/${MAX_LISTENER_RETRIES} ` +
- `in ${LISTENER_RETRY_DELAY / 1000}s`
+ `in ${LISTENER_RETRY_DELAY / 1000}s`,
);
setTimeout(listenForLightning, LISTENER_RETRY_DELAY);
}
@@ -150,8 +145,7 @@ export async function replay(index) {
handleZap(inv, pubkey);
}
} catch (e) {
- if (!e.message.includes("Unexpected"))
- warn("failed to handle zap", e.message);
+ if (!e.message.includes("Unexpected")) warn("failed to handle zap", e.message);
}
}
diff --git a/lib/ln.ts b/lib/ln.ts
index 0ef99fc5..b4bd148d 100644
--- a/lib/ln.ts
+++ b/lib/ln.ts
@@ -47,7 +47,7 @@ function lightningProxy(rpcPath: string) {
if (isUnavailable(e)) {
throw new LightningUnavailableError(
- `Lightning RPC unavailable at ${rpcPath} (${e?.code ?? e?.errno ?? "error"})`
+ `Lightning RPC unavailable at ${rpcPath} (${e?.code ?? e?.errno ?? "error"})`,
);
}
throw e;
@@ -75,7 +75,7 @@ function lightningProxy(rpcPath: string) {
}
};
},
- }
+ },
);
}
diff --git a/lib/locales/index.ts b/lib/locales/index.ts
index c3aaa7c8..c902a8d2 100644
--- a/lib/locales/index.ts
+++ b/lib/locales/index.ts
@@ -1,5 +1,7 @@
import fs from "fs";
-export default "ar,bn,de,el,en,es,fa,fr,hi,it,ja,ko,nl,pl,pt,ru,th,tr,zh".split(",").reduce((a, l) => {
- a[l] = JSON.parse(fs.readFileSync(`lib/locales/${l}.json`));
+export default "ar,bn,de,el,en,es,fa,fr,hi,it,ja,ko,nl,pl,pt,ru,th,tr,zh"
+ .split(",")
+ .reduce((a, l) => {
+ a[l] = JSON.parse(fs.readFileSync(`lib/locales/${l}.json`));
return a;
-}, {});
+ }, {});
diff --git a/lib/locations.ts b/lib/locations.ts
index 660645a8..91bc4599 100644
--- a/lib/locations.ts
+++ b/lib/locations.ts
@@ -6,10 +6,7 @@ import got from "got";
const dedup = (array) =>
Object.values(
array.reduce((acc, obj) => {
- if (
- !acc[obj.id] ||
- new Date(obj.updated_at) > new Date(acc[obj.id].updated_at)
- ) {
+ if (!acc[obj.id] || new Date(obj.updated_at) > new Date(acc[obj.id].updated_at)) {
acc[obj.id] = obj;
}
return acc;
diff --git a/lib/migrate.ts b/lib/migrate.ts
index d4bfd61a..e3cfa9bc 100644
--- a/lib/migrate.ts
+++ b/lib/migrate.ts
@@ -40,10 +40,7 @@ export const migrateAccounts = async () => {
account.pubkey = parsed.pubkey;
account.fingerprint = parsed.fingerprint;
try {
- account.nextIndex = await findLastUsedIndex(
- parsed.pubkey,
- parsed.fingerprint,
- );
+ account.nextIndex = await findLastUsedIndex(parsed.pubkey, parsed.fingerprint);
} catch (e) {
warn("account migration index scan skipped", key, e.message);
account.nextIndex = account.nextIndex || 0;
diff --git a/lib/nostr.ts b/lib/nostr.ts
index 354f928b..bcb20ea1 100644
--- a/lib/nostr.ts
+++ b/lib/nostr.ts
@@ -12,13 +12,9 @@ import { Relay } from "nostr-tools/relay";
export const EX = 60 * 60 * 24;
const coinos = await Relay.connect(config.nostr);
-export const serverSecret = bytesToHex(
- nip19.decode(config.nostrKey).data as Uint8Array,
-);
+export const serverSecret = bytesToHex(nip19.decode(config.nostrKey).data as Uint8Array);
-export const serverSecret2 = bytesToHex(
- nip19.decode(config.nostrKey2).data as Uint8Array,
-);
+export const serverSecret2 = bytesToHex(nip19.decode(config.nostrKey2).data as Uint8Array);
export const serverPubkey = getPublicKey(hexToBytes(serverSecret));
export const serverPubkey2 = getPublicKey(hexToBytes(serverSecret2));
@@ -54,29 +50,21 @@ export async function handleZap(invoice, sender = undefined) {
fail("No tags found");
}
- const ptags = zapreq.tags.filter(
- (t) => t?.length && t.length >= 2 && t[0] === "p",
- );
+ const ptags = zapreq.tags.filter((t) => t?.length && t.length >= 2 && t[0] === "p");
if (ptags.length !== 1) {
fail("None or multiple p tags found");
}
- const etags = zapreq.tags.filter(
- (t) => t?.length && t.length >= 2 && t[0] === "e",
- );
+ const etags = zapreq.tags.filter((t) => t?.length && t.length >= 2 && t[0] === "e");
if (!(etags.length === 0 || etags.length === 1)) {
fail("Expected none or 1 e tags");
}
- const atags = zapreq.tags.filter(
- (t) => t?.length && t.length >= 2 && t[0] === "a",
- );
+ const atags = zapreq.tags.filter((t) => t?.length && t.length >= 2 && t[0] === "a");
- const relays_tag = zapreq.tags.find(
- (t) => t?.length && t.length >= 2 && t[0] === "relays",
- );
+ const relays_tag = zapreq.tags.find((t) => t?.length && t.length >= 2 && t[0] === "relays");
if (!relays_tag) {
fail("No relays tag found");
@@ -168,8 +156,7 @@ export const getCount = async (pubkey) => {
if (follows === null) {
const result = await get({ authors: [pubkey], kinds: [3] });
follows = result ? result.tags.filter((t) => t[0] === "p").length : 0;
- if (follows?.length)
- await db.set(`${pubkey}:follows:n`, JSON.stringify(follows), { EX });
+ if (follows?.length) await db.set(`${pubkey}:follows:n`, JSON.stringify(follows), { EX });
}
const k = `${pubkey}:followers:n`;
diff --git a/lib/notes.ts b/lib/notes.ts
index 84d157ee..6e8e6afc 100644
--- a/lib/notes.ts
+++ b/lib/notes.ts
@@ -20,8 +20,7 @@ const first = (list: any) => (list ? list[0] : undefined);
export const fromNostrURI = (s: string) => s.replace(/^[\w+]+:\/?\/?/, "");
export const urlIsMedia = (url: string) =>
- !url.match(/\.(apk|docx|xlsx|csv|dmg)/) &&
- last(url.split("://"))?.includes("/");
+ !url.match(/\.(apk|docx|xlsx|csv|dmg)/) && last(url.split("://"))?.includes("/");
type ContentArgs = {
content: string;
@@ -35,13 +34,10 @@ export type ParsedPart = {
value: any;
};
-export const isEmbeddableMedia = (url: string) =>
- isImage(url) || isVideo(url) || isAudio(url);
+export const isEmbeddableMedia = (url: string) => isImage(url) || isVideo(url) || isAudio(url);
-export const isImage = (url: string) =>
- url?.match(/^.*\.(jpg|jpeg|png|webp|gif|avif|svg)/gi);
-export const isVideo = (url: string) =>
- url?.match(/^.*\.(mov|mkv|mp4|avi|m4v|webm)/gi);
+export const isImage = (url: string) => url?.match(/^.*\.(jpg|jpeg|png|webp|gif|avif|svg)/gi);
+export const isVideo = (url: string) => url?.match(/^.*\.(mov|mkv|mp4|avi|m4v|webm)/gi);
export const isAudio = (url: string) => url?.match(/^.*\.(ogg|mp3|wav)/gi);
/**
@@ -70,9 +66,7 @@ export function groupContent(parts: ParsedPart[]): ParsedPart[] {
parts.forEach((part, index) => {
if (
part.type === LINK &&
- (isImage(part.value.url) ||
- isVideo(part.value.url) ||
- isAudio(part.value.url))
+ (isImage(part.value.url) || isVideo(part.value.url) || isAudio(part.value.url))
) {
if (!buffer) {
buffer = {
@@ -87,8 +81,7 @@ export function groupContent(parts: ParsedPart[]): ParsedPart[] {
for (const nextPart of parts.slice(index + 1)) {
const isNewline = nextPart.type === NEWLINE;
- const isBlankText =
- nextPart.type === TEXT && nextPart.value.trim() === "";
+ const isBlankText = nextPart.type === TEXT && nextPart.value.trim() === "";
const isLink = nextPart.type === LINK;
// This is a noop, keep checking the next part
@@ -114,11 +107,7 @@ export function groupContent(parts: ParsedPart[]): ParsedPart[] {
return result;
}
-export const parseContent = ({
- content,
- tags = [],
- html = false,
-}: ContentArgs): ParsedPart[] => {
+export const parseContent = ({ content, tags = [], html = false }: ContentArgs): ParsedPart[] => {
const result: ParsedPart[] = [];
let text = content.trim();
let buffer = "";
@@ -174,9 +163,7 @@ export const parseContent = ({
const parseBech32 = () => {
const bech32 = first(
- text.match(
- /^(web\+)?(nostr:)?\/?\/?n(event|ote|profile|pub|addr)1[\d\w]+/i,
- ),
+ text.match(/^(web\+)?(nostr:)?\/?\/?n(event|ote|profile|pub|addr)1[\d\w]+/i),
);
if (bech32) {
@@ -211,9 +198,7 @@ export const parseContent = ({
};
const parseUrl = () => {
- const raw = first(
- text.match(/^([a-z+:]{2,30}:\/\/)?[^\s]+\.[a-z]{2,6}[^\s]*[^.!?,:\s]/gi),
- );
+ const raw = first(text.match(/^([a-z+:]{2,30}:\/\/)?[^\s]+\.[a-z]{2,6}[^\s]*[^.!?,:\s]/gi));
// Skip url if it's just the end of a filepath
if (raw) {
@@ -290,10 +275,7 @@ export const parseContent = ({
return result;
};
-export const truncateContent = (
- content,
- { showEntire, maxLength, showMedia = false },
-) => {
+export const truncateContent = (content, { showEntire, maxLength, showMedia = false }) => {
if (showEntire) {
return content;
}
@@ -303,13 +285,8 @@ export const truncateContent = (
const truncateAt = maxLength * 0.6;
content.every((part, i) => {
- const isText =
- [TOPIC, TEXT].includes(part.type) ||
- (part.type === LINK && !part.value.isMedia);
- const isMedia =
- part.type === INVOICE ||
- part.type.startsWith("nostr:") ||
- part.value.isMedia;
+ const isText = [TOPIC, TEXT].includes(part.type) || (part.type === LINK && !part.value.isMedia);
+ const isMedia = part.type === INVOICE || part.type.startsWith("nostr:") || part.value.isMedia;
if (isText) {
length += part.value.length;
diff --git a/lib/notifications.ts b/lib/notifications.ts
index d1e43392..261ad4bb 100644
--- a/lib/notifications.ts
+++ b/lib/notifications.ts
@@ -12,11 +12,7 @@ import { finalizeEvent, nip04 } from "nostr-tools";
import webpush from "web-push";
if (config.vapid) {
- webpush.setVapidDetails(
- `mailto:${config.support}`,
- config.vapid.pk,
- config.vapid.sk,
- );
+ webpush.setVapidDetails(`mailto:${config.support}`, config.vapid.pk, config.vapid.sk);
}
export const notify = async (p, user, withdrawal) => {
@@ -62,12 +58,10 @@ export const notify = async (p, user, withdrawal) => {
};
for (const s of subscriptions) {
- webpush
- .sendNotification(JSON.parse(s), JSON.stringify(payload))
- .catch((e) => {
- warn("sub failed", e.message);
- db.sRem(`${user.id}:subscriptions`, s);
- });
+ webpush.sendNotification(JSON.parse(s), JSON.stringify(payload)).catch((e) => {
+ warn("sub failed", e.message);
+ db.sRem(`${user.id}:subscriptions`, s);
+ });
}
if (config.mqtt) {
diff --git a/lib/nwc.ts b/lib/nwc.ts
index 47b632f7..b635f665 100644
--- a/lib/nwc.ts
+++ b/lib/nwc.ts
@@ -3,13 +3,7 @@ import { archive, db, g, gf } from "$lib/db";
import { generate } from "$lib/invoices";
import ln from "$lib/ln";
import { err, l, warn } from "$lib/logging";
-import {
- handleZap,
- serverPubkey,
- serverPubkey2,
- serverSecret,
- serverSecret2,
-} from "$lib/nostr";
+import { handleZap, serverPubkey, serverPubkey2, serverSecret, serverSecret2 } from "$lib/nostr";
import { sendInternal, sendKeysend, sendLightning } from "$lib/payments";
import { getBalance } from "$lib/tb";
import { fail, getInvoice, sleep } from "$lib/utils";
@@ -89,64 +83,62 @@ export default () => {
r.on("error", () => {});
r.on("event", async (sub, ev) => {
- try {
- if (sub !== "nwc") return;
- const now = Math.floor(Date.now() / 1000);
- if (ev.created_at && now - ev.created_at > nwcEventMaxAgeSeconds) return;
- if (await db.zScore(handledKey, ev.id)) return;
+ try {
+ if (sub !== "nwc") return;
+ const now = Math.floor(Date.now() / 1000);
+ if (ev.created_at && now - ev.created_at > nwcEventMaxAgeSeconds) return;
+ if (await db.zScore(handledKey, ev.id)) return;
- db.zAdd(handledKey, { score: now, value: ev.id });
- db.zRemRangeByScore(handledKey, 0, now - nwcEventMaxAgeSeconds);
- const size = await db.zCard(handledKey);
- if (size > handledMaxSize) {
- await db.zRemRangeByRank(handledKey, 0, size - handledMaxSize - 1);
- }
- let { content, pubkey } = ev;
- const pk = ev.tags.find((t) => t[0] === "p")[1];
- const sk = serverKeys[pk];
- const { params, method } = JSON.parse(
- await nip04.decrypt(sk, pubkey, content),
- );
+ db.zAdd(handledKey, { score: now, value: ev.id });
+ db.zRemRangeByScore(handledKey, 0, now - nwcEventMaxAgeSeconds);
+ const size = await db.zCard(handledKey);
+ if (size > handledMaxSize) {
+ await db.zRemRangeByRank(handledKey, 0, size - handledMaxSize - 1);
+ }
+ let { content, pubkey } = ev;
+ const pk = ev.tags.find((t) => t[0] === "p")[1];
+ const sk = serverKeys[pk];
+ const { params, method } = JSON.parse(await nip04.decrypt(sk, pubkey, content));
- // console.log("nwc", method, params, pubkey);
+ // console.log("nwc", method, params, pubkey);
- if (!methods.includes(method)) return;
+ if (!methods.includes(method)) return;
- try {
- const app = await g(`app:${pubkey}`);
- if (!app) fail("pubkey not found");
- const user = await g(`user:${app.uid}`);
+ try {
+ const app = await g(`app:${pubkey}`);
+ if (!app) fail("pubkey not found");
+ const user = await g(`user:${app.uid}`);
- const result = await handle(method, params, ev, app, user);
- const payload = JSON.stringify({ result_type: method, ...result });
- content = await nip04.encrypt(sk, pubkey, payload);
+ const result = await handle(method, params, ev, app, user);
+ const payload = JSON.stringify({ result_type: method, ...result });
+ content = await nip04.encrypt(sk, pubkey, payload);
- let response: UnsignedEvent = {
- created_at: Math.floor(Date.now() / 1000),
- kind: 23195,
- pubkey: serverPubkey,
- tags: [
- ["p", pubkey],
- ["e", ev.id],
- ],
- content,
- };
+ let response: UnsignedEvent = {
+ created_at: Math.floor(Date.now() / 1000),
+ kind: 23195,
+ pubkey: serverPubkey,
+ tags: [
+ ["p", pubkey],
+ ["e", ev.id],
+ ],
+ content,
+ };
- response = await finalizeEvent(response, hexToBytes(sk));
- r.send(["EVENT", response]);
+ response = await finalizeEvent(response, hexToBytes(sk));
+ r.send(["EVENT", response]);
+ } catch (e) {
+ // err(
+ // "problem with nwc",
+ // pubkey,
+ // method,
+ // JSON.stringify(params),
+ // e.message,
+ // );
+ }
} catch (e) {
- // err(
- // "problem with nwc",
- // pubkey,
- // method,
- // JSON.stringify(params),
- // e.message,
- // );
+ // err("problem with nwc", e.message);
}
- } catch (e) {
- // err("problem with nwc", e.message);
- }
- });
+ });
}
connect();
@@ -171,9 +163,7 @@ const handle = (method, params, ev, app, user) =>
const pids = await db.lRange(`${pubkey}:payments`, 0, -1);
let payments = await Promise.all(pids.map((pid) => g(`payment:${pid}`)));
- payments = payments.filter(
- (p) => p?.created > Date.now() - periods[budget_renewal],
- );
+ payments = payments.filter((p) => p?.created > Date.now() - periods[budget_renewal]);
const spent = payments.reduce(
(a, b) =>
@@ -186,14 +176,7 @@ const handle = (method, params, ev, app, user) =>
);
if (!created) {
- warn(
- "old nwc token",
- pubkey,
- user?.username,
- spent,
- amount,
- max_amount,
- );
+ warn("old nwc token", pubkey, user?.username, spent, amount, max_amount);
return error({
code: "UNAUTHORIZED",
message: `This NWC connection is no longer valid please create a new one at https://coinos.io/settings/nostr`,
@@ -344,11 +327,7 @@ const handle = (method, params, ev, app, user) =>
expiry,
};
- const {
- hash,
- created: created_at,
- paymentHash,
- } = await generate({ invoice, user });
+ const { hash, created: created_at, paymentHash } = await generate({ invoice, user });
return result({
type: "incoming",
@@ -401,8 +380,7 @@ const handle = (method, params, ev, app, user) =>
const { pays } = await ln.listpays({ bolt11: invoice, payment_hash });
- if (!pays.length)
- return error({ code: "NOT_FOUND", message: "Invoice not found" });
+ if (!pays.length) return error({ code: "NOT_FOUND", message: "Invoice not found" });
const {
amount_msat: amount,
diff --git a/lib/passkey.ts b/lib/passkey.ts
index b2cb96b4..29b9b095 100644
--- a/lib/passkey.ts
+++ b/lib/passkey.ts
@@ -4,9 +4,7 @@ import {
generateAuthenticationOptions,
verifyAuthenticationResponse,
} from "@simplewebauthn/server";
-import type {
- AuthenticatorTransportFuture,
-} from "@simplewebauthn/types";
+import type { AuthenticatorTransportFuture } from "@simplewebauthn/types";
import { db, g, s } from "$lib/db";
import { fail, getUser } from "$lib/utils";
import { v4 } from "uuid";
@@ -102,9 +100,7 @@ export async function verifyPasskeyLogin(response: any, challengeId: string, ori
if (!user) fail("User not found");
const passkeys = user.passkeys || [];
- const passkey = passkeys.find(
- (p: any) => p.credentialID === response.id,
- );
+ const passkey = passkeys.find((p: any) => p.credentialID === response.id);
if (!passkey) fail("Passkey not found");
const expectedChallenge = await db.get(`challenge:passkey:${challengeId}`);
diff --git a/lib/payments.ts b/lib/payments.ts
index 8fee1a26..841beac5 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -134,34 +134,16 @@ export const debit = async ({
}) => {
amount = Number.parseInt(amount);
- const whitelisted = await db.sIsMember(
- "whitelist",
- user?.username?.toLowerCase().trim(),
- );
+ const whitelisted = await db.sIsMember("whitelist", user?.username?.toLowerCase().trim());
- const blacklisted = await db.sIsMember(
- "blacklist",
- user?.username?.toLowerCase().trim(),
- );
+ const blacklisted = await db.sIsMember("blacklist", user?.username?.toLowerCase().trim());
const serverLimit = await g(`${type}:limit`);
const userLimit = await g("limit");
- const frozen =
- (await g("hardfreeze")) ||
- ((await g("freeze")) && type !== PaymentType.internal);
+ const frozen = (await g("hardfreeze")) || ((await g("freeze")) && type !== PaymentType.internal);
if (frozen || (amount > userLimit && !whitelisted) || amount > serverLimit) {
- warn(
- "Blocking",
- user.username,
- amount,
- hash,
- user.id,
- type,
- frozen,
- userLimit,
- serverLimit,
- );
+ warn("Blocking", user.username, amount, hash, user.id, type, frozen, userLimit, serverLimit);
fail("Problem sending payment");
}
@@ -184,8 +166,7 @@ export const debit = async ({
ref = invoice.uid;
- const equivalentRate =
- invoice.rate * (rates[currency] / rates[invoice.currency]);
+ const equivalentRate = invoice.rate * (rates[currency] / rates[invoice.currency]);
if (Math.abs(invoice.rate / rates[invoice.currency] - 1) < 0.01) {
rate = equivalentRate;
@@ -201,17 +182,12 @@ export const debit = async ({
let creditType = type;
if (creditType === PaymentType.bolt12) creditType = PaymentType.lightning;
- let ourfee: any = [
- PaymentType.bitcoin,
- PaymentType.liquid,
- PaymentType.lightning,
- ].includes(type)
+ let ourfee: any = [PaymentType.bitcoin, PaymentType.liquid, PaymentType.lightning].includes(type)
? Math.round((amount + fee + tip) * config.fee[creditType])
: 0;
if (aid !== uid) ourfee = 0;
- const frozenBalance =
- !blacklisted || whitelisted ? 0 : await getBalance(uid);
+ const frozenBalance = !blacklisted || whitelisted ? 0 : await getBalance(uid);
ourfee = await tbDebit(
aid,
@@ -334,8 +310,7 @@ export const credit = async ({
items: undefined,
};
- if ([PaymentType.bitcoin, PaymentType.liquid].includes(type))
- inv.pending += amount;
+ if ([PaymentType.bitcoin, PaymentType.liquid].includes(type)) inv.pending += amount;
else {
inv.received += amount;
inv.preimage = ref;
@@ -506,9 +481,7 @@ const pay = async ({ aid = undefined, amount, to, user }) => {
if (URL.includes(domain)) to = name;
lnurl = `https://${domain}/.well-known/lnurlp/${name}`;
} else if (to.startsWith("lnurl")) {
- lnurl = Buffer.from(
- bech32.fromWords(bech32.decode(to, 20000).words),
- ).toString();
+ lnurl = Buffer.from(bech32.fromWords(bech32.decode(to, 20000).words)).toString();
}
const recipient = await getUser(to);
@@ -552,8 +525,7 @@ const pay = async ({ aid = undefined, amount, to, user }) => {
const localInv = await getInvoice(to).catch(() => null);
if (localInv) {
const recipient = await getUser(localInv.uid);
- if (recipient)
- return sendInternal({ amount, invoice: localInv, recipient, sender: user });
+ if (recipient) return sendInternal({ amount, invoice: localInv, recipient, sender: user });
}
}
@@ -568,9 +540,12 @@ const pay = async ({ aid = undefined, amount, to, user }) => {
const { id: source } = await ln.getinfo();
const { offer_issuer_id } = await ln.decode(to);
const { routes } = await ln.getroutes(
- source, offer_issuer_id, amount * 1000,
+ source,
+ offer_issuer_id,
+ amount * 1000,
["auto.localchans", "auto.sourcefree"],
- Math.round(amount * 0.02) * 1000, 6,
+ Math.round(amount * 0.02) * 1000,
+ 6,
);
fee = routes.length
? Math.max(5, Math.round((routes[0].path[0].amount_msat - routes[0].amount_msat) / 1000))
@@ -627,12 +602,8 @@ const sendNonCustodial = async (params) => {
// Build set of own addresses for change detection
const ownAddresses = new Set<string>();
for (let i = 0; i <= nextIndex; i++) {
- ownAddresses.add(
- deriveAddress(account.pubkey, account.fingerprint, i, false).address,
- );
- ownAddresses.add(
- deriveAddress(account.pubkey, account.fingerprint, i, true).address,
- );
+ ownAddresses.add(deriveAddress(account.pubkey, account.fingerprint, i, false).address);
+ ownAddresses.add(deriveAddress(account.pubkey, account.fingerprint, i, true).address);
}
let totalIn = 0;
@@ -704,8 +675,7 @@ export const sendOnchain = async (params) => {
inflight[txid] = true;
if (!signed) {
- if (config[type].walletpass)
- await node.walletPassphrase(config[type].walletpass, 300);
+ if (config[type].walletpass) await node.walletPassphrase(config[type].walletpass, 300);
({ hex } = await node.signRawTransactionWithWallet(
type === PaymentType.liquid ? await node.blindRawTransaction(hex) : hex,
@@ -825,13 +795,7 @@ export const sendKeysend = async ({
}
};
-export const sendLightning = async ({
- user,
- pr,
- amount,
- fee = undefined,
- memo = undefined,
-}) => {
+export const sendLightning = async ({ user, pr, amount, fee = undefined, memo = undefined }) => {
let p;
if (typeof amount !== "undefined") {
@@ -842,8 +806,7 @@ export const sendLightning = async ({
}
}
- let { type, invoice_amount_msat, amount_msat, invoice_node_id, payee } =
- await ln.decode(pr);
+ let { type, invoice_amount_msat, amount_msat, invoice_node_id, payee } = await ln.decode(pr);
if (type.includes("bolt12")) {
amount_msat = invoice_amount_msat;
payee = invoice_node_id;
@@ -857,11 +820,9 @@ export const sendLightning = async ({
if (fee < 0) fail("Fee cannot be negative");
const { pays } = await ln.listpays(pr);
- if (pays.find((p) => p.status === "complete"))
- fail("Invoice has already been paid");
+ if (pays.find((p) => p.status === "complete")) fail("Invoice has already been paid");
- if (pays.find((p) => p.status === "pending"))
- fail("Payment is already underway");
+ if (pays.find((p) => p.status === "pending")) fail("Payment is already underway");
p = await debit({
hash: pr,
@@ -941,23 +902,16 @@ const getAddressType = async (a) => {
}
};
-const buildNonCustodial = async ({
- aid,
- amount,
- address,
- feeRate,
- subtract,
- user,
-}) => {
+const buildNonCustodial = async ({ aid, amount, address, feeRate, subtract, user }) => {
const account = await g(`account:${aid}`);
if (!account?.pubkey) fail("account missing pubkey");
amount = Number.parseInt(amount);
if (amount < 0) fail("invalid amount");
- const fees: any = await fetch(
- `${api[PaymentType.bitcoin]}/fees/recommended`,
- ).then((r) => r.json());
+ const fees: any = await fetch(`${api[PaymentType.bitcoin]}/fees/recommended`).then((r) =>
+ r.json(),
+ );
fees.hourFee = fees.halfHourFee;
fees.halfHourFee = fees.fastestFee;
@@ -969,18 +923,8 @@ const buildNonCustodial = async ({
const nextIndex = account.nextIndex || 0;
// Derive all used external + internal addresses and fetch UTXOs
- const externalAddrs = deriveAddresses(
- account.pubkey,
- account.fingerprint,
- nextIndex + 1,
- false,
- );
- const internalAddrs = deriveAddresses(
- account.pubkey,
- account.fingerprint,
- nextIndex + 1,
- true,
- );
+ const externalAddrs = deriveAddresses(account.pubkey, account.fingerprint, nextIndex + 1, false);
+ const internalAddrs = deriveAddresses(account.pubkey, account.fingerprint, nextIndex + 1, true);
const allAddrs = [...externalAddrs, ...internalAddrs];
const rawUtxos = await getAddressUtxos(allAddrs);
@@ -989,26 +933,14 @@ const buildNonCustodial = async ({
// Build address-to-path lookup
const addrToPath = {};
for (let i = 0; i <= nextIndex; i++) {
- const { address: extAddr } = deriveAddress(
- account.pubkey,
- account.fingerprint,
- i,
- false,
- );
+ const { address: extAddr } = deriveAddress(account.pubkey, account.fingerprint, i, false);
addrToPath[extAddr] = `m/0/${i}`;
- const { address: intAddr } = deriveAddress(
- account.pubkey,
- account.fingerprint,
- i,
- true,
- );
+ const { address: intAddr } = deriveAddress(account.pubkey, account.fingerprint, i, true);
addrToPath[intAddr] = `m/1/${i}`;
}
// Convert esplora UTXOs to selectUTXO input format
- const keyVersions = account.pubkey.startsWith("tpub")
- ? hdVersions
- : undefined;
+ const keyVersions = account.pubkey.startsWith("tpub") ? hdVersions : undefined;
const accountKey = HDKey.fromExtendedKey(account.pubkey, keyVersions);
const utxoInputs = rawUtxos.map((u) => {
@@ -1072,12 +1004,8 @@ const buildNonCustodial = async ({
// Build input metadata for client signing
const inputs = selected.inputs.map((input) => {
const inputTxid =
- typeof input.txid === "string"
- ? input.txid
- : Buffer.from(input.txid).toString("hex");
- const utxo = rawUtxos.find(
- (u) => u.txid === inputTxid && u.vout === input.index,
- );
+ typeof input.txid === "string" ? input.txid : Buffer.from(input.txid).toString("hex");
+ const utxo = rawUtxos.find((u) => u.txid === inputTxid && u.vout === input.index);
const path = utxo ? addrToPath[utxo.address] : undefined;
return {
witnessUtxo: {
@@ -1093,14 +1021,7 @@ const buildNonCustodial = async ({
return { feeRate, ourfee, fee, fees, hex, inputs, subtract };
};
-export const build = async ({
- aid,
- amount,
- address,
- feeRate,
- subtract,
- user,
-}) => {
+export const build = async ({ aid, amount, address, feeRate, subtract, user }) => {
const type = await getAddressType(address);
if (!aid) aid = user.id;
@@ -1138,8 +1059,7 @@ export const build = async ({
let outs = [{ [address]: btc(amount) }];
- if (type === PaymentType.liquid)
- outs = outs.map((o) => ({ ...o, asset: config.liquid.btc }));
+ if (type === PaymentType.liquid) outs = outs.map((o) => ({ ...o, asset: config.liquid.btc }));
let raw = await node.createRawTransaction([], outs, 0, replaceable);
@@ -1168,9 +1088,7 @@ export const build = async ({
if (subtract || amount + fee + ourfee > balance) {
subtract = true;
if (amount <= fee + ourfee + dust) {
- fail(
- `insufficient funds ⚡️${balance} of ⚡️${amount + fee + ourfee + dust}`,
- );
+ fail(`insufficient funds ⚡️${balance} of ⚡️${amount + fee + ourfee + dust}`);
}
outs = [{ [address]: btc(amount - ourfee) }];
@@ -1269,9 +1187,7 @@ export const importAccountHistory = async (account) => {
const addressSet = new Set(allAddrs);
const utxos = await getAddressUtxos(allAddrs);
- const confirmedUtxos = utxos.filter(
- (u) => u.status.confirmed && u.value >= 300,
- );
+ const confirmedUtxos = utxos.filter((u) => u.status.confirmed && u.value >= 300);
const total = confirmedUtxos.reduce((sum, u) => sum + u.value, 0);
const rates = await g("rates");
@@ -1294,9 +1210,7 @@ export const importAccountHistory = async (account) => {
continue;
}
- const created = u.status?.block_time
- ? u.status.block_time * 1000
- : Date.now();
+ const created = u.status?.block_time ? u.status.block_time * 1000 : Date.now();
const p = {
id: v4(),
aid: id,
@@ -1371,9 +1285,7 @@ export const importAccountHistory = async (account) => {
if (amount <= 0) continue;
const fee = Math.max(0, totalIn - totalOut);
- const created = tx.status?.block_time
- ? tx.status.block_time * 1000
- : Date.now();
+ const created = tx.status?.block_time ? tx.status.block_time * 1000 : Date.now();
const p = {
id: v4(),
@@ -1409,7 +1321,6 @@ export const importAccountHistory = async (account) => {
}
};
-
export const check = async () => {
if (process.env.URL.includes("dev")) return;
try {
@@ -1495,9 +1406,7 @@ const reverse = async (p) => {
const freezeCheck = async () => {
const funds = await ln.listfunds();
- const lnbalance = Math.round(
- funds.channels.reduce((a, b) => a + b.our_amount_msat, 0) / 1000,
- );
+ const lnbalance = Math.round(funds.channels.reduce((a, b) => a + b.our_amount_msat, 0) / 1000);
const bcbalance = Math.round((await bc.getBalance()) * SATS);
const { bitcoin } = await lq.getBalance();
diff --git a/lib/rates.ts b/lib/rates.ts
index dcb44464..74dfa50d 100644
--- a/lib/rates.ts
+++ b/lib/rates.ts
@@ -27,9 +27,7 @@ const connect = async () => {
try {
rates.IRT = (
- (await got(
- "https://api.nobitex.ir/v2/orderbook/BTCIRT",
- ).json()) as any
+ (await got("https://api.nobitex.ir/v2/orderbook/BTCIRT").json()) as any
).lastTradePrice;
} catch (e) {}
diff --git a/lib/register.ts b/lib/register.ts
index e99238b1..ac1997db 100644
--- a/lib/register.ts
+++ b/lib/register.ts
@@ -19,8 +19,7 @@ export default async (user, ip) => {
const reserved = ["ecash"];
if (!username) fail("Username required");
username = username.toLowerCase().replace(/\s/g, "");
- if (!valid.test(username))
- fail("Usernames can only have letters and numbers");
+ if (!valid.test(username)) fail("Usernames can only have letters and numbers");
if (reserved.includes(username)) fail("Invalid username");
if (username.includes("undefined")) fail("Invalid username");
@@ -41,7 +40,9 @@ export default async (user, ip) => {
if (config.ipregistry) {
try {
const {
- location: { country: { code } },
+ location: {
+ country: { code },
+ },
}: any = await got(
`https://api.ipregistry.co/${ip}?key=${config.ipregistry}&fields=location.country.code`,
).json();
diff --git a/lib/sockets.ts b/lib/sockets.ts
index 2e1c98e9..88817728 100644
--- a/lib/sockets.ts
+++ b/lib/sockets.ts
@@ -120,12 +120,8 @@ Bun.serve({
await track(ws, data);
} catch (e) {
- if (e.message.includes("not found"))
- ws.send(JSON.stringify({ type: "logout" }));
- setTimeout(
- () => ws?.close(code, `closing due to error ${e.message}`),
- 1000,
- );
+ if (e.message.includes("not found")) ws.send(JSON.stringify({ type: "logout" }));
+ setTimeout(() => ws?.close(code, `closing due to error ${e.message}`), 1000);
}
break;
case "token":
@@ -139,7 +135,7 @@ Bun.serve({
subscriptions.push({ invoice: data, ws });
break;
default:
- // warn("received socket message of unknown type", type, data);
+ // warn("received socket message of unknown type", type, data);
}
},
open(ws: any) {
@@ -158,8 +154,7 @@ Bun.serve({
}
let i;
- ~(i = subscriptions.findIndex((s) => s.ws.id === id)) &&
- subscriptions.splice(i, 1);
+ ~(i = subscriptions.findIndex((s) => s.ws.id === id)) && subscriptions.splice(i, 1);
if (all[id]) delete all[id];
} catch (e) {
diff --git a/lib/tb.ts b/lib/tb.ts
index ae1ac576..29411fef 100644
--- a/lib/tb.ts
+++ b/lib/tb.ts
@@ -1,11 +1,6 @@
import config from "$config";
import { warn } from "$lib/logging";
-import {
- createClient,
- CreateAccountError,
- AccountFlags,
- TransferFlags,
-} from "tigerbeetle-node";
+import { createClient, CreateAccountError, AccountFlags, TransferFlags } from "tigerbeetle-node";
import { createHash } from "crypto";
let client: any;
@@ -231,10 +226,7 @@ export async function tbDebit(
let coveredMicro = 0n;
if (ourfee > 0 && creditType && creditLedger[creditType]) {
const creditAcct = await getAccount(
- creditId(
- uid === aid ? uid : "00000000-0000-0000-0000-000000000000",
- creditType,
- ),
+ creditId(uid === aid ? uid : "00000000-0000-0000-0000-000000000000", creditType),
);
const creditBalMicro = accountBalanceMicro(creditAcct);
const ourfeeMicro = BigInt(ourfee) * MSATS;
@@ -349,9 +341,7 @@ export async function tbCredit(
// Add fee credit if applicable - MICROSATS for sub-satoshi precision!
// e.g., 2% of 10 sats = 0.2 sats = 200,000 microsats (accumulates over time)
if (creditType && creditLedger[creditType] && config.fee[creditType]) {
- const creditAmountMicro = BigInt(
- Math.round(amount * MSATS_NUM * config.fee[creditType]),
- );
+ const creditAmountMicro = BigInt(Math.round(amount * MSATS_NUM * config.fee[creditType]));
if (creditAmountMicro > 0n) {
transfers.push({
id: nextTransferId(),
@@ -421,11 +411,7 @@ export async function tbConfirm(aid: string, amount: number) {
}
}
-export async function tbReverse(
- uid: string,
- total: number,
- creditAmount: number,
-) {
+export async function tbReverse(uid: string, total: number, creditAmount: number) {
const transfers: any[] = [];
const linked = TransferFlags.linked;
@@ -650,11 +636,7 @@ export async function tbFundCredit(name: string, amount: number) {
}
}
-export async function tbFundDebit(
- name: string,
- amount: number,
- errMsg: string,
-): Promise<any> {
+export async function tbFundDebit(name: string, amount: number, errMsg: string): Promise<any> {
const transfers = [
{
id: nextTransferId(),
@@ -700,11 +682,7 @@ export async function tbMultiplyForMicrosats(uid: string): Promise<number> {
const MULTIPLIER = 999999n; // Adding this turns X into X*1000000
let count = 0;
- const multiplyAccount = async (
- accountId: bigint,
- ledger: number,
- houseId: bigint,
- ) => {
+ const multiplyAccount = async (accountId: bigint, ledger: number, houseId: bigint) => {
const acct = await client.lookupAccounts([u128(accountId)]);
if (!acct.length) return;
const current = acct[0].credits_posted - acct[0].debits_posted;
@@ -736,11 +714,7 @@ export async function tbMultiplyForMicrosats(uid: string): Promise<number> {
await multiplyAccount(balId, LEDGER_SATS, HOUSE_SATS);
await multiplyAccount(pendId, LEDGER_SATS, HOUSE_SATS);
await multiplyAccount(creditId(uid, "bitcoin"), LEDGER_CREDIT_BTC, HOUSE_BTC);
- await multiplyAccount(
- creditId(uid, "lightning"),
- LEDGER_CREDIT_LN,
- HOUSE_LN,
- );
+ await multiplyAccount(creditId(uid, "lightning"), LEDGER_CREDIT_LN, HOUSE_LN);
await multiplyAccount(creditId(uid, "liquid"), LEDGER_CREDIT_LQ, HOUSE_LQ);
return count;
diff --git a/lib/upload.ts b/lib/upload.ts
index 7b5ad86d..2ed5d905 100644
--- a/lib/upload.ts
+++ b/lib/upload.ts
@@ -16,15 +16,10 @@ export default async (req, res) => {
const [format, ext] = (await fileTypeFromBuffer(buf)).mime.split("/");
- if (format !== "image" && !["jpg", "jpeg", "png"].includes(ext))
- fail("unsupported file type");
+ if (format !== "image" && !["jpg", "jpeg", "png"].includes(ext)) fail("unsupported file type");
const w = type === "banner" ? 1920 : 240;
- buf = await sharp(buf, { failOnError: false })
- .rotate()
- .resize(w)
- .webp()
- .toBuffer();
+ buf = await sharp(buf, { failOnError: false }).rotate().resize(w).webp().toBuffer();
const hash = createHash("sha256").update(buf).digest("hex");
diff --git a/lib/utils.ts b/lib/utils.ts
index 878ef037..7a3d4c13 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -49,7 +49,7 @@ export const bip21 = (address, { amount, memo, tip, type }) => {
export const getAccount = async (id) => {
return g(`account:${id}`);
-}
+};
export const getUser = async (username, fields = undefined) => {
if (username === "undefined") fail("invalid user");
@@ -121,22 +121,17 @@ export function formatReceipt(items, currency) {
};
}
const maxLineWidth = 32;
- const { quantityColumnWidth, priceColumnWidth } =
- calculateColumnWidths(items);
+ const { quantityColumnWidth, priceColumnWidth } = calculateColumnWidths(items);
const nameColumnWidth = maxLineWidth - quantityColumnWidth - priceColumnWidth;
return items
.map((item) => {
const quantityStr = String(item.quantity).padEnd(quantityColumnWidth);
- const priceStr = f(item.price * item.quantity, currency).padStart(
- priceColumnWidth,
- );
+ const priceStr = f(item.price * item.quantity, currency).padStart(priceColumnWidth);
const nameLines = wrapText(item.name, nameColumnWidth);
// Construct the full line(s) with the first line including the price
- const fullLines = [
- `${quantityStr}${nameLines[0].padEnd(nameColumnWidth)}${priceStr}`,
- ];
+ const fullLines = [`${quantityStr}${nameLines[0].padEnd(nameColumnWidth)}${priceStr}`];
// Add any additional name lines, properly indented
for (let i = 1; i < nameLines.length; i++) {
fullLines.push(" ".repeat(quantityColumnWidth) + nameLines[i]);
@@ -162,9 +157,7 @@ export const time = (() => {
})();
export const fmt = (sats) =>
- `⚡️${new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(
- sats,
- )}`;
+ `⚡️${new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(sats)}`;
export const link = (id) => `${URL}/payment/${id}`;
diff --git a/lib/zmq.ts b/lib/zmq.ts
index 09f5a4ba..bce898d4 100644
--- a/lib/zmq.ts
+++ b/lib/zmq.ts
@@ -23,17 +23,14 @@ const TX_OPTS = {
const enc = new TextEncoder();
const dec = new TextDecoder();
-const reverseHex = (buf: Uint8Array) =>
- bytesToHex(Uint8Array.from(buf).reverse());
+const reverseHex = (buf: Uint8Array) => bytesToHex(Uint8Array.from(buf).reverse());
const readVarInt = (data: Uint8Array, offset: number) => {
const first = data[offset];
if (first < 0xfd) return { value: first, size: 1 };
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
- if (first === 0xfd)
- return { value: view.getUint16(offset + 1, true), size: 3 };
- if (first === 0xfe)
- return { value: view.getUint32(offset + 1, true), size: 5 };
+ if (first === 0xfd) return { value: view.getUint16(offset + 1, true), size: 3 };
+ if (first === 0xfe) return { value: view.getUint32(offset + 1, true), size: 5 };
return { value: Number(view.getBigUint64(offset + 1, true)), size: 9 };
};
@@ -150,10 +147,7 @@ const frame = (flags: number, body: Uint8Array) => {
};
const command = (name: string, props: Record<string, string> = {}) => {
- const parts: Uint8Array[] = [
- Uint8Array.of(name.length),
- enc.encode(name),
- ];
+ const parts: Uint8Array[] = [Uint8Array.of(name.length), enc.encode(name)];
for (const [k, v] of Object.entries(props)) {
const kb = enc.encode(k);
const vb = enc.encode(v);
@@ -169,23 +163,14 @@ const subscribeCommand = (topic: string) => {
const t = enc.encode(topic);
return frame(
0x04,
- concatBytes(
- Uint8Array.of(9),
- enc.encode("SUBSCRIBE"),
- Uint8Array.of(t.length),
- t,
- ),
+ concatBytes(Uint8Array.of(9), enc.encode("SUBSCRIBE"), Uint8Array.of(t.length), t),
);
};
const legacySubscribe = (topic: string) =>
frame(0x00, concatBytes(Uint8Array.of(1), enc.encode(topic)));
-const startSub = (
- url: string,
- topic: string,
- onMessage: (b: Uint8Array) => void,
-) =>
+const startSub = (url: string, topic: string, onMessage: (b: Uint8Array) => void) =>
new Promise<void>((resolve) => {
const [host, port] = url.replace("tcp://", "").split(":");
const socket = net.connect({ host, port: Number(port) });
@@ -212,11 +197,7 @@ const startSub = (
let header: number;
if (flags & 0x02) {
if (buffer.length < 9) return;
- const view = new DataView(
- buffer.buffer,
- buffer.byteOffset,
- buffer.byteLength,
- );
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
size = Number(view.getBigUint64(1));
header = 9;
} else {
@@ -262,11 +243,7 @@ export const startZmq = async () => {
if (process.env.DISABLE_ZMQ === "1") return;
l("zmq connecting", RAWTX_URL, RAWBLOCK_URL);
- const retry = async (
- url: string,
- topic: string,
- handler: (b: Uint8Array) => Promise<void>,
- ) => {
+ const retry = async (url: string, topic: string, handler: (b: Uint8Array) => Promise<void>) => {
let delay = 1000;
const maxDelay = 30000;
while (true) {