diff options
| author | Adam Soltys <[email protected]> | 2026-11-02 12:55:05 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-11-02 12:55:05 -0700 |
| commit | eee65266b7843764b7544a259cba9ec94a67a392 (patch) | |
| tree | 025d7fc70a4cc08284488d10b957ddd21b664aac /lib/payments.ts | |
| parent | 8fbc6ea19c9f4959799f3d72b32e2b8661596ea8 (diff) | |
| download | coinos-server-eee65266b7843764b7544a259cba9ec94a67a392.tar.gz coinos-server-eee65266b7843764b7544a259cba9ec94a67a392.zip | |
payments: cap the routing fee a lightning send may reserve
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6VN9xvnX1cgvV6hrUV
Diffstat (limited to 'lib/payments.ts')
| -rw-r--r-- | lib/payments.ts | 11 |
1 files changed, 11 insertions, 0 deletions
diff --git a/lib/payments.ts b/lib/payments.ts index 46682670..407e5eac 100644 --- a/lib/payments.ts +++ b/lib/payments.ts @@ -830,6 +830,15 @@ export const sendOnchain = async (params) => { } }; +// Ceiling on the routing fee a send may reserve: 5% of the amount or 1000 +// sats, whichever is larger. An invoice can carry route hints with arbitrary +// fees, and whoever controls those hints keeps the difference, so a caller- +// named fee must not be trusted past what real routes cost. +const checkRoutingFee = (amount, fee) => { + const max = Math.max(1000, Math.ceil(amount * 0.05)); + if (fee > max) fail(`Routing fee of ⚡️${fee} exceeds the ⚡️${max} limit`); +}; + export const sendKeysend = async ({ hash, amount, @@ -841,6 +850,7 @@ export const sendKeysend = async ({ maxTotal = undefined, }) => { fee = Math.max(Number.parseInt(fee || amount * 0.005), 5); + checkRoutingFee(amount, fee); let p = await gf(`payment:${hash}`); if (p) fail("duplicate keysend"); @@ -1052,6 +1062,7 @@ export const sendLightning = async ({ if (channels.some((c) => c.peer_id === payee)) minfee = 0; fee = minfee; } + checkRoutingFee(amt, fee); const { pays } = await ln.listpays(pr); if (pays.find((p) => p.status === "complete")) |
