aboutsummaryrefslogtreecommitdiffstats
path: root/lib/payments.ts
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/payments.ts
parent9e97e040617bfa0b40150c0eba298bf186bbeeaf (diff)
downloadcoinos-server-ad7b09b43323d0ffc0a8ddabb43e4838a184c697.tar.gz
coinos-server-ad7b09b43323d0ffc0a8ddabb43e4838a184c697.zip
formatting
Diffstat (limited to 'lib/payments.ts')
-rw-r--r--lib/payments.ts171
1 files changed, 40 insertions, 131 deletions
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();