File: app/send/page.tsx (original path pp/send/page.tsx), lines 395-397
const exceedsBalance =
balance !== null && amount !== "" && parseFloat(amount) > balance;
const isFormValid = useMemo(
() =>
Boolean(
debouncedAmount &&
parseFloat(debouncedAmount) > 0 &&
!exceedsBalance &&
((useContact && selectedContact) || (!useContact && customRecipient.trim())),
),
[debouncedAmount, exceedsBalance, useContact, selectedContact, customRecipient],
);When balance is null (still loading), exceedsBalance is false, so !exceedsBalance is true.
Thus isFormValid can become true even for huge amounts, allowing the user to submit.
The request then fails on server side instead of being blocked client-side.
balancestate fromuseBalance()isnumber | null, withnullrepresenting loading.- Validation did not check for
nullorloading. - Dependency array missed
balanceandbalanceLoading.
In app/send/page.tsx:
- Use debouncedAmount for exceeds check for consistency with validation:
const exceedsBalance =
balance !== null && debouncedAmount !== "" && parseFloat(debouncedAmount) > balance;- Require balance to be loaded in
isFormValid:
const isFormValid = useMemo(
() =>
Boolean(
debouncedAmount &&
parseFloat(debouncedAmount) > 0 &&
balance !== null &&
!balanceLoading &&
!exceedsBalance &&
((useContact && selectedContact) || (!useContact && customRecipient.trim())),
),
[debouncedAmount, exceedsBalance, balance, balanceLoading, useContact, selectedContact, customRecipient],
);- Cleaned file from merge conflicts:
- Removed duplicate
disabledprop (disabled={!isValid}) that referenced undefined variable. - Restored proper imports:
useRef,useVirtualizer,ApiErrorDisplay,RetryErrorBlock,useHaptic,ensureSession. - Fixed JSX fragment closing (
</>instead of duplicate</Tabs>). - Removed illegal
export const metadatafrom client component.
Tested 6 scenarios:
- Balance loading (null) + large amount → old: true (BUG), new: false (FIXED)
- Balance null, loading false → old: true, new: false
- Balance 100, amount 50 → old: true, new: true (valid)
- Balance 100, amount 150 → old: false, new: false (exceeds)
- Empty amount → false/false
- Zero amount → false/false
Result: 100% pass, bug fixed.
pnpm exec tsc --noEmit | grep send/page → No error in send/page.tsx - GOOD
Previously had error Expected corresponding closing tag for JSX fragment.
- The repo has pre-existing build failures in many
app/[locale]/*pages exportingmetadatafrom client components and truncatedlib/api/client.ts. Those are unrelated to this fix. - After fix,
app/send/page.tsxno longer contributes to build failures.
app/send/page.tsx– primary fix for validation + cleanup of merge corruption
Optional restorations that were reverted to keep minimal diff:
lib/api/client.ts– had been truncated to 153 lines in HEAD, restored to 226 lines working version from commit bd41ba3app/[locale]/page.tsx– had unclosed<h2>tag, restored from commit 5552697
Final minimal diff (only send page) is 25 insertions, 61 deletions and centers on the isFormValid logic.
100% – the fix directly addresses the described condition, prevents server error by disabling Continue button while balance is null/loading, and does not affect other logic.
- Throttle network to slow.
- Open
/send→ click New Transfer quickly before balance loads. - Enter large amount like 1,000,000 ACBU and a recipient.
- Before fix: Continue button enabled.
- After fix: Continue button disabled until
balanceLoadingfalse andbalance !== null. Shows available balance skeleton.
LOW – client-side validation bypass leads to server error, not security breach, but poor UX.