Skip to content

fix: compare LPT allowance and balance in wei#738

Open
rickstaa wants to merge 8 commits into
mainfrom
fix/delegate-allowance-units
Open

fix: compare LPT allowance and balance in wei#738
rickstaa wants to merge 8 commits into
mainfrom
fix/delegate-allowance-units

Conversation

@rickstaa

@rickstaa rickstaa commented Jul 21, 2026

Copy link
Copy Markdown
Member

Fixes #737.

Problem

/api/account-balance returns balance and allowance as raw wei, but Delegate.tsx compared them against amount, the human-readable LPT input:

Number(tokenBalance)      >= amount   // 1.4e19 >= 5
Number(transferAllowance) >= amount   // 1.1e17 >= 5

Both are true for any realistic input, so a wallet holding a partial (non-zero but insufficient) allowance never saw the approve flow, the bondWithHint simulation reverted, bondWithHintConfig stayed undefined, and onDelegate threw into a console.error — leaving an enabled Delegate button that did nothing. The Insufficient Balance guard could never fire either.

Only partial allowances are affected: at allowance 0 the approve flow shows correctly and grants MAX_UINT256, which is why the normal path works.

Fix

Convert the input with parseEther and compare in wei via BigInt. Also disable the Delegate button while bondWithHintConfig is undefined, so a failed simulation can no longer present as a working button.

Notes

Introduced in 989c09b (#165), which created /api/account-balance and rewired both consumers to it — InputBox kept its fromWei, Footer dropped it. That split is why the displayed balance stayed correct while the comparison broke.

Summary by CodeRabbit

  • Bug Fixes
    • Improved staking/undelegation and approval readiness by using consistent wei-based balance and allowance checks, preventing incorrect enablement.
    • Action buttons now more reliably disable when the entered amount is missing/invalid, when balance is insufficient, or when required bonding/approval prerequisites aren’t met.
  • New Features
    • Added a more robust amount parser for user-entered Ether values, supporting exponent notation.
  • Tests
    • Added coverage for precision, exponent handling, and invalid/empty inputs.
  • Documentation
    • Marked the legacy toWei helper as deprecated in its docs.

/api/account-balance returns balance and allowance in wei, but the delegating
widget compared them against the human-readable LPT input. Both checks were
true for any realistic amount, so wallets holding a partial allowance never
saw the approve flow, the bondWithHint simulation reverted, and the Delegate
button became a silent no-op.

Compare in wei with BigInt, and disable Delegate while the simulation has no
config so a failed simulation cannot present as a working button.

Fixes #737

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@rickstaa
rickstaa requested a review from ECWireless as a code owner July 21, 2026 09:42
Copilot AI review requested due to automatic review settings July 21, 2026 09:42
@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
explorer-arbitrum-one Ready Ready Preview, Comment Jul 21, 2026 1:17pm

Request Review

Copilot AI 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.

Pull request overview

Fixes the delegating widget’s allowance/balance gating by comparing the user-entered LPT amount against /api/account-balance’s balance/allowance values in wei (instead of comparing raw-wei strings to a human-readable input). Also prevents the Delegate button from appearing clickable when the bondWithHint simulation hasn’t produced a usable config.

Changes:

  • Convert the input amount to wei (parseEther) and compare using BigInt against tokenBalance and transferAllowance.
  • Disable Delegate actions when bondWithHintConfig is unavailable to avoid “enabled but no-op” clicks after a failed simulation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +170 to +174
// `tokenBalance` and `transferAllowance` are wei, so compare in wei.
const amountWei = useMemo(() => {
try {
return amount ? parseEther(amount) : BigInt(0);
} catch {
@coderabbitai

coderabbitai Bot commented Jul 21, 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1b157e8c-44b3-47c7-b7a8-c10d561a8f22

📥 Commits

Reviewing files that changed from the base of the PR and between 71f1910 and 11c81fd.

📒 Files selected for processing (1)
  • components/DelegatingWidget/Delegate.tsx

📝 Walkthrough

Walkthrough

The delegation widget and footer now parse entered amounts into wei, use bigint comparisons for balance and stake checks, and gate simulations and buttons on valid amounts and available configuration. Tests cover decimal, precision, exponent, and invalid input handling.

Changes

Delegation amount handling

Layer / File(s) Summary
Wei amount parser and coverage
utils/web3.ts, utils/web3.test.ts
Adds parseAmountToWei for nullable decimal or exponent-formatted input, documents toWei as deprecated, and tests valid and invalid conversions.
Delegation validation and simulation
components/DelegatingWidget/Delegate.tsx
Compares token balances and allowances against parsed wei amounts, gates bond simulation on allowance and configuration, and disables invalid or unavailable delegation actions.
Stake validation and deactivation
components/DelegatingWidget/Footer.tsx
Uses parsed wei amounts for stake sufficiency, active-set-order simulation, and orchestrator deactivation checks.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: ecwireless

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main fix: comparing LPT allowance and balance in wei.
Linked Issues check ✅ Passed The changes address #737 by comparing amounts in wei, restoring approve-flow gating, and disabling delegate when simulation config is missing.
Out of Scope Changes check ✅ Passed The extra helper and test updates support the same wei parsing fix and do not appear unrelated to #737.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/delegate-allowance-units

Warning

Tools execution failed with the following error:

Failed to run tools: 14 UNAVAILABLE: read ECONNRESET


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.

@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 `@components/DelegatingWidget/Delegate.tsx`:
- Around line 170-184: Move the safe amountWei useMemo above bondWithHintArgs in
Delegate and update bondWithHintArgs to use amountWei instead of calling
parseEther(amount) directly. Remove the amountWei > BigInt(0) conditions from
sufficientBalance and sufficientTransferAllowance so zero-amount inputs are
evaluated by the balance and allowance comparisons while preserving safe
handling of invalid input.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c0f5740e-1ef5-4b83-97da-b8a516979cad

📥 Commits

Reviewing files that changed from the base of the PR and between 6f2a4a1 and 26ae84c.

📒 Files selected for processing (1)
  • components/DelegatingWidget/Delegate.tsx

Comment thread components/DelegatingWidget/Delegate.tsx Outdated
parseEther was called unguarded when building bondWithHintArgs, so a value
the number input accepts but viem rejects (e.g. `1e3`) threw during render and
took the widget down.

The bondWithHint simulation also ran before the allowance was in place, and
nothing re-runs a cached failure once the approval confirms, leaving Delegate
disabled until the amount was retyped or the page reloaded.

Parse the amount once, defensively, and reuse it for both the simulation args
and the balance/allowance checks. Gate the simulation on the allowance so it
first runs when it can succeed; a zero amount moves no tokens and needs none,
which keeps "Move Delegated Stake" working.

Keeps the amountWei > 0 guards: they match the previous parseFloat(amount) > 0
behaviour, and dropping them would enable Delegate for a zero-amount bond.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Co-Authored-By: Copilot <copilot-pull-request-reviewer[bot]@users.noreply.github.com>
Co-Authored-By: CodeRabbit <coderabbitai[bot]@users.noreply.github.com>
rickstaa and others added 2 commits July 21, 2026 12:28
parseEther throws on values the amount input accepts but cannot represent as a
decimal (e.g. `1e3`). Footer called it unguarded when computing the active set
order, so typing one crashed the render.

Parse through a shared parseAmountToWei helper that returns null instead of
throwing, and treat null as "no amount entered" so the button falls back to its
existing disabled state.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The amount input accepts `1e-2`, but parseEther rejects it, so valid amounts
were treated as no input at all. Normalise exponent notation to a decimal
before parsing; plain decimals skip Number to avoid float precision loss.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
parseAmountToWei had no tests. Pin the decimal, exponent, and null cases, and
the precision behaviour that requires exponents alone to route through Number.
The boundary test records that exponents outside the range Number writes as a
decimal are rejected, while the same value in decimal notation is not.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/DelegatingWidget/Footer.tsx (1)

100-103: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Rely on amountWei instead of parseFloat for undelegation validation.

Problem: canUndelegate uses parseFloat(amount), which leniently parses partially invalid strings (like "1e" as 1). This enables the Undelegate button even when amountWei correctly rejects the amount as null.
Why it matters: Users can submit unparseable amounts to the underlying Undelegate component, which will either crash or submit an invalid zero-amount transaction.
Suggested fix: Use the correctly parsed amountWei to determine if the amount is strictly positive and valid.

💻 Proposed refactor
   const canUndelegate = useMemo(
-    () => isMyTranscoder && isDelegated && parseFloat(amount) > 0,
-    [isMyTranscoder, isDelegated, amount]
+    () => isMyTranscoder && isDelegated && amountWei !== null && amountWei > 0n,
+    [isMyTranscoder, isDelegated, amountWei]
   );
🤖 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 `@components/DelegatingWidget/Footer.tsx` around lines 100 - 103, Update the
canUndelegate useMemo to validate the parsed amountWei value instead of
parseFloat(amount). Require amountWei to be non-null and strictly greater than
zero, while preserving the existing isMyTranscoder and isDelegated conditions
and dependency tracking.
🤖 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 `@components/DelegatingWidget/Delegate.tsx`:
- Around line 226-232: Update the early-return validation in Delegate so an
unparseable amount cannot reach the stake-transfer execution path: treat
amountWei === null as invalid whenever amount is non-empty, including when
isTransferStake is true. Preserve the existing allowance for intentionally empty
amounts during stake transfers and keep the disabled “Enter an Amount” button
behavior.

---

Outside diff comments:
In `@components/DelegatingWidget/Footer.tsx`:
- Around line 100-103: Update the canUndelegate useMemo to validate the parsed
amountWei value instead of parseFloat(amount). Require amountWei to be non-null
and strictly greater than zero, while preserving the existing isMyTranscoder and
isDelegated conditions and dependency tracking.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7f77c803-d0dd-46e5-8919-c09b24ab4ace

📥 Commits

Reviewing files that changed from the base of the PR and between 26ae84c and 4f4d924.

📒 Files selected for processing (4)
  • components/DelegatingWidget/Delegate.tsx
  • components/DelegatingWidget/Footer.tsx
  • utils/web3.test.ts
  • utils/web3.ts

Comment thread components/DelegatingWidget/Delegate.tsx Outdated
amountIsNonEmpty returned the amount string rather than a boolean, and only
worked because every use site read it for truthiness. Its last use asks whether
there is an amount worth approving for, which amountWei answers directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
A stake transfer bonds zero to switch delegate, so the empty-amount branch let
any unparseable amount through as well. Entering one would move the delegation
without adding the stake that was typed.

Allow the fall-through only when the field is genuinely empty.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Co-Authored-By: CodeRabbit <coderabbitai[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: Delegate button silently fails when LPT allowance is partially approved

2 participants