aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-05-31 23:27:59 -0700
committerAdam Soltys <[email protected]>2026-05-31 23:27:59 -0700
commit7841b2d61830f13260e46d24ce69f24b2dbcea29 (patch)
tree81d5be901b6e566c4ac812e294e4f893347634ed /lib
parent2ea66724f331d4f24a0ca83a91ef90c27ed07353 (diff)
downloadcoinos-server-7841b2d61830f13260e46d24ce69f24b2dbcea29.tar.gz
coinos-server-7841b2d61830f13260e46d24ce69f24b2dbcea29.zip
debit: exempt internal sends from server liquidity limit
freezeCheck only populates ${type}:limit for external asset types (lightning/fund/ecash/bitcoin/liquid/bolt12), so internal:limit stays 0 forever. The atomic limit check added in 609fcebe applied to all types, so amount > 0 blocked every internal coinos-to-coinos payment with "serverLimit 0". Internal sends are pure ledger moves that never touch an external wallet — there's no hot-wallet liquidity to reserve — so exempt them, matching the existing soft-freeze and withdraw-lock gates. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'lib')
-rw-r--r--lib/payments.ts24
1 files changed, 16 insertions, 8 deletions
diff --git a/lib/payments.ts b/lib/payments.ts
index 52434739..acc9a462 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -135,14 +135,22 @@ export const debit = async ({
// Atomic check + reserve against the per-asset-type server limit. Decrement
// immediately so concurrent calls can't reuse the same budget; freezeCheck
// reconciles to actual on-chain balance every 10s.
- await withLimitLock(type, async () => {
- const serverLimit = Number.parseInt((await g(`${type}:limit`)) ?? "0", 10) || 0;
- if (amount > serverLimit) {
- warn("Blocking", user.username, amount, hash, user.id, type, "serverLimit", serverLimit);
- fail("Problem sending payment");
- }
- await db.decrBy(`${type}:limit`, amount);
- });
+ //
+ // Internal sends are exempt: they're pure ledger moves between coinos users
+ // and never touch an external wallet, so there's no hot-wallet liquidity to
+ // reserve against. freezeCheck only ever populates limits for external types
+ // (lightning/fund/ecash/bolt12/bitcoin/liquid), so `internal:limit` stays 0
+ // forever — applying the check here blocks every internal payment.
+ if (type !== PaymentType.internal) {
+ await withLimitLock(type, async () => {
+ const serverLimit = Number.parseInt((await g(`${type}:limit`)) ?? "0", 10) || 0;
+ if (amount > serverLimit) {
+ warn("Blocking", user.username, amount, hash, user.id, type, "serverLimit", serverLimit);
+ fail("Problem sending payment");
+ }
+ await db.decrBy(`${type}:limit`, amount);
+ });
+ }
let ref;
const { id: uid, currency } = user;