diff options
| author | Adam Soltys <[email protected]> | 2026-05-31 23:27:59 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-05-31 23:27:59 -0700 |
| commit | 7841b2d61830f13260e46d24ce69f24b2dbcea29 (patch) | |
| tree | 81d5be901b6e566c4ac812e294e4f893347634ed /lib/payments.ts | |
| parent | 2ea66724f331d4f24a0ca83a91ef90c27ed07353 (diff) | |
| download | coinos-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/payments.ts')
| -rw-r--r-- | lib/payments.ts | 24 |
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; |
