Skip to content

Fix asset burn max - #507

Merged
bordalix merged 6 commits into
masterfrom
fix/asset_ui
Apr 2, 2026
Merged

bordalix merged 6 commits into
masterfrom
fix/asset_ui

Conversation

@bordalix

@bordalix bordalix commented Apr 1, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #376

@pietro909 please review

Summary by CodeRabbit

  • New Features
    • Improved number formatting for asset balances and confirmation displays for clearer, consistent presentation.
    • Inputs now accept numeric values where applicable, preserving numeric precision across mint, burn, and reissue flows.
    • “Max” behavior and amount placeholders updated to use numeric values, yielding more consistent input and validation behavior.

@coderabbitai

coderabbitai Bot commented Apr 1, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@bordalix has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 22 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 6 minutes and 22 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 98cf3e12-09a2-4e65-ae51-c9663296262e

📥 Commits

Reviewing files that changed from the base of the PR and between a09083a and ebac595.

📒 Files selected for processing (1)
  • src/screens/Apps/Assets/Mint.tsx

Walkthrough

Asset amount handling changed from strings to numbers across asset screens; Input now accepts numeric values; asset balance rendering uses prettyNumber(...). No public/exported APIs were altered.

Changes

Cohort / File(s) Summary
Balance Display Formatting
src/components/AssetCard.tsx
Asset balance rendering now wraps centsToUnits(...) with prettyNumber(...); adds import from ../lib/format.
Input Component
src/components/Input.tsx
InputProps.value widened from string to `string
Burn Screen
src/screens/Apps/Assets/Burn.tsx
amount state changed from '' to 0 (string→number); Max handler sets numeric units via centsToUnits; parsing/confirmation use numeric amount and display uses prettyNumber; placeholder simplified to '0'.
Mint Screen
src/screens/Apps/Assets/Mint.tsx
amount and ctrlAmount states changed to numbers; removed parseFloat fallbacks; decimals input now validated as numeric and stored/serialized appropriately.
Reissue Screen
src/screens/Apps/Assets/Reissue.tsx
amount state changed to number; unitsToCents and validation updated to use numeric amount directly (removed parseFloat fallbacks).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • pietro909
  • tiero
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive Changes to Input.tsx and other asset screens (Mint, Reissue) extend numeric handling beyond burn; however, these appear to be necessary refactoring for type safety and consistency rather than unrelated changes. Clarify whether changes to Input.tsx, Mint.tsx, and Reissue.tsx are necessary dependencies for fixing the burn-max issue or represent scope creep beyond issue #376.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix asset burn max' is specific and directly related to the main change; it refers to the bug fix for the burn-max parsing issue described in the PR objectives.
Linked Issues check ✅ Passed The PR fixes the burn-max parsing bug by converting amount handling from string to numeric across multiple components, preventing comma misinterpretation as decimal separator.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/asset_ui

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 and usage tips.

@bordalix bordalix mentioned this pull request Apr 1, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 1, 2026 •

Copy link
Copy Markdown

Deploying wallet-mutinynet with  Cloudflare Pages  Cloudflare Pages

Latest commit: ebac595
Status: ✅  Deploy successful!
Preview URL: https://d491a181.arkade-wallet.pages.dev
Branch Preview URL: https://fix-asset-ui.arkade-wallet.pages.dev

View logs

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/Input.tsx`:
- Line 19: The value/onChange mismatch: update handleInput (the input change
handler) to preserve numeric types for number inputs or when the incoming prop
value is a number — detect input type or whether props.value is a number and
convert e.target.value to Number before calling onChange; ensure the onChange
prop signature in the component props (and any usages like setAmount in
Burn.tsx) expects number | string consistently, or prefer converting to number
for numeric inputs so setAmount receives a number. Locate the Input component
(props.value and handleInput) and adjust the conversion logic and prop types
accordingly.

In `@src/screens/Apps/Assets/Burn.tsx`:
- Line 28: The numeric state "amount" is declared as number and relies on the
Input component to pass numbers, but Input.handleInput currently forwards raw
string values; update Input.handleInput (and any related handler in Input.tsx)
to detect when the input's type is 'number' or when the value can be parsed as a
finite number and call onChange with Number(value) (or parsed numeric value)
instead of the raw string so that setAmount receives a number; ensure this
behavior triggers for type='number' props and preserve existing behavior for
non-numeric inputs.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ae642c97-956a-4727-b97c-fd19e97aa928

📥 Commits

Reviewing files that changed from the base of the PR and between abb8e4f and f8beaf9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • src/components/AssetCard.tsx
  • src/components/Input.tsx
  • src/screens/Apps/Assets/Burn.tsx

Comment thread src/components/Input.tsx Resolved
Comment thread src/screens/Apps/Assets/Burn.tsx Resolved
@ghost

ghost commented Apr 1, 2026

Copy link
Copy Markdown

🔍 Arkana PR Review — wallet#507

Fix asset burn max | 4 files changed | +16 / -15

Summary

Fixes the asset burn screen: the "max" button now works correctly, amounts display with proper formatting, and the input type is updated.

Changes Breakdown

Burn.tsx (core fix):

  • amount state changed from string to number (from useState('')→useState(0))
  • handleMax now uses centsToUnits(balance, decimals) instead of formatAssetAmount(balance, decimals) — returns a numeric value rather than a formatted string
  • unitsToCents(amount, decimals) no longer wraps parseFloat() since amount is already a number
  • Confirmation dialog uses prettyNumber(amount) for display formatting
  • Placeholder changed from formatAssetAmount(balance, decimals) to '0'

AssetCard.tsx:

  • Balance display now uses prettyNumber(centsToUnits(balance, decimals)) for thousand-separator formatting

Input.tsx:

  • value prop type widened from string to string | number

pnpm-lock.yaml:

  • Minor dep bump (baseline-browser-mapping 2.10.0 → 2.10.13)

Review

  1. Type safety concern (minor): The Input component's value prop now accepts string | number. This works in React (numbers are coerced to strings in input elements), but if Input does any string operations on value internally (e.g., .trim(), .length), passing a number could cause a runtime error. Worth verifying that Input only passes value through to the underlying <input> element.

  2. onChange type flow: When amount is a number but onChange from an <input> element returns a string, the setAmount call in onChange={setAmount} would set a string value into what TypeScript expects to be a number state. Unless onChange is intercepted and parsed, this could cause unitsToCents(amount, decimals) to receive a string after user input (the parseFloat wrapper was previously handling this). The removed parseFloat in handleBurnRequest and handleBurnConfirm means manual text input could pass a string directly to unitsToCents — check that unitsToCents handles string input gracefully or that onChange parses the value.

  3. Max button: The fix is correct — centsToUnits returns the raw numeric value, which is what the input needs for calculations. formatAssetAmount was likely returning a comma-formatted string that couldn't be parsed back for the burn operation.

Security

No security concerns — UI-only changes to asset burn display and input handling. No transaction signing, key, or VTXO logic affected. The burn amount still goes through unitsToCents validation before being sent to the wallet service.

Verdict

The core fix is correct. The type flow concern in point #2 is worth a quick check — if Input's onChange returns the raw string from the DOM event, TypeScript won't catch the mismatch at runtime. Otherwise looks good. 👍

@pietro909
pietro909 self-requested a review April 1, 2026 13:25
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 1, 2026 •

Copy link
Copy Markdown

Deploying wallet-bitcoin with  Cloudflare Pages  Cloudflare Pages

Latest commit: ebac595
Status: ✅  Deploy successful!
Preview URL: https://4d586c30.wallet-bitcoin.pages.dev
Branch Preview URL: https://fix-asset-ui.wallet-bitcoin.pages.dev

View logs

@ghost

ghost commented Apr 1, 2026

Copy link
Copy Markdown

🔍 Arkana PR Review

Change: Fixes asset burn/mint/reissue flows to use numeric state and proper formatting for asset amounts.

Files changed: AssetCard.tsx, Input.tsx, Burn.tsx, Mint.tsx, Reissue.tsx, pnpm-lock.yaml

Key Changes

  1. Input.tsx — value prop now accepts string | number; onChange converts to Number(v) when type="number". This is the core fix driving the rest.

  2. Burn.tsx — State changed from useState('')→useState(0). Uses centsToUnits() for max instead of formatAssetAmount() (returns a number, not a formatted string with commas). Removes redundant parseFloat() wrappers since amount is already numeric.

  3. Mint.tsx — Same pattern: numeric state for amount and ctrlAmount. Removes parseFloat() calls. For control token amount, passes ctrlAmount directly (decimals=0, so no conversion needed).

  4. Reissue.tsx — Same numeric state pattern, removes parseFloat() wrappers.

  5. AssetCard.tsx — Wraps balance display in prettyNumber() for consistent formatting.

Observations

⚠️ Type safety concern in Mint.tsx decimals handler (line ~259):

onChange={(v: number) => {
  if (!v && v !== 0) {

The Input component's onChange will pass Number(v) when type="number". For an empty input, Number('') is 0, so !v && v !== 0 will never be true — the user can't clear the decimals field. Previously the check was v === '' which worked with string state. Consider handling the empty-string case before the Number() conversion, or using isNaN on the raw string.

⚠️ Burn placeholder change:
Placeholder changed from formatAssetAmount(balance, decimals) (showing the max balance) to a static '0'. This loses the UX hint of showing available balance in the placeholder. Intentional?

✅ No security concerns — changes are UI-only (formatting/display), no transaction logic or key handling affected. The unitsToCents() conversion is preserved correctly in all paths.

✅ pnpm-lock.yaml — minor dep bump (baseline-browser-mapping 2.10.0→2.10.13), unrelated but harmless.

Overall a solid cleanup. The two items above are minor but worth confirming.


Automated review by Arkana

@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)
src/screens/Apps/Assets/Mint.tsx (1)

173-188: ⚠️ Potential issue | 🟠 Major

Validate decimals with Number, not parseInt.

parseInt('1.5') becomes 1, so a fractional entry like 1.5 passes this check and later mints with 1 decimal instead of being rejected. Use Number(decimals) plus Number.isInteger(...) here, and mirror the same change in handleMint on Lines 90-92.

Suggested fix
-  const parsedDecimals = decimals !== '' ? parseInt(decimals) : NaN
+  const parsedDecimals = decimals !== '' ? Number(decimals) : NaN
   const disabledReason = !name
     ? 'Enter a name'
     : name.length > 40
       ? 'Name must be 40 characters or less'
       : !ticker
         ? 'Enter a ticker'
         : ticker.length > 8
           ? 'Ticker must be 8 characters or less'
           : !amount
             ? 'Enter an amount'
             : isNaN(parsedUnits) || parsedUnits <= 0
               ? 'Amount must be a positive number'
-              : isNaN(parsedDecimals) || parsedDecimals < 0 || parsedDecimals > 8
+              : !Number.isInteger(parsedDecimals) || parsedDecimals < 0 || parsedDecimals > 8
                 ? 'Decimals must be 0–8'
                 : controlMode === 'New' && !ctrlAmount
                   ? 'Enter control asset amount'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/screens/Apps/Assets/Mint.tsx` around lines 173 - 188, The decimals
validation uses parseInt which accepts fractional strings like "1.5"; update the
validation to use Number(decimals) and Number.isInteger to ensure decimals is an
integer within 0–8 (replace parsedDecimals = decimals !== '' ?
parseInt(decimals) : NaN with using Number and Number.isInteger), and make the
identical change in the handleMint function (the logic around parsedDecimals in
handleMint) so both the UI disabledReason check and the mint execution reject
fractional decimal inputs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/Input.tsx`:
- Around line 48-50: The handler handleInput currently coerces number inputs
with Number(v) which turns an empty string into 0 and loses edit-state; change
handleInput to pass the raw string value for type === 'number' (i.e., call
onChange with the uncoerced v for number inputs) so callers can distinguish ""
from 0, and move normalization/parsing (Number(...) and validation) into the
screen-level submit/validation logic (where you should handle conversion from
the raw string to a numeric value and return validation errors as needed).

---

Outside diff comments:
In `@src/screens/Apps/Assets/Mint.tsx`:
- Around line 173-188: The decimals validation uses parseInt which accepts
fractional strings like "1.5"; update the validation to use Number(decimals) and
Number.isInteger to ensure decimals is an integer within 0–8 (replace
parsedDecimals = decimals !== '' ? parseInt(decimals) : NaN with using Number
and Number.isInteger), and make the identical change in the handleMint function
(the logic around parsedDecimals in handleMint) so both the UI disabledReason
check and the mint execution reject fractional decimal inputs.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b5dfea7e-0286-4ef3-b15d-78de03ed683e

📥 Commits

Reviewing files that changed from the base of the PR and between f8beaf9 and b1adc75.

📒 Files selected for processing (3)
  • src/components/Input.tsx
  • src/screens/Apps/Assets/Mint.tsx
  • src/screens/Apps/Assets/Reissue.tsx

Comment thread src/components/Input.tsx Resolved
@ghost

ghost commented Apr 1, 2026

Copy link
Copy Markdown

🔍 Arkana Review — Followup on a09083af

New commit: fix type safety concern — addresses the Input.tsx value/onChange mismatch from the previous review.

What changed

Input.tsx — handleInput now converts to Number(v) when type === 'number', raw string otherwise. This fixes the core issue: callers like Burn.tsx with useState(0) now correctly receive numeric values from onChange.

Mint.tsx — Decimals handler simplified to (v: number) => setDecimals(!isNaN(v) && v >= 0 && v <= 8 ? String(v) : ''). Cleaner than the old string-based handler.

Remaining nit

Number('') returns 0, so when a user clears the input field, the state gets set to 0 rather than empty. For burn/mint/reissue amounts this is fine (0 is a valid "nothing entered" sentinel). For the decimals field it's also OK since the onChange handler has its own validation gate.

However, parseInt(decimals) on line ~173 in Mint.tsx still accepts fractional strings (parseInt('1.5') → 1). CodeRabbit flagged this too — consider Number(decimals) + Number.isInteger() for the validation check. Minor since the input step='1' constrains the stepper, but direct keyboard entry could bypass it.

LGTM with that nit. ✅

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/screens/Apps/Assets/Mint.tsx`:
- Line 115: The code forwards ctrlAmount into ctrlRawAmount without ensuring
it's an integer despite the asset metadata using decimals: 0; update the
validation where ctrlRawAmount is used (and where you currently check
isNaN/positive) to require an integer by using
Number.isInteger(Number(ctrlAmount)) or parsing to a Number and verifying value
=== Math.floor(value) and > 0, and surface a validation error if it fails;
additionally, update the control amount input tied to ctrlAmount (the input
rendering logic) to include step="1" and inputMode="numeric" to prevent/limit
fractional entry at the UI level.
- Around line 173-174: The decimals field currently uses parseInt(decimals)
(assigned to parsedDecimals) which silently truncates floats; update the
decimals input onChange handler to validate using
Number.isInteger(Number(value)) and reject or prevent non-integer values (e.g.,
show validation error/ignore change) and replace parseInt(decimals) with
Number(decimals) so invalid (non-integer/NaN) values are caught by that
validation; apply the same change where decimals is parsed earlier (the other
parseInt usage) and ensure any logic that uses parsedDecimals checks
Number.isInteger(parsedDecimals) or NaN handling before proceeding with minting.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 47dd3d46-cc48-4d58-97ca-da8bd93ca0cc

📥 Commits

Reviewing files that changed from the base of the PR and between b1adc75 and a09083a.

📒 Files selected for processing (1)
  • src/screens/Apps/Assets/Mint.tsx

Comment thread src/screens/Apps/Assets/Mint.tsx Resolved
Comment thread src/screens/Apps/Assets/Mint.tsx Resolved
@ghost

ghost commented Apr 1, 2026

Copy link
Copy Markdown

🔍 Arkana Review — Followup on ebac5958

Commit: rabbit fixes — wraps ctrlRawAmount and parsedUnits with parseInt(...toString()).

Looks good. Ensures integer types flow through correctly. Minor nit: parseInt(ctrlAmount.toString()) could be Math.trunc(ctrlAmount) since ctrlAmount is already number state — avoids the string roundtrip. Not blocking.

@bordalix
bordalix merged commit 1225a37 into master Apr 2, 2026
5 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 14, 2026
@bordalix
bordalix deleted the fix/asset_ui branch May 18, 2026 16:12
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 Burn Max

2 participants