aboutsummaryrefslogtreecommitdiffstats
path: root/lib/spend-budget.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spend-budget.ts')
-rw-r--r--lib/spend-budget.ts39
1 files changed, 39 insertions, 0 deletions
diff --git a/lib/spend-budget.ts b/lib/spend-budget.ts
new file mode 100644
index 00000000..3f2a70ac
--- /dev/null
+++ b/lib/spend-budget.ts
@@ -0,0 +1,39 @@
+const number = (value: unknown) => {
+ const parsed = Number(value);
+ return Number.isFinite(parsed) ? parsed : 0;
+};
+
+export const debitTotal = ({
+ amount,
+ tip = 0,
+ fee = 0,
+ ourfee = 0,
+}: {
+ amount: unknown;
+ tip?: unknown;
+ fee?: unknown;
+ ourfee?: unknown;
+}) => number(amount) + number(tip) + number(fee) + number(ourfee);
+
+// Enforce a caller-specific spending allowance at debit's final choke point,
+// after mutable invoice metadata (notably an internal invoice tip) and fees have
+// been captured. Checking earlier lets a recipient mutate the tip between the
+// NWC budget check and the ledger debit.
+export const assertWithinSpendLimit = ({
+ amount,
+ tip = 0,
+ fee = 0,
+ ourfee = 0,
+ maxTotal,
+}: {
+ amount: unknown;
+ tip?: unknown;
+ fee?: unknown;
+ ourfee?: unknown;
+ maxTotal?: number;
+}) => {
+ const total = debitTotal({ amount, tip, fee, ourfee });
+ if (maxTotal !== undefined && total > maxTotal)
+ throw new Error(`Budget exceeded: ${total} of ${maxTotal} remaining`);
+ return total;
+};