Skip to content

fix(limit-twap): disable confirm when balance is insufficient - #7804

Merged
elena-zh merged 7 commits into
cowprotocol:developfrom
tenderdeve:fix/5645-limit-twap-balance
Jul 10, 2026
Merged

elena-zh merged 7 commits into
cowprotocol:developfrom
tenderdeve:fix/5645-limit-twap-balance

Conversation

@tenderdeve

@tenderdeve tenderdeve commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #5645.

The Swap confirm modal already re-checks the wallet balance against its frozen sell amount (#5614), but the Limit and TWAP confirm modals did not. So if you open the confirm modal with a max-balance order and a previous order fills while it's open, the balance is gone but the Confirm button stays enabled and shows no change — letting you submit an order you can't cover.

Changes

  • New shared hook useHasEnoughBalanceForAmount(amount) (modules/combinedBalances) that re-validates a (frozen) amount against live combined balances — the same logic the swap modal uses, extracted so all flows share it.
  • LimitOrdersConfirmModal and TwapConfirmModal now fold insufficient balance into isConfirmDisabled and switch the button label to Insufficient <token> balance.

Returns "enough" when there's no amount yet, so the button isn't blocked during loading.

Testing steps

  1. Place a limit (or TWAP) order using your full balance of a token.
  2. While it's Open, start another order with the same pair at max balance and open the Confirm modal.
  3. Wait for the first order to fill (spending the balance).
  4. The Confirm button is now disabled and reads Insufficient … balance instead of staying enabled.

Summary by CodeRabbit

  • New Features

    • Added real-time combined-balance affordability checks to order confirmation flows (including TWAP and limit orders).
    • Confirmation buttons now reflect insufficient funds with an “Insufficient balance” message.
  • Bug Fixes

    • Disabled confirmation when the available balance drops below the sell amount (including zero-balance conditions) while a modal is open.
    • Kept confirmations available during loading when the amount or currency isn’t ready yet.
  • Tests

    • Added unit tests for the new combined-balance hooks.

The Limit and TWAP confirmation modals froze their sell amount but never
re-checked the wallet balance, so if a previous order filled while the modal
was open the Confirm button stayed enabled with no balance change shown. Mirror
the swap confirm flow: re-validate the frozen amount against live combined
balances via a shared useHasEnoughBalanceForAmount hook, disable Confirm and
show an 'Insufficient balance' label when it no longer fits.

Closes cowprotocol#5645
@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

@tenderdeve is attempting to deploy a commit to the cow-dev Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c8fa3c3b-221d-4fb7-98f2-2c3dd47c1807

📥 Commits

Reviewing files that changed from the base of the PR and between c8eb08e and ece8d0e.

📒 Files selected for processing (2)
  • apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.test.ts
  • apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.test.ts
  • apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.ts

Walkthrough

Adds live balance validation hooks and wires them into the Limit Orders and TWAP confirm modals so confirmation is blocked and relabeled when balances are insufficient.

Changes

Insufficient Balance Validation

Layer / File(s) Summary
Amount affordability hook
apps/cowswap-frontend/src/modules/combinedBalances/hooks/useHasEnoughBalanceForAmount.ts, apps/cowswap-frontend/src/modules/combinedBalances/index.ts, apps/cowswap-frontend/src/modules/combinedBalances/hooks/useHasEnoughBalanceForAmount.test.ts
Adds a memoized hook that compares an input amount against live combined balances, re-exports it from the module, and covers null/equal/greater/less test cases.
Zero-balance hook and limit modal
apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.ts, apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.test.ts, apps/cowswap-frontend/src/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx
Adds a zero-balance hook with tests and uses it in the Limit Orders confirm modal to disable confirmation and update the button label when the token balance is zero.
TWAP confirm modal balance check
apps/cowswap-frontend/src/modules/twap/containers/TwapConfirmModal/index.tsx
Uses the amount-affordability hook to re-check the frozen sell amount, disables confirmation on insufficient balance, and updates the button label accordingly.

Estimated code review effort: 2 (Simple) | ~12 minutes

Suggested labels: Balances

Suggested reviewers: limitofzero, fairlighteth, elena-zh

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ConfirmModal
  participant useHasEnoughBalanceForAmount
  participant useIsZeroBalance
  participant useTokensBalancesCombined

  User->>ConfirmModal: Open confirm modal
  ConfirmModal->>useHasEnoughBalanceForAmount: check frozen sell amount
  ConfirmModal->>useIsZeroBalance: check current token balance
  useHasEnoughBalanceForAmount->>useTokensBalancesCombined: read combined balances
  useIsZeroBalance->>useTokensBalancesCombined: read combined balances
  useTokensBalancesCombined-->>useHasEnoughBalanceForAmount: balances
  useTokensBalancesCombined-->>useIsZeroBalance: balances
  useHasEnoughBalanceForAmount-->>ConfirmModal: insufficient? boolean
  useIsZeroBalance-->>ConfirmModal: zero balance? boolean
  alt insufficient balance
    ConfirmModal-->>User: disable confirm, show insufficient balance text
  else sufficient balance
    ConfirmModal-->>User: keep confirm enabled
  end
Loading

Poem

A rabbit checked the balances twice,
for TWAP and limits, swift and precise. 🐇
When tokens ran low, the button knew,
and showed a message straight and true.
Hop safe, hop sound, and count each slice!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: disabling confirm when balance is insufficient for limit/TWAP flows.
Description check ✅ Passed The description follows the template with Summary, Changes, and Testing steps, and includes the linked issue reference.
Linked Issues check ✅ Passed The PR matches #5645 by adding zero-balance validation for Limit and amount-based validation for TWAP, plus confirm-button messaging.
Out of Scope Changes check ✅ Passed The changes stay focused on shared balance validation and the two confirm modals, with no obvious unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@vercel

vercel Bot commented Jul 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
swap-dev Ready Ready Preview Jul 7, 2026 12:46pm

Request Review

@vercel
vercel Bot temporarily deployed to Preview – swap-dev July 2, 2026 14:38 Inactive
@vercel
vercel Bot temporarily deployed to Preview – swap-dev July 3, 2026 08:19 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/cowswap-frontend/src/modules/combinedBalances/hooks/useHasEnoughBalanceForAmount.ts (1)

1-33: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test coverage for new shared validation hook.

This is a critical-path hook (gates order confirmation across two flows) with no accompanying unit test in the diff.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/cowswap-frontend/src/modules/combinedBalances/hooks/useHasEnoughBalanceForAmount.ts`
around lines 1 - 33, The new shared validation logic in
useHasEnoughBalanceForAmount has no test coverage, so add unit tests for the
hook. Cover the key behaviors by mocking useTokensBalancesCombined and verifying
it returns true when inputAmount is nullish, true when the balance is equal or
higher, and false when the balance is lower. Place the tests alongside the hook
so the critical confirmation gating behavior stays protected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@apps/cowswap-frontend/src/modules/combinedBalances/hooks/useHasEnoughBalanceForAmount.ts`:
- Around line 1-33: The new shared validation logic in
useHasEnoughBalanceForAmount has no test coverage, so add unit tests for the
hook. Cover the key behaviors by mocking useTokensBalancesCombined and verifying
it returns true when inputAmount is nullish, true when the balance is equal or
higher, and false when the balance is lower. Place the tests alongside the hook
so the critical confirmation gating behavior stays protected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6d056b1d-9ff4-4b35-b3d9-0ab192d3cccc

📥 Commits

Reviewing files that changed from the base of the PR and between f6e65f4 and 3eb7b74.

📒 Files selected for processing (4)
  • apps/cowswap-frontend/src/modules/combinedBalances/hooks/useHasEnoughBalanceForAmount.ts
  • apps/cowswap-frontend/src/modules/combinedBalances/index.ts
  • apps/cowswap-frontend/src/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx
  • apps/cowswap-frontend/src/modules/twap/containers/TwapConfirmModal/index.tsx

@elena-zh elena-zh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @tenderdeve , it works perfectly fine with TWAP, but you've missed an important note from the original issue for Limit orders: the button on the modal should show this error when a sell token's balance is 0. Only 0. Because Limit orders are allowed to be placed with amount > balance

Image

In your current implementation the limit orders form allows to proceed

Image

but the button on the 'confirm' screen is disabled:

Image

This needs to be fixed

Limit orders may be placed with an amount greater than the wallet balance, so
the confirm modal must not disable on amount > balance. Per issue cowprotocol#5645, only
block (and show "Insufficient <token> balance") when the sell token balance
drops to exactly 0 while the modal is open.

Add a dedicated useIsZeroBalance hook and switch LimitOrdersConfirmModal to it;
TWAP keeps the amount-vs-balance check (useHasEnoughBalanceForAmount) unchanged.
Add unit tests for both hooks.
@tenderdeve

Copy link
Copy Markdown
Contributor Author

@elena-zh good catch — fixed in d0276c8.

Per the issue's note, Limit orders can be placed with amount > balance, so the confirm modal now only disables (and shows Insufficient … balance) when the sell token balance is exactly 0, not whenever amount > balance. So a limit order with amount > balance (balance > 0) now proceeds through Confirm as expected.

TWAP is unchanged — still blocks when balance < order amount, as the issue specifies.

Implementation: added a dedicated useIsZeroBalance(currency) hook for the limit path; TWAP keeps useHasEnoughBalanceForAmount. Also added unit tests for both hooks (covers coderabbit's nitpick about missing coverage). Typecheck/lint pass locally.

@elena-zh
elena-zh self-requested a review July 6, 2026 08:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.ts`:
- Around line 19-30: The useIsZeroBalance hook can incorrectly return true while
useTokensBalancesCombined is still loading, causing a false
zero-balance/“Insufficient balance” state before real balances arrive. Update
useIsZeroBalance to check the loading state (or hasFirstLoad) from
useTokensBalancesCombined before computing CurrencyAmount.fromRawAmount and
returning balance.equalTo(0), and treat the loading phase as non-zero until
balances are available.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0900d4ac-d70a-41f2-8291-d661c98d74cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3eb7b74 and c8eb08e.

📒 Files selected for processing (5)
  • apps/cowswap-frontend/src/modules/combinedBalances/hooks/useHasEnoughBalanceForAmount.test.ts
  • apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.test.ts
  • apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.ts
  • apps/cowswap-frontend/src/modules/combinedBalances/index.ts
  • apps/cowswap-frontend/src/modules/limitOrders/containers/LimitOrdersConfirmModal/index.tsx
✅ Files skipped from review due to trivial changes (2)
  • apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.test.ts
  • apps/cowswap-frontend/src/modules/combinedBalances/index.ts

@vercel
vercel Bot temporarily deployed to Preview – swap-dev July 6, 2026 09:31 Inactive

@elena-zh elena-zh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you

@elena-zh
elena-zh requested a review from limitofzero July 6, 2026 09:55
useIsZeroBalance read from balancesCombinedAtom whose default `values` is empty,
so it briefly returned true (and showed 'Insufficient balance') before balances
arrived. Gate on `hasFirstLoad` so the confirm button isn't blocked by a transient
zero. Adds a test for the pre-load case.
@tenderdeve

Copy link
Copy Markdown
Contributor Author

@coderabbitai good catch — addressed in 16245ea. useIsZeroBalance now gates on hasFirstLoad so it can't return true (and flash 'Insufficient balance') while values is still the empty default. Added a test for the pre-load case.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cd /repo 2>/dev/null || true
git log --all --oneline | grep -i "16245ea1a" || true
git show 16245ea1a --stat 2>/dev/null | head -50
git show 16245ea1a -- apps/cowswap-frontend/src/modules/combinedBalances/hooks/useIsZeroBalance.ts 2>/dev/null

Length of output: 3177


@tenderdeve Confirmed — commit 16245ea correctly gates useIsZeroBalance on hasFirstLoad, so it returns false until balances have loaded at least once, preventing the transient "Insufficient balance" flash. The updated useMemo deps and the added pre-load test case look good. Thanks for the quick fix!

ᕙ(⇀‸↼‶)ᕗ

@vercel
vercel Bot temporarily deployed to Preview – swap-dev July 7, 2026 12:46 Inactive
@elena-zh
elena-zh enabled auto-merge (squash) July 10, 2026 15:25
@elena-zh
elena-zh merged commit 089d4fd into cowprotocol:develop Jul 10, 2026
11 of 12 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add validation to not enough balance on Limit/TWAP forms

3 participants